/* Monolith calendar widget. * - by Richard W.M. Jones * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: ml_calendar_month.c,v 1.9 2003/02/22 15:34:26 rich Exp $ */ #include "config.h" #include #include #include #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #include #include #include #include #include #include #include #include #include "ml_calendar_lib.h" #include "ml_calendar_month.h" static void repaint (void *, ml_session, const char *, io_handle); struct ml_widget_operations calendar_month_ops = { repaint: repaint }; struct ml_calendar_month { struct ml_widget_operations *ops; pool pool; /* Pool for allocations. */ ml_session session; /* Current session. */ const char *conninfo; /* Database connection. */ ml_calendar calendar; /* Parent calendar. */ int yyyy, mm, dd; /* Current date. */ ml_table_layout tbl; /* Top-level table layout. */ ml_text_label days[7]; /* Days of the week labels. */ ml_button b[32]; /* Buttons (we reuse these). */ }; /* This structure is passed to the button callbacks. */ struct button_data { int dd; /* Day (1 - 31). */ ml_calendar_month w; /* Widget pointer. */ }; static void button_press (ml_session, void *); static void update_buttons (ml_calendar_month w); ml_calendar_month new_ml_calendar_month (pool pool, ml_session session, const char *conninfo, ml_calendar calendar) { ml_calendar_month w = pmalloc (pool, sizeof *w); int i, j; struct button_data *data; w->ops = &calendar_month_ops; w->pool = pool; w->session = session; w->conninfo = conninfo; w->calendar = calendar; /* The parent calendar must call ml_calendar_month_set_date to set these * before using this widget. */ w->yyyy = w->mm = w->dd = -1; /* Create the days of the week labels and buttons. */ /* XXX Waiting for ml_text_label and ml_button to give us more control * over the rendering of labels and buttons. */ w->days[0] = new_ml_text_label (pool, "Su"); ml_widget_set_property (w->days[0], "font.weight", "bold"); w->days[1] = new_ml_text_label (pool, "Mo"); ml_widget_set_property (w->days[1], "font.weight", "bold"); w->days[2] = new_ml_text_label (pool, "Tu"); ml_widget_set_property (w->days[2], "font.weight", "bold"); w->days[3] = new_ml_text_label (pool, "We"); ml_widget_set_property (w->days[3], "font.weight", "bold"); w->days[4] = new_ml_text_label (pool, "Th"); ml_widget_set_property (w->days[4], "font.weight", "bold"); w->days[5] = new_ml_text_label (pool, "Fr"); ml_widget_set_property (w->days[5], "font.weight", "bold"); w->days[6] = new_ml_text_label (pool, "Sa"); ml_widget_set_property (w->days[6], "font.weight", "bold"); w->b[0] = 0; /* This is a dummy entry. */ for (i = 1; i <= 31; ++i) { data = pmalloc (pool, sizeof *data); w->b[i] = new_ml_button (pool, pitoa (pool, i)); data->dd = i; data->w = w; ml_button_set_callback (w->b[i], button_press, session, data); } /* Create the top-level table widget, also reused. */ w->tbl = new_ml_table_layout (pool, 7, 7); for (i = 0; i < 7; ++i) { ml_table_layout_pack (w->tbl, w->days[i], 0, i); ml_table_layout_set_align (w->tbl, 0, i, "center"); } for (j = 1; j < 7; ++j) for (i = 0; i < 7; ++i) ml_table_layout_set_align (w->tbl, j, i, "right"); return w; } /* Update the date, then update the widget. */ void ml_calendar_month_set_date (ml_calendar_month w, int yyyy, int mm, int dd) { int j, i, d, n; /* Update the date. */ w->yyyy = yyyy; w->mm = mm; w->dd = dd; /* Clear the existing table of widgets. */ for (j = 1; j < 7; ++j) for (i = 0; i < 7; ++i) ml_table_layout_pack (w->tbl, 0, j, i); /* Redraw the month. */ j = 1; i = _ml_calendar_first_wday_of_month (mm, yyyy); n = _ml_calendar_days_in_month (mm, yyyy); for (d = 1; d <= n; ++d) { ml_table_layout_pack (w->tbl, w->b[d], j, i); i++; if (i == 7) { i = 0; j++; } } /* Update buttons from the database. */ update_buttons (w); } /* This function is called when a button is pressed. */ static void button_press (ml_session session, void *vdata) { struct button_data *data = (struct button_data *) vdata; /* This function, in the parent calendar, will eventually call our * ml_calendar_month_set_date. */ ml_calendar_set_date (data->w->calendar, data->w->yyyy, data->w->mm, data->dd); } /* This function updates the state of the buttons from the database, * and also highlights the currently selected day. */ static void update_buttons (ml_calendar_month w) { db_handle dbh; st_handle sth; unsigned char flags[32]; int dd; const vector resids = _ml_calendar_get_calendars (w->calendar); dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS); /* Find out which days contain data. Note how with PostgreSQL we * can leave the database to take the strain of date munging. This * is one of the great reasons to use and recommend PostgreSQL. */ sth = st_prepare_cached (dbh, "select date_part ('day', start_time) from ml_calendar_events " " where resid in (@) " " and start_time >= date (? || '/' || ? || '/01') " " and start_time < (date (? || '/' || ? || '/01') + " " interval '1 month')", DBI_VECTOR_INT, DBI_INT, DBI_INT, DBI_INT, DBI_INT); st_execute (sth, resids, w->yyyy, w->mm, w->yyyy, w->mm); st_bind (sth, 0, dd, DBI_INT); memset (flags, 0, sizeof flags); while (st_fetch (sth)) { assert (0 < dd && dd < 32); flags[dd] |= 1; } flags[w->dd] |= 2; /* Depending on the flags, we now set the class of each button. */ for (dd = 1; dd <= 31; ++dd) { switch (flags[dd]) { case 0: ml_widget_set_property (w->b[dd], "class", "ml_calendar_day"); break; case 1: ml_widget_set_property (w->b[dd], "class", "ml_calendar_day_events"); break; case 2: ml_widget_set_property (w->b[dd], "class", "ml_calendar_day_selected"); break; case 3: ml_widget_set_property (w->b[dd], "class", "ml_calendar_day_events_selected"); break; } } put_db_handle (dbh); } static void repaint (void *vw, ml_session session, const char *windowid, io_handle io) { ml_calendar_month w = (ml_calendar_month) vw; ml_widget_repaint (w->tbl, session, windowid, io); }