From 655218b7df40b3e159119933a4fa18efca2ff940 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Thu, 15 May 2014 14:37:49 +0200 Subject: ui-todo: Large-scale refactoring This is a complete overhaul of the TODO list user interface. The new implementation uses the generic list box panel. Signed-off-by: Lukas Fleischer --- src/calcurse.c | 52 ++++------- src/calcurse.h | 34 +++---- src/io.c | 4 - src/todo.c | 4 +- src/ui-todo.c | 274 ++++++++++++++++++++++++--------------------------------- src/wins.c | 20 ++--- 6 files changed, 155 insertions(+), 233 deletions(-) diff --git a/src/calcurse.c b/src/calcurse.c index 98075d9..3bab0af 100644 --- a/src/calcurse.c +++ b/src/calcurse.c @@ -70,10 +70,6 @@ static inline void key_generic_change_view(void) /* Select the event to highlight. */ switch (wins_slctd()) { - case TOD: - if ((ui_todo_hilt() == 0) && (ui_todo_nb() > 0)) - ui_todo_hilt_set(1); - break; case APP: if ((ui_day_hilt() == 0) && ((inday.nb_events + inday.nb_apoints) > 0)) @@ -113,9 +109,8 @@ static inline void key_view_item(void) { if ((wins_slctd() == APP) && (ui_day_hilt() != 0)) day_popup_item(day_get_item(ui_day_hilt())); - else if ((wins_slctd() == TOD) && (ui_todo_hilt() != 0)) - item_in_popup(NULL, NULL, ui_todo_saved_mesg(), - _("TODO:")); + else if (wins_slctd() == TOD) + ui_todo_popup_item(); wins_update(FLAG_ALL); } @@ -137,8 +132,6 @@ static inline void key_generic_add_appt(void) static inline void key_generic_add_todo(void) { ui_todo_add(); - if (ui_todo_hilt() == 0 && ui_todo_nb() == 1) - ui_todo_hilt_increase(1); wins_update(FLAG_TOD | FLAG_STA); } @@ -152,8 +145,6 @@ static inline void key_add_item(void) break; case TOD: ui_todo_add(); - if (ui_todo_hilt() == 0 && ui_todo_nb() == 1) - ui_todo_hilt_increase(1); wins_update(FLAG_TOD | FLAG_STA); break; default: @@ -167,7 +158,7 @@ static inline void key_edit_item(void) ui_day_item_edit(); inday = do_storage(0); wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); - } else if (wins_slctd() == TOD && ui_todo_hilt() != 0) { + } else if (wins_slctd() == TOD) { ui_todo_edit(); wins_update(FLAG_TOD | FLAG_STA); } @@ -180,7 +171,7 @@ static inline void key_del_item(void) reg); inday = do_storage(0); wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); - } else if (wins_slctd() == TOD && ui_todo_hilt() != 0) { + } else if (wins_slctd() == TOD) { ui_todo_delete(); wins_update(FLAG_TOD | FLAG_STA); } @@ -219,8 +210,8 @@ static inline void key_flag_item(void) day_item_switch_notify(day_get_item(ui_day_hilt())); inday = do_storage(0); wins_update(FLAG_APP); - } else if (wins_slctd() == TOD && ui_todo_hilt() != 0) { - todo_flag(todo_get_item(ui_todo_hilt())); + } else if (wins_slctd() == TOD) { + ui_todo_flag(); wins_update(FLAG_TOD); } } @@ -229,19 +220,15 @@ static inline void key_pipe_item(void) { if (wins_slctd() == APP && ui_day_hilt() != 0) ui_day_item_pipe(); - else if (wins_slctd() == TOD && ui_todo_hilt() != 0) + else if (wins_slctd() == TOD) ui_todo_pipe(); wins_update(FLAG_ALL); } static inline void change_priority(int diff) { - if (wins_slctd() == TOD && ui_todo_hilt() != 0) { - ui_todo_chg_priority(todo_get_item(ui_todo_hilt()), diff); - if (ui_todo_hilt_pos() < 0) - ui_todo_set_first(ui_todo_hilt()); - else if (ui_todo_hilt_pos() >= win[TOD].h - 4) - ui_todo_set_first(ui_todo_hilt() - win[TOD].h + 5); + if (wins_slctd() == TOD) { + ui_todo_chg_priority(diff); wins_update(FLAG_TOD); } } @@ -261,8 +248,8 @@ static inline void key_edit_note(void) if (wins_slctd() == APP && ui_day_hilt() != 0) { day_edit_note(day_get_item(ui_day_hilt()), conf.editor); inday = do_storage(0); - } else if (wins_slctd() == TOD && ui_todo_hilt() != 0) { - todo_edit_note(todo_get_item(ui_todo_hilt()), conf.editor); + } else if (wins_slctd() == TOD) { + ui_todo_edit_note(); } wins_update(FLAG_ALL); } @@ -271,8 +258,8 @@ static inline void key_view_note(void) { if (wins_slctd() == APP && ui_day_hilt() != 0) day_view_note(day_get_item(ui_day_hilt()), conf.pager); - else if (wins_slctd() == TOD && ui_todo_hilt() != 0) - todo_view_note(todo_get_item(ui_todo_hilt()), conf.pager); + else if (wins_slctd() == TOD) + ui_todo_view_note(); wins_update(FLAG_ALL); } @@ -365,11 +352,7 @@ static inline void key_move_up(void) ui_day_scroll_pad_up(inday.nb_events); wins_update(FLAG_APP); } else if (wins_slctd() == TOD) { - if (count >= ui_todo_hilt()) - count = ui_todo_hilt() - 1; - ui_todo_hilt_decrease(count); - if (ui_todo_hilt_pos() < 0) - ui_todo_first_increase(ui_todo_hilt_pos()); + ui_todo_sel_move(-1); wins_update(FLAG_TOD); } } @@ -395,12 +378,7 @@ static inline void key_move_down(void) ui_day_scroll_pad_down(inday.nb_events, win[APP].h); wins_update(FLAG_APP); } else if (wins_slctd() == TOD) { - if (count > ui_todo_nb() - ui_todo_hilt()) - count = ui_todo_nb() - ui_todo_hilt(); - ui_todo_hilt_increase(count); - if (ui_todo_hilt_pos() >= win[TOD].h - 4) - ui_todo_first_increase(ui_todo_hilt_pos() - - win[TOD].h + 5); + ui_todo_sel_move(1); wins_update(FLAG_TOD); } } diff --git a/src/calcurse.h b/src/calcurse.h index aedd2f3..d123269 100644 --- a/src/calcurse.h +++ b/src/calcurse.h @@ -758,10 +758,6 @@ void ui_day_item_repeat(void); void ui_day_item_cut_free(unsigned); void ui_day_item_copy(unsigned *, unsigned *, unsigned); void ui_day_item_paste(unsigned *, unsigned *, unsigned); -void ui_todo_add(void); -void ui_todo_delete(void); -void ui_todo_edit(void); -void ui_todo_pipe(void); /* io.c */ unsigned io_fprintln(const char *, const char *, ...); @@ -950,31 +946,34 @@ void sigs_unignore(void); /* todo.c */ extern llist_t todolist; struct todo *todo_get_item(int); -void ui_todo_hilt_set(int); -void ui_todo_hilt_decrease(int); -void ui_todo_hilt_increase(int); -int ui_todo_hilt(void); -int ui_todo_nb(void); -void ui_todo_set_nb(int); -void ui_todo_set_first(int); -void ui_todo_first_increase(int); -void ui_todo_first_decrease(int); -int ui_todo_hilt_pos(void); -char *ui_todo_saved_mesg(void); struct todo *todo_add(char *, int, char *); void todo_write(struct todo *, FILE *); void todo_delete_note(struct todo *); void todo_delete(struct todo *); void todo_flag(struct todo *); int todo_get_position(struct todo *); -void ui_todo_chg_priority(struct todo *, int); -void ui_todo_update_panel(int); void todo_edit_note(struct todo *, const char *); void todo_view_note(struct todo *, const char *); void todo_free(struct todo *); void todo_init_list(void); void todo_free_list(void); +/* ui-todo.c */ +void ui_todo_add(void); +void ui_todo_delete(void); +void ui_todo_edit(void); +void ui_todo_pipe(void); +void ui_todo_draw(int, WINDOW *, int, int, void *); +int ui_todo_height(int, void *); +void ui_todo_load_items(void); +void ui_todo_sel_move(int); +void ui_todo_update_panel(int); +void ui_todo_chg_priority(int); +void ui_todo_popup_item(void); +void ui_todo_flag(void); +void ui_todo_view_note(void); +void ui_todo_edit_note(void); + /* utf8.c */ int utf8_width(char *); int utf8_strwidth(char *); @@ -1057,6 +1056,7 @@ void vars_init(void); /* wins.c */ extern struct window win[NBWINS]; +extern struct listbox lb_todo; unsigned wins_nbar_lock(void); void wins_nbar_unlock(void); void wins_nbar_cleanup(void *); diff --git a/src/io.c b/src/io.c index b998fc4..4ff31e3 100644 --- a/src/io.c +++ b/src/io.c @@ -676,7 +676,6 @@ void io_load_todo(void) ++nb_tod; } file_close(data_file, __FILE_POS__); - ui_todo_set_nb(nb_tod); } static void @@ -1089,9 +1088,6 @@ void io_import_data(enum import_type type, const char *stream_name) stats.todos); snprintf(stats_str[3], BUFSIZ, _("%d skipped"), stats.skipped); - /* Update the number of todo items. */ - ui_todo_set_nb(ui_todo_nb() + stats.todos); - if (ui_mode == UI_CURSES && conf.system_dialogs) { char read[BUFSIZ], stat[BUFSIZ]; diff --git a/src/todo.c b/src/todo.c index 50ee447..16f1e20 100644 --- a/src/todo.c +++ b/src/todo.c @@ -45,7 +45,7 @@ llist_t todolist; /* Returns a structure containing the selected item. */ struct todo *todo_get_item(int item_number) { - return LLIST_GET_DATA(LLIST_NTH(&todolist, item_number - 1)); + return LLIST_GET_DATA(LLIST_NTH(&todolist, item_number)); } static int todo_cmp_id(struct todo *a, struct todo *b) @@ -131,9 +131,9 @@ int todo_get_position(struct todo *needle) int n = 0; LLIST_FOREACH(&todolist, i) { - n++; if (LLIST_TS_GET_DATA(i) == needle) return n; + n++; } EXIT(_("todo not found")); diff --git a/src/ui-todo.c b/src/ui-todo.c index 60838af..930b595 100644 --- a/src/ui-todo.c +++ b/src/ui-todo.c @@ -36,11 +36,6 @@ #include "calcurse.h" -static int hilt = 0; -static int todos = 0; -static int first = 1; -static char *msgsav; - /* Request user to enter a new todo item. */ void ui_todo_add(void) { @@ -58,7 +53,8 @@ void ui_todo_add(void) ch = wgetch(win[KEY].p); } todo_add(todo_input, ch - '0', NULL); - ui_todo_set_nb(ui_todo_nb() + 1); + ui_todo_sel_move(1); + ui_todo_load_items(); } } @@ -74,34 +70,28 @@ void ui_todo_delete(void) const int nb_erase_choice = 2; int answer; - if ((ui_todo_nb() <= 0) || + if (!LLIST_FIRST(&todolist) || (conf.confirm_delete && (status_ask_bool(del_todo_str) != 1))) { wins_erase_status_bar(); return; } - /* This todo item doesn't have any note associated. */ - if (todo_get_item(ui_todo_hilt())->note == NULL) - answer = 1; + struct todo *item = todo_get_item(listbox_get_sel(&lb_todo)); + + if (item->note) + answer = status_ask_choice(erase_warning, erase_choice, + nb_erase_choice); else - answer = - status_ask_choice(erase_warning, erase_choice, - nb_erase_choice); + answer = 1; switch (answer) { case 1: - todo_delete(todo_get_item(ui_todo_hilt())); - ui_todo_set_nb(ui_todo_nb() - 1); - if (ui_todo_hilt() > 1) - ui_todo_hilt_decrease(1); - if (ui_todo_nb() == 0) - ui_todo_hilt_set(0); - if (ui_todo_hilt_pos() < 0) - ui_todo_first_decrease(1); + todo_delete(item); + ui_todo_load_items(); break; case 2: - todo_delete_note(todo_get_item(ui_todo_hilt())); + todo_delete_note(item); break; default: wins_erase_status_bar(); @@ -112,12 +102,14 @@ void ui_todo_delete(void) /* Edit the description of an already existing todo item. */ void ui_todo_edit(void) { - struct todo *i; + if (!LLIST_FIRST(&todolist)) + return; + + struct todo *item = todo_get_item(listbox_get_sel(&lb_todo)); const char *mesg = _("Enter the new TODO description:"); status_mesg(mesg, ""); - i = todo_get_item(ui_todo_hilt()); - updatestring(win[STA].p, &i->mesg, 0, 1); + updatestring(win[STA].p, &item->mesg, 0, 1); } /* Pipe a todo item to an external program. */ @@ -128,7 +120,11 @@ void ui_todo_pipe(void) int pout; int pid; FILE *fpout; - struct todo *todo; + + if (!LLIST_FIRST(&todolist)) + return; + + struct todo *item = todo_get_item(listbox_get_sel(&lb_todo)); status_mesg(_("Pipe item to external command:"), ""); if (getstring(win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID) @@ -137,10 +133,7 @@ void ui_todo_pipe(void) wins_prepare_external(); if ((pid = shell_exec(NULL, &pout, *arg, arg))) { fpout = fdopen(pout, "w"); - - todo = todo_get_item(ui_todo_hilt()); - todo_write(todo, fpout); - + todo_write(item, fpout); fclose(fpout); child_wait(NULL, &pout, pid); press_any_key(); @@ -148,171 +141,130 @@ void ui_todo_pipe(void) wins_unprepare_external(); } -/* Sets which todo is highlighted. */ -void ui_todo_hilt_set(int highlighted) +/* Display todo items in the corresponding panel. */ +void ui_todo_draw(int n, WINDOW *win, int y, int hilt, void *cb_data) { - hilt = highlighted; -} + llist_item_t *i = *((llist_item_t **)cb_data); + struct todo *todo = LLIST_TS_GET_DATA(i); + int mark[2]; + int width = lb_todo.sw.w; + char buf[width * UTF8_MAXLEN]; + int j; -void ui_todo_hilt_decrease(int n) -{ - hilt -= n; -} + mark[0] = todo->id > 0 ? '0' + todo->id : 'X'; + mark[1] = todo->note ? '>' : '.'; -void ui_todo_hilt_increase(int n) -{ - hilt += n; -} + if (hilt) + custom_apply_attr(win, ATTR_HIGHEST); -/* Return which todo is highlighted. */ -int ui_todo_hilt(void) -{ - return hilt; -} + if (utf8_strwidth(todo->mesg) < width) { + mvwprintw(win, y, 0, "%c%c %s", mark[0], mark[1], todo->mesg); + } else { + for (j = 0; todo->mesg[j] && width > 0; j++) { + if (!UTF8_ISCONT(todo->mesg[j])) + width -= utf8_width(&todo->mesg[j]); + buf[j] = todo->mesg[j]; + } + if (j) + buf[j - 1] = 0; + else + buf[0] = 0; + mvwprintw(win, y, 0, "%c%c %s...", mark[0], mark[1], buf); + } -/* Set the number of todos. */ -void ui_todo_set_nb(int nb) -{ - todos = nb; -} + if (hilt) + custom_remove_attr(win, ATTR_HIGHEST); -/* Set which one is the first todo to be displayed. */ -void ui_todo_set_first(int nb) -{ - first = nb; + *((llist_item_t **)cb_data) = i->next; } -void ui_todo_first_increase(int n) +int ui_todo_height(int n, void *cb_data) { - first += n; + return 1; } -void ui_todo_first_decrease(int n) +void ui_todo_load_items(void) { - first -= n; + int n = 0; + llist_item_t *i; + + /* TODO: Optimize this by keeping the list size in a variable. */ + LLIST_FOREACH(&todolist, i) + n++; + + listbox_load_items(&lb_todo, n); } -/* - * Return the position of the hilghlighted item, relative to the first one - * displayed. - */ -int ui_todo_hilt_pos(void) +void ui_todo_sel_move(int delta) { - return hilt - first; + listbox_sel_move(&lb_todo, delta); } -/* Return the number of todos. */ -int ui_todo_nb(void) +/* Updates the TODO panel. */ +void ui_todo_update_panel(int which_pan) { - return todos; + /* + * This is used and modified by display_todo_item() to avoid quadratic + * running time. + */ + llist_item_t *p = LLIST_FIRST(&todolist); + + listbox_set_cb_data(&lb_todo, &p); + listbox_display(&lb_todo); } -/* Return the last visited todo. */ -char *ui_todo_saved_mesg(void) +/* Change an item priority by pressing '+' or '-' inside TODO panel. */ +void ui_todo_chg_priority(int diff) { - return msgsav; + if (!LLIST_FIRST(&todolist)) + return; + + struct todo *item = todo_get_item(listbox_get_sel(&lb_todo)); + int id = item->id + diff; + struct todo *item_new; + + if (id < 1) + id = 1; + else if (id > 9) + id = 9; + + item_new = todo_add(item->mesg, id, item->note); + todo_delete(item); + listbox_set_sel(&lb_todo, todo_get_position(item_new)); } -/* Display todo items in the corresponding panel. */ -static void -display_todo_item(int incolor, char *msg, int prio, int note, int width, - int y, int x) +void ui_todo_popup_item(void) { - WINDOW *w; - int ch_note; - char buf[width * UTF8_MAXLEN], priostr[2]; - int i; - - w = win[TOD].p; - ch_note = (note) ? '>' : '.'; - if (prio > 0) - snprintf(priostr, sizeof priostr, "%d", prio); - else - strncpy(priostr, "X", sizeof priostr); + if (!LLIST_FIRST(&todolist)) + return; - if (incolor == 0) - custom_apply_attr(w, ATTR_HIGHEST); - if (utf8_strwidth(msg) < width) { - mvwprintw(w, y, x, "%s%c %s", priostr, ch_note, msg); - } else { - 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) - custom_remove_attr(w, ATTR_HIGHEST); + struct todo *item = todo_get_item(listbox_get_sel(&lb_todo)); + item_in_popup(NULL, NULL, item->mesg, _("TODO:")); } -/* Updates the TODO panel. */ -void ui_todo_update_panel(int which_pan) +void ui_todo_flag(void) { - llist_item_t *i; - int len = win[TOD].w - 8; - int num_todo = 0; - int title_lines = conf.compact_panels ? 1 : 3; - int y_offset = title_lines, x_offset = 1; - int t_realpos = -1; - int todo_lines = 1; - int max_items = win[TOD].h - 4; - int incolor = -1; - - if ((int)win[TOD].h < 4) + if (!LLIST_FIRST(&todolist)) return; - /* Print todo item in the panel. */ - erase_window_part(win[TOD].p, 1, title_lines, win[TOD].w - 2, - win[TOD].h - 2); - LLIST_FOREACH(&todolist, i) { - struct todo *todo = LLIST_TS_GET_DATA(i); - num_todo++; - t_realpos = num_todo - first; - incolor = (which_pan == TOD) ? num_todo - hilt : num_todo; - if (incolor == 0) - msgsav = todo->mesg; - if (t_realpos >= 0 && t_realpos < max_items) { - display_todo_item(incolor, todo->mesg, todo->id, - (todo->note != NULL) ? 1 : 0, - len, y_offset, x_offset); - y_offset = y_offset + todo_lines; - } - } + struct todo *item = todo_get_item(listbox_get_sel(&lb_todo)); + todo_flag(item); +} - /* Draw the scrollbar if necessary. */ - if (todos > max_items) { - int sbar_length = max_items * (max_items + 1) / todos; - int highend = max_items * first / todos; - unsigned hilt_bar = (which_pan == TOD) ? 1 : 0; - int sbar_top = highend + title_lines; - - if ((sbar_top + sbar_length) > win[TOD].h - 1) - sbar_length = win[TOD].h - 1 - sbar_top; - draw_scrollbar(win[TOD].p, sbar_top, win[TOD].w - 2, - sbar_length, title_lines, win[TOD].h - 1, - hilt_bar); - } +void ui_todo_view_note(void) +{ + if (!LLIST_FIRST(&todolist)) + return; - wnoutrefresh(win[TOD].p); + struct todo *item = todo_get_item(listbox_get_sel(&lb_todo)); + todo_view_note(item, conf.pager); } -/* Change an item priority by pressing '+' or '-' inside TODO panel. */ -void ui_todo_chg_priority(struct todo *todo, int diff) +void ui_todo_edit_note(void) { - int id = todo->id + diff; - struct todo *todo_new; - - if (id < 1) - id = 1; - else if (id > 9) - id = 9; + if (!LLIST_FIRST(&todolist)) + return; - todo_new = todo_add(todo->mesg, id, todo->note); - todo_delete(todo); - hilt = todo_get_position(todo_new); + struct todo *item = todo_get_item(listbox_get_sel(&lb_todo)); + todo_edit_note(item, conf.editor); } diff --git a/src/wins.c b/src/wins.c index 09302ee..30d6529 100644 --- a/src/wins.c +++ b/src/wins.c @@ -50,6 +50,7 @@ /* Variables to handle calcurse windows. */ struct window win[NBWINS]; +struct listbox lb_todo; /* User-configurable side bar width. */ static unsigned sbarwidth_perc; @@ -252,14 +253,13 @@ static void wins_init_panels(void) apad.width = win[APP].w - 3; apad.ptrwin = newpad(apad.length, apad.width); - win[TOD].p = - newwin(win[TOD].h, win[TOD].w, win[TOD].y, win[TOD].x); - wins_show(win[TOD].p, _("TODO")); + listbox_init(&lb_todo, win[TOD].y, win[TOD].x, win[TOD].h, win[TOD].w, + _("TODO"), ui_todo_height, ui_todo_draw); + ui_todo_load_items(); /* Enable function keys (i.e. arrow keys) in those windows */ keypad(win[CAL].p, TRUE); keypad(win[APP].p, TRUE); - keypad(win[TOD].p, TRUE); } /* Create all the windows. */ @@ -403,7 +403,7 @@ void wins_reinit_panels(void) delwin(win[CAL].p); delwin(win[APP].p); delwin(apad.ptrwin); - delwin(win[TOD].p); + listbox_delete(&lb_todo); wins_get_config(); wins_init_panels(); } @@ -417,7 +417,7 @@ void wins_reinit(void) delwin(win[CAL].p); delwin(win[APP].p); delwin(apad.ptrwin); - delwin(win[TOD].p); + listbox_delete(&lb_todo); delwin(win[STA].p); delwin(win[KEY].p); wins_get_config(); @@ -556,12 +556,8 @@ void wins_update_border(int flags) else border_nocolor(win[APP].p); } - if (flags & FLAG_TOD) { - if (slctd_win == TOD) - border_color(win[TOD].p); - else - border_nocolor(win[TOD].p); - } + if (flags & FLAG_TOD) + listbox_draw_deco(&lb_todo, (slctd_win == TOD)); } void wins_update_panels(int flags) -- cgit v1.2.3-54-g00ecf