1 /* Monolith calendar widget.
2 * - by Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: ml_calendar_day.c,v 1.7 2003/02/22 15:34:25 rich Exp $
41 #include <ml_widget.h>
42 #include <ml_window.h>
43 #include <ml_dialog.h>
44 #include <ml_iframe.h>
45 #include <ml_button.h>
46 #include <ml_table_layout.h>
47 #include <ml_flow_layout.h>
48 #include <ml_form_layout.h>
49 #include <ml_text_label.h>
51 #include <ml_form_input.h>
52 #include <ml_form_text.h>
53 #include <ml_form_textarea.h>
54 #include <ml_form_select.h>
55 #include <ml_form_submit.h>
57 #include "ml_calendar_lib.h"
58 #include "ml_calendar_day.h"
60 static void repaint (void *, ml_session, const char *, io_handle);
62 struct ml_widget_operations calendar_day_ops =
67 struct ml_calendar_day
69 struct ml_widget_operations *ops;
70 pool pool; /* Pool for allocations. */
71 ml_session session; /* Current session. */
72 const char *conninfo; /* Database connection. */
73 ml_calendar calendar; /* Parent calendar. */
74 int yyyy, mm, dd; /* Current date. */
76 ml_iframe iframe; /* The iframe widget. */
77 ml_table_layout tbl; /* Top-level layout. */
78 ml_button b[24][2]; /* One button for each half hour of the day. */
80 /* This is the new event form: */
81 ml_form_select f_resid; /* Which calendar. */
82 vector f_resids; /* List of calendars. */
83 ml_form_select f_start; /* Start time. */
84 ml_form_select f_length; /* Length. */
85 ml_form_text f_subject; /* Subject (title). */
86 ml_form_textarea f_body; /* Body / note. */
88 /* The above fields are also used on the edit event form, plus these: */
89 int eventid; /* Event ID we are editing. */
90 ml_form_submit edit, delete; /* Which button was pressed. */
93 /* This is the parameter to the time_button_press function. */
94 struct time_button_data
100 /* This is the parameter to the event_button_press function. */
101 struct event_button_data
107 static pool cal_day_pool; /* Pool for global allocations. */
108 static vector start_times; /* List of start times (for forms). */
109 static vector lengths; /* List of lengths (for forms). */
110 static ml_text_label spacer; /* Used for spacing things out. */
112 /* The global start_times vector contains these. */
113 struct start_times_entry
115 const char *text; /* Text label. */
116 int hh, mm; /* Actual start time. */
119 /* The global lengths vector contains these. */
122 const char *text; /* Text label. */
123 int hh, mm; /* Actual length in hours, mins. */
126 static void calendar_day_init (void) __attribute__ ((constructor));
127 static void calendar_day_stop (void) __attribute__ ((destructor));
129 static void make_window (ml_session session, void *vw);
130 static void update_table (ml_calendar_day w);
131 static void time_button_press (ml_session session, void *vdata);
132 static void add_event (ml_session session, void *vw);
133 static void event_button_press (ml_session session, void *vdata);
134 static void edit_event (ml_session session, void *vw);
139 struct start_times_entry se;
140 struct lengths_entry le;
143 cal_day_pool = new_subpool (global_pool);
144 start_times = new_vector (cal_day_pool, struct start_times_entry);
145 lengths = new_vector (cal_day_pool, struct lengths_entry);
147 /* Populate the start_times array. */
148 for (hh = 0; hh < 24; ++hh)
149 for (mm = 0; mm < 60; mm += 15)
151 se.text = psprintf (cal_day_pool, "%02d:%02d", hh, mm);
154 vector_push_back (start_times, se);
157 /* Populate the lengths array. */
158 le.text = "¼ hour"; le.hh = 0; le.mm = 15;
159 vector_push_back (lengths, le);
160 le.text = "½ hour"; le.hh = 0; le.mm = 30;
161 vector_push_back (lengths, le);
162 le.text = "1 hour"; le.hh = 1; le.mm = 0;
163 vector_push_back (lengths, le);
164 le.text = "1½ hours"; le.hh = 1; le.mm = 30;
165 vector_push_back (lengths, le);
166 le.text = "2 hours"; le.hh = 2; le.mm = 0; vector_push_back (lengths, le);
167 le.text = "3 hours"; le.hh = 3; le.mm = 0; vector_push_back (lengths, le);
168 le.text = "4 hours"; le.hh = 4; le.mm = 0; vector_push_back (lengths, le);
169 le.text = "5 hours"; le.hh = 5; le.mm = 0; vector_push_back (lengths, le);
170 le.text = "6 hours"; le.hh = 6; le.mm = 0; vector_push_back (lengths, le);
171 le.text = "7 hours"; le.hh = 7; le.mm = 0; vector_push_back (lengths, le);
172 le.text = "8 hours"; le.hh = 8; le.mm = 0; vector_push_back (lengths, le);
173 le.text = "9 hours"; le.hh = 9; le.mm = 0; vector_push_back (lengths, le);
174 le.text = "10 hours"; le.hh = 10; le.mm = 0; vector_push_back (lengths, le);
175 le.text = "11 hours"; le.hh = 11; le.mm = 0; vector_push_back (lengths, le);
176 le.text = "12 hours"; le.hh = 12; le.mm = 0; vector_push_back (lengths, le);
178 /* This is just used to space events out. */
179 spacer = new_ml_text_label (cal_day_pool, " ");
185 delete_pool (cal_day_pool);
189 new_ml_calendar_day (pool pool, ml_session session, const char *conninfo,
190 ml_calendar calendar)
192 ml_calendar_day w = pmalloc (pool, sizeof *w);
193 struct time_button_data *data;
196 w->ops = &calendar_day_ops;
198 w->session = session;
199 w->conninfo = conninfo;
200 w->calendar = calendar;
202 /* The parent calendar must call ml_calendar_day_set_date to set these
203 * before using this widget.
205 w->yyyy = w->mm = w->dd = -1;
207 /* The widget is really an iframe. */
208 w->iframe = new_ml_iframe (pool, make_window, session, w, 0);
209 ml_widget_set_property (w->iframe, "height", 500);
210 ml_widget_set_property (w->iframe, "width", 400);
212 /* Create the top-level widgets. */
213 w->tbl = new_ml_table_layout (pool, 48, 2);
215 /* Create the buttons and populate them in the table widget. */
216 for (hh = 0; hh < 24; ++hh)
217 for (mm = i = 0; mm <= 30; mm += 30, i++)
219 data = pmalloc (pool, sizeof *data);
225 w->b[hh][0] = new_ml_button (pool, psprintf (pool, "%02d:00", hh));
227 w->b[hh][1] = new_ml_button (pool, "---");
228 ml_button_set_callback (w->b[hh][i], time_button_press, session, data);
229 ml_button_set_popup (w->b[hh][i], "ml_calendar_day_add_event");
230 ml_button_set_popup_size (w->b[hh][i], 640, 300);
231 ml_widget_set_property (w->b[hh][i], "button.style", "link");
232 if (hh >= 9 && hh < 18)
233 ml_widget_set_property (w->b[hh][i], "class", "ml_calendar_hour");
235 ml_widget_set_property (w->b[hh][i], "class",
236 "ml_calendar_hour_off_peak");
237 ml_table_layout_pack (w->tbl, w->b[hh][i], hh*2+i, 0);
244 ml_calendar_day_set_date (ml_calendar_day w, int yyyy, int mm, int dd)
253 /* Create the internal window.
254 * XXX This could be more efficiently written. It creates a window each
255 * time the widget is redrawn.
258 make_window (ml_session session, void *vw)
260 ml_calendar_day w = (ml_calendar_day) vw;
263 win = new_ml_window (session, w->pool);
264 ml_window_pack (win, w->tbl);
265 ml_window_scroll_to (win, 0, 360);
268 /* This form is used for adding events to the calendar. */
270 time_button_press (ml_session session, void *vdata)
272 struct time_button_data *data = (struct time_button_data *) vdata;
273 ml_calendar_day w = data->w;
279 ml_form_submit submit;
283 win = new_ml_window (w->session, w->pool);
284 form = new_ml_form (w->pool);
285 tbl = new_ml_form_layout (w->pool);
287 /* Set the callback on the form. */
288 ml_form_set_callback (form, add_event, session, w);
289 ml_widget_set_property (form, "method", "GET");
291 /* Pull out the list of calendar names from the database, and
292 * use this to populate the calendar drop-down menu.
294 dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
296 w->f_resid = new_ml_form_select (w->pool, form);
297 w->f_resids = new_vector (w->pool, int);
299 sth = st_prepare_cached
301 "select c.resid, r.name from ml_calendar c, ml_resources r "
302 "where c.resid in (@) and c.resid = r.resid order by 2",
304 st_execute (sth, _ml_calendar_get_calendars (w->calendar));
306 st_bind (sth, 0, resid, DBI_INT);
307 st_bind (sth, 1, resname, DBI_STRING);
309 while (st_fetch (sth))
311 vector_push_back (w->f_resids, resid);
312 ml_form_select_push_back (w->f_resid, resname);
315 ml_form_layout_pack (tbl, "Calendar: ", w->f_resid);
320 w->f_start = new_ml_form_select (w->pool, form);
322 for (i = 0; i < vector_size (start_times); ++i)
324 struct start_times_entry se;
326 vector_get (start_times, i, se);
327 ml_form_select_push_back (w->f_start, se.text);
328 if (se.hh == data->hh && se.mm == data->mm)
329 ml_form_select_set_selection (w->f_start,
330 ml_form_select_size (w->f_start) - 1);
332 ml_form_layout_pack (tbl, "Start time: ", w->f_start);
335 w->f_length = new_ml_form_select (w->pool, form);
337 for (i = 0; i < vector_size (lengths); ++i)
339 struct lengths_entry le;
341 vector_get (lengths, i, le);
342 ml_form_select_push_back (w->f_length, le.text);
345 ml_form_select_set_selection (w->f_length, 1);
346 ml_form_layout_pack (tbl, "Length: ", w->f_length);
349 w->f_subject = new_ml_form_text (w->pool, form);
350 ml_widget_set_property (w->f_subject, "form.text.size", 50);
351 ml_form_layout_pack (tbl, "Title: ", w->f_subject);
354 w->f_body = new_ml_form_textarea (w->pool, form, 5, 50);
355 ml_form_layout_pack (tbl, "Notes: ", w->f_body);
358 /* XXX Cancel button. */
359 submit = new_ml_form_submit (w->pool, form, "Save");
360 ml_form_layout_pack (tbl, 0, submit);
362 /* Pack everything into the window. */
363 ml_form_pack (form, tbl);
364 ml_window_pack (win, form);
367 /* When the form is submitted to save a new event, this function is called. */
369 add_event (ml_session session, void *vw)
371 ml_calendar_day w = (ml_calendar_day) vw;
373 const char *subject, *body;
376 struct start_times_entry se;
377 struct lengths_entry le;
381 /* Only logged-in users can post. */
382 /* XXX Resource-level security - later. */
383 userid = ml_session_userid (session);
386 ml_error_window (w->pool, session,
387 "You must be logged in to add events to the calendar.",
388 ML_DIALOG_CLOSE_BUTTON);
392 /* Verify that the user has at least given a subject line. If not, just
393 * return, which redisplays the form.
395 subject = ml_form_input_get_value (w->f_subject);
396 if (!subject || strlen (subject) == 0)
399 /* Verify the remaining fields are in range. */
400 i = ml_form_select_get_selection (w->f_resid);
401 si = ml_form_select_get_selection (w->f_start);
402 li = ml_form_select_get_selection (w->f_length);
403 if (i < 0 || i >= vector_size (w->f_resids) ||
404 si < 0 || si >= vector_size (start_times) ||
405 li < 0 || li >= vector_size (lengths))
408 /* Pull the other fields from the form. */
409 body = ml_form_input_get_value (w->f_body);
410 vector_get (w->f_resids, i, resid);
411 vector_get (start_times, si, se);
412 vector_get (lengths, li, le);
414 /* Save it in the database. */
415 dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
417 sth = st_prepare_cached
419 "insert into ml_calendar_events (resid, start_time, length, subject, "
420 "body, author, original_ip) "
421 "values (?, ?, ?, ?, ?, ?, ?)",
422 DBI_INT, DBI_STRING, DBI_STRING, DBI_STRING, DBI_STRING,
423 DBI_INT, DBI_STRING);
426 psprintf (w->pool, "%04d/%02d/%02d %02d:%02d:00",
427 w->yyyy, w->mm, w->dd, se.hh, se.mm),
428 psprintf (w->pool, "%d hours %d mins", le.hh, le.mm),
431 ml_session_get_peernamestr (session));
433 /* Commit change to the database. */
437 /* Print confirmation page. */
438 ml_ok_window (w->pool, session,
439 "That event was added to the calendar.",
440 ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
445 event_button_press (ml_session session, void *vdata)
447 struct event_button_data *data = (struct event_button_data *) vdata;
448 ml_calendar_day w = data->w;
454 ml_flow_layout buttons;
456 const char *resname, *subject, *body;
457 struct dbi_timestamp start_time;
458 struct dbi_interval length;
460 dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
462 /* Pull out the current values of the fields from the database. */
463 sth = st_prepare_cached
465 "select resid, start_time, length, subject, body "
466 " from ml_calendar_events "
467 " where id = ? and resid in (@)", DBI_INT, DBI_VECTOR_INT);
468 st_execute (sth, data->eventid, _ml_calendar_get_calendars (w->calendar));
470 st_bind (sth, 0, resid, DBI_INT);
471 st_bind (sth, 1, start_time, DBI_TIMESTAMP);
472 st_bind (sth, 2, length, DBI_INTERVAL);
473 st_bind (sth, 3, subject, DBI_STRING);
474 st_bind (sth, 4, body, DBI_STRING);
478 ml_error_window (w->pool, session,
479 "I cannot find that calendar event. Perhaps it has "
480 "been deleted by another user, or it is in a calendar "
481 "which you can no longer see.",
482 ML_DIALOG_CLOSE_BUTTON);
486 /* Save the event ID in the widget structure. We need it in edit_event. */
487 w->eventid = data->eventid;
489 win = new_ml_window (session, w->pool);
490 form = new_ml_form (w->pool);
491 tbl = new_ml_form_layout (w->pool);
493 /* Set the callback on the form. */
494 ml_form_set_callback (form, edit_event, session, w);
495 ml_widget_set_property (form, "method", "GET");
497 /* Pull out the list of calendar names from the database, and
498 * use this to populate the calendar drop-down menu.
500 w->f_resid = new_ml_form_select (w->pool, form);
501 w->f_resids = new_vector (w->pool, int);
503 sth = st_prepare_cached
505 "select c.resid, r.name from ml_calendar c, ml_resources r "
506 "where c.resid in (@) and c.resid = r.resid order by 2",
508 st_execute (sth, _ml_calendar_get_calendars (w->calendar));
510 st_bind (sth, 0, t, DBI_INT);
511 st_bind (sth, 1, resname, DBI_STRING);
513 while (st_fetch (sth))
515 vector_push_back (w->f_resids, resid);
516 ml_form_select_push_back (w->f_resid, resname);
519 ml_form_select_set_selection (w->f_resid,
520 ml_form_select_size (w->f_resid) - 1);
522 ml_form_layout_pack (tbl, "Calendar: ", w->f_resid);
525 w->f_start = new_ml_form_select (w->pool, form);
527 for (i = 0; i < vector_size (start_times); ++i)
529 struct start_times_entry se;
531 vector_get (start_times, i, se);
532 ml_form_select_push_back (w->f_start, se.text);
533 /* XXX This doesn't work for events spanning across day boundaries. */
534 if (se.hh == start_time.hour && se.mm == start_time.min)
535 ml_form_select_set_selection (w->f_start,
536 ml_form_select_size (w->f_start) - 1);
538 ml_form_layout_pack (tbl, "Start time: ", w->f_start);
541 w->f_length = new_ml_form_select (w->pool, form);
543 for (i = 0; i < vector_size (lengths); ++i)
545 struct lengths_entry le;
547 vector_get (lengths, i, le);
548 ml_form_select_push_back (w->f_length, le.text);
549 if (le.hh == length.hours && le.mm == length.mins)
550 ml_form_select_set_selection (w->f_length,
551 ml_form_select_size (w->f_length) - 1);
554 ml_form_layout_pack (tbl, "Length: ", w->f_length);
557 w->f_subject = new_ml_form_text (w->pool, form);
558 ml_widget_set_property (w->f_subject, "form.text.size", 50);
560 ml_form_input_set_value (w->f_subject, subject);
561 ml_form_layout_pack (tbl, "Title: ", w->f_subject);
564 w->f_body = new_ml_form_textarea (w->pool, form, 5, 50);
566 ml_form_input_set_value (w->f_body, body);
567 ml_form_layout_pack (tbl, "Notes: ", w->f_body);
569 /* Submit buttons. */
570 buttons = new_ml_flow_layout (w->pool);
571 w->edit = new_ml_form_submit (w->pool, form, "Edit");
572 ml_flow_layout_pack (buttons, w->edit);
573 w->delete = new_ml_form_submit (w->pool, form, "Delete");
574 ml_flow_layout_pack (buttons, w->delete);
576 ml_form_layout_pack (tbl, 0, buttons);
578 /* Pack everything into the window. */
579 ml_form_pack (form, tbl);
580 ml_window_pack (win, form);
583 /* When the edit or delete button is pressed, this function is called. */
585 edit_event (ml_session session, void *vw)
587 ml_calendar_day w = (ml_calendar_day) vw;
589 int edit_pressed, delete_pressed;
590 const char *s, *subject, *body;
593 struct start_times_entry se;
594 struct lengths_entry le;
598 /* Only logged-in users can post. */
599 /* XXX Resource-level security - later. */
600 userid = ml_session_userid (session);
603 ml_error_window (w->pool, session,
604 "You must be logged in to change "
605 "events in the calendar.",
606 ML_DIALOG_CLOSE_BUTTON);
610 dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
612 /* Which submit button was pressed? */
613 s = ml_form_input_get_value (w->edit);
614 edit_pressed = s && strlen (s) > 0;
616 s = ml_form_input_get_value (w->delete);
617 delete_pressed = s && strlen (s) > 0;
619 assert ((edit_pressed || delete_pressed) &&
620 !(edit_pressed && delete_pressed));
622 /* Delete button pressed? */
625 sth = st_prepare_cached
627 "delete from ml_calendar_events "
628 "where id = ? and resid in (@)",
629 DBI_INT, DBI_VECTOR_INT);
630 st_execute (sth, w->eventid, _ml_calendar_get_calendars (w->calendar));
632 /* Commit change to the database. */
636 /* Print confirmation page. */
637 ml_ok_window (w->pool, session,
638 "That event was deleted.",
639 ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
644 /* Verify that the user has at least given a subject line. If not, just
645 * return, which redisplays the form.
647 subject = ml_form_input_get_value (w->f_subject);
648 if (!subject || strlen (subject) == 0)
651 /* Verify the remaining fields are in range. */
652 i = ml_form_select_get_selection (w->f_resid);
653 si = ml_form_select_get_selection (w->f_start);
654 li = ml_form_select_get_selection (w->f_length);
655 if (i < 0 || i >= vector_size (w->f_resids) ||
656 si < 0 || si >= vector_size (start_times) ||
657 li < 0 || li >= vector_size (lengths))
660 /* Pull the other fields from the form. */
661 body = ml_form_input_get_value (w->f_body);
662 vector_get (w->f_resids, i, resid);
663 vector_get (start_times, si, se);
664 vector_get (lengths, li, le);
666 /* Update the database. */
667 sth = st_prepare_cached
669 "update ml_calendar_events "
670 "set resid = ?, start_time = ?, length = ?, subject = ?, body = ?, "
671 "last_modified_date = current_timestamp "
672 "where id = ? and resid in (@)",
673 DBI_INT, DBI_STRING, DBI_STRING, DBI_STRING, DBI_STRING,
674 DBI_INT, DBI_VECTOR_INT);
677 psprintf (w->pool, "%04d/%02d/%02d %02d:%02d:00",
678 w->yyyy, w->mm, w->dd, se.hh, se.mm),
679 psprintf (w->pool, "%d hours %d mins", le.hh, le.mm),
681 w->eventid, _ml_calendar_get_calendars (w->calendar));
683 /* Commit change to the database. */
687 /* Print confirmation page. */
688 ml_ok_window (w->pool, session,
689 "That event was edited.",
690 ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
694 /* Update the table from the database. */
696 update_table (ml_calendar_day w)
700 const vector resids = _ml_calendar_get_calendars (w->calendar);
701 int eventid, resid, start_time_hh, start_time_mm, end_time_hh, end_time_mm;
702 const char *subject, *body, *start_of_day;
704 ml_flow_layout flow[48] = { 0 };
706 struct event_button_data *data;
709 dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
711 /* Read the events from the database. */
712 /* SQL/PostgreSQL could do with having some way to name subexpressions
715 start_of_day = psprintf (w->pool,
716 "%04d/%02d/%02d 00:00:00",
717 w->yyyy, w->mm, w->dd);
719 sth = st_prepare_cached
721 "select id, resid, subject, body, "
722 " date_part ('hour', start_time - date ?), "
723 " date_part ('min', start_time - date ?), "
724 " date_part ('hour', start_time + length - date ?), "
725 " date_part ('min', start_time + length - date ?) "
726 " from ml_calendar_events "
727 " where resid in (@) "
728 " and length <> '1 day' "
729 " and start_time + length >= date ? "
730 " and start_time < date ? + interval '1 day'"
732 DBI_STRING, DBI_STRING, DBI_STRING, DBI_STRING,
734 DBI_STRING, DBI_STRING);
736 st_execute (sth, start_of_day, start_of_day, start_of_day, start_of_day,
737 resids, start_of_day, start_of_day);
739 st_bind (sth, 0, eventid, DBI_INT);
740 st_bind (sth, 1, resid, DBI_INT);
741 st_bind (sth, 2, subject, DBI_STRING);
742 st_bind (sth, 3, body, DBI_STRING);
743 st_bind (sth, 4, start_time_hh, DBI_INT);
744 st_bind (sth, 5, start_time_mm, DBI_INT);
745 st_bind (sth, 6, end_time_hh, DBI_INT);
746 st_bind (sth, 7, end_time_mm, DBI_INT);
748 while (st_fetch (sth))
750 data = pmalloc (w->pool, sizeof *data);
751 data->eventid = eventid;
754 /* Create the tooltip (title) for this button. */
755 title = psprintf (w->pool, "%02d:%02d - %02d:%02d: %s",
756 start_time_hh, start_time_mm,
757 end_time_hh, end_time_mm,
760 /* Create the button. */
761 b = new_ml_button (w->pool, subject);
762 ml_widget_set_property (b, "button.style", "link");
763 ml_widget_set_property (b, "color", _ml_calendar_colour (resid));
764 ml_widget_set_property (b, "title", title);
765 ml_button_set_callback (b, event_button_press, w->session, data);
766 ml_button_set_popup (b, "ml_calendar_day_edit_event");
767 ml_button_set_popup_size (b, 640, 300);
769 /* Work out the start / end indexes. */
770 if (start_time_hh < 0)
774 assert (start_time_hh <= 24);
775 assert (0 <= start_time_mm && start_time_mm < 60);
777 start = start_time_hh * 2 + start_time_mm / 30;
780 if (end_time_hh >= 24)
784 assert (end_time_hh >= 0);
785 assert (0 <= end_time_mm && end_time_mm < 60);
787 end = end_time_hh * 2 + end_time_mm / 30;
790 /* Make sure that short events are displayed. */
799 "fetched event #%d for day %04d/%02d/%02d: starts at %02d:%02d "
800 "ends at %02d:%02d startbox %d endbox %d\n",
801 eventid, w->yyyy, w->mm, w->dd, start_time_hh, start_time_mm,
802 end_time_hh, end_time_mm, start, end);
805 /* Pack it into the appropriate flow widget. */
806 for (i = start; i < end; ++i)
809 flow[i] = new_ml_flow_layout (w->pool);
810 ml_flow_layout_push_back (flow[i], b);
811 ml_flow_layout_push_back (flow[i], spacer);
815 /* Copy the flow widgets into the table's second column. */
816 for (i = 0; i < 48; ++i)
817 ml_table_layout_pack (w->tbl, flow[i], i, 1);
823 repaint (void *vw, ml_session session, const char *windowid, io_handle io)
825 ml_calendar_day w = (ml_calendar_day) vw;
827 ml_widget_repaint (w->iframe, session, windowid, io);