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