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