diff options
-rw-r--r-- | calendar/ChangeLog | 17 | ||||
-rw-r--r-- | calendar/gncal-full-day.c | 171 | ||||
-rw-r--r-- | calendar/gui/gncal-full-day.c | 171 | ||||
-rw-r--r-- | calendar/gui/layout.c | 155 | ||||
-rw-r--r-- | calendar/gui/layout.h | 20 | ||||
-rw-r--r-- | calendar/gui/quick-view.c | 2 | ||||
-rw-r--r-- | calendar/layout.c | 155 | ||||
-rw-r--r-- | calendar/layout.h | 20 | ||||
-rw-r--r-- | calendar/quick-view.c | 2 |
9 files changed, 313 insertions, 400 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 2012f7155d..4bb47d706f 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,20 @@ +1998-10-08 Federico Mena Quintero <federico@nuclecu.unam.mx> + + * layout.c: Do some cleanup; now we pass a struct with the layout + algorithm's state instead of passing a trillion parameters around. + + * gncal-full-day.c (layout_children): Use the new generic layout + engine. + (child_compare): Sort keys are start time then end time, not just + start time. This produces somewhat nicer results for the layout + algorithm. + + The new layout code uses a partition of the time range occupied by + the events, rather than using a fixed time granularity. This is + better since the different parts of the program that use the + layout module will have different semantics regarding snapping the + event bounds to a fixed "time grid". + 1998-10-07 Federico Mena Quintero <federico@nuclecu.unam.mx> * layout.[ch]: New files that abstract the event layout code from diff --git a/calendar/gncal-full-day.c b/calendar/gncal-full-day.c index 6acc8ca16b..402c19acee 100644 --- a/calendar/gncal-full-day.c +++ b/calendar/gncal-full-day.c @@ -12,6 +12,7 @@ #include "eventedit.h" #include "gncal-full-day.h" #include "view-utils.h" +#include "layout.h" #include "main.h" #include "popup-menu.h" @@ -46,11 +47,6 @@ typedef struct { time_t start, end; } Child; -struct layout_row { - int intersections; - int *slots; -}; - struct drag_info { enum { DRAG_NONE, @@ -778,145 +774,64 @@ calc_labels_width (GncalFullDay *fullday) return max_w; } -#define MAX_CHILDREN_ON_ROW 32 - -#define xy(a,x,y) (a[((y) * MAX_CHILDREN_ON_ROW) + (x)]) - -static int -range_empty (char *array, int slot, int lower, int count) -{ - int i; - - for (i = 0; i < count; i++) - if (xy (array, slot, lower+i) != 0) - return 0; - return 1; -} - +/* Used with layout_events(), takes in a list element and returns the start and end times for the + * event corresponding to that element. + */ static void -range_allocate (char *array, int slot, int lower, int count, int val) +child_layout_query_func (GList *event, time_t *start, time_t *end) { - int i; - - for (i = 0; i < count; i++) - xy (array, slot, lower+i) = val; -} - -static int -can_expand (char *array, int *allocations, int top_slot, int val, int lower, int count) -{ - int slot, i; - int cols = 0; - - for (slot = allocations [val] + 1; slot < top_slot; slot++){ - for (i = 0; i < count; i++) - if (xy (array, slot, lower+i)) - return cols; - cols++; - } - return cols; -} + Child *child; -static void -expand_space (char *array, int *allocations, int val, int lower, int count, int cols) -{ - int j, i, slot; + child = event->data; - for (i = 0; i < count; i++){ - slot = allocations [val] + 1; - for (j = 0; j < cols; j++) - xy (array, slot + j, lower+i) = val; - } + *start = child->start; + *end = child->end; } +/* Takes the list of children in the full day view and lays them out nicely without overlapping. + * Basically it calls the layout_events() function in layout.c and resizes the fullday's children. + */ static void layout_children (GncalFullDay *fullday) { GtkWidget *widget; - int lines = (24 * 60) / fullday->interval; - char *array = g_malloc0 (sizeof (char) * lines * MAX_CHILDREN_ON_ROW); GList *children; - int val, slot; - int *allocations, *columns; - int top_slot = 0; - int left_x, cols; - int pixels_per_col, extra_pixels, extra, usable_pixels; - int child_count; - + Child *child; + int num_slots; + int *allocations; + int *slots; + int left_x; + int usable_pixels, pixels_per_col, extra_pixels; + int i; + if (!fullday->children) return; - - /* initial allocation */ - child_count = g_list_length (fullday->children) + 2; - allocations = g_malloc0 (sizeof (int) * child_count); - columns = g_malloc0 (sizeof (int) * child_count); - val = 1; - for (children = fullday->children; children; children = children->next, val++){ - Child *child = children->data; - - allocations [val] = 0; - columns [val] = 1; - for (slot = 0; slot < MAX_CHILDREN_ON_ROW; slot++){ - if (range_empty (array, slot, child->lower_row, child->rows_used)){ - /* - printf ("Child %d uses %d-%d allocates slot=%d\n", val, child->lower_row, - child->lower_row + child->rows_used, slot); - */ - - range_allocate (array, slot, child->lower_row, child->rows_used, val); - allocations [val] = slot; - columns [val] = 1; - if (slot+1 > top_slot) - top_slot = slot+1; - break; - } - } - } -#if DEBUGME - for (val = 0; val < 48; val++){ - int j; - - printf ("%d: ", val); - for (j = 0; j < top_slot; j++){ - printf (" %d", xy (array, j, val)); - } - printf ("\n"); - } -#endif - /* Expand */ - val = 1; - for (children = fullday->children; children; children = children->next, val++){ - Child *child = children->data; - - cols = can_expand (array, allocations, top_slot, val, child->lower_row, child->rows_used); - /* printf ("Can expand regresa: %d\n", cols); */ - if (!cols) - continue; - expand_space (array, allocations, val, child->lower_row, child->rows_used, cols); - columns [val] += cols; - } - /* Assign the spaces */ + layout_events (fullday->children, child_layout_query_func, &num_slots, &allocations, &slots); + + /* Set the size and position of each child */ + widget = GTK_WIDGET (fullday); left_x = 2 * (widget->style->klass->xthickness + TEXT_BORDER) + calc_labels_width (fullday); - usable_pixels = widget->allocation.width-left_x - widget->style->klass->xthickness; - pixels_per_col = usable_pixels / top_slot; - extra_pixels = usable_pixels % top_slot; - - val = 1; - for (children = fullday->children; children; children = children->next, val++){ - Child *child = children->data; - - child->x = left_x + pixels_per_col * allocations [val]; - extra = (allocations [val] + columns [val] == top_slot) ? extra_pixels : 0; - child->width = pixels_per_col * columns [val] + extra; - child_set_size (child); + usable_pixels = widget->allocation.width - left_x - widget->style->klass->xthickness; + pixels_per_col = usable_pixels / num_slots; + extra_pixels = usable_pixels % num_slots; + + for (children = fullday->children, i = 0; children; children = children->next, i++) { + child = children->data; - /* printf ("Setting child %d to %d for %d pixels\n", val, allocations [val], columns [val]); */ + child->x = left_x + pixels_per_col * allocations[i]; + child->width = pixels_per_col * slots[i]; + + if ((allocations[i] + slots[i]) == num_slots) + child->width += extra_pixels; + + child_set_size (child); } + g_free (allocations); - g_free (columns); + g_free (slots); } guint @@ -2118,13 +2033,17 @@ gncal_full_day_forall (GtkContainer *container, gboolean include_internals, GtkC } static gint -child_compare_by_start (gconstpointer a, gconstpointer b) +child_compare (gconstpointer a, gconstpointer b) { const Child *ca = a; const Child *cb = b; time_t diff; diff = ca->start - cb->start; + + if (diff == 0) + diff = cb->end - ca->end; + return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; } @@ -2135,7 +2054,7 @@ fullday_add_children (iCalObject *obj, time_t start, time_t end, void *c) Child *child; child = child_new (fullday, start, end, obj); - fullday->children = g_list_insert_sorted (fullday->children, child, child_compare_by_start); + fullday->children = g_list_insert_sorted (fullday->children, child, child_compare); return 1; } diff --git a/calendar/gui/gncal-full-day.c b/calendar/gui/gncal-full-day.c index 6acc8ca16b..402c19acee 100644 --- a/calendar/gui/gncal-full-day.c +++ b/calendar/gui/gncal-full-day.c @@ -12,6 +12,7 @@ #include "eventedit.h" #include "gncal-full-day.h" #include "view-utils.h" +#include "layout.h" #include "main.h" #include "popup-menu.h" @@ -46,11 +47,6 @@ typedef struct { time_t start, end; } Child; -struct layout_row { - int intersections; - int *slots; -}; - struct drag_info { enum { DRAG_NONE, @@ -778,145 +774,64 @@ calc_labels_width (GncalFullDay *fullday) return max_w; } -#define MAX_CHILDREN_ON_ROW 32 - -#define xy(a,x,y) (a[((y) * MAX_CHILDREN_ON_ROW) + (x)]) - -static int -range_empty (char *array, int slot, int lower, int count) -{ - int i; - - for (i = 0; i < count; i++) - if (xy (array, slot, lower+i) != 0) - return 0; - return 1; -} - +/* Used with layout_events(), takes in a list element and returns the start and end times for the + * event corresponding to that element. + */ static void -range_allocate (char *array, int slot, int lower, int count, int val) +child_layout_query_func (GList *event, time_t *start, time_t *end) { - int i; - - for (i = 0; i < count; i++) - xy (array, slot, lower+i) = val; -} - -static int -can_expand (char *array, int *allocations, int top_slot, int val, int lower, int count) -{ - int slot, i; - int cols = 0; - - for (slot = allocations [val] + 1; slot < top_slot; slot++){ - for (i = 0; i < count; i++) - if (xy (array, slot, lower+i)) - return cols; - cols++; - } - return cols; -} + Child *child; -static void -expand_space (char *array, int *allocations, int val, int lower, int count, int cols) -{ - int j, i, slot; + child = event->data; - for (i = 0; i < count; i++){ - slot = allocations [val] + 1; - for (j = 0; j < cols; j++) - xy (array, slot + j, lower+i) = val; - } + *start = child->start; + *end = child->end; } +/* Takes the list of children in the full day view and lays them out nicely without overlapping. + * Basically it calls the layout_events() function in layout.c and resizes the fullday's children. + */ static void layout_children (GncalFullDay *fullday) { GtkWidget *widget; - int lines = (24 * 60) / fullday->interval; - char *array = g_malloc0 (sizeof (char) * lines * MAX_CHILDREN_ON_ROW); GList *children; - int val, slot; - int *allocations, *columns; - int top_slot = 0; - int left_x, cols; - int pixels_per_col, extra_pixels, extra, usable_pixels; - int child_count; - + Child *child; + int num_slots; + int *allocations; + int *slots; + int left_x; + int usable_pixels, pixels_per_col, extra_pixels; + int i; + if (!fullday->children) return; - - /* initial allocation */ - child_count = g_list_length (fullday->children) + 2; - allocations = g_malloc0 (sizeof (int) * child_count); - columns = g_malloc0 (sizeof (int) * child_count); - val = 1; - for (children = fullday->children; children; children = children->next, val++){ - Child *child = children->data; - - allocations [val] = 0; - columns [val] = 1; - for (slot = 0; slot < MAX_CHILDREN_ON_ROW; slot++){ - if (range_empty (array, slot, child->lower_row, child->rows_used)){ - /* - printf ("Child %d uses %d-%d allocates slot=%d\n", val, child->lower_row, - child->lower_row + child->rows_used, slot); - */ - - range_allocate (array, slot, child->lower_row, child->rows_used, val); - allocations [val] = slot; - columns [val] = 1; - if (slot+1 > top_slot) - top_slot = slot+1; - break; - } - } - } -#if DEBUGME - for (val = 0; val < 48; val++){ - int j; - - printf ("%d: ", val); - for (j = 0; j < top_slot; j++){ - printf (" %d", xy (array, j, val)); - } - printf ("\n"); - } -#endif - /* Expand */ - val = 1; - for (children = fullday->children; children; children = children->next, val++){ - Child *child = children->data; - - cols = can_expand (array, allocations, top_slot, val, child->lower_row, child->rows_used); - /* printf ("Can expand regresa: %d\n", cols); */ - if (!cols) - continue; - expand_space (array, allocations, val, child->lower_row, child->rows_used, cols); - columns [val] += cols; - } - /* Assign the spaces */ + layout_events (fullday->children, child_layout_query_func, &num_slots, &allocations, &slots); + + /* Set the size and position of each child */ + widget = GTK_WIDGET (fullday); left_x = 2 * (widget->style->klass->xthickness + TEXT_BORDER) + calc_labels_width (fullday); - usable_pixels = widget->allocation.width-left_x - widget->style->klass->xthickness; - pixels_per_col = usable_pixels / top_slot; - extra_pixels = usable_pixels % top_slot; - - val = 1; - for (children = fullday->children; children; children = children->next, val++){ - Child *child = children->data; - - child->x = left_x + pixels_per_col * allocations [val]; - extra = (allocations [val] + columns [val] == top_slot) ? extra_pixels : 0; - child->width = pixels_per_col * columns [val] + extra; - child_set_size (child); + usable_pixels = widget->allocation.width - left_x - widget->style->klass->xthickness; + pixels_per_col = usable_pixels / num_slots; + extra_pixels = usable_pixels % num_slots; + + for (children = fullday->children, i = 0; children; children = children->next, i++) { + child = children->data; - /* printf ("Setting child %d to %d for %d pixels\n", val, allocations [val], columns [val]); */ + child->x = left_x + pixels_per_col * allocations[i]; + child->width = pixels_per_col * slots[i]; + + if ((allocations[i] + slots[i]) == num_slots) + child->width += extra_pixels; + + child_set_size (child); } + g_free (allocations); - g_free (columns); + g_free (slots); } guint @@ -2118,13 +2033,17 @@ gncal_full_day_forall (GtkContainer *container, gboolean include_internals, GtkC } static gint -child_compare_by_start (gconstpointer a, gconstpointer b) +child_compare (gconstpointer a, gconstpointer b) { const Child *ca = a; const Child *cb = b; time_t diff; diff = ca->start - cb->start; + + if (diff == 0) + diff = cb->end - ca->end; + return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; } @@ -2135,7 +2054,7 @@ fullday_add_children (iCalObject *obj, time_t start, time_t end, void *c) Child *child; child = child_new (fullday, start, end, obj); - fullday->children = g_list_insert_sorted (fullday->children, child, child_compare_by_start); + fullday->children = g_list_insert_sorted (fullday->children, child, child_compare); return 1; } diff --git a/calendar/gui/layout.c b/calendar/gui/layout.c index 03fac94cc6..f97f0d5ffd 100644 --- a/calendar/gui/layout.c +++ b/calendar/gui/layout.c @@ -6,9 +6,25 @@ * Federico Mena <federico@nuclecu.unam.mx> */ +#include <config.h> +#include <stdlib.h> #include "layout.h" +/* This structure is used to pass around layout information among the internal layout functions */ +struct layout_info { + GList *events; /* List of events from client */ + int num_events; /* The number of events (length of the list) */ + LayoutQueryTimeFunc func; /* Function to convert a list item to a start/end time pair */ + int num_rows; /* Size of the time partition */ + time_t *partition; /* The time partition containing start and end time values */ + int *array; /* Working array of free and allocated time slots */ + int *allocations; /* Returned array of slot allocations */ + int *slots; /* Returned array of slots used */ + int num_slots; /* Number of slots used */ +}; + + /* This defines the maximum number of events to overlap per row. More than that number of events * will not be displayed. This is not ideal, so sue me. */ @@ -35,37 +51,35 @@ compare_time_t (const void *a, const void *b) /* Builds a partition of the time range occupied by the events in the list. It returns an array * with the times that define the partition and the number of items in the partition. */ -static time_t * -build_partition (GList *events, int num_events, int *num_rows) +static void +build_partition (struct layout_info *li) { time_t *rows, *p, *q; GList *list; - CalendarObject *co; int i, unique_vals; /* This is the maximum number of rows we would need */ - *num_rows = num_events * 2; + li->num_rows = li->num_events * 2; /* Fill the rows with the times */ - rows = g_new (time_t, *num_rows); + rows = g_new (time_t, li->num_rows); - for (list = events, p = rows; list; list = list->next) { - co = list->data; - *p++ = co->ev_start; - *p++ = co->ev_end; + for (list = li->events, p = rows; list; list = list->next) { + (* li->func) (list, &p[0], &p[1]); + p += 2; } /* Do a sort | uniq on the array */ - qsort (rows, *num_rows, sizeof (time_t), compare_time_t); + qsort (rows, li->num_rows, sizeof (time_t), compare_time_t); p = rows; q = rows + 1; unique_vals = 1; - for (i = 1; i < *num_rows; i++, q++) + for (i = 1; i < li->num_rows; i++, q++) if (*q != *p) { unique_vals++; p++; @@ -74,33 +88,33 @@ build_partition (GList *events, int num_events, int *num_rows) /* Return the number of unique values in the partition and the partition array itself */ - *num_rows = unique_vals; - return rows; + li->num_rows = unique_vals; + li->partition = rows; } /* Returns the index of the element in the partition that corresponds to the specified time */ int -find_index (time_t *partition, time_t t) +find_index (struct layout_info *li, time_t t) { int i; for (i = 0; ; i++) - if (partition[i] == t) + if (li->partition[i] == t) return i; } -#define xy(slot_array, x, y) slot_array[(y * MAX_EVENTS_PER_ROW) + (x)] +#define xy(li, x, y) li->array[(y * MAX_EVENTS_PER_ROW) + (x)] /* Checks that all the cells in the slot array at the specified slot column are free to use by an * event that has the specified range. */ static int -range_is_empty (time_t *partition, int *slot_array, int slot, time_t start, time_t end) +range_is_empty (struct layout_info *li, int slot, time_t start, time_t end) { int i; - for (i = find_index (partition, start); partition[i] < end; i++) - if (xy (slot_array, slot, i) != -1) + for (i = find_index (li, start); li->partition[i] < end; i++) + if (xy (li, slot, i) != -1) return FALSE; return TRUE; @@ -108,43 +122,44 @@ range_is_empty (time_t *partition, int *slot_array, int slot, time_t start, time /* Allocates a time in the slot array for the specified event's index */ static void -range_allocate (time_t *partition, int *slot_array, int slot, time_t start, time_t end, int ev_num) +range_allocate (struct layout_info *li, int slot, time_t start, time_t end, int ev_num) { int i; - for (i = find_index (partition, start); partition[i] < end; i++) - xy (slot_array, slot, i) = ev_num; + for (i = find_index (li, start); li->partition[i] < end; i++) + xy (li, slot, i) = ev_num; } /* Performs the initial allocation of slots for events. Each event gets one column; they will be * expanded in a later stage. Returns the number of columns used. */ -static int -initial_allocate (GList *events, time_t *partition, int *slot_array, int *allocations, int *columns_used) +static void +initial_allocate (struct layout_info *li) { - CalendarObject *co; + GList *events; int i; int slot; int num_slots; + time_t start, end; num_slots = 0; - for (i = 0; events; events = events->next, i++) { - co = events->data; + for (i = 0, events = li->events; events; events = events->next, i++) { + (* li->func) (events, &start, &end); /* Start with no allocation, no columns */ - allocations[i] = -1; - columns_used[i] = 0; + li->allocations[i] = -1; + li->slots[i] = 0; /* Find a free column for the event */ for (slot = 0; slot < MAX_EVENTS_PER_ROW; slot++) - if (range_is_empty (partition, slot_array, slot, co->ev_start, co->ev_end)) { - range_allocate (partition, slot_array, slot, co->ev_start, co->ev_end, i); + if (range_is_empty (li, slot, start, end)) { + range_allocate (li, slot, start, end, i); - allocations[i] = slot; - columns_used[i] = 1; + li->allocations[i] = slot; + li->slots[i] = 1; if ((slot + 1) > num_slots) num_slots = slot + 1; @@ -153,13 +168,12 @@ initial_allocate (GList *events, time_t *partition, int *slot_array, int *alloca } } - return num_slots; + li->num_slots = num_slots; } /* Returns the maximum number of columns that an event can expanded by in the slot array */ static int -columns_to_expand (time_t *partition, int *slot_array, int *allocations, int num_slots, int ev_num, - time_t start, time_t end) +columns_to_expand (struct layout_info *li, int ev_num, time_t start, time_t end) { int cols; int slot; @@ -168,11 +182,11 @@ columns_to_expand (time_t *partition, int *slot_array, int *allocations, int num cols = 0; - i_start = find_index (partition, start); + i_start = find_index (li, start); - for (slot = allocations[ev_num] + 1; slot < num_slots; slot++) { - for (i = i_start; partition[i] < end; i++) - if (xy (slot_array, slot, i) != -1) + for (slot = li->allocations[ev_num] + 1; slot < li->num_slots; slot++) { + for (i = i_start; li->partition[i] < end; i++) + if (xy (li, slot, i) != -1) return cols; cols++; @@ -181,18 +195,18 @@ columns_to_expand (time_t *partition, int *slot_array, int *allocations, int num return cols; } -/* Expands an event to occupy the specified number of columns */ +/* Expands an event by the specified number of columns */ static void -do_expansion (time_t *partition, int *slot_array, int *allocations, int ev_num, time_t start, time_t end, int num_cols) +do_expansion (struct layout_info *li, int ev_num, time_t start, time_t end, int num_cols) { int i, j; int slot; - for (i = find_index (partition, start); partition[i] < end; i++) { - slot = allocations[ev_num] + 1; + for (i = find_index (li, start); li->partition[i] < end; i++) { + slot = li->allocations[ev_num] + 1; for (j = 0; j < num_cols; j++) - xy (slot_array, slot + j, i) = ev_num; + xy (li, slot + j, i) = ev_num; } } @@ -200,33 +214,31 @@ do_expansion (time_t *partition, int *slot_array, int *allocations, int ev_num, * pass of the layout algorithm. */ static void -expand_events (GList *events, time_t *partition, int *slot_array, int num_slots, int *allocations, int *columns_used) +expand_events (struct layout_info *li) { + GList *events; + time_t start, end; int i; - CalendarObject *co; int cols; - for (i = 0; events; events = events->next, i++) { - co = events->data; + for (i = 0, events = li->events; events; events = events->next, i++) { + (* li->func) (events, &start, &end); - cols = columns_to_expand (partition, slot_array, allocations, num_slots, i, co->ev_start, co->ev_end); + cols = columns_to_expand (li, i, start, end); if (cols == 0) continue; /* We can't expand this event */ - do_expansion (partition, slot_array, allocations, i, co->ev_start, co->ev_end, cols); + do_expansion (li, i, start, end, cols); - columns_used[i] += cols; + li->slots[i] += cols; } } void -layout_events (GList *events, int *num_slots, int **allocations, int **slots) +layout_events (GList *events, LayoutQueryTimeFunc func, int *num_slots, int **allocations, int **slots) { - time_t *time_partition; - int *slot_array; - int num_events; - int num_rows; + struct layout_info li; int i; g_return_if_fail (num_slots != NULL); @@ -241,28 +253,33 @@ layout_events (GList *events, int *num_slots, int **allocations, int **slots) return; } - num_events = g_list_length (events); + li.events = events; + li.num_events = g_list_length (events); + li.func = func; /* Build the partition of the time range, and then build the array of slots */ - time_partition = build_partition (events, num_events, &num_rows); + build_partition (&li); - slot_array = g_new (int, num_rows * MAX_EVENTS_PER_ROW); - for (i = 0; i < (num_rows * MAX_EVENTS_PER_ROW); i++) - slot_array[i] = -1; /* This is our 'empty' value */ + li.array = g_new (int, li.num_rows * MAX_EVENTS_PER_ROW); + for (i = 0; i < (li.num_rows * MAX_EVENTS_PER_ROW); i++) + li.array[i] = -1; /* This is our 'empty' value */ /* Build the arrays for allocations and columns used */ - *allocations = g_new (int, num_events); - *slots = g_new (int, num_events); + li.allocations = g_new (int, li.num_events); + li.slots = g_new (int, li.num_events); - /* Perform initial allocation -- each event gets one column */ + /* Perform initial allocation and then expand the events to as many slots as they can occupy */ - *num_slots = initial_allocate (events, time_partition, slot_array, *allocations, *slots); + initial_allocate (&li); + expand_events (&li); - /* Expand the events to as many columns as possible */ + /* Clean up and return values */ - expand_events (events, time_partition, slot_array, *num_slots, *allocations, *slots); + g_free (li.array); - g_free (slot_array); + *num_slots = li.num_slots; + *allocations = li.allocations; + *slots = li.slots; } diff --git a/calendar/gui/layout.h b/calendar/gui/layout.h index b87cf7e36b..734b720b8b 100644 --- a/calendar/gui/layout.h +++ b/calendar/gui/layout.h @@ -9,17 +9,29 @@ #ifndef LAYOUT_H #define LAYOUT_H -#include "calendar.h" +#include <glib.h> +#include <time.h> -/* This is the main layout function for overlapping events. You pass in a list of CalendarObject - * structures and it will calculate a nice non-overlapping layout for them. +/* Functions of this type must translate the list item into two time_t values for the start and end + * of an event. + */ +typedef void (* LayoutQueryTimeFunc) (GList *event, time_t *start, time_t *end); + + +/* This is the main layout function for overlapping events. You pass in a list of (presumably) + * events and a function that should take a list element and return the start and end times for the + * event corresponding to that list element. * * It returns the number of slots ("columns") that you need to take into account when actually * painting the events, the array of the first slot index that each event occupies, and the array of * number of slots that each event occupies. You have to free both arrays. + * + * You will get somewhat better-looking results if the list of events is sorted by using the start + * time as the primary sort key and the end time as the secondary sort key -- so that "longer" + * events go first in the list. */ -void layout_events (GList *events, int *num_slots, int **allocations, int **slots); +void layout_events (GList *events, LayoutQueryTimeFunc func, int *num_slots, int **allocations, int **slots); #endif diff --git a/calendar/gui/quick-view.c b/calendar/gui/quick-view.c index 62cb433447..9f647841cb 100644 --- a/calendar/gui/quick-view.c +++ b/calendar/gui/quick-view.c @@ -194,7 +194,7 @@ quick_view_new (GnomeCalendar *calendar, char *title, GList *event_list) /* Create base widgets for the popup window */ w = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (w), GTK_SHADOW_OUT); + gtk_frame_set_shadow_type (GTK_FRAME (w), GTK_SHADOW_ETCHED_OUT); gtk_container_add (GTK_CONTAINER (qv), w); vbox = gtk_vbox_new (FALSE, 0); diff --git a/calendar/layout.c b/calendar/layout.c index 03fac94cc6..f97f0d5ffd 100644 --- a/calendar/layout.c +++ b/calendar/layout.c @@ -6,9 +6,25 @@ * Federico Mena <federico@nuclecu.unam.mx> */ +#include <config.h> +#include <stdlib.h> #include "layout.h" +/* This structure is used to pass around layout information among the internal layout functions */ +struct layout_info { + GList *events; /* List of events from client */ + int num_events; /* The number of events (length of the list) */ + LayoutQueryTimeFunc func; /* Function to convert a list item to a start/end time pair */ + int num_rows; /* Size of the time partition */ + time_t *partition; /* The time partition containing start and end time values */ + int *array; /* Working array of free and allocated time slots */ + int *allocations; /* Returned array of slot allocations */ + int *slots; /* Returned array of slots used */ + int num_slots; /* Number of slots used */ +}; + + /* This defines the maximum number of events to overlap per row. More than that number of events * will not be displayed. This is not ideal, so sue me. */ @@ -35,37 +51,35 @@ compare_time_t (const void *a, const void *b) /* Builds a partition of the time range occupied by the events in the list. It returns an array * with the times that define the partition and the number of items in the partition. */ -static time_t * -build_partition (GList *events, int num_events, int *num_rows) +static void +build_partition (struct layout_info *li) { time_t *rows, *p, *q; GList *list; - CalendarObject *co; int i, unique_vals; /* This is the maximum number of rows we would need */ - *num_rows = num_events * 2; + li->num_rows = li->num_events * 2; /* Fill the rows with the times */ - rows = g_new (time_t, *num_rows); + rows = g_new (time_t, li->num_rows); - for (list = events, p = rows; list; list = list->next) { - co = list->data; - *p++ = co->ev_start; - *p++ = co->ev_end; + for (list = li->events, p = rows; list; list = list->next) { + (* li->func) (list, &p[0], &p[1]); + p += 2; } /* Do a sort | uniq on the array */ - qsort (rows, *num_rows, sizeof (time_t), compare_time_t); + qsort (rows, li->num_rows, sizeof (time_t), compare_time_t); p = rows; q = rows + 1; unique_vals = 1; - for (i = 1; i < *num_rows; i++, q++) + for (i = 1; i < li->num_rows; i++, q++) if (*q != *p) { unique_vals++; p++; @@ -74,33 +88,33 @@ build_partition (GList *events, int num_events, int *num_rows) /* Return the number of unique values in the partition and the partition array itself */ - *num_rows = unique_vals; - return rows; + li->num_rows = unique_vals; + li->partition = rows; } /* Returns the index of the element in the partition that corresponds to the specified time */ int -find_index (time_t *partition, time_t t) +find_index (struct layout_info *li, time_t t) { int i; for (i = 0; ; i++) - if (partition[i] == t) + if (li->partition[i] == t) return i; } -#define xy(slot_array, x, y) slot_array[(y * MAX_EVENTS_PER_ROW) + (x)] +#define xy(li, x, y) li->array[(y * MAX_EVENTS_PER_ROW) + (x)] /* Checks that all the cells in the slot array at the specified slot column are free to use by an * event that has the specified range. */ static int -range_is_empty (time_t *partition, int *slot_array, int slot, time_t start, time_t end) +range_is_empty (struct layout_info *li, int slot, time_t start, time_t end) { int i; - for (i = find_index (partition, start); partition[i] < end; i++) - if (xy (slot_array, slot, i) != -1) + for (i = find_index (li, start); li->partition[i] < end; i++) + if (xy (li, slot, i) != -1) return FALSE; return TRUE; @@ -108,43 +122,44 @@ range_is_empty (time_t *partition, int *slot_array, int slot, time_t start, time /* Allocates a time in the slot array for the specified event's index */ static void -range_allocate (time_t *partition, int *slot_array, int slot, time_t start, time_t end, int ev_num) +range_allocate (struct layout_info *li, int slot, time_t start, time_t end, int ev_num) { int i; - for (i = find_index (partition, start); partition[i] < end; i++) - xy (slot_array, slot, i) = ev_num; + for (i = find_index (li, start); li->partition[i] < end; i++) + xy (li, slot, i) = ev_num; } /* Performs the initial allocation of slots for events. Each event gets one column; they will be * expanded in a later stage. Returns the number of columns used. */ -static int -initial_allocate (GList *events, time_t *partition, int *slot_array, int *allocations, int *columns_used) +static void +initial_allocate (struct layout_info *li) { - CalendarObject *co; + GList *events; int i; int slot; int num_slots; + time_t start, end; num_slots = 0; - for (i = 0; events; events = events->next, i++) { - co = events->data; + for (i = 0, events = li->events; events; events = events->next, i++) { + (* li->func) (events, &start, &end); /* Start with no allocation, no columns */ - allocations[i] = -1; - columns_used[i] = 0; + li->allocations[i] = -1; + li->slots[i] = 0; /* Find a free column for the event */ for (slot = 0; slot < MAX_EVENTS_PER_ROW; slot++) - if (range_is_empty (partition, slot_array, slot, co->ev_start, co->ev_end)) { - range_allocate (partition, slot_array, slot, co->ev_start, co->ev_end, i); + if (range_is_empty (li, slot, start, end)) { + range_allocate (li, slot, start, end, i); - allocations[i] = slot; - columns_used[i] = 1; + li->allocations[i] = slot; + li->slots[i] = 1; if ((slot + 1) > num_slots) num_slots = slot + 1; @@ -153,13 +168,12 @@ initial_allocate (GList *events, time_t *partition, int *slot_array, int *alloca } } - return num_slots; + li->num_slots = num_slots; } /* Returns the maximum number of columns that an event can expanded by in the slot array */ static int -columns_to_expand (time_t *partition, int *slot_array, int *allocations, int num_slots, int ev_num, - time_t start, time_t end) +columns_to_expand (struct layout_info *li, int ev_num, time_t start, time_t end) { int cols; int slot; @@ -168,11 +182,11 @@ columns_to_expand (time_t *partition, int *slot_array, int *allocations, int num cols = 0; - i_start = find_index (partition, start); + i_start = find_index (li, start); - for (slot = allocations[ev_num] + 1; slot < num_slots; slot++) { - for (i = i_start; partition[i] < end; i++) - if (xy (slot_array, slot, i) != -1) + for (slot = li->allocations[ev_num] + 1; slot < li->num_slots; slot++) { + for (i = i_start; li->partition[i] < end; i++) + if (xy (li, slot, i) != -1) return cols; cols++; @@ -181,18 +195,18 @@ columns_to_expand (time_t *partition, int *slot_array, int *allocations, int num return cols; } -/* Expands an event to occupy the specified number of columns */ +/* Expands an event by the specified number of columns */ static void -do_expansion (time_t *partition, int *slot_array, int *allocations, int ev_num, time_t start, time_t end, int num_cols) +do_expansion (struct layout_info *li, int ev_num, time_t start, time_t end, int num_cols) { int i, j; int slot; - for (i = find_index (partition, start); partition[i] < end; i++) { - slot = allocations[ev_num] + 1; + for (i = find_index (li, start); li->partition[i] < end; i++) { + slot = li->allocations[ev_num] + 1; for (j = 0; j < num_cols; j++) - xy (slot_array, slot + j, i) = ev_num; + xy (li, slot + j, i) = ev_num; } } @@ -200,33 +214,31 @@ do_expansion (time_t *partition, int *slot_array, int *allocations, int ev_num, * pass of the layout algorithm. */ static void -expand_events (GList *events, time_t *partition, int *slot_array, int num_slots, int *allocations, int *columns_used) +expand_events (struct layout_info *li) { + GList *events; + time_t start, end; int i; - CalendarObject *co; int cols; - for (i = 0; events; events = events->next, i++) { - co = events->data; + for (i = 0, events = li->events; events; events = events->next, i++) { + (* li->func) (events, &start, &end); - cols = columns_to_expand (partition, slot_array, allocations, num_slots, i, co->ev_start, co->ev_end); + cols = columns_to_expand (li, i, start, end); if (cols == 0) continue; /* We can't expand this event */ - do_expansion (partition, slot_array, allocations, i, co->ev_start, co->ev_end, cols); + do_expansion (li, i, start, end, cols); - columns_used[i] += cols; + li->slots[i] += cols; } } void -layout_events (GList *events, int *num_slots, int **allocations, int **slots) +layout_events (GList *events, LayoutQueryTimeFunc func, int *num_slots, int **allocations, int **slots) { - time_t *time_partition; - int *slot_array; - int num_events; - int num_rows; + struct layout_info li; int i; g_return_if_fail (num_slots != NULL); @@ -241,28 +253,33 @@ layout_events (GList *events, int *num_slots, int **allocations, int **slots) return; } - num_events = g_list_length (events); + li.events = events; + li.num_events = g_list_length (events); + li.func = func; /* Build the partition of the time range, and then build the array of slots */ - time_partition = build_partition (events, num_events, &num_rows); + build_partition (&li); - slot_array = g_new (int, num_rows * MAX_EVENTS_PER_ROW); - for (i = 0; i < (num_rows * MAX_EVENTS_PER_ROW); i++) - slot_array[i] = -1; /* This is our 'empty' value */ + li.array = g_new (int, li.num_rows * MAX_EVENTS_PER_ROW); + for (i = 0; i < (li.num_rows * MAX_EVENTS_PER_ROW); i++) + li.array[i] = -1; /* This is our 'empty' value */ /* Build the arrays for allocations and columns used */ - *allocations = g_new (int, num_events); - *slots = g_new (int, num_events); + li.allocations = g_new (int, li.num_events); + li.slots = g_new (int, li.num_events); - /* Perform initial allocation -- each event gets one column */ + /* Perform initial allocation and then expand the events to as many slots as they can occupy */ - *num_slots = initial_allocate (events, time_partition, slot_array, *allocations, *slots); + initial_allocate (&li); + expand_events (&li); - /* Expand the events to as many columns as possible */ + /* Clean up and return values */ - expand_events (events, time_partition, slot_array, *num_slots, *allocations, *slots); + g_free (li.array); - g_free (slot_array); + *num_slots = li.num_slots; + *allocations = li.allocations; + *slots = li.slots; } diff --git a/calendar/layout.h b/calendar/layout.h index b87cf7e36b..734b720b8b 100644 --- a/calendar/layout.h +++ b/calendar/layout.h @@ -9,17 +9,29 @@ #ifndef LAYOUT_H #define LAYOUT_H -#include "calendar.h" +#include <glib.h> +#include <time.h> -/* This is the main layout function for overlapping events. You pass in a list of CalendarObject - * structures and it will calculate a nice non-overlapping layout for them. +/* Functions of this type must translate the list item into two time_t values for the start and end + * of an event. + */ +typedef void (* LayoutQueryTimeFunc) (GList *event, time_t *start, time_t *end); + + +/* This is the main layout function for overlapping events. You pass in a list of (presumably) + * events and a function that should take a list element and return the start and end times for the + * event corresponding to that list element. * * It returns the number of slots ("columns") that you need to take into account when actually * painting the events, the array of the first slot index that each event occupies, and the array of * number of slots that each event occupies. You have to free both arrays. + * + * You will get somewhat better-looking results if the list of events is sorted by using the start + * time as the primary sort key and the end time as the secondary sort key -- so that "longer" + * events go first in the list. */ -void layout_events (GList *events, int *num_slots, int **allocations, int **slots); +void layout_events (GList *events, LayoutQueryTimeFunc func, int *num_slots, int **allocations, int **slots); #endif diff --git a/calendar/quick-view.c b/calendar/quick-view.c index 62cb433447..9f647841cb 100644 --- a/calendar/quick-view.c +++ b/calendar/quick-view.c @@ -194,7 +194,7 @@ quick_view_new (GnomeCalendar *calendar, char *title, GList *event_list) /* Create base widgets for the popup window */ w = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (w), GTK_SHADOW_OUT); + gtk_frame_set_shadow_type (GTK_FRAME (w), GTK_SHADOW_ETCHED_OUT); gtk_container_add (GTK_CONTAINER (qv), w); vbox = gtk_vbox_new (FALSE, 0); |