remove confirm plugin (replaced by script)
							parent
							
								
									fa0732d651
								
							
						
					
					
						commit
						ac91e2096f
					
				@ -1,21 +0,0 @@
 | 
			
		||||
confirm
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
.. dfhack-tool::
 | 
			
		||||
    :summary: Adds confirmation dialogs for destructive actions.
 | 
			
		||||
    :tags: fort interface
 | 
			
		||||
 | 
			
		||||
In the base game, it is frightenly easy to destroy hours of work with a single
 | 
			
		||||
misclick. Now you can avoid the consequences of accidentally disbanding a squad
 | 
			
		||||
(for example), or deleting a hauling route.
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
``enable confirm``, ``confirm enable all``
 | 
			
		||||
    Enable all confirmation options. Replace with ``disable`` to disable all.
 | 
			
		||||
``confirm enable option1 [option2...]``
 | 
			
		||||
    Enable (or ``disable``) specific confirmation dialogs.
 | 
			
		||||
 | 
			
		||||
When run without parameters, ``confirm`` will report which confirmation dialogs
 | 
			
		||||
are currently enabled.
 | 
			
		||||
@ -1,728 +0,0 @@
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <queue>
 | 
			
		||||
 | 
			
		||||
#include "Console.h"
 | 
			
		||||
#include "Core.h"
 | 
			
		||||
#include "DataDefs.h"
 | 
			
		||||
#include "Debug.h"
 | 
			
		||||
#include "Error.h"
 | 
			
		||||
#include "Export.h"
 | 
			
		||||
#include "LuaTools.h"
 | 
			
		||||
#include "LuaWrapper.h"
 | 
			
		||||
#include "PluginManager.h"
 | 
			
		||||
#include "VTableInterpose.h"
 | 
			
		||||
#include "modules/Gui.h"
 | 
			
		||||
#include "uicommon.h"
 | 
			
		||||
 | 
			
		||||
#include "df/gamest.h"
 | 
			
		||||
#include "df/general_ref.h"
 | 
			
		||||
#include "df/general_ref_contained_in_itemst.h"
 | 
			
		||||
#include "df/interfacest.h"
 | 
			
		||||
#include "df/viewscreen_dwarfmodest.h"
 | 
			
		||||
 | 
			
		||||
using namespace DFHack;
 | 
			
		||||
using namespace df::enums;
 | 
			
		||||
using std::map;
 | 
			
		||||
using std::queue;
 | 
			
		||||
using std::string;
 | 
			
		||||
using std::vector;
 | 
			
		||||
 | 
			
		||||
DFHACK_PLUGIN("confirm");
 | 
			
		||||
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
 | 
			
		||||
REQUIRE_GLOBAL(game);
 | 
			
		||||
REQUIRE_GLOBAL(gps);
 | 
			
		||||
 | 
			
		||||
typedef std::set<df::interface_key> ikey_set;
 | 
			
		||||
command_result df_confirm (color_ostream &out, vector <string> & parameters);
 | 
			
		||||
 | 
			
		||||
struct conf_wrapper;
 | 
			
		||||
static map<string, conf_wrapper*> confirmations;
 | 
			
		||||
string active_id;
 | 
			
		||||
queue<string> cmds;
 | 
			
		||||
 | 
			
		||||
namespace DFHack {
 | 
			
		||||
    DBG_DECLARE(confirm,status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename VT, typename FT>
 | 
			
		||||
inline bool in_vector (std::vector<VT> &vec, FT item)
 | 
			
		||||
{
 | 
			
		||||
    return std::find(vec.begin(), vec.end(), item) != vec.end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
string char_replace (string s, char a, char b)
 | 
			
		||||
{
 | 
			
		||||
    string res = s;
 | 
			
		||||
    size_t i = res.size();
 | 
			
		||||
    while (i--)
 | 
			
		||||
        if (res[i] == a)
 | 
			
		||||
            res[i] = b;
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool set_conf_state (string name, bool state);
 | 
			
		||||
bool set_conf_paused (string name, bool pause);
 | 
			
		||||
 | 
			
		||||
class confirmation_base {
 | 
			
		||||
public:
 | 
			
		||||
    bool dirty = false;
 | 
			
		||||
    enum cstate { INACTIVE, ACTIVE, SELECTED };
 | 
			
		||||
    virtual string get_id() = 0;
 | 
			
		||||
    virtual string get_focus_string() = 0;
 | 
			
		||||
    virtual bool set_state(cstate) = 0;
 | 
			
		||||
 | 
			
		||||
    static bool set_state(string id, cstate state)
 | 
			
		||||
    {
 | 
			
		||||
        if (active && active->get_id() == id)
 | 
			
		||||
        {
 | 
			
		||||
            active->set_state(state);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
protected:
 | 
			
		||||
    static confirmation_base *active;
 | 
			
		||||
};
 | 
			
		||||
confirmation_base *confirmation_base::active = nullptr;
 | 
			
		||||
 | 
			
		||||
struct conf_wrapper {
 | 
			
		||||
private:
 | 
			
		||||
    bool enabled;
 | 
			
		||||
    bool paused;
 | 
			
		||||
    std::set<VMethodInterposeLinkBase*> hooks;
 | 
			
		||||
public:
 | 
			
		||||
    conf_wrapper()
 | 
			
		||||
        :enabled(false),
 | 
			
		||||
        paused(false)
 | 
			
		||||
    {}
 | 
			
		||||
    void add_hook(VMethodInterposeLinkBase *hook)
 | 
			
		||||
    {
 | 
			
		||||
        if (!hooks.count(hook))
 | 
			
		||||
            hooks.insert(hook);
 | 
			
		||||
    }
 | 
			
		||||
    bool apply (bool state) {
 | 
			
		||||
        if (state == enabled)
 | 
			
		||||
            return true;
 | 
			
		||||
        for (auto hook : hooks)
 | 
			
		||||
        {
 | 
			
		||||
            if (!hook->apply(state))
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
        enabled = state;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    bool set_paused (bool pause) {
 | 
			
		||||
        paused = pause;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    inline bool is_enabled() { return enabled; }
 | 
			
		||||
    inline bool is_paused() { return paused; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace trade {
 | 
			
		||||
    static bool goods_selected (std::vector<uint8_t> &selected)
 | 
			
		||||
    {
 | 
			
		||||
        if(!game->main_interface.trade.open)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        for (uint8_t sel : selected)
 | 
			
		||||
            if (sel == 1)
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    inline bool trader_goods_selected ()
 | 
			
		||||
    {
 | 
			
		||||
        return goods_selected(game->main_interface.trade.goodflag[0]);
 | 
			
		||||
    }
 | 
			
		||||
    inline bool broker_goods_selected ()
 | 
			
		||||
    {
 | 
			
		||||
        return goods_selected(game->main_interface.trade.goodflag[1]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*static bool goods_all_selected(const std::vector<char>& selected, const std::vector<df::item*>& items)  \
 | 
			
		||||
    {
 | 
			
		||||
        for (size_t i = 0; i < selected.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            if (!selected[i])
 | 
			
		||||
            {
 | 
			
		||||
                // check to see if item is in a container
 | 
			
		||||
                // (if the container is not selected, it will be detected separately)
 | 
			
		||||
                bool in_container = false;
 | 
			
		||||
                for (auto ref : items[i]->general_refs)
 | 
			
		||||
                {
 | 
			
		||||
                    if (virtual_cast<df::general_ref_contained_in_itemst>(ref))
 | 
			
		||||
                    {
 | 
			
		||||
                        in_container = true;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (!in_container)
 | 
			
		||||
                    return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    inline bool trader_goods_all_selected()
 | 
			
		||||
    {
 | 
			
		||||
        return goods_all_selected(screen->trader_selected, screen->trader_items);
 | 
			
		||||
    }
 | 
			
		||||
    inline bool broker_goods_all_selected()
 | 
			
		||||
    {
 | 
			
		||||
        return goods_all_selected(screen->broker_selected, screen->broker_items);
 | 
			
		||||
    }*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace conf_lua {
 | 
			
		||||
    static color_ostream_proxy *out;
 | 
			
		||||
    static lua_State *l_state;
 | 
			
		||||
    bool init (color_ostream &dfout)
 | 
			
		||||
    {
 | 
			
		||||
        out = new color_ostream_proxy(Core::getInstance().getConsole());
 | 
			
		||||
        l_state = Lua::Open(*out);
 | 
			
		||||
        return l_state;
 | 
			
		||||
    }
 | 
			
		||||
    void cleanup()
 | 
			
		||||
    {
 | 
			
		||||
        if (out)
 | 
			
		||||
        {
 | 
			
		||||
            delete out;
 | 
			
		||||
            out = nullptr;
 | 
			
		||||
        }
 | 
			
		||||
        lua_close(l_state);
 | 
			
		||||
    }
 | 
			
		||||
    bool call (const char *func, int nargs = 0, int nres = 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (!Lua::PushModulePublic(*out, l_state, "plugins.confirm", func))
 | 
			
		||||
            return false;
 | 
			
		||||
        if (nargs > 0)
 | 
			
		||||
            lua_insert(l_state, lua_gettop(l_state) - nargs);
 | 
			
		||||
        return Lua::SafeCall(*out, l_state, nargs, nres);
 | 
			
		||||
    }
 | 
			
		||||
    bool simple_call (const char *func)
 | 
			
		||||
    {
 | 
			
		||||
        Lua::StackUnwinder top(l_state);
 | 
			
		||||
        return call(func, 0, 0);
 | 
			
		||||
    }
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    void push (T val)
 | 
			
		||||
    {
 | 
			
		||||
        Lua::Push(l_state, val);
 | 
			
		||||
    }
 | 
			
		||||
    namespace api {
 | 
			
		||||
        int get_ids (lua_State *L)
 | 
			
		||||
        {
 | 
			
		||||
            lua_newtable(L);
 | 
			
		||||
            for (auto item : confirmations)
 | 
			
		||||
                Lua::TableInsert(L, item.first, true);
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        int get_conf_data (lua_State *L)
 | 
			
		||||
        {
 | 
			
		||||
            lua_newtable(L);
 | 
			
		||||
            int i = 1;
 | 
			
		||||
            for (auto item : confirmations)
 | 
			
		||||
            {
 | 
			
		||||
                Lua::Push(L, i++);
 | 
			
		||||
                lua_newtable(L);
 | 
			
		||||
                Lua::TableInsert(L, "id", item.first);
 | 
			
		||||
                Lua::TableInsert(L, "enabled", item.second->is_enabled());
 | 
			
		||||
                Lua::TableInsert(L, "paused", item.second->is_paused());
 | 
			
		||||
                lua_settable(L, -3);
 | 
			
		||||
            }
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        int get_active_id (lua_State *L)
 | 
			
		||||
        {
 | 
			
		||||
            if (active_id.size())
 | 
			
		||||
                Lua::Push(L, active_id);
 | 
			
		||||
            else
 | 
			
		||||
                lua_pushnil(L);
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CONF_LUA_FUNC(ns, name) {#name, df::wrap_function(ns::name, true)}
 | 
			
		||||
DFHACK_PLUGIN_LUA_FUNCTIONS {
 | 
			
		||||
    CONF_LUA_FUNC( , set_conf_state),
 | 
			
		||||
    CONF_LUA_FUNC( , set_conf_paused),
 | 
			
		||||
    CONF_LUA_FUNC(trade, broker_goods_selected),
 | 
			
		||||
    //CONF_LUA_FUNC(trade, broker_goods_all_selected),
 | 
			
		||||
    CONF_LUA_FUNC(trade, trader_goods_selected),
 | 
			
		||||
    //CONF_LUA_FUNC(trade, trader_goods_all_selected),
 | 
			
		||||
    DFHACK_LUA_END
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define CONF_LUA_CMD(name) {#name, conf_lua::api::name}
 | 
			
		||||
DFHACK_PLUGIN_LUA_COMMANDS {
 | 
			
		||||
    CONF_LUA_CMD(get_ids),
 | 
			
		||||
    CONF_LUA_CMD(get_conf_data),
 | 
			
		||||
    CONF_LUA_CMD(get_active_id),
 | 
			
		||||
    DFHACK_LUA_END
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void show_options()
 | 
			
		||||
{
 | 
			
		||||
    cmds.push("gui/confirm");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <class T>
 | 
			
		||||
class confirmation : public confirmation_base {
 | 
			
		||||
public:
 | 
			
		||||
    typedef T screen_type;
 | 
			
		||||
    screen_type *screen;
 | 
			
		||||
 | 
			
		||||
    bool set_state (cstate s) override
 | 
			
		||||
    {
 | 
			
		||||
        if (confirmation_base::active && confirmation_base::active != this)
 | 
			
		||||
        {
 | 
			
		||||
            // Stop this confirmation from appearing over another one
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        state = s;
 | 
			
		||||
        dirty = true;
 | 
			
		||||
        if (s == INACTIVE) {
 | 
			
		||||
            active_id = "";
 | 
			
		||||
            confirmation_base::active = nullptr;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            active_id = get_id();
 | 
			
		||||
            confirmation_base::active = this;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    bool feed (ikey_set *input) {
 | 
			
		||||
        bool mouseExit = false;
 | 
			
		||||
        if(df::global::enabler->mouse_rbut) {
 | 
			
		||||
            mouseExit = true;
 | 
			
		||||
        }
 | 
			
		||||
        bool mouseSelect = false;
 | 
			
		||||
        if(df::global::enabler->mouse_lbut) {
 | 
			
		||||
            mouseSelect = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        conf_wrapper *wrapper = confirmations[this->get_id()];
 | 
			
		||||
        if(wrapper->is_paused()) {
 | 
			
		||||
            std::string concernedFocus = this->get_focus_string();
 | 
			
		||||
            if(!Gui::matchFocusString(this->get_focus_string()))
 | 
			
		||||
                wrapper->set_paused(false);
 | 
			
		||||
            return false;
 | 
			
		||||
        } else if (state == INACTIVE)
 | 
			
		||||
        {
 | 
			
		||||
            if(mouseExit) {
 | 
			
		||||
                if(intercept_key("MOUSE_RIGHT") && set_state(ACTIVE)) {
 | 
			
		||||
                    df::global::enabler->mouse_rbut = 0;
 | 
			
		||||
                    df::global::enabler->mouse_rbut_down = 0;
 | 
			
		||||
                    mouse_pos = df::coord2d(df::global::gps->mouse_x, df::global::gps->mouse_y);
 | 
			
		||||
                    last_key_is_right_click = true;
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            } else
 | 
			
		||||
                last_key_is_right_click = false;
 | 
			
		||||
 | 
			
		||||
            if(mouseSelect) {
 | 
			
		||||
                if(intercept_key("MOUSE_LEFT") && set_state(ACTIVE)) {
 | 
			
		||||
                    df::global::enabler->mouse_lbut = 0;
 | 
			
		||||
                    df::global::enabler->mouse_lbut_down = 0;
 | 
			
		||||
                    mouse_pos = df::coord2d(df::global::gps->mouse_x, df::global::gps->mouse_y);
 | 
			
		||||
                    last_key_is_left_click = true;
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            } else
 | 
			
		||||
                last_key_is_left_click = false;
 | 
			
		||||
 | 
			
		||||
            for (df::interface_key key : *input)
 | 
			
		||||
            {
 | 
			
		||||
                if (intercept_key(key) && set_state(ACTIVE))
 | 
			
		||||
                {
 | 
			
		||||
                    last_key = key;
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        else if (state == ACTIVE)
 | 
			
		||||
        {
 | 
			
		||||
            if (input->count(df::interface_key::LEAVESCREEN) || mouseExit) {
 | 
			
		||||
                if(mouseExit) {
 | 
			
		||||
                    df::global::enabler->mouse_rbut = 0;
 | 
			
		||||
                    df::global::enabler->mouse_rbut_down = 0;
 | 
			
		||||
                }
 | 
			
		||||
                set_state(INACTIVE);
 | 
			
		||||
            } else if (input->count(df::interface_key::SELECT))
 | 
			
		||||
                set_state(SELECTED);
 | 
			
		||||
            else if (input->count(df::interface_key::CUSTOM_P))
 | 
			
		||||
            {
 | 
			
		||||
                DEBUG(status).print("pausing\n");
 | 
			
		||||
 | 
			
		||||
                wrapper->set_paused(true);
 | 
			
		||||
                set_state(INACTIVE);
 | 
			
		||||
            }
 | 
			
		||||
            else if (input->count(df::interface_key::CUSTOM_S))
 | 
			
		||||
                show_options();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    bool key_conflict (df::interface_key key)
 | 
			
		||||
    {
 | 
			
		||||
        if (key == df::interface_key::SELECT || key == df::interface_key::LEAVESCREEN)
 | 
			
		||||
            return false;
 | 
			
		||||
        return state == ACTIVE;
 | 
			
		||||
    }
 | 
			
		||||
    void render() {
 | 
			
		||||
        if (state == ACTIVE)
 | 
			
		||||
        {
 | 
			
		||||
            static vector<string> lines;
 | 
			
		||||
            static const std::string pause_message =
 | 
			
		||||
                   "Pause confirmations until you exit this screen";
 | 
			
		||||
            Screen::Pen corner_ul = Screen::Pen((char)201, COLOR_GREY, COLOR_BLACK);
 | 
			
		||||
            Screen::Pen corner_ur = Screen::Pen((char)187, COLOR_GREY, COLOR_BLACK);
 | 
			
		||||
            Screen::Pen corner_dl = Screen::Pen((char)200, COLOR_GREY, COLOR_BLACK);
 | 
			
		||||
            Screen::Pen corner_dr = Screen::Pen((char)188, COLOR_GREY, COLOR_BLACK);
 | 
			
		||||
            Screen::Pen border_ud = Screen::Pen((char)205, COLOR_GREY, COLOR_BLACK);
 | 
			
		||||
            Screen::Pen border_lr = Screen::Pen((char)186, COLOR_GREY, COLOR_BLACK);
 | 
			
		||||
 | 
			
		||||
            split_string(&lines, get_message(), "\n");
 | 
			
		||||
            size_t max_length = 40;
 | 
			
		||||
            for (string line : lines)
 | 
			
		||||
                max_length = std::max(max_length, line.size());
 | 
			
		||||
            int width = max_length + 4;
 | 
			
		||||
            vector<string> pause_message_lines;
 | 
			
		||||
            word_wrap(&pause_message_lines, pause_message, max_length - 3);
 | 
			
		||||
            int height = lines.size() + pause_message_lines.size() + 5;
 | 
			
		||||
            int x1 = (gps->dimx / 2) - (width / 2);
 | 
			
		||||
            int x2 = x1 + width - 1;
 | 
			
		||||
            int y1 = (gps->dimy / 2) - (height / 2);
 | 
			
		||||
            int y2 = y1 + height - 1;
 | 
			
		||||
            for (int x = x1; x <= x2; x++)
 | 
			
		||||
            {
 | 
			
		||||
                Screen::paintTile(border_ud, x, y1);
 | 
			
		||||
                Screen::paintTile(border_ud, x, y2);
 | 
			
		||||
            }
 | 
			
		||||
            for (int y = y1; y <= y2; y++)
 | 
			
		||||
            {
 | 
			
		||||
                Screen::paintTile(border_lr, x1, y);
 | 
			
		||||
                Screen::paintTile(border_lr, x2, y);
 | 
			
		||||
            }
 | 
			
		||||
            Screen::paintTile(corner_ul, x1, y1);
 | 
			
		||||
            Screen::paintTile(corner_ur, x2, y1);
 | 
			
		||||
            Screen::paintTile(corner_dl, x1, y2);
 | 
			
		||||
            Screen::paintTile(corner_dr, x2, y2);
 | 
			
		||||
            string title = ' ' + get_title() + ' ';
 | 
			
		||||
            Screen::paintString(Screen::Pen(' ', COLOR_DARKGREY, COLOR_BLACK),
 | 
			
		||||
                x2 - 6, y1, "DFHack");
 | 
			
		||||
            Screen::paintString(Screen::Pen(' ', COLOR_BLACK, COLOR_GREY),
 | 
			
		||||
                (gps->dimx / 2) - (title.size() / 2), y1, title);
 | 
			
		||||
            int x = x1 + 2;
 | 
			
		||||
            int y = y2;
 | 
			
		||||
            OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::LEAVESCREEN));
 | 
			
		||||
            OutputString(COLOR_WHITE, x, y, ": Cancel");
 | 
			
		||||
            x = (gps->dimx - (Screen::getKeyDisplay(df::interface_key::CUSTOM_S) + ": Settings").size()) / 2 + 1;
 | 
			
		||||
            OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S));
 | 
			
		||||
            OutputString(COLOR_WHITE, x, y, ": Settings");
 | 
			
		||||
            x = x2 - 2 - 3 - Screen::getKeyDisplay(df::interface_key::SELECT).size();
 | 
			
		||||
            OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::SELECT));
 | 
			
		||||
            OutputString(COLOR_WHITE, x, y, ": Ok");
 | 
			
		||||
            Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), x1 + 1, y1 + 1, x2 - 1, y2 - 1);
 | 
			
		||||
            for (size_t i = 0; i < lines.size(); i++)
 | 
			
		||||
            {
 | 
			
		||||
                Screen::paintString(Screen::Pen(' ', get_color(), COLOR_BLACK), x1 + 2, y1 + 2 + i, lines[i]);
 | 
			
		||||
            }
 | 
			
		||||
            y = y1 + 3 + lines.size();
 | 
			
		||||
            for (size_t i = 0; i < pause_message_lines.size(); i++)
 | 
			
		||||
            {
 | 
			
		||||
                Screen::paintString(Screen::Pen(' ', COLOR_WHITE, COLOR_BLACK), x1 + 5, y + i, pause_message_lines[i]);
 | 
			
		||||
            }
 | 
			
		||||
            x = x1 + 2;
 | 
			
		||||
            OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_P));
 | 
			
		||||
            OutputString(COLOR_WHITE, x, y, ":");
 | 
			
		||||
        }
 | 
			
		||||
        else if (state == SELECTED)
 | 
			
		||||
        {
 | 
			
		||||
            ikey_set tmp;
 | 
			
		||||
            if(last_key_is_left_click) {
 | 
			
		||||
                long prevx = df::global::gps->mouse_x;
 | 
			
		||||
                long prevy = df::global::gps->mouse_y;
 | 
			
		||||
                df::global::gps->mouse_x = mouse_pos.x;
 | 
			
		||||
                df::global::gps->mouse_y = mouse_pos.y;
 | 
			
		||||
                df::global::enabler->mouse_lbut = 1;
 | 
			
		||||
                df::global::enabler->mouse_lbut_down = 1;
 | 
			
		||||
                screen->feed(&tmp);
 | 
			
		||||
                df::global::enabler->mouse_lbut = 0;
 | 
			
		||||
                df::global::enabler->mouse_lbut_down = 0;
 | 
			
		||||
                df::global::gps->mouse_x = prevx;
 | 
			
		||||
                df::global::gps->mouse_y = prevy;
 | 
			
		||||
            }
 | 
			
		||||
            else if(last_key_is_right_click) {
 | 
			
		||||
                tmp.insert(df::interface_key::LEAVESCREEN);
 | 
			
		||||
                screen->feed(&tmp);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                tmp.insert(last_key);
 | 
			
		||||
                screen->feed(&tmp);
 | 
			
		||||
            }
 | 
			
		||||
            set_state(INACTIVE);
 | 
			
		||||
        }
 | 
			
		||||
        if(dirty) {
 | 
			
		||||
            dirty = false;
 | 
			
		||||
            df::global::gps->force_full_display_count = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    string get_id() override = 0;
 | 
			
		||||
    string get_focus_string() override = 0;
 | 
			
		||||
    #define CONF_LUA_START using namespace conf_lua; Lua::StackUnwinder unwind(l_state); push(screen); push(get_id());
 | 
			
		||||
    bool intercept_key (df::interface_key key)
 | 
			
		||||
    {
 | 
			
		||||
        CONF_LUA_START;
 | 
			
		||||
        push(key);
 | 
			
		||||
        if (call("intercept_key", 3, 1))
 | 
			
		||||
            return lua_toboolean(l_state, -1);
 | 
			
		||||
        else
 | 
			
		||||
            return false;
 | 
			
		||||
    };
 | 
			
		||||
    bool intercept_key (std::string mouse_button = "MOUSE_LEFT")
 | 
			
		||||
    {
 | 
			
		||||
        CONF_LUA_START;
 | 
			
		||||
        push(mouse_button);
 | 
			
		||||
        if (call("intercept_key", 3, 1))
 | 
			
		||||
            return lua_toboolean(l_state, -1);
 | 
			
		||||
        else
 | 
			
		||||
            return false;
 | 
			
		||||
    };
 | 
			
		||||
    string get_title()
 | 
			
		||||
    {
 | 
			
		||||
        CONF_LUA_START;
 | 
			
		||||
        if (call("get_title", 2, 1) && lua_isstring(l_state, -1))
 | 
			
		||||
            return lua_tostring(l_state, -1);
 | 
			
		||||
        else
 | 
			
		||||
            return "Confirm";
 | 
			
		||||
    }
 | 
			
		||||
    string get_message()
 | 
			
		||||
    {
 | 
			
		||||
        CONF_LUA_START;
 | 
			
		||||
        if (call("get_message", 2, 1) && lua_isstring(l_state, -1))
 | 
			
		||||
            return lua_tostring(l_state, -1);
 | 
			
		||||
        else
 | 
			
		||||
            return "<Message generation failed>";
 | 
			
		||||
    };
 | 
			
		||||
    UIColor get_color()
 | 
			
		||||
    {
 | 
			
		||||
        CONF_LUA_START;
 | 
			
		||||
        if (call("get_color", 2, 1) && lua_isnumber(l_state, -1))
 | 
			
		||||
            return lua_tointeger(l_state, -1) % 16;
 | 
			
		||||
        else
 | 
			
		||||
            return COLOR_YELLOW;
 | 
			
		||||
    }
 | 
			
		||||
    #undef CONF_LUA_START
 | 
			
		||||
protected:
 | 
			
		||||
    cstate state;
 | 
			
		||||
    df::interface_key last_key;
 | 
			
		||||
    bool last_key_is_left_click;
 | 
			
		||||
    bool last_key_is_right_click;
 | 
			
		||||
    df::coord2d mouse_pos;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename T>
 | 
			
		||||
int conf_register(confirmation<T> *c, const vector<VMethodInterposeLinkBase*> &hooks)
 | 
			
		||||
{
 | 
			
		||||
    conf_wrapper *w = new conf_wrapper();
 | 
			
		||||
    confirmations[c->get_id()] = w;
 | 
			
		||||
    for (auto hook : hooks)
 | 
			
		||||
        w->add_hook(hook);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define IMPLEMENT_CONFIRMATION_HOOKS(cls, prio) \
 | 
			
		||||
static cls cls##_instance; \
 | 
			
		||||
struct cls##_hooks : cls::screen_type { \
 | 
			
		||||
    typedef cls::screen_type interpose_base; \
 | 
			
		||||
    DEFINE_VMETHOD_INTERPOSE(void, feed, (ikey_set *input)) \
 | 
			
		||||
    { \
 | 
			
		||||
        cls##_instance.screen = this; \
 | 
			
		||||
        if (!cls##_instance.feed(input)) \
 | 
			
		||||
            INTERPOSE_NEXT(feed)(input); \
 | 
			
		||||
    } \
 | 
			
		||||
    DEFINE_VMETHOD_INTERPOSE(void, render, ()) \
 | 
			
		||||
    { \
 | 
			
		||||
        cls##_instance.screen = this; \
 | 
			
		||||
        INTERPOSE_NEXT(render)(); \
 | 
			
		||||
        cls##_instance.render(); \
 | 
			
		||||
    } \
 | 
			
		||||
}; \
 | 
			
		||||
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, feed, prio); \
 | 
			
		||||
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, render, prio); \
 | 
			
		||||
static int conf_register_##cls = conf_register(&cls##_instance, {\
 | 
			
		||||
    &INTERPOSE_HOOK(cls##_hooks, feed), \
 | 
			
		||||
    &INTERPOSE_HOOK(cls##_hooks, render), \
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
#define DEFINE_CONFIRMATION(cls, screen, focusString) \
 | 
			
		||||
    class confirmation_##cls : public confirmation<df::screen> { \
 | 
			
		||||
        virtual string get_id() { static string id = char_replace(#cls, '_', '-'); return id; } \
 | 
			
		||||
        virtual string get_focus_string() { return focusString; } \
 | 
			
		||||
    }; \
 | 
			
		||||
    IMPLEMENT_CONFIRMATION_HOOKS(confirmation_##cls, 0);
 | 
			
		||||
 | 
			
		||||
/* This section defines stubs for all confirmation dialogs, with methods
 | 
			
		||||
    implemented in plugins/lua/confirm.lua.
 | 
			
		||||
    IDs (used in the "confirm enable/disable" command, by Lua, and in the docs)
 | 
			
		||||
    are obtained by replacing '_' with '-' in the first argument to DEFINE_CONFIRMATION
 | 
			
		||||
 | 
			
		||||
    The second argument to DEFINE_CONFIRMATION determines the viewscreen that any
 | 
			
		||||
    intercepted input will be fed to.
 | 
			
		||||
 | 
			
		||||
    The third argument to DEFINE_CONFIRMATION determines the focus string that will
 | 
			
		||||
    be used to determine if the confirmation should be unpaused. If a confirmation is paused
 | 
			
		||||
    and the focus string is no longer found in the current focus, the confirmation will be
 | 
			
		||||
    unpaused. Focus strings ending in "*" will use prefix matching e.g. "dwarfmode/Info*" would
 | 
			
		||||
    match "dwarfmode/Info/Foo", "dwarfmode/Info/Bar" and so on. All matching is case insensitive.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
DEFINE_CONFIRMATION(trade_cancel,         viewscreen_dwarfmodest, "dwarfmode/Trade");
 | 
			
		||||
DEFINE_CONFIRMATION(haul_delete_route,    viewscreen_dwarfmodest, "dwarfmode/Hauling");
 | 
			
		||||
DEFINE_CONFIRMATION(haul_delete_stop,     viewscreen_dwarfmodest, "dwarfmode/Hauling");
 | 
			
		||||
DEFINE_CONFIRMATION(depot_remove,         viewscreen_dwarfmodest, "dwarfmode/ViewSheets/BUILDING");
 | 
			
		||||
DEFINE_CONFIRMATION(squad_disband,        viewscreen_dwarfmodest, "dwarfmode/Squads");
 | 
			
		||||
DEFINE_CONFIRMATION(order_remove,         viewscreen_dwarfmodest, "dwarfmode/Info/WORK_ORDERS");
 | 
			
		||||
DEFINE_CONFIRMATION(zone_remove,          viewscreen_dwarfmodest, "dwarfmode/Zone");
 | 
			
		||||
DEFINE_CONFIRMATION(burrow_remove,        viewscreen_dwarfmodest, "dwarfmode/Burrow");
 | 
			
		||||
DEFINE_CONFIRMATION(stockpile_remove,     viewscreen_dwarfmodest, "dwarfmode/Some/Stockpile");
 | 
			
		||||
 | 
			
		||||
// these are more complex to implement
 | 
			
		||||
//DEFINE_CONFIRMATION(convict,            viewscreen_dwarfmodest);
 | 
			
		||||
//DEFINE_CONFIRMATION(trade,              viewscreen_dwarfmodest);
 | 
			
		||||
//DEFINE_CONFIRMATION(trade_seize,        viewscreen_dwarfmodest);
 | 
			
		||||
//DEFINE_CONFIRMATION(trade_offer,        viewscreen_dwarfmodest);
 | 
			
		||||
//DEFINE_CONFIRMATION(trade_select_all,   viewscreen_dwarfmodest);
 | 
			
		||||
//DEFINE_CONFIRMATION(uniform_delete,     viewscreen_dwarfmodest);
 | 
			
		||||
//DEFINE_CONFIRMATION(note_delete,        viewscreen_dwarfmodest);
 | 
			
		||||
//DEFINE_CONFIRMATION(route_delete,       viewscreen_dwarfmodest);
 | 
			
		||||
 | 
			
		||||
// locations can't be retired currently
 | 
			
		||||
//DEFINE_CONFIRMATION(location_retire,    viewscreen_locationsst);
 | 
			
		||||
 | 
			
		||||
DFhackCExport command_result plugin_init (color_ostream &out, vector <PluginCommand> &commands)
 | 
			
		||||
{
 | 
			
		||||
    if (!conf_lua::init(out))
 | 
			
		||||
        return CR_FAILURE;
 | 
			
		||||
    commands.push_back(PluginCommand(
 | 
			
		||||
        "confirm",
 | 
			
		||||
        "Add confirmation dialogs for destructive actions.",
 | 
			
		||||
        df_confirm));
 | 
			
		||||
    return CR_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
 | 
			
		||||
{
 | 
			
		||||
    is_enabled = enable;
 | 
			
		||||
    if (is_enabled)
 | 
			
		||||
    {
 | 
			
		||||
        conf_lua::simple_call("check");
 | 
			
		||||
    }
 | 
			
		||||
    return CR_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DFhackCExport command_result plugin_shutdown (color_ostream &out)
 | 
			
		||||
{
 | 
			
		||||
    if (plugin_enable(out, false) != CR_OK)
 | 
			
		||||
        return CR_FAILURE;
 | 
			
		||||
    conf_lua::cleanup();
 | 
			
		||||
 | 
			
		||||
    for (auto item : confirmations)
 | 
			
		||||
    {
 | 
			
		||||
        delete item.second;
 | 
			
		||||
    }
 | 
			
		||||
    confirmations.clear();
 | 
			
		||||
 | 
			
		||||
    return CR_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DFhackCExport command_result plugin_onupdate (color_ostream &out)
 | 
			
		||||
{
 | 
			
		||||
    while (!cmds.empty())
 | 
			
		||||
    {
 | 
			
		||||
        Core::getInstance().runCommand(out, cmds.front());
 | 
			
		||||
        cmds.pop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return CR_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool set_conf_state (string name, bool state)
 | 
			
		||||
{
 | 
			
		||||
    bool found = false;
 | 
			
		||||
    for (auto it : confirmations)
 | 
			
		||||
    {
 | 
			
		||||
        if (it.first == name)
 | 
			
		||||
        {
 | 
			
		||||
            found = true;
 | 
			
		||||
            it.second->apply(state);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (state == false)
 | 
			
		||||
    {
 | 
			
		||||
        // dismiss the confirmation too
 | 
			
		||||
        confirmation_base::set_state(name, confirmation_base::INACTIVE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return found;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool set_conf_paused (string name, bool pause)
 | 
			
		||||
{
 | 
			
		||||
    bool found = false;
 | 
			
		||||
    for (auto it : confirmations)
 | 
			
		||||
    {
 | 
			
		||||
        if (it.first == name)
 | 
			
		||||
        {
 | 
			
		||||
            found = true;
 | 
			
		||||
            it.second->set_paused(pause);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pause == true)
 | 
			
		||||
    {
 | 
			
		||||
        // dismiss the confirmation too
 | 
			
		||||
        confirmation_base::set_state(name, confirmation_base::INACTIVE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return found;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void enable_conf (color_ostream &out, string name, bool state)
 | 
			
		||||
{
 | 
			
		||||
    if (!set_conf_state(name, state))
 | 
			
		||||
        out.printerr("Unrecognized option: %s\n", name.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
command_result df_confirm (color_ostream &out, vector <string> & parameters)
 | 
			
		||||
{
 | 
			
		||||
    CoreSuspender suspend;
 | 
			
		||||
    bool state = true;
 | 
			
		||||
    if (parameters.empty() || in_vector(parameters, "help") || in_vector(parameters, "status"))
 | 
			
		||||
    {
 | 
			
		||||
        out << "Available options: \n";
 | 
			
		||||
        for (auto it : confirmations)
 | 
			
		||||
            out.print("  %20s: %s\n", it.first.c_str(), it.second->is_enabled() ? "enabled" : "disabled");
 | 
			
		||||
        return CR_OK;
 | 
			
		||||
    }
 | 
			
		||||
    for (string param : parameters)
 | 
			
		||||
    {
 | 
			
		||||
        if (param == "enable")
 | 
			
		||||
            state = true;
 | 
			
		||||
        else if (param == "disable")
 | 
			
		||||
            state = false;
 | 
			
		||||
        else if (param == "all")
 | 
			
		||||
        {
 | 
			
		||||
            for (auto it : confirmations)
 | 
			
		||||
                it.second->apply(state);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            enable_conf(out, param, state);
 | 
			
		||||
    }
 | 
			
		||||
    return CR_OK;
 | 
			
		||||
}
 | 
			
		||||
@ -1,287 +0,0 @@
 | 
			
		||||
local _ENV = mkmodule('plugins.confirm')
 | 
			
		||||
 | 
			
		||||
local confs = {}
 | 
			
		||||
-- Wraps df.interface_key[foo] functionality but fails with invalid keys
 | 
			
		||||
keys = {}
 | 
			
		||||
setmetatable(keys, {
 | 
			
		||||
    __index = function(self, k)
 | 
			
		||||
        return df.interface_key[k] or error('Invalid key: ' .. tostring(k))
 | 
			
		||||
    end,
 | 
			
		||||
    __newindex = function() error('Table is read-only') end
 | 
			
		||||
})
 | 
			
		||||
-- Mouse keys will be sent as a string instead of interface_key
 | 
			
		||||
local MOUSE_LEFT = "MOUSE_LEFT"
 | 
			
		||||
local MOUSE_RIGHT = "MOUSE_RIGHT"
 | 
			
		||||
 | 
			
		||||
--[[ The screen where a confirmation has been triggered
 | 
			
		||||
Note that this is *not* necessarily the topmost viewscreen, so do not use
 | 
			
		||||
gui.getCurViewscreen() or related functions. ]]
 | 
			
		||||
--screen = nil
 | 
			
		||||
 | 
			
		||||
function if_nil(obj, default)
 | 
			
		||||
    if obj == nil then
 | 
			
		||||
        return default
 | 
			
		||||
    else
 | 
			
		||||
        return obj
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function defconf(id)
 | 
			
		||||
    if not get_ids()[id] then
 | 
			
		||||
        error('Bad confirmation ID (not defined in plugin): ' .. id)
 | 
			
		||||
    end
 | 
			
		||||
    local cls = {}
 | 
			
		||||
    cls.intercept_key = function(key) return false end
 | 
			
		||||
    cls.get_title = function() return if_nil(cls.title, '<No title>') end
 | 
			
		||||
    cls.get_message = function() return if_nil(cls.message, '<No message>') end
 | 
			
		||||
    cls.get_color = function() return if_nil(cls.color, COLOR_YELLOW) end
 | 
			
		||||
    confs[id] = cls
 | 
			
		||||
    return cls
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--[[ Beginning of confirmation definitions
 | 
			
		||||
All confirmations declared in confirm.cpp must have a corresponding call to
 | 
			
		||||
defconf() here, and should implement intercept_key(), get_title(), and
 | 
			
		||||
get_message(). get_color() can also be implemented here, but the default should
 | 
			
		||||
be sufficient.
 | 
			
		||||
 | 
			
		||||
In cases where getter functions always return the same value (e.g. get_title()),
 | 
			
		||||
they can be replaced with a field named after the method without the "get_"
 | 
			
		||||
prefix:
 | 
			
		||||
 | 
			
		||||
    trade.title = "Confirm trade"
 | 
			
		||||
 | 
			
		||||
is equivalent to:
 | 
			
		||||
 | 
			
		||||
    function trade.get_title() return "Confirm trade" end
 | 
			
		||||
 | 
			
		||||
]]
 | 
			
		||||
 | 
			
		||||
trade_cancel = defconf('trade-cancel')
 | 
			
		||||
function trade_cancel.intercept_key(key)
 | 
			
		||||
    return dfhack.gui.matchFocusString("dwarfmode/Trade") and
 | 
			
		||||
    (key == keys.LEAVESCREEN or key == MOUSE_RIGHT) and
 | 
			
		||||
    (trader_goods_selected() or broker_goods_selected())
 | 
			
		||||
end
 | 
			
		||||
trade_cancel.title = "Cancel trade"
 | 
			
		||||
trade_cancel.message = "Are you sure you want leave this screen?\nSelected items will not be saved."
 | 
			
		||||
 | 
			
		||||
haul_delete_route = defconf('haul-delete-route')
 | 
			
		||||
function haul_delete_route.intercept_key(key)
 | 
			
		||||
    return df.global.game.main_interface.current_hover == 180 and key == MOUSE_LEFT
 | 
			
		||||
end
 | 
			
		||||
haul_delete_route.title = "Confirm deletion"
 | 
			
		||||
haul_delete_route.message = "Are you sure you want to delete this route?"
 | 
			
		||||
 | 
			
		||||
haul_delete_stop = defconf('haul-delete-stop')
 | 
			
		||||
function haul_delete_stop.intercept_key(key)
 | 
			
		||||
    return df.global.game.main_interface.current_hover == 185 and key == MOUSE_LEFT
 | 
			
		||||
end
 | 
			
		||||
haul_delete_stop.title = "Confirm deletion"
 | 
			
		||||
haul_delete_stop.message = "Are you sure you want to delete this stop?"
 | 
			
		||||
 | 
			
		||||
depot_remove = defconf('depot-remove')
 | 
			
		||||
function depot_remove.intercept_key(key)
 | 
			
		||||
    if df.global.game.main_interface.current_hover == 301 and
 | 
			
		||||
            key == MOUSE_LEFT and
 | 
			
		||||
            df.building_tradedepotst:is_instance(dfhack.gui.getSelectedBuilding(true)) then
 | 
			
		||||
        for _, caravan in pairs(df.global.plotinfo.caravans) do
 | 
			
		||||
            if caravan.time_remaining > 0 then
 | 
			
		||||
                return true
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
depot_remove.title = "Confirm depot removal"
 | 
			
		||||
depot_remove.message = "Are you sure you want to remove this depot?\n" ..
 | 
			
		||||
    "Merchants are present and will lose profits."
 | 
			
		||||
 | 
			
		||||
squad_disband = defconf('squad-disband')
 | 
			
		||||
function squad_disband.intercept_key(key)
 | 
			
		||||
    return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 343
 | 
			
		||||
end
 | 
			
		||||
squad_disband.title = "Disband squad"
 | 
			
		||||
squad_disband.message = "Are you sure you want to disband this squad?"
 | 
			
		||||
 | 
			
		||||
order_remove = defconf('order-remove')
 | 
			
		||||
function order_remove.intercept_key(key)
 | 
			
		||||
    return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 222
 | 
			
		||||
end
 | 
			
		||||
order_remove.title = "Remove manager order"
 | 
			
		||||
order_remove.message = "Are you sure you want to remove this order?"
 | 
			
		||||
 | 
			
		||||
zone_remove = defconf('zone-remove')
 | 
			
		||||
function zone_remove.intercept_key(key)
 | 
			
		||||
    return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 130
 | 
			
		||||
end
 | 
			
		||||
zone_remove.title = "Remove zone"
 | 
			
		||||
zone_remove.message = "Are you sure you want to remove this zone?"
 | 
			
		||||
 | 
			
		||||
burrow_remove = defconf('burrow-remove')
 | 
			
		||||
function burrow_remove.intercept_key(key)
 | 
			
		||||
    return key == MOUSE_LEFT and
 | 
			
		||||
        (df.global.game.main_interface.current_hover == 171 or
 | 
			
		||||
         df.global.game.main_interface.current_hover == 168)
 | 
			
		||||
end
 | 
			
		||||
burrow_remove.title = "Remove burrow"
 | 
			
		||||
burrow_remove.message = "Are you sure you want to remove this burrow?"
 | 
			
		||||
 | 
			
		||||
stockpile_remove = defconf('stockpile-remove')
 | 
			
		||||
function stockpile_remove.intercept_key(key)
 | 
			
		||||
    return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 118
 | 
			
		||||
end
 | 
			
		||||
stockpile_remove.title = "Remove stockpile"
 | 
			
		||||
stockpile_remove.message = "Are you sure you want to remove this stockpile?"
 | 
			
		||||
 | 
			
		||||
-- these confirmations have more complex button detection requirements
 | 
			
		||||
--[[
 | 
			
		||||
trade = defconf('trade')
 | 
			
		||||
function trade.intercept_key(key)
 | 
			
		||||
    dfhack.gui.matchFocusString("dwarfmode/Trade") and key == MOUSE_LEFT and hovering over trade button?
 | 
			
		||||
end
 | 
			
		||||
trade.title = "Confirm trade"
 | 
			
		||||
function trade.get_message()
 | 
			
		||||
    if trader_goods_selected() and broker_goods_selected() then
 | 
			
		||||
        return "Are you sure you want to trade the selected goods?"
 | 
			
		||||
    elseif trader_goods_selected() then
 | 
			
		||||
        return "You are not giving any items. This is likely\n" ..
 | 
			
		||||
            "to irritate the merchants.\n" ..
 | 
			
		||||
            "Attempt to trade anyway?"
 | 
			
		||||
    elseif broker_goods_selected() then
 | 
			
		||||
        return "You are not receiving any items. You may want to\n" ..
 | 
			
		||||
            "offer these items instead or choose items to receive.\n" ..
 | 
			
		||||
            "Attempt to trade anyway?"
 | 
			
		||||
    else
 | 
			
		||||
        return "No items are selected. This is likely\n" ..
 | 
			
		||||
            "to irritate the merchants.\n" ..
 | 
			
		||||
            "Attempt to trade anyway?"
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
trade_seize = defconf('trade-seize')
 | 
			
		||||
function trade_seize.intercept_key(key)
 | 
			
		||||
    return screen.in_edit_count == 0 and
 | 
			
		||||
        trader_goods_selected() and
 | 
			
		||||
        key == keys.TRADE_SEIZE
 | 
			
		||||
end
 | 
			
		||||
trade_seize.title = "Confirm seize"
 | 
			
		||||
trade_seize.message = "Are you sure you want to seize these goods?"
 | 
			
		||||
 | 
			
		||||
trade_offer = defconf('trade-offer')
 | 
			
		||||
function trade_offer.intercept_key(key)
 | 
			
		||||
    return screen.in_edit_count == 0 and
 | 
			
		||||
        broker_goods_selected() and
 | 
			
		||||
        key == keys.TRADE_OFFER
 | 
			
		||||
end
 | 
			
		||||
trade_offer.title = "Confirm offer"
 | 
			
		||||
trade_offer.message = "Are you sure you want to offer these goods?\nYou will receive no payment."
 | 
			
		||||
 | 
			
		||||
trade_select_all = defconf('trade-select-all')
 | 
			
		||||
function trade_select_all.intercept_key(key)
 | 
			
		||||
    if screen.in_edit_count == 0 and key == keys.SEC_SELECT then
 | 
			
		||||
        if screen.in_right_pane and broker_goods_selected() and not broker_goods_all_selected() then
 | 
			
		||||
            return true
 | 
			
		||||
        elseif not screen.in_right_pane and trader_goods_selected() and not trader_goods_all_selected() then
 | 
			
		||||
            return true
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    return false
 | 
			
		||||
end
 | 
			
		||||
trade_select_all.title = "Confirm selection"
 | 
			
		||||
trade_select_all.message = "Selecting all goods will overwrite your current selection\n" ..
 | 
			
		||||
        "and cannot be undone. Continue?"
 | 
			
		||||
 | 
			
		||||
uniform_delete = defconf('uniform-delete')
 | 
			
		||||
function uniform_delete.intercept_key(key)
 | 
			
		||||
    return key == keys.D_MILITARY_DELETE_UNIFORM and
 | 
			
		||||
        screen.page == screen._type.T_page.Uniforms and
 | 
			
		||||
        #screen.equip.uniforms > 0 and
 | 
			
		||||
        not screen.equip.in_name_uniform
 | 
			
		||||
end
 | 
			
		||||
uniform_delete.title = "Delete uniform"
 | 
			
		||||
uniform_delete.message = "Are you sure you want to delete this uniform?"
 | 
			
		||||
 | 
			
		||||
note_delete = defconf('note-delete')
 | 
			
		||||
function note_delete.intercept_key(key)
 | 
			
		||||
    return key == keys.D_NOTE_DELETE and
 | 
			
		||||
        ui.main.mode == df.ui_sidebar_mode.NotesPoints and
 | 
			
		||||
        not ui.waypoints.in_edit_name_mode and
 | 
			
		||||
        not ui.waypoints.in_edit_text_mode
 | 
			
		||||
end
 | 
			
		||||
note_delete.title = "Delete note"
 | 
			
		||||
note_delete.message = "Are you sure you want to delete this note?"
 | 
			
		||||
 | 
			
		||||
route_delete = defconf('route-delete')
 | 
			
		||||
function route_delete.intercept_key(key)
 | 
			
		||||
    return key == keys.D_NOTE_ROUTE_DELETE and
 | 
			
		||||
        ui.main.mode == df.ui_sidebar_mode.NotesRoutes and
 | 
			
		||||
        not ui.waypoints.in_edit_name_mode
 | 
			
		||||
end
 | 
			
		||||
route_delete.title = "Delete route"
 | 
			
		||||
route_delete.message = "Are you sure you want to delete this route?"
 | 
			
		||||
 | 
			
		||||
convict = defconf('convict')
 | 
			
		||||
convict.title = "Confirm conviction"
 | 
			
		||||
function convict.intercept_key(key)
 | 
			
		||||
    return key == keys.SELECT and
 | 
			
		||||
        screen.cur_column == df.viewscreen_justicest.T_cur_column.ConvictChoices
 | 
			
		||||
end
 | 
			
		||||
function convict.get_message()
 | 
			
		||||
    name = dfhack.TranslateName(dfhack.units.getVisibleName(screen.convict_choices[screen.cursor_right]))
 | 
			
		||||
    if name == "" then
 | 
			
		||||
        name = "this creature"
 | 
			
		||||
    end
 | 
			
		||||
    return "Are you sure you want to convict " .. name .. "?\n" ..
 | 
			
		||||
        "This action is irreversible."
 | 
			
		||||
end
 | 
			
		||||
]]--
 | 
			
		||||
 | 
			
		||||
-- locations cannot be retired currently
 | 
			
		||||
--[[
 | 
			
		||||
location_retire = defconf('location-retire')
 | 
			
		||||
function location_retire.intercept_key(key)
 | 
			
		||||
    return key == keys.LOCATION_RETIRE and
 | 
			
		||||
        (screen.menu == df.viewscreen_locationsst.T_menu.Locations or
 | 
			
		||||
            screen.menu == df.viewscreen_locationsst.T_menu.Occupations) and
 | 
			
		||||
        screen.in_edit == df.viewscreen_locationsst.T_in_edit.None and
 | 
			
		||||
        screen.locations[screen.location_idx]
 | 
			
		||||
end
 | 
			
		||||
location_retire.title = "Retire location"
 | 
			
		||||
location_retire.message = "Are you sure you want to retire this location?"
 | 
			
		||||
]]--
 | 
			
		||||
 | 
			
		||||
-- End of confirmation definitions
 | 
			
		||||
 | 
			
		||||
function check()
 | 
			
		||||
    local undefined = {}
 | 
			
		||||
    for id in pairs(get_ids()) do
 | 
			
		||||
        if not confs[id] then
 | 
			
		||||
            table.insert(undefined, id)
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    if #undefined > 0 then
 | 
			
		||||
        error('Confirmation definitions missing: ' .. table.concat(undefined, ', '))
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--[[
 | 
			
		||||
The C++ plugin invokes methods of individual confirmations through four
 | 
			
		||||
functions (corresponding to method names) which receive the relevant screen,
 | 
			
		||||
the confirmation ID, and extra arguments in some cases, but these don't have to
 | 
			
		||||
do aything unique.
 | 
			
		||||
]]
 | 
			
		||||
 | 
			
		||||
function define_wrapper(name)
 | 
			
		||||
    _ENV[name] = function(scr, id, ...)
 | 
			
		||||
        _ENV.screen = scr
 | 
			
		||||
        if not confs[id] then
 | 
			
		||||
            error('Bad confirmation ID: ' .. id)
 | 
			
		||||
        end
 | 
			
		||||
        return confs[id][name](...)
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
define_wrapper('intercept_key')
 | 
			
		||||
define_wrapper('get_title')
 | 
			
		||||
define_wrapper('get_message')
 | 
			
		||||
define_wrapper('get_color')
 | 
			
		||||
return _ENV
 | 
			
		||||
		Loading…
	
		Reference in New Issue