2000-02-11 Federico Mena Quintero <federico@helixcode.com> * cal-client.c (cal_client_update_object): Implemented. * cal.c (cal_notify_update): New function to notify the listener about an updated object. (Cal_update_object): Implemented. (Cal_get_uids): set_release() the sequence to TRUE. (Cal_get_events_in_range): Likewise. * cal-backend.c (remove_object): New function to remove objects from a calendar backend. (cal_backend_update_object): New public function to update an object and notify clients about it. * evolution-calendar.idl (Cal): Added update_object() and delete_object() methods. (Listener): Removed the obj_changed method and renamed obj_added to obj_updated. We now only have updated and removed notifiers. * cal-listener.[ch]: Removed the "changed" notification code. Changed the "added" notification code to the "updated" notification. * cal-client.c: Likewise. * tlacuache.c (create_cal_factory): Connect to "destroy" on the factory and exit the main loop when the factory is destroyed. * cal-factory.c (backend_destroy_cb): New callback used when a backend is destroyed. Removes the backend from the factory's hash table and unrefs the factory if all backends go away. (add_calendar_client): Free the environment. * cal.c (cal_new): Use bonobo_object_unref() if we fail to initialize. * cal-listener.c (cal_listener_new): Likewise. * layout.c (layout_events): Plug li.partition memory leak. svn path=/trunk/; revision=1742
289 lines
6.6 KiB
C
289 lines
6.6 KiB
C
/* Event layout engine for Gnomecal
|
|
*
|
|
* Copyright (C) 1998 The Free Software Foundation
|
|
*
|
|
* Authors: Miguel de Icaza <miguel@nuclecu.unam.mx>
|
|
* 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.
|
|
*/
|
|
#define MAX_EVENTS_PER_ROW 32
|
|
|
|
|
|
/* Compares two time_t values, used for qsort() */
|
|
static int
|
|
compare_time_t (const void *a, const void *b)
|
|
{
|
|
time_t ta, tb;
|
|
|
|
ta = *((time_t *) a);
|
|
tb = *((time_t *) b);
|
|
|
|
if (ta < tb)
|
|
return -1;
|
|
else if (ta > tb)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* 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 void
|
|
build_partition (struct layout_info *li)
|
|
{
|
|
time_t *rows, *p, *q;
|
|
GList *list;
|
|
int i, unique_vals;
|
|
|
|
/* This is the maximum number of rows we would need */
|
|
|
|
li->num_rows = li->num_events * 2;
|
|
|
|
/* Fill the rows with the times */
|
|
|
|
rows = g_new (time_t, li->num_rows);
|
|
|
|
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, li->num_rows, sizeof (time_t), compare_time_t);
|
|
|
|
p = rows;
|
|
q = rows + 1;
|
|
unique_vals = 1;
|
|
|
|
for (i = 1; i < li->num_rows; i++, q++)
|
|
if (*q != *p) {
|
|
unique_vals++;
|
|
p++;
|
|
*p = *q;
|
|
}
|
|
|
|
/* Return the number of unique values in the partition and the partition array itself */
|
|
|
|
li->num_rows = unique_vals;
|
|
li->partition = rows;
|
|
}
|
|
|
|
/* Returns the index of the element in the partition that corresponds to the specified time */
|
|
static int
|
|
find_index (struct layout_info *li, time_t t)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; ; i++)
|
|
if (li->partition[i] == t)
|
|
return i;
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
#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 (struct layout_info *li, int slot, time_t start, time_t end)
|
|
{
|
|
int i;
|
|
|
|
for (i = find_index (li, start); li->partition[i] < end; i++)
|
|
if (xy (li, slot, i) != -1)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Allocates a time in the slot array for the specified event's index */
|
|
static void
|
|
range_allocate (struct layout_info *li, int slot, time_t start, time_t end, int ev_num)
|
|
{
|
|
int i;
|
|
|
|
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 void
|
|
initial_allocate (struct layout_info *li)
|
|
{
|
|
GList *events;
|
|
int i;
|
|
int slot;
|
|
int num_slots;
|
|
time_t start, end;
|
|
|
|
num_slots = 0;
|
|
|
|
for (i = 0, events = li->events; events; events = events->next, i++) {
|
|
(* li->func) (events, &start, &end);
|
|
|
|
/* Start with no allocation, no columns */
|
|
|
|
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 (li, slot, start, end)) {
|
|
range_allocate (li, slot, start, end, i);
|
|
|
|
li->allocations[i] = slot;
|
|
li->slots[i] = 1;
|
|
|
|
if ((slot + 1) > num_slots)
|
|
num_slots = slot + 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 (struct layout_info *li, int ev_num, time_t start, time_t end)
|
|
{
|
|
int cols;
|
|
int slot;
|
|
int i_start;
|
|
int i;
|
|
|
|
cols = 0;
|
|
|
|
i_start = find_index (li, start);
|
|
|
|
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++;
|
|
}
|
|
|
|
return cols;
|
|
}
|
|
|
|
/* Expands an event by the specified number of columns */
|
|
static void
|
|
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 (li, start); li->partition[i] < end; i++) {
|
|
slot = li->allocations[ev_num] + 1;
|
|
|
|
for (j = 0; j < num_cols; j++)
|
|
xy (li, slot + j, i) = ev_num;
|
|
}
|
|
}
|
|
|
|
/* Expands the events in the slot array to occupy as many columns as possible. This is the second
|
|
* pass of the layout algorithm.
|
|
*/
|
|
static void
|
|
expand_events (struct layout_info *li)
|
|
{
|
|
GList *events;
|
|
time_t start, end;
|
|
int i;
|
|
int cols;
|
|
|
|
for (i = 0, events = li->events; events; events = events->next, i++) {
|
|
(* li->func) (events, &start, &end);
|
|
|
|
cols = columns_to_expand (li, i, start, end);
|
|
|
|
if (cols == 0)
|
|
continue; /* We can't expand this event */
|
|
|
|
do_expansion (li, i, start, end, cols);
|
|
|
|
li->slots[i] += cols;
|
|
}
|
|
}
|
|
|
|
void
|
|
layout_events (GList *events, LayoutQueryTimeFunc func, int *num_slots, int **allocations, int **slots)
|
|
{
|
|
struct layout_info li;
|
|
int i;
|
|
|
|
g_return_if_fail (num_slots != NULL);
|
|
g_return_if_fail (allocations != NULL);
|
|
g_return_if_fail (slots != NULL);
|
|
|
|
if (!events) {
|
|
*num_slots = 0;
|
|
*allocations = NULL;
|
|
*slots = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
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 */
|
|
|
|
build_partition (&li);
|
|
|
|
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 */
|
|
|
|
li.allocations = g_new (int, li.num_events);
|
|
li.slots = g_new (int, li.num_events);
|
|
|
|
/* Perform initial allocation and then expand the events to as many slots as they can occupy */
|
|
|
|
initial_allocate (&li);
|
|
expand_events (&li);
|
|
|
|
/* Clean up and return values */
|
|
|
|
g_free (li.partition);
|
|
g_free (li.array);
|
|
|
|
*num_slots = li.num_slots;
|
|
*allocations = li.allocations;
|
|
*slots = li.slots;
|
|
}
|