Skip to content

Commit

Permalink
editor: Add Recent Files menu with recently loaded/saved files
Browse files Browse the repository at this point in the history
File paths are recorded into the game preferences up to a (currently
hardcoded) limit of 6 and opening or saving maps adds or bumps existing
entries to the top. We may allow users to change the MRU limit in
Advanced Preferences in a later commit.

Adds two translatable strings.

In order to fit file paths in the menu without filling up the whole
screen sideways, we use only the file names for now. Because identical
file names could prove to be an actual issue later, I intend to look
into ellipsizing paths correctly in a later step.

(Note that I'm piggybacking on the crummy submenu support we already had
in the themable UI so that this can be safely backported to 1.12. It's
decidedly not optimal usability-wise, but it'll have to do for now.)
  • Loading branch information
irydacea committed Oct 9, 2015
1 parent cafd94f commit 744dd6c
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 3 deletions.
10 changes: 9 additions & 1 deletion data/themes/editor.cfg
Expand Up @@ -61,12 +61,20 @@
title= _ "File"
type=turbo
font_size=9
items=editor-scenario-edit,statustable,unitlist,editor-map-new,editor-scenario-new,editor-map-load,editor-map-revert,editor-map-save,editor-map-save-as,editor-scenario-save-as,editor-map-save-all,preferences,help,editor-close-map,quit-editor,editor-quit-to-desktop
items=editor-scenario-edit,statustable,unitlist,editor-map-new,editor-scenario-new,editor-map-load,menu-editor-recent,editor-map-revert,editor-map-save,editor-map-save-as,editor-scenario-save-as,editor-map-save-all,preferences,help,editor-close-map,quit-editor,editor-quit-to-desktop
ref=top-panel
rect="=+1,=+1,+100,+20"
xanchor=fixed
yanchor=fixed
[/menu]

[menu]
title= _ "Load Recent"
id=menu-editor-recent
button=false
items=EDITOR-LOAD-MRU-PLACEHOLDER
[/menu]

# [menu]
# id=menu-editor-edit
# title= _ "Edit"
Expand Down
12 changes: 12 additions & 0 deletions src/editor/editor_controller.cpp
Expand Up @@ -246,6 +246,7 @@ bool editor_controller::can_execute_command(const hotkey::hotkey_command& cmd, i
return true;
}
return false;
case editor::LOAD_MRU:
case editor::PALETTE:
case editor::AREA:
case editor::SIDE:
Expand Down Expand Up @@ -526,6 +527,8 @@ hotkey::ACTION_STATE editor_controller::get_action_state(hotkey::HOTKEY_COMMAND
case editor::MAP:
return index == context_manager_->current_context_index()
? ACTION_SELECTED : ACTION_DESELECTED;
case editor::LOAD_MRU:
return ACTION_STATELESS;
case editor::PALETTE:
return ACTION_STATELESS;
case editor::AREA:
Expand Down Expand Up @@ -587,6 +590,11 @@ bool editor_controller::execute_command(const hotkey::hotkey_command& cmd, int i
}
}
return false;
case LOAD_MRU:
if (index >= 0) {
context_manager_->load_mru_item(static_cast<unsigned>(index));
}
return true;
case PALETTE:
toolkit_->get_palette_manager()->set_group(index);
return true;
Expand Down Expand Up @@ -970,6 +978,10 @@ void editor_controller::show_menu(const std::vector<std::string>& items_arg, int
}
++i;
}
if (!items.empty() && items.front() == "EDITOR-LOAD-MRU-PLACEHOLDER") {
active_menu_ = editor::LOAD_MRU;
context_manager_->expand_load_mru_menu(items);
}
if (!items.empty() && items.front() == "editor-switch-map") {
active_menu_ = editor::MAP;
context_manager_->expand_open_maps_menu(items);
Expand Down
1 change: 1 addition & 0 deletions src/editor/editor_controller.hpp
Expand Up @@ -55,6 +55,7 @@ std::string get_left_button_function();

enum menu_type {
MAP,
LOAD_MRU,
PALETTE,
AREA,
SIDE,
Expand Down
94 changes: 94 additions & 0 deletions src/editor/editor_preferences.cpp
Expand Up @@ -13,10 +13,13 @@
*/

#include "editor/editor_preferences.hpp"
#include "config.hpp"
#include "game_preferences.hpp"
#include "serialization/string_utils.hpp"
#include "util.hpp"

#include <boost/foreach.hpp>

namespace preferences {

namespace editor {
Expand Down Expand Up @@ -94,6 +97,97 @@ namespace editor {
return lexical_cast_in_range<int>(preferences::get("editor_b"), 0, -255, 255);
}

namespace {
// TODO: make user-customizable in Advanced Preferences.
const size_t MAX_RECENT_FILES = 6;

//
// NOTE: The MRU read/save functions enforce the entry count limit in
// order to ensure the list on disk doesn't grow forever. Otherwise,
// normally this would be the UI's responsibility instead.
//

std::vector<std::string> do_read_editor_mru()
{
const config& cfg = preferences::get_child("editor_recent_files");

std::vector<std::string> mru;
if(!cfg) {
return mru;
}

BOOST_FOREACH(const config& child, cfg.child_range("entry"))
{
const std::string& entry = child["path"].str();
if(!entry.empty()) {
mru.push_back(entry);
}
}

mru.resize(std::min(MAX_RECENT_FILES, mru.size()));

return mru;
}

void do_commit_editor_mru(const std::vector<std::string>& mru)
{
config cfg;
unsigned n = 0;

BOOST_FOREACH(const std::string& entry, mru)
{
if(entry.empty()) {
continue;
}

config& child = cfg.add_child("entry");
child["path"] = entry;

if(++n >= MAX_RECENT_FILES) {
break;
}
}

preferences::set_child("editor_recent_files", cfg);
}
}

std::vector<std::string> recent_files()
{
return do_read_editor_mru();
}

void add_recent_files_entry(const std::string& path)
{
if(path.empty()) {
return;
}

std::vector<std::string> mru = do_read_editor_mru();

// Enforce uniqueness. Normally shouldn't do a thing unless somebody
// has been tampering with the preferences file.
mru.erase(std::remove(mru.begin(), mru.end(), path), mru.end());

mru.insert(mru.begin(), path);
mru.resize(std::min(MAX_RECENT_FILES, mru.size()));

do_commit_editor_mru(mru);
}

void remove_recent_files_entry(const std::string& path)
{
if(path.empty()) {
return;
}

std::vector<std::string> mru = do_read_editor_mru();

mru.erase(std::remove(mru.begin(), mru.end(), path), mru.end());

do_commit_editor_mru(mru);
}

} //end namespace editor

} //end namespace preferences
Expand Down
8 changes: 8 additions & 0 deletions src/editor/editor_preferences.hpp
Expand Up @@ -16,6 +16,7 @@
#define EDITOR_PREFERENCES_HPP_INCLUDED

#include <string>
#include <vector>

namespace preferences {

Expand Down Expand Up @@ -56,6 +57,13 @@ namespace editor {
/** Get editor blue tint level. */
int tod_b();

/** Retrieves the list of recently opened files. */
std::vector<std::string> recent_files();
/** Adds an entry to the recent files list. */
void add_recent_files_entry(const std::string& path);
/** Removes a single entry from the recent files list. */
void remove_recent_files_entry(const std::string& path);

} //end namespace editor

} //end namespace preferences
Expand Down
41 changes: 41 additions & 0 deletions src/editor/map/context_manager.cpp
Expand Up @@ -187,6 +187,17 @@ void context_manager::load_map_dialog(bool force_same_context /* = false */)
}
}

void context_manager::load_mru_item(unsigned int index, bool force_same_context /* = false */)
{
const std::vector<std::string>& mru = preferences::editor::recent_files();

if(mru.empty() || index >= mru.size()) {
return;
}

load_map(mru[index], !force_same_context);
}

void context_manager::edit_side_dialog(int side)
{
team& t = get_map_context().get_teams()[side];
Expand Down Expand Up @@ -302,6 +313,36 @@ void context_manager::expand_open_maps_menu(std::vector<std::string>& items)
}
}

void context_manager::expand_load_mru_menu(std::vector<std::string>& items)
{
std::vector<std::string> mru = preferences::editor::recent_files();

for (unsigned int i = 0; i < items.size(); ++i) {
if (items[i] != "EDITOR-LOAD-MRU-PLACEHOLDER") {
continue;
}

items.erase(items.begin() + i);

if(mru.empty()) {
items.insert(items.begin() + i, _("No Recent Files"));
continue;
}

BOOST_FOREACH(std::string& path, mru)
{
// TODO: add proper leading ellipsization instead, since otherwise
// it'll be impossible to tell apart files with identical names and
// different parent paths.
path = filesystem::base_name(path);
}

items.insert(items.begin() + i, mru.begin(), mru.end());
break;
}

}

void context_manager::expand_areas_menu(std::vector<std::string>& items)
{
tod_manager* tod = get_map_context().get_time_manager();
Expand Down
6 changes: 6 additions & 0 deletions src/editor/map/context_manager.hpp
Expand Up @@ -111,6 +111,9 @@ class context_manager {
/** Menu expanding for open maps list */
void expand_open_maps_menu(std::vector<std::string>& items);

/** Menu expanding for most recent loaded list */
void expand_load_mru_menu(std::vector<std::string>& items);

/** Menu expanding for the map's player sides */
void expand_sides_menu(std::vector<std::string>& items);

Expand All @@ -126,6 +129,9 @@ class context_manager {
/** Display a load map dialog and process user input. */
void load_map_dialog(bool force_same_context = false);

/** Open the specified entry from the recent files list. */
void load_mru_item(unsigned index, bool force_same_context = false);

/** Display a scenario edit dialog and process user input. */
void edit_scenario_dialog();

Expand Down
16 changes: 14 additions & 2 deletions src/editor/map/map_context.cpp
Expand Up @@ -14,6 +14,7 @@
#define GETTEXT_DOMAIN "wesnoth-editor"

#include "editor/action/action.hpp"
#include "editor/editor_preferences.hpp"
#include "map_context.hpp"

#include "display.hpp"
Expand Down Expand Up @@ -141,6 +142,8 @@ map_context::map_context(const config& game_config, const std::string& filename,
boost::regex_constants::match_not_dot_null)) {
map_ = editor_map::from_string(game_config, file_string); //throws on error
pure_map_ = true;

add_to_recent_files();
return;
}

Expand All @@ -160,14 +163,15 @@ map_context::map_context(const config& game_config, const std::string& filename,
} catch (config::error & e) {
throw editor_map_load_exception("load_scenario", e.message); //we already caught and rethrew this exception in load_scenario
}
return;
} else {
LOG_ED << "Loading embedded map file" << std::endl;
embedded_ = true;
pure_map_ = true;
map_ = editor_map::from_string(game_config, map_data);
return;
}

add_to_recent_files();
return;
}

// 3.0 Macro referenced pure map
Expand All @@ -186,6 +190,8 @@ map_context::map_context(const config& game_config, const std::string& filename,
file_string = filesystem::read_file(filename_);
map_ = editor_map::from_string(game_config, file_string);
pure_map_ = true;

add_to_recent_files();
}

void map_context::set_side_setup(int side, const std::string& team_name, const std::string& user_team_name,
Expand Down Expand Up @@ -545,6 +551,7 @@ bool map_context::save_map()
throw editor_map_save_exception(_("Could not save into scenario"));
}
}
add_to_recent_files();
clear_modified();
} catch (filesystem::io_exception& e) {
utils::string_map symbols;
Expand Down Expand Up @@ -610,6 +617,11 @@ void map_context::clear_modified()
actions_since_save_ = 0;
}

void map_context::add_to_recent_files()
{
preferences::editor::add_recent_files_entry(get_filename());
}

bool map_context::can_undo() const
{
return !undo_stack_.empty();
Expand Down
3 changes: 3 additions & 0 deletions src/editor/map/map_context.hpp
Expand Up @@ -311,6 +311,9 @@ class map_context : private boost::noncopyable
/** Clear the modified state */
void clear_modified();

/** Adds the map to the editor's recent files list. */
void add_to_recent_files();

/** @return true when undo can be performed, false otherwise */
bool can_undo() const;

Expand Down

0 comments on commit 744dd6c

Please sign in to comment.