In the todo panel, all uncompleted items are currently prefixed with their priorities. Drop this prefix from items with priority 0, i.e. undefined priority. Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
361 lines
8.0 KiB
C
361 lines
8.0 KiB
C
/*
|
|
* Calcurse - text-based organizer
|
|
*
|
|
* Copyright (c) 2004-2015 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"
|
|
|
|
static unsigned ui_todo_view = 0;
|
|
|
|
static struct todo *ui_todo_selitem(void)
|
|
{
|
|
return todo_get_item(listbox_get_sel(&lb_todo),
|
|
ui_todo_view == TODO_HIDE_COMPLETED_VIEW);
|
|
}
|
|
|
|
/* Request user to enter a new todo item. */
|
|
void ui_todo_add(void)
|
|
{
|
|
int ch = 0;
|
|
const char *mesg = _("Enter the new TODO item:");
|
|
const char *mesg_id =
|
|
_("Enter the TODO priority [0 (none), 1 (highest) - 9 (lowest)]:");
|
|
char todo_input[BUFSIZ] = "";
|
|
|
|
status_mesg(mesg, "");
|
|
if (getstring(win[STA].p, todo_input, BUFSIZ, 0, 1) ==
|
|
GETSTRING_VALID) {
|
|
while ((ch < '0') || (ch > '9')) {
|
|
status_mesg(mesg_id, "");
|
|
ch = wgetch(win[KEY].p);
|
|
}
|
|
todo_add(todo_input, ch - '0', 0, NULL);
|
|
ui_todo_load_items();
|
|
io_set_modified();
|
|
}
|
|
}
|
|
|
|
/* Delete an item from the TODO list. */
|
|
void ui_todo_delete(void)
|
|
{
|
|
const char *del_todo_str =
|
|
_("Do you really want to delete this task?");
|
|
const char *erase_warning =
|
|
_("This item has a note attached to it. "
|
|
"Delete (t)odo or just its (n)ote?");
|
|
const char *erase_choice = _("[tn]");
|
|
const int nb_erase_choice = 2;
|
|
int answer;
|
|
|
|
if (!LLIST_FIRST(&todolist) ||
|
|
(conf.confirm_delete
|
|
&& (status_ask_bool(del_todo_str) != 1))) {
|
|
wins_erase_status_bar();
|
|
return;
|
|
}
|
|
|
|
struct todo *item = ui_todo_selitem();
|
|
|
|
if (item->note)
|
|
answer = status_ask_choice(erase_warning, erase_choice,
|
|
nb_erase_choice);
|
|
else
|
|
answer = 1;
|
|
|
|
switch (answer) {
|
|
case 1:
|
|
todo_delete(item);
|
|
ui_todo_load_items();
|
|
io_set_modified();
|
|
break;
|
|
case 2:
|
|
todo_delete_note(item);
|
|
io_set_modified();
|
|
break;
|
|
default:
|
|
wins_erase_status_bar();
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Edit the description of an already existing todo item. */
|
|
void ui_todo_edit(void)
|
|
{
|
|
if (!LLIST_FIRST(&todolist))
|
|
return;
|
|
|
|
struct todo *item = ui_todo_selitem();
|
|
const char *mesg = _("Enter the new TODO description:");
|
|
|
|
status_mesg(mesg, "");
|
|
updatestring(win[STA].p, &item->mesg, 0, 1);
|
|
io_set_modified();
|
|
}
|
|
|
|
/* Pipe a todo item to an external program. */
|
|
void ui_todo_pipe(void)
|
|
{
|
|
char cmd[BUFSIZ] = "";
|
|
char const *arg[] = { cmd, NULL };
|
|
int pout;
|
|
int pid;
|
|
FILE *fpout;
|
|
|
|
if (!LLIST_FIRST(&todolist))
|
|
return;
|
|
|
|
struct todo *item = ui_todo_selitem();
|
|
|
|
status_mesg(_("Pipe item to external command:"), "");
|
|
if (getstring(win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID)
|
|
return;
|
|
|
|
wins_prepare_external();
|
|
if ((pid = shell_exec(NULL, &pout, *arg, arg))) {
|
|
fpout = fdopen(pout, "w");
|
|
todo_write(item, fpout);
|
|
fclose(fpout);
|
|
child_wait(NULL, &pout, pid);
|
|
press_any_key();
|
|
}
|
|
wins_unprepare_external();
|
|
}
|
|
|
|
/* Display todo items in the corresponding panel. */
|
|
void ui_todo_draw(int n, WINDOW *win, int y, int hilt, void *cb_data)
|
|
{
|
|
llist_item_t *i = *((llist_item_t **)cb_data);
|
|
struct todo *todo = LLIST_TS_GET_DATA(i);
|
|
char mark[] = { 0, 0, 0, 0 };
|
|
int width = lb_todo.sw.w - 2;
|
|
char buf[width * UTF8_MAXLEN];
|
|
char *mesg;
|
|
int j;
|
|
|
|
if (ui_todo_view == TODO_HIDE_COMPLETED_VIEW) {
|
|
while (i && todo->completed) {
|
|
i = i->next;
|
|
if (i)
|
|
todo = LLIST_TS_GET_DATA(i);
|
|
}
|
|
}
|
|
|
|
mark[0] = todo->completed ? 'X' : (todo->id > 0 ? '0' + todo->id : 0);
|
|
if (todo->note) {
|
|
if (mark[0] == '\0') {
|
|
mark[0] = '>';
|
|
mark[1] = ' ';
|
|
} else {
|
|
mark[1] = '>';
|
|
mark[2] = ' ';
|
|
}
|
|
} else if (mark[0] != '\0') {
|
|
mark[1] = '.';
|
|
mark[2] = ' ';
|
|
}
|
|
width -= strlen(mark);
|
|
|
|
hilt = hilt && (wins_slctd() == TOD);
|
|
|
|
if (hilt)
|
|
custom_apply_attr(win, ATTR_HIGHEST);
|
|
|
|
if (utf8_strwidth(todo->mesg) < width) {
|
|
mesg = todo->mesg;
|
|
} else {
|
|
width -= 3;
|
|
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] = '.';
|
|
buf[j] = '.';
|
|
buf[j + 1] = '.';
|
|
buf[j + 2] = '\0';
|
|
} else {
|
|
buf[0] = 0;
|
|
}
|
|
mesg = buf;
|
|
}
|
|
|
|
mvwprintw(win, y, 0, "%s%s", mark, mesg);
|
|
|
|
if (hilt)
|
|
custom_remove_attr(win, ATTR_HIGHEST);
|
|
|
|
*((llist_item_t **)cb_data) = i->next;
|
|
}
|
|
|
|
enum listbox_row_type ui_todo_row_type(int i, void *cb_data)
|
|
{
|
|
return LISTBOX_ROW_TEXT;
|
|
}
|
|
|
|
int ui_todo_height(int n, void *cb_data)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
void ui_todo_load_items(void)
|
|
{
|
|
int n = 0;
|
|
llist_item_t *i;
|
|
|
|
/* TODO: Optimize this by keeping the list size in a variable. */
|
|
LLIST_FOREACH(&todolist, i) {
|
|
struct todo *todo = LLIST_TS_GET_DATA(i);
|
|
if (ui_todo_view == TODO_HIDE_COMPLETED_VIEW &&
|
|
todo->completed)
|
|
continue;
|
|
n++;
|
|
}
|
|
|
|
listbox_load_items(&lb_todo, n);
|
|
}
|
|
|
|
void ui_todo_sel_reset(void)
|
|
{
|
|
listbox_sel_move(&lb_todo, 0);
|
|
}
|
|
|
|
void ui_todo_sel_move(int delta)
|
|
{
|
|
listbox_sel_move(&lb_todo, delta);
|
|
}
|
|
|
|
/* Updates the TODO panel. */
|
|
void ui_todo_update_panel(int which_pan)
|
|
{
|
|
/*
|
|
* This is used and modified by ui_todo_draw() to avoid quadratic
|
|
* running time.
|
|
*/
|
|
llist_item_t *p = LLIST_FIRST(&todolist);
|
|
|
|
listbox_set_cb_data(&lb_todo, &p);
|
|
listbox_display(&lb_todo);
|
|
}
|
|
|
|
/* Change an item priority by pressing '+' or '-' inside TODO panel. */
|
|
void ui_todo_chg_priority(int diff)
|
|
{
|
|
if (!LLIST_FIRST(&todolist))
|
|
return;
|
|
|
|
struct todo *item = ui_todo_selitem();
|
|
int id = item->id + diff;
|
|
struct todo *item_new;
|
|
|
|
if (id < 0)
|
|
id = 0;
|
|
else if (id > 9)
|
|
id = 9;
|
|
|
|
item_new = todo_add(item->mesg, id, item->completed, item->note);
|
|
todo_delete(item);
|
|
io_set_modified();
|
|
listbox_set_sel(&lb_todo, todo_get_position(item_new));
|
|
}
|
|
|
|
void ui_todo_popup_item(void)
|
|
{
|
|
if (!LLIST_FIRST(&todolist))
|
|
return;
|
|
|
|
struct todo *item = ui_todo_selitem();
|
|
item_in_popup(NULL, NULL, item->mesg, _("TODO:"));
|
|
}
|
|
|
|
void ui_todo_flag(void)
|
|
{
|
|
if (!LLIST_FIRST(&todolist))
|
|
return;
|
|
|
|
struct todo *item = ui_todo_selitem();
|
|
todo_flag(item);
|
|
if (ui_todo_view == TODO_HIDE_COMPLETED_VIEW)
|
|
ui_todo_load_items();
|
|
io_set_modified();
|
|
}
|
|
|
|
void ui_todo_view_note(void)
|
|
{
|
|
if (!LLIST_FIRST(&todolist))
|
|
return;
|
|
|
|
struct todo *item = ui_todo_selitem();
|
|
todo_view_note(item, conf.pager);
|
|
}
|
|
|
|
void ui_todo_edit_note(void)
|
|
{
|
|
if (!LLIST_FIRST(&todolist))
|
|
return;
|
|
|
|
struct todo *item = ui_todo_selitem();
|
|
todo_edit_note(item, conf.editor);
|
|
io_set_modified();
|
|
}
|
|
|
|
/* Switch to next todo view. */
|
|
void ui_todo_view_next(void)
|
|
{
|
|
ui_todo_view++;
|
|
if (ui_todo_view == TODO_VIEWS)
|
|
ui_todo_view = 0;
|
|
ui_todo_load_items();
|
|
}
|
|
|
|
/* Switch to previous todo view. */
|
|
void ui_todo_view_prev(void)
|
|
{
|
|
if (ui_todo_view == 0)
|
|
ui_todo_view = TODO_VIEWS;
|
|
ui_todo_view--;
|
|
ui_todo_load_items();
|
|
}
|
|
|
|
void ui_todo_set_view(int view)
|
|
{
|
|
ui_todo_view = (view < 0 || view >= TODO_VIEWS) ?
|
|
TODO_SHOW_COMPLETED_VIEW : view;
|
|
}
|
|
|
|
int ui_todo_get_view(void)
|
|
{
|
|
return (int)ui_todo_view;
|
|
}
|