1010 lines
25 KiB
C
Executable File
1010 lines
25 KiB
C
Executable File
/* $calcurse: utils.c,v 1.55 2008/12/07 09:20:38 culot Exp $ */
|
|
|
|
/*
|
|
* Calcurse - text-based organizer
|
|
* Copyright (c) 2004-2008 Frederic Culot
|
|
*
|
|
* 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., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*
|
|
* Send your feedback or comments to : calcurse@culot.org
|
|
* Calcurse home page : http://culot.org/calcurse
|
|
*
|
|
*/
|
|
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <math.h>
|
|
#include <errno.h>
|
|
|
|
#include "i18n.h"
|
|
#include "wins.h"
|
|
#include "custom.h"
|
|
#include "keys.h"
|
|
#include "utils.h"
|
|
|
|
#define NB_CAL_CMDS 26 /* number of commands while in cal view */
|
|
#define NB_APP_CMDS 30 /* same thing while in appointment view */
|
|
#define NB_TOD_CMDS 30 /* same thing while in todo view */
|
|
#define TOTAL_CMDS NB_CAL_CMDS + NB_APP_CMDS + NB_TOD_CMDS
|
|
#define CMDS_PER_LINE 6 /* max number of commands per line */
|
|
|
|
static unsigned status_page;
|
|
|
|
/* General routine to exit calcurse properly. */
|
|
void
|
|
exit_calcurse (int status)
|
|
{
|
|
clear ();
|
|
refresh ();
|
|
endwin ();
|
|
ui_mode = UI_CMDLINE;
|
|
calendar_stop_date_thread ();
|
|
exit (status);
|
|
}
|
|
|
|
/* Function to exit on internal error. */
|
|
void
|
|
ierror (const char *errmsg, ierror_sev_e sev)
|
|
{
|
|
WINDOW *errwin;
|
|
char *label = _("INTERNAL ERROR");
|
|
char *exitmsg = _("calcurse will now exit...");
|
|
char *reportmsg = _("Please report the following bug:");
|
|
const int winrow = 10;
|
|
const int wincol = col - 2;
|
|
const int msglen = wincol - 2;
|
|
char msg[msglen];
|
|
|
|
strncpy (msg, errmsg, msglen);
|
|
errwin = newwin (winrow, wincol, (row - winrow) / 2, (col - wincol) / 2);
|
|
custom_apply_attr (errwin, ATTR_HIGHEST);
|
|
box (errwin, 0, 0);
|
|
wins_show (errwin, label);
|
|
if (sev == IERROR_FATAL)
|
|
mvwprintw (errwin, 3, 1, reportmsg);
|
|
mvwprintw (errwin, 5, (wincol - strlen (msg)) / 2, "%s", msg);
|
|
if (sev == IERROR_FATAL)
|
|
mvwprintw (errwin, winrow - 2, wincol - strlen (exitmsg) - 1, "%s",
|
|
exitmsg);
|
|
custom_remove_attr (errwin, ATTR_HIGHEST);
|
|
wrefresh (errwin);
|
|
(void)wgetch (errwin);
|
|
if (sev == IERROR_FATAL)
|
|
exit_calcurse (EXIT_FAILURE);
|
|
}
|
|
|
|
/* Function to handle an assertion failure. */
|
|
void
|
|
aerror (const char *file, int line, const char *assertion)
|
|
{
|
|
char errmsg[BUFSIZ];
|
|
|
|
snprintf (errmsg, BUFSIZ,
|
|
"assert \"%s\" failed: file \"%s\", line %d", assertion, file,
|
|
line);
|
|
ierror (errmsg, IERROR_FATAL);
|
|
}
|
|
|
|
void
|
|
warnbox (const char *msg)
|
|
{
|
|
const int WINCOL = col - 2;
|
|
const int WINROW = 10;
|
|
const int MSGLEN = WINCOL - 2;
|
|
WINDOW *warnwin;
|
|
char displmsg[MSGLEN];
|
|
|
|
if (msg == NULL)
|
|
return;
|
|
strncpy (displmsg, msg, MSGLEN);
|
|
warnwin = popup (WINROW, WINCOL, (row - WINROW) / 2, (col - WINCOL) / 2,
|
|
"/!\\", displmsg, 1);
|
|
wrefresh (warnwin);
|
|
(void)wgetch (warnwin);
|
|
delwin (warnwin);
|
|
doupdate ();
|
|
}
|
|
|
|
/*
|
|
* Print a message in the status bar.
|
|
* Message texts for first line and second line are to be provided.
|
|
*/
|
|
void
|
|
status_mesg (char *mesg_line1, char *mesg_line2)
|
|
{
|
|
erase_status_bar ();
|
|
custom_apply_attr (win[STA].p, ATTR_HIGHEST);
|
|
mvwprintw (win[STA].p, 0, 0, mesg_line1);
|
|
mvwprintw (win[STA].p, 1, 0, mesg_line2);
|
|
custom_remove_attr (win[STA].p, ATTR_HIGHEST);
|
|
}
|
|
|
|
/* Erase status bar. */
|
|
void
|
|
erase_status_bar (void)
|
|
{
|
|
erase_window_part (win[STA].p, 0, 0, col, STATUSHEIGHT);
|
|
}
|
|
|
|
/* Erase part of a window. */
|
|
void
|
|
erase_window_part (WINDOW *win, int first_col, int first_row, int last_col,
|
|
int last_row)
|
|
{
|
|
int c, r;
|
|
|
|
for (r = first_row; r <= last_row; r++)
|
|
{
|
|
for (c = first_col; c <= last_col; c++)
|
|
{
|
|
mvwprintw (win, r, c, " ");
|
|
}
|
|
}
|
|
wnoutrefresh (win);
|
|
}
|
|
|
|
/* draws a popup window */
|
|
WINDOW *
|
|
popup (int pop_row, int pop_col, int pop_y, int pop_x, char *title, char *msg,
|
|
int hint)
|
|
{
|
|
char *any_key = _("Press any key to continue...");
|
|
char label[BUFSIZ];
|
|
WINDOW *popup_win;
|
|
const int MSGXPOS = 5;
|
|
|
|
popup_win = newwin (pop_row, pop_col, pop_y, pop_x);
|
|
keypad (popup_win, TRUE);
|
|
custom_apply_attr (popup_win, ATTR_HIGHEST);
|
|
box (popup_win, 0, 0);
|
|
snprintf (label, BUFSIZ, "%s", title);
|
|
wins_show (popup_win, label);
|
|
if (msg)
|
|
mvwprintw (popup_win, MSGXPOS, (pop_col - strlen (msg)) / 2, "%s", msg);
|
|
if (hint)
|
|
mvwprintw (popup_win, pop_row - 2, pop_col - (strlen (any_key) + 1), "%s",
|
|
any_key);
|
|
custom_remove_attr (popup_win, ATTR_HIGHEST);
|
|
wrefresh (popup_win);
|
|
doupdate ();
|
|
|
|
return popup_win;
|
|
}
|
|
|
|
/* prints in middle of a panel */
|
|
void
|
|
print_in_middle (WINDOW *win, int starty, int startx, int width, char *string)
|
|
{
|
|
int length, x, y;
|
|
float temp;
|
|
|
|
if (win == NULL)
|
|
win = stdscr;
|
|
getyx (win, y, x);
|
|
if (startx != 0)
|
|
x = startx;
|
|
if (starty != 0)
|
|
y = starty;
|
|
if (width == 0)
|
|
width = 80;
|
|
|
|
length = strlen (string);
|
|
temp = (width - length) / 2;
|
|
x = startx + (int) temp;
|
|
custom_apply_attr (win, ATTR_HIGHEST);
|
|
mvwprintw (win, y, x, "%s", string);
|
|
custom_remove_attr (win, ATTR_HIGHEST);
|
|
}
|
|
|
|
/*
|
|
* Draw the cursor at the correct position in string.
|
|
* As echoing is not set, we need to know the string we are working on to
|
|
* handle display correctly.
|
|
*/
|
|
static void
|
|
showcursor (WINDOW *win, int y, int pos, char *str, int l, int offset)
|
|
{
|
|
char *nc;
|
|
|
|
nc = str + pos;
|
|
wmove (win, y, pos - offset);
|
|
#define SPACE 32
|
|
(pos >= l) ? waddch (win, SPACE | A_REVERSE) : waddch (win, *nc | A_REVERSE);
|
|
#undef SPACE
|
|
}
|
|
|
|
/* Print the string at the desired position. */
|
|
static void
|
|
showstring (WINDOW *win, int y, int x, char *str, int len, int pos)
|
|
{
|
|
const int rec = 30, border = 3;
|
|
const int max_col = col - border, max_len = max_col - rec;
|
|
int page, max_page, offset, c = 0;
|
|
char *orig;
|
|
|
|
orig = str;
|
|
max_page = (len - rec) / max_len;
|
|
page = (pos - rec) / max_len;
|
|
offset = page * max_len;
|
|
str += offset;
|
|
mvwaddnstr (win, y, x, str, MIN (len, max_col));
|
|
wclrtoeol (win);
|
|
if (page > 0 && page < max_page)
|
|
{
|
|
c = '*';
|
|
}
|
|
else if (page > 0)
|
|
{
|
|
c = '<';
|
|
}
|
|
else if (page < max_page)
|
|
{
|
|
c = '>';
|
|
}
|
|
else
|
|
c = 0;
|
|
mvwprintw (win, y, col - 1, "%c", c);
|
|
showcursor (win, y, pos, orig, len, offset);
|
|
}
|
|
|
|
/* Delete a character at the given position in string. */
|
|
static void
|
|
del_char (int pos, char *str)
|
|
{
|
|
int len;
|
|
|
|
str += pos;
|
|
len = strlen (str) + 1;
|
|
memmove (str, str + 1, len);
|
|
}
|
|
|
|
/* Add a character at the given position in string. */
|
|
static char *
|
|
add_char (int pos, int ch, char *str)
|
|
{
|
|
int len;
|
|
|
|
str += pos;
|
|
len = strlen (str) + 1;
|
|
memmove (str + 1, str, len);
|
|
*str = ch;
|
|
return (str += len);
|
|
}
|
|
|
|
/*
|
|
* Getstring allows to get user input and to print it on a window,
|
|
* even if noecho() is on. This function is also used to modify an existing
|
|
* text (the variable string can be non-NULL).
|
|
* We need to do the echoing manually because of the multi-threading
|
|
* environment, otherwise the cursor would move from place to place without
|
|
* control.
|
|
*/
|
|
int
|
|
getstring (WINDOW *win, char *str, int l, int x, int y)
|
|
{
|
|
int ch, newpos, len = 0;
|
|
char *orig;
|
|
|
|
orig = str;
|
|
custom_apply_attr (win, ATTR_HIGHEST);
|
|
for (; *str; ++str, ++len)
|
|
;
|
|
newpos = x + len;
|
|
showstring (win, y, x, orig, len, newpos);
|
|
|
|
while ((ch = wgetch (win)) != '\n')
|
|
{
|
|
switch (ch)
|
|
{
|
|
case KEY_BACKSPACE: /* delete one character */
|
|
case 330:
|
|
case 127:
|
|
case CTRL ('H'):
|
|
if (len > 0)
|
|
{
|
|
--newpos;
|
|
--len;
|
|
if (newpos >= x + len)
|
|
--str;
|
|
else /* to be deleted inside string */
|
|
del_char (newpos, orig);
|
|
}
|
|
break;
|
|
|
|
case CTRL ('D'): /* delete next character */
|
|
if (newpos != (x + len))
|
|
{
|
|
--len;
|
|
if (newpos >= x + len)
|
|
--str;
|
|
else
|
|
del_char (newpos, orig);
|
|
}
|
|
else
|
|
printf ("\a");
|
|
break;
|
|
|
|
case CTRL ('K'): /* delete to end-of-line */
|
|
str = orig + newpos;
|
|
*str = 0;
|
|
len -= (len - newpos);
|
|
break;
|
|
|
|
case CTRL ('A'): /* go to begginning of string */
|
|
newpos = x;
|
|
break;
|
|
|
|
case CTRL ('E'): /* go to end of string */
|
|
newpos = x + len;
|
|
break;
|
|
|
|
case KEY_LEFT: /* move one char backward */
|
|
case CTRL ('B'):
|
|
if (newpos > x)
|
|
newpos--;
|
|
break;
|
|
|
|
case KEY_RIGHT: /* move one char forward */
|
|
case CTRL ('F'):
|
|
if (newpos < len)
|
|
newpos++;
|
|
break;
|
|
|
|
case KEY_GENERIC_ESCAPE: /* cancel editing */
|
|
return (GETSTRING_ESC);
|
|
break;
|
|
|
|
default: /* insert one character */
|
|
if (len < l - 1)
|
|
{
|
|
if (newpos >= len)
|
|
{
|
|
str = orig + newpos;
|
|
*str++ = ch;
|
|
}
|
|
else // char is to be inserted inside string
|
|
str = add_char (newpos, ch, orig);
|
|
++len;
|
|
++newpos;
|
|
}
|
|
|
|
}
|
|
showstring (win, y, x, orig, len, newpos);
|
|
doupdate ();
|
|
}
|
|
*str = 0;
|
|
custom_remove_attr (win, ATTR_HIGHEST);
|
|
return (len == 0 ? GETSTRING_RET : GETSTRING_VALID);
|
|
}
|
|
|
|
/* Update an already existing string. */
|
|
int
|
|
updatestring (WINDOW *win, char **str, int x, int y)
|
|
{
|
|
char *newstr;
|
|
int escape, len = strlen (*str) + 1;
|
|
|
|
newstr = (char *) malloc (BUFSIZ);
|
|
(void) memcpy (newstr, *str, len);
|
|
escape = getstring (win, newstr, BUFSIZ, x, y);
|
|
if (!escape)
|
|
{
|
|
len = strlen (newstr) + 1;
|
|
if ((*str = (char *) realloc (*str, len)) == NULL)
|
|
{
|
|
/* NOTREACHED */
|
|
ierror (_("FATAL ERROR in updatestring: out of memory"),
|
|
IERROR_FATAL);
|
|
}
|
|
else
|
|
(void) memcpy (*str, newstr, len);
|
|
}
|
|
free (newstr);
|
|
return (escape);
|
|
}
|
|
|
|
/* checks if a string is only made of digits */
|
|
int
|
|
is_all_digit (char *string)
|
|
{
|
|
int digit, i;
|
|
int all_digit;
|
|
|
|
digit = 0;
|
|
all_digit = 0;
|
|
|
|
for (i = 0; i <= strlen (string); i++)
|
|
if (isdigit (string[i]) != 0)
|
|
digit++;
|
|
if (digit == strlen (string))
|
|
all_digit = 1;
|
|
return (all_digit);
|
|
}
|
|
|
|
/*
|
|
* Draws the status bar.
|
|
* To add a keybinding, insert a new binding_t item, add it in the *binding
|
|
* table, and update the NB_CAL_CMDS, NB_APP_CMDS or NB_TOD_CMDS defines,
|
|
* depending on which panel the added keybind is assigned to.
|
|
*/
|
|
void
|
|
status_bar (void)
|
|
{
|
|
#define NB_PANELS 3 /* 3 panels: CALENDAR, APPOINTMENT, TODO */
|
|
window_e which_pan;
|
|
int start, end;
|
|
const int pos[NB_PANELS + 1] =
|
|
{ 0, NB_CAL_CMDS, NB_CAL_CMDS + NB_APP_CMDS, TOTAL_CMDS };
|
|
|
|
binding_t crdts = {_("Credits"), KEY_GENERIC_CREDITS};
|
|
binding_t help = {_("Help"), KEY_GENERIC_HELP};
|
|
binding_t quit = {_("Quit"), KEY_GENERIC_QUIT};
|
|
binding_t save = {_("Save"), KEY_GENERIC_SAVE};
|
|
binding_t chgvu = {_("Chg View"), KEY_GENERIC_CHANGE_VIEW};
|
|
binding_t import = {_("Import"), KEY_GENERIC_IMPORT};
|
|
binding_t export = {_("Export"), KEY_GENERIC_EXPORT};
|
|
binding_t togo = {_("Go to"), KEY_GENERIC_GOTO};
|
|
binding_t othr = {_("OtherCmd"), KEY_GENERIC_OTHER_CMD};
|
|
binding_t conf = {_("Config"), KEY_GENERIC_CONFIG_MENU};
|
|
binding_t draw = {_("Redraw"), KEY_GENERIC_REDRAW};
|
|
binding_t appt = {_("Add Appt"), KEY_GENERIC_ADD_APPT};
|
|
binding_t todo = {_("Add Todo"), KEY_GENERIC_ADD_TODO};
|
|
binding_t gnday = {_("+1 Day"), KEY_GENERIC_NEXT_DAY};
|
|
binding_t gpday = {_("-1 Day"), KEY_GENERIC_PREV_DAY};
|
|
binding_t gnweek = {_("+1 Week"), KEY_GENERIC_NEXT_WEEK};
|
|
binding_t gpweek = {_("-1 Week"), KEY_GENERIC_PREV_WEEK};
|
|
binding_t today = {_("Today"), KEY_GENERIC_GOTO_TODAY};
|
|
binding_t up = {_("Up"), KEY_MOVE_UP};
|
|
binding_t down = {_("Down"), KEY_MOVE_DOWN};
|
|
binding_t left = {_("Left"), KEY_MOVE_LEFT};
|
|
binding_t right = {_("Right"), KEY_MOVE_RIGHT};
|
|
binding_t weekb = {_("beg Week"), KEY_START_OF_WEEK};
|
|
binding_t weeke = {_("end Week"), KEY_END_OF_WEEK};
|
|
binding_t add = {_("Add Item"), KEY_ADD_ITEM};
|
|
binding_t del = {_("Del Item"), KEY_DEL_ITEM};
|
|
binding_t edit = {_("Edit Itm"), KEY_EDIT_ITEM};
|
|
binding_t view = {_("View"), KEY_VIEW_ITEM};
|
|
binding_t flag = {_("Flag Itm"), KEY_FLAG_ITEM};
|
|
binding_t rept = {_("Repeat"), KEY_REPEAT_ITEM};
|
|
binding_t enote = {_("EditNote"), KEY_EDIT_NOTE};
|
|
binding_t vnote = {_("ViewNote"), KEY_VIEW_NOTE};
|
|
binding_t rprio = {_("Prio.+"), KEY_RAISE_PRIORITY};
|
|
binding_t lprio = {_("Prio.-"), KEY_LOWER_PRIORITY};
|
|
|
|
binding_t *binding[TOTAL_CMDS] = {
|
|
/* calendar keys */
|
|
&help, &quit, &save, &chgvu, &import, &export, &up, &down, &left, &right,
|
|
&togo, &othr, &weekb, &weeke, &conf, &draw, &appt, &todo, &gnday, &gpday,
|
|
&gnweek, &gpweek, &today, &othr, &crdts, &othr,
|
|
/* appointment keys */
|
|
&help, &quit, &save, &chgvu, &import, &export, &add, &del, &edit, &view,
|
|
&draw, &othr, &rept, &flag, &enote, &vnote, &up, &down, &gnday, &gpday,
|
|
&gnweek, &gpweek, &togo, &othr, &today, &conf, &appt, &todo, &crdts, &othr,
|
|
/* todo keys */
|
|
&help, &quit, &save, &chgvu, &import, &export, &add, &del, &edit, &view,
|
|
&draw, &othr, &rprio, &lprio, &enote, &vnote, &up, &down, &gnday, &gpday,
|
|
&gnweek, &gpweek, &togo, &othr, &today, &conf, &appt, &todo, &crdts, &othr
|
|
};
|
|
|
|
/* Drawing the keybinding with attribute and label without. */
|
|
which_pan = wins_slctd ();
|
|
start = pos[which_pan] + 2 * KEYS_CMDS_PER_LINE * (status_page - 1);
|
|
end = MIN (start + 2 * KEYS_CMDS_PER_LINE, pos[which_pan + 1]);
|
|
keys_display_bindings_bar (win[STA].p, binding, start, end);
|
|
}
|
|
|
|
long
|
|
date2sec (date_t day, unsigned hour, unsigned min)
|
|
{
|
|
struct tm start, *lt;
|
|
time_t tstart, t;
|
|
|
|
t = time (NULL);
|
|
lt = localtime (&t);
|
|
start = *lt;
|
|
|
|
start.tm_mon = day.mm;
|
|
start.tm_mday = day.dd;
|
|
start.tm_year = day.yyyy;
|
|
start.tm_hour = hour;
|
|
start.tm_min = min;
|
|
start.tm_sec = 0;
|
|
start.tm_isdst = -1;
|
|
start.tm_year -= 1900;
|
|
start.tm_mon--;
|
|
tstart = mktime (&start);
|
|
if (tstart == -1)
|
|
{
|
|
fputs (_("FATAL ERROR in date2sec: failure in mktime\n"), stderr);
|
|
fprintf (stderr, "%u %u %u %u %u\n", day.yyyy, day.mm, day.dd,
|
|
hour, min);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
return (tstart);
|
|
}
|
|
|
|
/* Return a string containing the hour of a given date in seconds. */
|
|
char *
|
|
date_sec2hour_str (long sec)
|
|
{
|
|
const int TIME_LEN = 6;
|
|
struct tm *lt;
|
|
time_t t;
|
|
char *timestr;
|
|
|
|
t = sec;
|
|
lt = localtime (&t);
|
|
timestr = (char *) malloc (TIME_LEN);
|
|
snprintf (timestr, TIME_LEN, "%02u:%02u", lt->tm_hour, lt->tm_min);
|
|
return (timestr);
|
|
}
|
|
|
|
/* Return a string containing the date, given a date in seconds. */
|
|
char *
|
|
date_sec2date_str (long sec, char *datefmt)
|
|
{
|
|
struct tm *lt;
|
|
time_t t;
|
|
char *datestr;
|
|
|
|
datestr = (char *) malloc (sizeof (char) * BUFSIZ);
|
|
|
|
if (sec == 0)
|
|
snprintf (datestr, BUFSIZ, "0");
|
|
else
|
|
{
|
|
t = sec;
|
|
lt = localtime (&t);
|
|
strftime (datestr, BUFSIZ, datefmt, lt);
|
|
}
|
|
|
|
return (datestr);
|
|
}
|
|
|
|
/* Generic function to format date. */
|
|
void
|
|
date_sec2date_fmt (long sec, const char *fmt, char *datef)
|
|
{
|
|
struct tm *lt;
|
|
time_t t;
|
|
|
|
t = sec;
|
|
lt = localtime (&t);
|
|
strftime (datef, BUFSIZ, fmt, lt);
|
|
}
|
|
|
|
/*
|
|
* Used to change date by adding a certain amount of days or weeks.
|
|
*/
|
|
long
|
|
date_sec_change (long date, int delta_month, int delta_day)
|
|
{
|
|
struct tm *lt;
|
|
time_t t;
|
|
|
|
t = date;
|
|
lt = localtime (&t);
|
|
lt->tm_mon += delta_month;
|
|
lt->tm_mday += delta_day;
|
|
t = mktime (lt);
|
|
if (t == -1)
|
|
{
|
|
fputs (_("FATAL ERROR in date_sec_change: failure in mktime\n"), stderr);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
/*
|
|
* Return a long containing the date which is updated taking into account
|
|
* the new time and date entered by the user.
|
|
*/
|
|
long
|
|
update_time_in_date (long date, unsigned hr, unsigned mn)
|
|
{
|
|
struct tm *lt;
|
|
time_t t, new_date;
|
|
|
|
t = date;
|
|
lt = localtime (&t);
|
|
lt->tm_hour = hr;
|
|
lt->tm_min = mn;
|
|
new_date = mktime (lt);
|
|
ASSERT (new_date != -1);
|
|
|
|
return (new_date);
|
|
}
|
|
|
|
/*
|
|
* Returns the date in seconds from year 1900.
|
|
* If no date is entered, current date is chosen.
|
|
*/
|
|
long
|
|
get_sec_date (date_t date)
|
|
{
|
|
struct tm *ptrtime;
|
|
time_t timer;
|
|
long long_date;
|
|
char current_day[] = "dd ";
|
|
char current_month[] = "mm ";
|
|
char current_year[] = "yyyy ";
|
|
|
|
if (date.yyyy == 0 && date.mm == 0 && date.dd == 0)
|
|
{
|
|
timer = time (NULL);
|
|
ptrtime = localtime (&timer);
|
|
strftime (current_day, strlen (current_day), "%d", ptrtime);
|
|
strftime (current_month, strlen (current_month), "%m", ptrtime);
|
|
strftime (current_year, strlen (current_year), "%Y", ptrtime);
|
|
date.mm = atoi (current_month);
|
|
date.dd = atoi (current_day);
|
|
date.yyyy = atoi (current_year);
|
|
}
|
|
long_date = date2sec (date, 0, 0);
|
|
return (long_date);
|
|
}
|
|
|
|
long
|
|
min2sec (unsigned minutes)
|
|
{
|
|
return (minutes * MININSEC);
|
|
}
|
|
|
|
/*
|
|
* Checks if a time has a good format.
|
|
* The format could be either HH:MM or H:MM or MM, and we should have:
|
|
* 0 <= HH <= 24 and 0 <= MM < 999.
|
|
* This function returns 1 if the entered time is correct and in
|
|
* [h:mm] or [hh:mm] format, and 2 if the entered time is correct and entered
|
|
* in [mm] format.
|
|
*/
|
|
int
|
|
check_time (char *string)
|
|
{
|
|
int ok = 0;
|
|
char hour[] = " ";
|
|
char minutes[] = " ";
|
|
|
|
if (((strlen (string) == 2) || (strlen (string) == 3))
|
|
&& (isdigit (string[0]) != 0) && (isdigit (string[1]) != 0))
|
|
{
|
|
strncpy (minutes, string, 2);
|
|
if (atoi (minutes) >= 0)
|
|
ok = 2; /* [MM] format */
|
|
}
|
|
else if ((strlen (string) == 4) && (isdigit (string[0]) != 0)
|
|
&& (isdigit (string[2]) != 0) && (isdigit (string[3]) != 0)
|
|
&& (string[1] == ':'))
|
|
{
|
|
strncpy (hour, string, 1);
|
|
strncpy (minutes, string + 2, 2);
|
|
if ((atoi (hour) <= 24) && (atoi (hour) >= 0)
|
|
&& (atoi (minutes) < MININSEC) && (atoi (minutes) >= 0))
|
|
ok = 1; /* [H:MM] format */
|
|
}
|
|
else if ((strlen (string) == 5) && (isdigit (string[0]) != 0)
|
|
&& (isdigit (string[1]) != 0) && (isdigit (string[3]) != 0)
|
|
&& (isdigit (string[4]) != 0) && (string[2] == ':'))
|
|
{
|
|
strncpy (hour, string, 2);
|
|
strncpy (minutes, string + 3, 2);
|
|
if ((atoi (hour) <= 24) && (atoi (hour) >= 0)
|
|
&& (atoi (minutes) < MININSEC) && (atoi (minutes) >= 0))
|
|
ok = 1; /* [HH:MM] format */
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
/*
|
|
* Display a scroll bar when there are so many items that they
|
|
* can not be displayed inside the corresponding panel.
|
|
*/
|
|
void
|
|
draw_scrollbar (WINDOW *win, int y, int x, int length,
|
|
int bar_top, int bar_bottom, bool hilt)
|
|
{
|
|
mvwvline (win, bar_top, x, ACS_VLINE, bar_bottom - bar_top);
|
|
if (hilt)
|
|
custom_apply_attr (win, ATTR_HIGHEST);
|
|
wattron (win, A_REVERSE);
|
|
mvwvline (win, y, x, ' ', length);
|
|
wattroff (win, A_REVERSE);
|
|
if (hilt)
|
|
custom_remove_attr (win, ATTR_HIGHEST);
|
|
}
|
|
|
|
/*
|
|
* Print an item (either an appointment, event, or todo) in a
|
|
* popup window. This is useful if an item description is too
|
|
* long to fit in its corresponding panel window.
|
|
*/
|
|
void
|
|
item_in_popup (char *saved_a_start, char *saved_a_end, char *msg,
|
|
char *pop_title)
|
|
{
|
|
WINDOW *popup_win, *pad;
|
|
const int margin_left = 4, margin_top = 4;
|
|
const int winl = row - 5, winw = col - margin_left;
|
|
const int padl = winl - 2, padw = winw - margin_left;
|
|
|
|
pad = newpad (padl, padw);
|
|
popup_win = popup (winl, winw, 1, 2, pop_title, (char *)0, 1);
|
|
if (strncmp (pop_title, _("Appointment"), 11) == 0)
|
|
{
|
|
mvwprintw (popup_win, margin_top, margin_left, "- %s -> %s",
|
|
saved_a_start, saved_a_end);
|
|
}
|
|
mvwprintw (pad, 0, margin_left, "%s", msg);
|
|
wmove (win[STA].p, 0, 0);
|
|
pnoutrefresh (pad, 0, 0, margin_top + 2, margin_left, padl, winw);
|
|
doupdate ();
|
|
(void)wgetch (popup_win);
|
|
delwin (pad);
|
|
delwin (popup_win);
|
|
}
|
|
|
|
/* Reset the status bar page. */
|
|
void
|
|
reset_status_page (void)
|
|
{
|
|
status_page = 1;
|
|
}
|
|
|
|
/* Update the status bar page number to display other commands. */
|
|
void
|
|
other_status_page (int panel)
|
|
{
|
|
int nb_item = 0, max_page;
|
|
char *error = _("FATAL ERROR in other_status_page: unknown panel\n");
|
|
|
|
switch (panel)
|
|
{
|
|
case CAL:
|
|
nb_item = NB_CAL_CMDS;
|
|
break;
|
|
case APP:
|
|
nb_item = NB_APP_CMDS;
|
|
break;
|
|
case TOD:
|
|
nb_item = NB_TOD_CMDS;
|
|
break;
|
|
default:
|
|
ierror (error, IERROR_FATAL);
|
|
}
|
|
max_page = ceil (nb_item / (2 * CMDS_PER_LINE + 1)) + 1;
|
|
if (status_page < max_page)
|
|
status_page++;
|
|
else
|
|
status_page = 1;
|
|
}
|
|
|
|
/* Returns the beginning of current day in seconds from 1900. */
|
|
long
|
|
get_today (void)
|
|
{
|
|
struct tm *lt;
|
|
time_t current_time;
|
|
long current_day;
|
|
date_t day;
|
|
|
|
current_time = time (NULL);
|
|
lt = localtime (¤t_time);
|
|
day.mm = lt->tm_mon + 1;
|
|
day.dd = lt->tm_mday;
|
|
day.yyyy = lt->tm_year + 1900;
|
|
current_day = date2sec (day, 0, 0);
|
|
|
|
return (current_day);
|
|
}
|
|
|
|
/* Returns the current time in seconds. */
|
|
long
|
|
now (void)
|
|
{
|
|
time_t current_time;
|
|
|
|
current_time = time (NULL);
|
|
return (current_time);
|
|
}
|
|
|
|
/* Copy a string */
|
|
char *
|
|
mycpy (const char *src)
|
|
{
|
|
char *string = malloc (strlen (src) + 1);
|
|
|
|
if (string != NULL)
|
|
return (strncpy (string, src, strlen (src) + 1));
|
|
else
|
|
return (NULL);
|
|
}
|
|
|
|
long
|
|
mystrtol (const char *str)
|
|
{
|
|
char *ep;
|
|
long lval;
|
|
const char *not_a_number =
|
|
_("FATAL ERROR in mystrtol: could not convert string");
|
|
const char *out_of_range =
|
|
_("FATAL ERROR in mystrtol: number is out of range");
|
|
|
|
errno = 0;
|
|
lval = strtol (str, &ep, 10);
|
|
if (str[0] == '\0' || *ep != '\0')
|
|
ierror (not_a_number, IERROR_FATAL);
|
|
if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN))
|
|
ierror (out_of_range, IERROR_FATAL);
|
|
|
|
return (lval);
|
|
}
|
|
|
|
/* Print the given option value with appropriate color. */
|
|
void
|
|
print_bool_option_incolor (WINDOW *win, bool option, int pos_y, int pos_x)
|
|
{
|
|
int color = 0;
|
|
char option_value[BUFSIZ] = "";
|
|
|
|
if (option == true)
|
|
{
|
|
color = ATTR_TRUE;
|
|
strncpy (option_value, _("yes"), BUFSIZ);
|
|
}
|
|
else if (option == false)
|
|
{
|
|
color = ATTR_FALSE;
|
|
strncpy (option_value, _("no"), BUFSIZ);
|
|
}
|
|
else
|
|
{
|
|
ierror (_("option not defined - Problem in print_option_incolor()"),
|
|
IERROR_FATAL);
|
|
}
|
|
custom_apply_attr (win, color);
|
|
mvwprintw (win, pos_y, pos_x, "%s", option_value);
|
|
custom_remove_attr (win, color);
|
|
wnoutrefresh (win);
|
|
doupdate ();
|
|
}
|
|
|
|
/*
|
|
* Create a new unique file, and return a newly allocated string which contains
|
|
* the random part of the file name.
|
|
*/
|
|
char *
|
|
new_tempfile (const char *prefix, int trailing_len)
|
|
{
|
|
char fullname[BUFSIZ];
|
|
int prefix_len, fd;
|
|
FILE *file;
|
|
|
|
if (prefix == NULL)
|
|
return (NULL);
|
|
|
|
prefix_len = strlen (prefix);
|
|
if (prefix_len + trailing_len >= BUFSIZ)
|
|
return (NULL);
|
|
memcpy (fullname, prefix, prefix_len);
|
|
memset (fullname + prefix_len, 'X', trailing_len);
|
|
fullname[prefix_len + trailing_len] = '\0';
|
|
if ((fd = mkstemp (fullname)) == -1 || (file = fdopen (fd, "w+")) == NULL)
|
|
{
|
|
if (fd != -1)
|
|
{
|
|
unlink (fullname);
|
|
close (fd);
|
|
}
|
|
ierror (_("FATAL ERROR: temporary file could not be created!"),
|
|
IERROR_WARN);
|
|
return (NULL);
|
|
}
|
|
fclose (file);
|
|
|
|
return (strdup (fullname + prefix_len));
|
|
}
|
|
|
|
/* Erase a note previously attached to a todo, event or appointment. */
|
|
void
|
|
erase_note (char **note, erase_flag_e flag)
|
|
{
|
|
char fullname[BUFSIZ];
|
|
char *errmsg = _("FATAL ERROR in erase_note: could not remove note\n");
|
|
|
|
if (*note == NULL)
|
|
return;
|
|
if (flag != ERASE_FORCE_KEEP_NOTE)
|
|
{
|
|
snprintf (fullname, BUFSIZ, "%s%s", path_notes, *note);
|
|
if (unlink (fullname) != 0)
|
|
ierror (errmsg, IERROR_FATAL);
|
|
}
|
|
free (*note);
|
|
*note = NULL;
|
|
}
|
|
|
|
/*
|
|
* Convert a string containing a date into three integers containing the year,
|
|
* month and day.
|
|
* Returns 1 if sucessfully converted or 0 if the string is an invalid date.
|
|
*/
|
|
int
|
|
parse_date (char *date_string, int datefmt, int *year, int *month, int *day)
|
|
{
|
|
int in1, in2, in3;
|
|
int lyear, lmonth, lday;
|
|
if (date_string == NULL)
|
|
return (0);
|
|
if (sscanf (date_string, "%d / %d / %d", &in1, &in2, &in3) < 3)
|
|
return (0);
|
|
switch (datefmt)
|
|
{
|
|
case 1:
|
|
lmonth = in1;
|
|
lday = in2;
|
|
lyear = in3;
|
|
break;
|
|
case 2:
|
|
lday = in1;
|
|
lmonth = in2;
|
|
lyear = in3;
|
|
break;
|
|
case 3:
|
|
lyear = in1;
|
|
lmonth = in2;
|
|
lday = in3;
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
if (lyear < 1 || lyear > 9999 || lmonth < 1 || lmonth > 12 || lday < 1
|
|
|| lday > 31)
|
|
return (0);
|
|
if (year != NULL)
|
|
*year = lyear;
|
|
if (month != NULL)
|
|
*month = lmonth;
|
|
if (day != NULL)
|
|
*day = lday;
|
|
return (1);
|
|
}
|
|
|
|
char *
|
|
str_toupper (char *s)
|
|
{
|
|
int len;
|
|
|
|
for (len = 0; s && s[len]; len++)
|
|
s[len] = toupper (s[len]);
|
|
return s;
|
|
}
|
|
|
|
void
|
|
mem_free (void *ptr)
|
|
{
|
|
if (!ptr)
|
|
return;
|
|
free (ptr);
|
|
ptr = NULL;
|
|
}
|