diff options
-rw-r--r-- | TODO | 7 | ||||
-rw-r--r-- | doc/manual.txt | 3 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/args.c | 22 | ||||
-rw-r--r-- | src/calcurse.c | 12 | ||||
-rw-r--r-- | src/calcurse.h | 185 | ||||
-rw-r--r-- | src/custom.c | 370 | ||||
-rw-r--r-- | src/day.c | 95 | ||||
-rw-r--r-- | src/dmon.c | 7 | ||||
-rw-r--r-- | src/getstring.c | 299 | ||||
-rw-r--r-- | src/help.c | 13 | ||||
-rw-r--r-- | src/io.c | 80 | ||||
-rw-r--r-- | src/keys.c | 11 | ||||
-rw-r--r-- | src/note.c | 90 | ||||
-rw-r--r-- | src/recur.c | 4 | ||||
-rw-r--r-- | src/todo.c | 82 | ||||
-rw-r--r-- | src/utf8.c | 344 | ||||
-rw-r--r-- | src/utils.c | 308 | ||||
-rw-r--r-- | src/vars.c | 3 | ||||
-rw-r--r-- | src/wins.c | 61 |
20 files changed, 1399 insertions, 600 deletions
@@ -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..6bf7637 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 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 \ @@ -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..1a63657 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,13 @@ 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 */ pthread_mutex_t mutex; }; @@ -601,7 +608,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 +635,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 +658,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 +747,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 +795,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 +833,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 +841,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 +858,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 +879,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 +942,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..6385593 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, @@ -63,7 +63,36 @@ enum conf_var { 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_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 +102,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 +142,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 +151,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 +179,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 +221,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 +278,101 @@ 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_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 +382,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); @@ -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 (); } @@ -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; +} @@ -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" @@ -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,39 @@ 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# 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 +961,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 +1023,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 +2754,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 +2816,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 @@ -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/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; @@ -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); +} @@ -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). @@ -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. */ |