Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / dtcm / browser.c
1 /* $TOG: browser.c /main/10 1999/02/23 09:42:01 mgreess $ */
2 /*
3  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
4  *  (c) Copyright 1993, 1994 International Business Machines Corp.
5  *  (c) Copyright 1993, 1994 Novell, Inc.
6  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
7  */
8
9 #include <EUSCompat.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <csa.h>
13 #include <X11/Xlib.h>
14 #include <Xm/Xm.h>
15 #include <Xm/DrawingA.h>
16 #include <Xm/Form.h>
17 #include <Xm/LabelG.h>
18 #include <Xm/List.h>
19 #include <Xm/DialogS.h>
20 #include <Xm/PanedW.h>
21 #include <Xm/Protocols.h>
22 #include <Xm/PushBG.h>
23 #include <Xm/RowColumn.h>
24 #include <Xm/Scale.h>
25 #include <Xm/SeparatoG.h>
26 #include <Xm/Text.h>
27 #include <Xm/ToggleBG.h>
28 #include <Xm/SashP.h>
29 #include <Dt/HourGlass.h>
30 #include <Tt/tttk.h>
31 #include "getdate.h"
32 #include "calendar.h"
33 #include "util.h"
34 #include "misc.h"
35 #include "timeops.h"
36 #include "browser.h"
37 #include "blist.h"
38 #include "format.h"
39 #include "datefield.h"
40 #include "props.h"
41 #include "props_pu.h"
42 #include "editor.h"
43 #include "group_editor.h"
44 #include "select.h"
45 #include "help.h"
46 #ifdef FNS
47 #include "cmfns.h"
48 #endif
49
50 #define DATESIZ 40
51
52 extern int debug;
53
54 static void mb_resize_proc(Widget, XtPointer, XtPointer);
55 static void cancel_cb(Widget, XtPointer, XtPointer);
56 static void popup_cb(Widget, XtPointer, XtPointer);
57 static void init_browser(Calendar *);
58 static void bcanvas_repaint(Widget, XtPointer, XtPointer);
59 static void bcanvas_event(Widget, XtPointer, XtPointer);
60 static void goto_date_cb(Widget, XtPointer, XtPointer);
61 static void browselist_from_browser(Widget, XtPointer, XtPointer);
62 static void gotomenu_cb(Widget, XtPointer, XtPointer);
63 static void schedule_cb(Widget, XtPointer, XtPointer);
64 static void mail_cb(Widget, XtPointer, XtPointer);
65 static void mb_box_notify(Widget, XtPointer, XtPointer);
66 static void mb_update_handler(CSA_session_handle, CSA_flags, CSA_buffer,
67                               CSA_buffer, CSA_extension *);
68
69 extern void scrub_attr_list(Dtcm_appointment *);
70
71 static void
72 mb_init_array(Browser *b, int begin, int end) {
73         b->segs_in_array = BOX_SEG * (end - begin) * 7;
74         b->multi_array = (char*)ckalloc(b->segs_in_array);
75 }
76
77 static void
78 reset_ticks(Calendar *c, Boolean use_sel_idx) {
79         int     beg, end;
80         Props   *p = (Props *)c->properties;
81         Browser *b = (Browser*)c->browser;
82
83         beg = get_int_prop(p, CP_DAYBEGIN);
84         end = get_int_prop(p, CP_DAYEND);
85
86         if (b->date <= get_bot()) {
87                 b->date = get_bot();
88                 if (b->col_sel > 0)
89                         b->col_sel = b->col_sel - 4;
90         }
91         if ((b->begin_week_tick = first_dow(b->date)) < get_bot())
92                 b->begin_week_tick = get_bot();
93         if (use_sel_idx) {
94                 b->begin_day_tick =
95                         next_ndays(b->begin_week_tick, b->col_sel);
96                 b->begin_hr_tick =
97                         next_nhours(b->begin_day_tick, beg + b->row_sel);
98         } else {
99                 b->begin_day_tick = lowerbound(b->date);
100                 b->begin_hr_tick = next_nhours(b->begin_day_tick, beg);
101         }
102         b->end_day_tick = upperbound(b->begin_day_tick);
103         b->end_hr_tick = next_nhours(b->begin_hr_tick, 1);
104 }
105
106 extern void
107 br_display(Calendar *c) {
108         int             i, *pos_list = NULL, pos_cnt;
109         Browser         *b = (Browser *)c->browser;
110         BlistData       *bd;
111         Browselist      *bl = (Browselist *)c->browselist;
112         void            mb_update_array();
113
114         if (!b)
115                 return;
116
117         for (i = 0; i < b->segs_in_array; i++)
118                 b->multi_array[i] = 0;
119         b->add_to_array = True;
120
121         XmListGetSelectedPos(b->browse_list, &pos_list, &pos_cnt);
122         for (i = 0; i < pos_cnt; i++) {
123                 bd = (BlistData *)CmDataListGetData(bl->blist_data,
124                                                     pos_list[i]);
125
126                 destroy_paint_cache(bd->cache, bd->cache_size);
127                 bd->cache = NULL;
128                 bd->cache_size = 0;
129
130                 if (bd)
131                         mb_update_array(bd->name, c);
132         }
133         if (pos_list)
134                 XtFree((XtPointer)pos_list);
135
136         mb_refresh_canvas(b, c);
137 }
138
139 static void
140 invalid_date_msg(Calendar *c, Widget widget)
141 {
142         Browser *b = (Browser*)c->browser;
143         char *title = XtNewString(catgets(c->DT_catd, 1, 1070, 
144                                   "Calendar : Error - Compare Calendars"));
145         char *text = XtNewString(catgets(c->DT_catd, 1, 20,
146                                          "Invalid Date In Go To Field."));
147         char *ident = XtNewString(catgets(c->DT_catd, 1, 95, "Continue"));
148
149         dialog_popup(b->frame,
150                 DIALOG_TITLE, title,
151                 DIALOG_TEXT, text,
152                 BUTTON_IDENT, 1, ident,
153                 DIALOG_IMAGE, ((Props_pu *)c->properties_pu)->xm_error_pixmap,
154                 NULL);
155
156         XtFree(ident);
157         XtFree(text);
158         XtFree(title);
159 }
160
161 void 
162 set_entry_date(Calendar *c) {
163         char *date = NULL;
164         Browser *b;
165         Props *p;
166         OrderingType ot;
167         SeparatorType st;
168         Tick tick;
169
170         b = (Browser*)c->browser;
171         p = (Props*)c->properties;
172         ot = get_int_prop(p, CP_DATEORDERING);
173         st = get_int_prop(p, CP_DATESEPARATOR);
174
175         date = get_date_from_widget(c->view->date, b->datetext, ot, st);
176         if (date == NULL ) {
177                 invalid_date_msg(c, b->message_text);
178                 return;
179         }
180
181         tick = cm_getdate(date, NULL);
182         if (!timeok(tick)) {
183                 invalid_date_msg(c, b->message_text);
184                 return;
185         }
186
187         b->date = tick;
188         reset_ticks(c, False);
189         br_display(c);
190 }
191
192 /*
193  * A note about the browser:
194  * This custom dialog is built using two nested PanedWindowWidgets.
195  * The first child of the outer pane is itself paned (the other
196  * child is the dialog's action area).  The inner pane is movable
197  * to allow users to reproportion the calendar list versus the free
198  * time chart.  Most of the useful widget handles are stored in the
199  * Browser structure allocated and returned from here.
200  */
201 extern void
202 make_browser(Calendar *c)
203 {
204         Browser         *b;
205         Props           *p = (Props*) c->properties;
206         Widget          separator1;
207         Dimension       w, h, height;
208         XmString        xmstr;
209         XmString goto_label, prev_week, this_week, next_week, prev_month, next_month;
210         int             num_children;
211         Widget          *children;
212         OrderingType    ord_t = get_int_prop(p, CP_DATEORDERING);
213         SeparatorType   sep_t = get_int_prop(p, CP_DATESEPARATOR);
214         char            buf[BUFSIZ];
215         Widget          text_field_form;
216         int             outpane_width, outpane_height;
217         int             upform_min,item_count;
218         char            *title;
219
220         if (c->browser == NULL) {
221                 c->browser = (caddr_t)ckalloc(sizeof(Browser));
222                 b = (Browser*)c->browser;
223         }
224         else
225                 b = (Browser*)c->browser;
226
227         b->date = c->view->date;
228
229         mb_init_array(b, get_int_prop(p, CP_DAYBEGIN), get_int_prop(p, CP_DAYEND));
230         b->current_selection = (caddr_t) ckalloc(sizeof(Selection));
231
232         /* if the screen is small adjust the max size for width and height
233          * so the shell can be moved up and down using window facility
234          */
235         if ((WidthOfScreen(XtScreen(c->frame)) < 360) ||
236             (HeightOfScreen(XtScreen(c->frame)) < 600 ))
237         {
238                 outpane_width = 300;
239                 outpane_height = 430;
240                 item_count      = 3;
241                 upform_min      = 120;
242         }
243         else
244         {
245                 outpane_width = 360;
246                 outpane_height = 600;
247                 item_count      = 8;
248                 upform_min      = 200;
249         }
250
251         title = XtNewString(catgets(c->DT_catd, 1, 1010, 
252                                         "Calendar : Compare Calendars"));
253         b->frame = XtVaCreatePopupShell("frame",
254                 xmDialogShellWidgetClass, c->frame,
255                 XmNtitle, title,
256                 XmNallowShellResize, True,
257                 XmNmappedWhenManaged, False, 
258                 XmNdeleteResponse, XmDO_NOTHING,
259                 NULL);
260         XtFree(title);
261         XtAddCallback(b->frame, XmNpopupCallback, popup_cb, (XtPointer)c);
262
263
264         /*
265          * Create the outer pane, whose upper part will hold a
266          * nested pane, and whose lower part will hold the actions
267          * and message area
268          */
269         b->outer_pane = XtVaCreateManagedWidget("outerPane",
270                 xmPanedWindowWidgetClass, b->frame,
271                 XmNsashHeight, 1,
272                 XmNsashWidth, 1,
273                 XmNwidth, outpane_width,
274                 XmNheight, outpane_height,
275                 NULL);
276
277         b->inner_pane = XtVaCreateManagedWidget("innerPane",
278                 xmPanedWindowWidgetClass, b->outer_pane,
279                 XmNallowResize, True,
280                 NULL);
281
282         b->upper_form = XtVaCreateManagedWidget("upperForm",
283                 xmFormWidgetClass, b->inner_pane,
284                 XmNallowResize, True,
285                 XmNpaneMinimum, upform_min,
286                 NULL);
287
288         xmstr = XmStringCreateLocalized(catgets(c->DT_catd, 1, 22, "Browse Menu Items"));
289         b->list_label = XtVaCreateWidget("browseMenuLabel", 
290                 xmLabelGadgetClass, b->upper_form,
291                 XmNlabelString, xmstr,
292                 XmNleftAttachment, XmATTACH_FORM,
293                 XmNtopAttachment, XmATTACH_FORM,
294                 XmNtopOffset, 10,
295                 NULL);
296         XmStringFree(xmstr);
297
298         xmstr = XmStringCreateLocalized(catgets(c->DT_catd, 1, 21, "Edit List..."));
299         b->edit_list = XtVaCreateWidget("editList",
300                 xmPushButtonGadgetClass, b->upper_form,
301                 XmNlabelString, xmstr,
302                 XmNtopAttachment, XmATTACH_FORM,
303                 XmNrightAttachment, XmATTACH_FORM,
304                 XmNleftAttachment, XmATTACH_WIDGET,
305                 XmNleftWidget, b->list_label,
306                 XmNleftOffset, 80,
307                 NULL);
308         XmStringFree(xmstr);
309
310         XtAddCallback(b->edit_list, XmNactivateCallback,
311                 browselist_from_browser, (XtPointer)c);
312
313         b->browse_list = (Widget)XmCreateScrolledList(b->upper_form,
314                 "browseList", NULL, 0);
315         XtAddCallback(b->browse_list,
316                 XmNmultipleSelectionCallback, mb_box_notify, (XtPointer)c);
317         XtAddCallback(b->browse_list,
318                 XmNdefaultActionCallback, mb_box_notify, (XtPointer)c);
319         XtVaSetValues(b->browse_list,
320                 XmNselectionPolicy, XmMULTIPLE_SELECT,
321                 XmNvisibleItemCount, item_count,
322                 NULL);
323
324         b->browse_list_sw = XtParent(b->browse_list);
325         XtVaSetValues(b->browse_list_sw,
326                 XmNtopAttachment, XmATTACH_WIDGET,
327                 XmNtopWidget, b->edit_list,
328                 XmNleftAttachment, XmATTACH_FORM,
329                 XmNleftOffset, 20,
330                 XmNrightAttachment, XmATTACH_FORM,
331                 XmNrightOffset, 20,
332                 XmNbottomAttachment, XmATTACH_FORM,
333                 XmNvisualPolicy, XmVARIABLE,
334                 NULL);
335
336
337         /*
338          * Create the "go to" option menu for time navigation
339          */
340         prev_week =
341            XmStringCreateLocalized(catgets(c->DT_catd, 1, 23, "Prev Week"));
342         this_week =
343            XmStringCreateLocalized(catgets(c->DT_catd, 1, 24, "This Week"));
344         next_week =
345            XmStringCreateLocalized(catgets(c->DT_catd, 1, 25, "Next Week"));
346         prev_month =
347            XmStringCreateLocalized(catgets(c->DT_catd, 1, 26, "Prev Month"));
348         next_month =
349            XmStringCreateLocalized(catgets(c->DT_catd, 1, 27, "Next Month"));
350         goto_label =
351            XmStringCreateLocalized(catgets(c->DT_catd, 1, 28, "Go To:"));
352
353         /*
354          * remember - this returns a RowColumn widget!
355          */
356         b->gotomenu = XmVaCreateSimpleOptionMenu(b->upper_form,
357                 "goToOptionMenu", goto_label, NULL, 0, gotomenu_cb,
358                 XmVaPUSHBUTTON, prev_week, NULL, NULL, NULL,
359                 XmVaPUSHBUTTON, this_week, NULL, NULL, NULL,
360                 XmVaPUSHBUTTON, next_week, NULL, NULL, NULL,
361                 XmVaPUSHBUTTON, prev_month, NULL, NULL, NULL,
362                 XmVaPUSHBUTTON, next_month, NULL, NULL, NULL,
363                 XmNbottomAttachment, XmATTACH_FORM,
364                 XmNbottomOffset, 5,
365                 XmNleftAttachment, XmATTACH_FORM,
366                 XmNleftOffset, 20,
367                 XmNuserData, c,
368                 XmNnavigationType, XmTAB_GROUP,
369                 NULL);
370         XmStringFree(prev_week);
371         XmStringFree(this_week);
372         XmStringFree(next_week);
373         XmStringFree(prev_month);
374         XmStringFree(next_month);
375         XmStringFree(goto_label);
376
377         text_field_form = XtVaCreateManagedWidget("text_field_form",
378                 xmFormWidgetClass, b->upper_form,
379                 XmNbottomAttachment, XmATTACH_FORM,
380                 XmNbottomOffset, 5,
381                 XmNleftAttachment, XmATTACH_WIDGET,
382                 XmNleftWidget, b->gotomenu,
383                 NULL);
384
385         b->datetext = XtVaCreateWidget("dateText",
386                 xmTextWidgetClass, text_field_form,
387                 NULL);
388         XtAddCallback(b->datetext, XmNactivateCallback, goto_date_cb, 
389                                                                 (XtPointer)c);
390
391         /*
392          * We can now calc the proper offset for the bottom of scrolled
393          * list - allow for a small margin above and below the text field.
394          */
395         XtVaGetValues(b->datetext, XmNheight, &height, NULL);
396         XtVaSetValues(b->browse_list_sw, XmNbottomOffset, (height + 10), NULL);
397
398         b->lower_form = XtVaCreateManagedWidget("lowerForm",
399                 xmFormWidgetClass, b->inner_pane,
400                 XmNallowResize, True,
401                 XmNpaneMinimum, 200,
402                 XmNtraversalOn, False,
403                 NULL);
404
405         /*
406          * create drawing area for chart
407          */
408         b->canvas = XtVaCreateManagedWidget("canvas", xmDrawingAreaWidgetClass,
409                 b->lower_form,
410                 XmNtopAttachment, XmATTACH_FORM,
411                 XmNleftAttachment, XmATTACH_FORM,
412                 XmNrightAttachment, XmATTACH_FORM,
413                 XmNbottomAttachment, XmATTACH_FORM,
414                 XmNtraversalOn, False,
415                 NULL);
416         b->xcontext = gr_create_xcontext(c, b->canvas, gr_color,
417                 c->xcontext->app);
418
419         XtVaSetValues(b->canvas, XmNheight, 300, NULL);
420
421         XtAddCallback(b->canvas, XmNresizeCallback, mb_resize_proc, (XtPointer)c);
422         XtAddCallback(b->canvas, XmNinputCallback, bcanvas_event, (XtPointer)c);
423         XtAddCallback(b->canvas, XmNexposeCallback, bcanvas_repaint, (XtPointer)c);
424
425         /*
426          * Create action area of the dialog
427          */
428         b->action = XtVaCreateWidget("action",
429                 xmFormWidgetClass, b->outer_pane,
430                 NULL);
431
432         xmstr = XmStringCreateLocalized(catgets(c->DT_catd, 1, 29, "Schedule..."));
433         b->schedule = XtVaCreateWidget("schedule",
434                 xmPushButtonGadgetClass, b->action,
435                 XmNlabelString, xmstr,
436                 XmNtopAttachment, XmATTACH_FORM,
437                 XmNleftAttachment, XmATTACH_FORM,
438                 NULL);
439         XmStringFree(xmstr);
440         XtAddCallback(b->schedule, XmNactivateCallback, schedule_cb, (XtPointer)c);
441
442         xmstr = XmStringCreateLocalized(catgets(c->DT_catd, 1, 30, "Mail..."));
443         b->mail = XtVaCreateWidget("mail",
444                 xmPushButtonGadgetClass, b->action,
445                 XmNlabelString, xmstr,
446                 XmNleftAttachment, XmATTACH_WIDGET,
447                 XmNleftWidget, b->schedule,
448                 XmNtopAttachment, XmATTACH_FORM,
449                 NULL);
450         XmStringFree(xmstr);
451         XtAddCallback(b->mail, XmNactivateCallback, mail_cb, (XtPointer)c);
452
453         XtSetSensitive(b->mail, c->tt_procid == NULL ? False : True);
454
455         xmstr = XmStringCreateLocalized(catgets(c->DT_catd, 1, 923, "Cancel"));
456         b->cancel = XtVaCreateWidget("cancel",
457                 xmPushButtonGadgetClass, b->action,
458                 XmNlabelString, xmstr,
459                 XmNleftAttachment, XmATTACH_WIDGET,
460                 XmNleftWidget, b->mail,
461                 XmNtopAttachment, XmATTACH_FORM,
462                 NULL);
463         XmStringFree(xmstr);
464         XtAddCallback(b->cancel, XmNactivateCallback, cancel_cb, (XtPointer)c);
465
466         xmstr = XmStringCreateLocalized(catgets(c->DT_catd, 1, 77, "Help"));
467         b->helpbutton = XtVaCreateWidget("help",
468                 xmPushButtonGadgetClass, b->action,
469                 XmNlabelString, xmstr,
470                 XmNleftAttachment, XmATTACH_WIDGET,
471                 XmNleftWidget, b->cancel,
472                 XmNtopAttachment, XmATTACH_FORM,
473                 NULL);
474         XmStringFree(xmstr);
475         XtAddCallback(b->helpbutton, XmNactivateCallback,
476                 (XtCallbackProc)help_cb, COMPARE_CALS_HELP_BUTTON);
477         XtAddCallback(b->action, XmNhelpCallback,
478                 (XtCallbackProc)help_cb, (XtPointer) COMPARE_CALS_HELP_BUTTON);
479
480         b->message_text = XtVaCreateWidget("message",
481                 xmLabelGadgetClass, b->action,
482                 XmNtopAttachment, XmATTACH_WIDGET,
483                 XmNtopWidget, b->schedule,
484                 XmNleftAttachment, XmATTACH_FORM,
485                 XmNrightAttachment, XmATTACH_FORM,
486                 XmNbottomAttachment, XmATTACH_FORM,
487                 XmNalignment, XmALIGNMENT_BEGINNING,
488                 NULL);
489
490         /*
491          * Fix the size of action area
492          */
493         XtVaGetValues(b->schedule, XmNheight, &height, NULL);
494         XtVaSetValues(b->action,
495                 XmNpaneMaximum, (2*height),
496                 XmNpaneMinimum, (2*height),
497                 NULL);
498
499
500         XtVaGetValues(b->outer_pane,
501                 XmNchildren, &children,
502                 XmNnumChildren, &num_children,
503                 NULL);
504
505         while (num_children-- > 0) {
506                 if (XmIsSash(children[num_children])) {
507                                 XtVaSetValues(children[num_children], XmNtraversalOn, False, NULL);
508                 }
509         }
510
511         XtManageChild(b->edit_list);
512         XtManageChild(b->list_label);
513         XtManageChild(b->browse_list);
514         XtManageChild(b->gotomenu);
515         XtManageChild(b->datetext);
516         XtManageChild(b->schedule);
517         XtManageChild(b->mail);
518         XtManageChild(b->cancel);
519         XtManageChild(b->helpbutton);
520         XtManageChild(b->message_text);
521         XtManageChild(b->action);
522
523         gr_init(b->xcontext, b->canvas);
524
525         format_tick(b->date, ord_t, sep_t, buf);
526         XmTextSetString(b->datetext, buf);
527
528         set_entry_date(c);
529
530         /*
531          * set default button for dialog
532          */
533         XtVaSetValues(b->action, XmNdefaultButton, b->schedule, NULL);
534         XtVaSetValues(b->upper_form, XmNdefaultButton, b->schedule, NULL);
535
536         /* We don't want a ``return'' in the text field to trigger the
537          * default action so we create a form around the text field and
538          * designate the text-field as the default button.
539          */
540         XtVaSetValues(text_field_form, XmNdefaultButton, b->datetext, NULL);
541
542         XtVaSetValues(b->action, XmNcancelButton, b->cancel, NULL);
543         XtVaSetValues(b->upper_form, XmNcancelButton, b->cancel, NULL);
544         XmProcessTraversal(b->schedule, XmTRAVERSE_CURRENT);
545         XtVaSetValues(b->action, XmNinitialFocus, b->schedule, NULL);
546
547         XtVaSetValues(b->frame, XmNmappedWhenManaged, True, NULL);
548         XtRealizeWidget(b->frame);
549         /*
550          * Enforce a "reasonable" size.
551          * too narrow and the buttons are erased
552          * too short and the chart will look smashed
553         XtVaSetValues(b->frame,
554                 XmNminWidth, 300,
555                 XmNminHeight, 600,
556                 NULL);
557          */
558
559         /*
560         * Add a WM protocol callback to handle the
561         * case where the window manager closes the dialog.
562         * Pass the calendar ptr through client data to allow
563         * the callback to get at the shell and destroy it.
564         */
565         setup_quit_handler(b->frame, cancel_cb, (XtPointer)c);
566
567         init_browser(c);
568 }
569
570 static void 
571 browselist_from_browser(Widget w, XtPointer client_data, XtPointer call_data)
572 {
573         Calendar *c = (Calendar *)client_data;
574         Browselist      *bl;
575
576         bl = (Browselist *)c->browselist;
577
578         show_browselist(c);
579 }
580
581 static void
582 goto_date_cb(Widget w, XtPointer client_data, XtPointer call_data) 
583 {
584         Calendar *c = (Calendar *)client_data;
585         Browser *b;
586
587         set_entry_date(c);
588 }
589
590 static void
591 goto_unit(Calendar *c, int item_no)
592 {
593         char buf[DATESIZ];
594         Browser *b;
595         Props *p;
596         OrderingType    ord_t;
597         SeparatorType   sep_t;
598
599         b = (Browser*)c->browser;
600         p = (Props*)c->properties;
601
602         switch (item_no + 1) {
603         case MB_PREVWEEK:
604                 b->date = last_ndays(b->date, 7);
605                 break;
606         case MB_THISWEEK:
607                 b->date = now();
608                 break;
609         case MB_NEXTWEEK:
610                 b->date = next_ndays(b->date, 7);
611                 break;
612         case MB_NEXTMONTH:
613                 b->date = next_ndays(b->date, 28);
614                 break;
615         case MB_PREVMONTH:
616                 b->date = last_ndays(b->date, 28);
617                 break;
618         default:
619                 b->date = now();
620                 break;
621         }
622
623         reset_ticks(c, False);
624
625         ord_t = get_int_prop(p, CP_DATEORDERING);
626         sep_t = get_int_prop(p, CP_DATESEPARATOR);
627
628         format_tick(b->date, ord_t, sep_t, buf);
629         XmTextSetString(b->datetext, buf);
630         br_display(c);
631 }
632
633 static void
634 bcanvas_repaint(Widget w, XtPointer client_data, XtPointer call_data)
635 {
636         Calendar *c = (Calendar *)client_data;
637         Browser *b;
638         XRectangle clip;
639         XEvent ev;
640         new_XContext    *xc;
641         XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)call_data;
642
643         /* repair in full on final exposure (crude optimization scheme) */
644         if (cbs->event->xexpose.count != 0)
645            return;
646
647         b = (Browser *)c->browser;
648         if ((xc = b->xcontext)==NULL)
649                 return;
650
651         clip.x = 0;
652         clip.y = 0;
653         XtVaGetValues(w, XmNwidth, &clip.width, XmNy, &clip.height, NULL);
654
655         gr_set_clip_rectangles(xc, 0, 0, &clip, 1, Unsorted);
656
657         gr_clear_area(b->xcontext, 0, 0, b->canvas_w, b->canvas_h);
658         gr_clear_clip_rectangles(xc);
659         mb_refresh_canvas(b, c);
660  
661         XSync(XtDisplay(b->canvas), 0);
662 }
663
664 static void
665 mb_display_footermess(Browser *b, Calendar *c)
666 {
667         int num_cals;
668         char buf[BUFSIZ];
669
670         XtVaGetValues(b->browse_list, XmNselectedItemCount, &num_cals, NULL);
671         if (num_cals == 1)
672                 sprintf(buf, "%d %s", num_cals,
673                         catgets(c->DT_catd, 1, 31, "Calendar Displayed"));
674         else
675                 sprintf(buf,  "%d %s", num_cals,
676                         catgets(c->DT_catd, 1, 32, "Calendars Displayed"));
677         set_message(b->message_text, buf);
678 }
679
680 static void
681 browser_to_gaccess_list(Calendar *c) {
682         int             i, pos_cnt, *pos_list = NULL;
683         GEditor         *ge = (GEditor *)c->geditor;
684         Browser         *b = (Browser *)c->browser;
685         BlistData       *bd;
686         Browselist      *bl = (Browselist *)c->browselist;
687
688         /*
689          *  Add selected items to list box in group appt editor
690          */
691         XmListGetSelectedPos(b->browse_list, &pos_list, &pos_cnt);
692         for (i = 0; i < pos_cnt; i++) {
693                 if (bd = (BlistData *)CmDataListGetData(bl->blist_data,
694                                                         pos_list[i]))
695                         add_to_gaccess_list(bd->name, bd->cal_handle,
696                                 bd->user_access, bd->version, ge, True);
697         }
698         add_all_gappt(ge);
699         if (pos_cnt > 0)
700                 XtFree((XtPointer)pos_list);
701 }
702
703 void mb_update_busystatus(Browser *b, Calendar *c)
704 {
705         int                     i, j, r_cnt;
706         CSA_uint32              num_entries;
707         char                    buf[BUFSIZ + 5];
708         Boolean                 match = False;
709         XmString                xmstr;
710         Browselist              *bl = (Browselist *)c->browselist;
711         time_t                  start, stop;
712         CSA_entry_handle        *entries = NULL;
713         BlistData               *bd;
714         CSA_return_code         stat;
715         CSA_enum                *ops;
716         CSA_attribute           *range_attrs;
717         Tick                    start_tick, end_tick;
718
719         j = 1;
720         while (bd = (BlistData *)CmDataListGetData(bl->blist_data, j)) {
721                 if (!XmListPosSelected(b->browse_list, j++))
722                         continue;
723
724                 sprintf(buf, "  %s", bd->name);
725                 start = b->begin_day_tick;
726                 stop = b->end_hr_tick;
727
728                 if (bd->cache == NULL) {
729                         setup_range(&range_attrs, &ops, &r_cnt, start, stop, 
730                                         CSA_TYPE_EVENT, NULL, B_FALSE, bd->version);
731                         stat = csa_list_entries(bd->cal_handle, r_cnt, range_attrs, ops, &num_entries, &entries, NULL);
732                                 free_range(&range_attrs, &ops, r_cnt);
733                         backend_err_msg(b->frame, bd->name, stat,
734                                         ((Props_pu *)c->properties_pu)->xm_error_pixmap);
735                         if (stat != CSA_SUCCESS) {
736                                 csa_free(entries);
737                                 return;
738                         }
739
740                         allocate_paint_cache(entries, num_entries, &bd->cache);
741                         bd->cache_size = num_entries;
742                         csa_free(entries);
743                 }
744
745                 for (i = 0; i < bd->cache_size; i++) {
746
747                         start_tick = bd->cache[i].start_time;
748                         end_tick = bd->cache[i].end_time;
749                         if ((start_tick+1 <= b->end_hr_tick) && 
750                             (end_tick-1 
751                                 >= b->begin_hr_tick)) {
752                                 buf[0] = '*';
753                                 break;
754                         }
755
756                 } /* end for */
757
758                 xmstr = XmStringCreateLocalized(buf);
759                 XmListDeletePos(b->browse_list, j-1);
760                 XmListAddItem(b->browse_list, xmstr, j-1);
761                 XmListSelectPos(b->browse_list, j-1, False);
762                 XmStringFree(xmstr);
763         } /* end while */
764 }
765
766 static void
767 bcanvas_event(Widget w, XtPointer client_data, XtPointer call_data)
768 {
769         Calendar *c = (Calendar *)client_data;
770         Browser *b;
771         XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*) call_data;
772         XEvent  *event = cbs->event;
773         Tick start;
774         static XEvent lastevent;
775         int x, y, boxw, boxh;
776         pr_pos xy;
777
778         if ((event->type == ButtonRelease) || (event == NULL))
779                 return;
780
781         b = (Browser*)c->browser;
782
783         x       = event->xbutton.x;
784         y       = event->xbutton.y;
785
786         switch(event->type) {
787         case ButtonPress:
788                 if (x > b->chart_x && y > b->chart_y &&
789                         x < (b->chart_x + b->chart_width)
790                         && y < (b->chart_y + b->chart_height))
791                 {
792                         browser_deselect(c, b);
793                         b->col_sel = (x - b->chart_x) / b->boxw;
794                         b->row_sel = (y - b->chart_y) / b->boxh;
795                         xy.x = b->col_sel;
796                         xy.y = b->row_sel;
797
798                         /*
799                          *  Don't bring up multi-browser for an invalid date
800                          */
801                         if ((last_ndays(b->date, 2) <= get_bot()) &&
802                             (b->col_sel < 3))
803                                 return;
804                         else if ((next_ndays(b->date, 1) > get_eot()) &&
805                                  (b->col_sel > 3))
806                                 return;
807  
808                         reset_ticks(c, True);
809                         browser_select(c, b, &xy);
810                         if (ds_is_double_click(&lastevent, event)) {
811                                 _DtTurnOnHourGlass(b->frame);
812                                 show_geditor(c, b->begin_hr_tick,
813                                              b->end_hr_tick);
814                                 browser_to_gaccess_list(c);
815                                 _DtTurnOffHourGlass(b->frame);
816                         }
817                         /* add busyicon */
818                         mb_update_busystatus(b, c);
819                 }
820                 c->general->last_canvas_touched = browser;
821         };
822         lastevent = *event;
823 }
824
825 static void
826 schedule_cb(Widget w, XtPointer client_data, XtPointer call_data)
827 {
828         Calendar *c = (Calendar *)client_data;
829         Browser *b;
830         Props *p;
831         OrderingType    ord_t;
832         SeparatorType   sep_t;
833         XmPushButtonCallbackStruct *cbs = (XmPushButtonCallbackStruct*) call_data;
834
835         b = (Browser *)c->browser;
836         p = (Props *)c->properties;
837         _DtTurnOnHourGlass(b->frame);
838         ord_t = get_int_prop(p, CP_DATEORDERING);
839         sep_t = get_int_prop(p, CP_DATESEPARATOR);
840         show_geditor(c, b->begin_hr_tick, b->end_hr_tick);
841         browser_to_gaccess_list(c);
842         _DtTurnOffHourGlass(b->frame);
843 }
844
845
846 static char *
847 get_mail_address_list(Calendar *c) {
848         int             i, *pos_list = NULL, pos_cnt;
849         Browser         *b = (Browser *)c->browser;
850         BlistData       *bd;
851         Browselist      *bl = (Browselist *)c->browselist;
852         int             address_len;
853         char            *address;
854
855
856         XmListGetSelectedPos(b->browse_list, &pos_list, &pos_cnt);
857         for (i = 0, address_len = 0; i < pos_cnt; i++) {
858                 bd = (BlistData *)CmDataListGetData(bl->blist_data,
859                                                     pos_list[i]);
860                 if (bd)
861                         address_len += strlen(bd->name) + 1;
862         }
863
864         address = calloc(address_len+1, 1);
865         address[0] = NULL;
866
867         for (i = 0; i < pos_cnt; i++) {
868                 bd = (BlistData *)CmDataListGetData(bl->blist_data,
869                                                     pos_list[i]);
870                 if (bd) {
871                         strcat(address, bd->name);
872                         strcat(address, " ");
873                 }
874         }
875         if (pos_list)
876                 XtFree((XtPointer)pos_list);
877
878         return(address);
879 }
880
881 static Tt_message
882 reply_cb(Tt_message m, void *c_data, Tttk_op op, unsigned char *contents, int len, char *file)
883 {
884         char *client_procID = tt_message_handler(m);
885         if ( debug && (client_procID != NULL) ) {
886                 fprintf(stderr, "DEBUG: reply_cb():client_procID = %s\n", client_procID);
887                 fprintf(stderr, "DEBUG: reply_cb():message_op = %s\n", tt_message_op(m));
888         }
889         return(m);
890 }
891
892 static void
893 mail_cb(Widget w, XtPointer client_data, XtPointer call_data)
894 {
895         Calendar        *c = (Calendar *)client_data;
896         Props           *p = (Props *) c->properties;
897         Browser         *b = (Browser *)c->browser;
898         Tt_message      msg;
899         Tt_status       status;
900         char            *appointment_buf;
901         Dtcm_appointment        *appt;
902         char            *address = get_mail_address_list(c);
903         char            *address_list[1];
904         char            *mime_buf;
905  
906         /* Send ToolTalk message to bring up compose GUI with buffer as attachme
907 nt */
908
909         appt = allocate_appt_struct(appt_write, DATAVER_ARCHIVE, NULL);
910         load_appt_defaults(appt, p);
911
912         appt->time->value->item.date_time_value = malloc(BUFSIZ);
913         _csa_tick_to_iso8601(b->begin_hr_tick, appt->time->value->item.date_time_value);
914         appt->end_time->value->item.date_time_value = malloc(BUFSIZ);
915         _csa_tick_to_iso8601(b->end_hr_tick, appt->end_time->value->item.date_time_value);
916
917         /* set up the start time from the dialog */
918
919         scrub_attr_list(appt);
920  
921         appointment_buf = parse_attrs_to_string(appt, p, attrs_to_string(appt->attrs, appt->count));
922
923         free_appt_struct(&appt);
924
925         address_list[0] = appointment_buf;
926  
927         mime_buf = create_rfc_message(address, "message", address_list, 1);
928
929         msg = ttmedia_load(0, (Ttmedia_load_msg_cb)reply_cb, NULL, TTME_MAIL_EDIT, "RFC_822_MESSAGE", (unsigned char *)mime_buf, strlen(mime_buf), NULL, "dtcm_appointment_attachment", 0);
930  
931         status = tt_ptr_error(msg);
932         if (tt_is_err(status))
933         {
934             fprintf(stderr, "dtcm: ttmedia_load: %s\n",
935                     tt_status_message(status));
936         }
937         else
938         {
939             status = tt_message_send(msg);
940             if (tt_is_err(status))
941                 fprintf(stderr, "dtcm: tt_message_send: %s\n",
942                         tt_status_message(status));
943         }
944
945         free(appointment_buf);
946         free(mime_buf);
947         free(address);
948 }
949
950 static void
951 gotomenu_cb(Widget w, XtPointer data, XtPointer cbs) 
952 {
953         int     item_no = (int) data;
954         /* should really be getting this from the widget */
955         Calendar *c = calendar;
956
957         goto_unit(c, item_no);
958 }
959
960 extern void
961 mb_update_segs(Browser *b, Tick tick, Tick dur, int *start_index, int *end_index)
962 {
963         int     num_segs, i, start, start_hour, duration, nday;
964         Props *p;
965  
966         p = (Props*)calendar->properties;
967  
968         start_hour = hour(tick);
969         
970         if (start_hour >= get_int_prop(p, CP_DAYEND)) {
971                 *start_index = -1;
972                 *end_index = -1;
973                 return;
974         }
975  
976         if (start_hour < get_int_prop(p, CP_DAYBEGIN)) {
977                 start = 0;
978                 duration = dur - ((double)(get_int_prop(p, CP_DAYBEGIN) -
979                  ((double)start_hour + (double)minute(tick)/(double)60))
980                         * (double)hrsec);
981         } else{
982                 start = ((double)(start_hour - get_int_prop(p, CP_DAYBEGIN)) *
983                         (double)60 + (double)minute(tick));
984                 duration = dur;
985         }
986  
987         if (duration <= 0) {
988                 *start_index = -1;
989                 *end_index = -1;
990                 return;
991         }
992         nday = (nday=dow(tick))==0? 6: nday-1;
993         num_segs = (double)start / (double)MINS_IN_SEG;
994         *start_index = (double)start / (double)MINS_IN_SEG + (nday * (b->segs_in_array/7));
995         if (start - (num_segs * MINS_IN_SEG) > 7)
996                 (*start_index)++;
997         num_segs = ((double)duration / (double)60 / (double)MINS_IN_SEG);
998         *end_index = num_segs + *start_index;
999         if (((double)duration/(double)60-MINS_IN_SEG*num_segs) > 7)
1000                 (*end_index)++;
1001  
1002         if (*end_index > (i = ((nday + 1) * (b->segs_in_array / 7))) )
1003                 *end_index = i;
1004  
1005         for (i = *start_index; i < *end_index; i++)
1006                 if (b->add_to_array)
1007                         b->multi_array[i]++;
1008                 else if (b->multi_array[i] > 0)
1009                         b->multi_array[i]--;
1010 }
1011
1012 void
1013 mb_update_array(char *entry_text, Calendar *c)
1014 {
1015         int                     start_ind, end_ind, i, r_cnt;
1016         time_t                  start, stop;
1017         Browser                 *b = (Browser *)c->browser;
1018         CSA_entry_handle        *entries = NULL;
1019         BlistData               *bd;
1020         CSA_return_code         stat;
1021         Browselist              *bl = (Browselist *)c->browselist;
1022         CSA_enum                *ops;
1023         CSA_attribute           *range_attrs;
1024         CSA_uint32      num_entries;
1025         Tick                    start_tick, end_tick;
1026
1027         /*
1028          *  Search for the entry text in our list of calendar handles
1029          */
1030         i = 1;
1031         while ((bd = (BlistData *)CmDataListGetData(bl->blist_data, i++))
1032                && strcmp(bd->name, entry_text) != 0);
1033         if (!bd)
1034                 return;
1035
1036         start = b->begin_week_tick;
1037         stop = next_ndays(b->begin_week_tick, 7) - 1;
1038
1039         if (bd->cache == NULL) {
1040                 setup_range(&range_attrs, &ops, &r_cnt, start, 
1041                         stop, CSA_TYPE_EVENT,
1042                         NULL, B_FALSE, bd->version);
1043                 stat = csa_list_entries(bd->cal_handle, r_cnt, range_attrs, 
1044                                         ops, &num_entries, &entries, NULL);
1045                 free_range(&range_attrs, &ops, r_cnt);
1046                 backend_err_msg(b->frame, bd->name, stat,
1047                                 ((Props_pu *)c->properties_pu)->xm_error_pixmap);
1048                 if (stat != CSA_SUCCESS)
1049                         return;
1050
1051                 allocate_paint_cache(entries, num_entries, &bd->cache);
1052                 bd->cache_size = num_entries;
1053                 csa_free(entries);
1054
1055         }
1056
1057         for (i = 0; i < bd->cache_size; i++) {
1058
1059                 start_tick = bd->cache[i].start_time;
1060                 end_tick = bd->cache[i].end_time;
1061
1062                 mb_update_segs(b, start_tick,
1063                                end_tick - start_tick, &start_ind,
1064                                &end_ind);
1065         }
1066 }
1067
1068 static Boolean
1069 register_names(char *name, Calendar *c)
1070 {
1071         int             i;
1072         char            *user, *location;
1073         Props_pu        *p = (Props_pu *)c->properties_pu;
1074         Browser         *b = (Browser*)c->browser;
1075         BlistData       *bd = NULL;
1076         Browselist      *bl = (Browselist *)c->browselist;
1077         CSA_return_code stat;
1078         CSA_session_handle      cal = NULL;
1079         unsigned int            user_access;
1080         CSA_calendar_user       csa_user;
1081         CSA_flags               flags = NULL;
1082         CSA_extension           cb_ext;
1083         CSA_extension           logon_ext;
1084         char                    buf[BUFSIZ];
1085
1086         if (blank_buf(name))
1087                 return False;
1088
1089         i = 1;
1090         while ((bd = (BlistData *)CmDataListGetData(bl->blist_data, i++))
1091                && strcmp(bd->name, name) != 0);
1092         if (!bd) {
1093                 char *title = XtNewString(catgets(c->DT_catd, 1, 1070,
1094                                 "Calendar : Error - Compare Calendars"));
1095                 char *text = XtNewString(catgets(c->DT_catd, 1, 607,
1096                                 "Internal error registering calendar name."));
1097                 char *ident = XtNewString(catgets(c->DT_catd, 1, 95, "Continue"));
1098                 dialog_popup(b->frame,
1099                         DIALOG_TITLE, title,
1100                         DIALOG_TEXT, text,
1101                         BUTTON_IDENT, 1, ident,
1102                         DIALOG_IMAGE, p->xm_error_pixmap,
1103                         NULL);
1104                 XtFree(ident);
1105                 XtFree(text);
1106                 XtFree(title);
1107                 return False;
1108         }
1109         if (bd->cal_handle)
1110                 return True;
1111
1112         if (strcmp(c->calname, name) == 0) {
1113                 cal = c->my_cal_handle;
1114                 user_access = c->my_access;
1115         } else if (strcmp(c->view->current_calendar, name) == 0) {
1116                 cal = c->cal_handle;
1117                 user_access = c->user_access;
1118         } else {
1119                 user = cm_target2name(name);
1120                 location = cm_target2location(name);
1121                 csa_user.user_name = name;
1122                 csa_user.user_type = 0;
1123                 csa_user.calendar_user_extensions = NULL;
1124                 csa_user.calendar_address = name;
1125                 logon_ext.item_code = CSA_X_DT_GET_USER_ACCESS_EXT;
1126                 logon_ext.item_data = 0;
1127                 logon_ext.item_reference = NULL;
1128                 logon_ext.extension_flags = CSA_EXT_LAST_ELEMENT;
1129                 stat = csa_logon(NULL, &csa_user, NULL, NULL, NULL, &cal,
1130                         &logon_ext);
1131                 free(user);
1132                 free(location);
1133  
1134                 if (stat != CSA_SUCCESS) {
1135                         backend_err_msg(b->frame, name, stat,
1136                                ((Props_pu *)c->properties_pu)->xm_error_pixmap);
1137                         return False;
1138                 } else
1139                         user_access = logon_ext.item_data;
1140
1141         }
1142
1143         /* register for activity notification */
1144         flags = CSA_CB_ENTRY_ADDED | CSA_CB_ENTRY_UPDATED |
1145                 CSA_CB_ENTRY_DELETED;
1146         cb_ext.item_code = CSA_X_XT_APP_CONTEXT_EXT;
1147         cb_ext.item_data = (CSA_uint32)c->xcontext->app;
1148         cb_ext.extension_flags = CSA_EXT_LAST_ELEMENT;
1149
1150         stat = csa_register_callback(cal, flags, mb_update_handler, NULL, &cb_ext);
1151         if (stat != CSA_SUCCESS) {
1152                 backend_err_msg(b->frame, name, stat,
1153                         ((Props_pu *)c->properties_pu)->xm_error_pixmap);
1154                 return False;
1155         }
1156
1157         bd->cal_handle = cal;
1158         bd->user_access = user_access;
1159
1160         /* squirrel away data model version for later */
1161
1162         bd->version = get_data_version(cal);
1163
1164         return True;
1165 }
1166
1167 extern void
1168 mb_deregister_names(char *name, Calendar *c)
1169 {
1170         int             i;
1171         Browser         *b = (Browser *)c->browser;
1172         Props_pu        *p = (Props_pu *)c->properties_pu;
1173         BlistData       *bd;
1174         Browselist      *bl = (Browselist *)c->browselist;
1175         CSA_return_code stat;
1176
1177         i = 1;
1178         while ((bd = (BlistData *)CmDataListGetData(bl->blist_data, i))
1179                && strcmp(bd->name, name) != 0)
1180                 ++i;
1181         if (!bd)
1182                 return;
1183
1184         destroy_paint_cache(bd->cache, bd->cache_size);
1185         bd->cache = NULL;
1186         bd->cache_size = 0;
1187
1188         if (bd->cal_handle != c->my_cal_handle &&
1189             bd->cal_handle != c->cal_handle && bd->cal_handle) {
1190                 stat = csa_logoff(bd->cal_handle, NULL);
1191
1192
1193                 if (stat != CSA_SUCCESS) {
1194                         backend_err_msg(b->frame, bd->name, stat,
1195                                         p->xm_error_pixmap);
1196                 }
1197                 bd->cal_handle = NULL;
1198                 blist_clean(bl, False);
1199         }
1200 }
1201
1202 static void
1203 mb_box_notify(Widget widget, XtPointer client_data, XtPointer call_data)
1204 {
1205         XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
1206         Calendar *c = (Calendar *)client_data;
1207         int             i;
1208         char            *addr;
1209         Browser         *b;
1210         GEditor         *e;
1211         BlistData       *bd;
1212         Browselist      *bl;
1213         XmString        xmstr;
1214         char            name[BUFSIZ+5];
1215 #ifdef FNS
1216         int             rcode;
1217         char            *fns_name, buf[256];
1218 #endif
1219
1220         b = (Browser*)c->browser;
1221         bl = (Browselist*)c->browselist;
1222         e = (GEditor*)c->geditor;
1223
1224         i = 0;
1225         while (i < cbs->selected_item_count &&
1226                cbs->item_position != cbs->selected_item_positions[i])
1227                 ++i;
1228         b->add_to_array = (i < cbs->selected_item_count) ? True : False;
1229
1230         if ((bd = (BlistData *)CmDataListGetData(bl->blist_data,
1231                                                    cbs->item_position)) == NULL)
1232                 return;
1233         /* erase busy status if it was busy because it was deselected */
1234         if (!XmListPosSelected(b->browse_list, cbs->item_position)) {
1235                 sprintf(name, "  %s", bd->name);
1236                 xmstr = XmStringCreateLocalized(name);
1237                 XmListDeletePos(b->browse_list, cbs->item_position);
1238                 XmListAddItem(b->browse_list, xmstr, cbs->item_position);
1239                 XmStringFree(xmstr);
1240         }       
1241
1242 #ifdef FNS
1243         rcode = -1;
1244         if (cmfns_use_fns((Props *)c->properties)) {
1245                 /* Yes!  Try to use it */
1246                 rcode = cmfns_lookup_calendar(bd->name, buf, sizeof(buf));
1247         }
1248
1249         if (rcode > 0)
1250                 addr = buf;
1251         else 
1252 #endif
1253                 addr = bd->name;
1254
1255         _DtTurnOnHourGlass(b->frame);
1256         if (b->add_to_array) {
1257                 if (!register_names(addr, c)) {
1258                         XmListDeselectPos(b->browse_list, cbs->item_position);
1259                         _DtTurnOffHourGlass(b->frame);
1260                         return;
1261                 }
1262                 if (geditor_showing(e)) {
1263                         add_to_gaccess_list(addr, bd->cal_handle,
1264                                 bd->user_access, bd->version, e, True);
1265                         add_all_gappt(e);
1266                 }
1267                 mb_update_array(addr, c);
1268         } else {
1269                 /*
1270                  * Must update the array before we deregister names because we
1271                  * close the calendar handle when we deregister.
1272                  */
1273                 mb_update_array(addr, c);
1274                 mb_deregister_names(addr, c);
1275                 if (geditor_showing(e))
1276                         remove_from_gaccess_list(addr, e);
1277         }
1278         mb_refresh_canvas(b, c);
1279         _DtTurnOffHourGlass(b->frame);
1280 }
1281
1282 extern void
1283 mb_clear_selected_calendar(
1284         char            *name,
1285         Calendar        *c)
1286 {
1287         GEditor         *e = (GEditor*)c->geditor;
1288         Browser         *b = (Browser *)c->browser;
1289
1290         /*
1291          * Must update the array before we deregister names because we
1292          * close the calendar handle when we deregister.
1293          */
1294         b->add_to_array = False;
1295         mb_update_array(name, c);
1296         if (geditor_showing(e))
1297                 remove_from_gaccess_list(name, e);
1298         mb_deregister_names(name, c);
1299         mb_refresh_canvas(c->browser, c);
1300 }
1301
1302 extern void
1303 mb_init_canvas(Calendar *c)
1304 {
1305         Browser         *b = (Browser*)c->browser;
1306         Browselist      *bl = (Browselist *)c->browselist;
1307         BlistData       *bd;
1308         int             i;
1309
1310         b->add_to_array = True;
1311         gr_clear_area(b->xcontext, 0, 0, b->canvas_w, b->canvas_h);
1312         register_names(c->calname, c);
1313         mb_update_array(c->calname, c);
1314
1315         /*
1316          *  Search for the entry text in our list of calendar handles
1317          */
1318
1319         i = 1;
1320         while ((bd = (BlistData *)CmDataListGetData(bl->blist_data, i++))
1321                && strcmp(bd->name, c->calname) != 0);
1322
1323         if (!bd)
1324                 return;
1325
1326         XmListSelectPos(b->browse_list, i - 1, False);
1327 }
1328
1329
1330 extern void
1331 mb_init_datefield(Browser *b, Calendar *c)
1332 {
1333         char            *date;
1334         Props           *p = (Props *)c->properties;
1335         OrderingType    ot = get_int_prop(p, CP_DATEORDERING);
1336         SeparatorType   st = get_int_prop(p, CP_DATESEPARATOR);
1337
1338         date = XmTextGetString(b->datetext);
1339         if (!date || *date == '\0') {
1340                 date = get_date_from_widget(c->view->date, b->datetext, ot, st);
1341                 if (date != NULL)
1342                         XmTextSetString(b->datetext, date);
1343         }
1344 }
1345 static void
1346 mb_init_browchart(Browser *b, Calendar *c)
1347 {
1348         int             char_width, char_height, day_len, day_of_week;
1349         int             label_height, label_width;
1350         Props           *p = (Props *)c->properties;
1351         Dimension       canvas_width, canvas_height;
1352         XFontSetExtents fontextents;
1353  
1354         mb_init_datefield(b, c);
1355         XtVaGetValues(b->canvas,
1356                       XmNwidth, &canvas_width,
1357                       XmNheight, &canvas_height,
1358                       NULL);
1359         b->canvas_w = (int)canvas_width;
1360         b->canvas_h = (int)canvas_height;
1361         CalFontExtents(c->fonts->labelfont, &fontextents);
1362         char_height = fontextents.max_ink_extent.height;
1363         char_width = fontextents.max_ink_extent.width;
1364         label_height = char_height * 2;
1365         label_width = char_width + 2;
1366         b->chart_height =
1367                 b->canvas_h - (c->view->outside_margin * 2) - label_height - 5;
1368         b->chart_width =
1369                 b->canvas_w - (c->view->outside_margin * 2) - label_width;
1370         b->boxw = b->chart_width / 7;
1371         b->chart_width = b->boxw * 7;
1372
1373         day_len = get_int_prop(p, CP_DAYEND) - get_int_prop(p, CP_DAYBEGIN);
1374         b->boxh = b->chart_height / day_len;
1375
1376         /*
1377          * Make sure boxh is evenly divisable by BOX_SEG
1378          */
1379         b->boxh -= (b->boxh % BOX_SEG);
1380         b->chart_height = b->boxh * day_len;
1381         b->chart_x = c->view->outside_margin + label_width;
1382         b->chart_y = c->view->outside_margin + label_height + char_height;
1383 }       
1384
1385 extern void
1386 mb_draw_chartgrid(Browser *b, Calendar *c)
1387 {
1388         int             x, y;
1389         int             n;
1390         Props           *p = (Props*)c->properties;
1391         XFontSetExtents fontextents;
1392         int             char_height, char_width;
1393         char            label[5], buf[160];
1394         new_XContext    *xc = b->xcontext;
1395         int             dayy, dayweek;
1396         Tick            daytick;
1397         DisplayType     dt;
1398         int             nop;
1399         int             s_width;
1400  
1401  
1402         CalFontExtents(c->fonts->viewfont, &fontextents);
1403         char_height = fontextents.max_logical_extent.height;
1404         char_width = fontextents.max_logical_extent.width;
1405
1406         /*      Draw chart. It'll be filled in later.
1407                 Draw grid lines and hourly labels.      */
1408         x = b->chart_x;
1409         y = b->chart_y;
1410  
1411         /* clear header */
1412         gr_clear_area(xc, 0, 0, b->canvas_w, b->chart_y);
1413         label[0] = '\0';
1414
1415         /* draw hour labels */
1416         for (n = get_int_prop(p, CP_DAYBEGIN); n <= get_int_prop(p, CP_DAYEND); n++) {
1417         
1418                 dt = get_int_prop(p, CP_DEFAULTDISP);
1419                 if (dt == HOUR12)
1420                         sprintf(label, "%2d", n > 12 ? n - 12 : n);
1421                 else
1422                         sprintf(label, "%2d", n);
1423                 gr_text(xc, c->view->outside_margin-char_width, y+3,
1424                         c->fonts->viewfont, label, NULL);
1425                 gr_draw_line(xc, x, y, x + b->chart_width,
1426                          y, gr_solid, NULL);
1427                 y += b->boxh;
1428         }
1429  
1430         /*
1431          * Draw vertical lines and day labels
1432          */
1433         y = b->chart_y;
1434         dayy = y - char_height - 4;
1435         dayweek = dow(b->date);
1436         daytick = last_ndays(b->date, dayweek == 0 ? 6 : dayweek-1);
1437  
1438         /* draw month */
1439         format_date(b->begin_week_tick+1, get_int_prop(p, CP_DATEORDERING), buf, 0, 0, 0);
1440         gr_text(xc, c->view->outside_margin+4,
1441                  dayy-char_height-4, c->fonts->labelfont, buf, NULL);
1442  
1443         for (n = 0; n < 7; n++) {
1444                 if (daytick >= get_bot() && daytick < get_eot()) {
1445
1446                         CalTextExtents(c->fonts->viewfont, days3[n+1], strlen(days3[n+1]), &nop, &nop, &s_width, &nop);
1447
1448                         gr_text(xc, b->chart_x + (b->boxw * n) + ((b->boxw - s_width)/2),
1449                                 dayy, c->fonts->viewfont, days3[n+1], NULL);
1450
1451                         CalTextExtents(c->fonts->viewfont, numbers[dom(daytick)], strlen(numbers[dom(daytick)]), &nop, &nop, &s_width, &nop);
1452
1453                         gr_text(xc, b->chart_x + (b->boxw * n) + ((b->boxw - s_width)/2),
1454                                 y - char_height / 2, c->fonts->viewfont,
1455                                 numbers[dom(daytick)], NULL);
1456                 }
1457                 daytick += daysec;
1458                 gr_draw_line(xc, b->chart_x + (b->boxw * n),
1459                         y, b->chart_x + (b->boxw * n),
1460                         y + b->chart_height, gr_solid, NULL);
1461         }
1462  
1463         /*
1464          * Draw box around the whole thing.
1465          */
1466         gr_draw_box(xc, b->chart_x, b->chart_y, b->chart_width, b->chart_height, NULL);
1467         gr_draw_box(xc, b->chart_x-1, b->chart_y-1, b->chart_width+2, b->chart_height+2, NULL);
1468 }
1469
1470 extern void
1471 mb_draw_appts(Browser *b, int start, int end, Calendar *c)
1472 {
1473         int             x, y, h, i, end_of_day;
1474         Boolean         outofbounds = False;
1475         Colormap        cms;
1476
1477         if (next_ndays(b->date, 1) > get_eot())
1478                 outofbounds = True;
1479         XtVaGetValues(b->canvas, XmNcolormap, &cms, NULL);
1480
1481         h = (b->boxh/BOX_SEG);
1482         end_of_day = (b->segs_in_array / 7);
1483  
1484         y = b->chart_y + (start % end_of_day) * h;
1485         x = b->chart_x + (start/end_of_day * b->boxw);
1486  
1487         i = start;
1488         while (i < end) {
1489                 if (b->multi_array[i] <= 0) {
1490                         gr_clear_area(b->xcontext, x, y, b->boxw, h);
1491                         y += h;
1492                         i++;
1493                 }
1494                 else if (b->multi_array[i] == 1) {
1495                         /* batch up for one repaint */
1496                         if ( ((i+1) < b->segs_in_array)
1497                                  && b->multi_array[i+1] == 1 &&
1498                                 ( ((i+1) % end_of_day) != 0)) {
1499                                 h += (b->boxh/BOX_SEG);
1500                                 if (++i < end)
1501                                         continue;
1502                         }
1503                         if ((c->xcontext->screen_depth < 8) || FAKE_MONOCHROME)
1504                                 gr_make_gray(b->xcontext, x, y, b->boxw, h, 25);
1505                         else
1506                                 gr_make_grayshade(b->xcontext, x, y, b->boxw, h,
1507                                                         LIGHTGREY);
1508                         y += h;
1509                         h = (b->boxh/BOX_SEG);
1510                         i++;
1511                 }
1512                 else if (b->multi_array[i] == 2) {
1513                         /* batch up for one repaint */
1514                         if ( ((i+1) < b->segs_in_array)
1515                                  && b->multi_array[i+1] == 2 &&
1516                                 ( ((i+1) % end_of_day) != 0) ) {
1517                                 h += (b->boxh/BOX_SEG);
1518                                 if (++i < end)
1519                                         continue;
1520                         }
1521                         if ((c->xcontext->screen_depth < 8) || FAKE_MONOCHROME)
1522                                 gr_make_gray(b->xcontext, x, y, b->boxw, h, 50);
1523                         else
1524                                 gr_make_rgbcolor(b->xcontext, cms, x, y,
1525                                                  b->boxw, h, MIDGREY, MIDGREY,
1526                                                  MIDGREY);
1527                         y += h;
1528                         h = (b->boxh/BOX_SEG);
1529                         i++;
1530                 }
1531                 else if (b->multi_array[i] >= 3) {
1532                         /* batch up for one repaint */
1533                         if ( ((i+1) < b->segs_in_array)
1534                                 && b->multi_array[i+1] >= 3 &&
1535                                 ( ((i+1) % end_of_day) != 0) ) {
1536                                 h += (b->boxh/BOX_SEG);
1537                                 if (++i < end)
1538                                         continue;
1539                         }
1540                         if ((c->xcontext->screen_depth < 8) || FAKE_MONOCHROME)
1541                                 gr_make_gray(b->xcontext, x, y, b->boxw, h, 75);
1542                         else
1543                                 gr_make_rgbcolor(b->xcontext, cms, x, y,
1544                                                  b->boxw, h, DIMGREY, DIMGREY,
1545                                                  DIMGREY);
1546                         y += h;
1547                         h = (b->boxh/BOX_SEG);
1548                         i++;
1549                 }
1550                 if (i != 0 && ((i % end_of_day) == 0)) {
1551                         x += b->boxw;
1552                         y = b->chart_y;
1553                         h = (b->boxh/BOX_SEG);
1554                 }
1555         if (outofbounds && i > 4)
1556                 break;
1557         }
1558
1559         browser_select(c, b, NULL);
1560 }
1561
1562 extern void
1563 mb_refresh_canvas(Browser *b, Calendar *c)
1564 {
1565         mb_draw_appts(b, 0, b->segs_in_array, c);
1566         mb_draw_chartgrid(b, c);
1567         mb_display_footermess(b, c);
1568 }
1569
1570 void
1571 mb_resize_proc(Widget w, XtPointer client_data, XtPointer call)
1572 {
1573         Dimension width, height;
1574         Calendar *c = (Calendar *)client_data;
1575         Browser *b;
1576
1577         XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL);
1578         b = (Browser*)c->browser;
1579         gr_clear_area(b->xcontext, 0, 0, width, height);
1580         mb_init_browchart(b, c);
1581         mb_refresh_canvas(b, c);
1582 }
1583
1584 void
1585 mb_refigure_chart(Calendar *c) {
1586         mb_resize_proc(((Browser *)c->browser)->canvas, (XtPointer)c, NULL);
1587 }
1588
1589 extern void
1590 browser_reset_list(Calendar *c) {
1591         int             i;
1592         Browser         *b = (Browser *)c->browser;
1593         BlistData       *bd;
1594         Browselist      *bl = (Browselist *)c->browselist;
1595         XmStringTable   list_selected_items, selected_items;
1596         int             selected_items_count;
1597
1598         XtVaGetValues(b->browse_list,
1599                 XmNselectedItemCount,   &selected_items_count,
1600                 XmNselectedItems,       &list_selected_items,
1601                 NULL);
1602
1603         selected_items = (XmStringTable)calloc(selected_items_count, 
1604                                                         sizeof(XmStringTable));
1605         for (i = 0; i < selected_items_count; i++)
1606                 selected_items[i] = XmStringCopy(list_selected_items[i]);
1607
1608         XtVaSetValues(b->upper_form, XmNresizePolicy, XmRESIZE_NONE, NULL);
1609         /*
1610          * When a user removes a calendar from the menu we remove it
1611          * from the multi-browser.  If the calendar happens to be selected
1612          * in the multi-browser then we must deselect it and clean up
1613          * the browser.  That's what this first loops does.
1614          */
1615         for (i = 1; i <= bl->blist_data->count; i++) {
1616                 bd = (BlistData *)CmDataListGetData(bl->blist_data, i);
1617                 if (bd && bd->tag != BLIST_ACTIVE) {
1618                         mb_clear_selected_calendar(bd->name, c);
1619                         /* We need to reset this to one because the blist_data
1620                          * has changed which may cause us to miss an item.
1621                          */
1622                         i = 1;
1623                 }
1624         }
1625
1626         XmListDeleteAllItems(b->browse_list);
1627         for (i = 1; i <= bl->blist_data->count; i++) {
1628                 bd = (BlistData *)CmDataListGetData(bl->blist_data, i);
1629                 if (bd && bd->name) {
1630                         char            buf[BUFSIZ + 5];
1631                         XmString        xmstr;
1632
1633                         sprintf(buf, "  %s", bd->name); 
1634                         xmstr = XmStringCreateLocalized(buf);
1635                         if (!XmListItemExists(b->browse_list, xmstr))
1636                                 XmListAddItem(b->browse_list, xmstr, 0);
1637                         XmStringFree(xmstr);
1638                 }
1639         }
1640         XtVaSetValues(b->upper_form, XmNresizePolicy, XmRESIZE_ANY, NULL);
1641         
1642         /*
1643          * Reselect the items that were selected before we changed the
1644          * contents of the mb.
1645          */
1646         for (i = 0; i < selected_items_count; i++) {
1647                 int     *pos_list, 
1648                          pos_cnt;
1649
1650                 if (XmListGetMatchPos(b->browse_list, selected_items[i],
1651                                       &pos_list, &pos_cnt))
1652                         XmListSelectPos(b->browse_list, pos_list[0], False);
1653                 XmStringFree(selected_items[i]);
1654         }
1655
1656         if (selected_items)
1657                 free(selected_items);
1658 }
1659
1660 extern void
1661 init_browser(Calendar *c)
1662 {
1663         pr_pos  xy;
1664         Browser *b = (Browser*)c->browser;
1665  
1666         browser_reset_list(c);
1667         b->row_sel = b->col_sel = 0;
1668         mb_init_browchart(b, c);
1669         mb_init_canvas(c);
1670         mb_refresh_canvas(b, c);
1671         xy.x = dow(b->date) - 1;
1672         xy.y = 0;
1673         browser_select(c, b, &xy);
1674 }
1675
1676 static void
1677 cancel_cb(Widget w, XtPointer client, XtPointer call)
1678 {
1679         Calendar *c = (Calendar *)client;
1680         Browser *b = (Browser *)c->browser;
1681
1682         XtPopdown(b->frame);
1683
1684         XtDestroyWidget(b->frame);
1685         XtFree(b->multi_array); 
1686         XtFree(c->browser); c->browser = NULL;
1687 }
1688
1689 static void
1690 popup_cb(Widget w, XtPointer client, XtPointer call)
1691 {
1692         Calendar *c = (Calendar *)client;
1693         Browser *b = (Browser *)c->browser;
1694         Position x, y;
1695
1696         XtVaGetValues(c->frame, XmNx, &x, XmNy, &y, NULL);
1697         XtVaSetValues(b->frame, XmNx, x+100, XmNy, y+100, NULL);
1698 }
1699
1700 /*
1701  * This is the CSA_callback called from the CSA library when
1702  * an update occurs on a calendar to which we are logged on,
1703  * and have registered interest.  Registered in register_names.
1704  * When calendar is logged off, any registered callbacks for it
1705  * are destroyed automagically.
1706  */
1707 static void
1708 mb_update_handler(CSA_session_handle cal, CSA_flags reason,
1709                CSA_buffer call_data, CSA_buffer client_data, CSA_extension *ext)
1710 {
1711         Calendar        *c = calendar;
1712         Browser         *b = (Browser *)c->browser;
1713
1714         /* sync whatever needs sync'ing */
1715         if (b) {
1716                 br_display(c);
1717                 if (geditor_showing((GEditor *)c->geditor))
1718                         add_all_gappt((GEditor *)c->geditor);
1719         }
1720  
1721 }
1722