/* * Copyright (C) 2012-2017 Robin Haberkorn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __QREGISTERS_H #define __QREGISTERS_H #include #include #include #include #include #include "sciteco.h" #include "memory.h" #include "error.h" #include "interface.h" #include "ioview.h" #include "undo.h" #include "rbtree.h" #include "parser.h" #include "document.h" namespace SciTECO { namespace QRegisters { /* initialized after Interface::main() in main() */ extern IOView view; } /* * Classes */ class QRegisterData : public Object { protected: tecoInt integer; class QRegisterString : public Document { public: ~QRegisterString() { release_document(); } private: ViewCurrent & get_create_document_view(void) { return QRegisters::view; } } string; public: /* * Whether to generate UndoTokens (unnecessary in macro invocations). * * FIXME: Every QRegister has this field, but it only differs * between local and global QRegisters. This wastes space. * There must be a more clever way to inherit this property, e.g. * by setting QRegisters::current_must_undo. */ bool must_undo; QRegisterData() : integer(0), must_undo(true) {} virtual ~QRegisterData() {} virtual tecoInt set_integer(tecoInt i) { return integer = i; } virtual void undo_set_integer(void) { if (must_undo) undo.push_var(integer); } virtual tecoInt get_integer(void) { return integer; } virtual void set_string(const gchar *str, gsize len); inline void set_string(const gchar *str) { set_string(str, str ? strlen(str) : 0); } virtual void undo_set_string(void); virtual void append_string(const gchar *str, gsize len); inline void append_string(const gchar *str) { append_string(str, str ? strlen(str) : 0); } virtual inline void undo_append_string(void) { undo_set_string(); } virtual gchar *get_string(void); virtual gsize get_string_size(void); virtual gint get_character(gint position); virtual void exchange_string(QRegisterData ®) { string.exchange(reg.string); } virtual void undo_exchange_string(QRegisterData ®); /* * The QRegisterStack must currently still access the * string fields directly to exchange data efficiently. */ friend class QRegisterStack; }; class QRegister : public RBTreeString::RBEntryOwnString, public QRegisterData { protected: /** * The default constructor for subclasses. * This leaves the name uninitialized. */ QRegister() {} public: QRegister(const gchar *name) : RBTreeString::RBEntryOwnString(name) {} virtual ~QRegister() {} virtual void edit(void); virtual void undo_edit(void); void execute(bool locals = true); void undo_set_eol_mode(void); void set_eol_mode(gint mode); /* * Load and save already care about undo token * creation. */ void load(const gchar *filename); void save(const gchar *filename); }; class QRegisterBufferInfo : public QRegister { public: QRegisterBufferInfo() : QRegister("*") {} /* setting "*" is equivalent to nEB */ tecoInt set_integer(tecoInt v); void undo_set_integer(void); tecoInt get_integer(void); void set_string(const gchar *str, gsize len) { throw QRegOpUnsupportedError(name); } void undo_set_string(void) {} void append_string(const gchar *str, gsize len) { throw QRegOpUnsupportedError(name); } void undo_append_string(void) {} gchar *get_string(void); gsize get_string_size(void); gint get_character(gint pos); void edit(void); }; class QRegisterWorkingDir : public QRegister { public: QRegisterWorkingDir() : QRegister("$") {} void set_string(const gchar *str, gsize len); void undo_set_string(void); void append_string(const gchar *str, gsize len) { throw QRegOpUnsupportedError(name); } void undo_append_string(void) {} gchar *get_string(void); gsize get_string_size(void); gint get_character(gint pos); void edit(void); void exchange_string(QRegisterData ®); void undo_exchange_string(QRegisterData ®); }; class QRegisterClipboard : public QRegister { class UndoTokenSetClipboard : public UndoToken { gchar *name; gchar *str; gsize str_len; public: /** * Construct undo token. * * This passes ownership of the clipboard content string * to the undo token object. */ UndoTokenSetClipboard(const gchar *_name, gchar *_str, gsize _str_len) : name(g_strdup(_name)), str(_str), str_len(_str_len) {} ~UndoTokenSetClipboard() { g_free(str); g_free(name); } void run(void); }; /** * Gets the clipboard name. * Can be easily derived from the Q-Register name. */ inline const gchar * get_clipboard_name(void) const { return name+1; } public: QRegisterClipboard(const gchar *_name = NULL) { name = g_strconcat("~", _name, NIL); } void set_string(const gchar *str, gsize len); void undo_set_string(void); /* * FIXME: We could support that. */ void append_string(const gchar *str, gsize len) { throw QRegOpUnsupportedError(name); } void undo_append_string(void) {} gchar *get_string(gsize *out_len); gchar *get_string(void); gsize get_string_size(void); gint get_character(gint pos); void edit(void); void exchange_string(QRegisterData ®); void undo_exchange_string(QRegisterData ®); }; class QRegisterTable : private RBTreeString, public Object { class UndoTokenRemoveGlobal : public UndoToken { protected: QRegister *reg; public: UndoTokenRemoveGlobal(QRegister *_reg) : reg(_reg) {} void run(void); }; class UndoTokenRemoveLocal : public UndoTokenRemoveGlobal { public: UndoTokenRemoveLocal(QRegister *reg) : UndoTokenRemoveGlobal(reg) {} void run(void); }; bool must_undo; public: QRegisterTable(bool _must_undo = true) : must_undo(_must_undo) {} ~QRegisterTable() { QRegister *cur; while ((cur = (QRegister *)root())) delete (QRegister *)remove(cur); } void undo_remove(QRegister *reg); inline QRegister * insert(QRegister *reg) { reg->must_undo = must_undo; RBTreeString::insert(reg); return reg; } inline QRegister * insert(const gchar *name) { return insert(new QRegister(name)); } inline QRegister * insert(gchar name) { gchar buf[] = {name, '\0'}; return insert(buf); } void insert_defaults(void); inline QRegister * find(const gchar *name) { return (QRegister *)RBTreeString::find(name); } inline QRegister * operator [](const gchar *name) { return find(name); } inline QRegister * operator [](gchar chr) { gchar buf[] = {chr, '\0'}; return find(buf); } inline QRegister * nfind(const gchar *name) { return (QRegister *)RBTreeString::nfind(name); } void edit(QRegister *reg); inline QRegister * edit(const gchar *name) { QRegister *reg = find(name); if (!reg) return NULL; edit(reg); return reg; } void set_environ(void); gchar **get_environ(void); void update_environ(void); void clear(void); inline gchar * auto_complete(const gchar *name, gchar completed = '\0', gsize max_len = 0) { return RBTreeString::auto_complete(name, completed, max_len); } }; class QRegisterStack : public Object { class Entry : public QRegisterData { public: SLIST_ENTRY(Entry) entries; Entry() : QRegisterData() {} }; class UndoTokenPush : public UndoToken { QRegisterStack *stack; /* only remaining reference to stack entry */ Entry *entry; public: UndoTokenPush(QRegisterStack *_stack, Entry *_entry) : UndoToken(), stack(_stack), entry(_entry) {} ~UndoTokenPush() { delete entry; } void run(void); }; class UndoTokenPop : public UndoToken { QRegisterStack *stack; public: UndoTokenPop(QRegisterStack *_stack) : stack(_stack) {} void run(void); }; SLIST_HEAD(Head, Entry) head; public: QRegisterStack() { SLIST_INIT(&head); } ~QRegisterStack(); void push(QRegister ®); bool pop(QRegister ®); }; enum QRegSpecType { /** Register must exist, else fail */ QREG_REQUIRED, /** * Return NULL if register does not exist. * You can still call QRegSpecMachine::fail() to require it. */ QREG_OPTIONAL, /** Initialize register if it does not already exist */ QREG_OPTIONAL_INIT }; class QRegSpecMachine : public MicroStateMachine { StringBuildingMachine string_machine; QRegSpecType type; bool is_local; gint nesting; gchar *name; public: QRegSpecMachine(QRegSpecType _type = QREG_REQUIRED) : MicroStateMachine(), type(_type), is_local(false), nesting(0), name(NULL) {} ~QRegSpecMachine() { g_free(name); } void reset(void); bool input(gchar chr, QRegister *&result); inline void fail(void) G_GNUC_NORETURN { throw InvalidQRegError(name, is_local); } gchar *auto_complete(void); }; /* * Command states */ /** * Super class for states accepting Q-Register specifications */ class StateExpectQReg : public State { protected: QRegSpecMachine machine; public: StateExpectQReg(QRegSpecType type = QREG_REQUIRED); private: State *custom(gchar chr); protected: /** * Called when a register specification has been * successfully parsed. * The QRegSpecMachine is not reset automatically. * * @param reg Register of the parsed Q-Reg * specification. May be NULL in * parse-only mode or with QREG_OPTIONAL. * @returns Next parser state. */ virtual State *got_register(QRegister *reg) = 0; /* in cmdline.cpp */ void process_edit_cmd(gchar key); }; class StatePushQReg : public StateExpectQReg { private: State *got_register(QRegister *reg); }; class StatePopQReg : public StateExpectQReg { public: StatePopQReg() : StateExpectQReg(QREG_OPTIONAL_INIT) {} private: State *got_register(QRegister *reg); }; class StateEQCommand : public StateExpectQReg { public: StateEQCommand() : StateExpectQReg(QREG_OPTIONAL_INIT) {} private: State *got_register(QRegister *reg); }; class StateLoadQReg : public StateExpectFile { private: State *got_file(const gchar *filename); }; class StateEPctCommand : public StateExpectQReg { private: State *got_register(QRegister *reg); }; class StateSaveQReg : public StateExpectFile { private: State *got_file(const gchar *filename); }; class StateQueryQReg : public StateExpectQReg { public: StateQueryQReg() : StateExpectQReg(QREG_OPTIONAL) {} private: State *got_register(QRegister *reg); }; class StateCtlUCommand : public StateExpectQReg { public: StateCtlUCommand() : StateExpectQReg(QREG_OPTIONAL_INIT) {} private: State *got_register(QRegister *reg); }; class StateEUCommand : public StateExpectQReg { public: StateEUCommand() : StateExpectQReg(QREG_OPTIONAL_INIT) {} private: State *got_register(QRegister *reg); }; class StateSetQRegString : public StateExpectString { bool text_added; public: StateSetQRegString(bool building) : StateExpectString(building) {} private: void initial(void); State *done(const gchar *str); }; class StateGetQRegString : public StateExpectQReg { private: State *got_register(QRegister *reg); }; class StateSetQRegInteger : public StateExpectQReg { public: StateSetQRegInteger() : StateExpectQReg(QREG_OPTIONAL_INIT) {} private: State *got_register(QRegister *reg); }; class StateIncreaseQReg : public StateExpectQReg { public: StateIncreaseQReg() : StateExpectQReg(QREG_OPTIONAL_INIT) {} private: State *got_register(QRegister *reg); }; class StateMacro : public StateExpectQReg { private: State *got_register(QRegister *reg); }; class StateMacroFile : public StateExpectFile { private: State *got_file(const gchar *filename); }; class StateCopyToQReg : public StateExpectQReg { public: StateCopyToQReg() : StateExpectQReg(QREG_OPTIONAL_INIT) {} private: State *got_register(QRegister *reg); }; namespace States { extern StatePushQReg pushqreg; extern StatePopQReg popqreg; extern StateEQCommand eqcommand; extern StateLoadQReg loadqreg; extern StateEPctCommand epctcommand; extern StateSaveQReg saveqreg; extern StateQueryQReg queryqreg; extern StateCtlUCommand ctlucommand; extern StateEUCommand eucommand; extern StateSetQRegString setqregstring_nobuilding; extern StateSetQRegString setqregstring_building; extern StateGetQRegString getqregstring; extern StateSetQRegInteger setqreginteger; extern StateIncreaseQReg increaseqreg; extern StateMacro macro; extern StateMacroFile macro_file; extern StateCopyToQReg copytoqreg; } namespace QRegisters { /* object declared in main.cpp */ extern QRegisterTable globals; extern QRegisterTable *locals; extern QRegister *current; enum Hook { HOOK_ADD = 1, HOOK_EDIT, HOOK_CLOSE, HOOK_QUIT }; void hook(Hook type); } } /* namespace SciTECO */ #endif