00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "SDL.h"
00019 #include "SDL_keysym.h"
00020
00021 #include "../config.hpp"
00022 #include "../construct_dialog.hpp"
00023 #include "../cursor.hpp"
00024 #include "../file_chooser.hpp"
00025 #include "../filesystem.hpp"
00026 #include "../font.hpp"
00027 #include "../game_config.hpp"
00028 #include "../gettext.hpp"
00029 #include "../key.hpp"
00030 #include "../language.hpp"
00031 #include "../widgets/menu.hpp"
00032 #include "../pathfind.hpp"
00033
00034 #include "../preferences.hpp"
00035 #include "../sdl_utils.hpp"
00036 #include "../tooltips.hpp"
00037 #include "../team.hpp"
00038 #include "../util.hpp"
00039 #include "../video.hpp"
00040 #include "../wml_separators.hpp"
00041 #include "../wml_exception.hpp"
00042 #include "serialization/parser.hpp"
00043 #include "serialization/string_utils.hpp"
00044
00045 #include "editor.hpp"
00046 #include "map_manip.hpp"
00047 #include "editor_dialogs.hpp"
00048 #include "editor_palettes.hpp"
00049
00050 #include <cctype>
00051 #include <iostream>
00052 #include <map>
00053 #include <vector>
00054 #include <string>
00055 #include <cmath>
00056
00057 namespace {
00058 static std::string wm_title_string;
00059
00060
00061 const unsigned int sdl_delay = 20;
00062 const std::string prefs_filename = get_dir(get_user_data_dir() + "/editor")
00063 + "/preferences";
00064
00065
00066
00067 int starting_side_at(const gamemap& map, const gamemap::location hex) {
00068 int start_side = -1;
00069 for (int i = 0; i < gamemap::MAX_PLAYERS+1; i++) {
00070 if (map.starting_position(i) == hex) {
00071 start_side = i;
00072 }
00073 }
00074 return start_side;
00075 }
00076 }
00077
00078 namespace map_editor {
00079
00080 bool check_data(std::string &data, std::string &filename, bool &from_scenario, config &game_cfg)
00081 {
00082 std::string file_content = data;
00083
00084 if (valid_mapdata(file_content, game_cfg)) {
00085 from_scenario = false;
00086 return true;
00087 }
00088
00089 std::string::size_type start, stop;
00090 start = file_content.find("map_data=");
00091 if (start==std::string::npos)
00092 return false;
00093 start += 10;
00094 stop = file_content.find('\"', start);
00095 if (stop==std::string::npos)
00096 return false;
00097 std::string nfname;
00098 std::string newfilename = std::string(file_content, start, stop-start);
00099 if (newfilename[0]=='{' && newfilename[newfilename.size()-1]=='}') {
00100 newfilename.erase(newfilename.begin());
00101 newfilename.erase(newfilename.end() - 1);
00102
00103
00104
00105
00106 if(newfilename != "" && (newfilename[0] == '~' || newfilename[0] == '@')) {
00107 nfname = newfilename;
00108 nfname.erase(nfname.begin(),nfname.begin()+1);
00109 nfname = get_user_data_dir() + "/data/" + nfname;
00110
00111 std::cout << "got relative name '" << newfilename << "' -> '" << nfname << "'\n";
00112
00113 if(newfilename[0] == '@' && file_exists(nfname) == false && is_directory(nfname) == false) {
00114 nfname = "data/" + newfilename.substr(1);
00115 }
00116 } else
00117 if(newfilename.size() >= 2 && newfilename[0] == '.' &&
00118 newfilename[1] == '/' ) {
00119
00120
00121
00122 nfname = newfilename;
00123 nfname.erase(nfname.begin(),nfname.begin()+2);
00124 nfname = directory_name(filename) + nfname;
00125
00126 } else {
00127 nfname = "data/" + newfilename;
00128 }
00129
00130 data = read_file(nfname);
00131 filename = nfname;
00132 from_scenario = false;
00133 } else {
00134 data = newfilename;
00135 from_scenario = true;
00136 }
00137 return true;
00138 }
00139
00140
00141
00142
00143
00144
00145
00146
00147 bool map_editor::first_time_created_ = true;
00148 int map_editor::num_operations_since_save_ = 0;
00149 config map_editor::prefs_;
00150 config map_editor::hotkeys_;
00151
00152
00153 map_editor::LEFT_BUTTON_FUNC map_editor::l_button_func_ = PASTE;
00154 t_translation::t_terrain map_editor::old_fg_terrain_;
00155 t_translation::t_terrain map_editor::old_bg_terrain_;
00156 int map_editor::old_brush_size_;
00157
00158 map_editor::map_editor(editor_display &gui, editormap &map, config &theme, config &game_config)
00159 : gui_(gui), map_(map), abort_(DONT_ABORT),
00160 theme_(theme), game_config_(game_config), map_dirty_(false), auto_update_(true), l_button_palette_dirty_(true),
00161 everything_dirty_(false), palette_(gui, size_specs_, map, game_config), brush_(gui, size_specs_),
00162 l_button_held_func_(NONE), tooltip_manager_(gui_.video()), floating_label_manager_(),
00163 mouse_moved_(false),
00164 highlighted_locs_cleared_(false), prefs_disp_manager_(&gui_), all_hexes_selected_(false) {
00165
00166
00167 adjust_sizes(gui_, size_specs_);
00168 if (first_time_created_) {
00169
00170
00171 try {
00172 scoped_istream stream = istream_file(prefs_filename);
00173 read(prefs_, *stream);
00174 }
00175 catch (config::error e) {
00176 std::cerr << "Error when reading " << prefs_filename << ": "
00177 << e.message << std::endl;
00178 }
00179 hotkey::load_hotkeys(theme_);
00180 hotkey::load_hotkeys(prefs_);
00181 left_button_func_changed(DRAW);
00182 first_time_created_ = false;
00183 }
00184 else {
00185 hotkey::load_hotkeys(hotkeys_);
00186
00187
00188
00189 hotkey::load_hotkeys(theme_);
00190
00191 palette_.select_fg_terrain(old_fg_terrain_);
00192 palette_.select_bg_terrain(old_bg_terrain_);
00193 brush_.select_brush_size(old_brush_size_);
00194 }
00195
00196 hotkey::load_descriptions();
00197 recalculate_starting_pos_labels();
00198 gui_.invalidate_game_status();
00199
00200 gui_.invalidate_all();
00201 gui_.draw();
00202 palette_.adjust_size();
00203 brush_.adjust_size();
00204 events::raise_draw_event();
00205 redraw_everything();
00206 load_tooltips();
00207 }
00208
00209
00210
00211
00212
00213
00214
00215 void map_editor::load_tooltips()
00216 {
00217
00218 tooltips::clear_tooltips();
00219
00220
00221 const theme &t = gui_.get_theme();
00222 const std::vector<theme::menu> &menus = t.menus();
00223 std::vector<theme::menu>::const_iterator it;
00224 for (it = menus.begin(); it != menus.end(); it++) {
00225
00226
00227 SDL_Rect screen = gui_.screen_area();
00228
00229 const SDL_Rect tooltip_rect = (*it).location(screen);
00230 std::string text = "";
00231
00232 const std::vector<std::string> &menu_items = (*it).items();
00233 if (menu_items.size() == 1) {
00234 if(menu_items.back() == "editdraw")
00235 text = _("Draw tiles");
00236 else if(menu_items.back() == "editfloodfill")
00237 text = _("Fill");
00238 else if(menu_items.back() == "editsetstartpos")
00239 text = _("Set player's starting position");
00240 else if(menu_items.back() == "zoomin")
00241 text = _("Zoom in");
00242 else if(menu_items.back() == "zoomout")
00243 text = _("Zoom out");
00244 else if(menu_items.back() == "undo")
00245 text = _("Undo");
00246 else if(menu_items.back() == "redo")
00247 text = _("Redo");
00248 else if(menu_items.back() == "zoomdefault")
00249 text = _("Zoom to default view");
00250 else if(menu_items.back() == "togglegrid")
00251 text = _("Toggle grid");
00252 else if(menu_items.back() == "editresize")
00253 text = _("Resize the map");
00254 else if(menu_items.back() == "editflip")
00255 text = _("Flip map");
00256 else if(menu_items.back() == "editupdate")
00257 text = _("Update transitions");
00258 else if(menu_items.back() == "editautoupdate")
00259 text = _("Delay transition updates");
00260 }
00261
00262 if(text != "")
00263 tooltips::add_tooltip(tooltip_rect, text);
00264 }
00265
00266
00267 palette_.load_tooltips();
00268 }
00269
00270 map_editor::~map_editor() {
00271
00272 hotkey::save_hotkeys(hotkeys_);
00273 old_fg_terrain_ = palette_.selected_fg_terrain();
00274 old_bg_terrain_ = palette_.selected_bg_terrain();
00275 old_brush_size_ = brush_.selected_brush_size();
00276 try {
00277 scoped_ostream prefs_file = ostream_file(prefs_filename);
00278 write(*prefs_file, prefs_);
00279 }
00280 catch (io_exception& e) {
00281 std::cerr << "Error when writing to " << prefs_filename << ": "
00282 << e.what() << std::endl;
00283 }
00284 }
00285
00286 void map_editor::handle_event(const SDL_Event &event) {
00287 if (event.type == SDL_VIDEORESIZE) {
00288 everything_dirty_ = true;
00289 }
00290 int mousex, mousey;
00291
00292 SDL_GetMouseState(&mousex,&mousey);
00293 const SDL_KeyboardEvent keyboard_event = event.key;
00294 handle_keyboard_event(keyboard_event, mousex, mousey);
00295
00296 const SDL_MouseButtonEvent mouse_button_event = event.button;
00297 handle_mouse_button_event(mouse_button_event, mousex, mousey);
00298
00299 tooltips::process(mousex, mousey);
00300 }
00301
00302 void map_editor::handle_keyboard_event(const SDL_KeyboardEvent &event,
00303 const int , const int ) {
00304 if (event.type == SDL_KEYDOWN) {
00305 const SDLKey sym = event.keysym.sym;
00306
00307
00308
00309 if (sym == SDLK_ESCAPE) {
00310 set_abort();
00311 }
00312 else {
00313 const bool old_fullscreen = preferences::fullscreen();
00314 const std::pair<int, int> old_resolution = preferences::resolution();
00315 hotkey::key_event(gui_, event, this);
00316
00317
00318
00319
00320 if (preferences::fullscreen() != old_fullscreen
00321 || old_resolution != preferences::resolution()) {
00322 everything_dirty_ = true;
00323 }
00324 }
00325 }
00326 }
00327
00328 void map_editor::handle_mouse_button_event(const SDL_MouseButtonEvent &event,
00329 const int mousex, const int mousey) {
00330 if (event.type == SDL_MOUSEBUTTONDOWN) {
00331 const Uint8 button = event.button;
00332 int scrollx = 0;
00333 int scrolly = 0;
00334 if (button == SDL_BUTTON_RIGHT) {
00335 selected_hex_ = gui_.hex_clicked_on(mousex, mousey);
00336 const theme::menu* const m = gui_.get_theme().context_menu();
00337 if (m != NULL) {
00338 show_menu(m->items(), mousex, mousey + 1, true);
00339 }
00340 }
00341 if (button == SDL_BUTTON_LEFT) {
00342 gamemap::location hex_clicked = gui_.hex_clicked_on(mousex, mousey);
00343 if (map_.on_board(hex_clicked, true)) {
00344 left_click(hex_clicked);
00345 }
00346 }
00347 if (button == SDL_BUTTON_RIGHT) {
00348 gamemap::location hex_clicked = gui_.hex_clicked_on(mousex, mousey);
00349 if (map_.on_board(hex_clicked, true)) {
00350 right_click(hex_clicked);
00351 }
00352 }
00353
00354
00355
00356
00357 if (button == SDL_BUTTON_MIDDLE) {
00358 const SDL_Rect& rect = gui_.map_area();
00359 const int centerx = (rect.x + rect.w) / 2;
00360 const int centery = (rect.y + rect.h) / 2;
00361
00362 const int xdisp = mousex - centerx;
00363 const int ydisp = mousey - centery;
00364 gui_.scroll(xdisp, ydisp);
00365 }
00366 if (point_in_rect(mousex, mousey, gui_.map_outside_area())) {
00367 if (event.button == SDL_BUTTON_WHEELUP) {
00368 scrolly = - preferences::scroll_speed();
00369 } else if (event.button == SDL_BUTTON_WHEELDOWN) {
00370 scrolly = preferences::scroll_speed();
00371 } else if (event.button == SDL_BUTTON_WHEELLEFT) {
00372 scrollx = - preferences::scroll_speed();
00373 } else if (event.button == SDL_BUTTON_WHEELRIGHT) {
00374 scrollx = preferences::scroll_speed();
00375 }
00376 }
00377
00378 if (scrollx != 0 || scrolly != 0) {
00379 CKey pressed;
00380
00381 if (pressed[SDLK_LALT] || pressed[SDLK_RALT])
00382 gui_.scroll(scrolly,scrollx);
00383 else
00384 gui_.scroll(scrollx,scrolly);
00385 }
00386
00387 }
00388 if (event.type == SDL_MOUSEBUTTONUP) {
00389
00390
00391 if (l_button_held_func_ == MOVE_SELECTION) {
00392 perform_selection_move();
00393 }
00394 l_button_held_func_ = NONE;
00395 }
00396 }
00397
00398 void map_editor::left_click(const gamemap::location hex_clicked) {
00399 if (key_[SDLK_LSHIFT] || key_[SDLK_RSHIFT]) {
00400 if (selected_hexes_.find(hex_clicked)
00401 == selected_hexes_.end()) {
00402 l_button_held_func_ = ADD_SELECTION;
00403 }
00404 else {
00405 l_button_held_func_ = REMOVE_SELECTION;
00406 }
00407 }
00408 else if (key_[SDLK_RCTRL] || key_[SDLK_LCTRL]) {
00409 const t_translation::t_terrain terrain = map_.get_terrain(selected_hex_);
00410 if(palette_.selected_fg_terrain() != terrain) {
00411 palette_.select_fg_terrain(terrain);
00412 }
00413 l_button_held_func_ = NONE;
00414 }
00415 else if ( (key_[SDLK_RALT] || key_[SDLK_LALT]) && l_button_func_ == DRAW) {
00416 reset_mouseover_overlay();
00417 draw_on_mouseover_hexes(palette_.selected_fg_terrain(), true);
00418 l_button_held_func_ = DRAW_TERRAIN_LAYER;
00419 }
00420 else if (selected_hexes_.find(hex_clicked) != selected_hexes_.end()) {
00421 l_button_held_func_ = MOVE_SELECTION;
00422 selection_move_start_ = hex_clicked;
00423 }
00424 else if (!selected_hexes_.empty()) {
00425
00426
00427 selected_hexes_.clear();
00428 clear_highlighted_hexes_in_gui();
00429 }
00430 else if (l_button_func_ == DRAW) {
00431 reset_mouseover_overlay();
00432 draw_on_mouseover_hexes(palette_.selected_fg_terrain());
00433 l_button_held_func_ = DRAW_TERRAIN;
00434 }
00435 else if (l_button_func_ == FLOOD_FILL) {
00436 perform_flood_fill(palette_.selected_fg_terrain());
00437 }
00438 else if (l_button_func_ == SET_STARTING_POSITION) {
00439 perform_set_starting_pos();
00440 }
00441 else if (l_button_func_ == PASTE) {
00442 perform_paste();
00443 }
00444 }
00445
00446 void map_editor::right_click(const gamemap::location hex_clicked ) {
00447 if (key_[SDLK_RCTRL] || key_[SDLK_LCTRL]) {
00448 const t_translation::t_terrain terrain = map_.get_terrain(hex_clicked);
00449 if(palette_.selected_fg_terrain() != terrain) {
00450 palette_.select_bg_terrain(terrain);
00451 }
00452 }
00453 else if (l_button_func_ == FLOOD_FILL) {
00454 perform_flood_fill(palette_.selected_bg_terrain());
00455 }
00456 }
00457
00458
00459
00460 void map_editor::change_language() {
00461 std::vector<language_def> langdefs = get_languages();
00462
00463
00464
00465 const std::vector<language_def>::iterator current = std::find(langdefs.begin(),langdefs.end(),get_language());
00466 if(current != langdefs.end()) {
00467 (*current).language = "*" + (*current).language;
00468 }
00469
00470
00471 std::vector<std::string> langs;
00472 langs.reserve(langdefs.size());
00473 std::transform(langdefs.begin(),langdefs.end(),std::back_inserter(langs),languagedef_name);
00474
00475 const std::string language = _("Language");
00476 const std::string preferred = _("Choose your preferred language:");
00477 gui::dialog lmenu = gui::dialog(gui_, language, preferred,
00478 gui::OK_CANCEL);
00479 lmenu.set_menu(langs);
00480 int res = lmenu.show();
00481 const std::vector<language_def>& languages = get_languages();
00482 if(size_t(res) < languages.size()) {
00483 ::set_language(languages[res]);
00484 preferences::set_language(languages[res].localename);
00485
00486 game_config_.reset_translation();
00487
00488
00489 load_tooltips();
00490 }
00491
00492
00493 wm_title_string = _("Battle for Wesnoth Map Editor");
00494 wm_title_string += " - " + game_config::revision;
00495 SDL_WM_SetCaption(wm_title_string.c_str(), NULL);
00496
00497 font::load_font_config();
00498 hotkey::load_descriptions();
00499
00500
00501 editormap new_map(game_config_, map_.write());
00502 map_ = new_map;
00503
00504
00505 palette_.update_selected_terrains();
00506 }
00507
00508 void map_editor::edit_save_as() {
00509 const std::string default_dir =
00510 get_dir(get_dir(get_user_data_dir() + "/editor") + "/maps");
00511 std::string input_name = filename_.empty() ? default_dir : filename_;
00512 const std::string old_input_name = input_name;
00513
00514 int res = 0;
00515 int overwrite = 1;
00516 do {
00517 input_name = old_input_name;
00518 res = dialogs::show_file_chooser_dialog(gui_, input_name, _("Save the Map As"));
00519 if (res == 0) {
00520
00521
00522 if(!verify_filename(input_name, true))
00523 {
00524 input_name = old_input_name;
00525 continue;
00526 }
00527 else if (file_exists(input_name)) {
00528 overwrite = gui::dialog(gui_, "",
00529 _("The map already exists. Do you want to overwrite it?"),
00530 gui::YES_NO).show();
00531 }
00532 else
00533 overwrite = 0;
00534 }
00535 } while (res == 0 && overwrite != 0);
00536
00537
00538 if (res == 0) {
00539 std::string old_file_name = filename_;
00540 set_file_to_save_as(input_name, from_scenario_);
00541 if(!save_map("", true))
00542 {
00543 filename_ = old_file_name;
00544 }
00545 }
00546 }
00547
00548 void map_editor::perform_set_starting_pos() {
00549 std::vector<std::string> players;
00550
00551 std::stringstream none_str;
00552 none_str << _("None");
00553 players.push_back(none_str.str());
00554
00555 for (int i = 0; i < gamemap::MAX_PLAYERS; i++) {
00556 std::stringstream str;
00557 str << _("Player") << " " << i + 1;
00558 players.push_back(str.str());
00559 }
00560 gui::dialog pmenu = gui::dialog(gui_,
00561 _("Which Player?"),
00562 _("Which player should start here?"),
00563 gui::OK_CANCEL);
00564 pmenu.set_menu(players);
00565 int res = pmenu.show();
00566 if (res >= 0) {
00567
00568
00569
00570 const int current_side = starting_side_at(map_, selected_hex_);
00571 if (current_side != -1) {
00572 set_starting_position(current_side, gamemap::location());
00573 }
00574 if (res > 0) {
00575 set_starting_position(res, selected_hex_);
00576 }
00577 }
00578 }
00579
00580 void map_editor::edit_set_start_pos() {
00581 left_button_func_changed(SET_STARTING_POSITION);
00582 }
00583
00584 void map_editor::perform_flood_fill(const t_translation::t_terrain fill_with) {
00585
00586 t_translation::t_terrain fill_with_final = map_.get_terrain_info(fill_with).terrain_with_default_base();
00587
00588 terrain_log log;
00589 flood_fill(map_, selected_hex_, fill_with_final, &log);
00590 map_undo_action action;
00591 for (terrain_log::iterator it = log.begin(); it != log.end(); it++) {
00592 action.add_terrain((*it).second, palette_.selected_fg_terrain(),
00593 (*it).first);
00594 terrain_changed((*it).first);
00595 }
00596
00597 save_undo_action(action);
00598 }
00599
00600 void map_editor::edit_flood_fill() {
00601 left_button_func_changed(FLOOD_FILL);
00602 }
00603
00604 void map_editor::edit_save_map() {
00605 save_map("", true);
00606 }
00607
00608 void map_editor::edit_quit() {
00609 set_abort();
00610 }
00611
00612 void map_editor::edit_new_map() {
00613 t_translation::t_terrain bg_fill = map_.get_terrain_info(palette_.selected_bg_terrain()).terrain_with_default_base();
00614
00615 const std::string map = new_map_dialog(gui_, bg_fill,
00616 changed_since_save(), game_config_);
00617 if (map != "") {
00618 num_operations_since_save_ = 0;
00619 clear_undo_actions();
00620 throw new_map_exception(map);
00621 }
00622 }
00623
00624 void map_editor::edit_load_map() {
00625 std::string fn = filename_.empty() ?
00626 get_dir(get_dir(get_user_data_dir() + "/editor") + "/maps") : filename_;
00627 int res = dialogs::show_file_chooser_dialog(gui_, fn, _("Choose a Map to Load"));
00628 if (res == 0) {
00629
00630 if(!verify_filename(fn, false)) {
00631 gui::message_dialog(gui_, "", _("Warning: Illegal characters found in the map name. Please save under a different name.")).show();
00632 }
00633
00634 std::string new_map = read_file(fn);
00635 bool scenario;
00636 if (check_data(new_map, fn, scenario, game_config_)) {
00637 if (!changed_since_save() || confirm_modification_disposal(gui_)) {
00638 num_operations_since_save_ = 0;
00639 clear_undo_actions();
00640 throw new_map_exception(new_map, fn, scenario);
00641 }
00642 }
00643 else {
00644 gui::message_dialog(gui_, "", _("The file does not contain a valid map.")).show();
00645 }
00646 }
00647 }
00648
00649 void map_editor::edit_fill_selection() {
00650 map_undo_action undo_action;
00651 perform_fill_hexes(selected_hexes_, palette_.selected_bg_terrain(), undo_action);
00652 save_undo_action(undo_action);
00653 }
00654
00655 void map_editor::edit_cut() {
00656 edit_copy();
00657 edit_fill_selection();
00658 }
00659
00660 void map_editor::edit_copy() {
00661 clear_buffer(clipboard_);
00662 insert_selection_in_clipboard();
00663 }
00664
00665 void map_editor::perform_paste() {
00666 map_undo_action undo_action;
00667 paste_buffer(clipboard_, selected_hex_, undo_action);
00668 save_undo_action(undo_action);
00669 }
00670
00671 void map_editor::edit_paste() {
00672 left_button_func_changed(PASTE);
00673 }
00674
00675 void map_editor::edit_rotate_selection()
00676 {
00677 if (selected_hexes_.empty()) {return;}
00678
00679
00680 gamemap::location center = selected_hex_;
00681 if (!center.valid()) {
00682
00683
00684 center = gamemap::location(0,0);
00685 std::set<gamemap::location>::const_iterator it;
00686 for(it = selected_hexes_.begin(); it != selected_hexes_.end(); it++) {
00687 center = center + *it;
00688 }
00689 center.x = center.x / selected_hexes_.size();
00690 center.y = center.y / selected_hexes_.size();
00691 }
00692
00693 map_buffer buf;
00694 copy_buffer(buf, selected_hexes_, center);
00695
00696 std::vector<buffer_item>::iterator it;
00697 for(it = buf.begin(); it != buf.end(); it++) {
00698 gamemap::location l(0,0);
00699 int x = it->offset.x;
00700 int y = it->offset.y;
00701
00702
00703 l = l.get_direction(gamemap::location::SOUTH, (x+is_odd(x))/2);
00704 l = l.get_direction(gamemap::location::SOUTH_EAST, (x-is_odd(x))/2 );
00705 l = l.get_direction(gamemap::location::SOUTH_WEST, y);
00706 it->offset = l;
00707 }
00708
00709 map_undo_action undo_action;
00710 paste_buffer(buf, center, undo_action);
00711 save_undo_action(undo_action);
00712 }
00713
00714 void map_editor::edit_revert() {
00715 std::string new_map = read_file(filename_);
00716 bool scenario;
00717 if (check_data(new_map, filename_, scenario, game_config_)) {
00718 map_undo_action action;
00719 action.set_map_data(map_.write(), new_map);
00720 save_undo_action(action);
00721 throw new_map_exception(new_map, filename_, scenario);
00722 }
00723 else {
00724 gui::message_dialog(gui_, "", _("The file does not contain a valid map.")).show();
00725 }
00726 }
00727
00728 void map_editor::edit_resize() {
00729
00730 unsigned width = map_.w(), height = map_.h();
00731 int x_offset = 0, y_offset = 0;
00732 bool do_expand = true;
00733 if(resize_dialog(gui_, width, height, x_offset, y_offset, do_expand)) {
00734
00735 try {
00736
00737
00738 const std::string old_data = map_.write();
00739
00740 t_translation::t_terrain bg_fill = map_.get_terrain_info(palette_.selected_bg_terrain()).terrain_with_default_base();
00741 const std::string resized_map =
00742 resize_map(map_, width, height, x_offset, y_offset,
00743 do_expand, bg_fill);
00744
00745 if (resized_map != "") {
00746 map_undo_action action;
00747 action.set_map_data(old_data, resized_map);
00748 save_undo_action(action);
00749 throw new_map_exception(resized_map, filename_, from_scenario_);
00750 }
00751 } catch (gamemap::incorrect_format_exception& e) {
00752 std::cerr << "ERROR: " << e.msg_ << '\n';
00753 gui::message_dialog(gui_, "", e.msg_).show();
00754 } catch(twml_exception& e) {
00755 e.show(gui_);
00756 }
00757 }
00758 }
00759
00760 void map_editor::edit_flip() {
00761 const FLIP_AXIS flip_axis = flip_dialog(gui_);
00762 if (flip_axis != NO_FLIP) {
00763 const std::string flipped_map = flip_map(map_, flip_axis);
00764 map_undo_action action;
00765 action.set_map_data(map_.write(), flipped_map);
00766 save_undo_action(action);
00767 throw new_map_exception(flipped_map, filename_, from_scenario_);
00768 }
00769 }
00770
00771 void map_editor::edit_select_all() {
00772 if (!all_hexes_selected_) {
00773 for (int i = 0; i < map_.w(); i++) {
00774 for (int j = 0; j < map_.h(); j++) {
00775 selected_hexes_.insert(gamemap::location(i, j));
00776 }
00777 }
00778 all_hexes_selected_ = true;
00779 }
00780 else {
00781 selected_hexes_.clear();
00782 all_hexes_selected_ = false;
00783 }
00784 highlight_selected_hexes();
00785 }
00786
00787 void map_editor::edit_draw() {
00788 left_button_func_changed(DRAW);
00789 }
00790
00791 void map_editor::edit_refresh() {
00792 image::flush_cache();
00793 redraw_everything();
00794 }
00795
00796 void map_editor::edit_update() {
00797 if (map_dirty_) {
00798 map_dirty_ = false;
00799 gui_.rebuild_all();
00800 gui_.invalidate_all();
00801 recalculate_starting_pos_labels();
00802 gui_.recalculate_minimap();
00803 }
00804 }
00805
00806 void map_editor::edit_auto_update() {
00807 auto_update_ = !auto_update_;
00808 }
00809
00810 hotkey::ACTION_STATE map_editor::get_action_state(hotkey::HOTKEY_COMMAND command) const {
00811 if(command == hotkey::HOTKEY_EDIT_AUTO_UPDATE) {
00812 return (auto_update_) ? hotkey::ACTION_OFF : hotkey::ACTION_ON;
00813 }
00814 return command_executor::get_action_state(command);
00815 }
00816
00817 void map_editor::copy_buffer(map_buffer& buffer, const std::set<gamemap::location> &locs, const gamemap::location &origin)
00818 {
00819 std::set<gamemap::location>::const_iterator it;
00820 for (it = locs.begin(); it != locs.end(); it++) {
00821 t_translation::t_terrain terrain = map_.get_terrain(*it);
00822 buffer.push_back(buffer_item(*it-origin, terrain, starting_side_at(map_, *it)));
00823 }
00824 }
00825
00826 void map_editor::paste_buffer(const map_buffer &buffer, const gamemap::location &loc, map_undo_action &undo_action)
00827 {
00828 std::set<gamemap::location> filled;
00829 std::vector<buffer_item>::const_iterator it;
00830 for (it = buffer.begin(); it != buffer.end(); it++) {
00831
00832 gamemap::location target = it->offset + loc;
00833
00834 if (map_.on_board(target, true)) {
00835 undo_action.add_terrain(map_.get_terrain(target), it->terrain, target);
00836 map_.set_terrain(target, it->terrain);
00837 terrain_changed(target);
00838
00839 const int start_side = it->starting_side;
00840 if (start_side != -1) {
00841 undo_action.add_starting_location(start_side, start_side,
00842 map_.starting_position(start_side), target);
00843 map_.set_starting_position(start_side, target);
00844 }
00845 filled.insert(target);
00846 }
00847 }
00848
00849 undo_action.set_selection(selected_hexes_, filled);
00850 selected_hexes_ = filled;
00851 highlight_selected_hexes(true);
00852 }
00853
00854 void map_editor::insert_selection_in_clipboard() {
00855
00856
00857 gamemap::location origin(1000,1000);
00858 std::set<gamemap::location>::const_iterator it;
00859 for (it = selected_hexes_.begin(); it != selected_hexes_.end(); it++) {
00860 if (distance_between(selected_hex_, *it) <
00861 distance_between(selected_hex_, origin)) {
00862 origin = *it;
00863 }
00864 }
00865
00866 copy_buffer(clipboard_, selected_hexes_, origin);
00867 }
00868
00869 void map_editor::perform_fill_hexes(std::set<gamemap::location> &fill_hexes,
00870 const t_translation::t_terrain terrain, map_undo_action &undo_action) {
00871 std::set<gamemap::location>::const_iterator it;
00872 for (it = fill_hexes.begin(); it != fill_hexes.end(); it++) {
00873 if (map_.on_board(*it, true)) {
00874 undo_action.add_terrain(map_.get_terrain(*it), terrain, *it);
00875 if (terrain.base == t_translation::NO_LAYER) {
00876 map_.set_terrain(*it, terrain, gamemap::OVERLAY);
00877 }
00878 else {
00879 map_.set_terrain(*it, terrain);
00880 }
00881 terrain_changed(*it);
00882 }
00883 }
00884 }
00885
00886 void map_editor::perform_selection_move() {
00887 map_undo_action undo_action;
00888 std::set<gamemap::location> old_selection = selected_hexes_;
00889
00890 map_buffer buf;
00891 copy_buffer(buf, selected_hexes_, selection_move_start_);
00892 paste_buffer(buf,selected_hex_, undo_action);
00893
00894 std::set<gamemap::location>::const_iterator it;
00895 for (it = old_selection.begin(); it != old_selection.end(); it++) {
00896 if (selected_hexes_.find(*it) != selected_hexes_.end())
00897 old_selection.erase(*it);
00898 }
00899 perform_fill_hexes(old_selection, palette_.selected_bg_terrain(),undo_action);
00900
00901 save_undo_action(undo_action);
00902 }
00903
00904
00905 bool map_editor::can_execute_command(hotkey::HOTKEY_COMMAND command, int) const {
00906 switch (command) {
00907 case hotkey::HOTKEY_UNDO:
00908 case hotkey::HOTKEY_REDO:
00909 case hotkey::HOTKEY_ZOOM_IN:
00910 case hotkey::HOTKEY_ZOOM_OUT:
00911 case hotkey::HOTKEY_ZOOM_DEFAULT:
00912 case hotkey::HOTKEY_FULLSCREEN:
00913 case hotkey::HOTKEY_SCREENSHOT:
00914 case hotkey::HOTKEY_MAP_SCREENSHOT:
00915 case hotkey::HOTKEY_TOGGLE_GRID:
00916 case hotkey::HOTKEY_MOUSE_SCROLL:
00917 case hotkey::HOTKEY_PREFERENCES:
00918
00919 case hotkey::HOTKEY_EDIT_SAVE_MAP:
00920 case hotkey::HOTKEY_EDIT_SAVE_AS:
00921 case hotkey::HOTKEY_EDIT_QUIT:
00922 case hotkey::HOTKEY_EDIT_SET_START_POS:
00923 case hotkey::HOTKEY_EDIT_NEW_MAP:
00924 case hotkey::HOTKEY_EDIT_LOAD_MAP:
00925 case hotkey::HOTKEY_EDIT_FLOOD_FILL:
00926 case hotkey::HOTKEY_EDIT_FILL_SELECTION:
00927 case hotkey::HOTKEY_EDIT_ROTATE_SELECTION:
00928 case hotkey::HOTKEY_EDIT_COPY:
00929 case hotkey::HOTKEY_EDIT_CUT:
00930 case hotkey::HOTKEY_EDIT_PASTE:
00931 case hotkey::HOTKEY_EDIT_REVERT:
00932 case hotkey::HOTKEY_EDIT_RESIZE:
00933 case hotkey::HOTKEY_EDIT_FLIP:
00934 case hotkey::HOTKEY_EDIT_SELECT_ALL:
00935 case hotkey::HOTKEY_EDIT_DRAW:
00936 case hotkey::HOTKEY_EDIT_REFRESH:
00937 case hotkey::HOTKEY_EDIT_UPDATE:
00938 case hotkey::HOTKEY_EDIT_AUTO_UPDATE:
00939 case hotkey::HOTKEY_LANGUAGE:
00940 return true;
00941 default:
00942 return false;
00943 }
00944 }
00945
00946 void map_editor::toggle_grid() {
00947 preferences::set_grid(!preferences::grid());
00948 gui_.set_grid(preferences::grid());
00949 gui_.invalidate_all();
00950 }
00951
00952 void map_editor::save_undo_action(const map_undo_action &action) {
00953
00954
00955 if (action.something_set()) {
00956 add_undo_action(action);
00957 num_operations_since_save_++;
00958 }
00959 }
00960
00961 void map_editor::undo() {
00962 if(exist_undo_actions()) {
00963 --num_operations_since_save_;
00964 map_undo_action action = pop_undo_action();
00965 if (action.selection_set()) {
00966 selected_hexes_ = action.undo_selection();
00967 highlight_selected_hexes(true);
00968 }
00969 if (action.terrain_set()) {
00970 for(std::map<gamemap::location, t_translation::t_terrain>::const_iterator it =
00971 action.undo_terrains().begin();
00972 it != action.undo_terrains().end(); ++it) {
00973 map_.set_terrain(it->first, it->second);
00974 terrain_changed(it->first);
00975 }
00976 }
00977 if (action.starting_location_set()) {
00978 for (std::map<gamemap::location, int>::const_iterator it =
00979 action.undo_starting_locations().begin();
00980 it != action.undo_starting_locations().end(); it++) {
00981 map_.set_starting_position((*it).second, (*it).first);
00982 }
00983 recalculate_starting_pos_labels();
00984 }
00985
00986 if (action.map_data_set()) {
00987 throw new_map_exception(action.old_map_data(), filename_, from_scenario_);
00988 }
00989 }
00990 }
00991
00992 void map_editor::redo() {
00993 if(exist_redo_actions()) {
00994 ++num_operations_since_save_;
00995 map_undo_action action = pop_redo_action();
00996 if (action.selection_set()) {
00997 selected_hexes_ = action.redo_selection();
00998 highlight_selected_hexes(true);
00999 }
01000 if (action.terrain_set()) {
01001 for(std::map<gamemap::location, t_translation::t_terrain>::const_iterator it =
01002 action.redo_terrains().begin();
01003 it != action.redo_terrains().end(); ++it) {
01004 if (it->second.base == t_translation::NO_LAYER) {
01005 map_.set_terrain(it->first, it->second, gamemap::OVERLAY);
01006 }
01007 else {
01008 map_.set_terrain(it->first, it->second);
01009 }
01010 terrain_changed(it->first);
01011 }
01012 }
01013 if (action.starting_location_set()) {
01014 for (std::map<gamemap::location, int>::const_iterator it =
01015 action.redo_starting_locations().begin();
01016 it != action.redo_starting_locations().end(); it++) {
01017 map_.set_starting_position((*it).second, (*it).first);
01018 }
01019 recalculate_starting_pos_labels();
01020 }
01021 if (action.map_data_set()) {
01022 throw new_map_exception(action.new_map_data(), filename_, from_scenario_);
01023 }
01024 }
01025 }
01026
01027 void map_editor::preferences() {
01028 preferences_dialog(gui_, prefs_);
01029 everything_dirty_ = true;
01030 }
01031
01032 void map_editor::redraw_everything() {
01033 adjust_sizes(gui_, size_specs_);
01034 palette_.adjust_size();
01035 brush_.adjust_size();
01036 gui_.redraw_everything();
01037 update_l_button_palette();
01038 palette_.draw(true);
01039 brush_.draw(true);
01040 load_tooltips();
01041 }
01042
01043 void map_editor::highlight_selected_hexes(const bool clear_old) {
01044 if (clear_old) {
01045 clear_highlighted_hexes_in_gui();
01046 }
01047 for (std::set<gamemap::location>::const_iterator it = selected_hexes_.begin();
01048 it != selected_hexes_.end(); it++) {
01049 gui_.add_highlighted_loc(*it);
01050 }
01051 }
01052
01053 void map_editor::clear_highlighted_hexes_in_gui() {
01054 gui_.clear_highlighted_locs();
01055 highlighted_locs_cleared_ = true;
01056 }
01057
01058 void map_editor::set_mouseover_overlay()
01059 {
01060 surface image_fg(image::get_image("terrain/" + map_.get_terrain_info(
01061 palette_.selected_fg_terrain()).editor_image() +
01062 ".png"));
01063 surface image_bg(image::get_image("terrain/" + map_.get_terrain_info(
01064 palette_.selected_bg_terrain()).editor_image() +
01065 ".png"));
01066
01067 if (image_fg == NULL || image_bg == NULL) {
01068 std::cerr << "Missing terrain icon\n";
01069 gui_.set_mouseover_hex_overlay(NULL);
01070 return;
01071 }
01072
01073
01074 surface image = create_compatible_surface(image_fg, image_fg->w, image_fg->h);
01075 SDL_FillRect(image,NULL,SDL_MapRGBA(image->format,0,0,0, 0));
01076
01077
01078
01079
01080
01081
01082
01083 static const Uint8 alpha = 196;
01084 static const int size = image_fg->w;
01085 static const int half_size = size / 2;
01086 static const int quarter_size = size / 4;
01087 static const int offset = 2;
01088 static const int new_size = half_size - 2;
01089 const int zoom = static_cast<int>(size * gui_.get_zoom_factor());
01090
01091
01092 image_fg = scale_surface(image_fg, new_size, new_size);
01093 SDL_Rect rcDestLeft = { offset, quarter_size, 0, 0 };
01094 SDL_BlitSurface ( image_fg, NULL, image, &rcDestLeft );
01095
01096
01097 image_bg = scale_surface(image_bg, new_size, new_size);
01098 SDL_Rect rcDestRight = { half_size, quarter_size, 0, 0 };
01099 SDL_BlitSurface ( image_bg, NULL, image, &rcDestRight );
01100
01101
01102 image = scale_surface(adjust_surface_alpha(image, alpha), zoom, zoom);
01103
01104
01105 gui_.set_mouseover_hex_overlay(image);
01106 }
01107
01108 bool map_editor::changed_since_save() const {
01109 return num_operations_since_save_ != 0;
01110 }
01111
01112 void map_editor::set_starting_position(const int player, const gamemap::location loc) {
01113 if(map_.on_board(loc)) {
01114 map_undo_action action;
01115
01116 action.add_starting_location(player, player, map_.starting_position(player), loc);
01117 map_.set_starting_position(player, loc);
01118 save_undo_action(action);
01119 recalculate_starting_pos_labels();
01120 }
01121 else {
01122
01123
01124 map_undo_action action;
01125 action.add_starting_location(player, player, map_.starting_position(player), gamemap::location());
01126 map_.set_starting_position(player, gamemap::location());
01127 save_undo_action(action);
01128 recalculate_starting_pos_labels();
01129
01130
01131 }
01132 }
01133
01134 void map_editor::set_abort(const ABORT_MODE abort) {
01135 abort_ = abort;
01136 }
01137
01138 void map_editor::set_file_to_save_as(const std::string filename, bool from_scenario) {
01139 if (original_filename_.empty())
01140 original_filename_ = filename;
01141 filename_ = filename;
01142 from_scenario_ = from_scenario;
01143 }
01144
01145 void map_editor::left_button_down(const int mousex, const int mousey) {
01146 const gamemap::location& minimap_loc = gui_.minimap_location_on(mousex,mousey);
01147 const gamemap::location hex = gui_.hex_clicked_on(mousex, mousey);
01148 if (minimap_loc.valid()) {
01149 gui_.scroll_to_tile(minimap_loc,display::WARP,false);
01150 }
01151 else if (key_[SDLK_RSHIFT] || key_[SDLK_LSHIFT]) {
01152 if (key_[SDLK_RALT] || key_[SDLK_LALT]) {
01153
01154 std::set<gamemap::location> selected;
01155 selected = get_component(map_, selected_hex_);
01156 for (std::set<gamemap::location>::const_iterator it = selected.begin();
01157 it != selected.end(); it++) {
01158 if (l_button_held_func_ == ADD_SELECTION) {
01159 gui_.add_highlighted_loc(*it);
01160 selected_hexes_.insert(*it);
01161 }
01162 else {
01163 gui_.remove_highlighted_loc(*it);
01164 selected_hexes_.erase(*it);
01165 }
01166 }
01167 update_mouse_over_hexes(hex);
01168 }
01169 else {
01170
01171 std::vector<gamemap::location> selected;
01172 selected = get_tiles(map_, hex, brush_.selected_brush_size());
01173 for (std::vector<gamemap::location>::const_iterator it = selected.begin();
01174 it != selected.end(); it++) {
01175 if (l_button_held_func_ == ADD_SELECTION) {
01176 gui_.add_highlighted_loc(*it);
01177 selected_hexes_.insert(*it);
01178 }
01179 else {
01180 gui_.remove_highlighted_loc(*it);
01181 selected_hexes_.erase(*it);
01182 }
01183 }
01184 update_mouse_over_hexes(hex);
01185 }
01186 }
01187
01188
01189 else if (l_button_held_func_ == DRAW_TERRAIN && mouse_moved_) {
01190 reset_mouseover_overlay();
01191 draw_on_mouseover_hexes(palette_.selected_fg_terrain());
01192 }
01193 else if (l_button_held_func_ == DRAW_TERRAIN_LAYER && mouse_moved_) {
01194 reset_mouseover_overlay();
01195 draw_on_mouseover_hexes(palette_.selected_fg_terrain(), true);
01196 }
01197 else if (l_button_held_func_ == MOVE_SELECTION && mouse_moved_) {
01198 reset_mouseover_overlay();
01199
01200
01201 gui_.clear_highlighted_locs();
01202 std::set<gamemap::location>::const_iterator it;
01203 for (it = selected_hexes_.begin(); it != selected_hexes_.end(); it++) {
01204 const gamemap::location hl_loc = (*it-selection_move_start_) + hex;
01205 if (map_.on_board(hl_loc, true)) {
01206 gui_.add_highlighted_loc(hl_loc);
01207 }
01208 }
01209 }
01210 }
01211
01212 void map_editor::draw_on_mouseover_hexes(const t_translation::t_terrain terrain, const bool one_layer_only) {
01213 if(map_.on_board(selected_hex_, true)) {
01214 std::vector<gamemap::location> hexes =
01215 get_tiles(map_, selected_hex_, brush_.selected_brush_size());
01216 draw_terrain(terrain, hexes, one_layer_only);
01217 }
01218 }
01219
01220 void map_editor::draw_terrain(const t_translation::t_terrain terrain,
01221 const std::vector<gamemap::location> &hexes, const bool one_layer_only)
01222 {
01223 map_undo_action undo_action;
01224 t_translation::t_terrain final_terrain = terrain;
01225 if(!one_layer_only) {
01226 final_terrain = map_.get_terrain_info(terrain).terrain_with_default_base();
01227 }
01228
01229 for(std::vector<gamemap::location>::const_iterator it = hexes.begin();
01230 it != hexes.end(); ++it) {
01231 const t_translation::t_terrain old_terrain = map_.get_terrain(*it);
01232 if(final_terrain != old_terrain) {
01233 undo_action.add_terrain(old_terrain, final_terrain, *it);
01234 if (final_terrain.base == t_translation::NO_LAYER) {
01235 map_.set_terrain(*it, final_terrain, gamemap::OVERLAY);
01236 }
01237 else if (one_layer_only) {
01238 map_.set_terrain(*it, final_terrain, gamemap::BASE);
01239 }
01240 else {
01241 map_.set_terrain(*it, final_terrain);
01242 }
01243
01244
01245 gui_.rebuild_terrain(*it);
01246 gui_.invalidate(*it);
01247 map_dirty_ = true;
01248 }
01249 }
01250
01251 save_undo_action(undo_action);
01252 }
01253
01254 void map_editor::terrain_changed(const gamemap::location &hex)
01255 {
01256 if (!auto_update_) {
01257 gui_.rebuild_terrain(hex);
01258 gui_.invalidate(hex);
01259 }
01260 map_dirty_ = true;
01261 }
01262
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282 void map_editor::right_button_down(const int , const int ) {
01283
01284
01285
01286 draw_on_mouseover_hexes(palette_.selected_bg_terrain());
01287 }
01288
01289 void map_editor::middle_button_down(const int mousex, const int mousey) {
01290 const gamemap::location& minimap_loc = gui_.minimap_location_on(mousex,mousey);
01291 const gamemap::location hex = gui_.hex_clicked_on(mousex, mousey);
01292 if (minimap_loc.valid()) {
01293 gui_.scroll_to_tile(minimap_loc,display::WARP,false);
01294 }
01295 }
01296
01297 bool map_editor::confirm_exit_and_save() {
01298 if (!changed_since_save())
01299 return true;
01300 if (gui::dialog(gui_, "",
01301 _("Quit Editor"), gui::YES_NO).show() != 0) {
01302 return false;
01303 }
01304 if (gui::dialog(gui_, "",
01305 _("Do you want to save the map before quitting?"), gui::YES_NO).show() == 0) {
01306 if (!save_map("", false)) {
01307 return false;
01308 }
01309 }
01310 return true;
01311 }
01312
01313 bool map_editor::save_map(const std::string fn, const bool display_confirmation) {
01314 std::string filename = fn;
01315 if (filename == "") {
01316 filename = filename_;
01317 if(filename == "") {
01318 edit_save_as();
01319 return (filename_ != "");
01320 }
01321 }
01322 else {
01323 filename_ = filename;
01324 }
01325
01326
01327
01328
01329 if(!verify_filename(filename, display_confirmation)) {
01330 return false;
01331 }
01332
01333 try {
01334 std::string data;
01335 if (from_scenario_) {
01336 data = read_file(original_filename_);
01337 std::string::size_type start, stop;
01338 start = data.find("map_data=");
01339 if (start==std::string::npos)
01340 return false;
01341 start += 10;
01342 stop = data.find('\"', start);
01343 if (stop==std::string::npos)
01344 return false;
01345 data.replace(start, stop-start, map_.write());
01346 } else {
01347 data = map_.write();
01348 }
01349
01350 write_file(filename, data);
01351 num_operations_since_save_ = 0;
01352 if (display_confirmation) {
01353 gui::message_dialog(gui_, "", _("Map saved.")).show();
01354 }
01355 if (from_scenario_)
01356 original_filename_ = filename;
01357 }
01358 catch (io_exception& e) {
01359 utils::string_map symbols;
01360 symbols["msg"] = e.what();
01361 const std::string msg = vgettext("Could not save the map: $msg",symbols);
01362 gui::message_dialog(gui_, "", msg).show();
01363 return false;
01364 }
01365 return true;
01366 }
01367
01368 bool map_editor::verify_filename(const std::string& filename, bool show_error) const
01369 {
01370 static const std::string allowed_characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/\\.";
01371 static const std::string prefix = "\\/";
01372 size_t start_pos = filename.find_last_of(prefix);
01373
01374 if(filename.find_first_not_of(allowed_characters, start_pos) != std::string::npos) {
01375 if(show_error) {
01376 gui::message_dialog(gui_, "", _("Error: Illegal character in filename.")).show();
01377 }
01378 return false;
01379 }
01380
01381 return true;
01382 }
01383 void map_editor::show_menu(const std::vector<std::string>& items, const int xloc,
01384 const int yloc, const bool ) {
01385
01386 if(items.size() == 1) {
01387 execute_command(hotkey::get_hotkey(items.front()).get_id());
01388 return;
01389 }
01390 static const std::string style = "menu2";
01391 gui::dialog kmenu = gui::dialog(gui_, "", "", gui::MESSAGE,
01392 gui::dialog::hotkeys_style);
01393 kmenu.set_menu(get_menu_images(items));
01394 const int res = kmenu.show(xloc, yloc);
01395 if(res < 0 || static_cast<unsigned>(res) >= items.size())
01396 return;
01397 const hotkey::HOTKEY_COMMAND cmd = hotkey::get_hotkey(items[res]).get_id();
01398 execute_command(cmd);
01399 }
01400
01401 void map_editor::execute_command(const hotkey::HOTKEY_COMMAND command) {
01402 if (command == hotkey::HOTKEY_QUIT_GAME) {
01403 set_abort();
01404 }
01405 else {
01406 hotkey::execute_command(gui_, command, this);
01407 }
01408 }
01409
01410 void map_editor::recalculate_starting_pos_labels() {
01411
01412 for (std::vector<gamemap::location>::const_iterator it = starting_positions_.begin();
01413 it != starting_positions_.end(); it++) {
01414 gui_.labels().set_label(*it, "");
01415 }
01416 starting_positions_.clear();
01417
01418 for (int i = 1; i < gamemap::MAX_PLAYERS+1; i++) {
01419 gamemap::location loc = map_.starting_position(i);
01420 if (loc.valid()) {
01421 std::stringstream ss;
01422 ss << _("Player") << " " << i;
01423 gui_.labels().set_label(loc, ss.str());
01424 starting_positions_.push_back(loc);
01425 }
01426 }
01427 }
01428
01429 void map_editor::update_mouse_over_hexes(const gamemap::location mouse_over_hex)
01430 {
01431 const int size = (l_button_func_ == DRAW) ? brush_.selected_brush_size() : 1;
01432 std::vector<gamemap::location> curr_locs = get_tiles(map_, mouse_over_hex, size);
01433
01434 std::set<gamemap::location>::iterator it;
01435 for (it = mouse_over_hexes_.begin(); it != mouse_over_hexes_.end(); it++) {
01436 if (selected_hexes_.find(*it) == selected_hexes_.end()) {
01437
01438
01439 gui_.remove_highlighted_loc(*it);
01440 }
01441 }
01442 mouse_over_hexes_.clear();
01443 if (mouse_over_hex != gamemap::location()) {
01444
01445 for (std::vector<gamemap::location>::iterator itv = curr_locs.begin();
01446 itv != curr_locs.end(); itv++) {
01447 mouse_over_hexes_.insert(*itv);
01448 gui_.add_highlighted_loc(*itv);
01449 }
01450 }
01451
01452 gui_.highlight_hex(mouse_over_hex);
01453
01454 if(l_button_func_ == DRAW || l_button_func_ == FLOOD_FILL) {
01455 set_mouseover_overlay();
01456 }
01457 selected_hex_ = mouse_over_hex;
01458 }
01459
01460 void map_editor::left_button_func_changed(const LEFT_BUTTON_FUNC func) {
01461 if (func != l_button_func_) {
01462 reset_mouseover_overlay();
01463 l_button_func_ = func;
01464 gui_.set_report_content(reports::EDIT_LEFT_BUTTON_FUNCTION,
01465 hotkey::get_hotkey(get_action_name(func)).get_description());
01466 gui_.invalidate_game_status();
01467 l_button_palette_dirty_ = true;
01468 }
01469 }
01470
01471 void map_editor::update_l_button_palette() {
01472 const theme &t = gui_.get_theme();
01473 const std::vector<theme::menu> &menus = t.menus();
01474 std::vector<theme::menu>::const_iterator it;
01475 for (it = menus.begin(); it != menus.end(); it++) {
01476 if (is_left_button_func_menu(*it)) {
01477 const SDL_Rect &r = (*it).location(gui_.screen_area());
01478 const int draw_x = maximum<int>(r.x - 1, gui_.screen_area().x);
01479 const int draw_y = maximum<int>(r.y - 1, gui_.screen_area().y);
01480 const int draw_w = minimum<int>(r.w + 2, gui_.screen_area().w);
01481 const int draw_h = minimum<int>(r.h + 2, gui_.screen_area().h);
01482 const SDL_Rect draw_rect = {draw_x, draw_y, draw_w, draw_h};
01483 SDL_Surface* const screen = gui_.video().getSurface();
01484 Uint32 color;
01485 if ((*it).items().back() == get_action_name(l_button_func_)) {
01486 color = SDL_MapRGB(screen->format,0xFF,0x00,0x00);
01487 }
01488 else {
01489 color = SDL_MapRGB(screen->format,0x00,0x00,0x00);
01490 }
01491 draw_rectangle(draw_rect.x, draw_rect.y, draw_rect.w, draw_rect.h,
01492 color, gui_.video().getSurface());
01493 update_rect(draw_rect);
01494 }
01495 }
01496 }
01497
01498 std::string map_editor::get_action_name(const LEFT_BUTTON_FUNC func) const {
01499 switch (func) {
01500 case DRAW:
01501 return "editdraw";
01502 case SELECT_HEXES:
01503 return "";
01504 case FLOOD_FILL:
01505 return "editfloodfill";
01506 case SET_STARTING_POSITION:
01507 return "editsetstartpos";
01508 case PASTE:
01509 return "editpaste";
01510 default:
01511 return "";
01512 }
01513 }
01514
01515 bool map_editor::is_left_button_func_menu(const theme::menu &menu) const {
01516 const std::vector<std::string> &menu_items = menu.items();
01517 if (menu_items.size() == 1) {
01518 const std::string item_name = menu_items.back();
01519 for (size_t i = 0; i < NUM_L_BUTTON_FUNC; i++) {
01520 if (get_action_name(LEFT_BUTTON_FUNC(i)) == item_name) {
01521 return true;
01522 }
01523 }
01524 }
01525 return false;
01526 }
01527
01528 void map_editor::main_loop() {
01529 unsigned int last_brush_size = brush_.selected_brush_size();
01530 while (abort_ == DONT_ABORT) {
01531 int mousex, mousey;
01532 const int scroll_speed = preferences::scroll_speed();
01533 Uint8 mouse_flags = SDL_GetMouseState(&mousex,&mousey);
01534 const bool l_button_down = (0 != (mouse_flags & SDL_BUTTON_LMASK));
01535 const bool r_button_down = (0 != (mouse_flags & SDL_BUTTON_RMASK));
01536 const bool m_button_down = (0 != (mouse_flags & SDL_BUTTON_MMASK));
01537
01538 const gamemap::location cur_hex = gui_.hex_clicked_on(mousex,mousey);
01539 if (cur_hex != selected_hex_) {
01540 mouse_moved_ = true;
01541 }
01542 if (mouse_moved_ || last_brush_size != brush_.selected_brush_size()
01543 || highlighted_locs_cleared_) {
01544
01545
01546 highlighted_locs_cleared_ = false;
01547 update_mouse_over_hexes(cur_hex);
01548 last_brush_size = brush_.selected_brush_size();
01549 }
01550 const theme::menu* const m = gui_.menu_pressed();
01551 if (m != NULL) {
01552 const SDL_Rect& menu_loc = m->location(gui_.screen_area());
01553 const int x = menu_loc.x + 1;
01554 const int y = menu_loc.y + menu_loc.h + 1;
01555 show_menu(m->items(), x, y, false);
01556 }
01557
01558 if(key_[SDLK_UP] || mousey == 0) {
01559 gui_.scroll(0,-scroll_speed);
01560 }
01561 if(key_[SDLK_DOWN] || mousey == gui_.h()-1) {
01562 gui_.scroll(0,scroll_speed);
01563 }
01564 if(key_[SDLK_LEFT] || mousex == 0) {
01565 gui_.scroll(-scroll_speed,0);
01566 }
01567 if(key_[SDLK_RIGHT] || mousex == gui_.w()-1) {
01568 gui_.scroll(scroll_speed,0);
01569 }
01570
01571 if (l_button_down) {
01572 left_button_down(mousex, mousey);
01573 }
01574 else {
01575 if (l_button_held_func_ == MOVE_SELECTION) {
01576
01577
01578
01579 perform_selection_move();
01580 }
01581 }
01582 if (r_button_down) {
01583 right_button_down(mousex, mousey);
01584 }
01585 if (m_button_down) {
01586 middle_button_down(mousex, mousey);
01587 }
01588
01589 gui_.draw(false);
01590 events::raise_draw_event();
01591
01592
01593
01594
01595 if (map_dirty_) {
01596 if (!l_button_down && !r_button_down) {
01597 if (auto_update_) {
01598 gui_.rebuild_all();
01599 gui_.invalidate_all();
01600 map_dirty_ = false;
01601 }
01602 gui_.recalculate_minimap();
01603 recalculate_starting_pos_labels();
01604 }
01605 }
01606 if (l_button_palette_dirty_) {
01607 update_l_button_palette();
01608 l_button_palette_dirty_ = false;
01609 }
01610 gui_.update_display();
01611 SDL_Delay(sdl_delay);
01612 events::pump();
01613 if (everything_dirty_) {
01614 redraw_everything();
01615 everything_dirty_ = false;
01616 }
01617 if (abort_ == ABORT_NORMALLY) {
01618 if (!confirm_exit_and_save()) {
01619 set_abort(DONT_ABORT);
01620 }
01621 }
01622 mouse_moved_ = false;
01623 }
01624 }
01625 }
01626
01627