summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO7
-rw-r--r--doc/manual.txt7
-rw-r--r--src/Makefile.am3
-rw-r--r--src/args.c22
-rw-r--r--src/calcurse.c12
-rw-r--r--src/calcurse.h186
-rw-r--r--src/custom.c375
-rw-r--r--src/day.c95
-rw-r--r--src/dmon.c7
-rw-r--r--src/getstring.c299
-rw-r--r--src/help.c13
-rw-r--r--src/io.c84
-rw-r--r--src/keys.c11
-rw-r--r--src/note.c90
-rw-r--r--src/notify.c26
-rw-r--r--src/recur.c4
-rw-r--r--src/todo.c82
-rw-r--r--src/utf8.c344
-rw-r--r--src/utils.c308
-rw-r--r--src/vars.c3
-rw-r--r--src/wins.c61
21 files changed, 1434 insertions, 605 deletions
diff --git a/TODO b/TODO
index b28f33d..cc32fe8 100644
--- a/TODO
+++ b/TODO
@@ -3,15 +3,12 @@ calcurse TODO list
Here is a list of modifications we will perform on calcurse in future releases.
They are grouped into three different priority categories. Feel free to send us
-an email (misc@calcurse.org or bugs@calcurse.org) if you would like to see a
-feature added in calcurse which does not appear in this list.
+an email (misc@calcurse.org) if you would like to see a feature added in
+calcurse which does not appear in this list.
High
----
-* Add support for UTF-8
-* Implement the send-item functionality to export a single item and pipe it to
- an external process
* Add a key binding to toggle between visible/hidden tasks inside todo panel
* Add an optional argument to the --next flag to check for next appointment
starting from the specified time
diff --git a/doc/manual.txt b/doc/manual.txt
index c0a992d..b2b80b0 100644
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -113,8 +113,7 @@ library already installed on your computer, but if not, you can find it at the
following url: http://ftp.gnu.org/pub/gnu/ncurses/
NOTE: It is also possible to link `calcurse` against the `ncursesw` library
- (ncurses with support for unicode). However, UTF-8 is not yet supported
- by `calcurse`.
+ (ncurses with support for unicode).
[[install_requirements_gettext]]
gettext library
@@ -805,6 +804,10 @@ $ calcurse --next | mail -s "[calcurse] upcoming appointment!" user@host.com
----
====
+`notify-all` (default: *no*)::
+ Invert the sense of flagging an appointment as `important`. If this is
+ enabled, all appointments will be notified - except for flagged ones.
+
`notify-daemon_enable` (default: *no*)::
If set to yes, daemon mode will be enabled, meaning `calcurse` will run into
background when the user's interface is exited. This will allow the
diff --git a/src/Makefile.am b/src/Makefile.am
index faacf46..12147a5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,14 +14,17 @@ calcurse_SOURCES = \
custom.c \
day.c \
event.c \
+ getstring.c \
help.c \
io.c \
keys.c \
llist.c \
+ note.c \
notify.c \
recur.c \
sigs.c \
todo.c \
+ utf8.c \
utils.c \
vars.c \
wins.c \
diff --git a/src/args.c b/src/args.c
index e447997..060ac3d 100644
--- a/src/args.c
+++ b/src/args.c
@@ -51,7 +51,7 @@
static void
usage ()
{
- char *arg_usage =
+ const char *arg_usage =
_("Usage: calcurse [-h|-v] [-N] [-an] [-t[num]] [-i<file>] [-x[format]]\n"
" [-d <date>|<num>] [-s[date]] [-r[range]]\n"
" [-c<file> | -D<dir>] [-S<regex>] [--status]\n");
@@ -61,7 +61,7 @@ usage ()
static void
usage_try ()
{
- char *arg_usage_try = _("Try 'calcurse -h' for more information.\n");
+ const char *arg_usage_try = _("Try 'calcurse -h' for more information.\n");
fputs (arg_usage_try, stdout);
}
@@ -71,14 +71,11 @@ usage_try ()
static void
version_arg ()
{
- char vtitle[BUFSIZ];
- char *vtext =
+ const char *vtext =
_("\nCopyright (c) 2004-2011 calcurse Development Team.\n"
"This is free software; see the source for copying conditions.\n");
- (void)snprintf (vtitle, BUFSIZ, _("Calcurse %s - text-based organizer\n"),
- VERSION);
- fputs (vtitle, stdout);
+ fprintf (stdout, _("Calcurse %s - text-based organizer\n"), VERSION);
fputs (vtext, stdout);
}
@@ -88,8 +85,7 @@ version_arg ()
static void
help_arg ()
{
- char htitle[BUFSIZ];
- char *htext =
+ const char *htext =
_("\nMiscellaneous:\n"
" -h, --help\n"
" print this help and exit.\n"
@@ -146,9 +142,7 @@ help_arg ()
"or read the manpage.\n"
"Mail bug reports and suggestions to <misc@calcurse.org>.\n");
- (void)snprintf (htitle, BUFSIZ, _("Calcurse %s - text-based organizer\n"),
- VERSION);
- fputs (htitle, stdout);
+ fprintf (stdout, _("Calcurse %s - text-based organizer\n"), VERSION);
usage ();
fputs (htext, stdout);
}
@@ -944,7 +938,7 @@ parse_args (int argc, char **argv, struct conf *conf)
io_check_file (path_apts, (int *)0);
io_check_file (path_conf, (int *)0);
io_load_app ();
- custom_load_conf (conf, 0); /* To get output date format. */
+ custom_load_conf (conf); /* To get output date format. */
if (dflag)
date_arg (ddate, add_line, Nflag, conf, preg);
if (rflag || sflag)
@@ -959,7 +953,7 @@ parse_args (int argc, char **argv, struct conf *conf)
io_check_file (path_apts, (int *)0);
io_check_file (path_conf, (int *)0);
vars_init (conf);
- custom_load_conf (conf, 0); /* To get output date format. */
+ custom_load_conf (conf); /* To get output date format. */
io_load_app ();
day.dd = day.mm = day.yyyy = 0;
(void)app_arg (add_line, &day, 0, Nflag, conf, preg);
diff --git a/src/calcurse.c b/src/calcurse.c
index 662f185..ab55254 100644
--- a/src/calcurse.c
+++ b/src/calcurse.c
@@ -50,7 +50,6 @@ main (int argc, char **argv)
{
struct conf conf;
struct day_items_nb inday;
- int background, foreground;
int non_interactive;
int no_data_file = 1;
int sav_hilt_app = 0;
@@ -148,7 +147,7 @@ main (int argc, char **argv)
* configuration (the display is then updated), and then
* the todo list, appointments and events.
*/
- custom_load_conf (&conf, background);
+ custom_load_conf (&conf);
wins_erase_status_bar ();
io_load_keys (conf.pager);
io_load_todo ();
@@ -283,6 +282,8 @@ main (int argc, char **argv)
case 'S':
custom_sidebar_config ();
break;
+ default:
+ continue;
}
wins_reset ();
wins_update ();
@@ -368,6 +369,13 @@ main (int argc, char **argv)
do_storage = 1;
break;
+ case KEY_PIPE_ITEM:
+ if (wins_slctd () == APP && apoint_hilt () != 0)
+ day_pipe_item (&conf);
+ else if (wins_slctd () == TOD && todo_hilt () != 0)
+ todo_pipe_item ();
+ break;
+
case KEY_RAISE_PRIORITY:
case KEY_LOWER_PRIORITY:
if (wins_slctd () == TOD && todo_hilt () != 0)
diff --git a/src/calcurse.h b/src/calcurse.h
index 383f53e..b1b8e75 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -100,22 +100,22 @@
#define DPID_PATH DIR_NAME DPID_PATH_NAME
#define NOTES_DIR DIR_NAME NOTES_DIR_NAME
-#define ATTR_FALSE 0
-#define ATTR_TRUE 1
-#define ATTR_LOWEST 2
-#define ATTR_LOW 3
-#define ATTR_MIDDLE 4
-#define ATTR_HIGH 5
-#define ATTR_HIGHEST 6
+#define ATTR_FALSE 0
+#define ATTR_TRUE 1
+#define ATTR_LOWEST 2
+#define ATTR_LOW 3
+#define ATTR_MIDDLE 4
+#define ATTR_HIGH 5
+#define ATTR_HIGHEST 6
-#define STATUSHEIGHT 2
-#define NOTESIZ 6
+#define STATUSHEIGHT 2
+#define NOTESIZ 6
/* Format for appointment hours is: HH:MM */
#define HRMIN_SIZE 6
/* Maximum number of colors available. */
-#define NBUSERCOLORS 6
+#define NBUSERCOLORS 6
/* Side bar width acceptable boundaries. */
#define SBARMINWIDTH 32
@@ -131,68 +131,68 @@
#define TM_YEAR_BASE 1900
/* Calendar window. */
-#define CALHEIGHT 12
+#define CALHEIGHT 12
/* Key definitions. */
#define CTRLVAL 0x1F
#define CTRL(x) ((x) & CTRLVAL)
-#define ESCAPE 27
-#define TAB 9
+#define ESCAPE 27
+#define TAB 9
#define SPACE 32
-#define KEYS_KEYLEN 3 /* length of each keybinding */
-#define KEYS_LABELEN 8 /* length of command description */
-#define KEYS_CMDS_PER_LINE 6 /* max number of commands per line */
-
-#define ERROR_MSG(...) do { \
- char msg[BUFSIZ]; \
- int len; \
- \
- len = snprintf (msg, BUFSIZ, "%s: %d: ", __FILE__, __LINE__); \
- (void)snprintf (msg + len, BUFSIZ - len, __VA_ARGS__); \
- if (ui_mode == UI_CURSES) \
- fatalbox (msg); \
- else \
- (void)fprintf (stderr, "%s\n", msg); \
+#define KEYS_KEYLEN 3 /* length of each keybinding */
+#define KEYS_LABELEN 8 /* length of command description */
+#define KEYS_CMDS_PER_LINE 6 /* max number of commands per line */
+
+#define ERROR_MSG(...) do { \
+ char msg[BUFSIZ]; \
+ int len; \
+ \
+ len = snprintf (msg, BUFSIZ, "%s: %d: ", __FILE__, __LINE__); \
+ (void)snprintf (msg + len, BUFSIZ - len, __VA_ARGS__); \
+ if (ui_mode == UI_CURSES) \
+ fatalbox (msg); \
+ else \
+ (void)fprintf (stderr, "%s\n", msg); \
} while (0)
-#define WARN_MSG(...) do { \
- char msg[BUFSIZ]; \
- \
- (void)snprintf (msg, BUFSIZ, __VA_ARGS__); \
- if (ui_mode == UI_CURSES) \
- warnbox (msg); \
- else \
- (void)fprintf (stderr, "%s\n", msg); \
+#define WARN_MSG(...) do { \
+ char msg[BUFSIZ]; \
+ \
+ (void)snprintf (msg, BUFSIZ, __VA_ARGS__); \
+ if (ui_mode == UI_CURSES) \
+ warnbox (msg); \
+ else \
+ (void)fprintf (stderr, "%s\n", msg); \
} while (0)
-#define EXIT(...) do { \
- ERROR_MSG(__VA_ARGS__); \
- if (ui_mode == UI_CURSES) \
- exit_calcurse (EXIT_FAILURE); \
- else \
- exit (EXIT_FAILURE); \
+#define EXIT(...) do { \
+ ERROR_MSG(__VA_ARGS__); \
+ if (ui_mode == UI_CURSES) \
+ exit_calcurse (EXIT_FAILURE); \
+ else \
+ exit (EXIT_FAILURE); \
} while (0)
-#define EXIT_IF(cond, ...) do { \
- if ((cond)) \
- EXIT(__VA_ARGS__); \
+#define EXIT_IF(cond, ...) do { \
+ if ((cond)) \
+ EXIT(__VA_ARGS__); \
} while (0)
-#define RETURN_IF(cond, ...) do { \
- if ((cond)) \
- { \
- ERROR_MSG(__VA_ARGS__); \
- return; \
- } \
+#define RETURN_IF(cond, ...) do { \
+ if ((cond)) \
+ { \
+ ERROR_MSG(__VA_ARGS__); \
+ return; \
+ } \
} while (0)
-#define RETVAL_IF(cond, val, ...) do { \
- if ((cond)) \
- { \
- ERROR_MSG(__VA_ARGS__); \
- return (val); \
- } \
+#define RETVAL_IF(cond, val, ...) do { \
+ if ((cond)) \
+ { \
+ ERROR_MSG(__VA_ARGS__); \
+ return (val); \
+ } \
} while (0)
#define STRING_BUILD(str) {str, sizeof (str) - 1}
@@ -200,6 +200,15 @@
#define TOSTRING(x) STRINGIFY(x)
#define __FILE_POS__ __FILE__ ":" TOSTRING(__LINE__)
+#define UTF8_MAXLEN 6
+#define UTF8_LENGTH(ch) ((unsigned char)ch >= 0xFC ? 6 : \
+ ((unsigned char)ch >= 0xF8 ? 5 : \
+ ((unsigned char)ch >= 0xF0 ? 4 : \
+ ((unsigned char)ch >= 0xE0 ? 3 : \
+ ((unsigned char)ch >= 0xC0 ? 2 : 1)))))
+#define UTF8_ISCONT(ch) ((unsigned char)ch >= 0x80 && \
+ (unsigned char)ch <= 0xBF)
+
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
@@ -214,7 +223,7 @@ struct conf {
char *editor;
char *pager;
char output_datefmt[BUFSIZ]; /* format for displaying date */
- int input_datefmt; /* format for reading date */
+ int input_datefmt; /* format for reading date */
};
/* Daemon-related configuration. */
@@ -236,16 +245,13 @@ enum datefmt {
DATE_FORMATS
};
-#define DATEFMT(datefmt) (datefmt == DATEFMT_MMDDYYYY ? "%m/%d/%Y" : \
- (datefmt == DATEFMT_DDMMYYYY ? "%d/%m/%Y" : \
- (datefmt == DATEFMT_YYYYMMDD ? "%Y/%m/%d" : "%Y-%m-%d")))
+#define DATEFMT(datefmt) (datefmt == DATEFMT_MMDDYYYY ? "%m/%d/%Y" : \
+ (datefmt == DATEFMT_DDMMYYYY ? "%d/%m/%Y" : \
+ (datefmt == DATEFMT_YYYYMMDD ? "%Y/%m/%d" : "%Y-%m-%d")))
-#define DATEFMT_DESC(datefmt) (datefmt == DATEFMT_MMDDYYYY ? \
- _("mm/dd/yyyy") : \
- (datefmt == DATEFMT_DDMMYYYY ? \
- _("dd/mm/yyyy") : \
- (datefmt == DATEFMT_YYYYMMDD ? \
- _("yyyy/mm/dd") : _("yyyy-mm-dd"))))
+#define DATEFMT_DESC(datefmt) (datefmt == DATEFMT_MMDDYYYY ? \
+ _("mm/dd/yyyy") : (datefmt == DATEFMT_DDMMYYYY ? _("dd/mm/yyyy") : \
+ (datefmt == DATEFMT_YYYYMMDD ? _("yyyy/mm/dd") : _("yyyy-mm-dd"))))
struct date {
unsigned dd;
@@ -260,8 +266,8 @@ struct apoint
long dur; /* duration of the appointment in seconds */
#define APOINT_NULL 0x0
-#define APOINT_NOTIFY 0x1 /* Item needs to be notified */
-#define APOINT_NOTIFIED 0x2 /* Item was already notified */
+#define APOINT_NOTIFY 0x1 /* Item needs to be notified */
+#define APOINT_NOTIFIED 0x2 /* Item was already notified */
int state;
char *mesg;
@@ -392,6 +398,7 @@ enum key {
KEY_DEL_ITEM,
KEY_EDIT_ITEM,
KEY_VIEW_ITEM,
+ KEY_PIPE_ITEM,
KEY_FLAG_ITEM,
KEY_REPEAT_ITEM,
KEY_EDIT_NOTE,
@@ -452,13 +459,14 @@ struct pad {
/* Notification bar definition. */
struct nbar {
- int show; /* display or hide the notify-bar */
- int cntdwn; /* warn when time left before next app
- becomes lesser than cntdwn */
+ unsigned show; /* display or hide the notify-bar */
+ int cntdwn; /* warn when time left before next app
+ becomes lesser than cntdwn */
char datefmt[BUFSIZ]; /* format for displaying date */
char timefmt[BUFSIZ]; /* format for displaying time */
- char cmd[BUFSIZ]; /* notification command */
- char *shell; /* user shell to launch notif. cmd */
+ char cmd[BUFSIZ]; /* notification command */
+ char *shell; /* user shell to launch notif. cmd */
+ unsigned notify_all; /* notify all appointments */
pthread_mutex_t mutex;
};
@@ -601,7 +609,7 @@ char *calendar_get_pom (time_t);
void custom_init_attr (void);
void custom_apply_attr (WINDOW *, int);
void custom_remove_attr (WINDOW *, int);
-void custom_load_conf (struct conf *, int);
+void custom_load_conf (struct conf *);
void custom_config_bar (void);
void custom_layout_config (void);
void custom_sidebar_config (void);
@@ -628,6 +636,7 @@ struct day_item *day_get_item (int);
int day_item_nb (long, int, int);
void day_edit_note (char *);
void day_view_note (char *);
+void day_pipe_item (struct conf *);
/* dmon.c */
void dmon_start (int);
@@ -650,6 +659,10 @@ void event_paste_item (void);
void help_wins_init (struct scrollwin *, int, int, int, int);
void help_screen (void);
+/* getstring.c */
+enum getstr getstring (WINDOW *, char *, int, int, int);
+int updatestring (WINDOW *, char **, int, int);
+
/* io.c */
unsigned io_fprintln (const char *, const char *, ...);
void io_init (char *, char *);
@@ -735,6 +748,11 @@ void mem_stats (void);
#endif /* CALCURSE_MEMORY_DEBUG */
+/* note.c */
+void edit_note (char **, char *);
+void view_note (char *, char *);
+void erase_note (char **, enum eraseflg);
+
/* notify.c */
int notify_time_left (void);
unsigned notify_needs_reminder (void);
@@ -778,6 +796,8 @@ struct recur_apoint *recur_apoint_scan (FILE *, struct tm, struct tm,
struct recur_event *recur_event_scan (FILE *, struct tm, int, char,
int, struct tm, char *,
llist_t *);
+void recur_apoint_write (struct recur_apoint *, FILE *);
+void recur_event_write (struct recur_event *, FILE *);
void recur_save_data (FILE *);
unsigned recur_item_inday (long, llist_t *, int, int, long, long);
unsigned recur_apoint_inday(struct recur_apoint *, long);
@@ -814,6 +834,7 @@ int todo_hilt_pos (void);
char *todo_saved_mesg (void);
void todo_new_item (void);
struct todo *todo_add (char *, int, char *);
+void todo_write (struct todo *, FILE *);
void todo_flag (void);
void todo_delete (struct conf *);
void todo_chg_priority (int);
@@ -821,9 +842,14 @@ void todo_edit_item (void);
void todo_update_panel (int);
void todo_edit_note (char *);
void todo_view_note (char *);
+void todo_pipe_item (void);
void todo_init_list (void);
void todo_free_list (void);
+/* utf8.c */
+int utf8_width (char *);
+int utf8_strwidth (char *);
+
/* utils.c */
void exit_calcurse (int) __attribute__((__noreturn__));
void free_user_data (void);
@@ -833,8 +859,6 @@ void status_mesg (char *, char *);
void erase_window_part (WINDOW *, int, int, int, int);
WINDOW *popup (int, int, int, int, char *, char *, int);
void print_in_middle (WINDOW *, int, int, int, char *);
-enum getstr getstring (WINDOW *, char *, int, int, int);
-int updatestring (WINDOW *, char **, int, int);
int is_all_digit (char *);
long get_item_time (long);
int get_item_hour (long);
@@ -856,17 +880,21 @@ long mystrtol (const char *);
void print_bool_option_incolor (WINDOW *, unsigned, int, int);
const char *get_tempdir (void);
char *new_tempfile (const char *, int);
-void erase_note (char **, enum eraseflg);
int parse_date (char *, enum datefmt, int *, int *, int *,
struct date *);
void str_toupper (char *);
void file_close (FILE *, const char *);
void psleep (unsigned);
+int fork_exec (int *, int *, const char *, char *const *);
+int shell_exec (int *, int *, char *);
+int child_wait (int *, int *, int);
+void press_any_key (void);
/* vars.c */
extern int col, row;
extern int resize;
extern unsigned colorize;
+extern int foreground, background;
extern enum ui_mode ui_mode;
extern int days[12];
extern char *monthnames[12];
@@ -915,7 +943,9 @@ void wins_update_border (void);
void wins_update_panels (void);
void wins_update (void);
void wins_reset (void);
-void wins_launch_external (const char *, const char *);
+void wins_prepare_external (void);
+void wins_unprepare_external (void);
+void wins_launch_external (char *, char *);
void wins_status_bar (void);
void wins_erase_status_bar (void);
void wins_other_status_page (int);
diff --git a/src/custom.c b/src/custom.c
index 1f75978..b2679d2 100644
--- a/src/custom.c
+++ b/src/custom.c
@@ -37,12 +37,12 @@
#include <string.h>
#include <stdlib.h>
#include <math.h>
+#include <ctype.h>
#include "calcurse.h"
/* Available configuration variables. */
enum conf_var {
- CUSTOM_CONF_NOVARIABLE,
CUSTOM_CONF_AUTOSAVE,
CUSTOM_CONF_PERIODICSAVE,
CUSTOM_CONF_CONFIRMQUIT,
@@ -59,11 +59,42 @@ enum conf_var {
CUSTOM_CONF_NOTIFYBARCLOCK,
CUSTOM_CONF_NOTIFYBARWARNING,
CUSTOM_CONF_NOTIFYBARCOMMAND,
+ CUSTOM_CONF_NOTIFYALL,
CUSTOM_CONF_OUTPUTDATEFMT,
CUSTOM_CONF_INPUTDATEFMT,
CUSTOM_CONF_DMON_ENABLE,
CUSTOM_CONF_DMON_LOG,
- CUSTOM_CONF_VARIABLES
+ CUSTOM_CONF_INVALID
+};
+
+struct conf_varname {
+ enum conf_var var;
+ const char *name;
+};
+
+static struct conf_varname conf_varmap[] =
+{
+ { CUSTOM_CONF_AUTOSAVE, "auto_save" },
+ { CUSTOM_CONF_PERIODICSAVE, "periodic_save" },
+ { CUSTOM_CONF_CONFIRMQUIT, "confirm_quit" },
+ { CUSTOM_CONF_CONFIRMDELETE, "confirm_delete" },
+ { CUSTOM_CONF_SKIPSYSTEMDIALOGS, "skip_system_dialogs" },
+ { CUSTOM_CONF_SKIPPROGRESSBAR, "skip_progress_bar" },
+ { CUSTOM_CONF_CALENDAR_DEFAULTVIEW, "calendar_default_view" },
+ { CUSTOM_CONF_WEEKBEGINSONMONDAY, "week_begins_on_monday" },
+ { CUSTOM_CONF_COLORTHEME, "color-theme" },
+ { CUSTOM_CONF_LAYOUT, "layout" },
+ { CUSTOM_CONF_SBAR_WIDTH, "side-bar_width" },
+ { CUSTOM_CONF_NOTIFYBARSHOW, "notify-bar_show" },
+ { CUSTOM_CONF_NOTIFYBARDATE, "notify-bar_date" },
+ { CUSTOM_CONF_NOTIFYBARCLOCK, "notify-bar_clock" },
+ { CUSTOM_CONF_NOTIFYBARWARNING, "notify-bar_warning" },
+ { CUSTOM_CONF_NOTIFYBARCOMMAND, "notify-bar_command" },
+ { CUSTOM_CONF_NOTIFYALL, "notify-all" },
+ { CUSTOM_CONF_OUTPUTDATEFMT, "output_datefmt" },
+ { CUSTOM_CONF_INPUTDATEFMT, "input_datefmt" },
+ { CUSTOM_CONF_DMON_ENABLE, "notify-daemon_enable" },
+ { CUSTOM_CONF_DMON_LOG, "notify-daemon_log" }
};
struct attribute {
@@ -73,18 +104,39 @@ struct attribute {
static struct attribute attr;
-static unsigned
-fill_config_var (char *string)
+static int
+conf_parse_bool (unsigned *dest, char *val)
{
- if (strncmp (string, "yes", 3) == 0)
- return 1;
- else if (strncmp (string, "no", 2) == 0)
+ if (strncmp (val, "yes", 4) == 0)
+ *dest = 1;
+ else if (strncmp (val, "no", 3) == 0)
+ *dest = 0;
+ else
return 0;
+
+ return 1;
+}
+
+static int
+conf_parse_unsigned (unsigned *dest, char *val)
+{
+ if (is_all_digit (val))
+ *dest = atoi (val);
else
- {
- EXIT (_("wrong configuration variable format."));
- return 0;
- }
+ return 0;
+
+ return 1;
+}
+
+static int
+conf_parse_int (int *dest, char *val)
+{
+ if ((*val == '+' || *val == '-' || isdigit (*val)) && is_all_digit (val + 1))
+ *dest = atoi (val);
+ else
+ return 0;
+
+ return 1;
}
/*
@@ -92,8 +144,8 @@ fill_config_var (char *string)
* Need to handle calcurse versions prior to 1.8, where colors where handled
* differently (number between 1 and 8).
*/
-static void
-custom_load_color (char *color, int background)
+static int
+conf_parse_color (char *val)
{
#define AWAITED_COLORS 2
@@ -101,15 +153,12 @@ custom_load_color (char *color, int background)
char c[AWAITED_COLORS][BUFSIZ];
int colr[AWAITED_COLORS];
- len = strlen (color);
+ len = strlen (val);
if (len > 1)
{
/* New version configuration */
- if (sscanf (color, "%s on %s", c[0], c[1]) != AWAITED_COLORS)
- {
- EXIT (_("missing colors in config file"));
- /* NOTREACHED */
- }
+ if (sscanf (val, "%s on %s", c[0], c[1]) != AWAITED_COLORS)
+ return 0;
for (i = 0; i < AWAITED_COLORS; i++)
{
@@ -132,17 +181,17 @@ custom_load_color (char *color, int background)
else if (!strncmp (c[i], "default", 7))
colr[i] = background;
else
- {
- EXIT (_("wrong color name"));
- /* NOTREACHED */
- }
+ return 0;
}
init_pair (COLR_CUSTOM, colr[0], colr[1]);
}
- else if (len > 0 && len < 2)
+ else if (len == 1)
{
/* Old version configuration */
- color_num = atoi (color);
+ if (isdigit (*val))
+ color_num = atoi (val);
+ else
+ return 0;
switch (color_num)
{
@@ -174,15 +223,13 @@ custom_load_color (char *color, int background)
init_pair (COLR_CUSTOM, COLOR_RED, COLR_BLUE);
break;
default:
- EXIT (_("wrong color number"));
- /* NOTREACHED */
+ return 0;
}
}
else
- {
- EXIT (_("wrong configuration variable format"));
- /* NOTREACHED */
- }
+ return 0;
+
+ return 1;
}
/*
@@ -233,15 +280,104 @@ custom_remove_attr (WINDOW *win, int attr_num)
wattroff (win, attr.nocolor[attr_num]);
}
+/* Set a configuration variable. */
+static int
+custom_set_conf (struct conf *conf, enum conf_var var, char *val)
+{
+ unsigned tmp;
+
+ switch (var)
+ {
+ case CUSTOM_CONF_AUTOSAVE:
+ return conf_parse_bool (&conf->auto_save, val);
+ break;
+ case CUSTOM_CONF_PERIODICSAVE:
+ return conf_parse_unsigned (&conf->periodic_save, val);
+ break;
+ case CUSTOM_CONF_CONFIRMQUIT:
+ return conf_parse_bool (&conf->confirm_quit, val);
+ break;
+ case CUSTOM_CONF_CONFIRMDELETE:
+ return conf_parse_bool (&conf->confirm_delete, val);
+ break;
+ case CUSTOM_CONF_SKIPSYSTEMDIALOGS:
+ return conf_parse_bool (&conf->skip_system_dialogs, val);
+ break;
+ case CUSTOM_CONF_SKIPPROGRESSBAR:
+ return conf_parse_bool (&conf->skip_progress_bar, val);
+ break;
+ case CUSTOM_CONF_CALENDAR_DEFAULTVIEW:
+ calendar_set_view (atoi (val));
+ break;
+ case CUSTOM_CONF_WEEKBEGINSONMONDAY:
+ return conf_parse_bool (&tmp, val);
+ if (tmp)
+ calendar_set_first_day_of_week (MONDAY);
+ else
+ calendar_set_first_day_of_week (SUNDAY);
+ break;
+ case CUSTOM_CONF_COLORTHEME:
+ return conf_parse_color (val);
+ break;
+ case CUSTOM_CONF_LAYOUT:
+ wins_set_layout (atoi (val));
+ break;
+ case CUSTOM_CONF_SBAR_WIDTH:
+ wins_set_sbar_width (atoi (val));
+ break;
+ case CUSTOM_CONF_NOTIFYBARSHOW:
+ return conf_parse_bool (&nbar.show, val);
+ break;
+ case CUSTOM_CONF_NOTIFYBARDATE:
+ (void)strncpy (nbar.datefmt, val, strlen (val) + 1);
+ break;
+ case CUSTOM_CONF_NOTIFYBARCLOCK:
+ (void)strncpy (nbar.timefmt, val, strlen (val) + 1);
+ break;
+ case CUSTOM_CONF_NOTIFYBARWARNING:
+ return conf_parse_int (&nbar.cntdwn, val);
+ break;
+ case CUSTOM_CONF_NOTIFYBARCOMMAND:
+ (void)strncpy (nbar.cmd, val, strlen (val) + 1);
+ break;
+ case CUSTOM_CONF_NOTIFYALL:
+ return conf_parse_bool (&nbar.notify_all, val);
+ break;
+ case CUSTOM_CONF_OUTPUTDATEFMT:
+ if (val[0] != '\0')
+ (void)strncpy (conf->output_datefmt, val, strlen (val) + 1);
+ break;
+ case CUSTOM_CONF_INPUTDATEFMT:
+ return conf_parse_int (&conf->input_datefmt, val);
+ if (conf->input_datefmt <= 0 || conf->input_datefmt >= DATE_FORMATS)
+ conf->input_datefmt = 1;
+ break;
+ case CUSTOM_CONF_DMON_ENABLE:
+ return conf_parse_bool (&dmon.enable, val);
+ break;
+ case CUSTOM_CONF_DMON_LOG:
+ return conf_parse_bool (&dmon.log, val);
+ break;
+ default:
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+
/* Load the user configuration. */
void
-custom_load_conf (struct conf *conf, int background)
+custom_load_conf (struct conf *conf)
{
FILE *data_file;
char *mesg_line1 = _("Failed to open config file");
char *mesg_line2 = _("Press [ENTER] to continue");
char buf[BUFSIZ], e_conf[BUFSIZ];
+ int i;
+ char *name;
enum conf_var var;
+ char *val;
data_file = fopen (path_conf, "r");
if (data_file == NULL)
@@ -251,154 +387,55 @@ custom_load_conf (struct conf *conf, int background)
wins_doupdate ();
(void)keys_getch (win[STA].p);
}
- var = CUSTOM_CONF_NOVARIABLE;
+
pthread_mutex_lock (&nbar.mutex);
for (;;)
{
if (fgets (buf, sizeof buf, data_file) == NULL)
+ break;
+ io_extract_data (e_conf, buf, sizeof buf);
+
+ if (*e_conf == '\0')
+ continue;
+
+ name = e_conf;
+ val = strchr (e_conf, '=');
+ if (val)
{
- break;
+ *val = '\0';
+ val++;
}
- io_extract_data (e_conf, buf, sizeof buf);
- switch (var)
+ var = CUSTOM_CONF_INVALID;
+ for (i = 0; i < sizeof (conf_varmap) / sizeof (struct conf_varname); i++)
{
- case CUSTOM_CONF_NOVARIABLE:
- break;
- case CUSTOM_CONF_AUTOSAVE:
- conf->auto_save = fill_config_var (e_conf);
- var = 0;
- break;
- case CUSTOM_CONF_PERIODICSAVE:
- if (atoi (e_conf) < 0)
- conf->periodic_save = 0;
- else
- conf->periodic_save = atoi (e_conf);
- var = 0;
- break;
- case CUSTOM_CONF_CONFIRMQUIT:
- conf->confirm_quit = fill_config_var (e_conf);
- var = 0;
- break;
- case CUSTOM_CONF_CONFIRMDELETE:
- conf->confirm_delete = fill_config_var (e_conf);
- var = 0;
- break;
- case CUSTOM_CONF_SKIPSYSTEMDIALOGS:
- conf->skip_system_dialogs = fill_config_var (e_conf);
- var = 0;
- break;
- case CUSTOM_CONF_SKIPPROGRESSBAR:
- conf->skip_progress_bar = fill_config_var (e_conf);
- var = 0;
- break;
- case CUSTOM_CONF_CALENDAR_DEFAULTVIEW:
- calendar_set_view (atoi (e_conf));
- var = 0;
- break;
- case CUSTOM_CONF_WEEKBEGINSONMONDAY:
- if (fill_config_var (e_conf))
- calendar_set_first_day_of_week (MONDAY);
- else
- calendar_set_first_day_of_week (SUNDAY);
- var = 0;
- break;
- case CUSTOM_CONF_COLORTHEME:
- custom_load_color (e_conf, background);
- var = 0;
- break;
- case CUSTOM_CONF_LAYOUT:
- wins_set_layout (atoi (e_conf));
- var = 0;
- break;
- case CUSTOM_CONF_SBAR_WIDTH:
- wins_set_sbar_width (atoi (e_conf));
- var = 0;
- break;
- case CUSTOM_CONF_NOTIFYBARSHOW:
- nbar.show = fill_config_var (e_conf);
- var = 0;
- break;
- case CUSTOM_CONF_NOTIFYBARDATE:
- (void)strncpy (nbar.datefmt, e_conf, strlen (e_conf) + 1);
- var = 0;
- break;
- case CUSTOM_CONF_NOTIFYBARCLOCK:
- (void)strncpy (nbar.timefmt, e_conf, strlen (e_conf) + 1);
- var = 0;
- break;
- case CUSTOM_CONF_NOTIFYBARWARNING:
- nbar.cntdwn = atoi (e_conf);
- var = 0;
- break;
- case CUSTOM_CONF_NOTIFYBARCOMMAND:
- (void)strncpy (nbar.cmd, e_conf, strlen (e_conf) + 1);
- var = 0;
- break;
- case CUSTOM_CONF_OUTPUTDATEFMT:
- if (e_conf[0] != '\0')
- (void)strncpy (conf->output_datefmt, e_conf, strlen (e_conf) + 1);
- var = 0;
- break;
- case CUSTOM_CONF_INPUTDATEFMT:
- conf->input_datefmt = atoi (e_conf);
- if (conf->input_datefmt <= 0 || conf->input_datefmt >= DATE_FORMATS)
- conf->input_datefmt = 1;
- var = 0;
- break;
- case CUSTOM_CONF_DMON_ENABLE:
- dmon.enable = fill_config_var (e_conf);
- var = 0;
- break;
- case CUSTOM_CONF_DMON_LOG:
- dmon.log = fill_config_var (e_conf);
- var = 0;
- break;
- default:
- EXIT (_("configuration variable unknown"));
+ if (strncmp (name, conf_varmap[i].name, BUFSIZ) == 0)
+ {
+ var = conf_varmap[i].var;
+ break;
+ }
+ }
+
+ if (var == CUSTOM_CONF_INVALID)
+ {
+ EXIT (_("configuration variable unknown: \"%s\""), name);
/* NOTREACHED */
}
- if (strncmp (e_conf, "auto_save=", 10) == 0)
- var = CUSTOM_CONF_AUTOSAVE;
- else if (strncmp (e_conf, "periodic_save=", 14) == 0)
- var = CUSTOM_CONF_PERIODICSAVE;
- else if (strncmp (e_conf, "confirm_quit=", 13) == 0)
- var = CUSTOM_CONF_CONFIRMQUIT;
- else if (strncmp (e_conf, "confirm_delete=", 15) == 0)
- var = CUSTOM_CONF_CONFIRMDELETE;
- else if (strncmp (e_conf, "skip_system_dialogs=", 20) == 0)
- var = CUSTOM_CONF_SKIPSYSTEMDIALOGS;
- else if (strncmp (e_conf, "skip_progress_bar=", 18) == 0)
- var = CUSTOM_CONF_SKIPPROGRESSBAR;
- else if (strncmp (e_conf, "calendar_default_view=", 22) == 0)
- var = CUSTOM_CONF_CALENDAR_DEFAULTVIEW;
- else if (strncmp (e_conf, "week_begins_on_monday=", 22) == 0)
- var = CUSTOM_CONF_WEEKBEGINSONMONDAY;
- else if (strncmp (e_conf, "color-theme=", 12) == 0)
- var = CUSTOM_CONF_COLORTHEME;
- else if (strncmp (e_conf, "layout=", 7) == 0)
- var = CUSTOM_CONF_LAYOUT;
- else if (strncmp (e_conf, "side-bar_width=", 15) == 0)
- var = CUSTOM_CONF_SBAR_WIDTH;
- else if (strncmp (e_conf, "notify-bar_show=", 16) == 0)
- var = CUSTOM_CONF_NOTIFYBARSHOW;
- else if (strncmp (e_conf, "notify-bar_date=", 16) == 0)
- var = CUSTOM_CONF_NOTIFYBARDATE;
- else if (strncmp (e_conf, "notify-bar_clock=", 17) == 0)
- var = CUSTOM_CONF_NOTIFYBARCLOCK;
- else if (strncmp (e_conf, "notify-bar_warning=", 19) == 0)
- var = CUSTOM_CONF_NOTIFYBARWARNING;
- else if (strncmp (e_conf, "notify-bar_command=", 19) == 0)
- var = CUSTOM_CONF_NOTIFYBARCOMMAND;
- else if (strncmp (e_conf, "output_datefmt=", 15) == 0)
- var = CUSTOM_CONF_OUTPUTDATEFMT;
- else if (strncmp (e_conf, "input_datefmt=", 14) == 0)
- var = CUSTOM_CONF_INPUTDATEFMT;
- else if (strncmp (e_conf, "notify-daemon_enable=", 21) == 0)
- var = CUSTOM_CONF_DMON_ENABLE;
- else if (strncmp (e_conf, "notify-daemon_log=", 18) == 0)
- var = CUSTOM_CONF_DMON_LOG;
+ if (val && (*val == '\0' || *val == '\n'))
+ {
+ /* Backward compatibility mode. */
+ if (fgets (buf, sizeof buf, data_file) == NULL)
+ break;
+ io_extract_data (e_conf, buf, sizeof buf);
+ val = e_conf;
+ }
+
+ if (!val || !custom_set_conf (conf, var, val))
+ {
+ EXIT (_("wrong configuration variable format for \"%s\""), name);
+ /* NOTREACHED */
+ }
}
file_close (data_file, __FILE_POS__);
pthread_mutex_unlock (&nbar.mutex);
diff --git a/src/day.c b/src/day.c
index 7e2f6fb..458f06a 100644
--- a/src/day.c
+++ b/src/day.c
@@ -347,14 +347,15 @@ display_item_date (int incolor, struct apoint *i, int type, long date,
* Print an item description in the corresponding panel window.
*/
static void
-display_item (int incolor, char *msg, int recur, int note, int len, int y,
+display_item (int incolor, char *msg, int recur, int note, int width, int y,
int x)
{
WINDOW *win;
int ch_recur, ch_note;
- char buf[len];
+ char buf[width * UTF8_MAXLEN];
+ int i;
- if (len <= 0)
+ if (width <= 0)
return;
win = apad.ptrwin;
@@ -362,12 +363,20 @@ display_item (int incolor, char *msg, int recur, int note, int len, int y,
ch_note = (note) ? '>' : ' ';
if (incolor == 0)
custom_apply_attr (win, ATTR_HIGHEST);
- if (strlen (msg) < len)
+ if (utf8_strwidth (msg) < width)
mvwprintw (win, y, x, " %c%c%s", ch_recur, ch_note, msg);
else
{
- (void)strncpy (buf, msg, len - 1);
- buf[len - 1] = '\0';
+ for (i = 0; msg[i] && width > 0; i++)
+ {
+ if (!UTF8_ISCONT (msg[i]))
+ width -= utf8_width (&msg[i]);
+ buf[i] = msg[i];
+ }
+ if (i)
+ buf[i - 1] = 0;
+ else
+ buf[0] = 0;
mvwprintw (win, y, x, " %c%c%s...", ch_recur, ch_note, buf);
}
if (incolor == 0)
@@ -1045,25 +1054,12 @@ day_edit_note (char *editor)
struct apoint *a;
struct recur_event *re;
struct event *e;
- char fullname[BUFSIZ];
- char *filename;
long date;
int item_num;
item_num = apoint_hilt ();
p = day_get_item (item_num);
- if (p->note == NULL)
- {
- if ((filename = new_tempfile (path_notes, NOTESIZ)) == NULL)
- return;
- else
- p->note = filename;
- }
- (void)snprintf (fullname, BUFSIZ, "%s%s", path_notes, p->note);
- wins_launch_external (fullname, editor);
-
- if (io_file_is_empty (fullname) > 0)
- erase_note (&p->note, ERASE_FORCE);
+ edit_note (&p->note, editor);
date = calendar_get_slctd_day_sec ();
switch (p->type)
@@ -1091,12 +1087,61 @@ day_edit_note (char *editor)
void
day_view_note (char *pager)
{
+ struct day_item *p = day_get_item (apoint_hilt ());
+ view_note (p->note, pager);
+}
+
+/* Pipe an appointment or event to an external program. */
+void
+day_pipe_item (struct conf *conf)
+{
+ char cmd[BUFSIZ] = "";
+ int pout;
+ int pid;
+ FILE *fpout;
+ int item_num;
+ long date;
struct day_item *p;
- char fullname[BUFSIZ];
+ struct recur_apoint *ra;
+ struct apoint *a;
+ struct recur_event *re;
+ struct event *e;
- p = day_get_item (apoint_hilt ());
- if (p->note == NULL)
+ status_mesg (_("Pipe item to external command:"), "");
+ if (getstring (win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID)
return;
- (void)snprintf (fullname, BUFSIZ, "%s%s", path_notes, p->note);
- wins_launch_external (fullname, pager);
+
+ wins_prepare_external ();
+ if ((pid = shell_exec (NULL, &pout, cmd)))
+ {
+ fpout = fdopen (pout, "w");
+
+ item_num = apoint_hilt ();
+ p = day_get_item (item_num);
+ date = calendar_get_slctd_day_sec ();
+ switch (p->type)
+ {
+ case RECUR_EVNT:
+ re = recur_get_event (date, day_item_nb (date, item_num, RECUR_EVNT));
+ recur_event_write (re, fpout);
+ break;
+ case EVNT:
+ e = event_get (date, day_item_nb (date, item_num, EVNT));
+ event_write (e, fpout);
+ break;
+ case RECUR_APPT:
+ ra = recur_get_apoint (date, day_item_nb (date, item_num, RECUR_APPT));
+ recur_apoint_write (ra, fpout);
+ break;
+ case APPT:
+ a = apoint_get (date, day_item_nb (date, item_num, APPT));
+ apoint_write (a, fpout);
+ break;
+ }
+
+ fclose (fpout);
+ child_wait (NULL, &pout, pid);
+ press_any_key ();
+ }
+ wins_unprepare_external ();
}
diff --git a/src/dmon.c b/src/dmon.c
index 6a24739..47aec12 100644
--- a/src/dmon.c
+++ b/src/dmon.c
@@ -165,7 +165,7 @@ dmon_start (int parent_exit_status)
if (!io_file_exist (path_conf))
DMON_ABRT (_("Could not access \"%s\": %s\n"),
path_conf, strerror (errno));
- custom_load_conf (&conf, 0);
+ custom_load_conf (&conf);
if (!io_file_exist (path_apts))
DMON_ABRT (_("Could not access \"%s\": %s\n"),
@@ -194,8 +194,9 @@ dmon_start (int parent_exit_status)
DMON_LOG (_("error while sending notification\n"));
}
- DMON_LOG (_("sleeping at %s for %d seconds\n"), nowstr (),
- DMON_SLEEP_TIME);
+ DMON_LOG (ngettext ("sleeping at %s for %d second\n",
+ "sleeping at %s for %d seconds\n",
+ DMON_SLEEP_TIME), nowstr (), DMON_SLEEP_TIME);
psleep (DMON_SLEEP_TIME);
DMON_LOG (_("awakened at %s\n"), nowstr ());
}
diff --git a/src/getstring.c b/src/getstring.c
new file mode 100644
index 0000000..8633ef3
--- /dev/null
+++ b/src/getstring.c
@@ -0,0 +1,299 @@
+/*
+ * Calcurse - text-based organizer
+ *
+ * Copyright (c) 2004-2011 calcurse Development Team <misc@calcurse.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Send your feedback or comments to : misc@calcurse.org
+ * Calcurse home page : http://calcurse.org
+ *
+ */
+
+#include "calcurse.h"
+
+struct getstr_charinfo {
+ unsigned int offset, dpyoff;
+};
+
+struct getstr_status {
+ char *s;
+ struct getstr_charinfo *ci;
+ int pos, len;
+ int scrpos;
+};
+
+/* Print the string at the desired position. */
+static void
+getstr_print (WINDOW *win, int x, int y, struct getstr_status *st)
+{
+ char c = 0;
+
+ /* print string */
+ mvwaddnstr (win, y, x, &st->s[st->ci[st->scrpos].offset], -1);
+ wclrtoeol (win);
+
+ /* print scrolling indicator */
+ if (st->scrpos > 0 && st->ci[st->len].dpyoff -
+ st->ci[st->scrpos].dpyoff > col - 2)
+ c = '*';
+ else if (st->scrpos > 0)
+ c = '<';
+ else if (st->ci[st->len].dpyoff - st->ci[st->scrpos].dpyoff > col - 2)
+ c = '>';
+ mvwprintw (win, y, col - 2, " %c", c);
+
+ /* print cursor */
+ wmove (win, y, st->ci[st->pos].dpyoff - st->ci[st->scrpos].dpyoff);
+ wchgat (win, 1, A_REVERSE, COLR_CUSTOM, NULL);
+}
+
+/* Delete a character at the given position in string. */
+static void
+getstr_del_char (struct getstr_status *st)
+{
+ char *str = st->s + st->ci[st->pos].offset;
+ int cl = st->ci[st->pos + 1].offset - st->ci[st->pos].offset;
+ int cw = st->ci[st->pos + 1].dpyoff - st->ci[st->pos].dpyoff;
+ int i;
+
+ memmove (str, str + cl, strlen (str) + 1);
+
+ st->len--;
+ for (i = st->pos; i <= st->len; i++)
+ {
+ st->ci[i].offset = st->ci[i + 1].offset - cl;
+ st->ci[i].dpyoff = st->ci[i + 1].dpyoff - cw;
+ }
+}
+
+/* Add a character at the given position in string. */
+static void
+getstr_ins_char (struct getstr_status *st, char *c)
+{
+ char *str = st->s + st->ci[st->pos].offset;
+ int cl = UTF8_LENGTH (c[0]);
+ int cw = utf8_width (c);
+ int i;
+
+ memmove (str + cl, str, strlen (str) + 1);
+ for (i = 0; i < cl; i++, str++)
+ *str = c[i];
+
+ for (i = st->len; i >= st->pos; i--)
+ {
+ st->ci[i + 1].offset = st->ci[i].offset + cl;
+ st->ci[i + 1].dpyoff = st->ci[i].dpyoff + cw;
+ }
+ st->len++;
+}
+
+static void
+bell (void)
+{
+ printf ("\a");
+}
+
+/* Initialize getstring data structure. */
+static void
+getstr_init (struct getstr_status *st, char *str, struct getstr_charinfo *ci)
+{
+ int width;
+
+ st->s = str;
+ st->ci = ci;
+
+ st->len = width = 0;
+ while (*str)
+ {
+ st->ci[st->len].offset = str - st->s;
+ st->ci[st->len].dpyoff = width;
+
+ st->len++;
+ width += utf8_width (str);
+ str += UTF8_LENGTH (*str);
+ }
+ st->ci[st->len].offset = str - st->s;
+ st->ci[st->len].dpyoff = width;
+
+ st->pos = st->len;
+ st->scrpos = 0;
+}
+
+/* Scroll left/right if the cursor moves outside the window range. */
+static void
+getstr_fixscr (struct getstr_status *st)
+{
+ const int pgsize = col / 3;
+ int pgskip;
+
+ while (st->pos < st->scrpos)
+ {
+ pgskip = 0;
+ while (pgskip < pgsize && st->scrpos > 0)
+ {
+ st->scrpos--;
+ pgskip += st->ci[st->scrpos + 1].dpyoff - st->ci[st->scrpos].dpyoff;
+ }
+ }
+ while (st->ci[st->pos].dpyoff - st->ci[st->scrpos].dpyoff > col - 2)
+ {
+ pgskip = 0;
+ while (pgskip < pgsize && st->scrpos < st->len)
+ {
+ pgskip += st->ci[st->scrpos + 1].dpyoff - st->ci[st->scrpos].dpyoff;
+ st->scrpos++;
+ }
+ }
+}
+
+/*
+ * 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.
+ */
+enum getstr
+getstring (WINDOW *win, char *str, int l, int x, int y)
+{
+ struct getstr_status st;
+ struct getstr_charinfo ci[l + 1];
+
+ int ch, k;
+ char c[UTF8_MAXLEN];
+
+ getstr_init (&st, str, ci);
+ custom_apply_attr (win, ATTR_HIGHEST);
+
+ for (;;) {
+ getstr_fixscr (&st);
+ getstr_print (win, x, y, &st);
+ wins_doupdate ();
+
+ if ((ch = wgetch (win)) == '\n') break;
+ switch (ch)
+ {
+ case KEY_BACKSPACE: /* delete one character */
+ case 330:
+ case 127:
+ case CTRL ('H'):
+ if (st.pos > 0)
+ {
+ st.pos--;
+ getstr_del_char (&st);
+ }
+ else
+ bell ();
+ break;
+ case CTRL ('D'): /* delete next character */
+ if (st.pos < st.len)
+ getstr_del_char (&st);
+ else
+ bell ();
+ break;
+ case CTRL ('W'): /* delete a word */
+ if (st.pos > 0) {
+ while (st.pos && st.s[st.ci[st.pos - 1].offset] == ' ')
+ {
+ st.pos--;
+ getstr_del_char (&st);
+ }
+ while (st.pos && st.s[st.ci[st.pos - 1].offset] != ' ')
+ {
+ st.pos--;
+ getstr_del_char (&st);
+ }
+ }
+ else
+ bell ();
+ break;
+ case CTRL ('K'): /* delete to end-of-line */
+ st.s[st.ci[st.pos].offset] = 0;
+ st.len = st.pos;
+ break;
+ case CTRL ('A'): /* go to begginning of string */
+ st.pos = 0;
+ break;
+ case CTRL ('E'): /* go to end of string */
+ st.pos = st.len;
+ break;
+ case KEY_LEFT: /* move one char backward */
+ case CTRL ('B'):
+ if (st.pos > 0) st.pos--;
+ break;
+ case KEY_RIGHT: /* move one char forward */
+ case CTRL ('F'):
+ if (st.pos < st.len) st.pos++;
+ break;
+ case ESCAPE: /* cancel editing */
+ return (GETSTRING_ESC);
+ break;
+ default: /* insert one character */
+ c[0] = ch;
+ for (k = 1; k < MIN (UTF8_LENGTH (c[0]), UTF8_MAXLEN); k++)
+ c[k] = (unsigned char)wgetch (win);
+ if (st.ci[st.len].offset + k < l)
+ {
+ getstr_ins_char (&st, c);
+ st.pos++;
+ }
+ }
+ }
+
+ custom_remove_attr (win, ATTR_HIGHEST);
+
+ return (st.len == 0 ? GETSTRING_RET : GETSTRING_VALID);
+}
+
+/* Update an already existing string. */
+int
+updatestring (WINDOW *win, char **str, int x, int y)
+{
+ int len = strlen (*str);
+ char *buf;
+ enum getstr ret;
+
+ EXIT_IF (len + 1 > BUFSIZ, _("Internal error: line too long"));
+
+ buf = mem_malloc (BUFSIZ);
+ (void)memcpy (buf, *str, len + 1);
+
+ ret = getstring (win, buf, BUFSIZ, x, y);
+
+ if (ret == GETSTRING_VALID)
+ {
+ len = strlen (buf);
+ *str = mem_realloc (*str, len + 1, 1);
+ EXIT_IF (*str == NULL, _("out of memory"));
+ (void)memcpy (*str, buf, len + 1);
+ }
+
+ mem_free (buf);
+ return ret;
+}
diff --git a/src/help.c b/src/help.c
index 3f4f7c7..7c43d44 100644
--- a/src/help.c
+++ b/src/help.c
@@ -56,6 +56,7 @@ typedef enum
HELP_EXPORT,
HELP_DISPLACEMENT,
HELP_VIEW,
+ HELP_PIPE,
HELP_TAB,
HELP_GOTO,
HELP_DELETE,
@@ -296,6 +297,10 @@ wanted_page (int ch)
page = HELP_VIEW;
break;
+ case KEY_PIPE_ITEM:
+ page = HELP_PIPE;
+ break;
+
case KEY_RAISE_PRIORITY:
case KEY_LOWER_PRIORITY:
page = HELP_PRIORITY;
@@ -451,6 +456,14 @@ help_screen (void)
"Calcurse screen."),
keys_action_firstkey (KEY_VIEW_ITEM));
+ hscr[HELP_PIPE].title = _("Pipe\n");
+ (void)snprintf (hscr[HELP_PIPE].text, HELPTEXTSIZ,
+ _("Pipe the selected item to an external program.\n"
+ "\nPress the '%s' key to pipe the currently selected appointment or\n"
+ "todo entry to an external program.\n"
+ "\nYou will be driven back to calcurse as soon as the program exits.\n"),
+ keys_action_firstkey (KEY_PIPE_ITEM));
+
hscr[HELP_TAB].title = _("Tab\n");
(void)snprintf (hscr[HELP_TAB].text, HELPTEXTSIZ,
_("Switch between panels.\n"
diff --git a/src/io.c b/src/io.c
index de46cce..d8af1fb 100644
--- a/src/io.c
+++ b/src/io.c
@@ -136,7 +136,6 @@ static char *ical_recur_type[RECUR_TYPES] =
static void
progress_bar (progress_bar_t type, int progress)
{
-#define SLEEPTIME 125000
#define NBFILES 4
#define NBEXPORTED 3
#define LABELENGTH 15
@@ -194,8 +193,6 @@ progress_bar (progress_bar_t type, int progress)
custom_remove_attr (win[STA].p, ATTR_HIGHEST);
wmove (win[STA].p, 0, 0);
wins_wrefresh (win[STA].p);
- (void)usleep (SLEEPTIME);
-#undef SLEEPTIME
#undef NBFILES
#undef NBEXPORTED
#undef LABELENGTH
@@ -809,9 +806,10 @@ io_extract_data (char *dst_data, const char *org, int len)
{
int i;
+ for (; *org == ' ' || *org == '\t'; org++);
for (i = 0; i < len - 1; i++)
{
- if (*org == '\n' || *org == '\0')
+ if (*org == '\n' || *org == '\0' || *org == '#')
break;
*dst_data++ = *org++;
}
@@ -849,8 +847,9 @@ io_save_conf (struct conf *conf)
"# options are usually set from within Calcurse. A line beginning with \n"
"# a space or tab is considered to be a continuation of the previous "
"line.\n"
- "# For a variable to be unset its value must be blank.\n"
- "# To set a variable to the empty string its value should be \"\".\n"
+ "# For a variable to be unset its value must be blank, followed by an\n"
+ "# empty line. To set a variable to the empty string its value should be "
+ "\"\".\n"
"# Lines beginning with \"#\" are comments, and ignored by Calcurse.\n";
char theme_name[BUFSIZ];
FILE *fp;
@@ -864,56 +863,56 @@ io_save_conf (struct conf *conf)
(void)fprintf (fp, "# If this option is set to yes, "
"automatic save is done when quitting\n");
- (void)fprintf (fp, "auto_save=\n");
+ (void)fprintf (fp, "auto_save=");
(void)fprintf (fp, "%s\n", (conf->auto_save) ? "yes" : "no");
(void)fprintf (fp, "\n# If not null, perform automatic saves every "
"'periodic_save' minutes\n");
- (void)fprintf (fp, "periodic_save=\n");
+ (void)fprintf (fp, "periodic_save=");
(void)fprintf (fp, "%d\n", conf->periodic_save);
(void)fprintf (fp, "\n# If this option is set to yes, "
"confirmation is required before quitting\n");
- (void)fprintf (fp, "confirm_quit=\n");
+ (void)fprintf (fp, "confirm_quit=");
(void)fprintf (fp, "%s\n", (conf->confirm_quit) ? "yes" : "no");
(void)fprintf (fp, "\n# If this option is set to yes, "
"confirmation is required before deleting an event\n");
- (void)fprintf (fp, "confirm_delete=\n");
+ (void)fprintf (fp, "confirm_delete=");
(void)fprintf (fp, "%s\n", (conf->confirm_delete) ? "yes" : "no");
(void)fprintf (fp, "\n# If this option is set to yes, "
"messages about loaded and saved data will not be displayed\n");
- (void)fprintf (fp, "skip_system_dialogs=\n");
+ (void)fprintf (fp, "skip_system_dialogs=");
(void)fprintf (fp, "%s\n", (conf->skip_system_dialogs) ? "yes" : "no");
(void)fprintf (fp,
"\n# If this option is set to yes, progress bar appearing "
"when saving data will not be displayed\n");
- (void)fprintf (fp, "skip_progress_bar=\n");
+ (void)fprintf (fp, "skip_progress_bar=");
(void)fprintf (fp, "%s\n", (conf->skip_progress_bar) ? "yes" : "no");
(void)fprintf (fp, "\n# Default calendar view (0)monthly (1)weekly:\n");
- (void)fprintf (fp, "calendar_default_view=\n");
+ (void)fprintf (fp, "calendar_default_view=");
(void)fprintf (fp, "%d\n", calendar_get_view ());
(void)fprintf (fp, "\n# If this option is set to yes, "
"monday is the first day of the week, else it is sunday\n");
- (void)fprintf (fp, "week_begins_on_monday=\n");
+ (void)fprintf (fp, "week_begins_on_monday=");
(void)fprintf (fp, "%s\n",
(calendar_week_begins_on_monday ())? "yes" : "no");
(void)fprintf (fp, "\n# This is the color theme used for menus :\n");
- (void)fprintf (fp, "color-theme=\n");
+ (void)fprintf (fp, "color-theme=");
(void)fprintf (fp, "%s\n", theme_name);
(void)fprintf (fp, "\n# This is the layout of the calendar :\n");
- (void)fprintf (fp, "layout=\n");
+ (void)fprintf (fp, "layout=");
(void)fprintf (fp, "%d\n", wins_layout ());
(void)fprintf (fp, "\n# Width (in percentage, 0 being minimun width) "
"of the side bar :\n");
- (void)fprintf (fp, "side-bar_width=\n");
+ (void)fprintf (fp, "side-bar_width=");
(void)fprintf (fp, "%d\n", wins_sbar_wperc ());
if (ui_mode == UI_CURSES)
@@ -921,39 +920,43 @@ io_save_conf (struct conf *conf)
(void)fprintf (fp,
"\n# If this option is set to yes, "
"notify-bar will be displayed :\n");
- (void)fprintf (fp, "notify-bar_show=\n");
+ (void)fprintf (fp, "notify-bar_show=");
(void)fprintf (fp, "%s\n", (nbar.show) ? "yes" : "no");
(void)fprintf (fp,
"\n# Format of the date to be displayed inside notify-bar :\n");
- (void)fprintf (fp, "notify-bar_date=\n");
+ (void)fprintf (fp, "notify-bar_date=");
(void)fprintf (fp, "%s\n", nbar.datefmt);
(void)fprintf (fp,
"\n# Format of the time to be displayed inside notify-bar :\n");
- (void)fprintf (fp, "notify-bar_clock=\n");
+ (void)fprintf (fp, "notify-bar_clock=");
(void)fprintf (fp, "%s\n", nbar.timefmt);
(void)fprintf (fp,
"\n# Warn user if he has an appointment within next "
"'notify-bar_warning' seconds :\n");
- (void)fprintf (fp, "notify-bar_warning=\n");
+ (void)fprintf (fp, "notify-bar_warning=");
(void)fprintf (fp, "%d\n", nbar.cntdwn);
(void)fprintf (fp, "\n# Command used to notify user of "
"an upcoming appointment :\n");
- (void)fprintf (fp, "notify-bar_command=\n");
+ (void)fprintf (fp, "notify-bar_command=");
(void)fprintf (fp, "%s\n", nbar.cmd);
+ (void)fprintf (fp, "\n# Notify all appointments instead of flagged ones only\n");
+ (void)fprintf (fp, "notify-all=");
+ (void)fprintf (fp, "%s\n", (nbar.notify_all) ? "yes" : "no");
+
(void)fprintf (fp, "\n# Format of the date to be displayed "
"in non-interactive mode :\n");
- (void)fprintf (fp, "output_datefmt=\n");
+ (void)fprintf (fp, "output_datefmt=");
(void)fprintf (fp, "%s\n", conf->output_datefmt);
(void)fprintf (fp, "\n# Format to be used when entering a date "
"(1)mm/dd/yyyy (2)dd/mm/yyyy (3)yyyy/mm/dd) "
"(4)yyyy-mm-dd:\n");
- (void)fprintf (fp, "input_datefmt=\n");
+ (void)fprintf (fp, "input_datefmt=");
(void)fprintf (fp, "%d\n", conf->input_datefmt);
if (ui_mode == UI_CURSES)
@@ -962,12 +965,12 @@ io_save_conf (struct conf *conf)
(void)fprintf (fp, "\n# If this option is set to yes, "
"calcurse will run in background to get notifications "
"after exiting\n");
- (void)fprintf (fp, "notify-daemon_enable=\n");
+ (void)fprintf (fp, "notify-daemon_enable=");
(void)fprintf (fp, "%s\n", dmon.enable ? "yes" : "no");
(void)fprintf (fp, "\n# If this option is set to yes, "
"activity will be logged when running in background\n");
- (void)fprintf (fp, "notify-daemon_log=\n");
+ (void)fprintf (fp, "notify-daemon_log=");
(void)fprintf (fp, "%s\n", dmon.log ? "yes" : "no");
file_close (fp, __FILE_POS__);
@@ -1024,10 +1027,7 @@ io_save_todo (void)
LLIST_FOREACH (&todolist, i)
{
struct todo *todo = LLIST_TS_GET_DATA (i);
- if (todo->note)
- (void)fprintf (fp, "[%d]>%s %s\n", todo->id, todo->note, todo->mesg);
- else
- (void)fprintf (fp, "[%d] %s\n", todo->id, todo->mesg);
+ todo_write (todo, fp);
}
file_close (fp, __FILE_POS__);
@@ -2758,10 +2758,7 @@ io_import_data (enum import_type type, struct conf *conf, char *stream_name)
const struct string vevent = STRING_BUILD ("BEGIN:VEVENT");
const struct string vtodo = STRING_BUILD ("BEGIN:VTODO");
char *proc_report = _("Import process report: %04d lines read ");
- char *lines_stats =
- _("%d apps / %d events / %d todos / %d skipped ");
- char *lines_stats_interactive =
- _("%d apps / %d events / %d todos / %d skipped ([ENTER] to continue)");
+ char stats_str[4][BUFSIZ];
char buf[BUFSIZ];
FILE *stream = NULL;
struct io_file *log;
@@ -2823,23 +2820,30 @@ io_import_data (enum import_type type, struct conf *conf, char *stream_name)
if (stream != stdin)
file_close (stream, __FILE_POS__);
+ snprintf (stats_str[0], BUFSIZ,
+ ngettext ("%d app", "%d apps", stats.apoints), stats.apoints);
+ snprintf (stats_str[1], BUFSIZ,
+ ngettext ("%d event", "%d events", stats.events), stats.events);
+ snprintf (stats_str[2], BUFSIZ,
+ ngettext ("%d todo", "%d todos", stats.todos), stats.todos);
+ snprintf (stats_str[3], BUFSIZ, _("%d skipped"), stats.skipped);
+
if (ui_mode == UI_CURSES && !conf->skip_system_dialogs)
{
char read[BUFSIZ], stat[BUFSIZ];
(void)snprintf (read, BUFSIZ, proc_report, stats.lines);
- (void)snprintf (stat, BUFSIZ, lines_stats_interactive, stats.apoints,
- stats.events, stats.todos, stats.skipped);
+ (void)snprintf (stat, BUFSIZ, "%s / %s / %s / %s (%s)", stats_str[0],
+ stats_str[1], stats_str[2], stats_str[3],
+ _("Press [ENTER] to continue"));
status_mesg (read, stat);
(void)wgetch (win[STA].p);
}
else if (ui_mode == UI_CMDLINE)
{
printf (proc_report, stats.lines);
- printf ("\n");
- printf (lines_stats, stats.apoints, stats.events, stats.todos,
- stats.skipped);
- printf ("\n");
+ printf ("\n%s / %s / %s / %s\n", stats_str[0], stats_str[1],
+ stats_str[2], stats_str[3]);
}
/* User has the choice to look at the log file if some items could not be
diff --git a/src/keys.c b/src/keys.c
index 12bdd5c..1a46530 100644
--- a/src/keys.c
+++ b/src/keys.c
@@ -81,16 +81,17 @@ static struct keydef_s keydef[NBKEYS] = {
{"generic-scroll-up", "C-p"},
{"generic-goto-today", "C-g"},
- {"move-right", "l L"},
- {"move-left", "h H"},
- {"move-down", "j J"},
- {"move-up", "k K"},
+ {"move-right", "l L RGT"},
+ {"move-left", "h H LFT"},
+ {"move-down", "j J DWN"},
+ {"move-up", "k K UP"},
{"start-of-week", "0"},
{"end-of-week", "$"},
{"add-item", "a A"},
{"del-item", "d D"},
{"edit-item", "e E"},
{"view-item", "v V"},
+ {"pipe-item", "|"},
{"flag-item", "!"},
{"repeat", "r R"},
{"edit-note", "n N"},
@@ -567,6 +568,8 @@ keys_popup_info (enum key key)
_("Flag the currently selected item as important.");
info[KEY_REPEAT_ITEM] =
_("Repeat an item");
+ info[KEY_PIPE_ITEM] =
+ _("Pipe the currently selected item to an external program.");
info[KEY_EDIT_NOTE] =
_("Attach (or edit if one exists) a note to the currently selected item");
info[KEY_VIEW_NOTE] =
diff --git a/src/note.c b/src/note.c
new file mode 100644
index 0000000..57bfc2d
--- /dev/null
+++ b/src/note.c
@@ -0,0 +1,90 @@
+/*
+ * Calcurse - text-based organizer
+ *
+ * Copyright (c) 2004-2011 calcurse Development Team <misc@calcurse.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Send your feedback or comments to : misc@calcurse.org
+ * Calcurse home page : http://calcurse.org
+ *
+ */
+
+#include <unistd.h>
+
+#include "calcurse.h"
+
+/* Edit a note with an external editor. */
+void
+edit_note (char **note, char *editor)
+{
+ char fullname[BUFSIZ];
+ char *filename;
+
+ if (*note == NULL)
+ {
+ if ((filename = new_tempfile (path_notes, NOTESIZ)) != NULL)
+ *note = filename;
+ else
+ return;
+ }
+ (void)snprintf (fullname, BUFSIZ, "%s%s", path_notes, *note);
+ wins_launch_external (fullname, editor);
+
+ if (io_file_is_empty (fullname) > 0)
+ erase_note (note, ERASE_FORCE);
+}
+
+/* View a note in an external pager. */
+void
+view_note (char *note, char *pager)
+{
+ char fullname[BUFSIZ];
+
+ if (note == NULL)
+ return;
+ (void)snprintf (fullname, BUFSIZ, "%s%s", path_notes, note);
+ wins_launch_external (fullname, pager);
+}
+
+/* Erase a note previously attached to an item. */
+void
+erase_note (char **note, enum eraseflg flag)
+{
+ char fullname[BUFSIZ];
+
+ if (*note == NULL)
+ return;
+ if (flag != ERASE_FORCE_KEEP_NOTE)
+ {
+ (void)snprintf (fullname, BUFSIZ, "%s%s", path_notes, *note);
+ if (unlink (fullname) != 0)
+ EXIT (_("could not remove note"));
+ }
+ mem_free (*note);
+ *note = NULL;
+}
diff --git a/src/notify.c b/src/notify.c
index 1b7e924..590a3e4 100644
--- a/src/notify.c
+++ b/src/notify.c
@@ -80,7 +80,8 @@ unsigned
notify_needs_reminder (void)
{
if (notify_app.got_app
- && (notify_app.state & APOINT_NOTIFY)
+ && (((notify_app.state & APOINT_NOTIFY) && !nbar.notify_all) ||
+ (!(notify_app.state & APOINT_NOTIFY) && nbar.notify_all))
&& !(notify_app.state & APOINT_NOTIFIED))
return 1;
return 0;
@@ -132,6 +133,8 @@ notify_init_vars (void)
if ((nbar.shell = getenv ("SHELL")) == NULL)
nbar.shell = "/bin/sh";
+ nbar.notify_all = 0;
+
(void)pthread_attr_init (&detached_thread_attr);
(void)pthread_attr_setdetachstate (&detached_thread_attr,
PTHREAD_CREATE_DETACHED);
@@ -281,7 +284,9 @@ notify_update_bar (void)
minutes_left = (time_left - hours_left * HOURINSEC) / MININSEC;
pthread_mutex_lock (&nbar.mutex);
- if (time_left < nbar.cntdwn && (notify_app.state & APOINT_NOTIFY))
+ if (time_left < nbar.cntdwn &&
+ (((notify_app.state & APOINT_NOTIFY) && !nbar.notify_all) ||
+ (!(notify_app.state & APOINT_NOTIFY) && nbar.notify_all)))
blinking = 1;
else
blinking = 0;
@@ -619,7 +624,7 @@ print_config_options (WINDOW *optwin)
const int YOFF = 3;
enum
- { SHOW, DATE, CLOCK, WARN, CMD, DMON, DMON_LOG, NB_OPT };
+ { SHOW, DATE, CLOCK, WARN, CMD, NOTIFY_ALL, DMON, DMON_LOG, NB_OPT };
struct opt_s
{
@@ -647,6 +652,9 @@ print_config_options (WINDOW *optwin)
opt[CMD].name = _("notify-bar_command = ");
opt[CMD].desc = _("(Command used to notify user of an upcoming appointment)");
+ opt[NOTIFY_ALL].name = _("notify-all = ");
+ opt[NOTIFY_ALL].desc = _("(Notify all appointments instead of flagged ones only)");
+
opt[DMON].name = _("notify-daemon_enable = ");
opt[DMON].desc = _("(Run in background to get notifications after exiting)");
@@ -663,12 +671,14 @@ print_config_options (WINDOW *optwin)
/* Boolean options */
opt[SHOW].valnum = nbar.show;
+ opt[NOTIFY_ALL].valnum = nbar.notify_all;
pthread_mutex_unlock (&nbar.mutex);
opt[DMON].valnum = dmon.enable;
opt[DMON_LOG].valnum = dmon.log;
- opt[SHOW].valstr[0] = opt[DMON].valstr[0] = opt[DMON_LOG].valstr[0] = '\0';
+ opt[SHOW].valstr[0] = opt[NOTIFY_ALL].valstr[0] = opt[DMON].valstr[0] =
+ opt[DMON_LOG].valstr[0] = '\0';
for (i = 0; i < NB_OPT; i++)
{
@@ -797,9 +807,15 @@ notify_config_bar (void)
}
break;
case '6':
- dmon.enable = !dmon.enable;
+ pthread_mutex_lock (&nbar.mutex);
+ nbar.notify_all = !nbar.notify_all;
+ pthread_mutex_unlock (&nbar.mutex);
+ notify_check_next_app (1);
break;
case '7':
+ dmon.enable = !dmon.enable;
+ break;
+ case '8':
dmon.log = !dmon.log;
break;
}
diff --git a/src/recur.c b/src/recur.c
index caa5ec3..dbc52ee 100644
--- a/src/recur.c
+++ b/src/recur.c
@@ -450,7 +450,7 @@ recur_event_scan (FILE *f, struct tm start, int id, char type, int freq,
}
/* Writting of a recursive appointment into file. */
-static void
+void
recur_apoint_write (struct recur_apoint *o, FILE *f)
{
struct tm *lt;
@@ -492,7 +492,7 @@ recur_apoint_write (struct recur_apoint *o, FILE *f)
}
/* Writting of a recursive event into file. */
-static void
+void
recur_event_write (struct recur_event *o, FILE *f)
{
struct tm *lt;
diff --git a/src/todo.c b/src/todo.c
index 8000197..c489f51 100644
--- a/src/todo.c
+++ b/src/todo.c
@@ -183,6 +183,15 @@ todo_add (char *mesg, int id, char *note)
return todo;
}
+void
+todo_write (struct todo *todo, FILE *f)
+{
+ if (todo->note)
+ (void)fprintf (f, "[%d]>%s %s\n", todo->id, todo->note, todo->mesg);
+ else
+ (void)fprintf (f, "[%d] %s\n", todo->id, todo->mesg);
+}
+
/* Delete a note previously attached to a todo item. */
static void
todo_delete_note_bynum (unsigned num)
@@ -369,12 +378,13 @@ todo_edit_item (void)
/* Display todo items in the corresponding panel. */
static void
-display_todo_item (int incolor, char *msg, int prio, int note, int len, int y,
+display_todo_item (int incolor, char *msg, int prio, int note, int width, int y,
int x)
{
WINDOW *w;
int ch_note;
- char buf[len], priostr[2];
+ char buf[width * UTF8_MAXLEN], priostr[2];
+ int i;
w = win[TOD].p;
ch_note = (note) ? '>' : '.';
@@ -385,12 +395,20 @@ display_todo_item (int incolor, char *msg, int prio, int note, int len, int y,
if (incolor == 0)
custom_apply_attr (w, ATTR_HIGHEST);
- if (strlen (msg) < len)
+ if (utf8_strwidth (msg) < width)
mvwprintw (w, y, x, "%s%c %s", priostr, ch_note, msg);
else
{
- (void)strncpy (buf, msg, len - 1);
- buf[len - 1] = '\0';
+ for (i = 0; msg[i] && width > 0; i++)
+ {
+ if (!UTF8_ISCONT (msg[i]))
+ width -= utf8_width (&msg[i]);
+ buf[i] = msg[i];
+ }
+ if (i)
+ buf[i - 1] = 0;
+ else
+ buf[0] = 0;
mvwprintw (w, y, x, "%s%c %s...", priostr, ch_note, buf);
}
if (incolor == 0)
@@ -453,37 +471,45 @@ todo_update_panel (int which_pan)
void
todo_edit_note (char *editor)
{
- struct todo *i;
- char fullname[BUFSIZ];
- char *filename;
-
- i = todo_get_item (hilt);
- if (i->note == NULL)
- {
- if ((filename = new_tempfile (path_notes, NOTESIZ)) != NULL)
- i->note = filename;
- else
- return;
- }
- (void)snprintf (fullname, BUFSIZ, "%s%s", path_notes, i->note);
- wins_launch_external (fullname, editor);
-
- if (io_file_is_empty (fullname) > 0)
- erase_note (&i->note, ERASE_FORCE);
+ struct todo *i = todo_get_item (hilt);
+ edit_note (&i->note, editor);
}
/* View a note previously attached to a todo */
void
todo_view_note (char *pager)
{
- struct todo *i;
- char fullname[BUFSIZ];
+ struct todo *i = todo_get_item (hilt);
+ view_note (i->note, pager);
+}
- i = todo_get_item (hilt);
- if (i->note == NULL)
+/* Pipe a todo item to an external program. */
+void
+todo_pipe_item (void)
+{
+ char cmd[BUFSIZ] = "";
+ int pout;
+ int pid;
+ FILE *fpout;
+ struct todo *todo;
+
+ status_mesg (_("Pipe item to external command:"), "");
+ if (getstring (win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID)
return;
- (void)snprintf (fullname, BUFSIZ, "%s%s", path_notes, i->note);
- wins_launch_external (fullname, pager);
+
+ wins_prepare_external ();
+ if ((pid = shell_exec (NULL, &pout, cmd)))
+ {
+ fpout = fdopen (pout, "w");
+
+ todo = todo_get_item (hilt);
+ todo_write (todo, fpout);
+
+ fclose (fpout);
+ child_wait (NULL, &pout, pid);
+ press_any_key ();
+ }
+ wins_unprepare_external ();
}
void
diff --git a/src/utf8.c b/src/utf8.c
new file mode 100644
index 0000000..1bb199c
--- /dev/null
+++ b/src/utf8.c
@@ -0,0 +1,344 @@
+/*
+ * Calcurse - text-based organizer
+ *
+ * Copyright (c) 2004-2011 calcurse Development Team <misc@calcurse.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Send your feedback or comments to : misc@calcurse.org
+ * Calcurse home page : http://calcurse.org
+ *
+ */
+
+#include "calcurse.h"
+
+struct utf8_range {
+ int min, max, width;
+};
+
+static const struct utf8_range utf8_widthtab[] = {
+ { 0x00300, 0x0036f, 0 },
+ { 0x00483, 0x00489, 0 },
+ { 0x00591, 0x005bd, 0 },
+ { 0x005bf, 0x005bf, 0 },
+ { 0x005c1, 0x005c2, 0 },
+ { 0x005c4, 0x005c5, 0 },
+ { 0x005c7, 0x005c7, 0 },
+ { 0x00610, 0x0061a, 0 },
+ { 0x0064b, 0x0065e, 0 },
+ { 0x00670, 0x00670, 0 },
+ { 0x006d6, 0x006dc, 0 },
+ { 0x006de, 0x006e4, 0 },
+ { 0x006e7, 0x006e8, 0 },
+ { 0x006ea, 0x006ed, 0 },
+ { 0x00711, 0x00711, 0 },
+ { 0x00730, 0x0074a, 0 },
+ { 0x007a6, 0x007b0, 0 },
+ { 0x007eb, 0x007f3, 0 },
+ { 0x00816, 0x00819, 0 },
+ { 0x0081b, 0x00823, 0 },
+ { 0x00825, 0x00827, 0 },
+ { 0x00829, 0x0082d, 0 },
+ { 0x00900, 0x00903, 0 },
+ { 0x0093c, 0x0093c, 0 },
+ { 0x0093e, 0x0094e, 0 },
+ { 0x00951, 0x00955, 0 },
+ { 0x00962, 0x00963, 0 },
+ { 0x00981, 0x00983, 0 },
+ { 0x009bc, 0x009bc, 0 },
+ { 0x009be, 0x009c4, 0 },
+ { 0x009c7, 0x009c8, 0 },
+ { 0x009cb, 0x009cd, 0 },
+ { 0x009d7, 0x009d7, 0 },
+ { 0x009e2, 0x009e3, 0 },
+ { 0x00a01, 0x00a03, 0 },
+ { 0x00a3c, 0x00a3c, 0 },
+ { 0x00a3e, 0x00a42, 0 },
+ { 0x00a47, 0x00a48, 0 },
+ { 0x00a4b, 0x00a4d, 0 },
+ { 0x00a51, 0x00a51, 0 },
+ { 0x00a70, 0x00a71, 0 },
+ { 0x00a75, 0x00a75, 0 },
+ { 0x00a81, 0x00a83, 0 },
+ { 0x00abc, 0x00abc, 0 },
+ { 0x00abe, 0x00ac5, 0 },
+ { 0x00ac7, 0x00ac9, 0 },
+ { 0x00acb, 0x00acd, 0 },
+ { 0x00ae2, 0x00ae3, 0 },
+ { 0x00b01, 0x00b03, 0 },
+ { 0x00b3c, 0x00b3c, 0 },
+ { 0x00b3e, 0x00b44, 0 },
+ { 0x00b47, 0x00b48, 0 },
+ { 0x00b4b, 0x00b4d, 0 },
+ { 0x00b56, 0x00b57, 0 },
+ { 0x00b62, 0x00b63, 0 },
+ { 0x00b82, 0x00b82, 0 },
+ { 0x00bbe, 0x00bc2, 0 },
+ { 0x00bc6, 0x00bc8, 0 },
+ { 0x00bca, 0x00bcd, 0 },
+ { 0x00bd7, 0x00bd7, 0 },
+ { 0x00c01, 0x00c03, 0 },
+ { 0x00c3e, 0x00c44, 0 },
+ { 0x00c46, 0x00c48, 0 },
+ { 0x00c4a, 0x00c4d, 0 },
+ { 0x00c55, 0x00c56, 0 },
+ { 0x00c62, 0x00c63, 0 },
+ { 0x00c82, 0x00c83, 0 },
+ { 0x00cbc, 0x00cbc, 0 },
+ { 0x00cbe, 0x00cc4, 0 },
+ { 0x00cc6, 0x00cc8, 0 },
+ { 0x00cca, 0x00ccd, 0 },
+ { 0x00cd5, 0x00cd6, 0 },
+ { 0x00ce2, 0x00ce3, 0 },
+ { 0x00d02, 0x00d03, 0 },
+ { 0x00d3e, 0x00d44, 0 },
+ { 0x00d46, 0x00d48, 0 },
+ { 0x00d4a, 0x00d4d, 0 },
+ { 0x00d57, 0x00d57, 0 },
+ { 0x00d62, 0x00d63, 0 },
+ { 0x00d82, 0x00d83, 0 },
+ { 0x00dca, 0x00dca, 0 },
+ { 0x00dcf, 0x00dd4, 0 },
+ { 0x00dd6, 0x00dd6, 0 },
+ { 0x00dd8, 0x00ddf, 0 },
+ { 0x00df2, 0x00df3, 0 },
+ { 0x00e31, 0x00e31, 0 },
+ { 0x00e34, 0x00e3a, 0 },
+ { 0x00e47, 0x00e4e, 0 },
+ { 0x00eb1, 0x00eb1, 0 },
+ { 0x00eb4, 0x00eb9, 0 },
+ { 0x00ebb, 0x00ebc, 0 },
+ { 0x00ec8, 0x00ecd, 0 },
+ { 0x00f18, 0x00f19, 0 },
+ { 0x00f35, 0x00f35, 0 },
+ { 0x00f37, 0x00f37, 0 },
+ { 0x00f39, 0x00f39, 0 },
+ { 0x00f3e, 0x00f3f, 0 },
+ { 0x00f71, 0x00f84, 0 },
+ { 0x00f86, 0x00f87, 0 },
+ { 0x00f90, 0x00f97, 0 },
+ { 0x00f99, 0x00fbc, 0 },
+ { 0x00fc6, 0x00fc6, 0 },
+ { 0x0102b, 0x0103e, 0 },
+ { 0x01056, 0x01059, 0 },
+ { 0x0105e, 0x01060, 0 },
+ { 0x01062, 0x01064, 0 },
+ { 0x01067, 0x0106d, 0 },
+ { 0x01071, 0x01074, 0 },
+ { 0x01082, 0x0108d, 0 },
+ { 0x0108f, 0x0108f, 0 },
+ { 0x0109a, 0x0109d, 0 },
+ { 0x01100, 0x0115f, 2 },
+ { 0x011a3, 0x011a7, 2 },
+ { 0x011fa, 0x011ff, 2 },
+ { 0x0135f, 0x0135f, 0 },
+ { 0x01712, 0x01714, 0 },
+ { 0x01732, 0x01734, 0 },
+ { 0x01752, 0x01753, 0 },
+ { 0x01772, 0x01773, 0 },
+ { 0x017b6, 0x017d3, 0 },
+ { 0x017dd, 0x017dd, 0 },
+ { 0x0180b, 0x0180d, 0 },
+ { 0x018a9, 0x018a9, 0 },
+ { 0x01920, 0x0192b, 0 },
+ { 0x01930, 0x0193b, 0 },
+ { 0x019b0, 0x019c0, 0 },
+ { 0x019c8, 0x019c9, 0 },
+ { 0x01a17, 0x01a1b, 0 },
+ { 0x01a55, 0x01a5e, 0 },
+ { 0x01a60, 0x01a7c, 0 },
+ { 0x01a7f, 0x01a7f, 0 },
+ { 0x01b00, 0x01b04, 0 },
+ { 0x01b34, 0x01b44, 0 },
+ { 0x01b6b, 0x01b73, 0 },
+ { 0x01b80, 0x01b82, 0 },
+ { 0x01ba1, 0x01baa, 0 },
+ { 0x01c24, 0x01c37, 0 },
+ { 0x01cd0, 0x01cd2, 0 },
+ { 0x01cd4, 0x01ce8, 0 },
+ { 0x01ced, 0x01ced, 0 },
+ { 0x01cf2, 0x01cf2, 0 },
+ { 0x01dc0, 0x01de6, 0 },
+ { 0x01dfd, 0x01dff, 0 },
+ { 0x020d0, 0x020f0, 0 },
+ { 0x02329, 0x0232a, 2 },
+ { 0x02cef, 0x02cf1, 0 },
+ { 0x02de0, 0x02dff, 0 },
+ { 0x02e80, 0x02e99, 2 },
+ { 0x02e9b, 0x02ef3, 2 },
+ { 0x02f00, 0x02fd5, 2 },
+ { 0x02ff0, 0x02ffb, 2 },
+ { 0x03000, 0x03029, 2 },
+ { 0x0302a, 0x0302f, 0 },
+ { 0x03030, 0x0303e, 2 },
+ { 0x03041, 0x03096, 2 },
+ { 0x03099, 0x0309a, 0 },
+ { 0x0309b, 0x030ff, 2 },
+ { 0x03105, 0x0312d, 2 },
+ { 0x03131, 0x0318e, 2 },
+ { 0x03190, 0x031b7, 2 },
+ { 0x031c0, 0x031e3, 2 },
+ { 0x031f0, 0x0321e, 2 },
+ { 0x03220, 0x03247, 2 },
+ { 0x03250, 0x032fe, 2 },
+ { 0x03300, 0x04dbf, 2 },
+ { 0x04e00, 0x0a48c, 2 },
+ { 0x0a490, 0x0a4c6, 2 },
+ { 0x0a66f, 0x0a672, 0 },
+ { 0x0a67c, 0x0a67d, 0 },
+ { 0x0a6f0, 0x0a6f1, 0 },
+ { 0x0a802, 0x0a802, 0 },
+ { 0x0a806, 0x0a806, 0 },
+ { 0x0a80b, 0x0a80b, 0 },
+ { 0x0a823, 0x0a827, 0 },
+ { 0x0a880, 0x0a881, 0 },
+ { 0x0a8b4, 0x0a8c4, 0 },
+ { 0x0a8e0, 0x0a8f1, 0 },
+ { 0x0a926, 0x0a92d, 0 },
+ { 0x0a947, 0x0a953, 0 },
+ { 0x0a960, 0x0a97c, 2 },
+ { 0x0a980, 0x0a983, 0 },
+ { 0x0a9b3, 0x0a9c0, 0 },
+ { 0x0aa29, 0x0aa36, 0 },
+ { 0x0aa43, 0x0aa43, 0 },
+ { 0x0aa4c, 0x0aa4d, 0 },
+ { 0x0aa7b, 0x0aa7b, 0 },
+ { 0x0aab0, 0x0aab0, 0 },
+ { 0x0aab2, 0x0aab4, 0 },
+ { 0x0aab7, 0x0aab8, 0 },
+ { 0x0aabe, 0x0aabf, 0 },
+ { 0x0aac1, 0x0aac1, 0 },
+ { 0x0abe3, 0x0abea, 0 },
+ { 0x0abec, 0x0abed, 0 },
+ { 0x0ac00, 0x0d7a3, 2 },
+ { 0x0d7b0, 0x0d7c6, 2 },
+ { 0x0d7cb, 0x0d7fb, 2 },
+ { 0x0f900, 0x0faff, 2 },
+ { 0x0fb1e, 0x0fb1e, 0 },
+ { 0x0fe00, 0x0fe0f, 0 },
+ { 0x0fe10, 0x0fe19, 2 },
+ { 0x0fe20, 0x0fe26, 0 },
+ { 0x0fe30, 0x0fe52, 2 },
+ { 0x0fe54, 0x0fe66, 2 },
+ { 0x0fe68, 0x0fe6b, 2 },
+ { 0x0ff01, 0x0ff60, 2 },
+ { 0x0ffe0, 0x0ffe6, 2 },
+ { 0x101fd, 0x101fd, 0 },
+ { 0x10a01, 0x10a03, 0 },
+ { 0x10a05, 0x10a06, 0 },
+ { 0x10a0c, 0x10a0f, 0 },
+ { 0x10a38, 0x10a3a, 0 },
+ { 0x10a3f, 0x10a3f, 0 },
+ { 0x11080, 0x11082, 0 },
+ { 0x110b0, 0x110ba, 0 },
+ { 0x1d165, 0x1d169, 0 },
+ { 0x1d16d, 0x1d172, 0 },
+ { 0x1d17b, 0x1d182, 0 },
+ { 0x1d185, 0x1d18b, 0 },
+ { 0x1d1aa, 0x1d1ad, 0 },
+ { 0x1d242, 0x1d244, 0 },
+ { 0x1f200, 0x1f200, 2 },
+ { 0x1f210, 0x1f231, 2 },
+ { 0x1f240, 0x1f248, 2 },
+ { 0x20000, 0x2fffd, 2 },
+ { 0x30000, 0x3fffd, 2 },
+ { 0xe0100, 0xe01ef, 0 }
+};
+
+/* Get the width of a UTF-8 character. */
+int
+utf8_width (char *s)
+{
+ int val, low, high, cur;
+
+ if (UTF8_ISCONT (*s))
+ return 0;
+
+ switch (UTF8_LENGTH (*s))
+ {
+ case 1:
+ val = s[0];
+ break;
+ case 2:
+ val = (s[1] & 0x3f) | (s[0] & 0x1f) << 6;
+ break;
+ case 3:
+ val = ((s[2] & 0x3f) | (s[1] & 0x3f) << 6) |
+ (s[0] & 0x0f) << 12;
+ break;
+ case 4:
+ val = (((s[3] & 0x3f) | (s[2] & 0x3f) << 6) |
+ (s[1] & 0x3f) << 12) | (s[0] & 0x3f) << 18;
+ break;
+ case 5:
+ val = ((((s[4] & 0x3f) | (s[3] & 0x3f) << 6) |
+ (s[2] & 0x3f) << 12) | (s[1] & 0x3f) << 18) |
+ (s[0] & 0x3f) << 24;
+ break;
+ case 6:
+ val = (((((s[5] & 0x3f) | (s[4] & 0x3f) << 6) |
+ (s[3] & 0x3f) << 12) | (s[2] & 0x3f) << 18) |
+ (s[1] & 0x3f) << 24) | (s[0] & 0x3f) << 30;
+ break;
+ default:
+ return 0;
+ }
+
+ low = 0;
+ high = sizeof(utf8_widthtab) / sizeof(utf8_widthtab[0]);
+ do
+ {
+ cur = (low + high) / 2;
+ if (val >= utf8_widthtab[cur].min)
+ {
+ if (val <= utf8_widthtab[cur].max)
+ return utf8_widthtab[cur].width;
+ else
+ low = cur + 1;
+ }
+ else
+ high = cur - 1;
+ }
+ while (low <= high);
+
+ return 1;
+}
+
+/* Get the width of a UTF-8 string. */
+int
+utf8_strwidth (char *s)
+{
+ int width = 0;
+
+ for (; s && *s; s++)
+ {
+ if (!UTF8_ISCONT (*s))
+ width += utf8_width (s);
+ }
+
+ return width;
+}
diff --git a/src/utils.c b/src/utils.c
index b165111..4ea2ce3 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -42,6 +42,8 @@
#include <ctype.h>
#include <sys/types.h>
#include <errno.h>
+#include <fcntl.h>
+#include <sys/wait.h>
#include "calcurse.h"
@@ -230,189 +232,6 @@ print_in_middle (WINDOW *win, int starty, int startx, int width, char *string)
custom_remove_attr (win, ATTR_HIGHEST);
}
-/* Print the string at the desired position. */
-static void
-showstring (WINDOW *win, int x, int y, char *str, int len, int scroff,
- int curpos)
-{
- char c = 0;
-
- /* print string */
- mvwaddnstr (win, y, x, &str[scroff], -1);
- wclrtoeol (win);
-
- /* print scrolling indicator */
- if (scroff > 0 && scroff < len - col)
- c = '*';
- else if (scroff > 0)
- c = '<';
- else if (scroff < len - col)
- c = '>';
- mvwprintw (win, y, col - 1, "%c", c);
-
- /* print cursor */
- wmove (win, y, curpos - scroff);
-
- if (curpos >= len)
- waddch (win, SPACE | A_REVERSE);
- else
- waddch (win, str[curpos] | A_REVERSE);
-}
-
-/* Delete a character at the given position in string. */
-static void
-del_char (int pos, char *str)
-{
- str += pos;
- memmove (str, str + 1, strlen (str) + 1);
-}
-
-/* Add a character at the given position in string. */
-static void
-ins_char (int pos, int ch, char *str)
-{
- str += pos;
- memmove (str + 1, str, strlen (str) + 1);
- *str = ch;
-}
-
-static void
-bell (void)
-{
- printf ("\a");
-}
-
-/*
- * 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.
- */
-enum getstr
-getstring (WINDOW *win, char *str, int l, int x, int y)
-{
- const int pgsize = col / 3;
-
- int len = strlen (str);
- int curpos = len;
- int scroff = 0;
- int ch;
-
- custom_apply_attr (win, ATTR_HIGHEST);
-
- for (;;) {
- while (curpos < scroff)
- scroff -= pgsize;
- while (curpos >= scroff + col - 1)
- scroff += pgsize;
-
- showstring (win, x, y, str, len, scroff, curpos);
- wins_doupdate ();
-
- if ((ch = wgetch (win)) == '\n') break;
- switch (ch)
- {
- case KEY_BACKSPACE: /* delete one character */
- case 330:
- case 127:
- case CTRL ('H'):
- if (curpos > 0)
- {
- del_char ((--curpos), str);
- len--;
- }
- else
- bell ();
- break;
- case CTRL ('D'): /* delete next character */
- if (curpos < len)
- {
- del_char (curpos, str);
- len--;
- }
- else
- bell ();
- break;
- case CTRL ('W'): /* delete a word */
- if (curpos > 0) {
- while (curpos && str[curpos - 1] == ' ')
- {
- del_char ((--curpos), str);
- len--;
- }
- while (curpos && str[curpos - 1] != ' ')
- {
- del_char ((--curpos), str);
- len--;
- }
- }
- else
- bell ();
- break;
- case CTRL ('K'): /* delete to end-of-line */
- str[curpos] = 0;
- len = curpos;
- break;
- case CTRL ('A'): /* go to begginning of string */
- curpos = 0;
- break;
- case CTRL ('E'): /* go to end of string */
- curpos = len;
- break;
- case KEY_LEFT: /* move one char backward */
- case CTRL ('B'):
- if (curpos > 0) curpos--;
- break;
- case KEY_RIGHT: /* move one char forward */
- case CTRL ('F'):
- if (curpos < len) curpos++;
- break;
- case ESCAPE: /* cancel editing */
- return (GETSTRING_ESC);
- break;
- default: /* insert one character */
- if (len < l - 1)
- {
- ins_char ((curpos++), ch, str);
- len++;
- }
- }
- }
-
- 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)
-{
- int len = strlen (*str);
- char *buf;
- enum getstr ret;
-
- EXIT_IF (len + 1 > BUFSIZ, _("Internal error: line too long"));
-
- buf = mem_malloc (BUFSIZ);
- (void)memcpy (buf, *str, len + 1);
-
- ret = getstring (win, buf, BUFSIZ, x, y);
-
- if (ret == GETSTRING_VALID)
- {
- len = strlen (buf);
- *str = mem_realloc (*str, len + 1, 1);
- EXIT_IF (*str == NULL, _("out of memory"));
- (void)memcpy (*str, buf, len + 1);
- }
-
- mem_free (buf);
- return ret;
-}
-
/* checks if a string is only made of digits */
int
is_all_digit (char *string)
@@ -776,24 +595,6 @@ new_tempfile (const char *prefix, int trailing_len)
return mem_strdup (fullname + prefix_len);
}
-/* Erase a note previously attached to a todo, event or appointment. */
-void
-erase_note (char **note, enum eraseflg flag)
-{
- char fullname[BUFSIZ];
-
- if (*note == NULL)
- return;
- if (flag != ERASE_FORCE_KEEP_NOTE)
- {
- (void)snprintf (fullname, BUFSIZ, "%s%s", path_notes, *note);
- if (unlink (fullname) != 0)
- EXIT (_("could not remove note"));
- }
- mem_free (*note);
- *note = NULL;
-}
-
/*
* Convert a string containing a date into three integers containing the year,
* month and day.
@@ -916,3 +717,108 @@ psleep (unsigned secs)
for (unslept = sleep (secs); unslept; unslept = sleep (unslept))
;
}
+
+/*
+ * Fork and execute an external process.
+ *
+ * If pfdin and/or pfdout point to a valid address, a pipe is created and the
+ * appropriate file descriptors are written to pfdin/pfdout.
+ */
+int
+fork_exec (int *pfdin, int *pfdout, const char *path, char *const *arg)
+{
+ int pin[2], pout[2];
+ int pid;
+
+ if (pfdin && (pipe (pin) == -1))
+ return 0;
+ if (pfdout && (pipe (pout) == -1))
+ return 0;
+
+ if ((pid = fork ()) == 0)
+ {
+ if (pfdout)
+ {
+ if (dup2 (pout[0], STDIN_FILENO) < 0)
+ _exit (127);
+ close (pout[0]);
+ close (pout[1]);
+ }
+
+ if (pfdin)
+ {
+ if (dup2 (pin[1], STDOUT_FILENO) < 0)
+ _exit (127);
+ close (pin[0]);
+ close (pin[1]);
+ }
+
+ execvp (path, arg);
+ _exit (127);
+ }
+ else
+ {
+ if (pfdin)
+ close (pin[1]);
+ if (pfdout)
+ close (pout[0]);
+
+ if (pid > 0)
+ {
+ if (pfdin)
+ {
+ fcntl (pin[0], F_SETFD, FD_CLOEXEC);
+ *pfdin = pin[0];
+ }
+ if (pfdout)
+ {
+ fcntl (pout[1], F_SETFD, FD_CLOEXEC);
+ *pfdout = pout[1];
+ }
+ }
+ else
+ {
+ if (pfdin)
+ close (pin[0]);
+ if (pfdout)
+ close (pout[1]);
+ return 0;
+ }
+ }
+ return pid;
+}
+
+/* Execute an external program in a shell. */
+int
+shell_exec (int *pfdin, int *pfdout, char *cmd)
+{
+ char *arg[] = { "/bin/sh", "-c", cmd, NULL };
+ return fork_exec (pfdin, pfdout, *arg, arg);
+}
+
+/* Wait for a child process to terminate. */
+int
+child_wait (int *pfdin, int *pfdout, int pid)
+{
+ int stat;
+
+ if (pfdin)
+ close (*pfdin);
+ if (pfdout)
+ close (*pfdout);
+
+ waitpid (pid, &stat, 0);
+ return stat;
+}
+
+/* Display "Press any key to continue..." and wait for a key press. */
+void
+press_any_key (void)
+{
+ fflush (stdout);
+ fputs (_("Press any key to continue..."), stdout);
+ fflush (stdout);
+ fgetc (stdin);
+ fflush (stdin);
+ fputs ("\r\n", stdout);
+}
diff --git a/src/vars.c b/src/vars.c
index 4b1d7cd..ef2cd73 100644
--- a/src/vars.c
+++ b/src/vars.c
@@ -48,6 +48,9 @@ int resize = 0;
/* variable to tell if the terminal supports color */
unsigned colorize = 0;
+/* Default background and foreground colors. */
+int foreground, background;
+
/*
* To tell if curses interface was launched already or not (in that case
* calcurse is running in command-line mode).
diff --git a/src/wins.c b/src/wins.c
index 7b7384b..0e4b402 100644
--- a/src/wins.c
+++ b/src/wins.c
@@ -599,33 +599,23 @@ wins_reset (void)
wins_update ();
}
-/*
- * While inside interactive mode, launch the external command cmd on the given
- * file.
- */
+/* Prepare windows for the execution of an external command. */
void
-wins_launch_external (const char *file, const char *cmd)
+wins_prepare_external (void)
{
- char *p;
- int len;
-
- /* Beware of space between cmd and file. */
- len = strlen (file) + strlen (cmd) + 2;
-
- p = (char *) mem_calloc (len, sizeof (char));
- if (snprintf (p, len, "%s %s", cmd, file) == -1)
- {
- mem_free (p);
- return;
- }
if (notify_bar ())
notify_stop_main_thread ();
def_prog_mode ();
- endwin ();
ui_mode = UI_CMDLINE;
clear ();
wins_refresh ();
- (void)system (p);
+ endwin ();
+}
+
+/* Restore windows when returning from an external command. */
+void
+wins_unprepare_external (void)
+{
reset_prog_mode ();
clearok (curscr, TRUE);
curs_set (0);
@@ -633,12 +623,27 @@ wins_launch_external (const char *file, const char *cmd)
wins_refresh ();
if (notify_bar ())
notify_start_main_thread ();
- mem_free (p);
+}
+
+/*
+ * While inside interactive mode, launch the external command cmd on the given
+ * file.
+ */
+void
+wins_launch_external (char *file, char *cmd)
+{
+ char *arg[] = { cmd, file, NULL };
+ int pid;
+
+ wins_prepare_external ();
+ if ((pid = fork_exec (NULL, NULL, cmd, arg)))
+ child_wait (NULL, NULL, pid);
+ wins_unprepare_external ();
}
#define NB_CAL_CMDS 27 /* number of commands while in cal view */
-#define NB_APP_CMDS 31 /* same thing while in appointment view */
-#define NB_TOD_CMDS 30 /* same thing while in todo view */
+#define NB_APP_CMDS 32 /* same thing while in appointment view */
+#define NB_TOD_CMDS 31 /* 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 */
@@ -690,6 +695,7 @@ wins_status_bar (void)
struct binding del = {_("Del Item"), KEY_DEL_ITEM};
struct binding edit = {_("Edit Itm"), KEY_EDIT_ITEM};
struct binding view = {_("View"), KEY_VIEW_ITEM};
+ struct binding pipe = {_("Pipe"), KEY_PIPE_ITEM};
struct binding flag = {_("Flag Itm"), KEY_FLAG_ITEM};
struct binding rept = {_("Repeat"), KEY_REPEAT_ITEM};
struct binding enote = {_("EditNote"), KEY_EDIT_NOTE};
@@ -704,13 +710,14 @@ wins_status_bar (void)
&gnday, &gpday, &gnweek, &gpweek, &draw, &othr, &today, &conf, &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, &cut, &paste,
- &othr,
+ &pipe, &othr, &draw, &rept, &flag, &enote, &vnote, &up, &down, &gnday,
+ &gpday, &gnweek, &gpweek, &othr, &togo, &today, &conf, &appt, &todo, &cut,
+ &paste, &othr,
/* todo keys */
&help, &quit, &save, &chgvu, &import, &export, &add, &del, &edit, &view,
- &flag, &othr, &rprio, &lprio, &enote, &vnote, &up, &down, &gnday, &gpday,
- &gnweek, &gpweek, &togo, &othr, &today, &conf, &appt, &todo, &draw, &othr
+ &pipe, &othr, &flag, &rprio, &lprio, &enote, &vnote, &up, &down, &gnday,
+ &gpday, &gnweek, &gpweek, &othr, &togo, &today, &conf, &appt, &todo, &draw,
+ &othr
};
/* Drawing the keybinding with attribute and label without. */