Add hotkeys plugin

develop
Anuradha Dissanayake 2014-06-02 15:48:34 +12:00 committed by Eric Wald
parent c4d6c384ae
commit 07de1ce3f6
4 changed files with 392 additions and 0 deletions

@ -2758,6 +2758,19 @@ There are three ways to open this editor:
This editor allows to change and modify almost anything in df. Press '?' for an This editor allows to change and modify almost anything in df. Press '?' for an
in-game help. in-game help.
Hotkeys
=======
Opens an in-game screen showing DFHack keybindings that are valid in the current mode.
.. image:: images/hotkeys.png
Type ``hotkeys`` into the DFHack console to open the screen, or bind the command to a
globally active hotkey in dfhack.init, e.g.:
``keybinding add Ctrl-F1 hotkeys``
gui/liquids gui/liquids
=========== ===========

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

@ -122,6 +122,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(follow follow.cpp) DFHACK_PLUGIN(follow follow.cpp)
DFHACK_PLUGIN(forceequip forceequip.cpp) DFHACK_PLUGIN(forceequip forceequip.cpp)
DFHACK_PLUGIN(getplants getplants.cpp) DFHACK_PLUGIN(getplants getplants.cpp)
DFHACK_PLUGIN(hotkeys hotkeys.cpp)
DFHACK_PLUGIN(infiniteSky infiniteSky.cpp) DFHACK_PLUGIN(infiniteSky infiniteSky.cpp)
DFHACK_PLUGIN(initflags initflags.cpp) DFHACK_PLUGIN(initflags initflags.cpp)
DFHACK_PLUGIN(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote) DFHACK_PLUGIN(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote)

@ -0,0 +1,378 @@
#include "uicommon.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/ui.h"
#include "modules/Maps.h"
#include "modules/World.h"
#include "modules/Gui.h"
#include "PluginManager.h"
DFHACK_PLUGIN("hotkeys");
#define PLUGIN_VERSION 0.1
static map<string, string> current_bindings;
static vector<string> sorted_keys;
static bool show_usage = false;
static void send_key(const df::interface_key &key)
{
set< df::interface_key > keys;
keys.insert(key);
Gui::getCurViewscreen(true)->feed(&keys);
}
static bool can_invoke(string cmdline, df::viewscreen *screen)
{
vector<string> cmd_parts;
split_string(&cmd_parts, cmdline, " ");
if (toLower(cmd_parts[0]) == "hotkeys")
return false;
return Core::getInstance().getPluginManager()->CanInvokeHotkey(cmd_parts[0], screen);
}
static void add_binding_if_valid(string sym, string cmdline, df::viewscreen *screen)
{
if (!can_invoke(cmdline, screen))
return;
current_bindings[sym] = cmdline;
sorted_keys.push_back(sym);
string keyspec = sym + "@dfhack/viewscreen_hotkeys";
Core::getInstance().AddKeyBinding(keyspec, "hotkeys invoke " + int_to_string(sorted_keys.size() - 1));
}
static void find_active_keybindings(df::viewscreen *screen)
{
current_bindings.clear();
sorted_keys.clear();
vector<string> valid_keys;
for (char c = 'A'; c <= 'Z'; c++)
{
valid_keys.push_back(string(&c, 1));
}
for (int i = 1; i < 10; i++)
{
valid_keys.push_back("F" + int_to_string(i));
}
auto current_focus = Gui::getFocusString(screen);
for (int shifted = 0; shifted < 2; shifted++)
{
for (int ctrl = 0; ctrl < 2; ctrl++)
{
for (int alt = 0; alt < 2; alt++)
{
for (auto it = valid_keys.begin(); it != valid_keys.end(); it++)
{
string sym;
if (shifted) sym += "Shift-";
if (ctrl) sym += "Ctrl-";
if (alt) sym += "Alt-";
sym += *it;
auto list = Core::getInstance().ListKeyBindings(sym);
for (auto invoke_cmd = list.begin(); invoke_cmd != list.end(); invoke_cmd++)
{
bool add_temp_binding = false;
if (invoke_cmd->find(":") == string::npos)
{
add_binding_if_valid(sym, *invoke_cmd, screen);
}
else
{
vector<string> tokens;
split_string(&tokens, *invoke_cmd, ":");
string focus = tokens[0].substr(1);
if (prefix_matches(focus, current_focus))
{
auto cmdline = trim(tokens[1]);
add_binding_if_valid(sym, cmdline, screen);
}
}
}
}
}
}
}
}
static bool close_hotkeys_screen()
{
auto screen = Core::getTopViewscreen();
if (Gui::getFocusString(screen) != "dfhack/viewscreen_hotkeys")
return false;
Screen::dismiss(Core::getTopViewscreen());
for_each_(sorted_keys, [] (const string &sym)
{ Core::getInstance().ClearKeyBindings(sym + "@dfhack/viewscreen_hotkeys"); });
sorted_keys.clear();
return true;
}
static void invoke_command(const int index)
{
if (sorted_keys.size() <= index)
return;
auto cmd = current_bindings[sorted_keys[index]];
if (close_hotkeys_screen())
{
Core::getInstance().setHotkeyCmd(cmd);
}
}
class ViewscreenHotkeys : public dfhack_viewscreen
{
public:
ViewscreenHotkeys(df::viewscreen *top_screen) : top_screen(top_screen)
{
hotkeys_column.multiselect = false;
hotkeys_column.auto_select = true;
hotkeys_column.setTitle("Key Binding");
hotkeys_column.bottom_margin = 4;
hotkeys_column.allow_search = false;
focus = Gui::getFocusString(top_screen);
populateColumns();
}
void populateColumns()
{
hotkeys_column.clear();
int max_key_length = 0;
for_each_(sorted_keys, [&] (const string &sym)
{ if (sym.length() > max_key_length) { max_key_length = sym.length(); } });
int padding = max_key_length + 2;
for (int i = 0; i < sorted_keys.size(); i++)
{
string text = pad_string(sorted_keys[i], padding, false);
text += current_bindings[sorted_keys[i]];
hotkeys_column.add(text, i+1);
}
help_start = hotkeys_column.fixWidth() + 2;
hotkeys_column.filterDisplay();
}
void feed(set<df::interface_key> *input)
{
if (hotkeys_column.feed(input))
return;
if (input->count(interface_key::LEAVESCREEN))
{
close_hotkeys_screen();
}
else if (input->count(interface_key::SELECT))
{
invoke_command(hotkeys_column.highlighted_index);
}
else if (input->count(interface_key::CUSTOM_U))
{
show_usage = !show_usage;
}
}
void render()
{
if (Screen::isDismissed(this))
return;
dfhack_viewscreen::render();
Screen::clear();
Screen::drawBorder(" Hotkeys ");
hotkeys_column.display(true);
int32_t y = gps->dimy - 3;
int32_t x = 2;
OutputHotkeyString(x, y, "Leave", "Esc");
x += 3;
OutputHotkeyString(x, y, "Invoke", "Enter or Hotkey");
x += 3;
OutputToggleString(x, y, "Show Usage", "u", show_usage);
y = gps->dimy - 4;
x = 2;
OutputHotkeyString(x, y, focus.c_str(), "Context", false, help_start, COLOR_WHITE, COLOR_BROWN);
if (sorted_keys.size() == 0)
return;
y = 2;
x = help_start;
auto width = gps->dimx - help_start - 2;
vector <string> parts;
Core::cheap_tokenise(current_bindings[sorted_keys[hotkeys_column.highlighted_index]], parts);
if(parts.size() == 0)
return;
string first = parts[0];
parts.erase(parts.begin());
if (first[0] == '#')
return;
Plugin *plugin = Core::getInstance().getPluginManager()->getPluginByCommand(first);
if (plugin)
{
for (auto i = 0; i < plugin->size(); i++)
{
auto pc = plugin->operator[](i);
if (pc.name == first)
{
OutputString(COLOR_BROWN, x, y, "Help", true, help_start);
vector <string> lines;
string help_text = pc.description;
if (show_usage)
help_text += "\n\n" + pc.usage;
split_string(&lines, help_text, "\n");
for (auto it = lines.begin(); it != lines.end() && y < gps->dimy - 4; it++)
{
auto wrapped_lines = wrapString(*it, width);
for (auto wit = wrapped_lines.begin(); wit != wrapped_lines.end() && y < gps->dimy - 4; wit++)
{
OutputString(COLOR_WHITE, x, y, *wit, true, help_start);
}
}
break;
}
}
}
}
virtual std::string getFocusString()
{
return "viewscreen_hotkeys";
}
private:
ListColumn<int> hotkeys_column;
df::viewscreen *top_screen;
string focus;
int32_t help_start;
void resize(int32_t x, int32_t y)
{
dfhack_viewscreen::resize(x, y);
hotkeys_column.resize();
}
static vector<string> wrapString(string str, int width)
{
vector<string> result;
string excess;
if (str.length() > width)
{
auto cut_space = str.rfind(' ', width-1);
int excess_start;
if (cut_space == string::npos)
{
cut_space = width-1;
excess_start = cut_space;
}
else
{
excess_start = cut_space + 1;
}
string line = str.substr(0, cut_space);
excess = str.substr(excess_start);
result.push_back(line);
auto excess_lines = wrapString(excess, width);
result.insert(result.end(), excess_lines.begin(), excess_lines.end());
}
else
{
result.push_back(str);
}
return result;
}
};
static command_result hotkeys_cmd(color_ostream &out, vector <string> & parameters)
{
bool show_help = false;
if (parameters.empty())
{
if (Maps::IsValid())
{
auto top_screen = Core::getTopViewscreen();
if (Gui::getFocusString(top_screen) != "dfhack/viewscreen_hotkeys")
{
find_active_keybindings(top_screen);
Screen::show(new ViewscreenHotkeys(top_screen));
}
}
}
else
{
auto cmd = parameters[0][0];
if (cmd == 'v')
{
out << "Hotkeys" << endl << "Version: " << PLUGIN_VERSION << endl;
}
else if (cmd == 'i')
{
int index;
stringstream index_raw(parameters[1]);
index_raw >> index;
invoke_command(index);
}
else
{
return CR_WRONG_USAGE;
}
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
if (!gps)
out.printerr("Could not insert hotkeys hooks!\n");
commands.push_back(
PluginCommand(
"hotkeys", "Shows ingame viewscreen with all dfhack keybindings active in current mode.",
hotkeys_cmd, false, ""));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event) {
case SC_MAP_LOADED:
sorted_keys.clear();
break;
default:
break;
}
return CR_OK;
}