Convert our code base to adhere to Linux kernel coding style using Lindent, with the following exceptions: * Use spaces, instead of tabs, for indentation. * Use 2-character indentations (instead of 8 characters). Rationale: We currently have too much levels of indentation. Using 8-character tabs would make huge code parts unreadable. These need to be cleaned up before we can switch to 8 characters. Signed-off-by: Lukas Fleischer <calcurse@cryptocrack.de>
1119 lines
30 KiB
C
1119 lines
30 KiB
C
/*
|
|
* Calcurse - text-based organizer
|
|
*
|
|
* Copyright (c) 2004-2012 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
|
|
#include "calcurse.h"
|
|
|
|
struct day_saved_item {
|
|
char start[BUFSIZ];
|
|
char end[BUFSIZ];
|
|
char state;
|
|
char type;
|
|
char *mesg;
|
|
};
|
|
|
|
static llist_t day_items;
|
|
static struct day_saved_item day_saved_item;
|
|
|
|
static void day_free(struct day_item *day)
|
|
{
|
|
mem_free(day);
|
|
}
|
|
|
|
static void day_init_list(void)
|
|
{
|
|
LLIST_INIT(&day_items);
|
|
}
|
|
|
|
/*
|
|
* Free the current day linked list containing the events and appointments.
|
|
* Must not free associated message and note, because their are not dynamically
|
|
* allocated (only pointers to real objects are stored in this structure).
|
|
*/
|
|
void day_free_list(void)
|
|
{
|
|
LLIST_FREE_INNER(&day_items, day_free);
|
|
LLIST_FREE(&day_items);
|
|
}
|
|
|
|
/* Add an event in the current day list */
|
|
static struct day_item *day_add_event(int type, char *mesg, char *note,
|
|
long nday, int id)
|
|
{
|
|
struct day_item *day;
|
|
|
|
day = mem_malloc(sizeof(struct day_item));
|
|
day->mesg = mesg;
|
|
day->note = note;
|
|
day->type = type;
|
|
day->appt_dur = 0;
|
|
day->appt_pos = 0;
|
|
day->start = nday;
|
|
day->evnt_id = id;
|
|
|
|
LLIST_ADD(&day_items, day);
|
|
|
|
return day;
|
|
}
|
|
|
|
static int day_cmp_start(struct day_item *a, struct day_item *b)
|
|
{
|
|
if (a->type <= EVNT) {
|
|
if (b->type <= EVNT)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
} else if (b->type <= EVNT)
|
|
return 1;
|
|
else
|
|
return a->start < b->start ? -1 : (a->start == b->start ? 0 : 1);
|
|
}
|
|
|
|
/* Add an appointment in the current day list. */
|
|
static struct day_item *day_add_apoint(int type, char *mesg, char *note,
|
|
long start, long dur, char state,
|
|
int real_pos)
|
|
{
|
|
struct day_item *day;
|
|
|
|
day = mem_malloc(sizeof(struct day_item));
|
|
day->mesg = mesg;
|
|
day->note = note;
|
|
day->start = start;
|
|
day->appt_dur = dur;
|
|
day->appt_pos = real_pos;
|
|
day->state = state;
|
|
day->type = type;
|
|
day->evnt_id = 0;
|
|
|
|
LLIST_ADD_SORTED(&day_items, day, day_cmp_start);
|
|
|
|
return day;
|
|
}
|
|
|
|
/*
|
|
* Store the events for the selected day in structure pointed
|
|
* by day_items. This is done by copying the events
|
|
* from the general structure pointed by eventlist to the structure
|
|
* dedicated to the selected day.
|
|
* Returns the number of events for the selected day.
|
|
*/
|
|
static int day_store_events(long date)
|
|
{
|
|
llist_item_t *i;
|
|
int e_nb = 0;
|
|
|
|
LLIST_FIND_FOREACH_CONT(&eventlist, date, event_inday, i) {
|
|
struct event *ev = LLIST_TS_GET_DATA(i);
|
|
day_add_event(EVNT, ev->mesg, ev->note, ev->day, ev->id);
|
|
e_nb++;
|
|
}
|
|
|
|
return e_nb;
|
|
}
|
|
|
|
/*
|
|
* Store the recurrent events for the selected day in structure pointed
|
|
* by day_items. This is done by copying the recurrent events
|
|
* from the general structure pointed by recur_elist to the structure
|
|
* dedicated to the selected day.
|
|
* Returns the number of recurrent events for the selected day.
|
|
*/
|
|
static int day_store_recur_events(long date)
|
|
{
|
|
llist_item_t *i;
|
|
int e_nb = 0;
|
|
|
|
LLIST_FIND_FOREACH(&recur_elist, date, recur_event_inday, i) {
|
|
struct recur_event *rev = LLIST_TS_GET_DATA(i);
|
|
day_add_event(RECUR_EVNT, rev->mesg, rev->note, rev->day, rev->id);
|
|
e_nb++;
|
|
}
|
|
|
|
return e_nb;
|
|
}
|
|
|
|
/*
|
|
* Store the apoints for the selected day in structure pointed
|
|
* by day_items. This is done by copying the appointments
|
|
* from the general structure pointed by alist_p to the
|
|
* structure dedicated to the selected day.
|
|
* Returns the number of appointments for the selected day.
|
|
*/
|
|
static int day_store_apoints(long date)
|
|
{
|
|
llist_item_t *i;
|
|
int a_nb = 0;
|
|
|
|
LLIST_TS_LOCK(&alist_p);
|
|
LLIST_TS_FIND_FOREACH(&alist_p, date, apoint_inday, i) {
|
|
struct apoint *apt = LLIST_TS_GET_DATA(i);
|
|
|
|
if (apt->start >= date + DAYINSEC)
|
|
break;
|
|
|
|
day_add_apoint(APPT, apt->mesg, apt->note, apt->start, apt->dur,
|
|
apt->state, 0);
|
|
a_nb++;
|
|
}
|
|
LLIST_TS_UNLOCK(&alist_p);
|
|
|
|
return a_nb;
|
|
}
|
|
|
|
/*
|
|
* Store the recurrent apoints for the selected day in structure pointed
|
|
* by day_items. This is done by copying the appointments
|
|
* from the general structure pointed by recur_alist_p to the
|
|
* structure dedicated to the selected day.
|
|
* Returns the number of recurrent appointments for the selected day.
|
|
*/
|
|
static int day_store_recur_apoints(long date)
|
|
{
|
|
llist_item_t *i;
|
|
int a_nb = 0;
|
|
|
|
LLIST_TS_LOCK(&recur_alist_p);
|
|
LLIST_TS_FIND_FOREACH(&recur_alist_p, date, recur_apoint_inday, i) {
|
|
struct recur_apoint *rapt = LLIST_TS_GET_DATA(i);
|
|
unsigned real_start;
|
|
if (recur_apoint_find_occurrence(rapt, date, &real_start)) {
|
|
day_add_apoint(RECUR_APPT, rapt->mesg, rapt->note, real_start,
|
|
rapt->dur, rapt->state, a_nb);
|
|
a_nb++;
|
|
}
|
|
}
|
|
LLIST_TS_UNLOCK(&recur_alist_p);
|
|
|
|
return a_nb;
|
|
}
|
|
|
|
/*
|
|
* Store all of the items to be displayed for the selected day.
|
|
* Items are of four types: recursive events, normal events,
|
|
* recursive appointments and normal appointments.
|
|
* The items are stored in the linked list pointed by day_items
|
|
* and the length of the new pad to write is returned.
|
|
* The number of events and appointments in the current day are also updated.
|
|
*/
|
|
static int
|
|
day_store_items(long date, unsigned *pnb_events, unsigned *pnb_apoints)
|
|
{
|
|
int pad_length;
|
|
int nb_events, nb_recur_events;
|
|
int nb_apoints, nb_recur_apoints;
|
|
|
|
day_free_list();
|
|
day_init_list();
|
|
nb_recur_events = day_store_recur_events(date);
|
|
nb_events = day_store_events(date);
|
|
*pnb_events = nb_events;
|
|
nb_recur_apoints = day_store_recur_apoints(date);
|
|
nb_apoints = day_store_apoints(date);
|
|
*pnb_apoints = nb_apoints;
|
|
pad_length = (nb_recur_events + nb_events + 1 +
|
|
3 * (nb_recur_apoints + nb_apoints));
|
|
*pnb_apoints += nb_recur_apoints;
|
|
*pnb_events += nb_recur_events;
|
|
|
|
return pad_length;
|
|
}
|
|
|
|
/*
|
|
* Store the events and appointments for the selected day, and write
|
|
* those items in a pad. If selected day is null, then store items for current
|
|
* day. This is useful to speed up the appointment panel update.
|
|
*/
|
|
struct day_items_nb *day_process_storage(struct date *slctd_date,
|
|
unsigned day_changed,
|
|
struct day_items_nb *inday)
|
|
{
|
|
long date;
|
|
struct date day;
|
|
|
|
if (slctd_date)
|
|
day = *slctd_date;
|
|
else
|
|
calendar_store_current_date(&day);
|
|
|
|
date = date2sec(day, 0, 0);
|
|
|
|
/* Inits */
|
|
if (apad.length != 0)
|
|
delwin(apad.ptrwin);
|
|
|
|
/* Store the events and appointments (recursive and normal items). */
|
|
apad.length = day_store_items(date, &inday->nb_events, &inday->nb_apoints);
|
|
|
|
/* Create the new pad with its new length. */
|
|
if (day_changed)
|
|
apad.first_onscreen = 0;
|
|
apad.ptrwin = newpad(apad.length, apad.width);
|
|
|
|
return inday;
|
|
}
|
|
|
|
/*
|
|
* Returns a structure of type apoint_llist_node_t given a structure of type
|
|
* day_item_s
|
|
*/
|
|
static void day_item_s2apoint_s(struct apoint *a, struct day_item *p)
|
|
{
|
|
a->state = p->state;
|
|
a->start = p->start;
|
|
a->dur = p->appt_dur;
|
|
a->mesg = p->mesg;
|
|
}
|
|
|
|
/*
|
|
* Print an item date in the appointment panel.
|
|
*/
|
|
static void
|
|
display_item_date(int incolor, struct apoint *i, int type, long date,
|
|
int y, int x)
|
|
{
|
|
WINDOW *win;
|
|
char a_st[100], a_end[100];
|
|
|
|
win = apad.ptrwin;
|
|
apoint_sec2str(i, date, a_st, a_end);
|
|
if (incolor == 0)
|
|
custom_apply_attr(win, ATTR_HIGHEST);
|
|
if (type == RECUR_EVNT || type == RECUR_APPT)
|
|
if (i->state & APOINT_NOTIFY)
|
|
mvwprintw(win, y, x, " *!%s -> %s", a_st, a_end);
|
|
else
|
|
mvwprintw(win, y, x, " * %s -> %s", a_st, a_end);
|
|
else if (i->state & APOINT_NOTIFY)
|
|
mvwprintw(win, y, x, " -!%s -> %s", a_st, a_end);
|
|
else
|
|
mvwprintw(win, y, x, " - %s -> %s", a_st, a_end);
|
|
if (incolor == 0)
|
|
custom_remove_attr(win, ATTR_HIGHEST);
|
|
}
|
|
|
|
/*
|
|
* Print an item description in the corresponding panel window.
|
|
*/
|
|
static void
|
|
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[width * UTF8_MAXLEN];
|
|
int i;
|
|
|
|
if (width <= 0)
|
|
return;
|
|
|
|
win = apad.ptrwin;
|
|
ch_recur = (recur) ? '*' : ' ';
|
|
ch_note = (note) ? '>' : ' ';
|
|
if (incolor == 0)
|
|
custom_apply_attr(win, ATTR_HIGHEST);
|
|
if (utf8_strwidth(msg) < width)
|
|
mvwprintw(win, y, x, " %c%c%s", ch_recur, 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(win, y, x, " %c%c%s...", ch_recur, ch_note, buf);
|
|
}
|
|
if (incolor == 0)
|
|
custom_remove_attr(win, ATTR_HIGHEST);
|
|
}
|
|
|
|
/*
|
|
* Write the appointments and events for the selected day in a pad.
|
|
* An horizontal line is drawn between events and appointments, and the
|
|
* item selected by user is highlighted. This item is also saved inside
|
|
* structure (pointed by day_saved_item), to be later displayed in a
|
|
* popup window if requested.
|
|
*/
|
|
void day_write_pad(long date, int width, int length, int incolor)
|
|
{
|
|
llist_item_t *i;
|
|
struct apoint a;
|
|
int line, item_number, recur;
|
|
const int x_pos = 0;
|
|
unsigned draw_line = 0;
|
|
|
|
line = item_number = 0;
|
|
|
|
LLIST_FOREACH(&day_items, i) {
|
|
struct day_item *day = LLIST_TS_GET_DATA(i);
|
|
if (day->type == RECUR_EVNT || day->type == RECUR_APPT)
|
|
recur = 1;
|
|
else
|
|
recur = 0;
|
|
/* First print the events for current day. */
|
|
if (day->type < RECUR_APPT) {
|
|
item_number++;
|
|
if (item_number - incolor == 0) {
|
|
day_saved_item.type = day->type;
|
|
day_saved_item.mesg = day->mesg;
|
|
}
|
|
display_item(item_number - incolor, day->mesg, recur,
|
|
(day->note != NULL) ? 1 : 0, width - 7, line, x_pos);
|
|
line++;
|
|
draw_line = 1;
|
|
} else {
|
|
/* Draw a line between events and appointments. */
|
|
if (line > 0 && draw_line) {
|
|
wmove(apad.ptrwin, line, 0);
|
|
whline(apad.ptrwin, 0, width);
|
|
draw_line = 0;
|
|
}
|
|
/* Last print the appointments for current day. */
|
|
item_number++;
|
|
day_item_s2apoint_s(&a, day);
|
|
if (item_number - incolor == 0) {
|
|
day_saved_item.type = day->type;
|
|
day_saved_item.mesg = day->mesg;
|
|
apoint_sec2str(&a, date, day_saved_item.start, day_saved_item.end);
|
|
}
|
|
display_item_date(item_number - incolor, &a, day->type,
|
|
date, line + 1, x_pos);
|
|
display_item(item_number - incolor, day->mesg, 0,
|
|
(day->note != NULL) ? 1 : 0, width - 7, line + 2, x_pos);
|
|
line += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Display an item inside a popup window. */
|
|
void day_popup_item(void)
|
|
{
|
|
if (day_saved_item.type == EVNT || day_saved_item.type == RECUR_EVNT)
|
|
item_in_popup(NULL, NULL, day_saved_item.mesg, _("Event :"));
|
|
else if (day_saved_item.type == APPT || day_saved_item.type == RECUR_APPT)
|
|
item_in_popup(day_saved_item.start, day_saved_item.end,
|
|
day_saved_item.mesg, _("Appointment :"));
|
|
else
|
|
EXIT(_("unknown item type"));
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
/*
|
|
* Need to know if there is an item for the current selected day inside
|
|
* calendar. This is used to put the correct colors inside calendar panel.
|
|
*/
|
|
int day_check_if_item(struct date day)
|
|
{
|
|
const long date = date2sec(day, 0, 0);
|
|
|
|
if (LLIST_FIND_FIRST(&recur_elist, date, recur_event_inday))
|
|
return 1;
|
|
|
|
LLIST_TS_LOCK(&recur_alist_p);
|
|
if (LLIST_TS_FIND_FIRST(&recur_alist_p, date, recur_apoint_inday)) {
|
|
LLIST_TS_UNLOCK(&recur_alist_p);
|
|
return 1;
|
|
}
|
|
LLIST_TS_UNLOCK(&recur_alist_p);
|
|
|
|
if (LLIST_FIND_FIRST(&eventlist, date, event_inday))
|
|
return 1;
|
|
|
|
LLIST_TS_LOCK(&alist_p);
|
|
if (LLIST_TS_FIND_FIRST(&alist_p, date, apoint_inday)) {
|
|
LLIST_TS_UNLOCK(&alist_p);
|
|
return 1;
|
|
}
|
|
LLIST_TS_UNLOCK(&alist_p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned fill_slices(int *slices, int slicesno, int first, int last)
|
|
{
|
|
int i;
|
|
|
|
if (first < 0 || last < first)
|
|
return 0;
|
|
|
|
if (last >= slicesno)
|
|
last = slicesno - 1; /* Appointment spanning more than one day. */
|
|
|
|
for (i = first; i <= last; i++)
|
|
slices[i] = 1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Fill in the 'slices' vector given as an argument with 1 if there is an
|
|
* appointment in the corresponding time slice, 0 otherwise.
|
|
* A 24 hours day is divided into 'slicesno' number of time slices.
|
|
*/
|
|
unsigned day_chk_busy_slices(struct date day, int slicesno, int *slices)
|
|
{
|
|
llist_item_t *i;
|
|
int slicelen;
|
|
const long date = date2sec(day, 0, 0);
|
|
|
|
slicelen = DAYINSEC / slicesno;
|
|
|
|
#define SLICENUM(tsec) ((tsec) / slicelen % slicesno)
|
|
|
|
LLIST_TS_LOCK(&recur_alist_p);
|
|
LLIST_TS_FIND_FOREACH(&recur_alist_p, date, recur_apoint_inday, i) {
|
|
struct apoint *rapt = LLIST_TS_GET_DATA(i);
|
|
long start = get_item_time(rapt->start);
|
|
long end = get_item_time(rapt->start + rapt->dur);
|
|
|
|
if (!fill_slices(slices, slicesno, SLICENUM(start), SLICENUM(end))) {
|
|
LLIST_TS_UNLOCK(&recur_alist_p);
|
|
return 0;
|
|
}
|
|
}
|
|
LLIST_TS_UNLOCK(&recur_alist_p);
|
|
|
|
LLIST_TS_LOCK(&alist_p);
|
|
LLIST_TS_FIND_FOREACH(&alist_p, date, apoint_inday, i) {
|
|
struct apoint *apt = LLIST_TS_GET_DATA(i);
|
|
long start = get_item_time(apt->start);
|
|
long end = get_item_time(apt->start + apt->dur);
|
|
|
|
if (apt->start >= date + DAYINSEC)
|
|
break;
|
|
|
|
if (!fill_slices(slices, slicesno, SLICENUM(start), SLICENUM(end))) {
|
|
LLIST_TS_UNLOCK(&alist_p);
|
|
return 0;
|
|
}
|
|
}
|
|
LLIST_TS_UNLOCK(&alist_p);
|
|
|
|
#undef SLICENUM
|
|
return 1;
|
|
}
|
|
|
|
/* Request the user to enter a new time. */
|
|
static int day_edit_time(int time, unsigned *new_hour, unsigned *new_minute)
|
|
{
|
|
char *timestr = date_sec2date_str(time, "%H:%M");
|
|
const char *msg_time = _("Enter the new time ([hh:mm]) : ");
|
|
const char *enter_str = _("Press [Enter] to continue");
|
|
const char *fmt_msg = _("You entered an invalid time, should be [hh:mm]");
|
|
|
|
for (;;) {
|
|
status_mesg(msg_time, "");
|
|
if (updatestring(win[STA].p, ×tr, 0, 1) == GETSTRING_VALID) {
|
|
if (parse_time(timestr, new_hour, new_minute) == 1) {
|
|
mem_free(timestr);
|
|
return 1;
|
|
} else {
|
|
status_mesg(fmt_msg, enter_str);
|
|
wgetch(win[STA].p);
|
|
}
|
|
} else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Request the user to enter a new time or duration. */
|
|
static int day_edit_duration(int start, int dur, unsigned *new_duration)
|
|
{
|
|
char *timestr = date_sec2date_str(start + dur, "%H:%M");
|
|
const char *msg_time =
|
|
_
|
|
("Enter new end time ([hh:mm]) or duration ([+hh:mm], [+xxxdxxhxxm] or [+mm]) : ");
|
|
const char *enter_str = _("Press [Enter] to continue");
|
|
const char *fmt_msg = _("You entered an invalid time, should be [hh:mm]");
|
|
long newtime;
|
|
unsigned hr, mn;
|
|
|
|
for (;;) {
|
|
status_mesg(msg_time, "");
|
|
if (updatestring(win[STA].p, ×tr, 0, 1) == GETSTRING_VALID) {
|
|
if (*timestr == '+' && parse_duration(timestr + 1, new_duration) == 1) {
|
|
*new_duration *= MININSEC;
|
|
break;
|
|
} else if (parse_time(timestr, &hr, &mn) == 1) {
|
|
newtime = update_time_in_date(start + dur, hr, mn);
|
|
*new_duration = (newtime > start) ? newtime - start :
|
|
DAYINSEC + newtime - start;
|
|
break;
|
|
} else {
|
|
status_mesg(fmt_msg, enter_str);
|
|
wgetch(win[STA].p);
|
|
}
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
mem_free(timestr);
|
|
return 1;
|
|
}
|
|
|
|
/* Request the user to enter a new end time or duration. */
|
|
static void update_start_time(long *start, long *dur)
|
|
{
|
|
long newtime;
|
|
unsigned hr, mn;
|
|
int valid_date;
|
|
const char *msg_wrong_time =
|
|
_("Invalid time: start time must be before end time!");
|
|
const char *msg_enter = _("Press [Enter] to continue");
|
|
|
|
do {
|
|
day_edit_time(*start, &hr, &mn);
|
|
newtime = update_time_in_date(*start, hr, mn);
|
|
if (newtime < *start + *dur) {
|
|
*dur -= (newtime - *start);
|
|
*start = newtime;
|
|
valid_date = 1;
|
|
} else {
|
|
status_mesg(msg_wrong_time, msg_enter);
|
|
wgetch(win[STA].p);
|
|
valid_date = 0;
|
|
}
|
|
}
|
|
while (valid_date == 0);
|
|
}
|
|
|
|
static void update_duration(long *start, long *dur)
|
|
{
|
|
unsigned newdur;
|
|
|
|
day_edit_duration(*start, *dur, &newdur);
|
|
*dur = newdur;
|
|
}
|
|
|
|
static void update_desc(char **desc)
|
|
{
|
|
status_mesg(_("Enter the new item description:"), "");
|
|
updatestring(win[STA].p, desc, 0, 1);
|
|
}
|
|
|
|
static void update_rept(struct rpt **rpt, const long start)
|
|
{
|
|
int newtype, newfreq, date_entered;
|
|
long newuntil;
|
|
char outstr[BUFSIZ];
|
|
char *freqstr, *timstr;
|
|
const char *msg_rpt_prefix = _("Enter the new repetition type:");
|
|
const char *msg_rpt_daily = _("(d)aily");
|
|
const char *msg_rpt_weekly = _("(w)eekly");
|
|
const char *msg_rpt_monthly = _("(m)onthly");
|
|
const char *msg_rpt_yearly = _("(y)early");
|
|
|
|
/* Find the current repetition type. */
|
|
const char *rpt_current;
|
|
char msg_rpt_current[BUFSIZ];
|
|
switch (recur_def2char((*rpt)->type)) {
|
|
case 'D':
|
|
rpt_current = msg_rpt_daily;
|
|
break;
|
|
case 'W':
|
|
rpt_current = msg_rpt_weekly;
|
|
break;
|
|
case 'M':
|
|
rpt_current = msg_rpt_monthly;
|
|
break;
|
|
case 'Y':
|
|
rpt_current = msg_rpt_yearly;
|
|
break;
|
|
default:
|
|
/* NOTREACHED, but makes the compiler happier. */
|
|
rpt_current = msg_rpt_daily;
|
|
}
|
|
|
|
snprintf(msg_rpt_current, BUFSIZ, _("(currently using %s)"), rpt_current);
|
|
|
|
char msg_rpt_asktype[BUFSIZ];
|
|
snprintf(msg_rpt_asktype, BUFSIZ, "%s %s, %s, %s, %s ? %s",
|
|
msg_rpt_prefix,
|
|
msg_rpt_daily,
|
|
msg_rpt_weekly, msg_rpt_monthly, msg_rpt_yearly, msg_rpt_current);
|
|
|
|
const char *msg_rpt_choice = _("[dwmy]");
|
|
const char *msg_wrong_freq = _("The frequence you entered is not valid.");
|
|
const char *msg_wrong_time =
|
|
_("Invalid time: start time must be before end time!");
|
|
const char *msg_wrong_date = _("The entered date is not valid.");
|
|
const char *msg_fmts =
|
|
_("Possible formats are [%s] or '0' for an endless repetetition");
|
|
const char *msg_enter = _("Press [Enter] to continue");
|
|
|
|
switch (status_ask_choice(msg_rpt_asktype, msg_rpt_choice, 4)) {
|
|
case 1:
|
|
newtype = 'D';
|
|
break;
|
|
case 2:
|
|
newtype = 'W';
|
|
break;
|
|
case 3:
|
|
newtype = 'M';
|
|
break;
|
|
case 4:
|
|
newtype = 'Y';
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
do {
|
|
status_mesg(_("Enter the new repetition frequence:"), "");
|
|
freqstr = mem_malloc(BUFSIZ);
|
|
snprintf(freqstr, BUFSIZ, "%d", (*rpt)->freq);
|
|
if (updatestring(win[STA].p, &freqstr, 0, 1) == GETSTRING_VALID) {
|
|
newfreq = atoi(freqstr);
|
|
mem_free(freqstr);
|
|
if (newfreq == 0) {
|
|
status_mesg(msg_wrong_freq, msg_enter);
|
|
wgetch(win[STA].p);
|
|
}
|
|
} else {
|
|
mem_free(freqstr);
|
|
return;
|
|
}
|
|
}
|
|
while (newfreq == 0);
|
|
|
|
do {
|
|
snprintf(outstr, BUFSIZ, "Enter the new ending date: [%s] or '0'",
|
|
DATEFMT_DESC(conf.input_datefmt));
|
|
status_mesg(_(outstr), "");
|
|
timstr = date_sec2date_str((*rpt)->until, DATEFMT(conf.input_datefmt));
|
|
if (updatestring(win[STA].p, &timstr, 0, 1) != GETSTRING_VALID) {
|
|
mem_free(timstr);
|
|
return;
|
|
}
|
|
if (strcmp(timstr, "0") == 0) {
|
|
newuntil = 0;
|
|
date_entered = 1;
|
|
} else {
|
|
struct tm *lt;
|
|
time_t t;
|
|
struct date new_date;
|
|
int newmonth, newday, newyear;
|
|
|
|
if (parse_date(timstr, conf.input_datefmt, &newyear, &newmonth,
|
|
&newday, calendar_get_slctd_day())) {
|
|
t = start;
|
|
lt = localtime(&t);
|
|
new_date.dd = newday;
|
|
new_date.mm = newmonth;
|
|
new_date.yyyy = newyear;
|
|
newuntil = date2sec(new_date, lt->tm_hour, lt->tm_min);
|
|
if (newuntil < start) {
|
|
status_mesg(msg_wrong_time, msg_enter);
|
|
wgetch(win[STA].p);
|
|
date_entered = 0;
|
|
} else
|
|
date_entered = 1;
|
|
} else {
|
|
snprintf(outstr, BUFSIZ, msg_fmts, DATEFMT_DESC(conf.input_datefmt));
|
|
status_mesg(msg_wrong_date, _(outstr));
|
|
wgetch(win[STA].p);
|
|
date_entered = 0;
|
|
}
|
|
}
|
|
}
|
|
while (date_entered == 0);
|
|
|
|
mem_free(timstr);
|
|
(*rpt)->type = recur_char2def(newtype);
|
|
(*rpt)->freq = newfreq;
|
|
(*rpt)->until = newuntil;
|
|
}
|
|
|
|
/* Edit an already existing item. */
|
|
void day_edit_item(void)
|
|
{
|
|
struct day_item *p;
|
|
struct recur_event *re;
|
|
struct event *e;
|
|
struct recur_apoint *ra;
|
|
struct apoint *a;
|
|
long date;
|
|
int item_num;
|
|
int need_check_notify = 0;
|
|
|
|
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));
|
|
const char *choice_recur_evnt[2] = {
|
|
"Description",
|
|
"Repetition",
|
|
};
|
|
switch (status_ask_simplechoice(_("Edit: "), choice_recur_evnt, 2)) {
|
|
case 1:
|
|
update_desc(&re->mesg);
|
|
break;
|
|
case 2:
|
|
update_rept(&re->rpt, re->day);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
break;
|
|
case EVNT:
|
|
e = event_get(date, day_item_nb(date, item_num, EVNT));
|
|
update_desc(&e->mesg);
|
|
break;
|
|
case RECUR_APPT:
|
|
ra = recur_get_apoint(date, day_item_nb(date, item_num, RECUR_APPT));
|
|
const char *choice_recur_appt[4] = {
|
|
"Start time",
|
|
"End time",
|
|
"Description",
|
|
"Repetition",
|
|
};
|
|
switch (status_ask_simplechoice(_("Edit: "), choice_recur_appt, 4)) {
|
|
case 1:
|
|
need_check_notify = 1;
|
|
update_start_time(&ra->start, &ra->dur);
|
|
break;
|
|
case 2:
|
|
update_duration(&ra->start, &ra->dur);
|
|
break;
|
|
case 3:
|
|
if (notify_bar())
|
|
need_check_notify = notify_same_recur_item(ra);
|
|
update_desc(&ra->mesg);
|
|
break;
|
|
case 4:
|
|
need_check_notify = 1;
|
|
update_rept(&ra->rpt, ra->start);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
break;
|
|
case APPT:
|
|
a = apoint_get(date, day_item_nb(date, item_num, APPT));
|
|
const char *choice_appt[3] = {
|
|
"Start time",
|
|
"End time",
|
|
"Description",
|
|
};
|
|
switch (status_ask_simplechoice(_("Edit: "), choice_appt, 3)) {
|
|
case 1:
|
|
need_check_notify = 1;
|
|
update_start_time(&a->start, &a->dur);
|
|
break;
|
|
case 2:
|
|
update_duration(&a->start, &a->dur);
|
|
break;
|
|
case 3:
|
|
if (notify_bar())
|
|
need_check_notify = notify_same_item(a->start);
|
|
update_desc(&a->mesg);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (need_check_notify)
|
|
notify_check_next_app(1);
|
|
}
|
|
|
|
/*
|
|
* In order to erase an item, we need to count first the number of
|
|
* items for each type (in order: recurrent events, events,
|
|
* recurrent appointments and appointments) and then to test the
|
|
* type of the item to be deleted.
|
|
*/
|
|
int day_erase_item(long date, int item_number, enum eraseflg flag)
|
|
{
|
|
struct day_item *p;
|
|
|
|
const char *erase_warning =
|
|
_("This item is recurrent. "
|
|
"Delete (a)ll occurences or just this (o)ne ?");
|
|
const char *erase_choices = _("[ao]");
|
|
const int nb_erase_choices = 2;
|
|
|
|
const char *note_warning =
|
|
_("This item has a note attached to it. "
|
|
"Delete (i)tem or just its (n)ote ?");
|
|
const char *note_choices = _("[in]");
|
|
const int nb_note_choices = 2;
|
|
int ans;
|
|
unsigned delete_whole;
|
|
|
|
p = day_get_item(item_number);
|
|
if (flag == ERASE_DONT_FORCE) {
|
|
if (p->note == NULL)
|
|
ans = 1;
|
|
else
|
|
ans = status_ask_choice(note_warning, note_choices, nb_note_choices);
|
|
|
|
switch (ans) {
|
|
case 1:
|
|
flag = ERASE_FORCE;
|
|
break;
|
|
case 2:
|
|
flag = ERASE_FORCE_ONLY_NOTE;
|
|
break;
|
|
default: /* User escaped */
|
|
return 0;
|
|
}
|
|
}
|
|
if (p->type == EVNT) {
|
|
event_delete_bynum(date, day_item_nb(date, item_number, EVNT), flag);
|
|
} else if (p->type == APPT) {
|
|
apoint_delete_bynum(date, day_item_nb(date, item_number, APPT), flag);
|
|
} else {
|
|
if (flag == ERASE_FORCE_ONLY_NOTE)
|
|
ans = 1;
|
|
else
|
|
ans = status_ask_choice(erase_warning, erase_choices, nb_erase_choices);
|
|
|
|
switch (ans) {
|
|
case 1:
|
|
delete_whole = 1;
|
|
break;
|
|
case 2:
|
|
delete_whole = 0;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
if (p->type == RECUR_EVNT) {
|
|
recur_event_erase(date, day_item_nb(date, item_number, RECUR_EVNT),
|
|
delete_whole, flag);
|
|
} else {
|
|
recur_apoint_erase(date, p->appt_pos, delete_whole, flag);
|
|
}
|
|
}
|
|
if (flag == ERASE_FORCE_ONLY_NOTE)
|
|
return 0;
|
|
else
|
|
return p->type;
|
|
}
|
|
|
|
/* Cut an item so it can be pasted somewhere else later. */
|
|
int day_cut_item(long date, int item_number)
|
|
{
|
|
const int DELETE_WHOLE = 1;
|
|
struct day_item *p;
|
|
|
|
p = day_get_item(item_number);
|
|
switch (p->type) {
|
|
case EVNT:
|
|
event_delete_bynum(date, day_item_nb(date, item_number, EVNT), ERASE_CUT);
|
|
break;
|
|
case RECUR_EVNT:
|
|
recur_event_erase(date, day_item_nb(date, item_number, RECUR_EVNT),
|
|
DELETE_WHOLE, ERASE_CUT);
|
|
break;
|
|
case APPT:
|
|
apoint_delete_bynum(date, day_item_nb(date, item_number, APPT), ERASE_CUT);
|
|
break;
|
|
case RECUR_APPT:
|
|
recur_apoint_erase(date, p->appt_pos, DELETE_WHOLE, ERASE_CUT);
|
|
break;
|
|
default:
|
|
EXIT(_("unknwon type"));
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
return p->type;
|
|
}
|
|
|
|
/* Paste a previously cut item. */
|
|
int day_paste_item(long date, int cut_item_type)
|
|
{
|
|
int pasted_item_type;
|
|
|
|
pasted_item_type = cut_item_type;
|
|
switch (cut_item_type) {
|
|
case 0:
|
|
return 0;
|
|
case EVNT:
|
|
event_paste_item();
|
|
break;
|
|
case RECUR_EVNT:
|
|
recur_event_paste_item();
|
|
break;
|
|
case APPT:
|
|
apoint_paste_item();
|
|
break;
|
|
case RECUR_APPT:
|
|
recur_apoint_paste_item();
|
|
break;
|
|
default:
|
|
EXIT(_("unknwon type"));
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
return pasted_item_type;
|
|
}
|
|
|
|
/* Returns a structure containing the selected item. */
|
|
struct day_item *day_get_item(int item_number)
|
|
{
|
|
return LLIST_GET_DATA(LLIST_NTH(&day_items, item_number - 1));
|
|
}
|
|
|
|
/* Returns the real item number, given its type. */
|
|
int day_item_nb(long date, int day_num, int type)
|
|
{
|
|
int i, nb_item[MAX_TYPES];
|
|
llist_item_t *j;
|
|
|
|
for (i = 0; i < MAX_TYPES; i++)
|
|
nb_item[i] = 0;
|
|
|
|
j = LLIST_FIRST(&day_items);
|
|
for (i = 1; i < day_num; i++) {
|
|
struct day_item *day = LLIST_TS_GET_DATA(j);
|
|
nb_item[day->type - 1]++;
|
|
j = LLIST_TS_NEXT(j);
|
|
}
|
|
|
|
return nb_item[type - 1];
|
|
}
|
|
|
|
/* Attach a note to an appointment or event. */
|
|
void day_edit_note(const char *editor)
|
|
{
|
|
struct day_item *p;
|
|
struct recur_apoint *ra;
|
|
struct apoint *a;
|
|
struct recur_event *re;
|
|
struct event *e;
|
|
long date;
|
|
int item_num;
|
|
|
|
item_num = apoint_hilt();
|
|
p = day_get_item(item_num);
|
|
edit_note(&p->note, editor);
|
|
|
|
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));
|
|
re->note = p->note;
|
|
break;
|
|
case EVNT:
|
|
e = event_get(date, day_item_nb(date, item_num, EVNT));
|
|
e->note = p->note;
|
|
break;
|
|
case RECUR_APPT:
|
|
ra = recur_get_apoint(date, day_item_nb(date, item_num, RECUR_APPT));
|
|
ra->note = p->note;
|
|
break;
|
|
case APPT:
|
|
a = apoint_get(date, day_item_nb(date, item_num, APPT));
|
|
a->note = p->note;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* View a note previously attached to an appointment or event */
|
|
void day_view_note(const 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(void)
|
|
{
|
|
char cmd[BUFSIZ] = "";
|
|
char const *arg[] = { cmd, NULL };
|
|
int pout;
|
|
int pid;
|
|
FILE *fpout;
|
|
int item_num;
|
|
long date;
|
|
struct day_item *p;
|
|
struct recur_apoint *ra;
|
|
struct apoint *a;
|
|
struct recur_event *re;
|
|
struct event *e;
|
|
|
|
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");
|
|
|
|
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();
|
|
}
|