dtcm: remove register keyword
[oweals/cde.git] / cde / programs / dtcm / dtcm / weekglance.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: weekglance.c /main/13 1996/11/21 19:43:24 drk $ */
24 /*
25  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
26  *  (c) Copyright 1993, 1994 International Business Machines Corp.
27  *  (c) Copyright 1993, 1994 Novell, Inc.
28  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
29  */
30
31 #include <EUSCompat.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #include <unistd.h>
36 #include <sys/stat.h>
37 #include <sys/param.h> /* MAXPATHLEN defined here */
38 #ifdef SVR4
39 #include <sys/utsname.h> /* SYS_NMLN */
40 #endif /* SVR4 */
41 #include <dirent.h>
42 #include <ctype.h>
43 #include <string.h>
44 #include <sys/time.h>
45 #if defined(sun) && defined(_XOPEN_SOURCE)
46 #include <time.h>
47 #endif
48 #include <rpc/rpc.h>
49 #include <sys/resource.h>
50 #include <sys/wait.h>
51 #include <netinet/in.h>
52 #include <X11/Xlib.h>
53 #include <Xm/Xm.h>
54 #include <Xm/PushB.h>
55 #include <Xm/Text.h>
56 #include <Xm/ToggleBG.h>
57 #include <Dt/HourGlass.h>
58 #include "calendar.h"
59 #include "util.h"
60 #include "timeops.h"
61 #include "format.h"
62 #include "datefield.h"
63 #include "x_graphics.h"
64 #include "props.h"
65 #include "select.h"
66 #include "editor.h"
67 #include "group_editor.h"
68 #include "browser.h"
69 #include "blist.h"
70 #include "dayglance.h"
71 #include "monthglance.h"
72 #include "yearglance.h"
73 #include "weekglance.h"
74 #include "todo.h"
75 #include "find.h"
76 #include "goto.h"
77 #include "tempbr.h"
78
79 #define XOS_USE_XT_LOCKING
80 #define X_INCLUDE_TIME_H
81 #if defined(__linux__)
82 #undef SVR4
83 #endif
84 #include <X11/Xos_r.h>
85
86 static int week_xytoclock(Week *w, int x, int y);
87 static int week_xytohour(Week *w, int x, int y);
88 static void fill_day();
89 static void draw_week();
90 static void draw_chart();
91 static void quick_button_cb(Widget, XtPointer, XtPointer);
92 static void display_hot_btn(Calendar *, int, int);
93 static void clear_hot_btn(Calendar *, int);
94 static void allocator(Calendar *);
95 static void deallocator(Calendar *);
96 static Boolean print_week (Calendar *c,
97                            int num_page, 
98                            void *xp,
99                            Tick first_date, 
100                            Props *p, 
101                            Boolean first);
102
103
104 #define inchart(w, x, y) \
105                 ((x >= w->chart_x && x <= (w->chart_x + w->chart_width) && \
106                 y >= w->chart_y - w->label_height && \
107                 y <= (w->chart_y + w->chart_height-1)))
108
109 #define inweek(w, x, y) \
110                 ((x >= w->x && x <= w->x + w->width && \
111                 y >= w->y && y <= w->y + w->day_height) || \
112                 (x >= w->x + 3 * w->day_width && x <= w->x + w->width && \
113                 y >= w->y + w->day_height && y <= w->y + w->height))
114
115 extern void
116 format_week_header(Tick date, OrderingType order, char *buf)
117 {
118         Calendar *c = calendar;
119         struct tm *tm;
120         _Xltimeparams localtime_buf;
121
122         tm = _XLocaltime(&date, localtime_buf);
123
124         /* NL_COMMENT
125            Attention Translator:
126
127            This string is used in the calendar week view.  In the C locale
128            it has the form: 
129
130                         Monday, January 16, 1995
131
132            strftime conversion string: "%A, %B %e, %Y" is used.  The string
133            will be used in a label that looks like this:
134
135                         Week Starting Monday, January 16, 1995
136
137            Use the appropriate strftime conversion for your locale.
138         */
139         strftime(buf, 80, 
140                 catgets(c->DT_catd, 1, 993, "Week Starting %A, %B %e, %Y"), tm);
141 }
142  
143 static int
144 count_week_pages (Calendar *c, int lines_per_page, Tick start_date)
145 {
146         time_t start, stop;
147         CSA_return_code stat;
148         CSA_entry_handle *list;
149         CSA_attribute *range_attrs;
150         CSA_enum *ops;
151         CSA_uint32 a_total;
152         int num_appts, i, j, max = 0, pages;
153
154         /* count the times and text of appts */
155         for (i = 1; i <= 7; i++)
156         {
157                 /* setup a time limit for appts searched */
158                 start = (time_t) lowerbound (start_date);
159                 stop = (time_t) next_ndays(start_date, 1) - 1;
160                 setup_range(&range_attrs, &ops, &j, start, stop,
161                             CSA_TYPE_EVENT, 0, B_FALSE, c->general->version);
162                 csa_list_entries(c->cal_handle, j, range_attrs, ops, &a_total, &list, NULL);
163                 free_range(&range_attrs, &ops, j);
164
165                 num_appts = count_multi_appts(list, a_total, c);
166                 if (num_appts > max)
167                         max = num_appts;
168  
169                 start_date = nextday(start_date);
170                 csa_free(list);
171         }
172  
173         pages = max / lines_per_page;
174         if ((max % lines_per_page) > 0)
175                 pages++;
176
177         return(pages);
178 }
179
180 static Boolean
181 print_week (Calendar *c, 
182         int num_page, 
183         void *xp,
184         Tick first_date, 
185         Props *p, 
186         Boolean first)
187 {
188         Boolean more, done = False, all_done = True;
189         int num_appts, day_of_week;
190         char    buf[128];
191         int     i, j;
192         OrderingType ot = get_int_prop(p, CP_DATEORDERING);
193         time_t start, stop;
194         CSA_return_code stat;
195         CSA_entry_handle *list;
196         CSA_attribute *range_attrs;
197         CSA_enum *ops;
198         CSA_uint32 a_total;
199         int lines_per_page;
200         static Tick start_date = 0;
201         static int total_pages;
202  
203         static char *days[] = {
204                 (char *)NULL, (char *)NULL, (char *)NULL,
205                 (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL
206         };
207
208         if (days[0] == (char *)NULL)
209         {
210           days[0] = XtNewString(catgets(c->DT_catd, 1, 596, "Monday %d"));
211           days[1] = XtNewString(catgets(c->DT_catd, 1, 597, "Tuesday %d"));
212           days[2] = XtNewString(catgets(c->DT_catd, 1, 598, "Wednesday %d"));
213           days[3] = XtNewString(catgets(c->DT_catd, 1, 599, "Thursday %d"));
214           days[4] = XtNewString(catgets(c->DT_catd, 1, 600, "Friday %d"));
215           days[5] = XtNewString(catgets(c->DT_catd, 1, 601, "Saturday %d"));
216           days[6] = XtNewString(catgets(c->DT_catd, 1, 602, "Sunday %d"));
217         }
218
219         x_init_printer(xp, LANDSCAPE);
220         x_init_week(xp);
221         lines_per_page = x_get_week_lines_per_page(xp);
222
223         if (first)
224           start_date = first_date;
225
226         if (num_page > 1)
227         {
228           start_date = prevweek(start_date);
229           if (!timeok(start_date))
230             start_date = get_bot();
231         }
232         else
233           total_pages = (lines_per_page > 0) ?
234             count_week_pages(c, lines_per_page, start_date) : 1;
235
236         format_week_header(start_date, ot, buf);
237
238         x_print_header(xp, buf, num_page, total_pages);
239         x_week_appt_boxes(xp);
240         x_week_sched_boxes(xp);
241
242         /* print the times and text of appts */
243         for (i = (dow(start_date) + 6) % 7; i < 7; i++)
244         {
245           /* print <Weekday DD> centered at top of appt box */
246           x_week_sched_init(xp);
247
248           sprintf(buf, days[i], dom(start_date));
249
250           /* setup a time limit for appts searched */
251           start = (time_t) lowerbound (start_date);
252           stop = (time_t) next_ndays(start_date, 1) - 1;
253           setup_range(&range_attrs, &ops, &j, start, stop,
254                       CSA_TYPE_EVENT, 0, B_FALSE, c->general->version);
255           csa_list_entries(c->cal_handle, j, range_attrs,
256                            ops, &a_total, &list, NULL);
257           free_range(&range_attrs, &ops, j);
258
259           num_appts = count_multi_appts(list, a_total, c);
260
261           if ((lines_per_page > 0) &&
262               (num_appts > (lines_per_page * num_page)))
263             more = True;
264           else
265             more = False;
266
267           x_week_daynames(xp, buf, i, more);
268
269           /* print out times and appts */
270           if (lines_per_page > 0)
271             done = x_print_multi_appts(xp, list, a_total,
272                                        num_page, weekGlance);
273           else done = True;
274
275           if (!done)
276             all_done = False;
277
278           x_week_sched_draw(xp, i);
279
280           start_date = nextday(start_date);
281           csa_free(list);
282         }
283
284         x_finish_printer(xp);
285
286         return(all_done);
287 }
288
289 extern void
290 print_week_range(Calendar * c, Tick start_tick, Tick end_tick)
291 {
292
293         Props           *p = (Props *)c->properties;
294         Tick    first_date = start_tick;
295         int             num_weeks;
296         Boolean         done = False, first = True;
297         int             num_page = 1;
298         void *xp = (void *)NULL;
299
300         /* get number of weeks needed to print */
301
302         num_weeks = ((end_tick - start_tick)/wksec) + 1;
303
304         if (num_weeks <= 0)
305                 num_weeks = 1;
306
307         first_date = first_dow(first_date);
308         if (!timeok(first_date))
309           first_date = get_bot();
310
311         if ((xp = x_open_file(c)) == (void *)NULL)
312           return;
313
314         for (; num_weeks > 0; num_weeks--) {
315           while (!done) {
316             done = print_week(c, num_page, xp, first_date, p, first);
317             num_page++;
318             first = False;
319           }
320           done = False;
321           num_page = 1;
322         }
323
324         x_print_file(xp, c);
325 }
326
327 extern int
328 count_multi_appts(CSA_entry_handle *list, int num_entries, Calendar *c)
329 {
330   CSA_return_code stat;
331   Dtcm_appointment *appt;
332   int count = 0, i, meoval;
333   Props *pr = (Props*)c->properties;
334   Lines *lines, *l_ptr;
335  
336   meoval = get_int_prop(pr, CP_PRINTPRIVACY);
337
338   appt = allocate_appt_struct(appt_read,
339                               c->general->version,
340                               CSA_ENTRY_ATTR_CLASSIFICATION_I,
341                               CSA_X_DT_ENTRY_ATTR_SHOWTIME_I,
342                               CSA_ENTRY_ATTR_SUMMARY_I,
343                               NULL);
344   for (i = 0; i < num_entries; i++) {
345
346     stat = query_appt_struct(c->cal_handle, list[i], appt);
347
348     if (stat != CSA_SUCCESS) {
349       free_appt_struct(&appt);
350       return 0;
351     }
352
353     if ((privacy_set(appt) == CSA_CLASS_PUBLIC) && !(meoval & PRINT_PUBLIC))
354       continue;
355     else if ((privacy_set(appt) == CSA_CLASS_CONFIDENTIAL) &&
356              !(meoval & PRINT_SEMIPRIVATE))
357       continue;
358     else if ((privacy_set(appt) == CSA_CLASS_PRIVATE) &&
359              !(meoval & PRINT_PRIVATE))
360       continue;
361
362     if (showtime_set(appt)) {
363       count++;
364     }
365     l_ptr = lines = text_to_lines(appt->what->value->item.string_value, 10);
366     while (lines != NULL) {
367       count++;
368       lines = lines->next;
369     }
370     destroy_lines(l_ptr);
371   }        
372   free_appt_struct(&appt);
373   return(count);
374 }        
375  
376 /*
377  * handler for week view menu item.
378  */
379 void
380 week_button (Widget widget, XtPointer data, XtPointer cbs)
381 {
382         Calendar *c = calendar;
383
384         if (c->view->glance == weekGlance)
385                 return;
386
387         switch (c->view->glance) {
388                 case dayGlance:
389                         c->view->glance = weekGlance;
390                         cleanup_after_dayview(c);
391                         paint_weekview(c, NULL);
392                         break;
393                 case yearGlance:
394                         c->view->glance = weekGlance;
395                         cleanup_after_yearview(c);
396                         XtMapWidget(c->canvas);
397                         break;
398                 case monthGlance:
399                         c->view->glance = weekGlance;
400                         cleanup_after_monthview(c);
401                         break;
402                 default:
403                         break;
404         }
405 }
406
407 /*
408  * Clean up anything left around that won't be removed on
409  * switching to another view, such as the hot buttons for
410  * navigation to day view.
411  */
412 extern void
413 cleanup_after_weekview(Calendar *c)
414 {
415         int n;
416         Week *w = (Week *)c->view->week_info;
417
418         invalidate_cache(c);
419         set_message(c->message_text, "");
420
421         XtUnmanageChildren(w->hot_button, 7);
422
423         XmToggleButtonGadgetSetState(c->week_scope, False, False);
424
425         /*
426          * Deallocating everything causes all this view's memory to be
427          * freed (at least all we have allocated in this application)
428          * at the expense of rebuilding the view next time it is used.
429          * Commenting it out improves usability and avoids any risk
430          * of memory leakage from Xt and Motif.
431          */
432 /*
433         deallocator(c);
434 */
435 }
436
437 /*
438  * allocate storage & subwidgets used by week view
439  */
440 static void 
441 allocator(Calendar *c)
442 {
443         int n;
444         Week *w;
445         Arg args[1];
446
447         /* main storage for other week data */
448         calendar->view->week_info = (caddr_t) ckalloc(sizeof(Week));
449         w = (Week *)c->view->week_info;
450
451         /* navigation buttons to day view */
452         w->hot_button = (Widget *) ckalloc(7 * sizeof(Widget));
453
454         /* Set the recomputeSize of the PushButtons to False, to prevent a loop 
455            where a SetValues is done on the PushButton's width from the 
456            exposeCallback of the DrawingArea */
457         XtSetArg(args[0], XmNrecomputeSize, False);     
458
459         for (n=0; n<7; n++) {
460                 w->hot_button[n] =
461                         XmCreatePushButton(c->canvas, "week2day", args, 1);
462                 XtAddCallback(w->hot_button[n],XmNactivateCallback, 
463                         quick_button_cb, (XtPointer) (intptr_t) n);
464         }
465
466         /* selection info (and init its permanent attributes) */
467         w->current_selection = ckalloc(sizeof(Selection));
468         ((Selection*)w->current_selection)->row = 0;
469         ((Selection*)w->current_selection)->nunits = 1;
470 }
471
472 /*
473  * allocate storage & subwidgets used by week view
474  */
475 static void 
476 deallocator(Calendar *c)
477 {
478         Week *w = (Week *)c->view->week_info;
479         int n;
480
481         /* hot buttons */
482         for (n=0; n<7; n++)
483                 XtDestroyWidget(w->hot_button[n]);
484
485         /* array that held navigation buttons */
486         free(w->hot_button);
487
488         /* selection info */
489         free(w->current_selection);
490
491         /* allocated in init_week */
492         if (w->time_array != NULL)
493                 free(w->time_array);
494
495         /* structure holding week information */
496         free(w);
497         c->view->week_info = NULL;
498 }
499
500 /*
501  * Set up data needed to draw this particular week
502  */
503 static void
504 init_week(Calendar *c, Boundary *boundary)
505 {
506         Week *w = (Week *)c->view->week_info;
507         int     char_width, char_height;
508         Props   *p;
509         int     num_hrs,        day_of_week;
510         int     empty_space, day_box;
511         int     skip_days = 0;
512         XFontSetExtents regfontextents, boldfontextents;
513
514         *boundary = okay;
515
516         /*
517          * The week view starts on Monday.  Map Sunday to the last day of the
518          * week
519          */
520         if ((day_of_week = dow(c->view->date)) == 0)
521                 day_of_week = 6;
522         else
523                 day_of_week--;
524
525         ((Selection*)w->current_selection)->col = day_of_week;
526
527         w->start_date = lowerbound(c->view->date - (day_of_week * daysec));
528         /* make sure date is within bounds */
529         if (w->start_date == -1) {
530                 if (year(c->view->date) == year(get_bot())) {
531                         w->start_date = get_bot();
532                         *boundary = lower;
533                 }
534         }
535         else if (year(next_ndays(w->start_date, 7)) > year(get_eot())) {
536                         *boundary = upper;
537         }
538
539         /*
540          * Set up a bunch of variables which are needed to draw and fill
541          * the week at a glance screen
542          */
543         XtVaGetValues(c->canvas, 
544                         XmNwidth, &w->canvas_w, 
545                         XmNheight, &w->canvas_h, 
546                         NULL);
547
548         w->font = c->fonts->labelfont;
549         w->small_font = c->fonts->viewfont;
550         w->small_bold_font = c->fonts->boldfont;
551         CalFontExtents(w->font, &regfontextents);
552         CalFontExtents(w->small_bold_font, &boldfontextents);
553
554         w->x = c->view->outside_margin;
555         w->y = 2 * (int) boldfontextents.max_logical_extent.height;
556
557         char_height = regfontextents.max_logical_extent.height;
558         char_width = regfontextents.max_logical_extent.width;
559         w->label_height = char_height * 2;
560         w->day_width = ((int) (w->canvas_w - 2 * w->x)) / 5;
561         /* height of box with label */
562         w->day_height = ((int) (w->canvas_h - 2 * w->y)) / 2;
563         /*
564          * We compute week dimensions from day dimensions to remove rounding
565          * errors
566          */
567         w->width = w->day_width * 5;
568         /* height from top of box to bottom of weekend boxes */
569         w->height = w->day_height * 2;
570  
571         p = (Props *)c->properties;
572         w->begin_hour = get_int_prop(p, CP_DAYBEGIN);
573         w->end_hour = get_int_prop(p, CP_DAYEND);
574  
575         /* width of a column in chart */
576         w->chart_day_width = (3 * w->day_width - 3 * char_width) / 7;
577         /* width of chart */
578         w->chart_width = w->chart_day_width * 7;
579         num_hrs = w->end_hour - w->begin_hour;
580  
581         /* height of box without label */
582         day_box = w->day_height - w->label_height;
583  
584         /* height of an hour in chart */
585         w->chart_hour_height = day_box / num_hrs;
586         /* chart_hour_height must be evenly divisble by BOX_SEG */
587         w->chart_hour_height -= (w->chart_hour_height % BOX_SEG);
588         w->chart_height = w->chart_hour_height * num_hrs;
589  
590         /* x point of upper left corner of chart */
591         w->chart_x = w->x + 2 * boldfontextents.max_logical_extent.width;
592         /* y point of upper left corner of chart */
593         w->chart_y = w->y + w->height - w->chart_height;
594  
595         /* left over empty space above chart after round off error */
596         empty_space = day_box - w->chart_height;
597         /* add pixels to the height of each hour box in chart to fill gap*/
598         if (w->add_pixels = ((double)empty_space / (double)num_hrs)) {
599                 w->chart_y -= w->add_pixels * num_hrs;
600                 w->chart_height += w->add_pixels * num_hrs;
601         }
602  
603         w->segs_in_array = BOX_SEG * num_hrs * 7;
604         if (w->time_array != NULL)
605                 free(w->time_array);
606         w->time_array = (char*)ckalloc(w->segs_in_array);
607  
608         c->view->outside_margin = w->x;
609         c->view->topoffset = w->y;
610 }
611
612 extern void
613 paint_weekview(
614         Calendar        *c, 
615         XRectangle      *rect)
616 {
617         Boundary         boundary;
618
619         XmToggleButtonGadgetSetState(c->week_scope, True, False);
620         c->view->glance = weekGlance;
621
622         /* allocate weekview storage if it's never been used before */
623         if (c->view->week_info == NULL) {
624                 allocator(c);
625                 resize_weekview(c, &boundary);
626         } else 
627                 init_week(c, &boundary);
628
629         draw_week(c, rect, boundary);
630         calendar_select(c, weekdaySelect, NULL);
631 }
632
633 static void
634 cm_update_segs(Week *w, 
635         Tick tick, 
636         Tick dur, 
637         int *start_index, 
638         int *end_index, 
639         Boolean addto)
640 {
641         int     num_segs, i, start, start_hour, duration, nday;
642  
643         start_hour = hour(tick);
644        
645         if (start_hour >= w->end_hour) {
646                 *start_index = -1;
647                 *end_index = -1;
648                 return;
649         }
650  
651         if (start_hour < w->begin_hour) {
652                 start = 0;
653                 duration = dur - ((w->begin_hour -
654                  (start_hour + (double)minute(tick)/(double)60))
655                         * hrsec);
656         } else{
657                 start = ((start_hour - w->begin_hour) * 60 + minute(tick));
658                 duration = dur;
659         }
660  
661         if (duration <= 0) {
662                 *start_index = -1;
663                 *end_index = -1;
664                 return;
665         }
666
667         nday = (nday=dow(tick))==0? 6: nday-1;
668         num_segs = (double)start / (double)MINS_IN_SEG;
669         *start_index = (double)start / (double)MINS_IN_SEG + (nday * (w->segs_in_array/7));
670         if (start - (num_segs * MINS_IN_SEG) > 7)
671                 (*start_index)++;
672         num_segs = ((double)duration / (double)60 / (double)MINS_IN_SEG);
673         *end_index = num_segs + *start_index;
674         if (((double)duration/(double)60-MINS_IN_SEG*num_segs) > 7)
675                 (*end_index)++;
676
677         if (*end_index > (i = ((nday + 1) * (w->segs_in_array / 7))) )
678                 *end_index = i;
679
680         for (i = *start_index; i < *end_index; i++)
681                 if (addto)
682                         w->time_array[i]++;
683                 else      
684                         w->time_array[i]--;
685 }        
686
687 static void
688 add_extra_pixels(int i, int num_pixels, int *h)
689 {
690         if (((i+1) % BOX_SEG) == 0)
691                 *h += num_pixels;
692 }
693
694 static void
695 chart_draw_appts(Week *w, int start, int end)
696 {
697         int x, y, h, i, segs_in_col, no_boxes;
698         Calendar *c = calendar;
699         int col_remainder, boxseg_h;
700         Colormap cms;
701
702         XtVaGetValues(c->canvas, XmNcolormap, &cms, NULL);
703         h = boxseg_h = (double)w->chart_hour_height / (double)BOX_SEG;
704         segs_in_col = (w->segs_in_array / 7);
705         /* find starting segment in column */
706         col_remainder =  (start % segs_in_col);
707
708         no_boxes = (double)(col_remainder+1) / (double)BOX_SEG;
709         y = w->chart_y + ((double)col_remainder * (double)boxseg_h) +
710                         ((double)w->add_pixels * (double)no_boxes) + 1;
711         x = w->chart_x + ((double)start / (double)segs_in_col *
712                         (double)w->chart_day_width);
713  
714         for (i = start; i < end; i++) {
715                 if (w->time_array[i] == 0) {
716                         /* batch up repaints */
717                         if ( (i+1) < w->segs_in_array &&
718                                 w->time_array[i+1] == 0 &&
719                                 ((i+1) % segs_in_col) != 0 ) {
720                                 h += boxseg_h;
721                                 add_extra_pixels(i, w->add_pixels, &h);
722                                 continue;
723                         }
724                         add_extra_pixels(i, w->add_pixels, &h);
725                         gr_clear_area(c->xcontext, x, y, w->chart_day_width, h);
726                 }
727                 else if (w->time_array[i] == 1) {
728                         /* batch up for one repaint */
729                         if ( (i+1) < w->segs_in_array
730                                  && w->time_array[i+1] == 1 &&
731                                  ((i+1) % segs_in_col) != 0 ) {
732                                 h += boxseg_h;
733                                 add_extra_pixels(i, w->add_pixels, &h);
734                                 continue;
735                         }
736                         add_extra_pixels(i, w->add_pixels, &h);
737                         if ((c->xcontext->screen_depth < 8) || FAKE_MONOCHROME)
738                                 gr_make_gray(c->xcontext, x, y,
739                                         w->chart_day_width, h, 25);
740                         else
741                                 gr_make_grayshade(c->xcontext, x, y,
742                                         w->chart_day_width, h, LIGHTGREY);
743                 }
744                 else if (w->time_array[i] == 2) {
745                         /* batch up for one repaint */
746                         if ( (i+1) < w->segs_in_array
747                                  && w->time_array[i+1] == 2 &&
748                                  ((i+1) % segs_in_col) != 0 ) {
749                                 h += boxseg_h;
750                                 add_extra_pixels(i, w->add_pixels, &h);
751                                 continue;
752                         }
753                         add_extra_pixels(i, w->add_pixels, &h);
754                         if ((c->xcontext->screen_depth < 8) || FAKE_MONOCHROME)
755                                 gr_make_gray(c->xcontext, x, y,
756                                         w->chart_day_width, h, 50);
757                         else
758                                 gr_make_rgbcolor(c->xcontext, cms, x, y,
759                                         w->chart_day_width, h,
760                                         MIDGREY, MIDGREY, MIDGREY);
761                 }
762                 else if (w->time_array[i] >= 3) {
763                         /* batch up for one repaint */
764                         if ( (i+1) < w->segs_in_array
765                                 && w->time_array[i+1] >= 3 &&
766                                 ((i+1) % segs_in_col) != 0 ) {
767                                 h += boxseg_h;
768                                 add_extra_pixels(i, w->add_pixels, &h);
769                                 continue;
770                         }
771                         add_extra_pixels(i, w->add_pixels, &h);
772                         if ((c->xcontext->screen_depth < 8) || FAKE_MONOCHROME)
773                                 gr_make_gray(c->xcontext, x, y,
774                                         w->chart_day_width, h, 75);
775                         else
776                                 gr_make_grayshade(c->xcontext, x, y,
777                                         w->chart_day_width, h, DIMGREY);
778                 }
779                 if (i != 0 && (((i+1) % segs_in_col) == 0)) {
780                         x += w->chart_day_width;
781                         y = w->chart_y + 1;
782                         h = ((double)w->chart_hour_height/(double)BOX_SEG);
783                 }
784                 else {
785                         y += h;
786                         h = boxseg_h;
787                 }
788         }
789 }
790
791 extern void
792 resize_weekview(
793         Calendar        *c,
794         Boundary        *boundary)
795 {
796         int     x, y;
797         int      n;
798         Week            *w = (Week *)c->view->week_info;
799
800         /* allocate weekview storage if it's never been used before */
801         if (c->view->week_info == NULL) {
802                 allocator(c);
803                 w = (Week *)c->view->week_info;
804         }
805
806         init_week(c, boundary);
807
808         y = w->y;
809         x = w->x;
810
811         for (n = 0; n < 7; n++)  {
812
813                 if (n == 5) {
814                         y += w->day_height + 1;
815                         x = w->x + 3 * w->day_width;
816                 }
817
818                 XtVaSetValues(w->hot_button[n],
819                                 XmNx,           x + 2,
820                                 XmNy,           y + 1,
821                                 XmNwidth,       w->day_width - 3,
822                                 XmNheight,      w->label_height - 2,
823                                 NULL);
824                 x += w->day_width;
825         }
826 }
827
828 static void
829 draw_week(Calendar *c, XRectangle *rect, Boundary boundary)
830 {
831         Week *w = (Week *)c->view->week_info;
832         int    current_day;
833         int    n;
834         int    x, y;
835         int             char_height;
836         int             start_date;
837         char            **day_names;
838         char            label[80];
839         char            buf[MAXNAMELEN];
840         char            *footer_message = NULL;
841         int             start_ind, end_ind;
842         int             today_dom, day_om;
843         new_XContext        *xc;
844         Props           *p = (Props*)c->properties;
845         XRectangle      chartrect;
846         OrderingType    ot = get_int_prop(p, CP_DATEORDERING);
847         Tick            start_tick, end_tick;
848         time_t start, stop;
849         CSA_return_code stat;
850         CSA_entry_handle *list;
851         CSA_attribute *range_attrs;
852         CSA_enum *ops;
853         CSA_uint32 a_total;
854         int i, lower_bound = 0, upper_bound = 0;
855         XFontSetExtents regfontextents, boldfontextents;
856         int     notused, width1, width2, width3;
857         
858         CalFontExtents(w->font, &regfontextents);
859         char_height = regfontextents.max_logical_extent.height;
860         start_date      = w->start_date;
861         xc              = c->xcontext;
862
863         start = (time_t) lowerbound(start_date);
864         stop = (time_t) next_ndays(start, 7) - 1;
865
866         if (c->paint_cache == NULL) {
867                 setup_range(&range_attrs, &ops, &i, start, stop, CSA_TYPE_EVENT,
868                         0, B_FALSE, c->general->version);
869                 csa_list_entries(c->cal_handle, i, range_attrs, ops, 
870                                  &a_total, &list, NULL);
871                 free_range(&range_attrs, &ops, i);
872                 allocate_paint_cache(list, a_total, &c->paint_cache);
873                 c->paint_cache_size = a_total;
874                 csa_free(list);
875         }
876
877
878         gr_clear_box(xc, 0, 0, w->canvas_w, w->canvas_h);
879  
880         CalTextExtents(w->font, days2[3], cm_strlen(days2[3]), 
881                        &notused, &notused, &width1, &notused);
882         CalTextExtents(w->font, "  00", cm_strlen("  00"),
883                        &notused, &notused, &width2, &notused);
884         CalTextExtents(w->font, "Wed 00", cm_strlen("Wed 00"), 
885                        &notused, &notused, &width3, &notused);
886         if (width1 + width2 <= w->day_width - 2)
887                 day_names = days2;
888         else if (width3 <= w->day_width - 2)
889                 day_names = days;
890         else
891                 day_names = days3;
892         x = w->x;
893         y = w->y;
894         
895         format_week_header(start_date, ot, label);
896         gr_text(xc, x, y - char_height / 2, w->font, label, rect);
897  
898         /*
899          * Draw bold box around first 5 days
900          */
901         gr_draw_box(xc, x, y, w->width, w->day_height, rect);
902         gr_draw_box(xc, x - 1, y - 1, w->width + 2, w->day_height + 2, rect);
903         gr_draw_line(xc, x, y + w->label_height, x + w->width,
904                 y + w->label_height, gr_solid, rect);
905  
906         /*
907          * Draw bold box around last 2 days
908          */
909         x += 3 * w->day_width;
910         y += w->day_height;
911  
912         gr_draw_box(xc, x, y, 2 * w->day_width, w->day_height, rect);
913         gr_draw_box(xc, x - 1, y, 2 * w->day_width + 2, w->day_height + 1,rect);
914         gr_draw_line(xc, x, y + w->label_height, x + 2 * w->day_width, 
915                      y + w->label_height, gr_solid, rect);
916         y = w->y;
917         x = w->x + w->day_width;
918         for (n = 0; n < 4; n++) {
919                 if (n < 3) {
920                         gr_draw_line(xc, x, y, x, y + w->day_height, 
921                                      gr_solid, rect);
922                 } else {
923                         gr_draw_line(xc, x, y, x, y + 2 * w->day_height,
924                                      gr_solid, rect);
925                 }
926                 x += w->day_width;
927         }
928        /*
929          * Fill in week with appointments
930          */
931         x = w->x;
932         y = w->y;
933         current_day = start_date;
934         today_dom = dom(time(0));
935  
936         /* Crock alert!!!!  The obscure code below is doing something 
937            really nasty.  The variable boundary indicates whether the 
938            week being displayed falls across a boundary with the 
939            beginning or the end of time.  In the case of the beginning 
940            of time, the code then assumes that the first 2 days in the 
941            week need to be unbuttoned, and the rest buttoned and painted.  
942            Likewise, with the end of time case, the code assumes the last 
943            3 days of the week need similar treatment. */
944
945         for (n = 0; n < 7; n++)  {
946
947                 if (n == 5) {
948                         y += w->day_height;
949                         x = w->x + 3 * w->day_width;
950                 }
951  
952                 if (boundary == okay) {
953                         day_om = dom(current_day);
954                         display_hot_btn(c, n, day_om);
955                         fill_day(c, w, x, y, current_day, 
956                                  c->paint_cache, c->paint_cache_size, rect);
957                         current_day += daysec;
958                         if (lower_bound > 0) {
959                                 sprintf(buf, "%s", catgets(c->DT_catd, 1, 623, "Calendar does not display dates prior to January 1, 1970"));
960                                 footer_message = buf;
961                         }
962                         else
963                                 footer_message = NULL;
964                 }
965                 else if (boundary == lower) {
966                         /* skip days before Jan 1, 1970 */
967                         clear_hot_btn(c, n);
968                         if (lower_bound++ == 2)
969                                 boundary = okay;
970                 }
971                 else if (boundary == upper) {
972                         day_om = dom(current_day);
973                         if (++upper_bound <= 4)
974                                 display_hot_btn(c, n, day_om);
975                         fill_day(c, w, x, y, current_day, 
976                                  c->paint_cache, c->paint_cache_size, rect);
977                         current_day += daysec;
978                         sprintf(buf, "%s", catgets(c->DT_catd, 1, 624, "Calendar does not display dates after December 31, 2037"));
979                         footer_message = buf; 
980                         if (upper_bound > 4)
981                                 clear_hot_btn(c, n); 
982                 }
983                 x += w->day_width;
984         }
985         if (rect != NULL) {
986                 CalFontExtents(w->small_bold_font, &boldfontextents);
987
988                 chartrect.x = w->x;
989                 chartrect.y = w->chart_y - w->label_height;
990                 chartrect.width = w->chart_width +
991                         3*boldfontextents.max_logical_extent.width;
992                 chartrect.height = w->chart_height + 2*w->label_height;
993         }
994
995        if (rect == NULL || myrect_intersectsrect(rect,  &chartrect)) {
996                 for (i = 0; i < c->paint_cache_size; i++) {
997                         start_tick = (c->paint_cache)[i].start_time;
998                         end_tick = (c->paint_cache)[i].end_time;
999                         cm_update_segs(w, start_tick,
1000                                 (end_tick ? (end_tick - start_tick) : 0),
1001                                 &start_ind, &end_ind, True);
1002                 }
1003                 chart_draw_appts(w, 0, w->segs_in_array);
1004                 draw_chart(c, w, NULL);
1005         }
1006
1007         /* do not repaint the footer message in a damage display.  
1008            For some reason this causes the damage routine to get called 
1009            again, resulting in a recursive dsaster. */
1010
1011         if (footer_message && !rect)
1012                 set_message(c->message_text, footer_message);
1013 }
1014 /*
1015  *  Format 2 lines of appt data
1016  */
1017 static void
1018 format_entry(Paint_cache *cache_entry, char *buf1, char *buf2,
1019              DisplayType display) {
1020         Tick    tick, end_tick = 0;
1021         int     hour1, min1, hour2, min2;
1022         Lines   *lines;
1023         char    *s1, *s2;
1024         struct tm *tm;
1025         _Xltimeparams localtime_buf;
1026
1027
1028         tick = cache_entry->start_time;
1029         end_tick = cache_entry->end_time;
1030  
1031         /*
1032          * Extract an appointment and format it into 2 lines of no more
1033          * then maxchars
1034          */
1035         *buf1 = *buf2 = '\0';
1036         if (cache_entry == NULL || cache_entry->summary == NULL) return;
1037         tm = _XLocaltime(&tick, localtime_buf);
1038         hour1 = tm->tm_hour;
1039         min1  = tm->tm_min;
1040  
1041         if (!cache_entry->show_time || magic_time(tick)) {
1042                 lines = (Lines *) text_to_lines(cache_entry->summary, 1); 
1043                 if (lines==NULL) return;
1044                 strncpy(buf2, lines->s, 256);
1045                 destroy_lines(lines);
1046                 return;
1047         }
1048  
1049         s1 = s2 = "am";
1050         if (display == HOUR12 && !adjust_hour(&hour1))
1051                 s1="pm";
1052         if (end_tick) {
1053                 hour2 = hour(end_tick);
1054                 min2 = minute(end_tick);
1055                 if (display == HOUR12 && !adjust_hour(&hour2))
1056                         s2="pm";
1057         }
1058
1059         if (end_tick == 0 ||
1060             (hour1 == hour2 && min1 == min2 && (strcmp(s1, s2) == 0))) {
1061                 if (display == HOUR24)
1062                         sprintf(buf1, "%02d%.2d", hour1, min1);
1063                 else
1064                         sprintf(buf1, "%d:%.2d%s", hour1, min1, s1);
1065         }
1066         else {
1067                 if (display == HOUR12)
1068                         sprintf(buf1, "%d:%.2d%s-%d:%.2d%s", hour1, min1, s1,
1069                                  hour2, min2, s2);
1070                 else
1071                         sprintf(buf1, "%02d%02d-%02d%02d", hour1, min1,
1072                                  hour2, min2);
1073         }
1074
1075           
1076         lines = (Lines *) text_to_lines(cache_entry->summary, 1);
1077          
1078         if (lines == NULL || lines->s == NULL ||                        
1079                 (cm_strlen(lines->s) == 1 && lines->s[0] == ' '))
1080                 buf2[0] = '\0';
1081         else
1082                 sprintf(buf2, " %s", lines->s);
1083         destroy_lines(lines);
1084 }
1085
1086 static int
1087 paint_entry(Calendar *c, int x, int y, int maxchars, Paint_cache *cache_entry, XRectangle *rect)
1088 {
1089         XFontSetExtents fontextents;
1090         Props *p = (Props*)c->properties;
1091         int             nlines = 0, dt = get_int_prop(p, CP_DEFAULTDISP);
1092         new_XContext        *xc = c->xcontext;
1093         char            buf1[50], buf2[WHAT_LEN+1];
1094         Week            *w = (Week *)c->view->week_info;
1095         Tick            tick;
1096  
1097         /*
1098          * Write an appointment entry into a day
1099          */
1100  
1101         if (maxchars >= 40)             /* maxed out possible=40 */
1102                 maxchars = 40;
1103                  
1104         buf1[0] = '\0'; buf2[0] = '\0';
1105  
1106         format_entry(cache_entry, buf1, buf2, dt);
1107
1108         tick = cache_entry->start_time;
1109  
1110         if (cache_entry->show_time && !magic_time(tick) && (buf1[0] != '\0')) {
1111                 maxchars = gr_nchars(w->day_width - 5, buf1,c->fonts->boldfont); 
1112                 buf1[min(cm_strlen(buf1), maxchars)] = '\0';
1113                 gr_text(xc, x, y, c->fonts->boldfont, buf1, rect);
1114                 nlines++;
1115                 CalFontExtents(c->fonts->boldfont, &fontextents);
1116                 y += fontextents.max_logical_extent.height;;
1117         }
1118         if (buf2[0] != '\0') {
1119                 maxchars = gr_nchars(w->day_width - 5, buf2, 
1120                                                 c->fonts->viewfont);
1121                 buf2[min(cm_strlen(buf2), maxchars)] = '\0';
1122                 gr_text(xc, x, y, c->fonts->viewfont, buf2, rect);
1123                 nlines++;
1124         }
1125  
1126         return(nlines);
1127 }
1128
1129 static void
1130 fill_day(Calendar *c, Week *w, int x, int y, int day, 
1131                 Paint_cache *cache, int a_total, XRectangle *rect)
1132 {
1133         CSA_return_code stat;
1134         Dtcm_appointment *appt;
1135         int    lower = (int)lowerbound(day);
1136         int    upper = (int)next_ndays(day, 1);
1137         int    i, loop, n;
1138         int    nlines = 0;
1139         XFontSetExtents fontextents;
1140         int     char_width;
1141         int     char_height;
1142         int     maxlines;
1143         int     maxchars;
1144         Tick    tick;
1145  
1146         CalFontExtents(w->small_font, &fontextents);
1147         char_width = fontextents.max_logical_extent.width;
1148         char_height = fontextents.max_logical_extent.height;
1149         maxlines = ((w->day_height - w->label_height) / char_height)- 1;
1150         maxchars = (w->day_width / char_width);
1151 #if 0
1152         x += char_width;
1153 #endif
1154         x += 3;
1155         y += (w->label_height + char_height);
1156  
1157         /*
1158          * Fill in a day with appointments
1159          */
1160
1161         /* loop thru twice, first displaying "no time" appointments, 
1162            and then the others. */
1163
1164         for (loop = 0; loop < 2; loop++) {
1165                 for (i = 0; i < a_total; i++) {
1166         
1167                         if ((cache[i].start_time < lower) || (cache[i].start_time >= upper))
1168                                 continue;
1169         
1170                         if (cache[i].show_time != loop)
1171                                 continue;
1172         
1173                         if (nlines < maxlines) {
1174                                 n = paint_entry(c, x, y, maxchars, &cache[i], rect);
1175                                 y += n * char_height;
1176                                 nlines += n;
1177                         }
1178                 }        
1179         }
1180
1181  
1182 static void
1183 draw_chart(Calendar *c, Week *w, XRectangle *rect)
1184 {
1185         int    x, y;
1186         int     n;
1187         XFontSetExtents fontextents;
1188         int     char_height;
1189         char    label[5];
1190         new_XContext *xc = c->xcontext;
1191         Props *p = (Props*)c->properties;
1192         DisplayType dt = get_int_prop(p, CP_DEFAULTDISP);
1193
1194         CalFontExtents(w->font, &fontextents);
1195         char_height = fontextents.max_logical_extent.height;
1196  
1197         /*
1198          * Draw chart. We first draw all the lines, then the labels
1199          * so that Xlib can batch the lines into 1 X request
1200          */
1201  
1202         /* Draw horizontal lines for time */
1203         x = w->chart_x;
1204         y = w->chart_y;
1205         for (n = w->begin_hour; n <= w->end_hour; n++) {
1206                 gr_draw_line(xc, x, y, x + w->chart_width, y, gr_solid, rect);                y += w->chart_hour_height + w->add_pixels;
1207         }
1208  
1209         /* Draw vertical lines for days */
1210         y = w->chart_y;
1211         for (n = 0; n < 7; n++) {
1212                 gr_draw_line(xc, w->chart_x + (w->chart_day_width * n),
1213                         y, w->chart_x + (w->chart_day_width * n),
1214                         y + w->chart_height, gr_solid, rect);
1215         }
1216  
1217         /*
1218          * Draw box around the whole thing.
1219          */
1220         gr_draw_box(xc, w->chart_x, w->chart_y, w->chart_width,
1221                     w->chart_height, rect);
1222         gr_draw_box(xc, w->chart_x - 1, w->chart_y - 1,
1223                     w->chart_width + 2, w->chart_height + 2, rect);
1224         /* Label horizontal lines with time of day */
1225         x = w->chart_x;
1226         y = w->chart_y;
1227         for (n = w->begin_hour; n <= w->end_hour; n++) {
1228                 if (dt == HOUR12)
1229                         sprintf(label, "%2d", n > 12 ? n - 12 : n);
1230                 else
1231                         sprintf(label, "%2d", n);
1232                 gr_text(xc, w->x - 8, y+3,
1233                         w->small_bold_font, label, rect);
1234                 y += w->chart_hour_height + w->add_pixels;
1235         }
1236  
1237         /* Label  vertical lines with day labels */
1238         y = w->chart_y;
1239         for (n = 0; n < 7; n++) {
1240                 x = gr_center(w->chart_day_width, days3[n+1], w->font);
1241                 gr_text(xc, w->chart_x + (w->chart_day_width * n) + x,
1242                         y - char_height / 2, w->font, days3[n+1], rect);
1243         }
1244  
1245 }
1246
1247 extern void
1248 week_event(XEvent *event)
1249 {
1250         Calendar *c = calendar;
1251         Props *p = (Props*)c->properties;
1252         static int lastdate;
1253         static XEvent lastevent;
1254         int x, y, i, j, hr, id;
1255         Week    *w = (Week *)c->view->week_info;
1256         Selection *wsel;
1257         Editor *e = (Editor *)c->editor;
1258         ToDo *t = (ToDo*)c->todo;
1259         GEditor *ge = (GEditor*)c->geditor;
1260         static int lastrow, lastcol;
1261         int row, col;
1262
1263         x       = event->xbutton.x;
1264         y       = event->xbutton.y;
1265         wsel    = (Selection *)w->current_selection;
1266
1267         switch(event->type) {
1268         case MotionNotify:
1269                 j = week_xytoclock(w, x, y);
1270                 (col = dow(j)) == 0 ? col = 6 : col--;
1271                 if (inchart(w, x, y))
1272                         row = (double)(y - w->chart_y) /
1273                                 (double)(w->chart_hour_height+ w->add_pixels);
1274                 else
1275                         row = wsel->row;
1276                 if (j != lastdate || lastcol != col || lastrow != row) {
1277                         calendar_deselect(c);
1278                         wsel->row = row;
1279                         wsel->col = col;
1280                         if (j > 0) {
1281                                 c->view->olddate = c->view->date;
1282                                 c->view->date = j;
1283                                 calendar_select(c, weekdaySelect, NULL);
1284                         }
1285                 }
1286                 lastcol = wsel->col;
1287                 lastrow = wsel->row;
1288                 lastdate = c->view->date;
1289                 break;
1290      case ButtonPress:
1291                 j = week_xytoclock(w, x, y);
1292                 if (j == -1)
1293                         return;
1294                 hr = (inchart(w, x, y)) ? week_xytohour(w, x, y) : (wsel->row + w->begin_hour);
1295
1296                 if (ds_is_double_click(&lastevent, event)) {
1297                         _DtTurnOnHourGlass(c->frame);
1298                         if (j == lastdate) {
1299                                 show_editor(c, next_nhours(j, hr), next_nhours(j, hr + 1), False);
1300                         }
1301                         else if (editor_showing(e)) {
1302                                 set_editor_defaults(e, next_nhours(j, hr), next_nhours(j, hr + 1), False);
1303                                 add_all_appt(e);
1304                         }
1305                         _DtTurnOffHourGlass(c->frame);
1306                 }
1307                 else {
1308                         calendar_deselect(c);
1309                         (wsel->col = dow(j)) == 0 ?
1310                                 wsel->col = 6 : wsel->col--;
1311                         if (inchart(w, x, y))
1312                                 wsel->row = (double)(y - w->chart_y) /
1313                                 (double)(w->chart_hour_height + w->add_pixels);
1314                         if (j > 0) {
1315                                 c->view->olddate = c->view->date;
1316                                 c->view->date = j;
1317                                 calendar_select(c, weekdaySelect, NULL);
1318                         }
1319                         if (editor_showing(e)) {
1320                                 set_editor_defaults(e, next_nhours(j, hr), next_nhours(j, hr + 1), False);
1321                                 add_all_appt(e);
1322                         }
1323                         if (todo_showing(t)) {
1324                                 set_todo_defaults(t);
1325                                 add_all_todo(t);
1326                         }
1327                         if (geditor_showing(ge)) {
1328                                 set_geditor_defaults(ge, 0, 0);
1329                                 add_all_gappt(ge);
1330                         }
1331                 }
1332                 lastdate = c->view->date;
1333                 lastcol = wsel->col;
1334                 lastrow = wsel->row;
1335                 break;
1336         default: 
1337                 break;
1338         };             /* switch */
1339         lastevent = *event;
1340 }
1341
1342 static int
1343 week_xytohour(Week *w, int x, int y)
1344 {
1345         if (!inchart(w, x, y))
1346                 return(-1);
1347         y -= w->chart_y;
1348         return(w->begin_hour + ((double)y /
1349                 (double)(w->chart_hour_height + w->add_pixels)));
1350 }
1351
1352 static int
1353 week_xytoclock(Week *w, int x, int y)
1354 {
1355         int     dow;
1356
1357         /*
1358          * Convert the x and y location on the week view to a date
1359          */
1360         if (inchart(w, x, y)) {
1361                 dow = (double)(x - w->chart_x)/(double)w->chart_day_width;
1362         } else if (inweek(w, x, y)) {
1363                 if (y < w->y + w->day_height)
1364                         dow = (x - w->x)/w->day_width;
1365                 else
1366                         dow = (x - w->x - 3 * w->day_width)/w->day_width
1367 + 5;
1368         } else
1369                 return(0);
1370
1371         if (w->start_date == get_bot()) {
1372                 if (dow < 3)
1373                         return(-1);
1374                 else
1375                         dow = dow - 3;
1376         }
1377         else if (w->start_date == (time_t)last_ndays(get_eot(), 3)) {
1378                 if (dow > 3) {
1379                         return(-1);
1380                 }
1381         }
1382
1383         return(w->start_date + dow * daysec);
1384 }
1385
1386
1387 /*
1388  * Handler for "hot" buttons to navigate to day view
1389  */
1390 static void
1391 quick_button_cb(Widget widget, XtPointer client, XtPointer call)
1392 {
1393         Calendar *c = calendar;
1394         Week    *w = (Week *)c->view->week_info;
1395         int dow = (int) (intptr_t) client;
1396         char buf[BUFSIZ];
1397
1398         if (c->view->date != get_bot()) {
1399                 c->view->olddate = c->view->date;
1400                 c->view->date = w->start_date + dow * daysec;
1401         }
1402         calendar_select(c, weekhotboxSelect, NULL);
1403  
1404         cleanup_after_weekview(c);
1405
1406         c->view->glance = dayGlance;
1407         init_mo(c);
1408         (void)init_dayview(c);
1409
1410         paint_day(c);
1411
1412 }
1413
1414 /*
1415  * Handle labeling, positioning and managing of a given hotbutton
1416  */
1417 static void
1418 display_hot_btn(
1419         Calendar        *c,
1420         int              n,     /* which button (0..6) */
1421         int              date)  /* what day in month */
1422 {
1423         Week            *w = (Week *)c->view->week_info;
1424         Widget           btn = w->hot_button[n];
1425         XmString         str;
1426         char             buf[BUFSIZ];
1427
1428         /* REVISIT: I18N wrap the string for day name */
1429         sprintf(buf, "%s %d", n == 6 ? days[0] : days[n + 1], date);
1430
1431         str = XmStringCreateLocalized(buf);
1432         XtVaSetValues(btn, XmNlabelString, str, NULL);
1433         XmStringFree(str);
1434
1435         if (!XtIsManaged(btn)) {
1436                 XtManageChild(btn);
1437         }
1438 }
1439
1440 /*
1441  * Clear previous hot buttons because we can't display dates
1442  *  prior to Jan 1, 1970
1443  */
1444 static void
1445 clear_hot_btn(Calendar *c,
1446                 int n)                  /* which button (0..6) */
1447 {
1448         Week *w = (Week *)c->view->week_info;
1449         Widget btn = w->hot_button[n];
1450  
1451         if (XtIsManaged(btn)) {
1452                 XtUnmanageChild(btn);
1453         }
1454 }
1455