Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / dtcm / dtcm_editor.c
1 /* $TOG: dtcm_editor.c /main/14 1999/09/20 10:32:17 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 <unistd.h>
13 #include <Xm/XmAll.h>
14 #include <Dt/EnvControlP.h>
15 #include <Dt/Dnd.h>
16 #include <Dt/Icon.h>
17 #include <Dt/UserMsg.h>
18 #include <Tt/tttk.h>
19 #include <csa.h>
20 #include "calendar.h"
21 #include "props.h"
22 #include "props_pu.h"
23 #include "dssw.h"
24 #include "rfp.h"
25 #include "dnd.h"
26 #include "util.h"
27 #include "cm_tty.h"
28 #include <nl_types.h>
29 #include <locale.h>
30 #if !defined(NL_CAT_LOCALE)
31 #define NL_CAT_LOCALE       0
32 #endif
33
34 int debug = 0;
35 static Tt_message load_cb();
36
37 static const char *ptype = "Dt_AppointmentEditor";
38
39 typedef enum {no_tt, file_tt, buffer_tt} Dtcm_editor_start;
40
41 /* Absolute value macro */
42 #ifndef ABS
43 #define ABS(x) (((x) > 0) ? (x) : (-(x)))
44 #endif
45  
46
47 #include "drag_xbm"
48 #include "drag_mask_xbm"
49
50 /*
51 **  Structure for stand-alone editor
52 */
53 typedef struct {
54         Widget          attach_button;
55         Calendar        *c;
56         DSSW            *dssw;
57         char            *file;
58         char            *vtype;
59         Dtcm_editor_start init;
60         Boolean         read_only;
61         Tt_pattern      *contract_pats;
62         Tt_message      contract;
63         Widget          form;
64         Props           *p;
65         Props_pu        *pu;
66         Widget          reset_button;
67         RFP             *rfp;
68         Widget          top_level;
69         Boolean         modified;
70         Dtcm_appointment *orig_appt;
71         Widget          drag_source;
72         int             initialX;
73         int             initialY;
74         Boolean         doing_drag;
75         Widget          drag_icon;
76         Pixmap          drag_bitmap;
77         Pixmap          drag_mask;
78         int             dsswFlags;
79         int             rfpFlags;
80 } DTCM_editor;
81
82 static void de_apply_proc(Widget , XtPointer , XtPointer );
83
84 /*
85 **  Hate like hell to make this global, but I can find no way to get at it
86 **  when I need it in other methods ...
87 **
88 **  Also, help uses a global reference to calendar which hoses us ...
89 */
90 XtAppContext    app;
91 Calendar        *calendar;
92
93 static void
94 de_mark_change(Widget w, XtPointer data, XtPointer cbs) {
95  
96         DTCM_editor        *de = (DTCM_editor *)data;
97
98         de->modified = True;
99
100         if ((w == de->dssw->start_text) ||
101             (w == de->dssw->start_am) ||
102             (w == de->dssw->start_pm) ||
103             (w == de->dssw->stop_text) ||
104             (w == de->dssw->stop_am) ||
105             (w == de->dssw->stop_pm))
106           de->dsswFlags = 0;
107
108         if (w == de->rfp->repeat_menu)
109           de->rfpFlags = 0;
110 }
111
112 static void
113 merge_old_values(Dtcm_appointment *original, Dtcm_appointment *new) {
114
115         /* This routine takes in the original appointment structure 
116            from the edited appointment, and merges back in any attributes 
117            that aren't controlled by our editor, so that when the 
118            appointment is written back out to the buffering agent, those 
119            attributes are not lost. */
120
121         int     source_count, dest_count, new_attrs;
122         int     dest_attr_num = new->count;
123         Boolean found;
124
125         if (original == NULL)
126                 return;
127         
128         /* We need to see how much larger the attribute array needs to 
129            get.  For each of the guys in the source attribute array, 
130            we need to look and see if it's already a member of the 
131            destination array.  Those that don't exist, we count, and 
132            later move. */
133
134         for (source_count = 0, new_attrs = 0; source_count < original->count; source_count++) {
135                 found = False;
136                 for (dest_count = 0; dest_count < dest_attr_num; dest_count++) {
137                         if (original->attrs[source_count].name && new->attrs[dest_count].name) {
138                                 if (strcmp(original->attrs[source_count].name, new->attrs[dest_count].name) == 0) {
139                                         found = True;
140                                         break;
141                                 }
142                         }
143                 }
144
145                 if (found == False)
146                         new_attrs++;
147         }
148
149         if (new_attrs == 0)
150                 return;
151
152         /* realloc the attrs array to be large enough to accomodate the new 
153            attibutes */
154
155         new->attrs = (CSA_attribute *) realloc(new->attrs, (dest_attr_num + new_attrs) * sizeof(CSA_attribute));
156
157         /* Copy in the new attribute values */
158         for (source_count = 0; source_count < original->count; source_count++) {
159                 found = False;
160                 for (dest_count = 0; dest_count < new->count; dest_count++) {
161                         if (original->attrs[source_count].name && new->attrs[dest_count].name) {
162                                 if (strcmp(original->attrs[source_count].name, new->attrs[dest_count].name) == 0) {
163                                         found = True;
164                                         break;
165                                 }
166                         }
167                 }
168
169                 if (found == False)
170                 {
171                         new->attrs[new->count] = original->attrs[source_count];
172                         new->count++;
173                 }
174         }
175
176         /* reset the appointment links, as the old ones have been 
177            thrashed by reallocing the attribute array. */
178
179         set_appt_links(new);
180 }
181
182 Widget
183 CreateDragSourceIcon(
184         Widget          widget,
185         Pixmap          pixmap,
186         Pixmap          mask)
187 {
188         Widget          dragIcon;
189         Window          rootWindow;
190         int             pixmapX, pixmapY;
191         unsigned int    pixmapWidth, pixmapHeight, pixmapBorder, pixmapDepth;
192         Arg             args[20];
193         Cardinal        nn = 0;
194  
195         XGetGeometry (XtDisplayOfObject(widget), pixmap, &rootWindow,
196                 &pixmapX, &pixmapY, &pixmapWidth, &pixmapHeight,
197                 &pixmapBorder, &pixmapDepth);
198  
199         XtSetArg(args[nn], XmNwidth, pixmapWidth);  nn++;
200         XtSetArg(args[nn], XmNheight, pixmapHeight);  nn++;
201         XtSetArg(args[nn], XmNmaxWidth, pixmapWidth);  nn++;
202         XtSetArg(args[nn], XmNmaxHeight, pixmapHeight);  nn++;
203         XtSetArg(args[nn], XmNpixmap, pixmap);  nn++;
204         XtSetArg(args[nn], XmNmask, mask);  nn++;
205         XtSetArg(args[nn], XmNdepth, pixmapDepth);  nn++;
206         dragIcon = XmCreateDragIcon(widget, "sourceIcon", args, nn);
207
208         return(dragIcon);
209 }
210
211 /*
212  * getIcon
213  *
214  * Returns a new IconInfo structure with bitmap, mask, width, height,
215  * icon type and name.
216  */
217 static void
218 GetIcon(DTCM_editor *de)
219 {
220  
221         Display        *display = XtDisplay(calendar->frame);
222         Window          window = XtWindow(calendar->frame);
223         unsigned char  *bitmapData, *bitmapMask;
224  
225         if (de->drag_bitmap == NULL) {
226                 de->drag_bitmap = XCreateBitmapFromData(display,
227                         window, (char *) drag_xbm_bits,
228                         drag_xbm_width, drag_xbm_height);
229                 if (de->drag_bitmap == NULL) {
230
231                         printf(catgets(calendar->DT_catd, 1, 237, "XCreateBitmapFromData() failed for bitmap.\n"));
232                         return;
233                 }
234         }
235         if (de->drag_mask == NULL) {
236                 de->drag_mask = XCreateBitmapFromData(display,
237                         window, (char *) drag_mask_xbm_bits,
238                         drag_mask_xbm_width, drag_mask_xbm_height);
239                 if (de->drag_mask == NULL) {
240                         printf(catgets(calendar->DT_catd, 1, 238, "XCreateBitmapFromData() failed for mask.\n"));
241                         return;
242                 }
243         }
244 }
245  
246  
247 /*
248  * DragFinishCB
249  *
250  * Resets drag state to indicate the drag is over. Free memory allocated
251  * with the drag.
252  */
253 static void
254 DragFinishCB(
255         Widget          widget,
256         XtPointer       clientData,
257         XtPointer       callData)
258 {
259         DragContext     *context = (DragContext *) clientData;
260
261         if (!context)
262                 return;
263
264         if (context->editor_type == StandAloneEditor)
265             ((DTCM_editor *) context->editor)->doing_drag = False;
266
267         if (context->data)
268             free(context->data);
269
270         free(context);
271 }
272
273 /*
274  * ApptConvertCB
275  *
276  * Fills in data object with calendar appointment string based on which
277  * appointment in the list was under the pointer when the drag started.
278  */
279 static void
280 ApptConvertCB(
281         Widget          dragContext,
282         XtPointer       clientData,
283         XtPointer       callData)
284 {
285         DtDndConvertCallbackStruct *convertInfo 
286                                         = (DtDndConvertCallbackStruct*)callData;
287         DtDndBuffer     *data           = &(convertInfo->dragData->data.buffers[0]);
288         DragContext     *context        = (DragContext *)clientData;
289         Display         *display        = XtDisplay(dragContext);
290         Atom            CMAPPOINTMENT   
291                         = XmInternAtom(display, "CalendarAppointment", False);
292         Calendar        *c = context->calendar;
293
294         if (convertInfo->reason != DtCR_DND_CONVERT_DATA)
295                 return;
296
297         /* REMIND: Need to check convertInfo->reason, handle DELETE, etc */
298
299         data->bp   = XtNewString(context->data);
300         data->size = strlen(data->bp);
301         data->name = XtNewString(catgets(c->DT_catd, 1, 236, "CalendarAppointment"));
302 }
303
304 void
305 StandaloneApptDragStart(
306         Widget          widget,
307         XEvent          *event,
308         DTCM_editor     *de,
309         EditorType      editor_type)
310 {
311         static XtCallbackRec convertCBRec[] = { {ApptConvertCB, NULL},
312                                                 {NULL, NULL} };
313         static XtCallbackRec dragFinishCBRec[] =  { {DragFinishCB, NULL},
314                                                     {NULL, NULL} };
315  
316         Display        *display         = XtDisplay(widget);
317         int             itemCount, selectedPos;
318         DragContext     *context = calloc(sizeof(DragContext), 1);
319         Calendar        *c = de->c;
320         Dtcm_appointment        *appt;
321         int             old_attr_count;
322         char            *apptstr;
323         int             preDsswFlags, preRfpFlags;
324
325         /* Convert appointment into string.  If not successful, don't start drag. */
326         appt = allocate_appt_struct(appt_write, DATAVER_ARCHIVE, NULL);
327         load_appt_defaults(appt, de->p);
328         preDsswFlags = de->dsswFlags;
329         preRfpFlags = de->rfpFlags;
330         if (!dssw_form_flags_to_appt(de->dssw, appt, de->c->calname,
331                                      now(), &de->dsswFlags) ||
332             !rfp_form_flags_to_appt(de->rfp, appt, de->c->calname,
333                                     &de->rfpFlags) ||
334             (preDsswFlags != de->dsswFlags) ||
335             (preRfpFlags != de->rfpFlags))
336         {
337             de->doing_drag = False;
338             free_appt_struct(&appt);
339             free(context);
340             return;
341         }
342
343         /* save the old count of attributes so that when this appointment 
344            gets freed, we can prevent the added attribute references from 
345            being freed. */
346
347         old_attr_count = appt->count;
348         merge_old_values(de->orig_appt, appt);
349         apptstr = parse_attrs_to_string(appt, de->p,
350                                         attrs_to_string(appt->attrs, appt->count));
351         appt->count = old_attr_count;
352         free_appt_struct(&appt);
353
354         context->data = apptstr;
355         context->calendar = c;
356         context->editor_type = editor_type;
357         context->editor = (caddr_t) de;
358  
359         GetIcon(de);
360  
361         convertCBRec[0].closure = (XtPointer)context;
362         dragFinishCBRec[0].closure = (XtPointer)context;
363  
364         if (de->drag_icon == NULL) {
365                 de->drag_icon = CreateDragSourceIcon(widget, de->drag_bitmap, de->drag_mask);
366         }
367  
368         if (DtDndVaDragStart(widget, event, DtDND_BUFFER_TRANSFER, 1,
369                 XmDROP_COPY, 
370                 convertCBRec, dragFinishCBRec,
371                 DtNsourceIcon,          de->drag_icon,
372                 NULL)
373             == NULL) {
374  
375                 printf(catgets(c->DT_catd, 1, 239, "DragStart returned NULL.\n"));
376         }
377 }
378
379 /*
380  * dragMotionHandler
381  *
382  * Determine if the pointer has moved beyond the drag threshold while button 1
383  * was being held down.
384  */
385 static void
386 EditApptDragMotionHandler(
387         Widget          dragInitiator,
388         XtPointer       clientData,
389         XEvent         *event)
390 {
391         int             diffX, diffY;
392         DTCM_editor     *de = (DTCM_editor *) clientData;
393         Calendar        *c = de->c;
394         Dimension       source_height, source_width;
395         Position        source_x, source_y;
396  
397         if (!de->doing_drag) {
398
399                 /* check to see if the iniital value was within the
400                    bounds for the drag source icon. */
401  
402                 XtVaGetValues(de->drag_source,
403                                 XmNx, &source_x,
404                                 XmNy, &source_y,
405                                 XmNheight, &source_height,
406                                 XmNwidth, &source_width,
407                                 NULL);
408  
409                 if ((event->xmotion.x < source_x) ||
410                     (event->xmotion.y < source_y) ||
411                     (event->xmotion.x > (int) (source_x + source_width)) ||
412                     (event->xmotion.y > (int) (source_y + source_height)))
413                         return;
414  
415
416                 /*
417                  * If the drag is just starting, set initial button down coords
418                  */
419                 if (de->initialX == -1 && de->initialY == -1) {
420                         de->initialX = event->xmotion.x;
421                         de->initialY = event->xmotion.y;
422                 }
423                 /*
424                  * Find out how far pointer has moved since button press
425                  */
426                 diffX = de->initialX - event->xmotion.x;
427                 diffY = de->initialY - event->xmotion.y;
428  
429                 if ((ABS(diffX) >= DRAG_THRESHOLD) ||
430                     (ABS(diffY) >= DRAG_THRESHOLD)) {
431                         de->doing_drag = True;
432                         StandaloneApptDragStart(dragInitiator, event, de, StandAloneEditor);
433                         de->initialX = -1;
434                         de->initialY = -1;
435                 }
436         }
437 }
438
439 /*
440 **  Static callbacks
441 */
442 static void
443 de_quit_handler(Widget w, XtPointer cdata, XtPointer data) {
444         DTCM_editor *de = (DTCM_editor *)cdata;
445         int answer;
446
447         if (de->modified == True) {
448                 char *title = XtNewString(catgets(de->c->DT_catd, 1, 1008, "Calendar Appointment : Help"));
449                 char *text = XtNewString(catgets(de->c->DT_catd, 1, 451, "You have made unsaved changes.\nYou may save your changes, discard your changes, \nor return to your previous place in the dialog."));
450                 char *ident1 = XtNewString(catgets(de->c->DT_catd, 1, 452, "Save"));
451                 char *ident2 = XtNewString(catgets(de->c->DT_catd, 1, 700, "Discard"));
452                 char *ident3 = XtNewString(catgets(de->c->DT_catd, 1, 923, "Cancel"));
453                 answer = dialog_popup(de->top_level,
454                         DIALOG_TITLE, title,
455                         DIALOG_TEXT, text,
456                         BUTTON_IDENT, 1, ident1,
457                         BUTTON_IDENT, 2, ident2,
458                         BUTTON_IDENT, 3, ident3,
459                         DIALOG_IMAGE, de->pu->xm_warning_pixmap,
460                         NULL);
461                 XtFree(ident3);
462                 XtFree(ident2);
463                 XtFree(ident1);
464                 XtFree(text);
465                 XtFree(title);
466
467                 if (answer == 1) {
468                         de_apply_proc(NULL, (XtPointer)de, NULL);
469                         XtPopdown(de->top_level);
470                 }
471                 else  if (answer == 2) {
472                         if (de->contract)
473                                 tttk_message_fail(de->contract, TT_DESKTOP_ECANCELED, NULL, True);
474                         XtPopdown(de->top_level);
475                 }
476                 else  if (answer == 3) {
477                         return;
478                 }
479
480         }
481         else {
482                 if (de->contract)
483                         tttk_message_fail(de->contract, TT_DESKTOP_ECANCELED, NULL, True);
484                 XtPopdown(de->top_level);
485         }
486
487         free(de->c);
488         free(de->dssw);
489         if (de->file)
490                 free(de->file);
491         free(de->p);
492         free(de->pu);
493         free(de->rfp);
494         free(de);
495         exit(0);
496 }
497
498 static void
499 load_from_file(DTCM_editor *de) {
500         int                     i;
501         CmDataList              *list = NULL;
502         Dtcm_appointment        *appt;
503
504         list = CmDataListCreate();
505         parse_appt_from_file(de->c->DT_catd, de->file, list, de->p, 
506                              query_user, de->c, DATAVER_ARCHIVE);
507         if (appt = (Dtcm_appointment *)CmDataListGetData(list, 1)) {
508                 dssw_attrs_to_form(de->dssw, appt);
509                 rfp_attrs_to_form(de->rfp, appt);
510         }
511         for (i = 1; i <= list->count; i++)
512                 if (appt = (Dtcm_appointment *)
513                     CmDataListGetData(list, i)) {
514                         if (de->orig_appt)
515                                 free_appt_struct(&de->orig_appt);
516
517                         de->orig_appt = appt;
518                 }
519         CmDataListDestroy(list, B_FALSE);
520         de->dsswFlags = de->rfpFlags = 0;
521 }
522
523 static void
524 de_set_defaults(DTCM_editor *de) {
525
526         if (de->file && (access(de->file, R_OK) == 0)) {
527                 load_from_file(de);
528         } else {
529                 set_dssw_defaults(de->dssw, now(), True);
530                 set_rfp_defaults(de->rfp);
531                 de->dsswFlags = de->rfpFlags = 0;
532         }
533
534         de->modified = False;
535 }
536
537 static void
538 de_apply_proc(Widget w, XtPointer client_data, XtPointer data) {
539         char                    *str;
540         FILE                    *fp;
541         DTCM_editor             *de = (DTCM_editor *)client_data;
542         Dtcm_appointment        *appt;
543         int                     old_attr_count;
544         Display                 *dpy = XtDisplayOfObject(w);
545
546         appt = allocate_appt_struct(appt_write, 
547                                         DATAVER_ARCHIVE,
548                                         NULL);
549         load_appt_defaults(appt, de->p);
550         de->dsswFlags = 0;
551         dssw_form_to_appt(de->dssw, appt, de->c->calname, now());
552         de->rfpFlags = 0;
553         rfp_form_to_appt(de->rfp, appt, de->c->calname);
554
555         /* save the old count of attributes so that when this appointment 
556            gets freed, we can prevent the added attribute references from 
557            being freed. */
558
559         old_attr_count = appt->count;
560         merge_old_values(de->orig_appt, appt);
561         str = parse_attrs_to_string(appt, de->p, attrs_to_string(appt->attrs, appt->count));
562         appt->count = old_attr_count;
563         de->modified = False;
564
565         if (!str)
566                 return;
567
568         /* we need to distinguish between whether the data is being 
569            saved as part of application termination, or as part of 
570            pressing the "Apply" button.  If it's is app termination, 
571            we need to call ttmedia_load_reply, and if it is an 
572            intermediate save, we are supposed to call ttmedia_Deposit.  
573            Hmmph.  We also need to be pretty clear about saving it back 
574            in the same fashion is was supplied.  If it came as part of 
575            a buffer, it needs to go back that way. */
576
577
578         /* The first case is from application exit */
579
580         if (w == NULL) {
581
582                 /* if it was a file transfer, we should write out the 
583                    file, and set the buffer to null.  If it came from 
584                    a buffer, we don't bother to write out the file. */
585
586                 if (de->read_only) {
587                         ttmedia_load_reply(de->contract, NULL, 0, True);
588                 }
589                 else {
590                         if (de->init == file_tt) {
591                                 fp = fopen(de->file, "w");
592
593                                 if (!fp) {
594                                         XBell(dpy, 50);
595                                         return;
596                                 }
597
598                                 fprintf(fp, "%s", str);
599                                 fclose(fp);
600                                 ttmedia_load_reply(de->contract, NULL, 0, True);
601                         }
602                         else {
603                                 ttmedia_load_reply(de->contract, (unsigned char *) str, strlen(str), True);
604                         }
605                 }
606         }
607         else {
608
609                 /* This case is from the "Apply" button.  In this case, 
610                    if the app was started normally (no tt), then we 
611                    just write out the file.  If it was from tt, we then 
612                    use ttmedia_Deposit to send the data back. */
613
614                 if ((de->init == no_tt) || (de->init == file_tt)) {
615                         fp = fopen(de->file, "w");
616
617                         if (!fp) {
618                                 XBell(dpy, 50);
619                                 return;
620                         }
621
622                         fprintf(fp, "%s", str);
623                         fclose(fp);
624
625                         if (de->init == file_tt)
626                                 ttmedia_Deposit(de->contract,
627                                                 NULL,
628                                                 de->vtype,
629                                                 NULL,
630                                                 0,
631                                                 de->file,
632                                                 app,
633                                                 30000);
634                 }
635                 else {
636                         ttmedia_Deposit(de->contract,
637                                         NULL,
638                                         de->vtype,
639                                         (const unsigned char *)str,
640                                         strlen(str),
641                                         NULL,
642                                         app,
643                                         30000);
644                 }
645
646         }
647
648         free(str);
649         free_appt_struct(&appt);
650 }
651
652 static void
653 de_reset_proc(Widget w, XtPointer client_data, XtPointer data) {
654         DTCM_editor     *de = (DTCM_editor *)client_data;
655
656         de_set_defaults(de);
657         de->dsswFlags = de->rfpFlags = 0;
658 }
659
660 static void
661 display_command_usage() {
662         fprintf(stderr, "\ndtcm_editor Usage:  dtcm [filename]\n\n");
663         exit(0);
664 }
665
666 static void
667 OKCB (Widget dialog, XtPointer client_data, XtPointer call_data)
668 {
669     XtUnmanageChild((Widget) client_data);
670 }
671
672 void
673 DieFromToolTalkError(DTCM_editor *de, char *errfmt, Tt_status status)
674 {
675     Arg          args[10];
676     Widget       dialog, dialogShell;
677     char        *errmsg, *statmsg, *title;
678     XmString     xms_errmsg, xms_ok, xms_title;
679     int          n;
680
681     if (! tt_is_err(status)) return;
682
683     statmsg = tt_status_message(status);
684     errmsg = XtMalloc(strlen(errfmt) + strlen(statmsg) + 2);
685     sprintf(errmsg, errfmt, statmsg);
686
687     xms_ok = XmStringCreateLocalized(catgets(de->c->DT_catd, 2, 3, "OK"));
688     xms_errmsg = XmStringCreateLocalized(errmsg);
689     xms_title = XmStringCreateLocalized(catgets(de->c->DT_catd, 2, 4,
690                         "Calendar : Appointment Editor - Warning"));
691
692     n = 0;
693     XtSetArg(args[n], XmNautoUnmanage, False); n++;
694     XtSetArg(args[n], XmNokLabelString, xms_ok); n++;
695     XtSetArg(args[n], XmNdialogTitle, xms_title); n++;
696     XtSetArg(args[n], XmNmessageString, xms_errmsg); n++;
697     XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
698     XtSetArg(args[n], XmNdialogType, XmDIALOG_WARNING); n++;
699
700     dialog = XmCreateMessageDialog(de->top_level, "IconEditorError", args, n);
701     XtAddCallback(dialog, XmNokCallback, OKCB, (XtPointer) dialog);
702     XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
703     XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
704
705     /*
706      * Disable the frame menu from dialog since we don't want the user
707      * to be able to close dialogs with the frame menu
708      */
709     dialogShell = XtParent(dialog);
710     n = 0;
711     XtSetArg(args[n], XmNmwmDecorations, MWM_DECOR_ALL | MWM_DECOR_MENU); n++;
712     XtSetValues(dialogShell, args, n);
713     XtManageChild(dialog);
714     XtRealizeWidget(dialogShell);
715
716     _DtSimpleError("Dtcm", DtWarning, NULL, errmsg);
717
718     XtFree(errmsg);
719     XmStringFree(xms_ok);
720     XmStringFree(xms_errmsg);
721     XmStringFree(xms_title);
722 }
723
724
725
726 /* 
727  * Initialize tooltalk.  Can be called multiple times: the first call
728  * initializes tooltalk, subsequent calls are no-ops.
729  *
730  * Returns
731  *              -1      Error.  Tooltalk not initialized
732  *              0       Tooltalk already initialized
733  *              1       Tooltalk succussfully intialized
734  */
735
736 Tt_status
737 cmtt_init(
738         char            *toolname,
739         DTCM_editor     *de,
740         XtAppContext    context,
741         Widget           shell)
742
743 {
744         static int      initialized = 0;
745         int             ttfd;
746         Tt_status       status;
747         char            *ttenv;
748         char            *session;
749
750         if (de->c->tt_procid) {
751                 return 0;
752         }
753
754         ttenv = (char *)getenv("TT_SESSION");
755         if (!ttenv || strlen(ttenv) == 0) {
756                 session = tt_X_session(XDisplayString(XtDisplay(shell)));
757                 tt_default_session_set(session);
758                 tt_free(session);
759         }
760
761         de->c->tt_procid = ttdt_open(&ttfd, toolname, "SunSoft", "%I", 1);
762         status = tt_ptr_error(de->c->tt_procid);
763         if (tt_is_err(status)) {
764                 de->c->tt_procid = NULL;
765                 return status;
766         }
767
768         /*
769          * Declare our ptype, and register the callback to handle
770          * Edit/Display/Compose requests
771          */
772         status = ttmedia_ptype_declare(ptype, 0, load_cb, (void *)de, 1);
773
774         if (tt_is_err(status)) {
775                 fprintf(stderr, "cmtt_init could not declare ptype: %s\n",
776                         tt_status_message(status));
777                 return status;
778         }
779
780         status = ttmedia_ptype_declare(ptype, 1000, load_cb, (void *)de, 0);
781
782         if (tt_is_err(status)) {
783                 fprintf(stderr, "cmtt_init could not declare ptype: %s\n",
784                         tt_status_message(status));
785         }
786
787         ttdt_session_join(0, NULL, shell, de->c, 1);
788
789         XtAppAddInput(context, ttfd, (XtPointer)XtInputReadMask,
790                         tttk_Xt_input_handler, de->c->tt_procid);
791
792         tttk_Xt_input_handler( 0, 0, 0 );
793
794         return TT_OK;
795 }
796
797 Tt_message
798 reply_cb(Tt_message m, 
799         void *c_data, 
800         Tttk_op op, 
801         unsigned char *contents, 
802         int len, 
803         char *file)
804 {
805         char *client_procID = tt_message_handler(m);
806         if ( debug && (client_procID != NULL) ) {
807                 fprintf(stderr, "DEBUG: reply_cb():client_procID = %s\n", client_procID);
808                 fprintf(stderr, "DEBUG: reply_cb():message_op = %s\n", tt_message_op(m));
809         }
810         return(m);
811 }
812
813 static Tt_message
814 contract_cb(
815         Tt_message      msg,
816         void            *clientdata,
817         Tt_message      contract)
818 {
819
820         /* For now do nothing 
821
822         switch (op) {
823         case TTDT_QUIT:
824         case TTDT_GET_STATUS:
825         case TTDT_PAUSE:
826         case TTDT_RESUME:
827                 break;
828         }
829 */
830         return msg;
831 }
832
833 /*
834  * Handle Edit, Display and Compose requests
835  */
836 static Tt_message
837 load_cb(
838         Tt_message      msg,
839         void            *clientdata,
840         Tttk_op         op,
841         Tt_status       diagnosis,
842         unsigned char   *contents,
843         int             len,
844         char            *file,
845         char            *docname
846 )
847
848 {
849         Tt_status status;
850         char    *p;
851         DTCM_editor     *de;
852         FILE            *fp;
853         char            filename[20];
854         CmDataList              *list = NULL;
855         Dtcm_appointment        *appt;
856         int             i;
857
858         de = (DTCM_editor *)clientdata;
859
860         de->vtype = strdup(tt_message_arg_type(msg, 0));
861
862         if (diagnosis != TT_OK) {
863                 if (tt_message_status(msg) == TT_WRN_START_MESSAGE) {
864                         /*
865                          * Error in start message!  we may want to exit
866                          * here, but for now let toolkit handle error
867                          */
868                          return msg;
869                 }
870
871                 /* Let toolkit handle the error */
872                 return msg;
873         }
874
875         de->contract_pats = ttdt_message_accept(msg, contract_cb, clientdata,
876                                                 de->c->frame, 1, 1);
877
878         tt_ptype_undeclare(ptype);
879
880         if ((status = tt_ptr_error(de->contract_pats)) != TT_OK) {
881                 fprintf(stderr, "dtcm: load_cb could not accept message: %s\n",
882                         tt_status_message(status));
883         } else {
884         /*
885                 tttk_patterns_destroy(de->contract_pats);
886         */
887         }
888
889         de->read_only = False;
890
891         switch (op) {
892
893         case TTME_COMPOSE:
894                         XtSetSensitive(de->attach_button, True);
895                         break;
896         case TTME_DISPLAY:
897         case TTME_EDIT:
898                         /* for Display only messages, the "Attach" 
899                            button makes no sense */
900
901                         if (op == TTME_EDIT)
902                                 XtSetSensitive(de->attach_button, True);
903                         else {
904                                 XtSetSensitive(de->attach_button, False);
905                                 de->read_only = True;
906                         }
907                         list = CmDataListCreate();
908                         if (file == NULL)
909                         {
910                                 /*
911                                  * Save data to a file so we can pass it to parse_appt_from_file
912                                  */
913                                 
914                                 strcpy(filename, "/tmp/cmXXXXXX");
915                                 mktemp(filename);
916                                 if ((fp = fopen(filename, "w")) == 0) {
917                                         tttk_message_fail( msg, TT_DESKTOP_ENODATA, 0, 1 );
918                                         return 0;
919                                 }
920
921                                 fwrite(contents, 1, len, fp);
922                                 fclose(fp);
923                                 parse_appt_from_file(de->c->DT_catd, filename, 
924                                                 list, de->p, query_user, 
925                                                 de->c, DATAVER_ARCHIVE);
926                                 unlink(filename);
927                                 de->init = buffer_tt;
928                         }
929                         else
930                         {
931                                 de->file = strdup(file);
932                                 parse_appt_from_file(de->c->DT_catd, de->file, 
933                                                 list, de->p, query_user, 
934                                                 de->c, DATAVER_ARCHIVE);
935                                 de->init = file_tt;
936                         }
937
938                         if (appt = (Dtcm_appointment *)CmDataListGetData(list, 1)) {
939                                 dssw_attrs_to_form(de->dssw, appt);
940                                 rfp_attrs_to_form(de->rfp, appt);
941                         }
942                         for (i = 1; i <= list->count; i++)
943                                 if (appt = (Dtcm_appointment *)
944                                 CmDataListGetData(list, i)) {
945                                         if (de->orig_appt)
946                                                 free_appt_struct(&de->orig_appt);
947
948                                         de->orig_appt = appt;
949                                 }
950                         CmDataListDestroy(list, B_FALSE);
951
952                         break;
953         }
954
955         de->contract = msg;
956         de->modified = False;
957         de->dsswFlags = de->rfpFlags = 0;
958
959         tt_free((caddr_t)contents);
960         tt_free(file);
961         tt_free(docname);
962
963         return 0;
964 }
965
966 static void
967 handle_drop_cb(
968         Widget          w,
969         XtPointer       client_data,
970         XtPointer       call_data)
971 {
972         Display         *display = XtDisplay(w);
973         DtDndDropCallbackStruct *transfer_info = (DtDndDropCallbackStruct *)call_data;
974         DTCM_editor     *de;
975         char            filename[20];
976         char            *data;
977         int             size;
978         FILE            *fp;
979         int             i;
980
981         de = (DTCM_editor *)client_data;
982
983         transfer_info->status = DtDND_SUCCESS;
984
985         for (i = 0; i < transfer_info->dropData->numItems; i++) {
986                 switch(transfer_info->dropData->protocol) {
987                 case DtDND_FILENAME_TRANSFER:
988                         /* REMIND -- handle multiple filenames */
989                         data = transfer_info->dropData->data.files[0];
990         
991                         de->file = strdup(data);
992                         load_from_file(de);
993                         break;
994                 case DtDND_BUFFER_TRANSFER:
995         
996                         /*
997                          * Save data to a file so we can pass it to drag_load_proc().
998                          */
999                         strcpy(filename, "/tmp/cmXXXXXX");
1000                         mktemp(filename);
1001         
1002                         if ((fp = fopen(filename, "w")) == 0) {
1003                                 transfer_info->status = DtDND_FAILURE;
1004                                 return;
1005                         }
1006         
1007                         data = transfer_info->dropData->data.buffers[0].bp;
1008                         size = transfer_info->dropData->data.buffers[0].size;
1009                         fwrite(data, 1, size, fp);
1010                         fclose(fp);
1011         
1012                         de->file = strdup(filename);
1013                         load_from_file(de);
1014         
1015                         unlink(filename);
1016                         break;
1017                 default:
1018                         transfer_info->status = DtDND_FAILURE;
1019                         return;
1020                 }
1021         }
1022
1023         return;
1024 }
1025
1026 void
1027 de_register_drop_site(
1028         DTCM_editor     *de,
1029         Widget          w,
1030         Boolean         registerchildren)
1031
1032 {
1033         XtCallbackRec   transfer_cb_rec[] = { {handle_drop_cb, NULL},
1034                                               {NULL, NULL} };
1035         Display         *display = XtDisplayOfObject(w);
1036
1037         transfer_cb_rec[0].closure = (XtPointer)de;
1038
1039         DtDndVaDropRegister(w, DtDND_FILENAME_TRANSFER | DtDND_BUFFER_TRANSFER,
1040                         XmDROP_COPY | XmDROP_MOVE,
1041                         transfer_cb_rec, 
1042                         DtNregisterChildren, registerchildren,  
1043                         NULL);
1044         return;
1045 }
1046
1047 /*
1048 **  Main line
1049 */
1050 int 
1051 main(int argc, char **argv) {
1052         int             dssw_loffset, rfp_loffset, start, stop;
1053         Dimension       dssw_x, rfp_x;
1054         DTCM_editor     *de;
1055         XmString        xmstr;
1056         Boolean         btn1_transfer;
1057         Dimension       width, longest_dssw_label, longest_rfp_label;
1058         WidgetList      children;
1059         Widget          widgets[20];
1060         int             i = 0;
1061         int             j = 0;
1062         int             n;
1063         char            *title;
1064         XmString        label_str;
1065         Tt_status       status;
1066
1067         de = (DTCM_editor *)ckalloc(sizeof(DTCM_editor));
1068         de->file = NULL;
1069
1070         if (argc > 1) {
1071                 if (strcasecmp(argv[1], "-h") == 0)
1072                         display_command_usage();
1073                 else if (argv[1][0] != '-')
1074                         de->file = cm_strdup(argv[1]);
1075         }
1076         
1077         XtSetLanguageProc(NULL, NULL, NULL);
1078         _DtEnvControl(DT_ENV_SET); /* set up environment variables */
1079
1080         de->top_level = XtVaAppInitialize(&app,
1081                 "Dtcm", NULL, 0, &argc, argv, NULL,
1082                 XmNallowShellResize, True,
1083                 XmNdeleteResponse, XmDO_NOTHING,
1084                 NULL);
1085         setup_quit_handler(de->top_level, de_quit_handler, (caddr_t)de);
1086
1087         /*
1088         **  First, create a calendar and fill only the stuff we're going to
1089         **  use.
1090         */
1091         calendar = (Calendar *)ckalloc(sizeof(Calendar));
1092         de->c = calendar;
1093         de->p = (Props *)ckalloc(sizeof(Props));
1094         de->c->properties = (caddr_t)de->p;
1095         de->c->general = (General*) ckalloc(sizeof(General));
1096         de->c->general->version = DATAVER_ARCHIVE;
1097         de->c->frame = de->top_level;
1098         de->pu = (Props_pu *)ckalloc(sizeof(Props_pu));
1099         de->c->properties_pu = (caddr_t)de->pu;
1100         de->init = no_tt;
1101         read_props(de->p);
1102         cal_convert_cmrc(de->p);
1103         if ((start = get_int_prop(de->p, CP_DAYBEGIN)) < 0)
1104                 start = 0;
1105         else if (start > 22)
1106                 start = 22;
1107         if ((stop = get_int_prop(de->p, CP_DAYEND)) <= start)
1108                 stop = start + 1;
1109         else if (stop > 23)
1110                 stop = 23;
1111         set_int_prop(de->p, CP_DAYBEGIN, start);
1112         set_int_prop(de->p, CP_DAYEND, stop);
1113         de->c->calname = cm_strdup(get_char_prop(de->p, CP_DEFAULTCAL));
1114         de->c->DT_catd = catopen(DTCM_CAT, NL_CAT_LOCALE);
1115
1116         /* Open the message catalog for internationalization */
1117         calendar->DT_catd = catopen(DTCM_CAT, NL_CAT_LOCALE);
1118
1119         title = XtNewString(catgets(calendar->DT_catd, 1, 1074, 
1120                                                 "Calendar Appointment"));
1121         XtVaSetValues(de->top_level, 
1122                 XmNtitle, title,
1123                 NULL);
1124         XtFree(title);
1125
1126         /*
1127         **  Okay, now create the form manager and the widgets
1128         */
1129         de->form = XtVaCreateWidget("form",
1130                 xmFormWidgetClass, de->top_level,
1131                 XmNautoUnmanage, True,
1132                 XmNfractionBase, 5,
1133                 NULL);
1134
1135         label_str = XmStringCreateLocalized(catgets(de->c->DT_catd, 1, 846, "Save"));
1136         de->attach_button = XtVaCreateWidget("attach_button",
1137                 xmPushButtonGadgetClass, de->form,
1138                 XmNlabelString, label_str,
1139                 XmNleftAttachment, XmATTACH_POSITION,
1140                 XmNleftPosition, 1,
1141                 XmNrightAttachment, XmATTACH_POSITION,
1142                 XmNrightPosition, 2,
1143                 XmNbottomAttachment, XmATTACH_FORM,
1144                 XmNbottomOffset, 10,
1145                 NULL);
1146         XmStringFree(label_str);
1147         XtAddCallback(de->attach_button, XmNactivateCallback, de_apply_proc, (XtPointer)de);
1148         if (!de->file)
1149                 XtSetSensitive(de->attach_button, False);
1150
1151         label_str = XmStringCreateLocalized(catgets(de->c->DT_catd, 1, 691, "Reset"));
1152         de->reset_button = XtVaCreateWidget("reset_button",
1153                 xmPushButtonGadgetClass, de->form,
1154                 XmNlabelString, label_str,
1155                 XmNleftAttachment, XmATTACH_POSITION,
1156                 XmNleftPosition, 3,
1157                 XmNrightAttachment, XmATTACH_POSITION,
1158                 XmNrightPosition, 4,
1159                 XmNbottomAttachment, XmATTACH_FORM,
1160                 XmNbottomOffset, 10,
1161                 NULL);
1162         XmStringFree(label_str);
1163         XtAddCallback(de->reset_button, XmNactivateCallback, de_reset_proc, de);
1164
1165         create_all_pixmaps(de->pu, de->form);
1166
1167         de->rfpFlags = 0;
1168         de->rfp = (RFP *)ckalloc(sizeof(RFP));
1169         build_rfp(de->rfp, de->c, de->form);
1170         XtVaSetValues(de->rfp->rfp_form_mgr,
1171                 XmNbottomAttachment, XmATTACH_WIDGET,
1172                 XmNbottomWidget, de->attach_button,
1173                 XmNbottomOffset, 25,
1174                 XmNleftAttachment, XmATTACH_FORM,
1175                 XmNleftOffset, 5,
1176                 NULL);
1177         XtVaGetValues(de->rfp->rfp_form_mgr,
1178                 XmNchildren,            &children,
1179                 XmNnumChildren,         &n,
1180                 NULL);
1181         /* We don't want to manage the privacy widgets */
1182         for (i = 0; i < n; i++) {
1183                 if ((children[i] == de->rfp->privacy_label) ||
1184                     (children[i] == de->rfp->privacy_menu))
1185                     continue;
1186                 widgets[j++] = children[i];
1187         }
1188         XtManageChildren(widgets, n - 2);       
1189
1190         /*
1191          * Add a drag source icon inside the dssw, lower right
1192          */
1193         xmstr = XmStringCreateLocalized(
1194                         catgets(de->c->DT_catd, 1, 627, "Drag Appt"));
1195         de->drag_source = XtVaCreateWidget("drag_source",
1196                 dtIconGadgetClass, de->form,
1197                 XmNpixmapPosition, XmPIXMAP_TOP,
1198                 XmNstringPosition, XmSTRING_BOTTOM,
1199                 XmNalignment, XmALIGNMENT_CENTER,
1200                 XmNstring, xmstr,
1201                 XmNbottomAttachment, XmATTACH_WIDGET,
1202                 XmNbottomWidget, de->attach_button,
1203                 XmNbottomOffset, 25,
1204                 XmNleftAttachment, XmATTACH_WIDGET,
1205                 XmNleftWidget, de->rfp->rfp_form_mgr,
1206                 XmNrightAttachment, XmATTACH_FORM,
1207                 XmNtraversalOn, False,
1208                 NULL);
1209         XmStringFree(xmstr);
1210
1211         XtAddEventHandler(XtParent(de->drag_source), Button1MotionMask, False,
1212                 (XtEventHandler)EditApptDragMotionHandler, (XtPointer) de);
1213
1214         XtVaGetValues((Widget)XmGetXmDisplay(XtDisplay(de->form)),
1215                 "enableBtn1Transfer",   &btn1_transfer,
1216                 NULL);
1217
1218         /* btn1_transfer is a tri-state variable - see 1195846 */
1219         if ((Boolean)btn1_transfer != True)
1220                 XtAddEventHandler(XtParent(de->drag_source),
1221                                 Button2MotionMask, False,
1222                                 (XtEventHandler)EditApptDragMotionHandler,
1223                                 (XtPointer) de);
1224
1225
1226         if (de->pu->drag_icon_xbm)
1227                 XtVaSetValues(de->drag_source,
1228                                 XmNpixmap, de->pu->drag_icon_xbm,
1229                                 NULL);
1230
1231         de->dsswFlags = 0;
1232         de->dssw = (DSSW *)ckalloc(sizeof(DSSW));
1233         build_dssw(de->dssw, de->c, de->form, True, True);
1234         XtVaSetValues(de->dssw->dssw_form_mgr,
1235                 XmNtopAttachment, XmATTACH_FORM,
1236                 XmNtopOffset, 10,
1237                 XmNbottomAttachment, XmATTACH_WIDGET,
1238                 XmNbottomWidget, de->rfp->rfp_form_mgr,
1239                 XmNbottomOffset, 15,
1240                 XmNleftAttachment, XmATTACH_FORM,
1241                 XmNleftOffset, 10,
1242                 NULL);
1243         ManageChildren(de->dssw->dssw_form_mgr);
1244
1245         /* set up callback to detect whether the appointment 
1246            definition has been modified */
1247
1248         XtAddCallback(de->dssw->start_text, XmNvalueChangedCallback, de_mark_change, de);
1249         XtAddCallback(de->dssw->start_am, XmNvalueChangedCallback, de_mark_change, de);
1250         XtAddCallback(de->dssw->start_pm, XmNvalueChangedCallback, de_mark_change, de);
1251         XtAddCallback(de->dssw->stop_text, XmNvalueChangedCallback, de_mark_change, de);
1252         XtAddCallback(de->dssw->stop_am, XmNvalueChangedCallback, de_mark_change, de);
1253         XtAddCallback(de->dssw->stop_pm, XmNvalueChangedCallback, de_mark_change, de);
1254         XtAddCallback(de->dssw->what_text, XmNvalueChangedCallback, de_mark_change, de);
1255         XtAddCallback(de->rfp->repeat_menu, XmNselectionCallback, de_mark_change, de);
1256         XtAddCallback(de->rfp->for_menu, XmNselectionCallback, de_mark_change, de);
1257
1258         ManageChildren(de->form);
1259         XtManageChild(de->form);
1260         XtRealizeWidget(de->top_level);
1261
1262         /*
1263         **  Do some monkeying to compensate for Motif's pitiful form managers
1264         XtVaGetValues(de->dssw->start_menu, XmNx, &dssw_x,
1265                 XmNleftOffset, &dssw_loffset,
1266                 NULL);
1267         XtVaGetValues(de->rfp->repeat_menu, XmNx, &rfp_x,
1268                 XmNleftOffset, &rfp_loffset,
1269                 NULL);
1270
1271         if (dssw_x > rfp_x)
1272                 XtVaSetValues(de->rfp->rfp_form_mgr,
1273                         XmNleftOffset, rfp_loffset + (dssw_x - rfp_x),
1274                         NULL);
1275         else if (rfp_x > dssw_x)
1276                 XtVaSetValues(de->dssw->dssw_form_mgr,
1277                         XmNleftOffset, dssw_loffset + (rfp_x - dssw_x),
1278                         NULL);
1279         */
1280
1281 /*      Don't need these any more.      
1282         XtVaGetValues(de->dssw->date_label, XmNwidth, &longest_dssw_label, NULL);
1283
1284         XtVaGetValues(de->dssw->start_label, XmNwidth, &width, NULL);
1285         if (width > longest_dssw_label)
1286                 longest_dssw_label = width;
1287
1288         XtVaGetValues(de->dssw->stop_label, XmNwidth, &width, NULL);
1289         if (width > longest_dssw_label)
1290                 longest_dssw_label = width;
1291
1292         XtVaGetValues(de->dssw->what_label, XmNwidth, &width, NULL);
1293         if (width > longest_dssw_label)
1294                 longest_dssw_label = width;
1295
1296         XtVaGetValues(de->rfp->frequency_label, XmNwidth, &longest_rfp_label, NULL);
1297         XtVaGetValues(de->rfp->for_label, XmNwidth, &width, NULL);
1298         if (width > longest_rfp_label)
1299                 longest_rfp_label = width;
1300
1301         XtVaSetValues(de->dssw->dssw_form_mgr, 
1302                         XmNleftOffset, longest_rfp_label - longest_dssw_label, 
1303                         NULL);
1304 */
1305
1306         de_set_defaults(de);
1307
1308         de_register_drop_site(de, de->form, True);
1309         de_register_drop_site(de, de->dssw->dssw_form_mgr, False);
1310         de_register_drop_site(de, de->rfp->rfp_form_mgr, False); 
1311
1312         XmProcessTraversal(de->dssw->what_text, XmTRAVERSE_CURRENT);
1313         XtVaSetValues(de->form, XmNinitialFocus, de->dssw->what_text, NULL);
1314
1315
1316         status = cmtt_init("AppointmentEditor", de, app, calendar->frame);
1317         if (TT_OK != status) {
1318             char *errfmt;
1319             errfmt = catgets(calendar->DT_catd, 2, 2,
1320                         "Could not connect to ToolTalk:\n%s\n");
1321             DieFromToolTalkError( de, errfmt, status );
1322         }
1323
1324         XtAppMainLoop(app);
1325
1326         return 0;
1327 }