"Fossies" - the Fresh Open Source Software Archive

Member "calcurse-4.5.1/src/notify.c" (20 Apr 2019, 21309 Bytes) of package /linux/privat/calcurse-4.5.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "notify.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 4.3.0_vs_4.4.0.

    1 /*
    2  * Calcurse - text-based organizer
    3  *
    4  * Copyright (c) 2004-2017 calcurse Development Team <misc@calcurse.org>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  *
   11  *      - Redistributions of source code must retain the above
   12  *        copyright notice, this list of conditions and the
   13  *        following disclaimer.
   14  *
   15  *      - Redistributions in binary form must reproduce the above
   16  *        copyright notice, this list of conditions and the
   17  *        following disclaimer in the documentation and/or other
   18  *        materials provided with the distribution.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   31  *
   32  * Send your feedback or comments to : misc@calcurse.org
   33  * Calcurse home page : http://calcurse.org
   34  *
   35  */
   36 
   37 #include <time.h>
   38 #include <stdlib.h>
   39 #include <string.h>
   40 #include <unistd.h>
   41 
   42 #include "calcurse.h"
   43 
   44 #define NOTIFY_FIELD_LENGTH 25
   45 
   46 struct notify_vars {
   47     WINDOW *win;
   48     char *apts_file;
   49     char time[NOTIFY_FIELD_LENGTH];
   50     char date[NOTIFY_FIELD_LENGTH];
   51     pthread_mutex_t mutex;
   52 };
   53 
   54 static struct notify_vars notify;
   55 static struct notify_app notify_app;
   56 static pthread_attr_t detached_thread_attr;
   57 
   58 /*
   59  * Return the number of seconds before next appointment
   60  * (0 if no upcoming appointment).
   61  */
   62 int notify_time_left(void)
   63 {
   64     time_t ntimer;
   65     int left;
   66 
   67     ntimer = time(NULL);
   68     left = notify_app.time - ntimer;
   69 
   70     return left > 0 ? left : 0;
   71 }
   72 
   73 static unsigned notify_trigger(void)
   74 {
   75     int flagged = notify_app.state & APOINT_NOTIFY;
   76 
   77     if (!notify_app.got_app)
   78         return 0;
   79     if (nbar.notify_all == NOTIFY_ALL)
   80         return 1;
   81     if (nbar.notify_all == NOTIFY_UNFLAGGED_ONLY)
   82         flagged = !flagged;
   83     return flagged;
   84 }
   85 
   86 /*
   87  * Return 1 if the reminder was not sent already for the upcoming appointment.
   88  */
   89 unsigned notify_needs_reminder(void)
   90 {
   91     if (notify_app.state & APOINT_NOTIFIED)
   92         return 0;
   93     return notify_trigger();
   94 }
   95 
   96 /*
   97  * This is used to update the notify_app structure.
   98  * Note: the mutex associated with this structure must be locked by the
   99  * caller!
  100  */
  101 void notify_update_app(time_t start, char state, char *msg)
  102 {
  103     notify_free_app();
  104     notify_app.got_app = 1;
  105     notify_app.time = start;
  106     notify_app.state = state;
  107     notify_app.txt = mem_strdup(msg);
  108 }
  109 
  110 /* Return 1 if we need to display the notify-bar, else 0. */
  111 int notify_bar(void)
  112 {
  113     int display_bar = 0;
  114 
  115     pthread_mutex_lock(&nbar.mutex);
  116     display_bar = (nbar.show) ? 1 : 0;
  117     pthread_mutex_unlock(&nbar.mutex);
  118 
  119     return display_bar;
  120 }
  121 
  122 /* Initialize the nbar variable used to store notification options. */
  123 void notify_init_vars(void)
  124 {
  125     const char *time_format = "%T";
  126     const char *date_format = "%a %F";
  127     const char *cmd = "printf '\\a'";
  128 
  129     pthread_mutex_init(&nbar.mutex, NULL);
  130     nbar.show = 1;
  131     nbar.cntdwn = 300;
  132     strncpy(nbar.datefmt, date_format, BUFSIZ);
  133     nbar.datefmt[BUFSIZ - 1] = '\0';
  134     strncpy(nbar.timefmt, time_format, BUFSIZ);
  135     nbar.timefmt[BUFSIZ - 1] = '\0';
  136     strncpy(nbar.cmd, cmd, BUFSIZ);
  137     nbar.cmd[BUFSIZ - 1] = '\0';
  138 
  139     if ((nbar.shell = getenv("SHELL")) == NULL)
  140         nbar.shell = "/bin/sh";
  141 
  142     nbar.notify_all = 0;
  143 
  144     pthread_attr_init(&detached_thread_attr);
  145     pthread_attr_setdetachstate(&detached_thread_attr,
  146                     PTHREAD_CREATE_DETACHED);
  147 }
  148 
  149 /* Extract the appointment file name from the complete file path. */
  150 static void extract_aptsfile(void)
  151 {
  152     char *file;
  153 
  154     file = strrchr(path_apts, '/');
  155     if (!file) {
  156         notify.apts_file = path_apts;
  157     } else {
  158         notify.apts_file = file;
  159         notify.apts_file++;
  160     }
  161 }
  162 
  163 /*
  164  * Create the notification bar, by initializing all the variables and
  165  * creating the notification window (l is the number of lines, c the
  166  * number of columns, y and x are its coordinates).
  167  */
  168 void notify_init_bar(void)
  169 {
  170     pthread_mutex_init(&notify.mutex, NULL);
  171     pthread_mutex_init(&notify_app.mutex, NULL);
  172     notify_app.got_app = 0;
  173     notify_app.txt = 0;
  174     notify.win =
  175         newwin(win[NOT].h, win[NOT].w, win[NOT].y, win[NOT].x);
  176     extract_aptsfile();
  177 }
  178 
  179 /*
  180  * Free memory associated with the notify_app structure.
  181  */
  182 void notify_free_app(void)
  183 {
  184     notify_app.time = 0;
  185     notify_app.got_app = 0;
  186     notify_app.state = APOINT_NULL;
  187     if (notify_app.txt)
  188         mem_free(notify_app.txt);
  189     notify_app.txt = 0;
  190 }
  191 
  192 /* Stop the notify-bar main thread. */
  193 void notify_stop_main_thread(void)
  194 {
  195     /* Is the thread running? */
  196     if (pthread_equal(notify_t_main, pthread_self()))
  197         return;
  198 
  199     pthread_cancel(notify_t_main);
  200     pthread_join(notify_t_main, NULL);
  201     notify_t_main = pthread_self();
  202 }
  203 
  204 /*
  205  * The calcurse window geometry has changed so we need to reset the
  206  * notification window.
  207  */
  208 void notify_reinit_bar(void)
  209 {
  210     delwin(notify.win);
  211     notify.win =
  212         newwin(win[NOT].h, win[NOT].w, win[NOT].y, win[NOT].x);
  213 }
  214 
  215 /* Launch user defined command as a notification. */
  216 unsigned notify_launch_cmd(void)
  217 {
  218     int pid;
  219 
  220     if (notify_app.state & APOINT_NOTIFIED)
  221         return 1;
  222 
  223     notify_app.state |= APOINT_NOTIFIED;
  224 
  225     pid = fork();
  226 
  227     if (pid < 0) {
  228         ERROR_MSG(_("error while launching command: could not fork"));
  229         return 0;
  230     } else if (pid == 0) {
  231         /* Child: launch user defined command */
  232         if (execlp(nbar.shell, nbar.shell, "-c", nbar.cmd, NULL) <
  233             0) {
  234             ERROR_MSG(_("error while launching command"));
  235             _exit(1);
  236         }
  237         _exit(0);
  238     }
  239 
  240     return 1;
  241 }
  242 
  243 /*
  244  * Update the notification bar. This is useful when changing color theme
  245  * for example.
  246  */
  247 void notify_update_bar(void)
  248 {
  249     const int space = 3;
  250     int file_pos, date_pos, app_pos, txt_max_len;
  251     int time_left, blinking;
  252 
  253     date_pos = space;
  254     pthread_mutex_lock(&notify.mutex);
  255 
  256     file_pos =
  257         strlen(notify.date) + strlen(notify.time) + 7 + 2 * space;
  258     app_pos = file_pos + strlen(notify.apts_file) + 2 + space;
  259     txt_max_len = MAX(col - (app_pos + 12 + space), 3);
  260 
  261     WINS_NBAR_LOCK;
  262     custom_apply_attr(notify.win, ATTR_HIGHEST);
  263     wattron(notify.win, A_UNDERLINE | A_REVERSE);
  264     mvwhline(notify.win, 0, 0, ACS_HLINE, col);
  265     mvwprintw(notify.win, 0, date_pos, "[ %s | %s ]", notify.date,
  266           notify.time);
  267     mvwprintw(notify.win, 0, file_pos, "(%s)", notify.apts_file);
  268     WINS_NBAR_UNLOCK;
  269 
  270     pthread_mutex_lock(&notify_app.mutex);
  271     if (notify_app.got_app) {
  272         char buf[txt_max_len * UTF8_MAXLEN];
  273 
  274         strncpy(buf, notify_app.txt, txt_max_len * UTF8_MAXLEN);
  275         buf[sizeof(buf) - 1] = '\0';
  276         utf8_chop(buf, txt_max_len);
  277 
  278         time_left = notify_time_left();
  279         if (time_left > 0) {
  280             int hours_left, minutes_left;
  281 
  282             /* In minutes rounded up. */
  283             minutes_left = time_left / MININSEC +
  284                        (time_left % MININSEC ?  1 : 0);
  285 
  286             hours_left = minutes_left / HOURINMIN;
  287             minutes_left = minutes_left % HOURINMIN;
  288 
  289             pthread_mutex_lock(&nbar.mutex);
  290             blinking = time_left <= nbar.cntdwn && notify_trigger();
  291 
  292             WINS_NBAR_LOCK;
  293             if (blinking)
  294                 wattron(notify.win, A_BLINK);
  295             mvwprintw(notify.win, 0, app_pos,
  296                   "> %02d:%02d :: %s <", hours_left,
  297                   minutes_left, buf);
  298             if (blinking)
  299                 wattroff(notify.win, A_BLINK);
  300             WINS_NBAR_UNLOCK;
  301 
  302             if (blinking)
  303                 notify_launch_cmd();
  304             pthread_mutex_unlock(&nbar.mutex);
  305         } else {
  306             notify_app.got_app = 0;
  307             pthread_mutex_unlock(&notify_app.mutex);
  308             pthread_mutex_unlock(&notify.mutex);
  309             notify_check_next_app(0);
  310             return;
  311         }
  312     }
  313     pthread_mutex_unlock(&notify_app.mutex);
  314 
  315     WINS_NBAR_LOCK;
  316     wattroff(notify.win, A_UNDERLINE | A_REVERSE);
  317     custom_remove_attr(notify.win, ATTR_HIGHEST);
  318     WINS_NBAR_UNLOCK;
  319     wins_wrefresh(notify.win);
  320 
  321     pthread_mutex_unlock(&notify.mutex);
  322 }
  323 
  324 static void
  325 notify_main_thread_cleanup(void *arg)
  326 {
  327     pthread_mutex_trylock(&notify.mutex);
  328     pthread_mutex_unlock(&notify.mutex);
  329     pthread_mutex_trylock(&nbar.mutex);
  330     pthread_mutex_unlock(&nbar.mutex);
  331 }
  332 
  333 /* Update the notication bar content */
  334 /* ARGSUSED0 */
  335 static void *notify_main_thread(void *arg)
  336 {
  337     const unsigned thread_sleep = 1;
  338     const unsigned check_app = MININSEC;
  339     int elapse = 0;
  340     int got_app;
  341     struct tm ntime;
  342     time_t ntimer;
  343 
  344     elapse = 0;
  345 
  346     pthread_cleanup_push(notify_main_thread_cleanup, NULL);
  347 
  348     for (;;) {
  349         ntimer = time(NULL);
  350         localtime_r(&ntimer, &ntime);
  351         pthread_mutex_lock(&notify.mutex);
  352         pthread_mutex_lock(&nbar.mutex);
  353         strftime(notify.time, NOTIFY_FIELD_LENGTH, nbar.timefmt,
  354              &ntime);
  355         strftime(notify.date, NOTIFY_FIELD_LENGTH, nbar.datefmt,
  356              &ntime);
  357         pthread_mutex_unlock(&nbar.mutex);
  358         pthread_mutex_unlock(&notify.mutex);
  359         notify_update_bar();
  360         psleep(thread_sleep);
  361         elapse += thread_sleep;
  362         if (elapse >= check_app) {
  363             elapse = 0;
  364             pthread_mutex_lock(&notify_app.mutex);
  365             got_app = notify_app.got_app;
  366             pthread_mutex_unlock(&notify_app.mutex);
  367             if (!got_app)
  368                 notify_check_next_app(0);
  369         }
  370     }
  371 
  372     pthread_cleanup_pop(0);
  373     pthread_exit(NULL);
  374 }
  375 
  376 /* Fill the given structure with information about next appointment. */
  377 unsigned notify_get_next(struct notify_app *a)
  378 {
  379     time_t current_time;
  380 
  381     if (!a)
  382         return 0;
  383 
  384     current_time = time(NULL);
  385 
  386     a->time = current_time + DAYINSEC;
  387     a->got_app = 0;
  388     a->state = 0;
  389     a->txt = NULL;
  390     recur_apoint_check_next(a, current_time, get_today());
  391     apoint_check_next(a, current_time);
  392 
  393     return 1;
  394 }
  395 
  396 /*
  397  * This is used for the daemon to check if we have an upcoming appointment or
  398  * not.
  399  */
  400 unsigned notify_get_next_bkgd(void)
  401 {
  402     struct notify_app a;
  403 
  404     a.txt = NULL;
  405     if (!notify_get_next(&a))
  406         return 0;
  407 
  408     if (!a.got_app) {
  409         /* No next appointment, reset the previous notified one. */
  410         notify_app.got_app = 0;
  411         return 1;
  412     } else {
  413         if (!notify_same_item(a.time))
  414             notify_update_app(a.time, a.state, a.txt);
  415     }
  416 
  417     if (a.txt)
  418         mem_free(a.txt);
  419 
  420     return 1;
  421 }
  422 
  423 /* Return the description of next appointment to be notified. */
  424 char *notify_app_txt(void)
  425 {
  426     if (notify_app.got_app)
  427         return notify_app.txt;
  428     else
  429         return NULL;
  430 }
  431 
  432 /* Look for the next appointment within the next 24 hours. */
  433 /* ARGSUSED0 */
  434 static void *notify_thread_app(void *arg)
  435 {
  436     struct notify_app tmp_app;
  437     int force = (arg ? 1 : 0);
  438 
  439     if (!notify_get_next(&tmp_app))
  440         pthread_exit(NULL);
  441 
  442     if (!tmp_app.got_app) {
  443         pthread_mutex_lock(&notify_app.mutex);
  444         notify_free_app();
  445         pthread_mutex_unlock(&notify_app.mutex);
  446     } else {
  447         if (force || !notify_same_item(tmp_app.time)) {
  448             pthread_mutex_lock(&notify_app.mutex);
  449             notify_update_app(tmp_app.time, tmp_app.state,
  450                       tmp_app.txt);
  451             pthread_mutex_unlock(&notify_app.mutex);
  452         }
  453     }
  454 
  455     if (tmp_app.txt)
  456         mem_free(tmp_app.txt);
  457     notify_update_bar();
  458 
  459     pthread_exit(NULL);
  460 }
  461 
  462 /* Launch the thread notify_thread_app to look for next appointment. */
  463 void notify_check_next_app(int force)
  464 {
  465     pthread_t notify_t_app;
  466     void *arg = (force ? (void *)1 : NULL);
  467 
  468     pthread_create(&notify_t_app, &detached_thread_attr,
  469                notify_thread_app, arg);
  470     return;
  471 }
  472 
  473 /* Check if the newly created appointment is to be notified. */
  474 void notify_check_added(char *mesg, time_t start, char state)
  475 {
  476     time_t current_time;
  477     int update_notify = 0;
  478     long gap;
  479 
  480     current_time = time(NULL);
  481     pthread_mutex_lock(&notify_app.mutex);
  482     if (!notify_app.got_app) {
  483         gap = start - current_time;
  484         if (gap >= 0 && gap <= DAYINSEC)
  485             update_notify = 1;
  486     } else if (start < notify_app.time && start >= current_time) {
  487         update_notify = 1;
  488     } else if (start == notify_app.time && state != notify_app.state) {
  489         update_notify = 1;
  490     }
  491 
  492     if (update_notify) {
  493         notify_update_app(start, state, mesg);
  494     }
  495     pthread_mutex_unlock(&notify_app.mutex);
  496     notify_update_bar();
  497 }
  498 
  499 /* Check if the newly repeated appointment is to be notified. */
  500 void notify_check_repeated(struct recur_apoint *i)
  501 {
  502     time_t current_time, real_app_time;
  503     int update_notify = 0;
  504 
  505     current_time = time(NULL);
  506     pthread_mutex_lock(&notify_app.mutex);
  507     if (recur_item_find_occurrence
  508         (i->start, i->dur, &i->exc, i->rpt->type, i->rpt->freq,
  509          i->rpt->until, get_today(), &real_app_time)) {
  510         if (!notify_app.got_app) {
  511             if (real_app_time - current_time <= DAYINSEC)
  512                 update_notify = 1;
  513         } else if (real_app_time < notify_app.time
  514                && real_app_time >= current_time) {
  515             update_notify = 1;
  516         } else if (real_app_time == notify_app.time
  517                && i->state != notify_app.state) {
  518             update_notify = 1;
  519         }
  520     }
  521     if (update_notify) {
  522         notify_update_app(real_app_time, i->state, i->mesg);
  523     }
  524     pthread_mutex_unlock(&notify_app.mutex);
  525     notify_update_bar();
  526 }
  527 
  528 int notify_same_item(time_t time)
  529 {
  530     int same = 0;
  531 
  532     pthread_mutex_lock(&(notify_app.mutex));
  533     if (notify_app.got_app && notify_app.time == time)
  534         same = 1;
  535     pthread_mutex_unlock(&(notify_app.mutex));
  536 
  537     return same;
  538 }
  539 
  540 int notify_same_recur_item(struct recur_apoint *i)
  541 {
  542     int same = 0;
  543     time_t item_start = 0;
  544 
  545     recur_item_find_occurrence(i->start, i->dur, &i->exc, i->rpt->type,
  546                    i->rpt->freq, i->rpt->until,
  547                    get_today(), &item_start);
  548     pthread_mutex_lock(&notify_app.mutex);
  549     if (notify_app.got_app && item_start == notify_app.time)
  550         same = 1;
  551     pthread_mutex_unlock(&(notify_app.mutex));
  552 
  553     return same;
  554 }
  555 
  556 /* Launch the notify-bar main thread. */
  557 void notify_start_main_thread(void)
  558 {
  559         /* Avoid starting the notification bar thread twice. */
  560     notify_stop_main_thread();
  561 
  562     pthread_create(&notify_t_main, NULL, notify_main_thread, NULL);
  563     notify_check_next_app(0);
  564 }
  565 
  566 /*
  567  * Print an option in the configuration menu.
  568  * Specific treatment is needed depending on if the option is of type boolean
  569  * (either YES or NO), or an option holding a string value.
  570  */
  571 static void
  572 print_option(WINDOW * win, unsigned x, unsigned y, char *name,
  573          char *valstr, unsigned valbool, char *desc)
  574 {
  575     const int MAXCOL = col - 3;
  576     int x_opt, len;
  577 
  578     x_opt = x + strlen(name);
  579     mvwprintw(win, y, x, "%s", name);
  580     erase_window_part(win, x_opt, y, MAXCOL, y);
  581     if ((len = strlen(valstr)) != 0) {
  582         unsigned maxlen = MAX(MAXCOL - x_opt - 2, 3);
  583         char buf[maxlen * UTF8_MAXLEN];
  584 
  585         strncpy(buf, valstr, maxlen * UTF8_MAXLEN);
  586         buf[maxlen * UTF8_MAXLEN - 1] = '\0';
  587         utf8_chop(buf, maxlen);
  588 
  589         custom_apply_attr(win, ATTR_HIGHEST);
  590         mvwaddstr(win, y, x_opt, buf);
  591         custom_remove_attr(win, ATTR_HIGHEST);
  592     } else {
  593         print_bool_option_incolor(win, valbool, y, x_opt);
  594     }
  595     mvwaddstr(win, y + 1, x, desc);
  596 }
  597 
  598 /* Print options related to the notify-bar. */
  599 static void print_config_option(int i, WINDOW *win, int y, int hilt, void *cb_data)
  600 {
  601     enum { SHOW, DATE, CLOCK, WARN, CMD, NOTIFYALL, DMON, DMON_LOG,
  602             NB_OPT };
  603 
  604     struct opt_s {
  605         char *name;
  606         char *desc;
  607         char valstr[BUFSIZ];
  608         unsigned valnum;
  609     } opt[NB_OPT];
  610 
  611     opt[SHOW].name = "appearance.notifybar = ";
  612     opt[SHOW].desc =
  613         _("(if set to YES, notify-bar will be displayed)");
  614 
  615     opt[DATE].name = "format.notifydate = ";
  616     opt[DATE].desc =
  617         _("(Format of the date to be displayed inside notify-bar)");
  618 
  619     opt[CLOCK].name = "format.notifytime = ";
  620     opt[CLOCK].desc =
  621         _("(Format of the time to be displayed inside notify-bar)");
  622 
  623     opt[WARN].name = "notification.warning = ";
  624     opt[WARN].desc = _("(Warn user if an appointment is within next "
  625                "'notify-bar_warning' seconds)");
  626 
  627     opt[CMD].name = "notification.command = ";
  628     opt[CMD].desc =
  629         _("(Command used to notify user of an upcoming appointment)");
  630 
  631     opt[NOTIFYALL].name = "notification.notifyall = ";
  632     opt[NOTIFYALL].desc =
  633         _("(Notify all appointments instead of flagged ones only)");
  634 
  635     opt[DMON].name = "daemon.enable = ";
  636     opt[DMON].desc =
  637         _("(Run in background to get notifications after exiting)");
  638 
  639     opt[DMON_LOG].name = "daemon.log = ";
  640     opt[DMON_LOG].desc =
  641         _("(Log activity when running in background)");
  642 
  643     pthread_mutex_lock(&nbar.mutex);
  644 
  645     /* String value options */
  646     strncpy(opt[DATE].valstr, nbar.datefmt, BUFSIZ);
  647     strncpy(opt[CLOCK].valstr, nbar.timefmt, BUFSIZ);
  648     snprintf(opt[WARN].valstr, BUFSIZ, "%d", nbar.cntdwn);
  649     strncpy(opt[CMD].valstr, nbar.cmd, BUFSIZ);
  650 
  651     /* Boolean options */
  652     opt[SHOW].valnum = nbar.show;
  653     pthread_mutex_unlock(&nbar.mutex);
  654 
  655     opt[DMON].valnum = dmon.enable;
  656     opt[DMON_LOG].valnum = dmon.log;
  657 
  658     opt[SHOW].valstr[0] = opt[DMON].valstr[0] =
  659         opt[DMON_LOG].valstr[0] = '\0';
  660 
  661     opt[NOTIFYALL].valnum = nbar.notify_all;
  662     if (opt[NOTIFYALL].valnum == NOTIFY_FLAGGED_ONLY)
  663         strcpy(opt[NOTIFYALL].valstr, "flagged-only");
  664     else if (opt[NOTIFYALL].valnum == NOTIFY_UNFLAGGED_ONLY)
  665         strcpy(opt[NOTIFYALL].valstr, "unflagged-only");
  666     else if (opt[NOTIFYALL].valnum == NOTIFY_ALL)
  667         strcpy(opt[NOTIFYALL].valstr, "all");
  668 
  669     if (hilt)
  670         custom_apply_attr(win, ATTR_HIGHEST);
  671 
  672     print_option(win, 1, y, opt[i].name, opt[i].valstr,
  673              opt[i].valnum, opt[i].desc);
  674 
  675     if (hilt)
  676         custom_remove_attr(win, ATTR_HIGHEST);
  677 }
  678 
  679 static enum listbox_row_type config_option_row_type(int i, void *cb_data)
  680 {
  681     return LISTBOX_ROW_TEXT;
  682 }
  683 
  684 static int config_option_height(int i, void *cb_data)
  685 {
  686     return 3;
  687 }
  688 
  689 static void config_option_edit(int i)
  690 {
  691     char *buf;
  692     const char *date_str =
  693         _("Enter the date format (see 'man 3 strftime' for possible formats) ");
  694     const char *time_str =
  695         _("Enter the time format (see 'man 3 strftime' for possible formats) ");
  696     const char *count_str =
  697         _("Enter the number of seconds (0 not to be warned before an appointment)");
  698     const char *cmd_str = _("Enter the notification command ");
  699 
  700     buf = mem_malloc(BUFSIZ);
  701     buf[0] = '\0';
  702 
  703     switch (i) {
  704     case 0:
  705         pthread_mutex_lock(&nbar.mutex);
  706         nbar.show = !nbar.show;
  707         pthread_mutex_unlock(&nbar.mutex);
  708         if (notify_bar())
  709             notify_start_main_thread();
  710         else
  711             notify_stop_main_thread();
  712         resize = 1;
  713         break;
  714     case 1:
  715         status_mesg(date_str, "");
  716         pthread_mutex_lock(&nbar.mutex);
  717         strncpy(buf, nbar.datefmt, BUFSIZ);
  718         buf[BUFSIZ - 1] = '\0';
  719         pthread_mutex_unlock(&nbar.mutex);
  720         if (updatestring(win[STA].p, &buf, 0, 1) == 0) {
  721             pthread_mutex_lock(&nbar.mutex);
  722             strncpy(nbar.datefmt, buf, BUFSIZ);
  723             nbar.datefmt[BUFSIZ - 1] = '\0';
  724             pthread_mutex_unlock(&nbar.mutex);
  725         }
  726         break;
  727     case 2:
  728         status_mesg(time_str, "");
  729         pthread_mutex_lock(&nbar.mutex);
  730         strncpy(buf, nbar.timefmt, BUFSIZ);
  731         buf[BUFSIZ - 1] = '\0';
  732         pthread_mutex_unlock(&nbar.mutex);
  733         if (updatestring(win[STA].p, &buf, 0, 1) == 0) {
  734             pthread_mutex_lock(&nbar.mutex);
  735             strncpy(nbar.timefmt, buf, BUFSIZ);
  736             nbar.timefmt[BUFSIZ - 1] = '\0';
  737             pthread_mutex_unlock(&nbar.mutex);
  738         }
  739         break;
  740     case 3:
  741         status_mesg(count_str, "");
  742         pthread_mutex_lock(&nbar.mutex);
  743         snprintf(buf, BUFSIZ, "%d", nbar.cntdwn);
  744         pthread_mutex_unlock(&nbar.mutex);
  745         if (updatestring(win[STA].p, &buf, 0, 1) == 0 &&
  746             is_all_digit(buf) && atoi(buf) >= 0
  747             && atoi(buf) <= DAYINSEC) {
  748             pthread_mutex_lock(&nbar.mutex);
  749             nbar.cntdwn = atoi(buf);
  750             pthread_mutex_unlock(&nbar.mutex);
  751         }
  752         break;
  753     case 4:
  754         status_mesg(cmd_str, "");
  755         pthread_mutex_lock(&nbar.mutex);
  756         strncpy(buf, nbar.cmd, BUFSIZ);
  757         buf[BUFSIZ - 1] = '\0';
  758         pthread_mutex_unlock(&nbar.mutex);
  759         if (updatestring(win[STA].p, &buf, 0, 1) == 0) {
  760             pthread_mutex_lock(&nbar.mutex);
  761             strncpy(nbar.cmd, buf, BUFSIZ);
  762             nbar.cmd[BUFSIZ - 1] = '\0';
  763             pthread_mutex_unlock(&nbar.mutex);
  764         }
  765         break;
  766     case 5:
  767         pthread_mutex_lock(&nbar.mutex);
  768         nbar.notify_all = (nbar.notify_all + 1) % 3;
  769         pthread_mutex_unlock(&nbar.mutex);
  770         notify_check_next_app(1);
  771         break;
  772     case 6:
  773         dmon.enable = !dmon.enable;
  774         break;
  775     case 7:
  776         dmon.log = !dmon.log;
  777         break;
  778     }
  779 
  780     mem_free(buf);
  781 }
  782 
  783 /* Notify-bar configuration. */
  784 void notify_config_bar(void)
  785 {
  786     static int bindings[] = {
  787         KEY_GENERIC_QUIT, KEY_MOVE_UP, KEY_MOVE_DOWN, KEY_EDIT_ITEM
  788     };
  789     struct listbox lb;
  790     int key;
  791 
  792     clear();
  793     listbox_init(&lb, 0, 0, notify_bar() ? row - 3 : row - 2, col,
  794              _("notification options"), config_option_row_type,
  795              config_option_height, print_config_option);
  796     listbox_load_items(&lb, 8);
  797     listbox_draw_deco(&lb, 0);
  798     listbox_display(&lb, NOHILT);
  799     wins_set_bindings(bindings, ARRAY_SIZE(bindings));
  800     wins_status_bar();
  801     wnoutrefresh(win[STA].p);
  802     wmove(win[STA].p, 0, 0);
  803     wins_doupdate();
  804 
  805     while ((key = keys_get(win[KEY].p, NULL, NULL)) != KEY_GENERIC_QUIT) {
  806         switch (key) {
  807         case KEY_MOVE_DOWN:
  808             listbox_sel_move(&lb, 1);
  809             break;
  810         case KEY_MOVE_UP:
  811             listbox_sel_move(&lb, -1);
  812             break;
  813         case KEY_EDIT_ITEM:
  814             config_option_edit(listbox_get_sel(&lb));
  815             break;
  816         }
  817 
  818         if (resize) {
  819             resize = 0;
  820             wins_get_config();
  821             wins_reset_noupdate();
  822             listbox_resize(&lb, 0, 0, notify_bar() ? row - 3 : row - 2, col);
  823             listbox_draw_deco(&lb, 0);
  824             delwin(win[STA].p);
  825             win[STA].p =
  826                 newwin(win[STA].h, win[STA].w, win[STA].y,
  827                    win[STA].x);
  828             keypad(win[STA].p, TRUE);
  829             if (notify_bar()) {
  830                 notify_reinit_bar();
  831                 notify_update_bar();
  832             }
  833             clearok(curscr, TRUE);
  834         }
  835 
  836         listbox_display(&lb, NOHILT);
  837         wins_status_bar();
  838         wnoutrefresh(win[STA].p);
  839         wmove(win[STA].p, 0, 0);
  840         wins_doupdate();
  841     }
  842 
  843     listbox_delete(&lb);
  844 }