#pragma once
#include <vector>
#include <string>
#include <memory>
#include <optional>
#include <unordered_map>
#include <mutex>
#include <qdatetime.h>
#include "path.h"
#include "coordinates.h"
#include "utils.h"
class QLocalSocket;
class MainWidget;
class ConfigManager;
enum RequirementType {
Text,
Symbol,
File,
Folder,
Rect,
Point,
Generic,
Password
};
struct Requirement {
RequirementType type;
std::string name;
};
class Command {
private:
virtual void perform() = 0;
protected:
int num_repeats = 1;
MainWidget* widget = nullptr;
std::optional<std::wstring> result = {};
std::string command_cname;
public:
static inline const bool developer_only = false;
QLocalSocket* result_socket = nullptr;
std::wstring* result_holder = nullptr;
bool* is_done = nullptr;
Command(std::string name, MainWidget* widget);
virtual std::optional<Requirement> next_requirement(MainWidget* widget);
virtual std::optional<std::wstring> get_result();
virtual void set_text_requirement(std::wstring value);
virtual void set_symbol_requirement(char value);
virtual void set_file_requirement(std::wstring value);
virtual void set_rect_requirement(AbsoluteRect value);
virtual void set_point_requirement(AbsoluteDocumentPos value);
virtual void set_generic_requirement(QVariant value);
virtual void handle_generic_requirement();
virtual void set_num_repeats(int nr);
virtual int get_num_repeats();
virtual std::vector<char> special_symbols();
virtual void pre_perform();
virtual bool pushes_state();
virtual bool requires_document();
virtual void on_cancel();
virtual void on_result_computed();
virtual void set_result_socket(QLocalSocket* result_socket);
virtual void set_result_mutex(bool* res_mut, std::wstring* result_location);
virtual std::optional<std::wstring> get_text_suggestion(int index);
virtual bool is_menu_command();
virtual void perform_up();
virtual bool is_holdable();
virtual void on_key_hold();
virtual void on_text_change(const QString& new_text);
virtual std::optional<QString> get_file_path_requirement_root_dir();
void set_next_requirement_with_string(std::wstring str);
virtual void run();
virtual std::string get_name();
virtual std::string get_pending_name();
virtual std::string get_human_readable_name();
virtual std::wstring get_text_default_value();
virtual ~Command();
};
class CommandManager {
private:
public:
std::map < std::string, std::function<std::unique_ptr<Command>(MainWidget*)> > new_commands;
std::map<std::string, std::string> command_human_readable_names;
std::map<std::string, QDateTime> command_last_uses;
std::unordered_map<QString, QString> command_required_prefixes;
CommandManager(ConfigManager* config_manager);
std::unique_ptr<Command> get_command_with_name(MainWidget* w, std::string name);
std::unique_ptr<Command> create_macro_command(MainWidget* w, std::string name, std::wstring macro_string);
QStringList get_all_command_names();
void handle_new_javascript_command(std::wstring command_name, JsCommandInfo command_files_pair, bool is_async);
void update_command_last_use(std::string command_name);
template<typename T>
void register_command(std::string alias_name="") {
bool is_developer_mode = false;
#ifdef SIOYEK_DEVELOPER
is_developer_mode = true;
#endif
if (is_developer_mode || !T::developer_only) {
std::string name = alias_name.size() > 0 ? alias_name : T::cname;
bool is_alias = alias_name.size() > 0;
new_commands[name] = [](MainWidget* widget) {return std::make_unique<T>(widget); };
command_human_readable_names[name] = is_alias ? "alias for " + T::cname : T::hname;
command_required_prefixes[QString::fromStdString(name)] = "";
}
}
};
struct InputParseTreeNode {
std::vector<InputParseTreeNode*> children;
int command;
std::vector<std::string> name_;
std::optional<std::function<std::unique_ptr<Command>(MainWidget*)>> generator = {};
bool shift_modifier = false;
bool control_modifier = false;
bool alt_modifier = false;
bool command_modifier = false;
bool requires_text = false;
bool requires_symbol = false;
bool is_root = false;
bool is_final = false;
std::wstring defining_file_path;
int defining_file_line;
bool is_same(const InputParseTreeNode* other);
bool matches(int key, bool shift, bool ctrl, bool cmd, bool alt);
};
class InputHandler {
private:
InputParseTreeNode* root = nullptr;
InputParseTreeNode* current_node = nullptr;
CommandManager* command_manager;
std::string number_stack;
std::vector<Path> user_key_paths;
std::string get_key_string_from_tree_node_sequence(const std::vector<InputParseTreeNode*> seq) const;
std::string get_key_name_from_key_code(int key_code) const;
void add_command_key_mappings(InputParseTreeNode* root, std::unordered_map<std::string, std::vector<std::string>>& map, std::vector<InputParseTreeNode*> prefix) const;
void get_commands_with_current_prefix_helper(InputParseTreeNode* node, std::vector<InputParseTreeNode*> prefix, std::unordered_map<std::string, std::vector<std::string>>& map) const;
public:
InputHandler(const Path& default_path, const std::vector<Path>& user_paths, CommandManager* cm);
void reload_config_files(const Path& default_path, const std::vector<Path>& user_path);
int get_event_key(QKeyEvent* key_event, bool* shift_pressed, bool* control_pressed, bool* is_command_pressed, bool* alt_pressed);
std::unique_ptr<Command> handle_key(MainWidget* w, QKeyEvent* key_event, bool shift_pressed, bool control_pressed, bool is_command_pressed, bool alt_pressed, int* num_repeats);
std::unique_ptr<Command> get_menu_command(MainWidget* w, QKeyEvent* key_event, bool shift_pressed, bool control_pressed, bool command_pressed, bool alt_pressed);
void delete_current_parse_tree(InputParseTreeNode* node_to_delete);
std::optional<Path> get_or_create_user_keys_path();
std::vector<Path> get_all_user_keys_paths();
std::unordered_map<std::string, std::vector<std::string>> get_command_key_mappings() const;
std::unordered_map<std::string, std::vector<std::string>> get_commands_with_current_prefix() const;
bool is_on_final_or_root_node() const;
};
bool is_macro_command_enabled(Command* command);
class KeyboardSelectPointCommand : public Command {
protected:
std::optional<std::wstring> text = {};
bool already_pre_performed = false;
std::unique_ptr<Command> origin;
bool requires_rect = false;
public:
KeyboardSelectPointCommand(MainWidget* w, std::unique_ptr<Command> original_command);
bool is_done();
virtual std::optional<Requirement> next_requirement(MainWidget* widget);
virtual void perform();
virtual void on_cancel() override;
void pre_perform();
virtual std::string get_name();
virtual void set_symbol_requirement(char value);
};
struct MenuItems;
using RecursiveItem = std::variant<std::unique_ptr<Command>, std::unique_ptr<MenuItems>>;
struct MenuItems {
std::wstring name;
std::vector<RecursiveItem> items;
};
std::unique_ptr<MenuItems> parse_menu_string(MainWidget* widget, QString name, QString menu_string);