home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3064 / day.c next >
C/C++ Source or Header  |  1991-03-14  |  15KB  |  555 lines

  1. /*
  2.  *    day.c : The day windows that display a single day's appoints.
  3.  *
  4.  *    George Ferguson, ferguson@cs.rochester.edu, 27 Oct 1990.
  5.  *    Version 1.1 - 27 Feb 1991.
  6.  *
  7.  *    $Id: day.c,v 2.2 91/03/13 13:31:09 ferguson Exp $
  8.  */
  9. #include <stdio.h>
  10. #include <ctype.h>
  11. #include <X11/Intrinsic.h>
  12. #include <X11/Shell.h>
  13. #include <X11/StringDefs.h>
  14. #include <X11/Xaw/Form.h>
  15. #include <X11/Xaw/Command.h>
  16. #include <X11/Xaw/Label.h>
  17. #include <X11/Xaw/AsciiText.h>
  18. #include <X11/Xaw/Cardinals.h>
  19. #include "xkal.h"
  20. #include "month.h"
  21. #include "day.h"
  22. #include "db.h"
  23. #include "util.h"
  24. #include "date-strings.h"
  25. #include "app-resources.h"
  26. #include "patchlevel.h"
  27. #ifdef USE_ALERT
  28. #include "alert.h"
  29. #endif
  30.  
  31. /*
  32.  * Functions defined in this file
  33.  */
  34. DayFormData *createDayFormData(),*createPopupDayFormData();
  35. void setDayFormData();
  36. void checkpointAppoints();
  37. int timetopos();
  38. void postotime();
  39.  
  40. static void textEventProc(),dismissProc(),selectProc(),unselectProc();
  41. static char *skipLabel();
  42.  
  43. /*
  44.  * Data defined in this file
  45.  */
  46. DayFormData *currentDayFormData;
  47. int numDisplayedAppoints = -1;        /* total number of text widgets */
  48. int displayStartTime;            /* time of earliest appoint (mins) */
  49. int displayEndTime;            /* time of latest appoint (mins) */
  50. int displayInc;                /* interval between appoints (mins) */
  51. int displayFirstNotePos;        /* index of first notes entry */
  52.  
  53. static int dayAppointsChanged;        /* fast, local test for changes */
  54. static Msgrec **apps;            /* used for lookupEntry() to set */
  55. static int *appFlags;            /* used in setDayFormData() */
  56.  
  57. #define CALLBACK_PROC(NAME)     static void NAME(w,client_data,call_data) \
  58.                     Widget w; \
  59.                     caddr_t client_data,call_data;
  60.  
  61.  
  62. /*    -    -    -    -    -    -    -    -    */
  63. /* Creation routines */
  64. /*
  65.  * createDayFormData() : Creates a new dayForm. The first time this
  66.  *    routine is called, it sets several globals to prevent having
  67.  *    to parse them from the resources every time. malloc()'s tons
  68.  *    of space, for all the variable length stuff we allow.
  69.  */
  70. DayFormData *
  71. createDayFormData(parent)
  72. Widget parent;
  73. {
  74.     DayFormData *d;
  75.     DayTextData *t;
  76.     Arg args[3];
  77.     int i,n;
  78.  
  79.     if (numDisplayedAppoints == -1) {
  80.     displayStartTime = strtotime(appResources.appsStartTime);
  81.     displayEndTime = strtotime(appResources.appsEndTime);
  82.     displayInc = strtotime(appResources.appsIncrement);
  83.     displayFirstNotePos = (displayEndTime-displayStartTime)/displayInc+1;
  84.     numDisplayedAppoints = displayFirstNotePos+appResources.numNotes;
  85.     apps = (Msgrec **)XtCalloc(numDisplayedAppoints,sizeof(Msgrec *));
  86.     appFlags = (int *)XtCalloc(numDisplayedAppoints,sizeof(int));
  87.     }
  88.     d = XtNew(DayFormData);
  89.     d->form = XtCreateManagedWidget("dayForm",formWidgetClass,parent,
  90.                                 NULL,ZERO);
  91.     if (appResources.bothShown || !appResources.useTitlebar) {
  92.     d->date = XtCreateManagedWidget("dayLabel",labelWidgetClass,d->form,
  93.                                 NULL,ZERO);
  94.     } else {
  95.     d->date = NULL;
  96.     }
  97.     d->items = (DayTextData **)XtCalloc(numDisplayedAppoints,
  98.                             sizeof(DayTextData));
  99.     i = 0;
  100.     XtSetArg(args[0],XtNfromVert,d->date);
  101.     for (n = displayStartTime; n <= displayEndTime; n += displayInc) {
  102.     t = d->items[i++] = XtNew(DayTextData);
  103.     XtSetArg(args[1],XtNfromHoriz,NULL);
  104.     XtSetArg(args[2],XtNlabel,timetostr(n));
  105.     t->time = XtCreateManagedWidget("appointLabel",labelWidgetClass,
  106.                             d->form,args,THREE);
  107.     XtSetArg(args[1],XtNfromHoriz,t->time);
  108.     t->text = XtCreateManagedWidget("appointText",asciiTextWidgetClass,
  109.                             d->form,args,TWO);
  110.     XtAddEventHandler(t->text,KeyPressMask|ButtonPressMask,False,
  111.                             textEventProc,d);
  112.     XtSetArg(args[0],XtNfromVert,t->time);
  113.     }
  114.     for (n = 0; n < appResources.numNotes; n++) {
  115.     t = d->items[i++] = XtNew(DayTextData);
  116.     XtSetArg(args[1],XtNfromHoriz,NULL);
  117.     XtSetArg(args[2],XtNlabel,appResources.notesLabel);
  118.     t->time = XtCreateManagedWidget("appointLabel",labelWidgetClass,
  119.                             d->form,args,THREE);
  120.     XtSetArg(args[1],XtNfromHoriz,t->time);
  121.     t->text = XtCreateManagedWidget("appointText",asciiTextWidgetClass,
  122.                             d->form,args,TWO);
  123.     XtAddEventHandler(t->text,KeyPressMask|ButtonPressMask,False,
  124.                             textEventProc,d);
  125.     XtSetArg(args[0],XtNfromVert,t->time);
  126.     }
  127.     d->shell = toplevel;    /* may be overridden */
  128.     d->msgText = (String *)XtCalloc(numDisplayedAppoints,sizeof(String));
  129.     d->msg = (Msgrec **)XtCalloc(numDisplayedAppoints,sizeof(Msgrec *));
  130.     d->changed = False;
  131.     d->editing = False;
  132.     return(d);
  133. }
  134.  
  135. /*
  136.  * createPopupDayFormData() : Creates a popup dayForm by wrapping one of
  137.  *    the above dayForms in a shell and adding some buttons.
  138.  */
  139. DayFormData *
  140. createPopupDayFormData()
  141. {
  142.     Widget shell,form,select,dismiss,unselect;
  143.     DayFormData *d;
  144.  
  145.     shell = XtCreatePopupShell("popupDayShell",topLevelShellWidgetClass,
  146.                             toplevel,NULL,ZERO);
  147.     form = XtCreateManagedWidget("popupDayForm",formWidgetClass,shell,
  148.                                 NULL,ZERO);
  149.     dismiss = XtCreateManagedWidget("dismissButton",commandWidgetClass,form,
  150.                                 NULL,ZERO);
  151.     select = XtCreateManagedWidget("selectButton",commandWidgetClass,form,
  152.                                 NULL,ZERO);
  153.     unselect = XtCreateManagedWidget("unselectButton",commandWidgetClass,form,
  154.                                 NULL,ZERO);
  155.     d = createDayFormData(form);
  156.     d->shell = shell;        /* override default */
  157.     XtAddCallback(select,"callback",selectProc,d);
  158.     XtAddCallback(dismiss,"callback",dismissProc,d);
  159.     XtAddCallback(unselect,"callback",unselectProc,d);
  160.     XtPopup(shell,XtGrabNone);
  161.     return(d);
  162. }
  163.  
  164. /*    -    -    -    -    -    -    -    -    */
  165. /* Event handlers */
  166.  
  167. /*ARGSUSED*/
  168. /*
  169.  * textEventProc() : Called whenever the user types in any slot. This
  170.  *    provides a quick check as to whether the appoints have changed.
  171.  *    Note that this does NOT detect, eg., selection pastes, as documented
  172.  *    in the BUGS section of the man page.
  173.  */
  174. static void
  175. textEventProc(w,client_data,event,continue_flag)
  176. Widget w;
  177. XtPointer client_data;
  178. XEvent *event;
  179. Boolean *continue_flag;
  180. {
  181.     DayFormData *d = (DayFormData *)client_data;
  182.  
  183.     d->changed = True;
  184. }
  185.  
  186. /*
  187.  * dismissProc() : Callback for dismiss button - Destroy a dayForm unless
  188.  *    one of it's appoints is being edited.
  189.  */
  190. /*ARGSUSED*/
  191. CALLBACK_PROC(dismissProc)
  192. {
  193.     DayFormData *d = (DayFormData *)client_data;
  194.     int i;
  195.  
  196.     if (d->editing) {
  197.     XBell(display,0);
  198. #ifdef USE_ALERT
  199.     alert("Error: Can't dimiss day while editing it.");
  200. #else
  201.     fprintf(stderr,"\007%s: can't dimiss day while editing it\n",program);
  202. #endif
  203.     return;
  204.     }
  205.     checkpointAppoints(d);
  206.     if (currentDayFormData == d)
  207.     currentDayFormData = NULL;
  208.     XtPopdown(d->shell);
  209.     XtDestroyWidget(d->shell);
  210.     for (i=0; i < numDisplayedAppoints; i++)
  211.     XtFree(d->msgText[i]);
  212.     XtFree(d->msgText);
  213. }
  214.  
  215. /*
  216.  * selectProc() : Callback for select button - Make this dayForm the
  217.  *    current one, resensitizing its widgets.
  218.  */
  219. /*ARGSUSED*/
  220. CALLBACK_PROC(selectProc)
  221. {
  222.     DayFormData *d = (DayFormData *)client_data;
  223.  
  224.     checkpointAppoints(d);
  225.     if (currentDayFormData == d)
  226.     return;
  227.     if (currentDayFormData != NULL)
  228.     XtSetSensitive(currentDayFormData->form,False);
  229.     XtSetSensitive(d->form,True);
  230.     currentDayFormData = d;
  231. }
  232.  
  233. /*
  234.  * unselectProc() : Callback for unselect button - Desensitize this dayForm's
  235.  *    widgets, and make there be no current dayForm.
  236.  */
  237. /*ARGSUSED*/
  238. CALLBACK_PROC(unselectProc)
  239. {
  240.     DayFormData *d = (DayFormData *)client_data;
  241.  
  242.     checkpointAppoints(d);
  243.     XtSetSensitive(d->form,False);
  244.     currentDayFormData = NULL;
  245. }
  246.  
  247. /*    -    -    -    -    -    -    -    -    */
  248. /* Set/reset routines */
  249.  
  250. /*
  251.  * clearDayFormData() : Clear all the text widgets (slots) and free the
  252.  *    memory used to hold their (original) contents.
  253.  */
  254. void
  255. clearDayFormData(d)
  256. DayFormData *d;
  257. {
  258.     Arg args[1];
  259.     int i;
  260.  
  261.     XtSetArg(args[0],XtNlabel,"");
  262.     if (d->date != NULL)
  263.     XtSetValues(d->date,args,ONE);
  264.     for (i=0; i < numDisplayedAppoints; i++) {
  265.     XtSetArg(args[0],XtNstring,"");
  266.     XtSetValues(d->items[i]->text,args,ONE);
  267.     XtFree(d->msgText[i]);
  268.     d->msgText[i] = NULL;
  269.     }
  270. }
  271.  
  272. /*
  273.  * setDayFormData() : Sets all the text widgets and the label and/or
  274.  *    titlebar. Saves these texts in malloc()'d space so we can test
  275.  *    later and see if they've changed. Looks after relabelling items
  276.  *    that had to be moved due to collisions.
  277.  */
  278. void
  279. setDayFormData(d,day,month,year)
  280. DayFormData *d;
  281. int day,month,year;
  282. {
  283.     Arg args[2];
  284.     char text[32];
  285.     int dow,i,j,n,pos;
  286.     Boolean exact;
  287.  
  288.     /*
  289.      * set the label at the top of the day
  290.      */
  291.     dow = computeDOW(day,month,year);
  292.     if (appResources.bothShown || !appResources.useTitlebar) {
  293.     sprintf(text,"%s %d %s %d",shortDowStr[dow-1],day,
  294.                         shortMonthStr[month-1],year);
  295.     XtSetArg(args[0],XtNlabel,text);
  296.     XtSetValues(d->date,args,ONE);
  297.     }
  298.     if (appResources.useTitlebar) {
  299.     sprintf(text,"Xkal %.2f - %s %d %s %d",XKAL_VERSION,shortDowStr[dow-1],
  300.                     day,shortMonthStr[month-1],year);
  301.     XStoreName(display,XtWindow(d->shell),text);
  302.     }
  303.     /*
  304.      * clear the saved text strings
  305.      */
  306.     for (i=0; i < numDisplayedAppoints; i++) {
  307.     XtFree(d->msgText[i]);
  308.     d->msgText[i] = NULL;
  309.     d->msg[i] = NULL;
  310.     }
  311.     /*
  312.      * get the appoints for today
  313.      */
  314.     n = lookupEntry(day,month,year,-1,-1,numDisplayedAppoints,apps,False);
  315.     if (n < 0) {
  316. #ifdef USE_ALERT
  317.     alert("Error: too many entries (>%d) for %s %d %s %d.",
  318.                     numDisplayedAppoints,
  319.                     shortDowStr[dow-1],day,
  320.                     shortMonthStr[month-1],year);
  321. #else
  322.     fprintf(stderr,"\007%s: too many entries (>%d) for %s %d %s %d\n",
  323.                     program,numDisplayedAppoints,
  324.                     shortDowStr[dow-1],day,
  325.                     shortMonthStr[month-1],year);
  326. #endif
  327.     n = numDisplayedAppoints;
  328.     }
  329.     for (i=0; i < n; i++)
  330.     appFlags[i] = False;
  331.     /*
  332.      * set the msgText[]'s for any appoints without conflicts
  333.      */
  334.     for (i=0; i < n; i++) {
  335.     pos = timetopos(apps[i]->hour,apps[i]->mins,&exact);
  336.     /*
  337.      * skip appoints that need to be labelled no matter what
  338.      */
  339.     if (!exact)
  340.         continue;
  341.     /*
  342.      * no conflict -> save string and set flag to say it's done
  343.      */
  344.     if (d->msgText[pos] == NULL) {
  345.         d->msgText[pos] = XtNewString(apps[i]->text);
  346.         d->msg[pos] = apps[i];
  347.         appFlags[i] = True;
  348.     }
  349.     }
  350.     /*
  351.      * move appoints that had conflicts
  352.      */
  353.     for (i=0; i < n; i++) {
  354.     if (!appFlags[i]) {
  355.         pos = timetopos(apps[i]->hour,apps[i]->mins,&exact);
  356.         /*
  357.          * pos can actually be open if we skipped it above
  358.          */
  359.         if (d->msgText[pos] == NULL) {
  360.         j = pos;
  361.         } else {
  362.         /*
  363.           * scan up for empty slot
  364.           */
  365.          for (j=pos+1; j < numDisplayedAppoints; j++)
  366.             if (d->msgText[j] == NULL)
  367.             break;
  368.         /*
  369.          * scan down if still not placed
  370.           */
  371.         if (j == numDisplayedAppoints) {
  372.             for (j=pos-1; j >= 0; j--)
  373.             if (d->msgText[j] == NULL)
  374.                 break;
  375.         }
  376.         }
  377.         /*
  378.          * now figure out how to label it and save the new string
  379.          */
  380.         if (!appResources.rearrangeSilently || !exact) {
  381.             if (apps[i]->hour == -1) {
  382.             if (j < displayFirstNotePos) {
  383.             /*
  384.              * was note, now not in notes -> label
  385.              */
  386.             d->msgText[j] = XtMalloc(strlen(apps[i]->text)+
  387.                     strlen(appResources.notesLabel)+1);
  388.             sprintf(d->msgText[j],"[%s] %s",appResources.notesLabel,
  389.                                 apps[i]->text);
  390.             } else {
  391.             /*
  392.              * was note, still in notes -> no label
  393.              */
  394.             d->msgText[j] = XtNewString(apps[i]->text);
  395.             }
  396.         } else {
  397.             /*
  398.              * non-note -> label
  399.              */
  400.             d->msgText[j] = XtMalloc(strlen(apps[i]->text)+9);
  401.             sprintf(d->msgText[j],"[%s] %s",
  402.                 timetostr(apps[i]->hour*60+apps[i]->mins),
  403.                 apps[i]->text);
  404.         }
  405.         } else {
  406.         /*
  407.          * user requested no labels
  408.          */
  409.         d->msgText[j] = XtNewString(apps[i]->text);
  410.         }
  411.         d->msg[j] = apps[i];
  412.     }
  413.     }
  414.     /*
  415.      * set/clear the actual widgets
  416.      */
  417.     for (i=0; i < numDisplayedAppoints; i++) {
  418.     if (d->msgText[i])
  419.         XtSetArg(args[0],XtNstring,d->msgText[i]);
  420.     else
  421.         XtSetArg(args[0],XtNstring,"");
  422.     if (d->msg[i] && d->msg[i]->system)
  423.         XtSetArg(args[1],XtNeditType,XawtextRead);
  424.     else
  425.         XtSetArg(args[1],XtNeditType,XawtextEdit);
  426.     XtSetValues(d->items[i]->text,args,TWO);
  427.     }
  428.     d->day = day;
  429.     d->month = month;
  430.     d->year = year;
  431. }
  432.  
  433. /*
  434.  * checkpointAppoints() : Transfers the appoints from the text widgets
  435.  *    to the DB, adding or deleting entries as required. Looks after
  436.  *    parsing back the labelling that was possibly added above.
  437.  */
  438. void
  439. checkpointAppoints(d)
  440. DayFormData *d;
  441. {
  442.     Arg args[2];
  443.     int i,type,h,m,n;
  444.     char *new,c;
  445.  
  446.     if (!d->changed)
  447.     return;
  448.     XtSetArg(args[0],XtNeditType,&type);
  449.     XtSetArg(args[1],XtNstring,&new);
  450.     for (i=0; i < numDisplayedAppoints; i++) {
  451.     XtGetValues(d->items[i]->text,args,TWO);
  452.     if (type == XawtextRead)
  453.         continue;
  454.     if (d->msgText[i] && strcmp(d->msgText[i],new) != 0) {
  455.         deleteEntry(0,d->year,d->month,d->day,d->msg[i]->hour,
  456.                     d->msg[i]->mins,d->msg[i]->text);
  457.         d->msg[i] = NULL;
  458.         appointsChanged = True;
  459.     }
  460.     if (*new != '\0' && (!d->msgText[i] ||
  461.                 strcmp(d->msgText[i],new) != 0)) {
  462.         if (*new == '[') {
  463.         n = strtotime(new+1);
  464.         if (n == -1) {
  465.             h = m = -1;        /* and leave trash in text */
  466.         } else {
  467.             h = n / 60;
  468.             m = n % 60;
  469.             new = skipLabel(new);
  470.         }
  471.         } else {
  472.         postotime(i,&h,&m);
  473.         }
  474.         d->msg[i] = addEntry(0,d->year,d->month,d->day,h,m,new,False,
  475.                         appResources.defaultLevel);
  476.         XtFree(d->msgText[i]);
  477.         d->msgText[i] = XtNewString(new);
  478.         appointsChanged = True;
  479.     }
  480.     }
  481.     d->changed = False;
  482.     if (currentDayFormData != NULL)
  483.     shadeButton(currentDayFormData->buttonData,(GC)NULL,0,0);
  484. }
  485.  
  486. /*
  487.  * skipLabels() : Skip over "[...]" in a string.
  488.  */
  489. static char *
  490. skipLabel(s)
  491. char *s;
  492. {
  493.     if (*s != '[')
  494.     return(s);
  495.     while (*s != ']')
  496.     s += 1;
  497.     s += 1;
  498.     while (*s && isspace(*s))
  499.     s += 1;
  500.     return(s);
  501. }
  502.  
  503. /*    -    -    -    -    -    -    -    -    */
  504. /* Various conversion routines (see also util.c) */
  505.  
  506. /*
  507.  * timetopos(h,m,exactp) : Returns the appropriate place in the DayForm for
  508.  *    the given time. *exactp is set to True if this is exact, False if it
  509.  *    has been rounded (and hence if the appoint should be labelled).
  510.  */
  511. int
  512. timetopos(h,m,exactp)
  513. int h,m;
  514. Boolean *exactp;
  515. {
  516.     int t;
  517.  
  518.     if (h == -1) {
  519.     *exactp = True;
  520.     return(displayFirstNotePos);
  521.     }
  522.     if (m != -1)
  523.     t = h * 60 + m;
  524.     else
  525.     t = h * 60;
  526.     if (t < displayStartTime || t > displayEndTime) {
  527.     *exactp = False;
  528.     return(displayFirstNotePos);
  529.     } else {
  530.     *exactp = (t % displayInc == 0);
  531.     return((t-displayStartTime)/displayInc);
  532.     }
  533. }
  534.  
  535. /*
  536.  * postotime(p,h,m) : Sets *h and *m to a time corresponding to the position
  537.  *    given by p.
  538.  */
  539. void
  540. postotime(p,h,m)
  541. int p;
  542. int *h,*m;
  543. {
  544.     int t;
  545.  
  546.     if (p >= displayFirstNotePos) {
  547.     *h = *m = -1;
  548.     } else {
  549.     t = displayStartTime + p * displayInc;
  550.     *h = t / 60;
  551.     *m = t % 60;
  552.     }
  553. }
  554.  
  555.