Add to git.
[monolith.git] / calendar / ml_calendar.c
1 /* Monolith calendar widget.
2  * - by Richard W.M. Jones <rich@annexia.org>
3  *
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.
8  *
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.
13  *
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.
17  *
18  * $Id: ml_calendar.c,v 1.7 2003/02/22 15:34:25 rich Exp $
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <assert.h>
24
25 #ifdef HAVE_TIME_H
26 #include <time.h>
27 #endif
28
29 #ifdef HAVE_STRING_H
30 #include <string.h>
31 #endif
32
33 #include <pool.h>
34 #include <pstring.h>
35
36 #include <pthr_dbi.h>
37
38 #include <monolith.h>
39 #include <ml_widget.h>
40 #include <ml_button.h>
41 #include <ml_table_layout.h>
42 #include <ml_vertical_layout.h>
43 #include <ml_horizontal_layout.h>
44 #include <ml_text_label.h>
45 #include <ml_form.h>
46 #include <ml_form_input.h>
47 #include <ml_form_textarea.h>
48 #include <ml_form_select.h>
49 #include <ml_form_text.h>
50 #include <ml_form_submit.h>
51
52 #include "ml_calendar_lib.h"
53 #include "ml_calendar_month.h"
54 #include "ml_calendar_day.h"
55 #include "ml_calendar_notes.h"
56 #include "ml_calendar_visible.h"
57 #include "ml_calendar.h"
58
59 static void repaint (void *, ml_session, const char *, io_handle);
60
61 struct ml_widget_operations calendar_ops =
62   {
63     repaint: repaint
64   };
65
66 struct ml_calendar
67 {
68   struct ml_widget_operations *ops;
69   pool pool;                    /* Pool for allocations. */
70   ml_session session;           /* Current session. */
71   const char *conninfo;         /* Database connection. */
72   vector calendars;             /* Visible calendars (resource IDs). */
73   int yyyy, mm, dd;             /* Currently visible year, month, day. */
74
75   ml_table_layout tbl;          /* Top-level table layout. */
76   ml_button prev_month;         /* Previous month button. */
77   ml_text_label month_lbl;      /* Month label. */
78   ml_button next_month;         /* Next month button. */
79   ml_button prev_year;          /* Previous year button. */
80   ml_text_label year_lbl;       /* Year label. */
81   ml_button next_year;          /* Next year button. */
82   ml_calendar_month month;      /* Month view (left hand side). */
83   ml_button prev_day;           /* Previous day button. */
84   ml_button today;              /* Today button. */
85   ml_button next_day;           /* Next day button. */
86   ml_calendar_notes notes;      /* Day notes. */
87   ml_calendar_day day;          /* Day view (right hand side). */
88   ml_calendar_visible visible;  /* Visible calendar controls. */
89 };
90
91 static void prev_month (ml_session, void *);
92 static void next_month (ml_session, void *);
93 static void prev_year (ml_session, void *);
94 static void next_year (ml_session, void *);
95 static void prev_day (ml_session, void *);
96 static void today (ml_session, void *);
97 static void next_day (ml_session, void *);
98
99 ml_calendar
100 new_ml_calendar (pool pool, ml_session session, const char *conninfo,
101                  const char *res_name)
102 {
103   ml_calendar w = pmalloc (pool, sizeof *w);
104   ml_vertical_layout vert;
105   ml_horizontal_layout horiz;
106
107   w->ops = &calendar_ops;
108   w->pool = pool;
109   w->session = session;
110   w->conninfo = conninfo;
111   w->calendars = new_vector (pool, int);
112
113   w->visible = 0;
114   if (ml_calendar_add_calendar (w, res_name) == -1)
115     return 0;
116
117   /* We won't initialise these until the end of this function, so set
118    * these to known bad values in case one of the constructors below
119    * accidentally tries to use them.
120    */
121   w->yyyy = w->mm = w->dd = -1;
122
123   /* Create the calendar layout. */
124   w->tbl = new_ml_table_layout (pool, 2, 2);
125
126   vert = new_ml_vertical_layout (pool);
127
128   horiz = new_ml_horizontal_layout (pool);
129   w->prev_month = new_ml_button (pool, "&lt;&lt;");
130   ml_button_set_callback (w->prev_month, prev_month, session, w);
131   ml_widget_set_property (w->prev_month, "button.style", "link");
132   ml_horizontal_layout_pack (horiz, w->prev_month);
133   w->month_lbl = new_ml_text_label (pool, 0);
134   ml_horizontal_layout_pack (horiz, w->month_lbl);
135   w->next_month = new_ml_button (pool, "&gt;&gt;");
136   ml_button_set_callback (w->next_month, next_month, session, w);
137   ml_widget_set_property (w->next_month, "button.style", "link");
138   ml_horizontal_layout_pack (horiz, w->next_month);
139   w->prev_year = new_ml_button (pool, "&lt;&lt;");
140   ml_button_set_callback (w->prev_year, prev_year, session, w);
141   ml_widget_set_property (w->prev_year, "button.style", "link");
142   ml_horizontal_layout_pack (horiz, w->prev_year);
143   w->year_lbl = new_ml_text_label (pool, 0);
144   ml_horizontal_layout_pack (horiz, w->year_lbl);
145   w->next_year = new_ml_button (pool, "&gt;&gt;");
146   ml_button_set_callback (w->next_year, next_year, session, w);
147   ml_widget_set_property (w->next_year, "button.style", "link");
148   ml_horizontal_layout_pack (horiz, w->next_year);
149   ml_vertical_layout_pack (vert, horiz);
150
151   w->month = new_ml_calendar_month (pool, session, conninfo, w);
152   ml_vertical_layout_pack (vert, w->month);
153
154   horiz = new_ml_horizontal_layout (pool);
155   w->prev_day = new_ml_button (pool, "Prev");
156   ml_button_set_callback (w->prev_day, prev_day, session, w);
157   ml_horizontal_layout_pack (horiz, w->prev_day);
158   w->today = new_ml_button (pool, "Today");
159   ml_button_set_callback (w->today, today, session, w);
160   ml_horizontal_layout_pack (horiz, w->today);
161   w->next_day = new_ml_button (pool, "Next");
162   ml_button_set_callback (w->next_day, next_day, session, w);
163   ml_horizontal_layout_pack (horiz, w->next_day);
164   ml_vertical_layout_pack (vert, horiz);
165
166   w->notes = new_ml_calendar_notes (pool, session, conninfo, w);
167   ml_vertical_layout_pack (vert, w->notes);
168
169   ml_table_layout_pack (w->tbl, vert, 0, 0);
170   ml_table_layout_set_align (w->tbl, 0, 0, "center");
171   ml_table_layout_set_valign (w->tbl, 0, 0, "top");
172
173   w->day = new_ml_calendar_day (pool, session, conninfo, w);
174   ml_table_layout_pack (w->tbl, w->day, 0, 1);
175   ml_table_layout_set_valign (w->tbl, 0, 1, "top");
176
177   w->visible = new_ml_calendar_visible (pool, session, conninfo, w);
178   ml_table_layout_pack (w->tbl, w->visible, 1, 0);
179   ml_table_layout_set_colspan (w->tbl, 1, 0, 2);
180   ml_table_layout_set_valign (w->tbl, 1, 0, "top");
181
182   /* Update visible calendars. */
183   ml_calendar_visible_update (w->visible);
184
185   /* Set the calendar to today's date. */
186   today (session, w);
187
188   return w;
189 }
190
191 int
192 ml_calendar_add_calendar (ml_calendar w, const char *res_name)
193 {
194   db_handle dbh;
195   st_handle sth;
196   int resid;
197
198   dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
199
200   /* Get the resource ID. */
201   sth = st_prepare_cached
202     (dbh,
203      "select r.resid from ml_resources r, ml_calendar c "
204      "where r.name = ? and r.resid = c.resid",
205      DBI_STRING);
206   st_execute (sth, res_name);
207
208   st_bind (sth, 0, resid, DBI_INT);
209
210   if (!st_fetch (sth)) return -1; /* Not found. */
211
212   vector_push_back (w->calendars, resid);
213
214   put_db_handle (dbh);
215
216   if (w->visible)
217     ml_calendar_visible_update (w->visible);
218
219   return 0;
220 }
221
222 int
223 ml_calendar_del_calendar (ml_calendar w, const char *res_name)
224 {
225   db_handle dbh;
226   st_handle sth;
227   int resid, i, r;
228
229   /* Don't allow the last calendar to be deleted, no matter what. */
230   if (vector_size (w->calendars) == 1)
231     return -1;
232
233   dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
234
235   /* Get the resource ID. */
236   sth = st_prepare_cached
237     (dbh,
238      "select r.resid from ml_resources r, ml_calendar c "
239      "where r.name = ? and r.resid = c.resid",
240      DBI_INT);
241   st_execute (sth, res_name);
242
243   st_bind (sth, 0, resid, DBI_INT);
244
245   if (!st_fetch (sth)) return -1; /* Not found. */
246
247   /* Search for the resource in the list of calendars, and delete it. */
248   for (i = 0; i < vector_size (w->calendars); ++i)
249     {
250       vector_get (w->calendars, i, r);
251       if (r == resid)
252         {
253           vector_erase (w->calendars, i);
254           goto found;
255         }
256     }
257
258   return -1;                    /* Not found. */
259
260  found:
261   put_db_handle (dbh);
262
263   if (w->visible)
264     ml_calendar_visible_update (w->visible);
265
266   return 0;
267 }
268
269 const vector
270 _ml_calendar_get_calendars (ml_calendar w)
271 {
272   return w->calendars;
273 }
274
275 static void
276 prev_month (ml_session session, void *vw)
277 {
278   ml_calendar w = (ml_calendar) vw;
279   int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
280
281   if (mm == 1 && yyyy == 1900) return;
282
283   mm--;
284   if (mm == 0)
285     {
286       yyyy--;
287       mm = 12;
288     }
289   if (dd > _ml_calendar_days_in_month (mm, yyyy))
290     dd = _ml_calendar_days_in_month (mm, yyyy);
291
292   ml_calendar_set_date (w, yyyy, mm, dd);
293 }
294
295 static void
296 next_month (ml_session session, void *vw)
297 {
298   ml_calendar w = (ml_calendar) vw;
299   int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
300
301   mm++;
302   if (mm == 13)
303     {
304       yyyy++;
305       mm = 1;
306     }
307   if (dd > _ml_calendar_days_in_month (mm, yyyy))
308     dd = _ml_calendar_days_in_month (mm, yyyy);
309
310   ml_calendar_set_date (w, yyyy, mm, dd);
311 }
312
313 static void
314 prev_year (ml_session session, void *vw)
315 {
316   ml_calendar w = (ml_calendar) vw;
317   int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
318
319   if (yyyy == 1900) return;
320
321   yyyy--;
322   if (dd > _ml_calendar_days_in_month (mm, yyyy))
323     dd = _ml_calendar_days_in_month (mm, yyyy);
324
325   ml_calendar_set_date (w, yyyy, mm, dd);
326 }
327
328 static void
329 next_year (ml_session session, void *vw)
330 {
331   ml_calendar w = (ml_calendar) vw;
332   int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
333
334   yyyy++;
335   if (dd > _ml_calendar_days_in_month (mm, yyyy))
336     dd = _ml_calendar_days_in_month (mm, yyyy);
337
338   ml_calendar_set_date (w, yyyy, mm, dd);
339 }
340
341 static void
342 prev_day (ml_session session, void *vw)
343 {
344   ml_calendar w = (ml_calendar) vw;
345   int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
346
347   if (dd == 1 && mm == 1 && yyyy == 1900) return;
348
349   dd--;
350   if (dd == 0)
351     {
352       mm--;
353       if (mm == 0)
354         {
355           yyyy--;
356           mm = 12;
357         }
358       dd = _ml_calendar_days_in_month (mm, yyyy);
359     }
360
361   ml_calendar_set_date (w, yyyy, mm, dd);
362 }
363
364 static void
365 today (ml_session session, void *vw)
366 {
367   ml_calendar w = (ml_calendar) vw;
368   struct tm *tm;
369   time_t t;
370
371   time (&t);
372   tm = localtime (&t);
373   ml_calendar_set_date (w, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
374 }
375
376 static void
377 next_day (ml_session session, void *vw)
378 {
379   ml_calendar w = (ml_calendar) vw;
380   int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
381
382   dd++;
383   if (dd > _ml_calendar_days_in_month (mm, yyyy))
384     {
385       mm++;
386       if (mm == 13)
387         {
388           yyyy++;
389           mm = 1;
390         }
391       dd = 1;
392     }
393
394   ml_calendar_set_date (w, yyyy, mm, dd);
395 }
396
397 extern void
398 ml_calendar_set_date (ml_calendar w, int yyyy, int mm, int dd)
399 {
400   /* Verify that this is a correct date. */
401   assert (yyyy >= 1900);
402   assert (1 <= mm && mm <= 12);
403   assert (1 <= dd && dd <= _ml_calendar_days_in_month (mm, yyyy));
404
405   /* Set the date and update the widgets. */
406   w->yyyy = yyyy;
407   w->mm = mm;
408   w->dd = dd;
409
410   ml_widget_set_property (w->month_lbl, "text",
411                           _ml_calendar_text_month (w->mm));
412   ml_widget_set_property (w->year_lbl, "text", pitoa (w->pool, w->yyyy));
413
414   ml_calendar_month_set_date (w->month, yyyy, mm, dd);
415   ml_calendar_day_set_date (w->day, yyyy, mm, dd);
416   ml_calendar_notes_set_date (w->notes, yyyy, mm, dd);
417 }
418
419 extern void
420 ml_calendar_get_date (ml_calendar w, int *yyyy, int *mm, int *dd)
421 {
422   *yyyy = w->yyyy;
423   *mm = w->mm;
424   *dd = w->dd;
425 }
426
427 static void
428 repaint (void *vw, ml_session session, const char *windowid, io_handle io)
429 {
430   ml_calendar w = (ml_calendar) vw;
431
432   /* Repaint the top-level table layout widget. */
433   ml_widget_repaint (w->tbl, session, windowid, io);
434 }