mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Merge branch 'hooks' into 1.9.0
Conflicts: src/command.cpp src/report.cpp
This commit is contained in:
commit
585cbdfcac
17 changed files with 1592 additions and 409 deletions
30
configure.ac
30
configure.ac
|
@ -51,6 +51,35 @@ else
|
||||||
AC_DEFINE([UNKNOWN], [], [Compiling on Unknown])
|
AC_DEFINE([UNKNOWN], [], [Compiling on Unknown])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check for Lua.
|
||||||
|
AC_ARG_ENABLE([lua], AS_HELP_STRING([--enable-lua], [Disable feature lua]))
|
||||||
|
AC_ARG_WITH(lua-inc, [--with-lua-inc=DIR, Lua include files are in DIR],lua_inc=$withval,lua_inc='')
|
||||||
|
AC_ARG_WITH(lua-lib, [--with-lua-lib=DIR, Lua library files are in DIR],lua_lib=$withval,lua_lib='')
|
||||||
|
|
||||||
|
if test "x$enable_lua" = "xyes" ; then
|
||||||
|
ac_save_CPPFLAGS="$CPPFLAGS"
|
||||||
|
ac_save_CFLAGS="$CFLAGS"
|
||||||
|
ac_save_LDFLAGS="$LDFLAGS"
|
||||||
|
|
||||||
|
LUA_CFLAGS=""
|
||||||
|
LUA_LFLAGS=""
|
||||||
|
|
||||||
|
if test -n "$lua_inc"; then
|
||||||
|
CFLAGS="$CFLAGS -I$lua_inc"
|
||||||
|
CPPFLAGS="$CPPFLAGS -I$lua_inc"
|
||||||
|
fi
|
||||||
|
if test -n "$lua_lib"; then
|
||||||
|
LDFLAGS="$LDFLAGS -L$lua_lib"
|
||||||
|
LUA_LFLAGS="-llua"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$enable_lua" = "xyes" ; then
|
||||||
|
AC_SUBST(LUA_CFLAGS)
|
||||||
|
AC_SUBST(LUA_LFLAGS)
|
||||||
|
AC_DEFINE([HAVE_LIBLUA], [1], [Building with Lua support])
|
||||||
|
fi
|
||||||
|
|
||||||
AM_INIT_AUTOMAKE
|
AM_INIT_AUTOMAKE
|
||||||
AC_CONFIG_SRCDIR([src/main.cpp])
|
AC_CONFIG_SRCDIR([src/main.cpp])
|
||||||
AC_CONFIG_HEADER([auto.h])
|
AC_CONFIG_HEADER([auto.h])
|
||||||
|
@ -64,6 +93,7 @@ AC_SUBST(CFLAGS)
|
||||||
|
|
||||||
# Checks for libraries.
|
# Checks for libraries.
|
||||||
AC_CHECK_LIB(ncurses,initscr)
|
AC_CHECK_LIB(ncurses,initscr)
|
||||||
|
AC_CHECK_LIB(lua,lua_open)
|
||||||
|
|
||||||
# Checks for header files.
|
# Checks for header files.
|
||||||
AC_HEADER_STDC
|
AC_HEADER_STDC
|
||||||
|
|
617
src/API.cpp
Normal file
617
src/API.cpp
Normal file
|
@ -0,0 +1,617 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Task Lua API
|
||||||
|
//
|
||||||
|
// Copyright 2006 - 2010, Paul Beckingham.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 2 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, write to the
|
||||||
|
//
|
||||||
|
// Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor,
|
||||||
|
// Boston, MA
|
||||||
|
// 02110-1301
|
||||||
|
// USA
|
||||||
|
//
|
||||||
|
// -------------
|
||||||
|
//
|
||||||
|
// Copyright © 1994–2008 Lua.org, PUC-Rio.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <iostream> // TODO Remove
|
||||||
|
#include <algorithm>
|
||||||
|
#include "Context.h"
|
||||||
|
#include "API.h"
|
||||||
|
|
||||||
|
extern Context context;
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBLUA
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Returns a string representing the task version number, such as '1.9.0'.
|
||||||
|
static int api_task_version (lua_State* L)
|
||||||
|
{
|
||||||
|
lua_pushstring (L, PACKAGE_VERSION);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Returns a string representing the Lua version number, such as '5.1.4'.
|
||||||
|
// Lua 5.2.0 has a 'lua_version' call, but 5.1.4 is the target.
|
||||||
|
static int api_task_lua_version (lua_State* L)
|
||||||
|
{
|
||||||
|
// Convert "Lua 5.1.4" -> "5.1.4"
|
||||||
|
std::string ver = LUA_RELEASE;
|
||||||
|
lua_pushstring (L, ver.substr (4, std::string::npos).c_str ());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Returns the type of OS that task is running on.
|
||||||
|
static int api_task_os (lua_State* L)
|
||||||
|
{
|
||||||
|
#if defined (DARWIN)
|
||||||
|
lua_pushstring (L, "darwin");
|
||||||
|
#elif defined (SOLARIS)
|
||||||
|
lua_pushstring (L, "solaris");
|
||||||
|
#elif defined (CYGWIN)
|
||||||
|
lua_pushstring (L, "cygwin");
|
||||||
|
#elif defined (OPENBSD)
|
||||||
|
lua_pushstring (L, "openbsd");
|
||||||
|
#elif defined (HAIKU)
|
||||||
|
lua_pushstring (L, "haiku");
|
||||||
|
#elif defined (FREEBSD)
|
||||||
|
lua_pushstring (L, "freebsd");
|
||||||
|
#elif defined (LINUX)
|
||||||
|
lua_pushstring (L, "linux");
|
||||||
|
#else
|
||||||
|
lua_pushstring (L, "unknown");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_feature (lua_State* L)
|
||||||
|
{
|
||||||
|
std::string name = luaL_checkstring (L, 1);
|
||||||
|
bool value = false;
|
||||||
|
|
||||||
|
if (name == "readline")
|
||||||
|
{
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
value = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (name == "ncurses")
|
||||||
|
{
|
||||||
|
#ifdef HAVE_NCURSES
|
||||||
|
value = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushnumber (L, value ? 1 : 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_aliases ()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Returns values from .taskrc, by name.
|
||||||
|
static int api_task_get_config (name)
|
||||||
|
{
|
||||||
|
return "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Temporarily sets .taskrc values, by name.
|
||||||
|
static int api_task_set_config (name, value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Returns an internationalized string, by string ID, from the appropriate
|
||||||
|
-- locale-based strings file.
|
||||||
|
static int api_task_i18n_string (id)
|
||||||
|
{
|
||||||
|
return "le foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Returns a list of tips, from the appropriate locale-based tips file.
|
||||||
|
static int api_task_i18n_tips ()
|
||||||
|
{
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Returns the name of the current command.
|
||||||
|
static int api_task_get_command ()
|
||||||
|
{
|
||||||
|
return "list"
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Returns a list of string messages generated so far.
|
||||||
|
static int api_task_get_header_messages ()
|
||||||
|
{
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_footnote_messages ()
|
||||||
|
{
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_debug_messages ()
|
||||||
|
{
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Records additional messages, for subsequent display.
|
||||||
|
static int api_task_header_message (text)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_footnote_message (text)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_debug_message (text)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Causes the shell or interactive mode task to exit. Ordinarily this does not
|
||||||
|
// occur.
|
||||||
|
static int api_task_exit (lua_State* L)
|
||||||
|
{
|
||||||
|
// TODO Is this the correct exception? How does the shell handle this?
|
||||||
|
throw std::string ("Exiting.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Shuts off the hook system for any subsequent hook calls for this command.
|
||||||
|
static int api_task_inhibit_further_hooks ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Returns a table that contains a complete copy of the task.
|
||||||
|
static int api_task_get (id)
|
||||||
|
{
|
||||||
|
return task
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Creates a new task from the data specified in the table t.
|
||||||
|
static int api_task_add (t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- Modifies the task described in the table t.
|
||||||
|
static int api_task_modify (t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- 'id' is the task id passed to the hook function. Date attributes are
|
||||||
|
-- returned as a numeric epoch offset. Tags and annotations are returned
|
||||||
|
-- as tables. A nil value indicates a missing value.
|
||||||
|
static int api_task_get_uuid (id)
|
||||||
|
{
|
||||||
|
return task.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_description (id)
|
||||||
|
{
|
||||||
|
return task.description
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_annotations (id)
|
||||||
|
{
|
||||||
|
return task.annotations
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_project (id)
|
||||||
|
{
|
||||||
|
return task.project
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_priority (id)
|
||||||
|
{
|
||||||
|
return task.priority
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_tags (id)
|
||||||
|
{
|
||||||
|
return task.tags
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_status (id)
|
||||||
|
{
|
||||||
|
return task.status
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_due (id)
|
||||||
|
{
|
||||||
|
return task.due_date
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_entry (id)
|
||||||
|
{
|
||||||
|
return task.entry_date
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_start (id)
|
||||||
|
{
|
||||||
|
return task.start_date
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_end (id)
|
||||||
|
{
|
||||||
|
return task.end_date
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_recur (id)
|
||||||
|
{
|
||||||
|
return task.recur
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_until (id)
|
||||||
|
{
|
||||||
|
return task.until_date
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_get_wait (id)
|
||||||
|
{
|
||||||
|
return task.wait_date
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
-- 'id' is the task id passed to the hook function. Date attributes are
|
||||||
|
-- expected as numeric epoch offsets. Tags and annotations are expected
|
||||||
|
-- as tables. A nil value indicates a missing value.
|
||||||
|
static int api_task_set_description (id, value)
|
||||||
|
{
|
||||||
|
task.description = value
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_set_annotations (id, value)
|
||||||
|
{
|
||||||
|
task.annotations = value
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_set_project (id, value)
|
||||||
|
{
|
||||||
|
task.project = value
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_set_priority (id, value)
|
||||||
|
{
|
||||||
|
task.priority = value
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_set_tags (id, value)
|
||||||
|
{
|
||||||
|
task.tags = value
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_set_status (id, value)
|
||||||
|
{
|
||||||
|
task.status = value
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_set_due (id, value)
|
||||||
|
{
|
||||||
|
task.due_date = value
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_set_start (id, value)
|
||||||
|
{
|
||||||
|
task.start_date = value
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_set_recur (id, value)
|
||||||
|
{
|
||||||
|
task.recur = value
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_set_until (id, value)
|
||||||
|
{
|
||||||
|
task.until_date = value
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
static int api_task_set_wait (id, value)
|
||||||
|
{
|
||||||
|
task.wait_date = value
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
API::API ()
|
||||||
|
: L (NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
API::~API ()
|
||||||
|
{
|
||||||
|
if (L)
|
||||||
|
{
|
||||||
|
lua_close (L);
|
||||||
|
L = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void API::initialize ()
|
||||||
|
{
|
||||||
|
// Initialize Lua.
|
||||||
|
L = lua_open ();
|
||||||
|
luaL_openlibs (L); // TODO Error handling
|
||||||
|
|
||||||
|
// Register all the API functions in Lua global space.
|
||||||
|
lua_pushcfunction (L, api_task_version); lua_setglobal (L, "task_version");
|
||||||
|
lua_pushcfunction (L, api_task_lua_version); lua_setglobal (L, "task_lua_version");
|
||||||
|
lua_pushcfunction (L, api_task_os); lua_setglobal (L, "task_os");
|
||||||
|
lua_pushcfunction (L, api_task_feature); lua_setglobal (L, "task_feature");
|
||||||
|
/*
|
||||||
|
lua_pushcfunction (L, api_task_aliases); lua_setglobal (L, "api_task_aliases");
|
||||||
|
lua_pushcfunction (L, api_task_get_config); lua_setglobal (L, "api_task_get_config");
|
||||||
|
lua_pushcfunction (L, api_task_set_config); lua_setglobal (L, "api_task_set_config");
|
||||||
|
lua_pushcfunction (L, api_task_i18n_string); lua_setglobal (L, "api_task_i18n_string");
|
||||||
|
lua_pushcfunction (L, api_task_i18n_tips); lua_setglobal (L, "api_task_i18n_tips");
|
||||||
|
lua_pushcfunction (L, api_task_get_command); lua_setglobal (L, "api_task_get_command");
|
||||||
|
lua_pushcfunction (L, api_task_get_header_messages); lua_setglobal (L, "api_task_get_header_messages");
|
||||||
|
lua_pushcfunction (L, api_task_get_footnote_messages); lua_setglobal (L, "api_task_get_footnote_messages");
|
||||||
|
lua_pushcfunction (L, api_task_get_debug_messages); lua_setglobal (L, "api_task_get_debug_messages");
|
||||||
|
lua_pushcfunction (L, api_task_header_message); lua_setglobal (L, "api_task_header_message");
|
||||||
|
lua_pushcfunction (L, api_task_footnote_message); lua_setglobal (L, "api_task_footnote_message");
|
||||||
|
lua_pushcfunction (L, api_task_debug_message); lua_setglobal (L, "api_task_debug_message");
|
||||||
|
*/
|
||||||
|
lua_pushcfunction (L, api_task_exit); lua_setglobal (L, "task_exit");
|
||||||
|
/*
|
||||||
|
lua_pushcfunction (L, api_task_inhibit_further_hooks); lua_setglobal (L, "api_task_inhibit_further_hooks");
|
||||||
|
lua_pushcfunction (L, api_task_get); lua_setglobal (L, "api_task_get");
|
||||||
|
lua_pushcfunction (L, api_task_add); lua_setglobal (L, "api_task_add");
|
||||||
|
lua_pushcfunction (L, api_task_modify); lua_setglobal (L, "api_task_modify");
|
||||||
|
lua_pushcfunction (L, api_task_get_uuid); lua_setglobal (L, "api_task_get_uuid");
|
||||||
|
lua_pushcfunction (L, api_task_get_description); lua_setglobal (L, "api_task_get_description");
|
||||||
|
lua_pushcfunction (L, api_task_get_annotations); lua_setglobal (L, "api_task_get_annotations");
|
||||||
|
lua_pushcfunction (L, api_task_get_project); lua_setglobal (L, "api_task_get_project");
|
||||||
|
lua_pushcfunction (L, api_task_get_priority); lua_setglobal (L, "api_task_get_priority");
|
||||||
|
lua_pushcfunction (L, api_task_get_tags); lua_setglobal (L, "api_task_get_tags");
|
||||||
|
lua_pushcfunction (L, api_task_get_status); lua_setglobal (L, "api_task_get_status");
|
||||||
|
lua_pushcfunction (L, api_task_get_due); lua_setglobal (L, "api_task_get_due");
|
||||||
|
lua_pushcfunction (L, api_task_get_entry); lua_setglobal (L, "api_task_get_entry");
|
||||||
|
lua_pushcfunction (L, api_task_get_start); lua_setglobal (L, "api_task_get_start");
|
||||||
|
lua_pushcfunction (L, api_task_get_end); lua_setglobal (L, "api_task_get_end");
|
||||||
|
lua_pushcfunction (L, api_task_get_recur); lua_setglobal (L, "api_task_get_recur");
|
||||||
|
lua_pushcfunction (L, api_task_get_until); lua_setglobal (L, "api_task_get_until");
|
||||||
|
lua_pushcfunction (L, api_task_get_wait); lua_setglobal (L, "api_task_get_wait");
|
||||||
|
lua_pushcfunction (L, api_task_set_description); lua_setglobal (L, "api_task_set_description");
|
||||||
|
lua_pushcfunction (L, api_task_set_annotations); lua_setglobal (L, "api_task_set_annotations");
|
||||||
|
lua_pushcfunction (L, api_task_set_project); lua_setglobal (L, "api_task_set_project");
|
||||||
|
lua_pushcfunction (L, api_task_set_priority); lua_setglobal (L, "api_task_set_priority");
|
||||||
|
lua_pushcfunction (L, api_task_set_tags); lua_setglobal (L, "api_task_set_tags");
|
||||||
|
lua_pushcfunction (L, api_task_set_status); lua_setglobal (L, "api_task_set_status");
|
||||||
|
lua_pushcfunction (L, api_task_set_due); lua_setglobal (L, "api_task_set_due");
|
||||||
|
lua_pushcfunction (L, api_task_set_start); lua_setglobal (L, "api_task_set_start");
|
||||||
|
lua_pushcfunction (L, api_task_set_recur); lua_setglobal (L, "api_task_set_recur");
|
||||||
|
lua_pushcfunction (L, api_task_set_until); lua_setglobal (L, "api_task_set_until");
|
||||||
|
lua_pushcfunction (L, api_task_set_wait); lua_setglobal (L, "api_task_set_wait");
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool API::callProgramHook (
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& function)
|
||||||
|
{
|
||||||
|
loadFile (file);
|
||||||
|
|
||||||
|
// 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.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make call.
|
||||||
|
if (lua_pcall (L, 0, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool API::callListHook (
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& function/*,
|
||||||
|
iterator i*/)
|
||||||
|
{
|
||||||
|
loadFile (file);
|
||||||
|
|
||||||
|
// TODO Get function.
|
||||||
|
// TODO Prepare args.
|
||||||
|
// TODO Make call.
|
||||||
|
// TODO Get exit status.
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool API::callTaskHook (
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& function,
|
||||||
|
int id)
|
||||||
|
{
|
||||||
|
loadFile (file);
|
||||||
|
|
||||||
|
// 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.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool API::callFieldHook (
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& function,
|
||||||
|
const std::string& field,
|
||||||
|
const std::string& value)
|
||||||
|
{
|
||||||
|
loadFile (file);
|
||||||
|
|
||||||
|
// TODO Get function.
|
||||||
|
// TODO Prepare args.
|
||||||
|
// TODO Make call.
|
||||||
|
// TODO Get exit status.
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
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.
|
||||||
|
if (luaL_loadfile (L, file.c_str ()) || lua_pcall (L, 0, 0, 0))
|
||||||
|
throw std::string ("Error: ") + std::string (lua_tostring (L, -1));
|
||||||
|
|
||||||
|
// Mark this as loaded, so as to not bother again.
|
||||||
|
loaded.push_back (file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
66
src/API.h
Normal file
66
src/API.h
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// task - a command line task list manager.
|
||||||
|
//
|
||||||
|
// Copyright 2006 - 2010, Paul Beckingham.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 2 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, write to the
|
||||||
|
//
|
||||||
|
// Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor,
|
||||||
|
// Boston, MA
|
||||||
|
// 02110-1301
|
||||||
|
// USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#ifndef INCLUDED_API
|
||||||
|
#define INCLUDED_API
|
||||||
|
|
||||||
|
#include "auto.h"
|
||||||
|
#ifdef HAVE_LIBLUA
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include "lua.h"
|
||||||
|
#include "lualib.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
class API
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
API ();
|
||||||
|
API (const API&);
|
||||||
|
API& operator= (const API&);
|
||||||
|
~API ();
|
||||||
|
|
||||||
|
void initialize ();
|
||||||
|
bool callProgramHook (const std::string&, const std::string&);
|
||||||
|
bool callListHook (const std::string&, const std::string& /*, iterator */);
|
||||||
|
bool callTaskHook (const std::string&, const std::string&, int);
|
||||||
|
bool callFieldHook (const std::string&, const std::string&, const std::string&, const std::string&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loadFile (const std::string&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
lua_State* L;
|
||||||
|
std::vector <std::string> loaded;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
|
@ -83,6 +83,11 @@ void Context::initialize (int argc, char** argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize ();
|
initialize ();
|
||||||
|
|
||||||
|
// Hook system init, plus post-start event occurring at the first possible
|
||||||
|
// moment after hook initialization.
|
||||||
|
hooks.initialize ();
|
||||||
|
hooks.trigger ("post-start");
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -134,8 +139,6 @@ void Context::initialize ()
|
||||||
int Context::run ()
|
int Context::run ()
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
Timer t ("Context::run");
|
|
||||||
|
|
||||||
std::string output;
|
std::string output;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -156,30 +159,39 @@ int Context::run ()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump all debug messages.
|
// Dump all debug messages.
|
||||||
|
hooks.trigger ("pre-debug");
|
||||||
if (config.getBoolean ("debug"))
|
if (config.getBoolean ("debug"))
|
||||||
foreach (d, debugMessages)
|
foreach (d, debugMessages)
|
||||||
if (config.getBoolean ("color") || config.getBoolean ("_forcecolor"))
|
if (config.getBoolean ("color") || config.getBoolean ("_forcecolor"))
|
||||||
std::cout << colorizeDebug (*d) << std::endl;
|
std::cout << colorizeDebug (*d) << std::endl;
|
||||||
else
|
else
|
||||||
std::cout << *d << std::endl;
|
std::cout << *d << std::endl;
|
||||||
|
hooks.trigger ("post-debug");
|
||||||
|
|
||||||
// Dump all headers.
|
// Dump all headers.
|
||||||
|
hooks.trigger ("pre-header");
|
||||||
foreach (h, headers)
|
foreach (h, headers)
|
||||||
if (config.getBoolean ("color") || config.getBoolean ("_forcecolor"))
|
if (config.getBoolean ("color") || config.getBoolean ("_forcecolor"))
|
||||||
std::cout << colorizeHeader (*h) << std::endl;
|
std::cout << colorizeHeader (*h) << std::endl;
|
||||||
else
|
else
|
||||||
std::cout << *h << std::endl;
|
std::cout << *h << std::endl;
|
||||||
|
hooks.trigger ("post-header");
|
||||||
|
|
||||||
// Dump the report output.
|
// Dump the report output.
|
||||||
|
hooks.trigger ("pre-output");
|
||||||
std::cout << output;
|
std::cout << output;
|
||||||
|
hooks.trigger ("post-output");
|
||||||
|
|
||||||
// Dump all footnotes.
|
// Dump all footnotes.
|
||||||
|
hooks.trigger ("pre-footnote");
|
||||||
foreach (f, footnotes)
|
foreach (f, footnotes)
|
||||||
if (config.getBoolean ("color") || config.getBoolean ("_forcecolor"))
|
if (config.getBoolean ("color") || config.getBoolean ("_forcecolor"))
|
||||||
std::cout << colorizeFootnote (*f) << std::endl;
|
std::cout << colorizeFootnote (*f) << std::endl;
|
||||||
else
|
else
|
||||||
std::cout << *f << std::endl;
|
std::cout << *f << std::endl;
|
||||||
|
hooks.trigger ("post-footnote");
|
||||||
|
|
||||||
|
hooks.trigger ("pre-exit");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,8 +199,11 @@ int Context::run ()
|
||||||
int Context::dispatch (std::string &out)
|
int Context::dispatch (std::string &out)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
Timer t ("Context::dispatch");
|
Timer t ("Context::dispatch");
|
||||||
|
|
||||||
|
hooks.trigger ("pre-dispatch");
|
||||||
|
|
||||||
// TODO Just look at this thing. It cries out for a dispatch table.
|
// TODO Just look at this thing. It cries out for a dispatch table.
|
||||||
if (cmd.command == "projects") { rc = handleProjects (out); }
|
if (cmd.command == "projects") { rc = handleProjects (out); }
|
||||||
else if (cmd.command == "tags") { rc = handleTags (out); }
|
else if (cmd.command == "tags") { rc = handleTags (out); }
|
||||||
|
@ -239,6 +254,7 @@ int Context::dispatch (std::string &out)
|
||||||
if (cmd.isWriteCommand () && !inShadow)
|
if (cmd.isWriteCommand () && !inShadow)
|
||||||
shadow ();
|
shadow ();
|
||||||
|
|
||||||
|
hooks.trigger ("post-dispatch");
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,8 +373,6 @@ void Context::loadCorrectConfigFile ()
|
||||||
"Could not read home directory from the passwd file."));
|
"Could not read home directory from the passwd file."));
|
||||||
|
|
||||||
std::string home = pw->pw_dir;
|
std::string home = pw->pw_dir;
|
||||||
// std::string rc = home + "/.taskrc";
|
|
||||||
// std::string data = home + "/.task";
|
|
||||||
File rc (home + "/.taskrc");
|
File rc (home + "/.taskrc");
|
||||||
Directory data (home + "./task");
|
Directory data (home + "./task");
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "Task.h"
|
#include "Task.h"
|
||||||
#include "TDB.h"
|
#include "TDB.h"
|
||||||
#include "StringTable.h"
|
#include "StringTable.h"
|
||||||
|
#include "Hooks.h"
|
||||||
|
|
||||||
class Context
|
class Context
|
||||||
{
|
{
|
||||||
|
@ -90,6 +91,7 @@ public:
|
||||||
std::map <std::string, std::string> aliases;
|
std::map <std::string, std::string> aliases;
|
||||||
std::vector <std::string> tagAdditions;
|
std::vector <std::string> tagAdditions;
|
||||||
std::vector <std::string> tagRemovals;
|
std::vector <std::string> tagRemovals;
|
||||||
|
Hooks hooks;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector <std::string> headers;
|
std::vector <std::string> headers;
|
||||||
|
|
218
src/Hooks.cpp
Normal file
218
src/Hooks.cpp
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// task - a command line task list manager.
|
||||||
|
//
|
||||||
|
// Copyright 2006 - 2010, Paul Beckingham.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 2 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, write to the
|
||||||
|
//
|
||||||
|
// Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor,
|
||||||
|
// Boston, MA
|
||||||
|
// 02110-1301
|
||||||
|
// USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "Context.h"
|
||||||
|
#include "Hooks.h"
|
||||||
|
|
||||||
|
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 ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Hooks::~Hooks ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void Hooks::initialize ()
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LIBLUA
|
||||||
|
api.initialize ();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO Enumerate all hooks, and tell API about the script files it must load
|
||||||
|
// in order to call them. Note that API will perform a deferred read,
|
||||||
|
// which means that if it isn't called, a script will not be loaded.
|
||||||
|
|
||||||
|
std::vector <std::string> vars;
|
||||||
|
context.config.all (vars);
|
||||||
|
|
||||||
|
std::vector <std::string>::iterator it;
|
||||||
|
for (it = vars.begin (); it != vars.end (); ++it)
|
||||||
|
{
|
||||||
|
std::string type;
|
||||||
|
std::string name;
|
||||||
|
std::string value;
|
||||||
|
|
||||||
|
// "<type>.<name>"
|
||||||
|
Nibbler n (*it);
|
||||||
|
if (n.getUntil ('.', type) &&
|
||||||
|
type == "hook" &&
|
||||||
|
n.skip ('.') &&
|
||||||
|
n.getUntilEOS (name))
|
||||||
|
{
|
||||||
|
std::string value = context.config.get (*it);
|
||||||
|
Nibbler n (value);
|
||||||
|
|
||||||
|
// <path>:<function> [, ...]
|
||||||
|
while (!n.depleted ())
|
||||||
|
{
|
||||||
|
std::string file;
|
||||||
|
std::string function;
|
||||||
|
if (n.getUntil (':', file) &&
|
||||||
|
n.skip (':') &&
|
||||||
|
n.getUntil (',', function))
|
||||||
|
{
|
||||||
|
context.debug (std::string ("Event '") + name + "' hooked by " + file + ", function " + function);
|
||||||
|
Hook h (name, Path::expand (file), function);
|
||||||
|
all.push_back (h);
|
||||||
|
|
||||||
|
(void) n.skip (',');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::string ("Malformed hook definition '") + *it + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void Hooks::setTaskId (int id)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LIBLUA
|
||||||
|
task_id = id;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool Hooks::trigger (const std::string& event)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LIBLUA
|
||||||
|
std::vector <Hook>::iterator it;
|
||||||
|
for (it = all.begin (); it != all.end (); ++it)
|
||||||
|
{
|
||||||
|
if (it->event == event)
|
||||||
|
{
|
||||||
|
bool rc = true;
|
||||||
|
std::string type;
|
||||||
|
if (eventType (event, type))
|
||||||
|
{
|
||||||
|
context.debug (std::string ("Event ") + event + " triggered");
|
||||||
|
|
||||||
|
// 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, task_id);
|
||||||
|
else if (type == "field") rc = api.callFieldHook (it->file, it->function, "field", "value");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::string ("Unrecognized hook event '") + event + "'";
|
||||||
|
|
||||||
|
// If any hook returns false, stop.
|
||||||
|
if (!rc)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool Hooks::eventType (const std::string& event, std::string& type)
|
||||||
|
{
|
||||||
|
if (event == "post-start" ||
|
||||||
|
event == "pre-exit" ||
|
||||||
|
event == "pre-debug" || event == "post-debug" ||
|
||||||
|
event == "pre-header" || event == "post-header" ||
|
||||||
|
event == "pre-footnote" || event == "post-footnote" ||
|
||||||
|
event == "pre-output" || event == "post-output" ||
|
||||||
|
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-add-command" || event == "post-add-command" ||
|
||||||
|
event == "pre-delete-command" || event == "post-delete-command" ||
|
||||||
|
event == "pre-info-command" || event == "post-info-command")
|
||||||
|
{
|
||||||
|
type = "program";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (event == "?")
|
||||||
|
{
|
||||||
|
type = "list";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (event == "pre-tag" || event == "post-tag" ||
|
||||||
|
event == "pre-detag" || event == "post-detag" ||
|
||||||
|
event == "pre-delete" || event == "post-delete" ||
|
||||||
|
event == "pre-completed" || event == "post-completed")
|
||||||
|
{
|
||||||
|
type = "task";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (event == "?")
|
||||||
|
{
|
||||||
|
type = "field";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
80
src/Hooks.h
Normal file
80
src/Hooks.h
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// task - a command line task list manager.
|
||||||
|
//
|
||||||
|
// Copyright 2006 - 2010, Paul Beckingham.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 2 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, write to the
|
||||||
|
//
|
||||||
|
// Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor,
|
||||||
|
// Boston, MA
|
||||||
|
// 02110-1301
|
||||||
|
// USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#ifndef INCLUDED_HOOKS
|
||||||
|
#define INCLUDED_HOOKS
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include "API.h"
|
||||||
|
#include "auto.h"
|
||||||
|
|
||||||
|
// Hook class representing a single hook, which is just a three-way map.
|
||||||
|
class Hook
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Hook ();
|
||||||
|
Hook (const std::string&, const std::string&, const std::string&);
|
||||||
|
Hook (const Hook&);
|
||||||
|
Hook& operator= (const Hook&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string event;
|
||||||
|
std::string file;
|
||||||
|
std::string function;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hooks class for managing the loading and calling of hook functions.
|
||||||
|
class Hooks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Hooks (); // Default constructor
|
||||||
|
~Hooks (); // Destructor
|
||||||
|
Hooks (const Hooks&); // Deliberately unimplemented
|
||||||
|
Hooks& operator= (const Hooks&); // Deliberately unimplemented
|
||||||
|
|
||||||
|
void initialize ();
|
||||||
|
|
||||||
|
void setTaskId (int);
|
||||||
|
// void setField (const std::string&, const std::string&);
|
||||||
|
// void setTaskList (const std::vector <int>&);
|
||||||
|
bool trigger (const std::string&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool eventType (const std::string&, std::string&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef HAVE_LIBLUA
|
||||||
|
API api;
|
||||||
|
#endif
|
||||||
|
std::vector <Hook> all; // All current hooks.
|
||||||
|
#ifdef HAVE_LIBLUA
|
||||||
|
int task_id;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
|
@ -1,14 +1,16 @@
|
||||||
bin_PROGRAMS = task
|
bin_PROGRAMS = task
|
||||||
task_SOURCES = Att.cpp Cmd.cpp Color.cpp Config.cpp Context.cpp Date.cpp \
|
task_SHORTNAME = t
|
||||||
Directory.cpp Duration.cpp File.cpp Filter.cpp Grid.cpp \
|
task_SOURCES = API.cpp Att.cpp Cmd.cpp Color.cpp Config.cpp Context.cpp \
|
||||||
Keymap.cpp Location.cpp Nibbler.cpp Path.cpp Permission.cpp \
|
Date.cpp Directory.cpp Duration.cpp File.cpp Filter.cpp \
|
||||||
Record.cpp Sequence.cpp StringTable.cpp Subst.cpp TDB.cpp \
|
Grid.cpp Hooks.cpp Keymap.cpp Location.cpp Nibbler.cpp \
|
||||||
Table.cpp Task.cpp Timer.cpp command.cpp custom.cpp edit.cpp \
|
Path.cpp Permission.cpp Record.cpp Sequence.cpp \
|
||||||
import.cpp interactive.cpp main.cpp recur.cpp report.cpp \
|
StringTable.cpp Subst.cpp TDB.cpp Table.cpp Task.cpp Timer.cpp \
|
||||||
rules.cpp text.cpp util.cpp \
|
command.cpp custom.cpp edit.cpp import.cpp interactive.cpp \
|
||||||
Att.h Cmd.h Color.h Config.h Context.h Date.h Directory.h \
|
main.cpp recur.cpp report.cpp rules.cpp text.cpp util.cpp \
|
||||||
Duration.h File.h Filter.h Grid.h Keymap.h Location.h \
|
API.h Att.h Cmd.h Color.h Config.h Context.h Date.h \
|
||||||
Nibbler.h Path.h Permission.h Record.h Sequence.h \
|
Directory.h Duration.h File.h Filter.h Grid.h Hooks.h Keymap.h \
|
||||||
|
Location.h Nibbler.h Path.h Permission.h Record.h Sequence.h \
|
||||||
StringTable.h Subst.h TDB.h Table.h Task.h Timer.h i18n.h \
|
StringTable.h Subst.h TDB.h Table.h Task.h Timer.h i18n.h \
|
||||||
main.h text.h util.h
|
main.h text.h util.h
|
||||||
|
task_CPPFLAGS=$(LUA_CFLAGS)
|
||||||
|
task_LDFLAGS=$(LUA_LFLAGS)
|
||||||
|
|
69
src/TDB.cpp
69
src/TDB.cpp
|
@ -121,48 +121,49 @@ void TDB::location (const std::string& path)
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void TDB::lock (bool lockFile /* = true */)
|
void TDB::lock (bool lockFile /* = true */)
|
||||||
{
|
{
|
||||||
mLock = lockFile;
|
if (context.hooks.trigger ("pre-file-lock"))
|
||||||
|
|
||||||
mPending.clear ();
|
|
||||||
mNew.clear ();
|
|
||||||
mPending.clear ();
|
|
||||||
|
|
||||||
foreach (location, mLocations)
|
|
||||||
{
|
{
|
||||||
location->pending = openAndLock (location->path + "/pending.data");
|
mLock = lockFile;
|
||||||
location->completed = openAndLock (location->path + "/completed.data");
|
|
||||||
location->undo = openAndLock (location->path + "/undo.data");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ()
|
void TDB::unlock ()
|
||||||
{
|
{
|
||||||
if (mAllOpenAndLocked)
|
mPending.clear ();
|
||||||
|
mNew.clear ();
|
||||||
|
mModified.clear ();
|
||||||
|
|
||||||
|
foreach (location, mLocations)
|
||||||
{
|
{
|
||||||
mPending.clear ();
|
fflush (location->pending);
|
||||||
mNew.clear ();
|
fclose (location->pending);
|
||||||
mModified.clear ();
|
location->pending = NULL;
|
||||||
|
|
||||||
foreach (location, mLocations)
|
fflush (location->completed);
|
||||||
{
|
fclose (location->completed);
|
||||||
fflush (location->pending);
|
location->completed = NULL;
|
||||||
fclose (location->pending);
|
|
||||||
location->pending = NULL;
|
|
||||||
|
|
||||||
fflush (location->completed);
|
fflush (location->undo);
|
||||||
fclose (location->completed);
|
fclose (location->undo);
|
||||||
location->completed = NULL;
|
location->completed = NULL;
|
||||||
|
|
||||||
fflush (location->undo);
|
|
||||||
fclose (location->undo);
|
|
||||||
location->completed = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mAllOpenAndLocked = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mAllOpenAndLocked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -343,6 +344,7 @@ void TDB::update (const Task& task)
|
||||||
int TDB::commit ()
|
int TDB::commit ()
|
||||||
{
|
{
|
||||||
Timer t ("TDB::commit");
|
Timer t ("TDB::commit");
|
||||||
|
context.hooks.trigger ("pre-gc");
|
||||||
|
|
||||||
int quantity = mNew.size () + mModified.size ();
|
int quantity = mNew.size () + mModified.size ();
|
||||||
|
|
||||||
|
@ -362,6 +364,7 @@ int TDB::commit ()
|
||||||
writeUndo (*task, mLocations[0].undo);
|
writeUndo (*task, mLocations[0].undo);
|
||||||
|
|
||||||
mNew.clear ();
|
mNew.clear ();
|
||||||
|
context.hooks.trigger ("post-gc");
|
||||||
return quantity;
|
return quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,6 +411,7 @@ int TDB::commit ()
|
||||||
mNew.clear ();
|
mNew.clear ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.hooks.trigger ("post-gc");
|
||||||
return quantity;
|
return quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,6 +510,7 @@ int TDB::nextId ()
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void TDB::undo ()
|
void TDB::undo ()
|
||||||
{
|
{
|
||||||
|
context.hooks.trigger ("pre-undo");
|
||||||
Directory location (context.config.get ("data.location"));
|
Directory location (context.config.get ("data.location"));
|
||||||
|
|
||||||
std::string undoFile = location.data + "/undo.data";
|
std::string undoFile = location.data + "/undo.data";
|
||||||
|
@ -670,6 +675,7 @@ void TDB::undo ()
|
||||||
// Rewrite files.
|
// Rewrite files.
|
||||||
File::write (pendingFile, p);
|
File::write (pendingFile, p);
|
||||||
File::write (undoFile, u);
|
File::write (undoFile, u);
|
||||||
|
context.hooks.trigger ("post-undo");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -708,6 +714,7 @@ void TDB::undo ()
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Undo complete." << std::endl;
|
std::cout << "Undo complete." << std::endl;
|
||||||
|
context.hooks.trigger ("post-undo");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include "Context.h"
|
||||||
#include "Nibbler.h"
|
#include "Nibbler.h"
|
||||||
#include "Date.h"
|
#include "Date.h"
|
||||||
#include "Duration.h"
|
#include "Duration.h"
|
||||||
|
@ -34,6 +35,8 @@
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
extern Context context;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
Task::Task ()
|
Task::Task ()
|
||||||
: id (0)
|
: id (0)
|
||||||
|
|
333
src/command.cpp
333
src/command.cpp
|
@ -52,66 +52,71 @@ extern Context context;
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
int handleAdd (std::string &outs)
|
int handleAdd (std::string &outs)
|
||||||
{
|
{
|
||||||
std::stringstream out;
|
if (context.hooks.trigger ("pre-add-command"))
|
||||||
|
|
||||||
context.task.set ("uuid", uuid ());
|
|
||||||
context.task.setEntry ();
|
|
||||||
|
|
||||||
// Recurring tasks get a special status.
|
|
||||||
if (context.task.has ("due") &&
|
|
||||||
context.task.has ("recur"))
|
|
||||||
{
|
{
|
||||||
context.task.setStatus (Task::recurring);
|
std::stringstream out;
|
||||||
context.task.set ("mask", "");
|
|
||||||
}
|
|
||||||
else if (context.task.has ("wait"))
|
|
||||||
context.task.setStatus (Task::waiting);
|
|
||||||
else
|
|
||||||
context.task.setStatus (Task::pending);
|
|
||||||
|
|
||||||
// Override with default.project, if not specified.
|
context.task.set ("uuid", uuid ());
|
||||||
if (context.task.get ("project") == "")
|
context.task.setEntry ();
|
||||||
context.task.set ("project", context.config.get ("default.project"));
|
|
||||||
|
|
||||||
// Override with default.priority, if not specified.
|
// Recurring tasks get a special status.
|
||||||
if (context.task.get ("priority") == "")
|
if (context.task.has ("due") &&
|
||||||
{
|
context.task.has ("recur"))
|
||||||
std::string defaultPriority = context.config.get ("default.priority");
|
{
|
||||||
if (Att::validNameValue ("priority", "", defaultPriority))
|
context.task.setStatus (Task::recurring);
|
||||||
context.task.set ("priority", defaultPriority);
|
context.task.set ("mask", "");
|
||||||
}
|
}
|
||||||
|
else if (context.task.has ("wait"))
|
||||||
|
context.task.setStatus (Task::waiting);
|
||||||
|
else
|
||||||
|
context.task.setStatus (Task::pending);
|
||||||
|
|
||||||
// Include tags.
|
// Override with default.project, if not specified.
|
||||||
foreach (tag, context.tagAdditions)
|
if (context.task.get ("project") == "")
|
||||||
context.task.addTag (*tag);
|
context.task.set ("project", context.config.get ("default.project"));
|
||||||
|
|
||||||
// Perform some logical consistency checks.
|
// Override with default.priority, if not specified.
|
||||||
if (context.task.has ("recur") &&
|
if (context.task.get ("priority") == "")
|
||||||
!context.task.has ("due"))
|
{
|
||||||
throw std::string ("You cannot specify a recurring task without a due date.");
|
std::string defaultPriority = context.config.get ("default.priority");
|
||||||
|
if (Att::validNameValue ("priority", "", defaultPriority))
|
||||||
|
context.task.set ("priority", defaultPriority);
|
||||||
|
}
|
||||||
|
|
||||||
if (context.task.has ("until") &&
|
// Include tags.
|
||||||
!context.task.has ("recur"))
|
foreach (tag, context.tagAdditions)
|
||||||
throw std::string ("You cannot specify an until date for a non-recurring task.");
|
context.task.addTag (*tag);
|
||||||
|
|
||||||
// Only valid tasks can be added.
|
// Perform some logical consistency checks.
|
||||||
context.task.validate ();
|
if (context.task.has ("recur") &&
|
||||||
|
!context.task.has ("due"))
|
||||||
|
throw std::string ("You cannot specify a recurring task without a due date.");
|
||||||
|
|
||||||
context.tdb.lock (context.config.getBoolean ("locking"));
|
if (context.task.has ("until") &&
|
||||||
context.tdb.add (context.task);
|
!context.task.has ("recur"))
|
||||||
|
throw std::string ("You cannot specify an until date for a non-recurring task.");
|
||||||
|
|
||||||
|
// Only valid tasks can be added.
|
||||||
|
context.task.validate ();
|
||||||
|
|
||||||
|
context.tdb.lock (context.config.getBoolean ("locking"));
|
||||||
|
context.tdb.add (context.task);
|
||||||
|
|
||||||
#ifdef FEATURE_NEW_ID
|
#ifdef FEATURE_NEW_ID
|
||||||
// All this, just for an id number.
|
// All this, just for an id number.
|
||||||
std::vector <Task> all;
|
std::vector <Task> all;
|
||||||
Filter none;
|
Filter none;
|
||||||
context.tdb.loadPending (all, none);
|
context.tdb.loadPending (all, none);
|
||||||
out << "Created task " << context.tdb.nextId () << std::endl;
|
out << "Created task " << context.tdb.nextId () << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
context.tdb.commit ();
|
context.tdb.commit ();
|
||||||
context.tdb.unlock ();
|
context.tdb.unlock ();
|
||||||
|
|
||||||
|
outs = out.str ();
|
||||||
|
context.hooks.trigger ("post-add-command");
|
||||||
|
}
|
||||||
|
|
||||||
outs = out.str ();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,8 +495,12 @@ int handleVersion (std::string &outs)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
<< std::endl
|
<< std::endl
|
||||||
<< "Copyright (C) 2006 - 2010, P. Beckingham, F. Hernandez."
|
<< "Copyright (C) 2006 - 2010 P. Beckingham, F. Hernandez."
|
||||||
<< std::endl
|
<< std::endl
|
||||||
|
#ifdef HAVE_LIBLUA
|
||||||
|
<< "Portions of this software Copyright (C) 1994 – 2008 Lua.org, PUC-Rio."
|
||||||
|
<< std::endl
|
||||||
|
#endif
|
||||||
<< disclaimer.render ()
|
<< disclaimer.render ()
|
||||||
<< link.render ()
|
<< link.render ()
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
@ -711,6 +720,7 @@ int handleConfig (std::string &outs)
|
||||||
out << context.config.checkForDeprecatedColor ();
|
out << context.config.checkForDeprecatedColor ();
|
||||||
// TODO Check for referenced but missing theme files.
|
// TODO Check for referenced but missing theme files.
|
||||||
// TODO Check for referenced but missing string files.
|
// TODO Check for referenced but missing string files.
|
||||||
|
// TODO Check for referenced but missing hook scripts.
|
||||||
|
|
||||||
// Check for bad values in rc.annotations.
|
// Check for bad values in rc.annotations.
|
||||||
std::string annotations = context.config.get ("annotations");
|
std::string annotations = context.config.get ("annotations");
|
||||||
|
@ -784,104 +794,116 @@ int handleConfig (std::string &outs)
|
||||||
int handleDelete (std::string &outs)
|
int handleDelete (std::string &outs)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
std::stringstream out;
|
|
||||||
|
|
||||||
context.disallowModification ();
|
if (context.hooks.trigger ("pre-delete-command"))
|
||||||
|
|
||||||
std::vector <Task> tasks;
|
|
||||||
context.tdb.lock (context.config.getBoolean ("locking"));
|
|
||||||
Filter filter;
|
|
||||||
context.tdb.loadPending (tasks, filter);
|
|
||||||
|
|
||||||
// Filter sequence.
|
|
||||||
std::vector <Task> all = tasks;
|
|
||||||
context.filter.applySequence (tasks, context.sequence);
|
|
||||||
|
|
||||||
// Determine the end date.
|
|
||||||
char endTime[16];
|
|
||||||
sprintf (endTime, "%u", (unsigned int) time (NULL));
|
|
||||||
|
|
||||||
foreach (task, tasks)
|
|
||||||
{
|
{
|
||||||
std::stringstream question;
|
std::stringstream out;
|
||||||
question << "Permanently delete task "
|
|
||||||
<< task->id
|
|
||||||
<< " '"
|
|
||||||
<< task->get ("description")
|
|
||||||
<< "'?";
|
|
||||||
|
|
||||||
if (!context.config.getBoolean ("confirmation") || confirm (question.str ()))
|
context.disallowModification ();
|
||||||
|
|
||||||
|
std::vector <Task> tasks;
|
||||||
|
context.tdb.lock (context.config.getBoolean ("locking"));
|
||||||
|
Filter filter;
|
||||||
|
context.tdb.loadPending (tasks, filter);
|
||||||
|
|
||||||
|
// Filter sequence.
|
||||||
|
std::vector <Task> all = tasks;
|
||||||
|
context.filter.applySequence (tasks, context.sequence);
|
||||||
|
|
||||||
|
// Determine the end date.
|
||||||
|
char endTime[16];
|
||||||
|
sprintf (endTime, "%u", (unsigned int) time (NULL));
|
||||||
|
|
||||||
|
foreach (task, tasks)
|
||||||
{
|
{
|
||||||
// Check for the more complex case of a recurring task. If this is a
|
context.hooks.setTaskId (task->id);
|
||||||
// recurring task, get confirmation to delete them all.
|
if (context.hooks.trigger ("pre-delete"))
|
||||||
std::string parent = task->get ("parent");
|
|
||||||
if (parent != "")
|
|
||||||
{
|
{
|
||||||
if (confirm ("This is a recurring task. Do you want to delete all pending recurrences of this same task?"))
|
std::stringstream question;
|
||||||
{
|
question << "Permanently delete task "
|
||||||
// Scan all pending tasks for siblings of this task, and the parent
|
<< task->id
|
||||||
// itself, and delete them.
|
<< " '"
|
||||||
foreach (sibling, all)
|
<< task->get ("description")
|
||||||
{
|
<< "'?";
|
||||||
if (sibling->get ("parent") == parent ||
|
|
||||||
sibling->get ("uuid") == parent)
|
|
||||||
{
|
|
||||||
sibling->setStatus (Task::deleted);
|
|
||||||
sibling->set ("end", endTime);
|
|
||||||
context.tdb.update (*sibling);
|
|
||||||
|
|
||||||
if (context.config.getBoolean ("echo.command"))
|
if (!context.config.getBoolean ("confirmation") || confirm (question.str ()))
|
||||||
out << "Deleting recurring task "
|
{
|
||||||
<< sibling->id
|
// Check for the more complex case of a recurring task. If this is a
|
||||||
<< " '"
|
// recurring task, get confirmation to delete them all.
|
||||||
<< sibling->get ("description")
|
std::string parent = task->get ("parent");
|
||||||
<< "'"
|
if (parent != "")
|
||||||
<< std::endl;
|
{
|
||||||
|
if (confirm ("This is a recurring task. Do you want to delete all pending recurrences of this same task?"))
|
||||||
|
{
|
||||||
|
// Scan all pending tasks for siblings of this task, and the parent
|
||||||
|
// itself, and delete them.
|
||||||
|
foreach (sibling, all)
|
||||||
|
{
|
||||||
|
if (sibling->get ("parent") == parent ||
|
||||||
|
sibling->get ("uuid") == parent)
|
||||||
|
{
|
||||||
|
sibling->setStatus (Task::deleted);
|
||||||
|
sibling->set ("end", endTime);
|
||||||
|
context.tdb.update (*sibling);
|
||||||
|
|
||||||
|
if (context.config.getBoolean ("echo.command"))
|
||||||
|
out << "Deleting recurring task "
|
||||||
|
<< sibling->id
|
||||||
|
<< " '"
|
||||||
|
<< sibling->get ("description")
|
||||||
|
<< "'"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Update mask in parent.
|
||||||
|
task->setStatus (Task::deleted);
|
||||||
|
updateRecurrenceMask (all, *task);
|
||||||
|
|
||||||
|
task->set ("end", endTime);
|
||||||
|
context.tdb.update (*task);
|
||||||
|
|
||||||
|
out << "Deleting recurring task "
|
||||||
|
<< task->id
|
||||||
|
<< " '"
|
||||||
|
<< task->get ("description")
|
||||||
|
<< "'"
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
task->setStatus (Task::deleted);
|
||||||
|
task->set ("end", endTime);
|
||||||
|
context.tdb.update (*task);
|
||||||
|
|
||||||
|
if (context.config.getBoolean ("echo.command"))
|
||||||
|
out << "Deleting task "
|
||||||
|
<< task->id
|
||||||
|
<< " '"
|
||||||
|
<< task->get ("description")
|
||||||
|
<< "'"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
out << "Task not deleted." << std::endl;
|
||||||
// Update mask in parent.
|
rc = 1;
|
||||||
task->setStatus (Task::deleted);
|
|
||||||
updateRecurrenceMask (all, *task);
|
|
||||||
|
|
||||||
task->set ("end", endTime);
|
|
||||||
context.tdb.update (*task);
|
|
||||||
|
|
||||||
out << "Deleting recurring task "
|
|
||||||
<< task->id
|
|
||||||
<< " '"
|
|
||||||
<< task->get ("description")
|
|
||||||
<< "'"
|
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
task->setStatus (Task::deleted);
|
|
||||||
task->set ("end", endTime);
|
|
||||||
context.tdb.update (*task);
|
|
||||||
|
|
||||||
if (context.config.getBoolean ("echo.command"))
|
context.hooks.trigger ("post-delete");
|
||||||
out << "Deleting task "
|
|
||||||
<< task->id
|
|
||||||
<< " '"
|
|
||||||
<< task->get ("description")
|
|
||||||
<< "'"
|
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
out << "Task not deleted." << std::endl;
|
context.tdb.commit ();
|
||||||
rc = 1;
|
context.tdb.unlock ();
|
||||||
}
|
|
||||||
|
outs = out.str ();
|
||||||
|
context.hooks.trigger ("post-delete-command");
|
||||||
}
|
}
|
||||||
|
|
||||||
context.tdb.commit ();
|
|
||||||
context.tdb.unlock ();
|
|
||||||
|
|
||||||
outs = out.str ();
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1036,20 +1058,28 @@ int handleDone (std::string &outs)
|
||||||
|
|
||||||
if (taskDiff (before, *task))
|
if (taskDiff (before, *task))
|
||||||
{
|
{
|
||||||
if (permission.confirmed (before, taskDifferences (before, *task) + "Proceed with change?"))
|
context.hooks.setTaskId (task->id);
|
||||||
|
if (context.hooks.trigger ("pre-completed"))
|
||||||
{
|
{
|
||||||
context.tdb.update (*task);
|
if (permission.confirmed (before, taskDifferences (before, *task) + "Proceed with change?"))
|
||||||
|
{
|
||||||
|
context.tdb.update (*task);
|
||||||
|
|
||||||
if (context.config.getBoolean ("echo.command"))
|
if (context.config.getBoolean ("echo.command"))
|
||||||
out << "Completed "
|
out << "Completed "
|
||||||
<< task->id
|
<< task->id
|
||||||
<< " '"
|
<< " '"
|
||||||
<< task->get ("description")
|
<< task->get ("description")
|
||||||
<< "'"
|
<< "'"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
++count;
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.hooks.trigger ("post-completed");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRecurrenceMask (all, *task);
|
updateRecurrenceMask (all, *task);
|
||||||
|
@ -1066,7 +1096,9 @@ int handleDone (std::string &outs)
|
||||||
rc = 1;
|
rc = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.tdb.commit ();
|
if (count)
|
||||||
|
context.tdb.commit ();
|
||||||
|
|
||||||
context.tdb.unlock ();
|
context.tdb.unlock ();
|
||||||
|
|
||||||
if (context.config.getBoolean ("echo.command"))
|
if (context.config.getBoolean ("echo.command"))
|
||||||
|
@ -1744,20 +1776,29 @@ int deltaDescription (Task& task)
|
||||||
int deltaTags (Task& task)
|
int deltaTags (Task& task)
|
||||||
{
|
{
|
||||||
int changes = 0;
|
int changes = 0;
|
||||||
|
context.hooks.setTaskId (task.id);
|
||||||
|
|
||||||
// Apply or remove tags, if any.
|
// Apply or remove tags, if any.
|
||||||
std::vector <std::string> tags;
|
std::vector <std::string> tags;
|
||||||
context.task.getTags (tags);
|
context.task.getTags (tags);
|
||||||
foreach (tag, tags)
|
foreach (tag, tags)
|
||||||
{
|
{
|
||||||
task.addTag (*tag);
|
if (context.hooks.trigger ("pre-tag"))
|
||||||
++changes;
|
{
|
||||||
|
task.addTag (*tag);
|
||||||
|
++changes;
|
||||||
|
context.hooks.trigger ("post-tag");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (tag, context.tagRemovals)
|
foreach (tag, context.tagRemovals)
|
||||||
{
|
{
|
||||||
task.removeTag (*tag);
|
if (context.hooks.trigger ("pre-detag"))
|
||||||
++changes;
|
{
|
||||||
|
task.removeTag (*tag);
|
||||||
|
++changes;
|
||||||
|
context.hooks.trigger ("post-detag");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return changes;
|
return changes;
|
||||||
|
|
|
@ -586,39 +586,13 @@ int runCustomReport (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now auto colorize all rows.
|
// Now auto colorize all rows.
|
||||||
std::string due;
|
if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor"))
|
||||||
Color color_due (context.config.get ("color.due"));
|
|
||||||
Color color_overdue (context.config.get ("color.overdue"));
|
|
||||||
|
|
||||||
bool imminent;
|
|
||||||
bool overdue;
|
|
||||||
for (unsigned int row = 0; row < tasks.size (); ++row)
|
|
||||||
{
|
{
|
||||||
imminent = false;
|
for (unsigned int row = 0; row < tasks.size (); ++row)
|
||||||
overdue = false;
|
|
||||||
due = tasks[row].get ("due");
|
|
||||||
if (due.length ())
|
|
||||||
{
|
|
||||||
switch (getDueState (due))
|
|
||||||
{
|
|
||||||
case 2: overdue = true; break;
|
|
||||||
case 1: imminent = true; break;
|
|
||||||
case 0:
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor"))
|
|
||||||
{
|
{
|
||||||
Color c (tasks[row].get ("fg") + " " + tasks[row].get ("bg"));
|
Color c (tasks[row].get ("fg") + " " + tasks[row].get ("bg"));
|
||||||
autoColorize (tasks[row], c);
|
autoColorize (tasks[row], c);
|
||||||
table.setRowColor (row, c);
|
table.setRowColor (row, c);
|
||||||
|
|
||||||
if (dueColumn != -1)
|
|
||||||
{
|
|
||||||
c.blend (overdue ? color_overdue : color_due);
|
|
||||||
table.setCellColor (row, columnCount, c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
350
src/report.cpp
350
src/report.cpp
|
@ -300,231 +300,219 @@ int longUsage (std::string &outs)
|
||||||
int handleInfo (std::string &outs)
|
int handleInfo (std::string &outs)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
// Get all the tasks.
|
|
||||||
std::vector <Task> tasks;
|
|
||||||
context.tdb.lock (context.config.getBoolean ("locking"));
|
|
||||||
handleRecurrence ();
|
|
||||||
context.tdb.loadPending (tasks, context.filter);
|
|
||||||
context.tdb.commit ();
|
|
||||||
context.tdb.unlock ();
|
|
||||||
|
|
||||||
// Filter sequence.
|
if (context.hooks.trigger ("pre-info-command"))
|
||||||
context.filter.applySequence (tasks, context.sequence);
|
|
||||||
|
|
||||||
// Find the task.
|
|
||||||
std::stringstream out;
|
|
||||||
foreach (task, tasks)
|
|
||||||
{
|
{
|
||||||
Table table;
|
// Get all the tasks.
|
||||||
table.setTableWidth (context.getWidth ());
|
std::vector <Task> tasks;
|
||||||
table.setDateFormat (context.config.get ("dateformat"));
|
context.tdb.lock (context.config.getBoolean ("locking"));
|
||||||
|
handleRecurrence ();
|
||||||
|
context.tdb.loadPending (tasks, context.filter);
|
||||||
|
context.tdb.commit ();
|
||||||
|
context.tdb.unlock ();
|
||||||
|
|
||||||
table.addColumn ("Name");
|
// Filter sequence.
|
||||||
table.addColumn ("Value");
|
context.filter.applySequence (tasks, context.sequence);
|
||||||
|
|
||||||
if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) &&
|
// Find the task.
|
||||||
context.config.getBoolean ("fontunderline"))
|
std::stringstream out;
|
||||||
|
foreach (task, tasks)
|
||||||
{
|
{
|
||||||
table.setColumnUnderline (0);
|
Table table;
|
||||||
table.setColumnUnderline (1);
|
table.setTableWidth (context.getWidth ());
|
||||||
}
|
table.setDateFormat (context.config.get ("dateformat"));
|
||||||
else
|
|
||||||
table.setTableDashedUnderline ();
|
|
||||||
|
|
||||||
table.setColumnWidth (0, Table::minimum);
|
table.addColumn ("Name");
|
||||||
table.setColumnWidth (1, Table::flexible);
|
table.addColumn ("Value");
|
||||||
|
|
||||||
table.setColumnJustification (0, Table::left);
|
if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) &&
|
||||||
table.setColumnJustification (1, Table::left);
|
context.config.getBoolean ("fontunderline"))
|
||||||
Date now;
|
{
|
||||||
|
table.setColumnUnderline (0);
|
||||||
|
table.setColumnUnderline (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
table.setTableDashedUnderline ();
|
||||||
|
|
||||||
int row = table.addRow ();
|
table.setColumnWidth (0, Table::minimum);
|
||||||
table.addCell (row, 0, "ID");
|
table.setColumnWidth (1, Table::flexible);
|
||||||
table.addCell (row, 1, task->id);
|
|
||||||
|
|
||||||
std::string status = ucFirst (Task::statusToText (task->getStatus ()));
|
table.setColumnJustification (0, Table::left);
|
||||||
|
table.setColumnJustification (1, Table::left);
|
||||||
|
Date now;
|
||||||
|
|
||||||
if (task->has ("parent"))
|
int row = table.addRow ();
|
||||||
status += " (Recurring)";
|
table.addCell (row, 0, "ID");
|
||||||
|
table.addCell (row, 1, task->id);
|
||||||
|
|
||||||
row = table.addRow ();
|
std::string status = ucFirst (Task::statusToText (task->getStatus ()));
|
||||||
table.addCell (row, 0, "Status");
|
|
||||||
table.addCell (row, 1, status);
|
|
||||||
|
|
||||||
row = table.addRow ();
|
|
||||||
table.addCell (row, 0, "Description");
|
|
||||||
table.addCell (row, 1, getFullDescription (*task, "info"));
|
|
||||||
|
|
||||||
if (task->has ("project"))
|
|
||||||
{
|
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Project");
|
table.addCell (row, 0, "Description");
|
||||||
table.addCell (row, 1, task->get ("project"));
|
table.addCell (row, 1, getFullDescription (*task, "info"));
|
||||||
}
|
|
||||||
|
|
||||||
if (task->has ("priority"))
|
|
||||||
{
|
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Priority");
|
table.addCell (row, 0, "Status");
|
||||||
table.addCell (row, 1, task->get ("priority"));
|
table.addCell (row, 1, status);
|
||||||
}
|
|
||||||
|
|
||||||
if (task->getStatus () == Task::recurring ||
|
if (task->has ("project"))
|
||||||
task->has ("parent"))
|
|
||||||
{
|
|
||||||
if (task->has ("recur"))
|
|
||||||
{
|
{
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Recurrence");
|
table.addCell (row, 0, "Project");
|
||||||
table.addCell (row, 1, task->get ("recur"));
|
table.addCell (row, 1, task->get ("project"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task->has ("until"))
|
if (task->has ("priority"))
|
||||||
{
|
{
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Recur until");
|
table.addCell (row, 0, "Priority");
|
||||||
table.addCell (row, 1, task->get ("until"));
|
table.addCell (row, 1, task->get ("priority"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task->has ("mask"))
|
if (task->getStatus () == Task::recurring ||
|
||||||
|
task->has ("parent"))
|
||||||
|
{
|
||||||
|
if (task->has ("recur"))
|
||||||
|
{
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Recurrence");
|
||||||
|
table.addCell (row, 1, task->get ("recur"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task->has ("until"))
|
||||||
|
{
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Recur until");
|
||||||
|
table.addCell (row, 1, task->get ("until"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task->has ("mask"))
|
||||||
|
{
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Mask");
|
||||||
|
table.addCell (row, 1, task->get ("mask"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task->has ("parent"))
|
||||||
|
{
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Parent task");
|
||||||
|
table.addCell (row, 1, task->get ("parent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Mask Index");
|
||||||
|
table.addCell (row, 1, task->get ("imask"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// due (colored)
|
||||||
|
if (task->has ("due"))
|
||||||
{
|
{
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Mask");
|
table.addCell (row, 0, "Due");
|
||||||
table.addCell (row, 1, task->get ("mask"));
|
|
||||||
|
Date dt (atoi (task->get ("due").c_str ()));
|
||||||
|
std::string format = context.config.get ("reportdateformat");
|
||||||
|
if (format == "")
|
||||||
|
format = context.config.get ("dateformat");
|
||||||
|
|
||||||
|
std::string due = getDueDate (*task, format);
|
||||||
|
table.addCell (row, 1, due);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task->has ("parent"))
|
// wait
|
||||||
|
if (task->has ("wait"))
|
||||||
{
|
{
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Parent task");
|
table.addCell (row, 0, "Waiting until");
|
||||||
table.addCell (row, 1, task->get ("parent"));
|
Date dt (atoi (task->get ("wait").c_str ()));
|
||||||
|
table.addCell (row, 1, dt.toString (context.config.get ("dateformat")));
|
||||||
}
|
}
|
||||||
|
|
||||||
row = table.addRow ();
|
// start
|
||||||
table.addCell (row, 0, "Mask Index");
|
if (task->has ("start"))
|
||||||
table.addCell (row, 1, task->get ("imask"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// due (colored)
|
|
||||||
bool imminent = false;
|
|
||||||
bool overdue = false;
|
|
||||||
if (task->has ("due"))
|
|
||||||
{
|
|
||||||
row = table.addRow ();
|
|
||||||
table.addCell (row, 0, "Due");
|
|
||||||
|
|
||||||
Date dt (atoi (task->get ("due").c_str ()));
|
|
||||||
std::string format = context.config.get ("dateformat.report");
|
|
||||||
if (format == "")
|
|
||||||
format = context.config.get ("dateformat");
|
|
||||||
|
|
||||||
std::string due = getDueDate (*task, format);
|
|
||||||
table.addCell (row, 1, due);
|
|
||||||
|
|
||||||
overdue = (dt < now) ? true : false;
|
|
||||||
int imminentperiod = context.config.getInteger ("due");
|
|
||||||
Date imminentDay = now + imminentperiod * 86400;
|
|
||||||
imminent = dt < imminentDay ? true : false;
|
|
||||||
|
|
||||||
if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor"))
|
|
||||||
{
|
{
|
||||||
if (overdue)
|
row = table.addRow ();
|
||||||
table.setCellColor (row, 1, Color (context.config.get ("color.overdue")));
|
table.addCell (row, 0, "Start");
|
||||||
else if (imminent)
|
Date dt (atoi (task->get ("start").c_str ()));
|
||||||
table.setCellColor (row, 1, Color (context.config.get ("color.due")));
|
table.addCell (row, 1, dt.toString (context.config.get ("dateformat")));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// wait
|
// end
|
||||||
if (task->has ("wait"))
|
if (task->has ("end"))
|
||||||
{
|
{
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "End");
|
||||||
|
Date dt (atoi (task->get ("end").c_str ()));
|
||||||
|
table.addCell (row, 1, dt.toString (context.config.get ("dateformat")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// tags ...
|
||||||
|
std::vector <std::string> tags;
|
||||||
|
task->getTags (tags);
|
||||||
|
if (tags.size ())
|
||||||
|
{
|
||||||
|
std::string allTags;
|
||||||
|
join (allTags, " ", tags);
|
||||||
|
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Tags");
|
||||||
|
table.addCell (row, 1, allTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// uuid
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Waiting until");
|
table.addCell (row, 0, "UUID");
|
||||||
Date dt (atoi (task->get ("wait").c_str ()));
|
table.addCell (row, 1, task->get ("uuid"));
|
||||||
table.addCell (row, 1, dt.toString (context.config.get ("dateformat")));
|
|
||||||
}
|
|
||||||
|
|
||||||
// start
|
// entry
|
||||||
if (task->has ("start"))
|
|
||||||
{
|
|
||||||
row = table.addRow ();
|
row = table.addRow ();
|
||||||
table.addCell (row, 0, "Start");
|
table.addCell (row, 0, "Entered");
|
||||||
Date dt (atoi (task->get ("start").c_str ()));
|
Date dt (atoi (task->get ("entry").c_str ()));
|
||||||
table.addCell (row, 1, dt.toString (context.config.get ("dateformat")));
|
std::string entry = dt.toString (context.config.get ("dateformat"));
|
||||||
|
|
||||||
|
std::string age;
|
||||||
|
std::string created = task->get ("entry");
|
||||||
|
if (created.length ())
|
||||||
|
{
|
||||||
|
Date dt (atoi (created.c_str ()));
|
||||||
|
age = formatSeconds ((time_t) (now - dt));
|
||||||
|
}
|
||||||
|
|
||||||
|
table.addCell (row, 1, entry + " (" + age + ")");
|
||||||
|
|
||||||
|
// fg
|
||||||
|
std::string color = task->get ("fg");
|
||||||
|
if (color != "")
|
||||||
|
{
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Foreground color");
|
||||||
|
table.addCell (row, 1, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bg
|
||||||
|
color = task->get ("bg");
|
||||||
|
if (color != "")
|
||||||
|
{
|
||||||
|
row = table.addRow ();
|
||||||
|
table.addCell (row, 0, "Background color");
|
||||||
|
table.addCell (row, 1, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
out << optionalBlankLine ()
|
||||||
|
<< table.render ()
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// end
|
if (! tasks.size ()) {
|
||||||
if (task->has ("end"))
|
out << "No matches." << std::endl;
|
||||||
{
|
rc = 1;
|
||||||
row = table.addRow ();
|
|
||||||
table.addCell (row, 0, "End");
|
|
||||||
Date dt (atoi (task->get ("end").c_str ()));
|
|
||||||
table.addCell (row, 1, dt.toString (context.config.get ("dateformat")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tags ...
|
outs = out.str ();
|
||||||
std::vector <std::string> tags;
|
context.hooks.trigger ("post-info-command");
|
||||||
task->getTags (tags);
|
|
||||||
if (tags.size ())
|
|
||||||
{
|
|
||||||
std::string allTags;
|
|
||||||
join (allTags, " ", tags);
|
|
||||||
|
|
||||||
row = table.addRow ();
|
|
||||||
table.addCell (row, 0, "Tags");
|
|
||||||
table.addCell (row, 1, allTags);
|
|
||||||
}
|
|
||||||
|
|
||||||
// uuid
|
|
||||||
row = table.addRow ();
|
|
||||||
table.addCell (row, 0, "UUID");
|
|
||||||
table.addCell (row, 1, task->get ("uuid"));
|
|
||||||
|
|
||||||
// entry
|
|
||||||
row = table.addRow ();
|
|
||||||
table.addCell (row, 0, "Entered");
|
|
||||||
Date dt (atoi (task->get ("entry").c_str ()));
|
|
||||||
std::string entry = dt.toString (context.config.get ("dateformat"));
|
|
||||||
|
|
||||||
std::string age;
|
|
||||||
std::string created = task->get ("entry");
|
|
||||||
if (created.length ())
|
|
||||||
{
|
|
||||||
Date dt (atoi (created.c_str ()));
|
|
||||||
age = formatSeconds ((time_t) (now - dt));
|
|
||||||
}
|
|
||||||
|
|
||||||
table.addCell (row, 1, entry + " (" + age + ")");
|
|
||||||
|
|
||||||
// fg
|
|
||||||
std::string color = task->get ("fg");
|
|
||||||
if (color != "")
|
|
||||||
{
|
|
||||||
row = table.addRow ();
|
|
||||||
table.addCell (row, 0, "Foreground color");
|
|
||||||
table.addCell (row, 1, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// bg
|
|
||||||
color = task->get ("bg");
|
|
||||||
if (color != "")
|
|
||||||
{
|
|
||||||
row = table.addRow ();
|
|
||||||
table.addCell (row, 0, "Background color");
|
|
||||||
table.addCell (row, 1, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
out << optionalBlankLine ()
|
|
||||||
<< table.render ()
|
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! tasks.size ()) {
|
|
||||||
out << "No matches." << std::endl;
|
|
||||||
rc = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
outs = out.str ();
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
PROJECT = t.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t \
|
PROJECT = t.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t \
|
||||||
config.t seq.t att.t stringtable.t record.t nibbler.t subst.t filt.t \
|
config.t seq.t att.t stringtable.t record.t nibbler.t subst.t filt.t \
|
||||||
cmd.t util.t color.t list.t path.t file.t directory.t
|
cmd.t util.t color.t list.t path.t file.t directory.t
|
||||||
CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti
|
CFLAGS = -I. -I.. -I../.. -Wall -pedantic -ggdb3 -fno-rtti
|
||||||
LFLAGS = -L/usr/local/lib -lncurses
|
LFLAGS = -L/usr/local/lib -lncurses
|
||||||
OBJECTS = ../TDB.o ../Task.o ../text.o ../Date.o ../Table.o ../Duration.o \
|
OBJECTS = ../t-TDB.o ../t-Task.o ../t-text.o ../t-Date.o ../t-Table.o \
|
||||||
../util.o ../Config.o ../Sequence.o ../Att.o ../Cmd.o ../Record.o \
|
../t-Duration.o ../t-util.o ../t-Config.o ../t-Sequence.o ../t-Att.o \
|
||||||
../StringTable.o ../Subst.o ../Nibbler.o ../Location.o ../Filter.o \
|
../t-Cmd.o ../t-Record.o ../t-StringTable.o ../t-Subst.o \
|
||||||
../Context.o ../Keymap.o ../command.o ../interactive.o ../report.o \
|
../t-Nibbler.o ../t-Location.o ../t-Filter.o ../t-Context.o \
|
||||||
../Grid.o ../Color.o ../rules.o ../recur.o ../custom.o ../import.o \
|
../t-Keymap.o ../t-command.o ../t-interactive.o ../t-report.o \
|
||||||
../edit.o ../Timer.o ../Permission.o ../Path.o ../File.o \
|
../t-Grid.o ../t-Color.o ../t-rules.o ../t-recur.o ../t-custom.o \
|
||||||
../Directory.o
|
../t-import.o ../t-edit.o ../t-Timer.o ../t-Permission.o ../t-Path.o \
|
||||||
|
../t-File.o ../t-Directory.o ../t-Hooks.o ../t-API.o
|
||||||
|
|
||||||
all: $(PROJECT)
|
all: $(PROJECT)
|
||||||
|
|
||||||
|
|
67
src/tests/hook.post-start.t
Executable file
67
src/tests/hook.post-start.t
Executable file
|
@ -0,0 +1,67 @@
|
||||||
|
#! /usr/bin/perl
|
||||||
|
################################################################################
|
||||||
|
## task - a command line task list manager.
|
||||||
|
##
|
||||||
|
## Copyright 2006 - 2010, Paul Beckingham.
|
||||||
|
## All rights reserved.
|
||||||
|
##
|
||||||
|
## 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 2 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, write to the
|
||||||
|
##
|
||||||
|
## Free Software Foundation, Inc.,
|
||||||
|
## 51 Franklin Street, Fifth Floor,
|
||||||
|
## Boston, MA
|
||||||
|
## 02110-1301
|
||||||
|
## USA
|
||||||
|
##
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Test::More tests => 7;
|
||||||
|
|
||||||
|
# Create the rc file.
|
||||||
|
if (open my $fh, '>', 'hook.rc')
|
||||||
|
{
|
||||||
|
print $fh "data.location=.\n",
|
||||||
|
"hook.post-start=" . $ENV{'PWD'} . "/hook:test\n";
|
||||||
|
close $fh;
|
||||||
|
ok (-r 'hook.rc', 'Created hook.rc');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (open my $fh, '>', 'hook')
|
||||||
|
{
|
||||||
|
print $fh "function test () print ('marker') return 0, nil end\n";
|
||||||
|
close $fh;
|
||||||
|
ok (-r 'hook', 'Created hook');
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test the hook.
|
||||||
|
my $output = qx{../task rc:hook.rc _version};
|
||||||
|
like ($output, qr/^marker.+\n\d\.\d+\.\d+\n$/ms, 'Found marker before output');
|
||||||
|
|
||||||
|
# Cleanup.
|
||||||
|
unlink 'pending.data';
|
||||||
|
ok (!-r 'pending.data', 'Removed pending.data');
|
||||||
|
|
||||||
|
unlink 'undo.data';
|
||||||
|
ok (!-r 'undo.data', 'Removed undo.data');
|
||||||
|
|
||||||
|
unlink 'hook';
|
||||||
|
ok (!-r 'hook', 'Removed hook');
|
||||||
|
|
||||||
|
unlink 'hook.rc';
|
||||||
|
ok (!-r 'hook.rc', 'Removed hook.rc');
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
|
67
src/tests/hook.pre-exit.t
Executable file
67
src/tests/hook.pre-exit.t
Executable file
|
@ -0,0 +1,67 @@
|
||||||
|
#! /usr/bin/perl
|
||||||
|
################################################################################
|
||||||
|
## task - a command line task list manager.
|
||||||
|
##
|
||||||
|
## Copyright 2006 - 2010, Paul Beckingham.
|
||||||
|
## All rights reserved.
|
||||||
|
##
|
||||||
|
## 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 2 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, write to the
|
||||||
|
##
|
||||||
|
## Free Software Foundation, Inc.,
|
||||||
|
## 51 Franklin Street, Fifth Floor,
|
||||||
|
## Boston, MA
|
||||||
|
## 02110-1301
|
||||||
|
## USA
|
||||||
|
##
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Test::More tests => 7;
|
||||||
|
|
||||||
|
# Create the rc file.
|
||||||
|
if (open my $fh, '>', 'hook.rc')
|
||||||
|
{
|
||||||
|
print $fh "data.location=.\n",
|
||||||
|
"hook.pre-exit=" . $ENV{'PWD'} . "/hook:test\n";
|
||||||
|
close $fh;
|
||||||
|
ok (-r 'hook.rc', 'Created hook.rc');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (open my $fh, '>', 'hook')
|
||||||
|
{
|
||||||
|
print $fh "function test () print ('marker') return 0, nil end\n";
|
||||||
|
close $fh;
|
||||||
|
ok (-r 'hook', 'Created hook');
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test the hook.
|
||||||
|
my $output = qx{../task rc:hook.rc _version};
|
||||||
|
like ($output, qr/\n\d\.\d+\.\d+\nmarker\n$/ms, 'Found marker after output');
|
||||||
|
|
||||||
|
# Cleanup.
|
||||||
|
unlink 'pending.data';
|
||||||
|
ok (!-r 'pending.data', 'Removed pending.data');
|
||||||
|
|
||||||
|
unlink 'undo.data';
|
||||||
|
ok (!-r 'undo.data', 'Removed undo.data');
|
||||||
|
|
||||||
|
unlink 'hook';
|
||||||
|
ok (!-r 'hook', 'Removed hook');
|
||||||
|
|
||||||
|
unlink 'hook.rc';
|
||||||
|
ok (!-r 'hook.rc', 'Removed hook.rc');
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
|
6
with_lua
Executable file
6
with_lua
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
# This is currently what is required to build with Lua.
|
||||||
|
|
||||||
|
#./configure --enable-lua=yes --with-lua --with-lua-inc=/usr/local/include --with-lua-lib=/usr/local/lib
|
||||||
|
#./configure --enable-lua --with-lua --with-lua-inc=/usr/local/include --with-lua-lib=/usr/local/lib
|
||||||
|
./configure --enable-lua --with-lua-inc=/usr/local/include --with-lua-lib=/usr/local/lib
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue