commit bd411e62764da49b932372ff1c6974dad087460e
parent a31899549bf17de899704727045913496adb42c5
Author: William Casarin <jb55@jb55.com>
Date: Wed, 10 Oct 2018 17:38:37 -0700
edit mode
Diffstat:
M | viscal.c | | | 272 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
1 file changed, 239 insertions(+), 33 deletions(-)
diff --git a/viscal.c b/viscal.c
@@ -1,6 +1,7 @@
#include <cairo/cairo.h>
#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
#include <libical/ical.h>
#include <assert.h>
#include <time.h>
@@ -11,6 +12,8 @@
#define length(array) (sizeof((array))/sizeof((array)[0]))
+#define clamp(val, low, high) (val < low ? low : (val > high ? high : val))
+
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
@@ -21,6 +24,8 @@
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
+#define EDITBUF_MAX 32768
+
// TODO: heap-realloc events array
#define MAX_EVENTS 1024
@@ -44,6 +49,7 @@ enum cal_flags {
CAL_MDOWN = 1 << 0
, CAL_DRAGGING = 1 << 1
, CAL_SPLIT = 1 << 2
+ , CAL_EDITING = 1 << 3
};
union rgba {
@@ -71,6 +77,10 @@ struct event {
time_t drag_time;
};
+// used for temporary storage when editing summaries, descriptions, etc
+static char g_editbuf[EDITBUF_MAX] = {0};
+static int g_editbuf_pos = 0;
+
struct cal {
GtkWidget *widget;
struct ical calendars[128];
@@ -150,13 +160,17 @@ calendar_create(struct cal *cal) {
cal->zoom = 2.0;
}
+static void warn(const char *msg) {
+ printf("WARN %s\n", msg);
+}
+
static void set_current_calendar(struct cal *cal, struct ical *ical)
{
for (int i = 0; i < cal->ncalendars; i++) {
if (&cal->calendars[i] == ical)
cal->selected_calendar_ind = i;
}
-}
+}
static struct ical *current_calendar(struct cal *cal) {
if (cal->ncalendars == 0)
@@ -266,6 +280,49 @@ static void select_closest_to_now(struct cal *cal)
}
+static void set_edit_buffer(const char *src)
+{
+ char *dst = g_editbuf;
+ int n = 0;
+ int c = EDITBUF_MAX;
+
+ while (c-- && (*dst++ = *src++))
+ n++;
+
+ g_editbuf_pos = n;
+}
+
+static struct event *get_selected_event(struct cal *cal)
+{
+ if (cal->nevents == 0 || cal->selected_event_ind == -1)
+ return NULL;
+ return &cal->events[cal->selected_event_ind];
+}
+
+
+static void edit_mode(struct cal *cal)
+{
+ // TODO: STATUS BAR for edit mode
+
+ struct event *event =
+ get_selected_event(cal);
+
+ // don't enter edit mode if we're not selecting any event
+ if (!event)
+ return;
+
+ cal->flags |= CAL_EDITING;
+
+ const char *summary =
+ icalcomponent_get_summary(event->vevent);
+
+ // TODO: what are we editing? for now assume summary
+ // copy current summary to edit buffer
+ set_edit_buffer(summary);
+}
+
+
+
static void
events_for_view(struct cal *cal, time_t start, time_t end)
{
@@ -302,6 +359,8 @@ events_for_view(struct cal *cal, time_t start, time_t end)
for (i = 0; i < cal->nevents; i++) {
if (cal->events[i].vevent == cal->select_after_sort) {
cal->selected_event_ind = i;
+ // HACK: we might not always want to do this...
+ edit_mode(cal);
break;
}
}
@@ -503,17 +562,14 @@ static char *format_locale_timet(char *buffer, int bsize, time_t time) {
static icalcomponent *
create_event(struct cal *cal, time_t start, time_t end, icalcomponent *ical) {
- static char c = 'a';
- static char buf[128] = "New Event ";
+ static const char *default_event_summary = "";
icalcomponent *vevent;
icaltimetype dtstart = icaltime_from_timet_with_zone(start, 0, NULL);
icaltimetype dtend = icaltime_from_timet_with_zone(end, 0, NULL);
vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
- buf[10] = c++;
- buf[11] = 0;
- icalcomponent_set_summary(vevent, buf);
+ icalcomponent_set_summary(vevent, default_event_summary);
icalcomponent_set_dtstart(vevent, dtstart);
icalcomponent_set_dtend(vevent, dtend);
icalcomponent_add_component(ical, vevent);
@@ -603,15 +659,6 @@ static void zoom(struct cal *cal, double amt)
cal->zoom_at = cal->my;
}
-static struct event *get_selected_event(struct cal *cal)
-{
- if (cal->nevents == 0 || cal->selected_event_ind == -1)
- return NULL;
- return &cal->events[cal->selected_event_ind];
-}
-
-#define clamp(val, low, high) (val < low ? low : (val > high ? high : val))
-
static inline int relative_selection(struct cal *cal, int rel)
{
/* if (cal->selected_event_ind == -1) { */
@@ -639,6 +686,7 @@ static void move_now(struct cal *cal)
closest_timeblock_for_timet(now, cal->timeblock_size);
}
+
static void insert_event(struct cal *cal)
{
// we should eventually always have a calendar
@@ -669,7 +717,7 @@ static int query_span(struct cal *cal, int index_hint, time_t start, time_t end,
continue;
vevent_span_timet(ev->vevent, &st, &et);
-
+
if ((min_start != 0 && st < min_start) ||
(max_end != 0 && et > max_end))
continue;
@@ -704,7 +752,7 @@ static void move_relative(struct cal *cal, int rel)
st = cal->current;
et = cal->current + timeblock;
-
+
if ((hit = query_span(cal, 0, st, et, 0, 0)) != -1) {
struct event *ev = &cal->events[hit];
vevent_span_timet(ev->vevent, &st, &et);
@@ -818,6 +866,128 @@ static void open_below(struct cal *cal)
}
+static void finish_editing(struct cal *cal)
+{
+ struct event *event = get_selected_event(cal);
+
+ if (!event)
+ return;
+
+ // TODO: what are we editing?
+ // Right now we can only edit the summary
+
+ // set summary of selected event
+ icalcomponent_set_summary(event->vevent, g_editbuf);
+
+ // leave edit mode
+ cal->flags &= ~CAL_EDITING;
+}
+
+static void append_str_edit_buffer(const char *src)
+{
+ if (*src == '\0')
+ return;
+
+ char *dst = &g_editbuf[g_editbuf_pos];
+ int c = g_editbuf_pos;
+
+ while (c < EDITBUF_MAX && (*dst++ = *src++))
+ c++;
+
+ if (c == EDITBUF_MAX-1)
+ g_editbuf[EDITBUF_MAX-1] = '\0';
+
+ g_editbuf_pos = c;
+
+}
+
+/* static void append_edit_buffer(char key) */
+/* { */
+/* if (g_editbuf_pos + 1 >= EDITBUF_MAX) { */
+/* warn("attempting to write past end of edit buffer"); */
+/* return; */
+/* } */
+/* g_editbuf[g_editbuf_pos++] = key; */
+/* } */
+
+static void pop_edit_buffer(int amount)
+{
+ amount = clamp(amount, 0, EDITBUF_MAX-1);
+ int top = g_editbuf_pos - amount;
+ top = clamp(top, 0, EDITBUF_MAX-1);
+ g_editbuf[top] = '\0';
+ g_editbuf_pos = top;
+}
+
+static void cancel_editing(struct cal *cal)
+{
+ cal->flags &= ~CAL_EDITING;
+}
+
+static void pop_word_edit_buffer()
+{
+ int c = clamp(g_editbuf_pos - 2, 0, EDITBUF_MAX-1);
+ char *p = &g_editbuf[c];
+
+ while (p >= g_editbuf && *(p--) != ' ')
+ ;
+
+ if (*(p + 1) == ' ') {
+ p += 2;
+ *p = '\0';
+ }
+ else
+ *(++p) = '\0';
+
+ g_editbuf_pos = p - g_editbuf;
+
+ return;
+}
+
+
+static int on_edit_keypress(struct cal *cal, GdkEventKey *event)
+{
+ char key = *event->string;
+
+ switch (event->keyval) {
+ case GDK_KEY_Escape:
+ cancel_editing(cal);
+ return 1;
+
+ case GDK_KEY_Return:
+ finish_editing(cal);
+ return 1;
+
+ case GDK_KEY_BackSpace:
+ pop_edit_buffer(1);
+ return 1;
+ }
+
+ if (key == 0x17) {
+ pop_word_edit_buffer();
+ return 1;
+ }
+
+ // TODO: more special edit keys
+
+ if (*event->string >= 0x20)
+ append_str_edit_buffer(event->string);
+
+ return 1;
+}
+
+static void debug_edit_buffer(GdkEventKey *event)
+{
+ int len = strlen(event->string);
+ printf("edit buffer: %s[%x][%ld] %d %d '%s'\n",
+ event->string,
+ len > 0 ? *event->string : '\0',
+ strlen(event->string),
+ event->state,
+ g_editbuf_pos,
+ g_editbuf);
+}
+
static gboolean on_keypress (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct extra_data *data = (struct extra_data*)user_data;
@@ -830,7 +1000,14 @@ static gboolean on_keypress (GtkWidget *widget, GdkEvent *event, gpointer user_
switch (event->type) {
case GDK_KEY_PRESS:
+ if (cal->flags & CAL_EDITING) {
+ state_changed = on_edit_keypress(cal, &event->key);
+ debug_edit_buffer(&event->key);
+ goto check_state;
+ }
+
key = *event->key.string;
+
int nkey = key - '0';
if (nkey >= 2 && nkey <= 9) {
@@ -845,6 +1022,10 @@ static gboolean on_keypress (GtkWidget *widget, GdkEvent *event, gpointer user_
cal->repeat = 1;
break;
+ case 'e':
+ edit_mode(cal);
+ break;
+
case 't':
move_now(cal);
break;
@@ -919,6 +1100,7 @@ static gboolean on_keypress (GtkWidget *widget, GdkEvent *event, gpointer user_
break;
}
+check_state:
if (state_changed)
on_state_change(widget, event, user_data);
@@ -1267,9 +1449,8 @@ static void saturate(union rgba *c, double change)
c->b = P+((c->b)-P)*change;
}
-
static void
-draw_event (cairo_t *cr, struct cal *cal, struct event *ev) {
+draw_event (cairo_t *cr, struct cal *cal, struct event *ev, struct event *sel) {
// double height = Math.fmin(, MIN_EVENT_HEIGHT);
// stdout.printf("sloc %f eloc %f dloc %f eheight %f\n",
// sloc, eloc, dloc, eheight);
@@ -1286,6 +1467,9 @@ draw_event (cairo_t *cr, struct cal *cal, struct event *ev) {
/* icaltimetype dtend = icalcomponent_get_dtend(ev->vevent); */
int isdate = dtstart.is_date;
+ int is_selected = sel == ev;
+ int is_editing = is_selected && (cal->flags & CAL_EDITING);
+
time_t st, et;
vevent_span_timet(ev->vevent, &st, &et);
@@ -1301,8 +1485,10 @@ draw_event (cairo_t *cr, struct cal *cal, struct event *ev) {
time_t len = et - st;
cairo_text_extents_t exts;
+ // TODO: we may not be editing the summary
const char * const summary =
- icalcomponent_get_summary(ev->vevent);
+ is_editing ? g_editbuf
+ : icalcomponent_get_summary(ev->vevent);
if (is_dragging || ev->flags & EV_HIGHLIGHTED) {
c.a *= 0.95;
@@ -1322,9 +1508,8 @@ draw_event (cairo_t *cr, struct cal *cal, struct event *ev) {
cairo_move_to(cr, x, y);
// TODO: selected event rendering
- if (get_selected_event(cal) == ev) {
+ if (is_selected)
saturate(&c, 0.5);
- }
cairo_set_source_rgba(cr, c.r, c.g, c.b, c.a);
draw_rectangle(cr, ev->width, evheight);
@@ -1333,7 +1518,7 @@ draw_event (cairo_t *cr, struct cal *cal, struct event *ev) {
static const double txtc = 0.2;
cairo_set_source_rgb(cr, txtc, txtc, txtc);
if (isdate) {
- sprintf(buffer, "%s", summary);
+ sprintf(buffer, is_selected ? "'%s'" : "%s", summary);
cairo_text_extents(cr, buffer, &exts);
cairo_move_to(cr, x + EVPAD, y + (evheight / 2.0)
+ ((double)exts.height / 2.0));
@@ -1365,17 +1550,35 @@ draw_event (cairo_t *cr, struct cal *cal, struct event *ev) {
format_time_duration(duration_format_in, sizeof(duration_format), in);
format_time_duration(duration_format_out, sizeof(duration_format), out);
- if (out >= 0 && in >= 0 && out < len)
- sprintf(buffer, "%s | %s | %s in | %s left", summary,
- duration_format,
- duration_format_in,
- duration_format_out);
- else if (in >= 0 && in < 0)
- sprintf(buffer, "%s | %s | %s in", summary,
+ if (out >= 0 && in >= 0 && out < len) {
+ const char *fmt =
+ is_editing
+ ? "'%s' | %s | %s in | %s left"
+ : "%s | %s | %s in | %s left";
+
+ sprintf(buffer, fmt, summary,
+ duration_format,
+ duration_format_in,
+ duration_format_out);
+ }
+ else if (in >= 0 && in < 0) {
+ const char *fmt =
+ is_editing
+ ? "'%s' | %s | %s in"
+ : "%s | %s | %s in";
+
+ sprintf(buffer, fmt, summary,
duration_format,
duration_format_in);
- else
- sprintf(buffer, "%s | %s", summary, duration_format);
+ }
+ else {
+ const char *fmt =
+ is_editing
+ ? "'%s' | %s"
+ : "%s | %s";
+
+ sprintf(buffer, fmt, summary, duration_format);
+ }
cairo_text_extents(cr, buffer, &exts);
double ey = evheight < exts.height
@@ -1441,10 +1644,13 @@ draw_calendar (cairo_t *cr, struct cal *cal) {
draw_background(cr, width, height);
draw_hours(cr, cal);
+ struct event *selected =
+ get_selected_event(cal);
+
// draw calendar events
for (i = 0; i < cal->nevents; ++i) {
struct event *ev = &cal->events[i];
- draw_event(cr, cal, ev);
+ draw_event(cr, cal, ev, selected);
}
if (cal->selected_event_ind == -1)