Source refactoring; no functional changes.
This commit is contained in:
parent
b174130c91
commit
332fdba0bd
20 changed files with 1806 additions and 1440 deletions
|
|
@ -1,13 +1,15 @@
|
||||||
VPATH = @top_srcdir@
|
VPATH = @top_srcdir@
|
||||||
|
|
||||||
CC = @CC@
|
CC = @CC@
|
||||||
CPPFLAGS = @CPPFLAGS@
|
# _GNU_SOURCE is required for wcsdup() on older version of glibc that don't
|
||||||
|
# implement XPG7.
|
||||||
|
CPPFLAGS = @CPPFLAGS@ -D_XOPEN_SOURCE=700 -D_XOPEN_SOURCE_EXTENDED -D__EXTENSIONS__
|
||||||
CFLAGS = @CFLAGS@ -I@top_srcdir@ -I@top_builddir@
|
CFLAGS = @CFLAGS@ -I@top_srcdir@ -I@top_builddir@
|
||||||
LDFLAGS = @LDFLAGS@
|
LDFLAGS = @LDFLAGS@
|
||||||
LIBS = @LIBS@
|
LIBS = @LIBS@
|
||||||
INSTALL = @INSTALL@
|
INSTALL = @INSTALL@
|
||||||
|
|
||||||
OBJS = tts.o
|
OBJS = tts.o wide.o entry.o ui.o functions.o commands.o bindings.o str.o
|
||||||
|
|
||||||
prefix = @prefix@
|
prefix = @prefix@
|
||||||
exec_prefix = @exec_prefix@
|
exec_prefix = @exec_prefix@
|
||||||
|
|
|
||||||
13
bindings.c
Normal file
13
bindings.c
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bindings.h"
|
||||||
|
|
||||||
|
binding_list_t bindings = TAILQ_HEAD_INITIALIZER(bindings);
|
||||||
36
bindings.h
Normal file
36
bindings.h
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_BINDINGS_H
|
||||||
|
#define TTS_BINDINGS_H
|
||||||
|
|
||||||
|
#include "wide.h"
|
||||||
|
#include "functions.h"
|
||||||
|
#include "queue.h"
|
||||||
|
|
||||||
|
typedef struct tkey {
|
||||||
|
INT ky_code;
|
||||||
|
const WCHAR *ky_name;
|
||||||
|
} tkey_t;
|
||||||
|
|
||||||
|
typedef struct binding {
|
||||||
|
INT bi_code;
|
||||||
|
tkey_t *bi_key;
|
||||||
|
function_t *bi_func;
|
||||||
|
TAILQ_ENTRY(binding) bi_entries;
|
||||||
|
} binding_t;
|
||||||
|
|
||||||
|
typedef TAILQ_HEAD(bindlist, binding) binding_list_t;
|
||||||
|
extern binding_list_t bindings;
|
||||||
|
|
||||||
|
tkey_t *find_key(const WCHAR *name);
|
||||||
|
void bind_key(const WCHAR *key, const WCHAR *func);
|
||||||
|
|
||||||
|
#endif /* !TTS_BINDINGS_H */
|
||||||
133
commands.c
Normal file
133
commands.c
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "commands.h"
|
||||||
|
#include "style.h"
|
||||||
|
#include "bindings.h"
|
||||||
|
#include "variable.h"
|
||||||
|
|
||||||
|
static command_t commands[] = {
|
||||||
|
{ WIDE("bind"), c_bind },
|
||||||
|
{ WIDE("style"), c_style },
|
||||||
|
{ WIDE("set"), c_set },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
command_t *
|
||||||
|
find_command(name)
|
||||||
|
const WCHAR *name;
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < sizeof(commands) / sizeof(*commands); i++)
|
||||||
|
if (STRCMP(name, commands[i].cm_name) == 0)
|
||||||
|
return &commands[i];
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
c_style(argc, argv)
|
||||||
|
size_t argc;
|
||||||
|
WCHAR **argv;
|
||||||
|
{
|
||||||
|
style_t *sy;
|
||||||
|
WCHAR *last, *tok;
|
||||||
|
|
||||||
|
if (argc < 3 || argc > 4) {
|
||||||
|
cmderr(WIDE("Usage: style <item> <foreground> [background]"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STRCMP(argv[1], WIDE("header")) == 0)
|
||||||
|
sy = &sy_header;
|
||||||
|
else if (STRCMP(argv[1], WIDE("status")) == 0)
|
||||||
|
sy = &sy_status;
|
||||||
|
else if (STRCMP(argv[1], WIDE("entry")) == 0)
|
||||||
|
sy = &sy_entry;
|
||||||
|
else if (STRCMP(argv[1], WIDE("selected")) == 0)
|
||||||
|
sy = &sy_selected;
|
||||||
|
else if (STRCMP(argv[1], WIDE("running")) == 0)
|
||||||
|
sy = &sy_running;
|
||||||
|
else if (STRCMP(argv[1], WIDE("date")) == 0)
|
||||||
|
sy = &sy_date;
|
||||||
|
else {
|
||||||
|
cmderr(WIDE("Unknown style item."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
style_clear(sy);
|
||||||
|
for (tok = STRTOK(argv[2], WIDE(","), &last); tok != NULL;
|
||||||
|
tok = STRTOK(NULL, WIDE(","), &last)) {
|
||||||
|
style_add(sy, tok, argv[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_styles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
c_bind(argc, argv)
|
||||||
|
size_t argc;
|
||||||
|
WCHAR **argv;
|
||||||
|
{
|
||||||
|
if (argc != 3) {
|
||||||
|
cmderr(WIDE("Usage: bind <key> <function>"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind_key(argv[1], argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
c_set(argc, argv)
|
||||||
|
size_t argc;
|
||||||
|
WCHAR **argv;
|
||||||
|
{
|
||||||
|
variable_t *var;
|
||||||
|
int val;
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
cmderr(WIDE("Usage: set <variable> <value>"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((var = find_variable(argv[1])) == NULL) {
|
||||||
|
cmderr(WIDE("Unknown variable \"%"FMT_L"s\"."), argv[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (var->va_type) {
|
||||||
|
case VTYPE_BOOL:
|
||||||
|
if (STRCMP(argv[2], WIDE("true")) == 0 ||
|
||||||
|
STRCMP(argv[2], WIDE("yes")) == 0 ||
|
||||||
|
STRCMP(argv[2], WIDE("on")) == 0 ||
|
||||||
|
STRCMP(argv[2], WIDE("1")) == 0) {
|
||||||
|
val = 1;
|
||||||
|
} else if (STRCMP(argv[2], WIDE("false")) == 0 ||
|
||||||
|
STRCMP(argv[2], WIDE("no")) == 0 ||
|
||||||
|
STRCMP(argv[2], WIDE("off")) == 0 ||
|
||||||
|
STRCMP(argv[2], WIDE("0")) == 0) {
|
||||||
|
val = 0;
|
||||||
|
} else {
|
||||||
|
cmderr(WIDE("Invalid value for boolean: \"%"FMT_L"s\"."), argv[2]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(int *)var->va_addr = val;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VTYPE_STRING:
|
||||||
|
*(WCHAR **)var->va_addr = STRDUP(argv[2]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VTYPE_INT:
|
||||||
|
*(int *)var->va_addr = STRTOL(argv[2], NULL, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
32
commands.h
Normal file
32
commands.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_COMMANDS_H
|
||||||
|
#define TTS_COMMANDS_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "wide.h"
|
||||||
|
|
||||||
|
typedef struct command {
|
||||||
|
const WCHAR *cm_name;
|
||||||
|
void (*cm_hdl) (size_t, WCHAR **);
|
||||||
|
} command_t;
|
||||||
|
|
||||||
|
command_t *find_command(const WCHAR *);
|
||||||
|
|
||||||
|
void c_bind (size_t, WCHAR **);
|
||||||
|
void c_style (size_t, WCHAR **);
|
||||||
|
void c_set (size_t, WCHAR **);
|
||||||
|
|
||||||
|
void cmderr (const WCHAR *, ...);
|
||||||
|
void vcmderr (const WCHAR *, va_list);
|
||||||
|
|
||||||
|
#endif /* !TTS_COMMANDS_H */
|
||||||
124
entry.c
Normal file
124
entry.c
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "entry.h"
|
||||||
|
#include "wide.h"
|
||||||
|
#include "tts.h"
|
||||||
|
|
||||||
|
entry_list entries = TAILQ_HEAD_INITIALIZER(entries);
|
||||||
|
|
||||||
|
entry_t *running;
|
||||||
|
|
||||||
|
entry_t *
|
||||||
|
entry_new(desc)
|
||||||
|
const WCHAR *desc;
|
||||||
|
{
|
||||||
|
entry_t *en;
|
||||||
|
if ((en = calloc(1, sizeof(*en))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (auto_nonbillable && STRSTR(desc, auto_nonbillable))
|
||||||
|
en->en_flags.efl_nonbillable = 1;
|
||||||
|
|
||||||
|
TAILQ_INSERT_HEAD(&entries, en, en_entries);
|
||||||
|
|
||||||
|
en->en_desc = STRDUP(desc);
|
||||||
|
time(&en->en_created);
|
||||||
|
|
||||||
|
return en;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
entry_start(en)
|
||||||
|
entry_t *en;
|
||||||
|
{
|
||||||
|
if (running)
|
||||||
|
entry_stop(running);
|
||||||
|
time(&en->en_started);
|
||||||
|
running = en;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
entry_stop(en)
|
||||||
|
entry_t *en;
|
||||||
|
{
|
||||||
|
if (running == en)
|
||||||
|
running = NULL;
|
||||||
|
en->en_secs += time(NULL) - en->en_started;
|
||||||
|
en->en_started = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
entry_free(en)
|
||||||
|
entry_t *en;
|
||||||
|
{
|
||||||
|
if (en == running)
|
||||||
|
entry_stop(en);
|
||||||
|
free(en->en_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
entry_account(en)
|
||||||
|
entry_t *en;
|
||||||
|
{
|
||||||
|
if (!en->en_started)
|
||||||
|
return;
|
||||||
|
en->en_secs += time(NULL) - en->en_started;
|
||||||
|
time(&en->en_started);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the amount of time for the day on which the timestamp .when falls.
|
||||||
|
* If .inv is 0, sum non-invoiced entries; if 1, sum invoiced entries; if
|
||||||
|
* 2, sum billable entries; if -1, sum all entries. If .incr is non-zero,
|
||||||
|
* individual entry time will be rounded up to intervals of that many minutes.
|
||||||
|
*/
|
||||||
|
time_t
|
||||||
|
entry_time_for_day(when, inv, incr)
|
||||||
|
time_t when;
|
||||||
|
{
|
||||||
|
time_t day = time_day(when);
|
||||||
|
time_t sum = 0;
|
||||||
|
entry_t *en;
|
||||||
|
int rnd = incr * 60;
|
||||||
|
TAILQ_FOREACH(en, &entries, en_entries) {
|
||||||
|
time_t n;
|
||||||
|
|
||||||
|
if (entry_day(en) > day)
|
||||||
|
continue;
|
||||||
|
if (entry_day(en) < day)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (inv == 0 && en->en_flags.efl_invoiced)
|
||||||
|
continue;
|
||||||
|
if (inv == 1 && !en->en_flags.efl_invoiced)
|
||||||
|
continue;
|
||||||
|
if (inv == 2 && en->en_flags.efl_nonbillable)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
n = en->en_secs;
|
||||||
|
if (en->en_started)
|
||||||
|
n += time(NULL) - en->en_started;
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (rnd)
|
||||||
|
n = (1 + round((n - 1) / rnd)) * rnd;
|
||||||
|
sum += n;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
59
entry.h
Normal file
59
entry.h
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_ENTRY_H
|
||||||
|
#define TTS_ENTRY_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "queue.h"
|
||||||
|
#include "wide.h"
|
||||||
|
|
||||||
|
typedef struct entry {
|
||||||
|
WCHAR *en_desc;
|
||||||
|
int en_secs;
|
||||||
|
time_t en_started;
|
||||||
|
time_t en_created;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int efl_visible:1;
|
||||||
|
int efl_invoiced:1;
|
||||||
|
int efl_marked:1;
|
||||||
|
int efl_deleted:1;
|
||||||
|
int efl_nonbillable:1;
|
||||||
|
} en_flags;
|
||||||
|
TAILQ_ENTRY(entry) en_entries;
|
||||||
|
} entry_t;
|
||||||
|
|
||||||
|
typedef TAILQ_HEAD(entrylist, entry) entry_list;
|
||||||
|
extern entry_list entries;
|
||||||
|
|
||||||
|
extern entry_t *running;
|
||||||
|
|
||||||
|
entry_t *entry_new (const WCHAR *);
|
||||||
|
void entry_start (entry_t *);
|
||||||
|
void entry_stop (entry_t *);
|
||||||
|
void entry_free (entry_t *);
|
||||||
|
void entry_account (entry_t *);
|
||||||
|
time_t entry_time_for_day (time_t, int, int);
|
||||||
|
|
||||||
|
#define time_day(t) (((t) / (60 * 60 * 24)) * (60 * 60 * 24))
|
||||||
|
#define entry_day(e) (time_day((e)->en_created))
|
||||||
|
|
||||||
|
#define time_to_hms(t, h, m, s) do { \
|
||||||
|
time_t n = t; \
|
||||||
|
h = n / (60 * 60); \
|
||||||
|
n %= (60 * 60); \
|
||||||
|
m = n / 60; \
|
||||||
|
n %= 60; \
|
||||||
|
s = n; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif /* !TTS_ENTRY_H */
|
||||||
800
functions.c
Normal file
800
functions.c
Normal file
|
|
@ -0,0 +1,800 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "functions.h"
|
||||||
|
#include "tts.h"
|
||||||
|
#include "entry.h"
|
||||||
|
#include "ui.h"
|
||||||
|
#include "commands.h"
|
||||||
|
#include "tts_curses.h"
|
||||||
|
#include "bindings.h"
|
||||||
|
#include "str.h"
|
||||||
|
|
||||||
|
function_t funcs[] = {
|
||||||
|
{ WIDE("help"), khelp, WIDE("display help screen") },
|
||||||
|
{ WIDE("add"), kadd, WIDE("add a new entry and start the timer") },
|
||||||
|
{ WIDE("add-old"), kaddold, WIDE("add a new entry and specify its duration") },
|
||||||
|
{ WIDE("delete"), kmarkdel, WIDE("delete the current entry") },
|
||||||
|
{ WIDE("undelete"), kundel, WIDE("undelete the current entry") },
|
||||||
|
{ WIDE("quit"), kquit, WIDE("exit TTS") },
|
||||||
|
{ WIDE("invoice"), kinvoiced, WIDE("toggle current entry as invoiced") },
|
||||||
|
{ WIDE("billable"), kbillable, WIDE("toggle current entry as billable") },
|
||||||
|
{ WIDE("mark"), kmark, WIDE("mark the current entry") },
|
||||||
|
{ WIDE("unmarkall"), kunmarkall, WIDE("unmark all entries") },
|
||||||
|
{ WIDE("startstop"), ktoggle, WIDE("start or stop the timer") },
|
||||||
|
{ WIDE("edit-desc"), keddesc, WIDE("edit the current entry's description") },
|
||||||
|
{ WIDE("edit-time"), kedtime, WIDE("edit the current entry's duration") },
|
||||||
|
{ WIDE("showhide-inv"), ktoggleinv, WIDE("show or hide invoiced entries") },
|
||||||
|
{ WIDE("copy"), kcopy, WIDE("copy the current entry's description to a new entry") },
|
||||||
|
{ WIDE("add-time"), kaddtime, WIDE("add time to the current entry") },
|
||||||
|
{ WIDE("sub-time"), kdeltime, WIDE("subtract time from the current entry") },
|
||||||
|
{ WIDE("search"), ksearch, WIDE("search for an entry by name") },
|
||||||
|
{ WIDE("sync"), ksync, WIDE("purge all deleted entries") },
|
||||||
|
{ WIDE("prev"), kup, WIDE("move to the previous entry") },
|
||||||
|
{ WIDE("next"), kdown, WIDE("move to the next entry") },
|
||||||
|
{ WIDE("execute"), kexec, WIDE("execute a configuration command") },
|
||||||
|
{ WIDE("merge"), kmerge, WIDE("merge marked entries into current entry") },
|
||||||
|
{ WIDE("interrupt"), kint, WIDE("split current entry into new entry")},
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
kquit()
|
||||||
|
{
|
||||||
|
entry_t *en;
|
||||||
|
int ndel = 0;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(en, &entries, en_entries) {
|
||||||
|
if (en->en_flags.efl_deleted)
|
||||||
|
ndel++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ndel) {
|
||||||
|
WCHAR s[128];
|
||||||
|
SNPRINTF(s, WSIZEOF(s), WIDE("Purge %d deleted entries?"), ndel);
|
||||||
|
if (yesno(s)) {
|
||||||
|
ksync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doexit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kadd()
|
||||||
|
{
|
||||||
|
WCHAR *name;
|
||||||
|
entry_t *en;
|
||||||
|
name = prompt(WIDE("Description:"), NULL, NULL);
|
||||||
|
if (!name || !*name) {
|
||||||
|
free(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
en = entry_new(name);
|
||||||
|
entry_start(en);
|
||||||
|
curent = en;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kaddold()
|
||||||
|
{
|
||||||
|
WCHAR *name;
|
||||||
|
entry_t *en;
|
||||||
|
name = prompt(WIDE("Description:"), NULL, NULL);
|
||||||
|
|
||||||
|
if (!name || !*name) {
|
||||||
|
free(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
en = entry_new(name);
|
||||||
|
curent = en;
|
||||||
|
kedtime();
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ktoggle()
|
||||||
|
{
|
||||||
|
itime = 0;
|
||||||
|
|
||||||
|
if (!curent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (curent == running) {
|
||||||
|
entry_stop(curent);
|
||||||
|
save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (running)
|
||||||
|
entry_stop(running);
|
||||||
|
entry_start(curent);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kundel()
|
||||||
|
{
|
||||||
|
if (!curent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
curent->en_flags.efl_deleted = 0;
|
||||||
|
if (delete_advance)
|
||||||
|
cursadvance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kmarkdel()
|
||||||
|
{
|
||||||
|
entry_t *en;
|
||||||
|
int nmarked = 0;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(en, &entries, en_entries) {
|
||||||
|
if (en->en_flags.efl_marked) {
|
||||||
|
nmarked++;
|
||||||
|
en->en_flags.efl_deleted = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nmarked)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entries to delete."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curent->en_flags.efl_deleted = 1;
|
||||||
|
|
||||||
|
if (delete_advance)
|
||||||
|
cursadvance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ksync()
|
||||||
|
{
|
||||||
|
entry_t *en, *ten;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(en, &entries, en_entries, ten) {
|
||||||
|
if (!en->en_flags.efl_deleted)
|
||||||
|
continue;
|
||||||
|
if (en == curent)
|
||||||
|
curent = NULL;
|
||||||
|
TAILQ_REMOVE(&entries, en, en_entries);
|
||||||
|
entry_free(en);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curent == NULL)
|
||||||
|
curent = TAILQ_FIRST(&entries);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kup()
|
||||||
|
{
|
||||||
|
entry_t *prev = curent;
|
||||||
|
if (!curent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if ((prev = TAILQ_PREV(prev, entrylist, en_entries)) == NULL)
|
||||||
|
break;
|
||||||
|
} while (!showinv && prev->en_flags.efl_invoiced);
|
||||||
|
|
||||||
|
if (prev == NULL) {
|
||||||
|
drawstatus(WIDE("Already at first entry."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curent = prev;
|
||||||
|
if (!curent->en_flags.efl_visible)
|
||||||
|
pagestart--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kdown()
|
||||||
|
{
|
||||||
|
entry_t *next = curent;
|
||||||
|
if (!curent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if ((next = TAILQ_NEXT(next, en_entries)) == NULL)
|
||||||
|
break;
|
||||||
|
} while (!showinv && next->en_flags.efl_invoiced);
|
||||||
|
|
||||||
|
if (next == NULL) {
|
||||||
|
drawstatus(WIDE("Already at last entry."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curent = next;
|
||||||
|
if (!curent->en_flags.efl_visible)
|
||||||
|
pagestart++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kinvoiced()
|
||||||
|
{
|
||||||
|
entry_t *en;
|
||||||
|
int anymarked = 0;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(en, &entries, en_entries) {
|
||||||
|
if (!en->en_flags.efl_marked)
|
||||||
|
continue;
|
||||||
|
anymarked = 1;
|
||||||
|
en->en_flags.efl_invoiced = !en->en_flags.efl_invoiced;
|
||||||
|
en->en_flags.efl_marked = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anymarked) {
|
||||||
|
save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entry selected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curent->en_flags.efl_invoiced = !curent->en_flags.efl_invoiced;
|
||||||
|
save();
|
||||||
|
|
||||||
|
en = curent;
|
||||||
|
|
||||||
|
if (showinv) {
|
||||||
|
if (TAILQ_NEXT(curent, en_entries) != NULL)
|
||||||
|
curent = TAILQ_NEXT(curent, en_entries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to find the next uninvoiced request to move the cursor to.
|
||||||
|
*/
|
||||||
|
for (;;) {
|
||||||
|
if ((curent = TAILQ_NEXT(curent, en_entries)) == NULL)
|
||||||
|
break; /* end of list */
|
||||||
|
if (!curent->en_flags.efl_invoiced)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We didn't find any, so try searching backwards instead.
|
||||||
|
*/
|
||||||
|
for (curent = en;;) {
|
||||||
|
if ((curent = TAILQ_PREV(curent, entrylist, en_entries)) == NULL)
|
||||||
|
break; /* end of list */
|
||||||
|
if (!curent->en_flags.efl_invoiced)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kbillable()
|
||||||
|
{
|
||||||
|
entry_t *en;
|
||||||
|
int anymarked = 0;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(en, &entries, en_entries) {
|
||||||
|
if (!en->en_flags.efl_marked)
|
||||||
|
continue;
|
||||||
|
anymarked = 1;
|
||||||
|
en->en_flags.efl_nonbillable = !en->en_flags.efl_nonbillable;
|
||||||
|
en->en_flags.efl_marked = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anymarked) {
|
||||||
|
save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entry selected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curent->en_flags.efl_nonbillable = !curent->en_flags.efl_nonbillable;
|
||||||
|
save();
|
||||||
|
|
||||||
|
if (bill_advance)
|
||||||
|
cursadvance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
keddesc()
|
||||||
|
{
|
||||||
|
WCHAR *new;
|
||||||
|
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entry selected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((new = prompt(WIDE("Description:"), curent->en_desc, NULL)) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(curent->en_desc);
|
||||||
|
curent->en_desc = new;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kedtime()
|
||||||
|
{
|
||||||
|
WCHAR *new, old[64];
|
||||||
|
time_t n;
|
||||||
|
int h, m, s;
|
||||||
|
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entry selected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = curent->en_secs;
|
||||||
|
if (curent->en_started)
|
||||||
|
n += time(NULL) - curent->en_started;
|
||||||
|
h = n / (60 * 60);
|
||||||
|
n %= (60 * 60);
|
||||||
|
m = n / 60;
|
||||||
|
n %= 60;
|
||||||
|
s = n;
|
||||||
|
|
||||||
|
SNPRINTF(old, WSIZEOF(old), WIDE("%02d:%02d:%02d"), h, m, s);
|
||||||
|
if ((new = prompt(WIDE("Duration [HH:MM:SS]:"), old, NULL)) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!SSCANF(new, WIDE("%d:%d:%d"), &h, &m, &s)) {
|
||||||
|
free(new);
|
||||||
|
drawstatus(WIDE("Invalid duration."));
|
||||||
|
}
|
||||||
|
|
||||||
|
curent->en_secs = (h * 60 * 60) + (m * 60) + s;
|
||||||
|
if (curent->en_started)
|
||||||
|
time(&curent->en_started);
|
||||||
|
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ktoggleinv()
|
||||||
|
{
|
||||||
|
entry_t *en = curent;
|
||||||
|
showinv = !showinv;
|
||||||
|
drawstatus(WIDE("%"FMT_L"s invoiced entries."),
|
||||||
|
showinv ? L"Showing" : L"Hiding");
|
||||||
|
|
||||||
|
if (curent && !curent->en_flags.efl_invoiced)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!curent) {
|
||||||
|
curent = TAILQ_FIRST(&entries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to find the next uninvoiced request to move the cursor to.
|
||||||
|
*/
|
||||||
|
for (;;) {
|
||||||
|
if ((curent = TAILQ_NEXT(curent, en_entries)) == NULL)
|
||||||
|
break; /* end of list */
|
||||||
|
if (!curent->en_flags.efl_invoiced)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We didn't find any, so try searching backwards instead.
|
||||||
|
*/
|
||||||
|
for (curent = en;;) {
|
||||||
|
if ((curent = TAILQ_PREV(curent, entrylist, en_entries)) == NULL)
|
||||||
|
break; /* end of list */
|
||||||
|
if (!curent->en_flags.efl_invoiced)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kcopy()
|
||||||
|
{
|
||||||
|
entry_t *en;
|
||||||
|
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entry selected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
en = entry_new(curent->en_desc);
|
||||||
|
curent = en;
|
||||||
|
entry_start(en);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kaddtime()
|
||||||
|
{
|
||||||
|
WCHAR *tstr;
|
||||||
|
int h = 0, m = 0, s = 0, secs;
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entry selected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tstr = prompt(WIDE("Time to add ([[HH:]MM:]SS):"), NULL, NULL)) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!*tstr) {
|
||||||
|
drawstatus(WIDE(""));
|
||||||
|
free(tstr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSCANF(tstr, WIDE("%d:%d:%d"), &h, &m, &s) != 3) {
|
||||||
|
h = 0;
|
||||||
|
if (SSCANF(tstr, WIDE("%d:%d"), &m, &s) != 2) {
|
||||||
|
m = 0;
|
||||||
|
if (SSCANF(tstr, WIDE("%d"), &s) != 1) {
|
||||||
|
free(tstr);
|
||||||
|
drawstatus(WIDE("Invalid time format."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tstr);
|
||||||
|
|
||||||
|
if (m >= 60) {
|
||||||
|
drawstatus(WIDE("Minutes cannot be more than 59."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s >= 60) {
|
||||||
|
drawstatus(WIDE("Seconds cannot be more than 59."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
secs = s + m*60 + h*60*60;
|
||||||
|
curent->en_secs += secs;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kdeltime()
|
||||||
|
{
|
||||||
|
WCHAR *tstr;
|
||||||
|
int h = 0, m = 0, s = 0, secs;
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entry selected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tstr = prompt(WIDE("Time to subtract, ([[HH:]MM:]SS):"), NULL, NULL)) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!*tstr) {
|
||||||
|
drawstatus(WIDE(""));
|
||||||
|
free(tstr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSCANF(tstr, WIDE("%d:%d:%d"), &h, &m, &s) != 3) {
|
||||||
|
h = 0;
|
||||||
|
if (SSCANF(tstr, WIDE("%d:%d"), &m, &s) != 2) {
|
||||||
|
m = 0;
|
||||||
|
if (SSCANF(tstr, WIDE("%d"), &s) != 1) {
|
||||||
|
free(tstr);
|
||||||
|
drawstatus(WIDE("Invalid time format."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tstr);
|
||||||
|
if (m >= 60) {
|
||||||
|
drawstatus(WIDE("Minutes cannot be more than 59."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s >= 60) {
|
||||||
|
drawstatus(WIDE("Seconds cannot be more than 59."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_account(curent);
|
||||||
|
|
||||||
|
secs = s + m*60 + h*60*60;
|
||||||
|
if (curent->en_secs - secs < 0) {
|
||||||
|
drawstatus(WIDE("Remaining time cannot be less than zero."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curent->en_secs -= secs;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kmerge()
|
||||||
|
{
|
||||||
|
entry_t *en, *ten;
|
||||||
|
int nmarked = 0;
|
||||||
|
WCHAR pr[128];
|
||||||
|
int h, m, s = 0;
|
||||||
|
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entry selected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Count number of marked entries and the summed time.
|
||||||
|
*/
|
||||||
|
TAILQ_FOREACH(en, &entries, en_entries) {
|
||||||
|
if (!en->en_flags.efl_marked || en == curent)
|
||||||
|
continue;
|
||||||
|
nmarked++;
|
||||||
|
s += en->en_secs;
|
||||||
|
if (en->en_started)
|
||||||
|
s += time(NULL) - en->en_started;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nmarked == 0) {
|
||||||
|
drawstatus(WIDE("No marked entries."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = s / (60 * 60);
|
||||||
|
s %= (60 * 60);
|
||||||
|
m = s / 60;
|
||||||
|
s %= 60;
|
||||||
|
|
||||||
|
SNPRINTF(pr, WSIZEOF(pr), WIDE("Merge %d marked entries [%02d:%02d:%02d] into current entry?"),
|
||||||
|
nmarked, h, m, s);
|
||||||
|
if (!yesno(pr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(en, &entries, en_entries, ten) {
|
||||||
|
if (!en->en_flags.efl_marked || en == curent)
|
||||||
|
continue;
|
||||||
|
if (en->en_started)
|
||||||
|
entry_stop(en);
|
||||||
|
curent->en_secs += en->en_secs;
|
||||||
|
TAILQ_REMOVE(&entries, en, en_entries);
|
||||||
|
entry_free(en);
|
||||||
|
}
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
khelp()
|
||||||
|
{
|
||||||
|
WINDOW *hwin;
|
||||||
|
size_t nhelp = 0;
|
||||||
|
WCHAR **help;
|
||||||
|
#define HTITLE WIDE(" TTS keys ")
|
||||||
|
size_t width = 0;
|
||||||
|
size_t i;
|
||||||
|
INT c;
|
||||||
|
binding_t *bi;
|
||||||
|
|
||||||
|
/* Count the number of bindings */
|
||||||
|
TAILQ_FOREACH(bi, &bindings, bi_entries)
|
||||||
|
nhelp++;
|
||||||
|
help = calloc(nhelp, sizeof(const WCHAR *));
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
TAILQ_FOREACH(bi, &bindings, bi_entries) {
|
||||||
|
WCHAR s[128];
|
||||||
|
if (bi->bi_key)
|
||||||
|
SNPRINTF(s, WSIZEOF(s), WIDE("%-10"FMT_L"s %"FMT_L"s (%"FMT_L"s)"),
|
||||||
|
bi->bi_key->ky_name, bi->bi_func->fn_desc,
|
||||||
|
bi->bi_func->fn_name);
|
||||||
|
else
|
||||||
|
SNPRINTF(s, WSIZEOF(s), WIDE("%"FMT_L"c %"FMT_L"s (%"FMT_L"s)"),
|
||||||
|
bi->bi_code, bi->bi_func->fn_desc, bi->bi_func->fn_name);
|
||||||
|
help[i] = STRDUP(s);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nhelp > (LINES - 6))
|
||||||
|
nhelp = LINES - 6;
|
||||||
|
|
||||||
|
for (i = 0; i < nhelp; i++)
|
||||||
|
if (STRLEN(help[i]) > width)
|
||||||
|
width = STRLEN(help[i]);
|
||||||
|
|
||||||
|
hwin = newwin(nhelp + 4, width + 4,
|
||||||
|
(LINES / 2) - ((nhelp + 2) / 2),
|
||||||
|
(COLS / 2) - ((width + 2) / 2));
|
||||||
|
wborder(hwin, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
wattron(hwin, A_REVERSE | A_BOLD);
|
||||||
|
wmove(hwin, 0, (width / 2) - (WSIZEOF(HTITLE) - 1)/2);
|
||||||
|
WADDSTR(hwin, HTITLE);
|
||||||
|
wattroff(hwin, A_REVERSE | A_BOLD);
|
||||||
|
|
||||||
|
for (i = 0; i < nhelp; i++) {
|
||||||
|
wmove(hwin, i + 2, 2);
|
||||||
|
WADDSTR(hwin, help[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrefresh(hwin);
|
||||||
|
|
||||||
|
while (WGETCH(hwin, &c) == ERR
|
||||||
|
#ifdef KEY_RESIZE
|
||||||
|
|| (c == KEY_RESIZE)
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
delwin(hwin);
|
||||||
|
|
||||||
|
for (i = 0; i < nhelp; i++)
|
||||||
|
free(help[i]);
|
||||||
|
free(help);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kmark()
|
||||||
|
{
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entry selected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curent->en_flags.efl_marked = !curent->en_flags.efl_marked;
|
||||||
|
|
||||||
|
if (mark_advance)
|
||||||
|
cursadvance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kunmarkall()
|
||||||
|
{
|
||||||
|
entry_t *en;
|
||||||
|
TAILQ_FOREACH(en, &entries, en_entries)
|
||||||
|
en->en_flags.efl_marked = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kint()
|
||||||
|
{
|
||||||
|
time_t duration;
|
||||||
|
entry_t *en;
|
||||||
|
WCHAR *name;
|
||||||
|
|
||||||
|
if (!itime) {
|
||||||
|
if (!running) {
|
||||||
|
drawstatus(WIDE("No running entry."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
itime = time(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!running) {
|
||||||
|
drawstatus(WIDE("No running entry."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = prompt(WIDE("Description:"), NULL, NULL);
|
||||||
|
|
||||||
|
if (!name || !*name) {
|
||||||
|
itime = 0;
|
||||||
|
free(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itime) {
|
||||||
|
duration = time(NULL) - itime;
|
||||||
|
} else {
|
||||||
|
int h, m, s;
|
||||||
|
if (prduration(WIDE("Duration [HH:MM:SS]:"), &h, &m, &s) == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
duration = (h * 60 * 60) + (m * 60) + s;
|
||||||
|
}
|
||||||
|
|
||||||
|
itime = 0;
|
||||||
|
running->en_secs += (time(NULL) - running->en_started);
|
||||||
|
running->en_started = time(NULL);
|
||||||
|
running->en_secs -= duration;
|
||||||
|
|
||||||
|
en = entry_new(name);
|
||||||
|
en->en_created = time(NULL) - duration;
|
||||||
|
en->en_secs = duration;
|
||||||
|
save();
|
||||||
|
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ksearch()
|
||||||
|
{
|
||||||
|
static WCHAR *lastsearch;
|
||||||
|
WCHAR *term;
|
||||||
|
entry_t *start, *cur;
|
||||||
|
|
||||||
|
if (!curent) {
|
||||||
|
drawstatus(WIDE("No entries."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((term = prompt(WIDE("Search:"), NULL, NULL)) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!*term) {
|
||||||
|
free(term);
|
||||||
|
if (!lastsearch)
|
||||||
|
return;
|
||||||
|
|
||||||
|
term = lastsearch;
|
||||||
|
} else {
|
||||||
|
free(lastsearch);
|
||||||
|
lastsearch = term;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = start = curent;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
cur = TAILQ_NEXT(cur, en_entries);
|
||||||
|
if (cur == NULL) {
|
||||||
|
drawstatus(WIDE("Search reached last entry, continuing from top."));
|
||||||
|
cur = TAILQ_FIRST(&entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur == start) {
|
||||||
|
drawstatus(WIDE("No matches."));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STRSTR(cur->en_desc, term)) {
|
||||||
|
curent = cur;
|
||||||
|
if (!showinv && cur->en_flags.efl_invoiced)
|
||||||
|
showinv = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kexec()
|
||||||
|
{
|
||||||
|
WCHAR *cmd;
|
||||||
|
WCHAR **args;
|
||||||
|
command_t *cmds;
|
||||||
|
size_t nargs;
|
||||||
|
|
||||||
|
if ((cmd = prompt(WIDE(":"), NULL, NULL)) == NULL || !*cmd) {
|
||||||
|
free(cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nargs = tokenise(cmd, &args);
|
||||||
|
free(cmd);
|
||||||
|
|
||||||
|
if (nargs == 0) {
|
||||||
|
tokfree(&args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((cmds = find_command(args[0])) == NULL) {
|
||||||
|
drawstatus(WIDE("Unknown command."));
|
||||||
|
tokfree(&args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds->cm_hdl(nargs, args);
|
||||||
|
tokfree(&args);
|
||||||
|
}
|
||||||
|
|
||||||
51
functions.h
Normal file
51
functions.h
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_FUNCTIONS_H
|
||||||
|
#define TTS_FUNCTIONS_H
|
||||||
|
|
||||||
|
#include "wide.h"
|
||||||
|
|
||||||
|
void kadd(void);
|
||||||
|
void kaddold(void);
|
||||||
|
void kquit(void);
|
||||||
|
void kup(void);
|
||||||
|
void kdown(void);
|
||||||
|
void ktoggle(void);
|
||||||
|
void kinvoiced(void);
|
||||||
|
void kbillable(void);
|
||||||
|
void keddesc(void);
|
||||||
|
void kedtime(void);
|
||||||
|
void ktoggleinv(void);
|
||||||
|
void kcopy(void);
|
||||||
|
void kaddtime(void);
|
||||||
|
void kdeltime(void);
|
||||||
|
void khelp(void);
|
||||||
|
void kmark(void);
|
||||||
|
void kunmarkall(void);
|
||||||
|
void ksearch(void);
|
||||||
|
void kmarkdel(void);
|
||||||
|
void kundel(void);
|
||||||
|
void ksync(void);
|
||||||
|
void kexec(void);
|
||||||
|
void kmerge(void);
|
||||||
|
void kint(void);
|
||||||
|
|
||||||
|
typedef struct function {
|
||||||
|
const WCHAR *fn_name;
|
||||||
|
void (*fn_hdl) (void);
|
||||||
|
const WCHAR *fn_desc;
|
||||||
|
} function_t;
|
||||||
|
|
||||||
|
extern function_t funcs[];
|
||||||
|
|
||||||
|
function_t *find_func(const WCHAR *name);
|
||||||
|
|
||||||
|
#endif /* !TTS_FUNCTIONS_H */
|
||||||
107
str.c
Normal file
107
str.c
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "str.h"
|
||||||
|
|
||||||
|
size_t
|
||||||
|
tokenise(str, res)
|
||||||
|
const WCHAR *str;
|
||||||
|
WCHAR ***res;
|
||||||
|
{
|
||||||
|
int ntoks = 0;
|
||||||
|
const WCHAR *p, *q;
|
||||||
|
|
||||||
|
*res = NULL;
|
||||||
|
p = str;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
ptrdiff_t sz;
|
||||||
|
int qskip = 0;
|
||||||
|
|
||||||
|
/* Skip leading whitespace */
|
||||||
|
while (ISSPACE(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
/* End of string - no more arguments */
|
||||||
|
if (!*p)
|
||||||
|
break;
|
||||||
|
|
||||||
|
q = p;
|
||||||
|
|
||||||
|
if (*q == '"') {
|
||||||
|
/* Quoted string - scan for end of string */
|
||||||
|
int isbsl = 0;
|
||||||
|
p++;
|
||||||
|
|
||||||
|
while (*++q) {
|
||||||
|
/* Handle escaping with backslash; currently works but the \ isn't
|
||||||
|
* removed from the string.
|
||||||
|
*/
|
||||||
|
if (*q == '\\') {
|
||||||
|
isbsl = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isbsl && (*q == '"'))
|
||||||
|
break;
|
||||||
|
|
||||||
|
isbsl = 0;
|
||||||
|
}
|
||||||
|
/* At this point, *q == '"'. If it's NUL instead, then the
|
||||||
|
* string was not terminated with a closing '"' before the end
|
||||||
|
* of the line. We could give an error here, but it seems
|
||||||
|
* more useful to just accept it.
|
||||||
|
*/
|
||||||
|
if (*q == '"')
|
||||||
|
qskip = 1;
|
||||||
|
} else {
|
||||||
|
/* Not quoted - just find the next whitespace */
|
||||||
|
while (!ISSPACE(*q) && *q)
|
||||||
|
q++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the argument (which is sz bytes long) into the result array */
|
||||||
|
sz = (q - p);
|
||||||
|
*res = realloc(*res, sizeof(WCHAR *) * (ntoks + 1));
|
||||||
|
(*res)[ntoks] = malloc(sizeof(WCHAR) * (sz + 1));
|
||||||
|
MEMCPY((*res)[ntoks], p, sz);
|
||||||
|
(*res)[ntoks][sz] = 0;
|
||||||
|
ntoks++;
|
||||||
|
|
||||||
|
if (qskip)
|
||||||
|
q += qskip;
|
||||||
|
|
||||||
|
while (ISSPACE(*q))
|
||||||
|
q++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* q is the start of the next token (with leading whitespace); reset
|
||||||
|
* p to process the next argument.
|
||||||
|
*/
|
||||||
|
if (!*q)
|
||||||
|
break;
|
||||||
|
p = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
*res = realloc(*res, sizeof(WCHAR *) * (ntoks + 1));
|
||||||
|
(*res)[ntoks] = NULL;
|
||||||
|
return ntoks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tokfree(vec)
|
||||||
|
WCHAR ***vec;
|
||||||
|
{
|
||||||
|
WCHAR **p;
|
||||||
|
for (p = (*vec); *p; p++)
|
||||||
|
free(*p);
|
||||||
|
free(*vec);
|
||||||
|
}
|
||||||
|
|
||||||
21
str.h
Normal file
21
str.h
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_STR_H
|
||||||
|
#define TTS_STR_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "wide.h"
|
||||||
|
|
||||||
|
size_t tokenise(const WCHAR *, WCHAR ***result);
|
||||||
|
void tokfree(WCHAR ***);
|
||||||
|
|
||||||
|
#endif /* !TTS_STR_H */
|
||||||
50
style.h
Normal file
50
style.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_STYLE_H
|
||||||
|
#define TTS_STYLE_H
|
||||||
|
|
||||||
|
#include "tts_curses.h"
|
||||||
|
|
||||||
|
typedef struct style {
|
||||||
|
short sy_pair;
|
||||||
|
attr_t sy_attrs;
|
||||||
|
} style_t;
|
||||||
|
|
||||||
|
#define style_fg(s) (COLOR_PAIR((s).sy_pair) | (s).sy_attrs)
|
||||||
|
#define style_bg(s) ((INT) ' ' | COLOR_PAIR((s).sy_pair) | ((s).sy_attrs & ~WA_UNDERLINE))
|
||||||
|
|
||||||
|
typedef struct attrname {
|
||||||
|
const WCHAR *an_name;
|
||||||
|
attr_t an_value;
|
||||||
|
} attrname_t;
|
||||||
|
|
||||||
|
typedef struct colour {
|
||||||
|
const WCHAR *co_name;
|
||||||
|
short co_value;
|
||||||
|
} colour_t;
|
||||||
|
|
||||||
|
extern style_t sy_header,
|
||||||
|
sy_status,
|
||||||
|
sy_entry,
|
||||||
|
sy_running,
|
||||||
|
sy_selected,
|
||||||
|
sy_date;
|
||||||
|
|
||||||
|
int attr_find(const WCHAR *name, attr_t *result);
|
||||||
|
int colour_find(const WCHAR *name, short *result);
|
||||||
|
|
||||||
|
void style_clear(style_t *);
|
||||||
|
int style_set(style_t *, const WCHAR *fg, const WCHAR *bg);
|
||||||
|
int style_add(style_t *, const WCHAR *fg, const WCHAR *bg);
|
||||||
|
|
||||||
|
void apply_styles(void);
|
||||||
|
|
||||||
|
#endif /* !TTS_STYLE_H */
|
||||||
65
tts.h
Normal file
65
tts.h
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_H
|
||||||
|
#define TTS_H
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "wide.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include "entry.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configuration options.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int show_billable;
|
||||||
|
extern int delete_advance;
|
||||||
|
extern int mark_advance;
|
||||||
|
extern int bill_advance;
|
||||||
|
extern int bill_increment;
|
||||||
|
extern WCHAR *auto_nonbillable;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global state.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern entry_t *curent;
|
||||||
|
extern volatile sig_atomic_t doexit;
|
||||||
|
extern int pagestart;
|
||||||
|
extern time_t itime;
|
||||||
|
|
||||||
|
int load(void);
|
||||||
|
int save(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Command history.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NHIST 50
|
||||||
|
typedef struct histent {
|
||||||
|
WCHAR *he_text;
|
||||||
|
TAILQ_ENTRY(histent) he_entries;
|
||||||
|
} histent_t;
|
||||||
|
typedef TAILQ_HEAD(hentlist, histent) hentlist_t;
|
||||||
|
|
||||||
|
typedef struct history {
|
||||||
|
int hi_nents;
|
||||||
|
hentlist_t hi_ents;
|
||||||
|
} history_t;
|
||||||
|
|
||||||
|
history_t *hist_new(void);
|
||||||
|
void hist_add(history_t *, WCHAR const *);
|
||||||
|
|
||||||
|
extern history_t *searchhist;
|
||||||
|
extern history_t *prompthist;
|
||||||
|
|
||||||
|
#endif /* !TTS_H */
|
||||||
30
tts_curses.h
Normal file
30
tts_curses.h
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_CURSES_H
|
||||||
|
#define TTS_CURSES_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#if defined HAVE_NCURSESW_CURSES_H
|
||||||
|
# include <ncursesw/curses.h>
|
||||||
|
#elif defined HAVE_NCURSESW_H
|
||||||
|
# include <ncursesw.h>
|
||||||
|
#elif defined HAVE_NCURSES_CURSES_H
|
||||||
|
# include <ncurses/curses.h>
|
||||||
|
#elif defined HAVE_NCURSES_H
|
||||||
|
# include <ncurses.h>
|
||||||
|
#elif defined HAVE_CURSES_H
|
||||||
|
# include <curses.h>
|
||||||
|
#else
|
||||||
|
# error "SVR4 or XSI compatible curses header file required"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !TTS_CURSES_H */
|
||||||
65
ui.c
Normal file
65
ui.c
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ui.h"
|
||||||
|
#include "tts.h"
|
||||||
|
|
||||||
|
WINDOW *titwin, *statwin, *listwin;
|
||||||
|
int in_curses;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move the cursor to the next entry after an operation like mark or deleted.
|
||||||
|
* If there are no suitable entries after this one, move it backwards instead.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
cursadvance()
|
||||||
|
{
|
||||||
|
entry_t *en;
|
||||||
|
|
||||||
|
if (!curent) {
|
||||||
|
curent = TAILQ_FIRST(&entries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to find the next suitable entry to move the cursor to.
|
||||||
|
*/
|
||||||
|
for (en = TAILQ_NEXT(curent, en_entries); en; en = TAILQ_NEXT(en, en_entries)) {
|
||||||
|
if (!showinv && en->en_flags.efl_invoiced)
|
||||||
|
continue;
|
||||||
|
curent = en;
|
||||||
|
if (!curent->en_flags.efl_visible)
|
||||||
|
pagestart++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No entries; if the current entry is visible, stay here, otherwise
|
||||||
|
* try moving backwards instead.
|
||||||
|
*/
|
||||||
|
if (showinv || !curent->en_flags.efl_invoiced)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (en = TAILQ_PREV(curent, entrylist, en_entries); en;
|
||||||
|
en = TAILQ_PREV(en, entrylist, en_entries)) {
|
||||||
|
if (!showinv && en->en_flags.efl_invoiced)
|
||||||
|
continue;
|
||||||
|
curent = en;
|
||||||
|
if (!curent->en_flags.efl_visible)
|
||||||
|
pagestart--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Couldn't find any entries at all?
|
||||||
|
*/
|
||||||
|
curent = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
37
ui.h
Normal file
37
ui.h
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_UI_H
|
||||||
|
#define TTS_UI_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "tts.h"
|
||||||
|
#include "tts_curses.h"
|
||||||
|
#include "wide.h"
|
||||||
|
|
||||||
|
extern WINDOW *titwin, *statwin, *listwin;
|
||||||
|
extern int in_curses;
|
||||||
|
extern int showinv;
|
||||||
|
|
||||||
|
void cursadvance(void);
|
||||||
|
|
||||||
|
void drawstatus (const WCHAR *msg, ...);
|
||||||
|
void vdrawstatus (const WCHAR *msg, va_list);
|
||||||
|
void drawheader (void);
|
||||||
|
void drawentries (void);
|
||||||
|
|
||||||
|
WCHAR *prompt (WCHAR const *, WCHAR const *, history_t *);
|
||||||
|
int prduration (WCHAR *prompt, int *h, int *m, int *s);
|
||||||
|
int yesno (WCHAR const *);
|
||||||
|
void errbox (WCHAR const *, ...);
|
||||||
|
void verrbox (WCHAR const *, va_list);
|
||||||
|
|
||||||
|
#endif /* !TTS_UI_H */
|
||||||
28
variable.h
Normal file
28
variable.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_VARIABLE_H
|
||||||
|
#define TTS_VARIABLE_H
|
||||||
|
|
||||||
|
#include "wide.h"
|
||||||
|
|
||||||
|
typedef struct variable {
|
||||||
|
WCHAR const *va_name;
|
||||||
|
int va_type;
|
||||||
|
void *va_addr;
|
||||||
|
} variable_t;
|
||||||
|
|
||||||
|
#define VTYPE_INT 1
|
||||||
|
#define VTYPE_BOOL 2
|
||||||
|
#define VTYPE_STRING 3
|
||||||
|
|
||||||
|
variable_t *find_variable(const WCHAR *name);
|
||||||
|
|
||||||
|
#endif /* !TTS_VARIABLE_H */
|
||||||
26
wide.c
Normal file
26
wide.c
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "wide.h"
|
||||||
|
#include "tts_curses.h"
|
||||||
|
|
||||||
|
#ifdef NEED_TTS_WGETCH
|
||||||
|
static int
|
||||||
|
tts_wgetch(win, d)
|
||||||
|
WINDOW *win;
|
||||||
|
int *d;
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
if ((c = wgetch(win)) == ERR)
|
||||||
|
return ERR;
|
||||||
|
*d = c;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
#endif /* !NEED_TTS_WGETCH */
|
||||||
81
wide.h
Normal file
81
wide.h
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* TTS - track your time.
|
||||||
|
* Copyright (c) 2012-2014 Felicity Tarnell.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely. This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TTS_WIDE_H
|
||||||
|
#define TTS_WIDE_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "tts_curses.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_CURSES_ENHANCED
|
||||||
|
# include <wchar.h>
|
||||||
|
|
||||||
|
# define WPFX(x) wcs##x
|
||||||
|
# define WIDE(x) L##x
|
||||||
|
# define ISX(x) isw##x
|
||||||
|
# define WCHAR wchar_t
|
||||||
|
# define FMT_L "l"
|
||||||
|
# define SNPRINTF swprintf
|
||||||
|
# define VSNPRINTF vswprintf
|
||||||
|
# define SSCANF swscanf
|
||||||
|
# define MEMCPY wmemcpy
|
||||||
|
# define MEMMOVE wmemmove
|
||||||
|
# define MBSTOWCS mbstowcs
|
||||||
|
# define WCSTOMBS wcstombs
|
||||||
|
# define FPRINTF fwprintf
|
||||||
|
# define STRTOK wcstok
|
||||||
|
|
||||||
|
# define GETCH get_wch
|
||||||
|
# define WGETCH wget_wch
|
||||||
|
# define ADDSTR addwstr
|
||||||
|
# define WADDSTR waddwstr
|
||||||
|
# define INT wint_t
|
||||||
|
#else
|
||||||
|
# define WPFX(x) str##x
|
||||||
|
# define WIDE(x) x
|
||||||
|
# define ISX(x) is##x
|
||||||
|
# define WCHAR char
|
||||||
|
# define FMT_L
|
||||||
|
# define SNPRINTF snprintf
|
||||||
|
# define VSNPRINTF vsnprintf
|
||||||
|
# define SSCANF sscanf
|
||||||
|
# define MEMCPY memcpy
|
||||||
|
# define MEMMOVE memmove
|
||||||
|
# define MBSTOWCS strncpy
|
||||||
|
# define WCSTOMBS strncpy
|
||||||
|
# define FPRINTF fprintf
|
||||||
|
# define STRTOK strtok_r
|
||||||
|
|
||||||
|
# define ADDSTR addstr
|
||||||
|
# define WADDSTR waddstr
|
||||||
|
# define INT int
|
||||||
|
|
||||||
|
# define NEED_TTS_WGETCH
|
||||||
|
extern int tts_wgetch(WINDOW *, int *);
|
||||||
|
|
||||||
|
# define WGETCH tts_wgetch
|
||||||
|
# define GETCH(c) tts_wgetch(stdscr,c)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define STRLEN WPFX(len)
|
||||||
|
#define STRCMP WPFX(cmp)
|
||||||
|
#define STRNCMP WPFX(ncmp)
|
||||||
|
#define STRCPY WPFX(cpy)
|
||||||
|
#define STRNCPY WPFX(ncpy)
|
||||||
|
#define STRSTR WPFX(str)
|
||||||
|
#define STRFTIME WPFX(ftime)
|
||||||
|
#define STRDUP WPFX(dup)
|
||||||
|
#define STRTOL WPFX(tol)
|
||||||
|
|
||||||
|
#define ISSPACE ISX(space)
|
||||||
|
|
||||||
|
#define WSIZEOF(s) (sizeof(s) / sizeof(WCHAR))
|
||||||
|
|
||||||
|
#endif /* !TTS_WIDE_H */
|
||||||
Loading…
Add table
Add a link
Reference in a new issue