Petr Mrázek 2012-05-20 17:33:09 +02:00
commit 3fa155e8e6
11 changed files with 707 additions and 334 deletions

@ -613,6 +613,15 @@ can be omitted.
Gui module Gui module
---------- ----------
* ``dfhack.gui.getCurViewscreen()``
Returns the viewscreen that is current in the core.
* ``dfhack.gui.getFocusString(viewscreen)``
Returns a string representation of the current focus position
in the ui. The string has a "screen/foo/bar/baz..." format.
* ``dfhack.gui.getSelectedWorkshopJob([silent])`` * ``dfhack.gui.getSelectedWorkshopJob([silent])``
When a job is selected in *'q'* mode, returns the job, else When a job is selected in *'q'* mode, returns the job, else

@ -868,6 +868,13 @@ can be omitted.</p>
<div class="section" id="gui-module"> <div class="section" id="gui-module">
<h3><a class="toc-backref" href="#id14">Gui module</a></h3> <h3><a class="toc-backref" href="#id14">Gui module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getCurViewscreen()</tt></p>
<p>Returns the viewscreen that is current in the core.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getFocusString(viewscreen)</tt></p>
<p>Returns a string representation of the current focus position
in the ui. The string has a &quot;screen/foo/bar/baz...&quot; format.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.gui.getSelectedWorkshopJob([silent])</span></tt></p> <li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.gui.getSelectedWorkshopJob([silent])</span></tt></p>
<p>When a job is selected in <em>'q'</em> mode, returns the job, else <p>When a job is selected in <em>'q'</em> mode, returns the job, else
prints error unless silent and returns <em>nil</em>.</p> prints error unless silent and returns <em>nil</em>.</p>

@ -75,7 +75,7 @@ using df::global::world;
// FIXME: A lot of code in one file, all doing different things... there's something fishy about it. // FIXME: A lot of code in one file, all doing different things... there's something fishy about it.
static bool parseKeySpec(std::string keyspec, int *psym, int *pmod); static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string *pfocus = NULL);
struct Core::Cond struct Core::Cond
{ {
@ -558,10 +558,15 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
{ {
con << "Usage:" << endl con << "Usage:" << endl
<< " keybinding list <key>" << endl << " keybinding list <key>" << endl
<< " keybinding clear <key> <key>..." << endl << " keybinding clear <key>[@context]..." << endl
<< " keybinding set <key> \"cmdline\" \"cmdline\"..." << endl << " keybinding set <key>[@context] \"cmdline\" \"cmdline\"..." << endl
<< " keybinding add <key> \"cmdline\" \"cmdline\"..." << endl << " keybinding add <key>[@context] \"cmdline\" \"cmdline\"..." << endl
<< "Later adds, and earlier items within one command have priority." << endl; << "Later adds, and earlier items within one command have priority." << endl
<< "Supported keys: [Ctrl-][Alt-][Shift-](A-Z, or F1-F9, or Enter)." << endl
<< "Context may be used to limit the scope of the binding, by" << endl
<< "requiring the current context to have a certain prefix." << endl
<< "Current UI context is: "
<< Gui::getFocusString(Core::getTopViewscreen()) << endl;
} }
} }
else if(first == "fpause") else if(first == "fpause")
@ -1254,7 +1259,7 @@ int Core::SDL_Event(SDL::Event* ev)
if(!started) return true; if(!started) return true;
if(!ev) if(!ev)
return true; return true;
if(ev && ev->type == SDL::ET_KEYDOWN || ev->type == SDL::ET_KEYUP) if(ev && (ev->type == SDL::ET_KEYDOWN || ev->type == SDL::ET_KEYUP))
{ {
SDL::KeyboardEvent * ke = (SDL::KeyboardEvent *)ev; SDL::KeyboardEvent * ke = (SDL::KeyboardEvent *)ev;
@ -1299,6 +1304,9 @@ bool Core::SelectHotkey(int sym, int modifiers)
while (screen->child) while (screen->child)
screen = screen->child; screen = screen->child;
if (sym == SDL::K_KP_ENTER)
sym = SDL::K_RETURN;
std::string cmd; std::string cmd;
{ {
@ -1309,6 +1317,9 @@ bool Core::SelectHotkey(int sym, int modifiers)
for (int i = bindings.size()-1; i >= 0; --i) { for (int i = bindings.size()-1; i >= 0; --i) {
if (bindings[i].modifiers != modifiers) if (bindings[i].modifiers != modifiers)
continue; continue;
if (!bindings[i].focus.empty() &&
!prefix_matches(bindings[i].focus, Gui::getFocusString(screen)))
continue;
if (!plug_mgr->CanInvokeHotkey(bindings[i].command[0], screen)) if (!plug_mgr->CanInvokeHotkey(bindings[i].command[0], screen))
continue; continue;
cmd = bindings[i].cmdline; cmd = bindings[i].cmdline;
@ -1340,10 +1351,22 @@ bool Core::SelectHotkey(int sym, int modifiers)
return false; return false;
} }
static bool parseKeySpec(std::string keyspec, int *psym, int *pmod) static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string *pfocus)
{ {
*pmod = 0; *pmod = 0;
if (pfocus)
{
*pfocus = "";
size_t idx = keyspec.find('@');
if (idx != std::string::npos)
{
*pfocus = keyspec.substr(idx+1);
keyspec = keyspec.substr(0, idx);
}
}
// ugh, ugly // ugh, ugly
for (;;) { for (;;) {
if (keyspec.size() > 6 && keyspec.substr(0, 6) == "Shift-") { if (keyspec.size() > 6 && keyspec.substr(0, 6) == "Shift-") {
@ -1365,6 +1388,9 @@ static bool parseKeySpec(std::string keyspec, int *psym, int *pmod)
} else if (keyspec.size() == 2 && keyspec[0] == 'F' && keyspec[1] >= '1' && keyspec[1] <= '9') { } else if (keyspec.size() == 2 && keyspec[0] == 'F' && keyspec[1] >= '1' && keyspec[1] <= '9') {
*psym = SDL::K_F1 + (keyspec[1]-'1'); *psym = SDL::K_F1 + (keyspec[1]-'1');
return true; return true;
} else if (keyspec == "Enter") {
*psym = SDL::K_RETURN;
return true;
} else } else
return false; return false;
} }
@ -1372,14 +1398,15 @@ static bool parseKeySpec(std::string keyspec, int *psym, int *pmod)
bool Core::ClearKeyBindings(std::string keyspec) bool Core::ClearKeyBindings(std::string keyspec)
{ {
int sym, mod; int sym, mod;
if (!parseKeySpec(keyspec, &sym, &mod)) std::string focus;
if (!parseKeySpec(keyspec, &sym, &mod, &focus))
return false; return false;
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex); tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
std::vector<KeyBinding> &bindings = key_bindings[sym]; std::vector<KeyBinding> &bindings = key_bindings[sym];
for (int i = bindings.size()-1; i >= 0; --i) { for (int i = bindings.size()-1; i >= 0; --i) {
if (bindings[i].modifiers == mod) if (bindings[i].modifiers == mod && prefix_matches(focus, bindings[i].focus))
bindings.erase(bindings.begin()+i); bindings.erase(bindings.begin()+i);
} }
@ -1390,7 +1417,7 @@ bool Core::AddKeyBinding(std::string keyspec, std::string cmdline)
{ {
int sym; int sym;
KeyBinding binding; KeyBinding binding;
if (!parseKeySpec(keyspec, &sym, &binding.modifiers)) if (!parseKeySpec(keyspec, &sym, &binding.modifiers, &binding.focus))
return false; return false;
cheap_tokenise(cmdline, binding.command); cheap_tokenise(cmdline, binding.command);
@ -1403,7 +1430,8 @@ bool Core::AddKeyBinding(std::string keyspec, std::string cmdline)
std::vector<KeyBinding> &bindings = key_bindings[sym]; std::vector<KeyBinding> &bindings = key_bindings[sym];
for (int i = bindings.size()-1; i >= 0; --i) { for (int i = bindings.size()-1; i >= 0; --i) {
if (bindings[i].modifiers == binding.modifiers && if (bindings[i].modifiers == binding.modifiers &&
bindings[i].cmdline == cmdline) bindings[i].cmdline == cmdline &&
bindings[i].focus == binding.focus)
return true; return true;
} }
@ -1424,7 +1452,12 @@ std::vector<std::string> Core::ListKeyBindings(std::string keyspec)
std::vector<KeyBinding> &bindings = key_bindings[sym]; std::vector<KeyBinding> &bindings = key_bindings[sym];
for (int i = bindings.size()-1; i >= 0; --i) { for (int i = bindings.size()-1; i >= 0; --i) {
if (bindings[i].modifiers == mod) if (bindings[i].modifiers == mod)
rv.push_back(bindings[i].cmdline); {
std::string cmd = bindings[i].cmdline;
if (!bindings[i].focus.empty())
cmd = "@" + bindings[i].focus + ": " + cmd;
rv.push_back(cmd);
}
} }
return rv; return rv;

@ -61,6 +61,7 @@ distribution.
#include "df/unit.h" #include "df/unit.h"
#include "df/item.h" #include "df/item.h"
#include "df/material.h" #include "df/material.h"
#include "df/viewscreen.h"
#include "df/assumed_identity.h" #include "df/assumed_identity.h"
#include "df/nemesis_record.h" #include "df/nemesis_record.h"
#include "df/historical_figure.h" #include "df/historical_figure.h"
@ -652,6 +653,8 @@ static const LuaWrapper::FunctionReg dfhack_module[] = {
/***** Gui module *****/ /***** Gui module *****/
static const LuaWrapper::FunctionReg dfhack_gui_module[] = { static const LuaWrapper::FunctionReg dfhack_gui_module[] = {
WRAPM(Gui, getCurViewscreen),
WRAPM(Gui, getFocusString),
WRAPM(Gui, getSelectedWorkshopJob), WRAPM(Gui, getSelectedWorkshopJob),
WRAPM(Gui, getSelectedJob), WRAPM(Gui, getSelectedJob),
WRAPM(Gui, getSelectedUnit), WRAPM(Gui, getSelectedUnit),

@ -36,6 +36,7 @@ distribution.
#include <ctype.h> #include <ctype.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h>
#include <sstream> #include <sstream>
#include <map> #include <map>
@ -124,6 +125,29 @@ std::string toLower(const std::string &str)
return rv; return rv;
} }
bool prefix_matches(const std::string &prefix, const std::string &key, std::string *tail)
{
size_t ksize = key.size();
size_t psize = prefix.size();
if (ksize < psize || memcmp(prefix.data(), key.data(), psize) != 0)
return false;
if (tail)
tail->clear();
if (ksize == psize)
return true;
if (psize == 0 || prefix[psize-1] == '/')
{
if (tail) *tail = key.substr(psize);
return true;
}
if (key[psize] == '/')
{
if (tail) *tail = key.substr(psize+1);
return true;
}
return false;
}
#ifdef LINUX_BUILD // Linux #ifdef LINUX_BUILD // Linux
uint64_t GetTimeMs64() uint64_t GetTimeMs64()
{ {

@ -205,6 +205,7 @@ namespace DFHack
int modifiers; int modifiers;
std::vector<std::string> command; std::vector<std::string> command;
std::string cmdline; std::string cmdline;
std::string focus;
}; };
std::map<int, std::vector<KeyBinding> > key_bindings; std::map<int, std::vector<KeyBinding> > key_bindings;

@ -452,6 +452,9 @@ namespace df
} }
}; };
template<class ET, class IT>
struct enum_traits<enum_field<ET, IT> > : public enum_traits<ET> {};
template<class EnumType, class IntType1, class IntType2> template<class EnumType, class IntType1, class IntType2>
inline bool operator== (enum_field<EnumType,IntType1> a, enum_field<EnumType,IntType2> b) inline bool operator== (enum_field<EnumType,IntType1> a, enum_field<EnumType,IntType2> b)
{ {

@ -262,6 +262,51 @@ Link *linked_list_insert_after(Link *pos, Link *link)
return link; return link;
} }
template<typename T>
inline typename T::mapped_type map_find(
const T &map, const typename T::key_type &key,
const typename T::mapped_type &defval = typename T::mapped_type()
) {
auto it = map.find(key);
return (it == map.end()) ? defval : it->second;
}
DFHACK_EXPORT bool prefix_matches(const std::string &prefix, const std::string &key, std::string *tail = NULL);
template<typename T>
typename T::mapped_type findPrefixInMap(
const T &table, const std::string &key,
const typename T::mapped_type& defval = typename T::mapped_type()
) {
auto it = table.lower_bound(key);
if (it != table.end() && it->first == key)
return it->second;
if (it != table.begin()) {
--it;
if (prefix_matches(it->first, key))
return it->second;
}
return defval;
}
#ifdef __GNUC__
#define VARIABLE_IS_NOT_USED __attribute__ ((unused))
#else
#define VARIABLE_IS_NOT_USED
#endif
template<class CT>
inline bool static_add_to_map(CT *pmap, typename CT::key_type key, typename CT::mapped_type value) {
(*pmap)[key] = value;
return true;
}
#define CONCAT_TOKENS2(a,b) a##b
#define CONCAT_TOKENS(a,b) CONCAT_TOKENS2(a,b)
#define DFHACK_STATIC_ADD_TO_MAP(pmap,key,value) \
static bool VARIABLE_IS_NOT_USED CONCAT_TOKENS(static_add_to_map_,__LINE__)\
= static_add_to_map(pmap,key,value)
/* /*
* MISC * MISC
*/ */

@ -55,6 +55,10 @@ namespace DFHack
*/ */
namespace Gui namespace Gui
{ {
inline df::viewscreen *getCurViewscreen() { return Core::getTopViewscreen(); }
DFHACK_EXPORT std::string getFocusString(df::viewscreen *top);
// Full-screen item details view // Full-screen item details view
DFHACK_EXPORT bool item_details_hotkey(df::viewscreen *top); DFHACK_EXPORT bool item_details_hotkey(df::viewscreen *top);
// 'u'nits or 'j'obs full-screen view // 'u'nits or 'j'obs full-screen view

@ -55,6 +55,7 @@ using namespace DFHack;
#include "df/viewscreen_layer_noblelistst.h" #include "df/viewscreen_layer_noblelistst.h"
#include "df/viewscreen_layer_overall_healthst.h" #include "df/viewscreen_layer_overall_healthst.h"
#include "df/viewscreen_layer_assigntradest.h" #include "df/viewscreen_layer_assigntradest.h"
#include "df/viewscreen_layer_militaryst.h"
#include "df/viewscreen_petst.h" #include "df/viewscreen_petst.h"
#include "df/viewscreen_tradegoodsst.h" #include "df/viewscreen_tradegoodsst.h"
#include "df/ui_unit_view_mode.h" #include "df/ui_unit_view_mode.h"
@ -64,6 +65,8 @@ using namespace DFHack;
#include "df/ui_build_selector.h" #include "df/ui_build_selector.h"
#include "df/building_workshopst.h" #include "df/building_workshopst.h"
#include "df/building_furnacest.h" #include "df/building_furnacest.h"
#include "df/building_trapst.h"
#include "df/building_civzonest.h"
#include "df/general_ref.h" #include "df/general_ref.h"
#include "df/unit_inventory_item.h" #include "df/unit_inventory_item.h"
#include "df/report.h" #include "df/report.h"
@ -77,12 +80,340 @@ using namespace df::enums;
using df::global::gview; using df::global::gview;
using df::global::init; using df::global::init;
using df::global::gps; using df::global::gps;
using df::global::ui;
using df::global::world;
static df::layer_object_listst *getLayerList(df::viewscreen_layerst *layer, int idx) static df::layer_object_listst *getLayerList(df::viewscreen_layerst *layer, int idx)
{ {
return virtual_cast<df::layer_object_listst>(vector_get(layer->layer_objects,idx)); return virtual_cast<df::layer_object_listst>(vector_get(layer->layer_objects,idx));
} }
static std::string getNameChunk(virtual_identity *id, int start, int end)
{
if (!id)
return "UNKNOWN";
const char *name = id->getName();
int len = strlen(name);
if (len > start + end)
return std::string(name+start, len-start-end);
else
return name;
}
/*
* Classifying focus context by means of a string path.
*/
typedef void (*getFocusStringHandler)(std::string &str, df::viewscreen *screen);
static std::map<virtual_identity*, getFocusStringHandler> getFocusStringHandlers;
#define VIEWSCREEN(name) df::viewscreen_##name##st
#define DEFINE_GET_FOCUS_STRING_HANDLER(screen_type) \
static void getFocusString_##screen_type(std::string &focus, VIEWSCREEN(screen_type) *screen);\
DFHACK_STATIC_ADD_TO_MAP(\
&getFocusStringHandlers, &VIEWSCREEN(screen_type)::_identity, \
(getFocusStringHandler)getFocusString_##screen_type \
); \
static void getFocusString_##screen_type(std::string &focus, VIEWSCREEN(screen_type) *screen)
DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
{
using namespace df::enums::ui_sidebar_mode;
using df::global::ui_workshop_in_add;
using df::global::ui_build_selector;
using df::global::ui_selected_unit;
using df::global::ui_look_list;
using df::global::ui_look_cursor;
using df::global::ui_building_item_cursor;
using df::global::ui_building_assign_type;
using df::global::ui_building_assign_is_marked;
using df::global::ui_building_assign_units;
using df::global::ui_building_assign_items;
using df::global::ui_building_in_assign;
focus += "/" + enum_item_key(ui->main.mode);
switch (ui->main.mode)
{
case QueryBuilding:
if (df::building *selected = world->selected_building)
{
if (!selected->jobs.empty() &&
selected->jobs[0]->job_type == job_type::DestroyBuilding)
{
focus += "/Destroying";
break;
}
focus += "/Some";
virtual_identity *id = virtual_identity::get(selected);
bool jobs = false;
if (id == &df::building_workshopst::_identity ||
id == &df::building_furnacest::_identity)
{
focus += "/Workshop";
jobs = true;
}
else if (id == &df::building_trapst::_identity)
{
auto trap = (df::building_trapst*)selected;
if (trap->trap_type == trap_type::Lever) {
focus += "/Lever";
jobs = true;
}
}
else if (ui_building_in_assign && *ui_building_in_assign &&
ui_building_assign_type && ui_building_assign_units &&
ui_building_assign_type->size() == ui_building_assign_units->size())
{
focus += "/Assign";
if (ui_building_item_cursor)
{
auto unit = vector_get(*ui_building_assign_units, *ui_building_item_cursor);
focus += unit ? "/Unit" : "/None";
}
}
if (jobs)
{
if (ui_workshop_in_add && *ui_workshop_in_add)
focus += "/AddJob";
else if (!selected->jobs.empty())
focus += "/Job";
else
focus += "/Empty";
}
}
else
focus += "/None";
break;
case Build:
if (ui_build_selector)
{
// Not selecting, or no choices?
if (ui_build_selector->building_type < 0)
focus += "/Type";
else if (ui_build_selector->stage != 2)
focus += "/Position";
else
{
focus += "/Material";
if (ui_build_selector->is_grouped)
focus += "/Groups";
else
focus += "/Items";
}
}
break;
case ViewUnits:
if (ui_selected_unit)
{
if (auto unit = vector_get(world->units.active, *ui_selected_unit))
{
focus += "/Some";
using df::global::ui_unit_view_mode;
if (ui_unit_view_mode)
focus += "/" + enum_item_key(ui_unit_view_mode->value);
}
else
focus += "/None";
}
break;
case LookAround:
if (ui_look_list && ui_look_cursor)
{
auto item = vector_get(ui_look_list->items, *ui_look_cursor);
if (item)
focus += "/" + enum_item_key(item->type);
else
focus += "/None";
}
break;
case BuildingItems:
if (VIRTUAL_CAST_VAR(selected, df::building_actual, world->selected_building))
{
if (selected->contained_items.empty())
focus += "/Some/Empty";
else
focus += "/Some/Item";
}
else
focus += "/None";
break;
case ZonesPenInfo:
if (ui_building_assign_type && ui_building_assign_units &&
ui_building_assign_is_marked && ui_building_assign_items &&
ui_building_assign_type->size() == ui_building_assign_units->size())
{
focus += "/Assign";
if (ui_building_item_cursor)
{
if (vector_get(*ui_building_assign_units, *ui_building_item_cursor))
focus += "/Unit";
else if (vector_get(*ui_building_assign_items, *ui_building_item_cursor))
focus += "/Vermin";
else
focus += "/None";
}
}
break;
case Burrows:
if (ui->burrows.in_add_units_mode)
focus += "/AddUnits";
else if (ui->burrows.in_edit_name_mode)
focus += "/EditName";
else if (ui->burrows.in_define_mode)
focus += "/Define";
else
focus += "/List";
break;
default:
break;
}
}
DEFINE_GET_FOCUS_STRING_HANDLER(unitlist)
{
focus += "/" + enum_item_key(screen->page);
}
DEFINE_GET_FOCUS_STRING_HANDLER(layer_military)
{
auto list1 = getLayerList(screen, 0);
auto list2 = getLayerList(screen, 1);
auto list3 = getLayerList(screen, 2);
if (!list1 || !list2 || !list3) return;
focus += "/" + enum_item_key(screen->page);
int cur_list;
if (list1->bright) cur_list = 0;
else if (list2->bright) cur_list = 1;
else if (list3->bright) cur_list = 2;
else return;
switch (screen->page)
{
case df::viewscreen_layer_militaryst::Positions:
{
static const char *lists[] = { "/Squads", "/Positions", "/Candidates" };
focus += lists[cur_list];
break;
}
default:
break;
}
}
DEFINE_GET_FOCUS_STRING_HANDLER(layer_workshop_profile)
{
auto list1 = getLayerList(screen, 0);
if (!list1) return;
if (vector_get(screen->workers, list1->cursor))
focus += "/Unit";
else
focus += "/None";
}
DEFINE_GET_FOCUS_STRING_HANDLER(layer_noblelist)
{
auto list1 = getLayerList(screen, 0);
auto list2 = getLayerList(screen, 1);
if (!list1 || !list2) return;
focus += "/" + enum_item_key(screen->mode);
}
DEFINE_GET_FOCUS_STRING_HANDLER(pet)
{
focus += "/" + enum_item_key(screen->mode);
switch (screen->mode)
{
case df::viewscreen_petst::List:
focus += vector_get(screen->is_vermin, screen->cursor) ? "/Vermin" : "/Unit";
break;
case df::viewscreen_petst::SelectTrainer:
if (vector_get(screen->trainer_unit, screen->trainer_cursor))
focus += "/Unit";
break;
}
}
DEFINE_GET_FOCUS_STRING_HANDLER(layer_overall_health)
{
auto list1 = getLayerList(screen, 0);
if (!list1) return;
focus += "/Units";
}
DEFINE_GET_FOCUS_STRING_HANDLER(tradegoods)
{
if (!screen->has_traders || screen->is_unloading)
focus += "/NoTraders";
else if (screen->in_edit_count)
focus += "/EditCount";
else
focus += (screen->in_right_pane ? "/Items/Broker" : "/Items/Trader");
}
DEFINE_GET_FOCUS_STRING_HANDLER(layer_assigntrade)
{
auto list1 = getLayerList(screen, 0);
auto list2 = getLayerList(screen, 1);
if (!list1 || !list2) return;
int list_idx = vector_get(screen->visible_lists, list1->cursor, (int16_t)-1);
unsigned num_lists = sizeof(screen->lists)/sizeof(screen->lists[0]);
if (unsigned(list_idx) >= num_lists)
return;
if (list1->bright)
focus += "/Groups";
else
focus += "/Items";
}
std::string Gui::getFocusString(df::viewscreen *top)
{
if (!top)
return "";
if (virtual_identity *id = virtual_identity::get(top))
{
std::string name = getNameChunk(id, 11, 2);
auto handler = map_find(getFocusStringHandlers, id);
if (handler)
handler(name, top);
return name;
}
else
{
Core &core = Core::getInstance();
std::string name = core.p->readClassName(*(void**)top);
return name.substr(11, name.size()-11-2);
}
}
// Predefined common guard functions // Predefined common guard functions
bool Gui::default_hotkey(df::viewscreen *top) bool Gui::default_hotkey(df::viewscreen *top)

@ -204,7 +204,8 @@ static bool ParseSpec(color_ostream &out, lua_State *L, const char *type, vector
} }
#define PARSE_SPEC(type, params) \ #define PARSE_SPEC(type, params) \
if (!ParseSpec(*pout, L, type, params)) return false; std::vector<unsigned> order; \
if (!ParseSpec(*pout, L, type, params)) return;
static bool prepare_sort(color_ostream *pout, lua_State *L) static bool prepare_sort(color_ostream *pout, lua_State *L)
{ {
@ -230,310 +231,226 @@ static df::layer_object_listst *getLayerList(df::viewscreen_layerst *layer, int
return virtual_cast<df::layer_object_listst>(vector_get(layer->layer_objects,idx)); return virtual_cast<df::layer_object_listst>(vector_get(layer->layer_objects,idx));
} }
static bool maybe_sort_units(color_ostream *pout, lua_State *L, typedef void (*SortHandler)(color_ostream *pout, lua_State *L, int top,
df::viewscreen *screen, vector<string> &parameters) df::viewscreen *screen, vector<string> &parameters);
{
Lua::StackUnwinder top(L);
if (!prepare_sort(pout, L)) #define VIEWSCREEN(name) df::viewscreen_##name##st
return false; #define DEFINE_SORT_HANDLER(map, screen_type, tail, screen) \
static void CONCAT_TOKENS(SortHandler_##screen_type,__LINE__)\
(color_ostream *pout, lua_State *L, int top, \
VIEWSCREEN(screen_type) *screen, vector<string> &parameters); \
DFHACK_STATIC_ADD_TO_MAP(&map, #screen_type tail, \
(SortHandler)CONCAT_TOKENS(SortHandler_##screen_type,__LINE__) ); \
static void CONCAT_TOKENS(SortHandler_##screen_type,__LINE__)\
(color_ostream *pout, lua_State *L, int top, \
VIEWSCREEN(screen_type) *screen, vector<string> &parameters)
std::vector<unsigned> order; static std::map<std::string, SortHandler> unit_sorters;
if (auto units = strict_virtual_cast<df::viewscreen_unitlistst>(screen)) /*
{ * Sort units in the 'u'nit list screen.
if (!L) return true; */
/* DEFINE_SORT_HANDLER(unit_sorters, unitlist, "", units)
* Sort units in the 'u'nit list screen. {
*/ PARSE_SPEC("units", parameters);
PARSE_SPEC("units", parameters); int page = units->page;
int page = units->page; if (compute_order(*pout, L, top, &order, units->units[page]))
{
reorder_cursor(&units->cursor_pos[page], order);
reorder_vector(&units->units[page], order);
reorder_vector(&units->jobs[page], order);
}
}
if (compute_order(*pout, L, top, &order, units->units[page])) /*
{ * Sort units in the 'j'ob list screen.
reorder_cursor(&units->cursor_pos[page], order); */
reorder_vector(&units->units[page], order);
reorder_vector(&units->jobs[page], order);
}
return true; DEFINE_SORT_HANDLER(unit_sorters, joblist, "", jobs)
} {
else if (auto jobs = strict_virtual_cast<df::viewscreen_joblistst>(screen)) PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, jobs->units))
{ {
if (!L) return true; reorder_cursor(&jobs->cursor_pos, order);
reorder_vector(&jobs->units, order);
reorder_vector(&jobs->jobs, order);
}
}
/* /*
* Sort units in the 'j'ob list screen. * Sort candidate units in the 'p'osition page of the 'm'ilitary screen.
*/ */
PARSE_SPEC("units", parameters); DEFINE_SORT_HANDLER(unit_sorters, layer_military, "/Positions/Candidates", military)
{
auto &candidates = military->positions.candidates;
auto list3 = getLayerList(military, 2);
if (compute_order(*pout, L, top, &order, jobs->units)) PARSE_SPEC("units", parameters);
{
reorder_cursor(&jobs->cursor_pos, order);
reorder_vector(&jobs->units, order);
reorder_vector(&jobs->jobs, order);
}
return true; if (compute_order(*pout, L, top, &order, candidates))
}
else if (auto military = strict_virtual_cast<df::viewscreen_layer_militaryst>(screen))
{ {
switch (military->page) reorder_cursor(&list3->cursor, order);
{ reorder_vector(&candidates, order);
case df::viewscreen_layer_militaryst::Positions: }
{ }
auto &candidates = military->positions.candidates;
auto list3 = getLayerList(military, 2);
/*
* Sort candidate units in the 'p'osition page of the 'm'ilitary screen.
*/
if (list3 && !candidates.empty() && list3->bright) /*
{ * Sort units in the workshop 'q'uery 'P'rofile modification screen.
if (!L) return true; */
PARSE_SPEC("units", parameters); DEFINE_SORT_HANDLER(unit_sorters, layer_workshop_profile, "/Unit", profile)
{
auto list1 = getLayerList(profile, 0);
if (compute_order(*pout, L, top, &order, candidates)) PARSE_SPEC("units", parameters);
{
reorder_cursor(&list3->cursor, order);
reorder_vector(&candidates, order);
}
return true; if (compute_order(*pout, L, top, &order, profile->workers))
} {
reorder_cursor(&list1->cursor, order);
reorder_vector(&profile->workers, order);
}
}
return false; DEFINE_SORT_HANDLER(unit_sorters, layer_noblelist, "/Appoint", nobles)
} {
auto list2 = getLayerList(nobles, 1);
default: sort_null_first(parameters);
return false; PARSE_SPEC("units", parameters);
}
}
else if (auto profile = strict_virtual_cast<df::viewscreen_layer_workshop_profilest>(screen))
{
auto list1 = getLayerList(profile, 0);
if (!list1) return false; std::vector<df::unit*> units;
if (!L) return true; for (size_t i = 0; i < nobles->candidates.size(); i++)
units.push_back(nobles->candidates[i]->unit);
/* if (compute_order(*pout, L, top, &order, units))
* Sort units in the workshop 'q'uery 'P'rofile modification screen. {
*/ reorder_cursor(&list2->cursor, order);
reorder_vector(&nobles->candidates, order);
}
}
PARSE_SPEC("units", parameters); /*
* Sort animal units in the Animal page of the 'z' status screen.
*/
if (compute_order(*pout, L, top, &order, profile->workers)) DEFINE_SORT_HANDLER(unit_sorters, pet, "/List", animals)
{ {
reorder_cursor(&list1->cursor, order); PARSE_SPEC("units", parameters);
reorder_vector(&profile->workers, order);
}
return true; std::vector<df::unit*> units;
} for (size_t i = 0; i < animals->animal.size(); i++)
else if (auto nobles = strict_virtual_cast<df::viewscreen_layer_noblelistst>(screen)) units.push_back(animals->is_vermin[i] ? NULL : (df::unit*)animals->animal[i]);
{
switch (nobles->mode)
{
case df::viewscreen_layer_noblelistst::Appoint:
{
auto list2 = getLayerList(nobles, 1);
/* if (compute_order(*pout, L, top, &order, units))
* Sort units in the appointment candidate list of the 'n'obles screen. {
*/ reorder_cursor(&animals->cursor, order);
reorder_vector(&animals->animal, order);
reorder_vector(&animals->is_vermin, order);
reorder_vector(&animals->pet_info, order);
reorder_vector(&animals->is_tame, order);
reorder_vector(&animals->is_adopting, order);
}
}
if (list2) /*
{ * Sort candidate trainers in the Animal page of the 'z' status screen.
if (!L) return true; */
sort_null_first(parameters); DEFINE_SORT_HANDLER(unit_sorters, pet, "/SelectTrainer", animals)
PARSE_SPEC("units", parameters); {
sort_null_first(parameters);
PARSE_SPEC("units", parameters);
std::vector<df::unit*> units; if (compute_order(*pout, L, top, &order, animals->trainer_unit))
for (size_t i = 0; i < nobles->candidates.size(); i++) {
units.push_back(nobles->candidates[i]->unit); reorder_cursor(&animals->trainer_cursor, order);
reorder_vector(&animals->trainer_unit, order);
reorder_vector(&animals->trainer_mode, order);
}
}
if (compute_order(*pout, L, top, &order, units)) /*
{ * Sort units in the Health page of the 'z' status screen.
reorder_cursor(&list2->cursor, order); */
reorder_vector(&nobles->candidates, order);
}
return true; DEFINE_SORT_HANDLER(unit_sorters, layer_overall_health, "/Units", health)
} {
auto list1 = getLayerList(health, 0);
return false; PARSE_SPEC("units", parameters);
}
default: if (compute_order(*pout, L, top, &order, health->unit))
return false;
}
}
else if (auto animals = strict_virtual_cast<df::viewscreen_petst>(screen))
{ {
switch (animals->mode) reorder_cursor(&list1->cursor, order);
{ reorder_vector(&health->unit, order);
case df::viewscreen_petst::List: reorder_vector(&health->bits1, order);
{ reorder_vector(&health->bits2, order);
if (!L) return true; reorder_vector(&health->bits3, order);
/*
* Sort animal units in the Animal page of the 'z' status screen.
*/
PARSE_SPEC("units", parameters);
std::vector<df::unit*> units;
for (size_t i = 0; i < animals->animal.size(); i++)
units.push_back(animals->is_vermin[i] ? NULL : (df::unit*)animals->animal[i]);
if (compute_order(*pout, L, top, &order, units))
{
reorder_cursor(&animals->cursor, order);
reorder_vector(&animals->animal, order);
reorder_vector(&animals->is_vermin, order);
reorder_vector(&animals->pet_info, order);
reorder_vector(&animals->is_tame, order);
reorder_vector(&animals->is_adopting, order);
}
return true;
}
case df::viewscreen_petst::SelectTrainer:
{
if (!L) return true;
/*
* Sort candidate trainers in the Animal page of the 'z' status screen.
*/
sort_null_first(parameters);
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, animals->trainer_unit))
{
reorder_cursor(&animals->trainer_cursor, order);
reorder_vector(&animals->trainer_unit, order);
reorder_vector(&animals->trainer_mode, order);
}
return true;
}
default:
return false;
}
} }
else if (auto health = strict_virtual_cast<df::viewscreen_layer_overall_healthst>(screen)) }
{
auto list1 = getLayerList(health, 0); /*
* Sort burrow member candidate units in the 'w' sidebar mode.
*/
DEFINE_SORT_HANDLER(unit_sorters, dwarfmode, "/Burrows/AddUnits", screen)
{
PARSE_SPEC("units", parameters);
if (!list1) return false; if (compute_order(*pout, L, top, &order, ui->burrows.list_units))
if (!L) return true; {
reorder_cursor(&ui->burrows.unit_cursor_pos, order);
reorder_vector(&ui->burrows.list_units, order);
reorder_vector(&ui->burrows.sel_units, order);
}
}
/* /*
* Sort units in the Health page of the 'z' status screen. * Sort building owner candidate units in the 'q' sidebar mode, or cage assignment.
*/ */
PARSE_SPEC("units", parameters); DEFINE_SORT_HANDLER(unit_sorters, dwarfmode, "/QueryBuilding/Some/Assign", screen)
{
sort_null_first(parameters);
if (compute_order(*pout, L, top, &order, health->unit)) PARSE_SPEC("units", parameters);
{
reorder_cursor(&list1->cursor, order);
reorder_vector(&health->unit, order);
reorder_vector(&health->bits1, order);
reorder_vector(&health->bits2, order);
reorder_vector(&health->bits3, order);
}
return true; if (compute_order(*pout, L, top, &order, *ui_building_assign_units))
}
else if (strict_virtual_cast<df::viewscreen_dwarfmodest>(screen))
{ {
switch (ui->main.mode) reorder_cursor(ui_building_item_cursor, order);
{ reorder_vector(ui_building_assign_type, order);
case ui_sidebar_mode::Burrows: reorder_vector(ui_building_assign_units, order);
if (!L) return true; }
}
/*
* Sort burrow member candidate units in the 'w' sidebar mode.
*/
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, ui->burrows.list_units))
{
reorder_cursor(&ui->burrows.unit_cursor_pos, order);
reorder_vector(&ui->burrows.list_units, order);
reorder_vector(&ui->burrows.sel_units, order);
}
return true;
case ui_sidebar_mode::QueryBuilding:
if (!ui_building_in_assign || !*ui_building_in_assign)
return false;
// fall through for building owner / chain assign animal
case ui_sidebar_mode::ZonesPenInfo:
if (ui_building_item_cursor &&
ui_building_assign_type &&
ui_building_assign_is_marked &&
ui_building_assign_units &&
ui_building_assign_items &&
ui_building_assign_type->size() == ui_building_assign_units->size() &&
!ui_building_assign_type->empty())
{
if (!L) return true;
/*
* Sort building owner candidate units in the 'q' sidebar mode,
* or pen assignment candidate units in 'z'->'N', or cage assignment.
*/
// TODO: better way
bool is_assign_owner = ((*ui_building_assign_type)[0] == -1);
if (is_assign_owner)
sort_null_first(parameters);
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, *ui_building_assign_units))
{
reorder_cursor(ui_building_item_cursor, order);
reorder_vector(ui_building_assign_type, order);
reorder_vector(ui_building_assign_units, order);
if (ui_building_assign_units->size() == ui_building_assign_items->size())
reorder_vector(ui_building_assign_items, order);
if (ui_building_assign_units->size() == ui_building_assign_is_marked->size())
reorder_vector(ui_building_assign_is_marked, order);
}
return true;
}
return false;
default: /*
return false; * Sort pen assignment candidate units in 'z'->'N'.
} */
DEFINE_SORT_HANDLER(unit_sorters, dwarfmode, "/ZonesPenInfo/Assign", screen)
{
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, *ui_building_assign_units))
{
reorder_cursor(ui_building_item_cursor, order);
reorder_vector(ui_building_assign_type, order);
reorder_vector(ui_building_assign_units, order);
reorder_vector(ui_building_assign_items, order);
reorder_vector(ui_building_assign_is_marked, order);
} }
else
return false;
} }
static bool unit_list_hotkey(df::viewscreen *screen) static bool unit_list_hotkey(df::viewscreen *screen)
{ {
vector<string> dummy; auto focus = Gui::getFocusString(screen);
return maybe_sort_units(NULL, NULL, screen, dummy); return findPrefixInMap(unit_sorters, focus) != NULL;
} }
static command_result sort_units(color_ostream &out, vector <string> &parameters) static command_result sort_units(color_ostream &out, vector <string> &parameters)
@ -544,89 +461,75 @@ static command_result sort_units(color_ostream &out, vector <string> &parameters
auto L = Lua::Core::State; auto L = Lua::Core::State;
auto screen = Core::getInstance().getTopViewscreen(); auto screen = Core::getInstance().getTopViewscreen();
if (!maybe_sort_units(&out, L, screen, parameters)) Lua::StackUnwinder top(L);
if (!prepare_sort(&out, L))
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
auto focus = Gui::getFocusString(screen);
auto handler = findPrefixInMap(unit_sorters, focus);
if (!handler)
return CR_WRONG_USAGE;
else
handler(&out, L, top, screen, parameters);
return CR_OK; return CR_OK;
} }
static bool maybe_sort_items(color_ostream *pout, lua_State *L, static std::map<std::string, SortHandler> item_sorters;
df::viewscreen *screen, vector<string> &parameters)
{
Lua::StackUnwinder top(L);
if (!prepare_sort(pout, L))
return false;
std::vector<unsigned> order; DEFINE_SORT_HANDLER(item_sorters, tradegoods, "/Items/Broker", trade)
{
PARSE_SPEC("items", parameters);
if (auto trade = strict_virtual_cast<df::viewscreen_tradegoodsst>(screen)) if (compute_order(*pout, L, top, &order, trade->broker_items))
{ {
if (!L) return true; reorder_cursor(&trade->broker_cursor, order);
reorder_vector(&trade->broker_items, order);
PARSE_SPEC("items", parameters); reorder_vector(&trade->broker_selected, order);
reorder_vector(&trade->broker_count, order);
if (trade->in_right_pane)
{
if (compute_order(*pout, L, top, &order, trade->broker_items))
{
reorder_cursor(&trade->broker_cursor, order);
reorder_vector(&trade->broker_items, order);
reorder_vector(&trade->broker_selected, order);
reorder_vector(&trade->broker_count, order);
}
}
else
{
if (compute_order(*pout, L, top, &order, trade->trader_items))
{
reorder_cursor(&trade->trader_cursor, order);
reorder_vector(&trade->trader_items, order);
reorder_vector(&trade->trader_selected, order);
reorder_vector(&trade->trader_count, order);
}
}
return true;
} }
else if (auto bring = strict_virtual_cast<df::viewscreen_layer_assigntradest>(screen)) }
{
auto list1 = getLayerList(bring, 0);
auto list2 = getLayerList(bring, 1);
if (!list1 || !list2 || !list2->bright)
return false;
int list_idx = vector_get(bring->visible_lists, list1->cursor, (int16_t)-1); DEFINE_SORT_HANDLER(item_sorters, tradegoods, "/Items/Trader", trade)
unsigned num_lists = sizeof(bring->lists)/sizeof(std::vector<int32_t>); {
if (unsigned(list_idx) >= num_lists) PARSE_SPEC("items", parameters);
return false;
if (!L) return true; if (compute_order(*pout, L, top, &order, trade->trader_items))
{
reorder_cursor(&trade->trader_cursor, order);
reorder_vector(&trade->trader_items, order);
reorder_vector(&trade->trader_selected, order);
reorder_vector(&trade->trader_count, order);
}
}
PARSE_SPEC("items", parameters); DEFINE_SORT_HANDLER(item_sorters, layer_assigntrade, "/Items", bring)
{
auto list1 = getLayerList(bring, 0);
auto list2 = getLayerList(bring, 1);
int list_idx = vector_get(bring->visible_lists, list1->cursor, (int16_t)-1);
auto &vec = bring->lists[list_idx]; PARSE_SPEC("items", parameters);
std::vector<df::item*> items; auto &vec = bring->lists[list_idx];
for (size_t i = 0; i < vec.size(); i++)
items.push_back(bring->info[vec[i]]->item);
if (compute_order(*pout, L, top, &order, items)) std::vector<df::item*> items;
{ for (size_t i = 0; i < vec.size(); i++)
reorder_cursor(&list2->cursor, order); items.push_back(bring->info[vec[i]]->item);
reorder_vector(&vec, order);
}
return true; if (compute_order(*pout, L, top, &order, items))
{
reorder_cursor(&list2->cursor, order);
reorder_vector(&vec, order);
} }
else
return false;
} }
static bool item_list_hotkey(df::viewscreen *screen) static bool item_list_hotkey(df::viewscreen *screen)
{ {
vector<string> dummy; auto focus = Gui::getFocusString(screen);
return maybe_sort_items(NULL, NULL, screen, dummy); return findPrefixInMap(item_sorters, focus) != NULL;
} }
static command_result sort_items(color_ostream &out, vector <string> &parameters) static command_result sort_items(color_ostream &out, vector <string> &parameters)
@ -637,8 +540,18 @@ static command_result sort_items(color_ostream &out, vector <string> &parameters
auto L = Lua::Core::State; auto L = Lua::Core::State;
auto screen = Core::getInstance().getTopViewscreen(); auto screen = Core::getInstance().getTopViewscreen();
if (!maybe_sort_items(&out, L, screen, parameters)) Lua::StackUnwinder top(L);
if (!prepare_sort(&out, L))
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
auto focus = Gui::getFocusString(screen);
auto handler = findPrefixInMap(item_sorters, focus);
if (!handler)
return CR_WRONG_USAGE;
else
handler(&out, L, top, screen, parameters);
return CR_OK; return CR_OK;
} }