diff --git a/src/API.cpp b/src/API.cpp index 566692710..a991e413c 100644 --- a/src/API.cpp +++ b/src/API.cpp @@ -539,12 +539,44 @@ bool API::callTaskHook ( { loadFile (file); - // TODO Get function. - // TODO Prepare args. - // TODO Make call. - // TODO Get exit status. + // Get function. + lua_getglobal (L, function.c_str ()); + if (!lua_isfunction (L, -1)) + { + lua_pop (L, 1); + throw std::string ("The Lua function '") + function + "' was not found."; + } - return true; + // Prepare args. + lua_pushnumber (L, id); + + // Make call. + if (lua_pcall (L, 1, 2, 0) != 0) + throw std::string ("Error calling '") + function + "' - " + lua_tostring (L, -1); + + // Call successful - get return values. + if (!lua_isnumber (L, -2)) + throw std::string ("Error: '") + function + "' did not return a success indicator"; + + if (!lua_isstring (L, -1) && !lua_isnil (L, -1)) + throw std::string ("Error: '") + function + "' did not return a message or nil"; + + int rc = lua_tointeger (L, -2); + const char* message = lua_tostring (L, -1); + + if (rc == 0) + { + if (message) + context.footnote (std::string ("Warning: ") + message); + } + else + { + if (message) + throw std::string (message); + } + + lua_pop (L, 1); + return rc == 0 ? true : false; } //////////////////////////////////////////////////////////////////////////////// @@ -567,6 +599,7 @@ bool API::callFieldHook ( //////////////////////////////////////////////////////////////////////////////// void API::loadFile (const std::string& file) { + // If the file is not loaded. if (std::find (loaded.begin (), loaded.end (), file) == loaded.end ()) { // Load the file, if possible. diff --git a/src/Hooks.cpp b/src/Hooks.cpp index 72ef8d840..495e41866 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -31,6 +31,43 @@ extern Context context; +//////////////////////////////////////////////////////////////////////////////// +Hook::Hook () +: event ("") +, file ("") +, function ("") +{ +} + +//////////////////////////////////////////////////////////////////////////////// +Hook::Hook (const std::string& e, const std::string& f, const std::string& fn) +: event (e) +, file (f) +, function (fn) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +Hook::Hook (const Hook& other) +{ + event = other.event; + file = other.file; + function = other.function; +} + +//////////////////////////////////////////////////////////////////////////////// +Hook& Hook::operator= (const Hook& other) +{ + if (this != &other) + { + event = other.event; + file = other.file; + function = other.function; + } + + return *this; +} + //////////////////////////////////////////////////////////////////////////////// Hooks::Hooks () { @@ -92,6 +129,14 @@ void Hooks::initialize () } } +//////////////////////////////////////////////////////////////////////////////// +void Hooks::setTaskId (int id) +{ +#ifdef HAVE_LIBLUA + task_id = id; +#endif +} + //////////////////////////////////////////////////////////////////////////////// bool Hooks::trigger (const std::string& event) { @@ -105,10 +150,10 @@ bool Hooks::trigger (const std::string& event) std::string type; if (eventType (event, type)) { - // TODO Figure out where to get the calling-context info from. + // Figure out where to get the calling-context info from. if (type == "program") rc = api.callProgramHook (it->file, it->function); else if (type == "list") rc = api.callListHook (it->file, it->function/*, tasks*/); - else if (type == "task") rc = api.callTaskHook (it->file, it->function, 0); + else if (type == "task") rc = api.callTaskHook (it->file, it->function, task_id); else if (type == "field") rc = api.callFieldHook (it->file, it->function, "field", "value"); } else @@ -136,6 +181,8 @@ bool Hooks::eventType (const std::string& event, std::string& type) event == "pre-dispatch" || event == "post-dispatch" || event == "pre-gc" || event == "post-gc" || event == "pre-undo" || event == "post-undo" || + event == "pre-file-lock" || event == "post-file-lock" || + event == "pre-file-unlock" || event == "post-file-unlock" || event == "pre-add-command" || event == "post-add-command") { type = "program"; @@ -146,7 +193,8 @@ bool Hooks::eventType (const std::string& event, std::string& type) type = "list"; return true; } - else if (event == "?") + else if (event == "pre-tag" || event == "post-tag" || + event == "pre-detag" || event == "post-detag") { type = "task"; return true; diff --git a/src/Hooks.h b/src/Hooks.h index bf0c8df93..43c809244 100644 --- a/src/Hooks.h +++ b/src/Hooks.h @@ -32,42 +32,14 @@ #include "API.h" #include "auto.h" -// Hook class representing a single hook. +// Hook class representing a single hook, which is just a three-way map. class Hook { public: - Hook () - : event ("") - , file ("") - , function ("") - { - } - - Hook (const std::string& e, const std::string& f, const std::string& fn) - : event (e) - , file (f) - , function (fn) - { - } - - Hook (const Hook& other) - { - event = other.event; - file = other.file; - function = other.function; - } - - Hook& operator= (const Hook& other) - { - if (this != &other) - { - event = other.event; - file = other.file; - function = other.function; - } - - return *this; - } + Hook (); + Hook (const std::string&, const std::string&, const std::string&); + Hook (const Hook&); + Hook& operator= (const Hook&); public: std::string event; @@ -85,7 +57,13 @@ public: Hooks& operator= (const Hooks&); // Deliberately unimplemented void initialize (); + + void setTaskId (int); +// void setField (const std::string&, const std::string&); +// void setTaskList (const std::vector &); bool trigger (const std::string&); + +private: bool eventType (const std::string&, std::string&); private: @@ -93,6 +71,9 @@ private: API api; #endif std::vector all; // All current hooks. +#ifdef HAVE_LIBLUA + int task_id; +#endif }; #endif diff --git a/src/TDB.cpp b/src/TDB.cpp index 49794aca1..baa97ed70 100644 --- a/src/TDB.cpp +++ b/src/TDB.cpp @@ -121,26 +121,30 @@ void TDB::location (const std::string& path) //////////////////////////////////////////////////////////////////////////////// void TDB::lock (bool lockFile /* = true */) { - mLock = lockFile; - - mPending.clear (); - mNew.clear (); - mPending.clear (); - - foreach (location, mLocations) + if (context.hooks.trigger ("pre-file-lock")) { - location->pending = openAndLock (location->path + "/pending.data"); - location->completed = openAndLock (location->path + "/completed.data"); - location->undo = openAndLock (location->path + "/undo.data"); - } + mLock = lockFile; - mAllOpenAndLocked = true; + mPending.clear (); + mNew.clear (); + mPending.clear (); + + foreach (location, mLocations) + { + location->pending = openAndLock (location->path + "/pending.data"); + location->completed = openAndLock (location->path + "/completed.data"); + location->undo = openAndLock (location->path + "/undo.data"); + } + + mAllOpenAndLocked = true; + context.hooks.trigger ("post-file-lock"); + } } //////////////////////////////////////////////////////////////////////////////// void TDB::unlock () { - if (mAllOpenAndLocked) + if (mAllOpenAndLocked && context.hooks.trigger ("pre-file-unlock")) { mPending.clear (); mNew.clear (); @@ -162,6 +166,7 @@ void TDB::unlock () } mAllOpenAndLocked = false; + context.hooks.trigger ("post-file-unlock"); } } diff --git a/src/Task.cpp b/src/Task.cpp index 27f6258fd..15d51a864 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -27,6 +27,7 @@ #include #include +#include "Context.h" #include "Nibbler.h" #include "Date.h" #include "Duration.h" @@ -34,6 +35,8 @@ #include "text.h" #include "util.h" +extern Context context; + //////////////////////////////////////////////////////////////////////////////// Task::Task () : id (0) diff --git a/src/command.cpp b/src/command.cpp index e325c7962..ee94247c5 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -1701,20 +1701,29 @@ int deltaDescription (Task& task) int deltaTags (Task& task) { int changes = 0; + context.hooks.setTaskId (task.id); // Apply or remove tags, if any. std::vector tags; context.task.getTags (tags); foreach (tag, tags) { - task.addTag (*tag); - ++changes; + if (context.hooks.trigger ("pre-tag")) + { + task.addTag (*tag); + ++changes; + context.hooks.trigger ("post-tag"); + } } foreach (tag, context.tagRemovals) { - task.removeTag (*tag); - ++changes; + if (context.hooks.trigger ("pre-detag")) + { + task.removeTag (*tag); + ++changes; + context.hooks.trigger ("post-detag"); + } } return changes;