/* stuff to control the calendar in the main menu. */ #include #include #include #include #include #include #include #include #include #include #include #include "xephem.h" static void today_cb (Widget w, XtPointer client, XtPointer call); static void date_changed_cb (Widget w, XtPointer client, XtPointer call); static void mm_nfmoon (double jd, double tzone, int m, int f, int nd); #define CAL_ROWS 6 /* rows in the date matrix */ #define CAL_COLS 7 /* columns in the date matrix */ #define NYEARS 12 /* num entries in the year pulldown */ #define NMONTHS 12 /* num entries in the month pulldown */ static Widget m_w, mmenu_w[NMONTHS]; /* month cascade btn and pulldwn menu */ static Widget y_w, ymenu_w[NYEARS]; /* year cascade btn and pulldwn menu */ static Widget d_w[CAL_ROWS*CAL_COLS]; /* pushbtns in the date matrix */ static Widget tz_w; /* timezone + title label */ /* must all be fixed-width since XmMENU_BAR RowColumns don't allow for resizing. */ static char mnames[][10] = { "January ", "February ", "March ", "April ", "May ", "June ", "July ", "August ", "September", "October ", "November ", "December " }; typedef struct { char name[3]; Widget w; } DOW; static DOW dnames[CAL_COLS] = { {"Su"}, {"Mo"}, {"Tu"}, {"We"}, {"Th"}, {"Fr"}, {"Sa"} }; enum {DAY, MONTH, YEAR}; typedef enum { BACKBACK, BACK, TONOW, FORW, FORWFORW } TodayCuts; static Pixel fg_pix, bg_pix; /* used to reverse colors "today" */ #define NO_DATE (999) /* anything outside -31..31 */ /* create a calendar from parent and return the outtermost widget. */ Widget calm_create (parent) Widget parent; { Arg args[20]; Widget form_w; Widget rc_w; Widget mb_w; Widget menu_w; Widget bw, fw; Widget w; int n; int i; /* create the outter form */ n = 0; form_w = XmCreateForm (parent, "CalForm", args, n); /* put a timezone and title at the top */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_CENTER); n++; tz_w = XmCreateLabel (form_w, "Calendar", args, n); wtip (tz_w, "Controls to set the date"); XtManageChild (tz_w); /* create a menu bar for the Month and Year menus */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, tz_w); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightOffset, 3); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftOffset, 3); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; XtSetArg (args[n], XmNmarginWidth, 1); n++; mb_w = XmCreateMenuBar (form_w, "MB", args, n); XtManageChild (mb_w); /* create the Month and Year cascade button/pulldown menu */ n = 0; menu_w = XmCreatePulldownMenu (mb_w, "MonthPD", args, n); /* managed by the CascadeButton */ for (i = 0; i < NMONTHS; i++) { n = 0; XtSetArg (args[n], XmNuserData, i+1); n++; w = XmCreatePushButton (menu_w, mnames[i], args, n); XtAddCallback (w, XmNactivateCallback, date_changed_cb, (XtPointer)MONTH); XtManageChild (w); mmenu_w[i] = w; } n = 0; XtSetArg (args[n], XmNsubMenuId, menu_w); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; m_w = XmCreateCascadeButton (mb_w, "MonthCB", args, n); XtManageChild (m_w); wtip (m_w, "Click to set the month by picking from a list"); n = 0; menu_w = XmCreatePulldownMenu (mb_w, "YearPD", args, n); /* managed by the CascadeButton */ for (i = 0; i < NYEARS; i++) { n = 0; w = XmCreatePushButton (menu_w, "YearPB", args, n); XtAddCallback (w, XmNactivateCallback, date_changed_cb, (XtPointer)YEAR); XtManageChild (w); ymenu_w[i] = w; } n = 0; XtSetArg (args[n], XmNsubMenuId, menu_w); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; y_w = XmCreateCascadeButton (mb_w, "YearCB", args, n); XtManageChild (y_w); wtip (y_w, "Click to set a recent year by picking from a list"); /* slide to the right */ set_something (mb_w, XmNmenuHelpWidget, (XtArgVal)y_w); /* create the rowcol for the main date matrix */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopWidget, mb_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNnumColumns, CAL_ROWS+1); n++; /* +1 for dnames*/ XtSetArg (args[n], XmNpacking, XmPACK_COLUMN); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNentryAlignment, XmALIGNMENT_END); n++; XtSetArg (args[n], XmNadjustMargin, False); n++; XtSetArg (args[n], XmNspacing, 0); n++; rc_w = XmCreateRowColumn (form_w, "CalRC", args, n); XtManageChild (rc_w); /* add the fixed day abbreviations */ for (i = 0; i < XtNumber(dnames); i++) { n = 0; dnames[i].w = XmCreateLabel (rc_w, "XX", args, n); XtManageChild (dnames[i].w); } /* add the calendar entries proper */ for (i = 0; i < CAL_ROWS*CAL_COLS; i++) { n = 0; XtSetArg (args[n], XmNrecomputeSize, False); n++; /* SLOW!! */ d_w[i] = XmCreatePushButton (rc_w, "CD", args, n); XtAddCallback (d_w[i], XmNactivateCallback, date_changed_cb, (XtPointer)DAY); XtManageChild (d_w[i]); } /* add the Now and surrounding buttons */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; w = XmCreatePushButton (form_w, "<<", args, n); XtManageChild (w); XtAddCallback (w, XmNactivateCallback, today_cb, (XtPointer)BACKBACK); wtip (w, "Move back one week"); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, w); n++; bw = XmCreatePushButton (form_w, "<", args, n); XtManageChild (bw); XtAddCallback (bw, XmNactivateCallback, today_cb, (XtPointer)BACK); wtip (bw, "Move back one day"); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; w = XmCreatePushButton (form_w, ">>", args, n); XtManageChild (w); XtAddCallback (w, XmNactivateCallback, today_cb, (XtPointer)FORWFORW); wtip (w, "Move forward one week"); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNrightWidget, w); n++; fw = XmCreatePushButton (form_w, ">", args, n); XtManageChild (fw); XtAddCallback (fw, XmNactivateCallback, today_cb, (XtPointer)FORW); wtip (fw, "Move forward one day"); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNtopOffset, 3); n++; XtSetArg (args[n], XmNtopWidget, rc_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, bw); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNrightWidget, fw); n++; w = XmCreatePushButton (form_w, "Now", args, n); XtManageChild (w); XtAddCallback (w, XmNactivateCallback, today_cb, (XtPointer)TONOW); wtip (w, "Set time and date from computer clock"); get_something (d_w[0], XmNforeground, (XtArgVal)&fg_pix); get_something (d_w[0], XmNbackground, (XtArgVal)&bg_pix); return (form_w); } /* called when new resources have been set so we can update fg/bg_pix */ void calm_newres() { Widget middle = d_w[CAL_COLS*CAL_ROWS/2]; get_something (middle, XmNforeground, (XtArgVal)&fg_pix); get_something (middle, XmNbackground, (XtArgVal)&bg_pix); } /* set the calendar to the time of the given Now *np. * use local time if PREF_ZONE is PREF_LOCALTZ * only really do it if f_ison() changed or f_ison() now and day changed. */ void calm_set (np) Now *np; { static double last_jd = -1e5; static double last_tz = 1e10; static PrefWeekStart last_wkstart = PREF_SUN; static int last_zp = -1; static int wason = -1; int f; /* day of week of first day of month Sun=0 */ int nd; /* number of days in this month */ double jd; /* current mjd; corrected for TZ if PREF_LOCALTZ */ double jd0; /* mjd of first day of the month */ Pixel om_pix; int localtz; char buf[64]; int zonepref; int m, y; double d; int ison, new; int i; /* preliminaries for time zone */ zonepref = pref_get(PREF_ZONE); localtz = (zonepref == PREF_LOCALTZ); jd = localtz ? mjd - tz/24.0 : mjd; /* set name of current timezone preference in title */ fs_tz (buf, zonepref, np); (void) strcat (buf, " Calendar"); f_showit (tz_w, buf); /* proceed if f_ison() changed or on and things have changed */ ison = f_ison(); new = ison != wason || tz != last_tz || last_zp != zonepref || (ison && mjd_day(last_jd) != mjd_day(jd)) || pref_get (PREF_WEEKSTART) != last_wkstart; wason = ison; last_tz = tz; last_zp = zonepref; last_wkstart = pref_get (PREF_WEEKSTART); if (!new) return; /* remember the new jd we are working on */ last_jd = jd; /* get today's month, day, year */ mjd_cal (jd, &m, &d, &y); /* label the month */ f_showit (m_w, mnames[m-1]); /* label the year and set the menu entries around it */ (void) sprintf (buf, "%d", y); f_showit (y_w, buf); for (i = 0; i < NYEARS; i++) { int ty = (y - NYEARS/3) + i; /* current year about 1/3 down list */ (void) sprintf (buf, "%d", ty); f_showit (ymenu_w[i], buf); set_something (ymenu_w[i], XmNuserData, (XtArgVal)ty); } /* find day of week of first day of month */ cal_mjd (m, 1.0, y, &jd0); if (mjd_dow (jd0, &f) < 0) { /* can't figure it out - too hard before Gregorian */ for (i = 0; i < CAL_ROWS*CAL_COLS; i++) { set_something (d_w[i], XmNuserData, (XtArgVal)NO_DATE); f_showit (d_w[i], " "); } return; } /* slip f one if start weeks on sat or mon, and * print names of days slipped too */ switch (pref_get(PREF_WEEKSTART)) { case PREF_SAT: f = (f+1)%7; for (i = 0; i < XtNumber(dnames); i++) set_xmstring(dnames[i].w, XmNlabelString, dnames[(i+6)%7].name); break; case PREF_SUN: /* f is ok */ for (i = 0; i < XtNumber(dnames); i++) set_xmstring(dnames[i].w, XmNlabelString, dnames[i].name); break; case PREF_MON: f = (f+6)%7; /* really (f-1)%7 */ for (i = 0; i < XtNumber(dnames); i++) set_xmstring(dnames[i].w, XmNlabelString, dnames[(i+1)%7].name); break; } /* find number of days in this month */ mjd_dpm (jd0, &nd); /* print the calendar. * set userData to the day of the month. */ (void) get_color_resource (d_w[0], "CalOtherMonthColor", &om_pix); for (i = 0; i < CAL_ROWS*CAL_COLS; i++) { int date = i-f+1; if (i < f || i > f + nd - 1) { /* prev or next month */ double tmpjd, tmpd; int tmpm, tmpy; cal_mjd (m, (double)(date), y, &tmpjd); mjd_cal (tmpjd, &tmpm, &tmpd, &tmpy); (void) sprintf (buf, "%2d", (int)floor(tmpd+0.5)); set_something (d_w[i], XmNforeground, (XtArgVal)om_pix); set_something (d_w[i], XmNbackground, (XtArgVal)bg_pix); } else { (void) sprintf (buf, "%2d", date); if (date == (int)d) { /* reverse colors on today */ set_something (d_w[i], XmNforeground, (XtArgVal)bg_pix); set_something (d_w[i], XmNbackground, (XtArgVal)fg_pix); } else { set_something (d_w[i], XmNforeground, (XtArgVal)fg_pix); set_something (d_w[i], XmNbackground, (XtArgVal)bg_pix); } } f_showit (d_w[i], buf); set_something (d_w[i], XmNuserData, (XtArgVal)(date)); } /* over print the new and full moons for days near this month. * TODO: don't really know which dates to use here (see moonnf()) * so try several to be fairly safe. have to go back to 4/29/1988 * to find the full moon on 5/1 for example. */ mm_nfmoon (jd0-15, tz, m, f, nd); mm_nfmoon (jd0-3, tz, m, f, nd); mm_nfmoon (jd0+15, tz, m, f, nd); mm_nfmoon (jd0+30, tz, m, f, nd); } /* called when Now or its helpers are pressed. * client is one of TodayCuts. * set m/d/y but retain any partion day. */ /* ARGSUSED */ static void today_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { TodayCuts c = (TodayCuts)client; Now *np = mm_get_now(); Now now; double newmjd; switch (c) { case BACKBACK: newmjd = mjd - 7; break; case BACK: newmjd = mjd - 1; break; case TONOW: time_fromsys (&now); newmjd = now.n_mjd; break; case FORW: newmjd = mjd + 1; break; case FORWFORW: newmjd = mjd + 7; break; default: newmjd = mjd; break; } /* this always wants UTC */ mm_newcaldate (newmjd); } /* called when any of the calendar pushbuttons is activated. * client is one of DAY, MONTH or YEAR. * userData is new value, ie, a month, day or year. * N.B. we honor PREF_ZONE */ /* ARGSUSED */ static void date_changed_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { Now *np = mm_get_now(); int code = (long int)client; double jd; double newmjd; int localtz; int x; int m, y; double d; get_something (w, XmNuserData, (XtArgVal)&x); if (x == NO_DATE) return; localtz = pref_get (PREF_ZONE) == PREF_LOCALTZ; jd = localtz ? mjd - tz/24.0 : mjd; mjd_cal (jd, &m, &d, &y); switch (code) { case MONTH: cal_mjd (x, d, y, &newmjd); break; case DAY: d = x + mjd_hr(jd)/24.0; /* preserve any partial day */ cal_mjd (m, d, y, &newmjd); break; case YEAR: cal_mjd (m, d, x, &newmjd); break; } if (localtz) newmjd += tz/24.0; /* this function always wants UTC */ mm_newcaldate (newmjd); } /* print the new and full moons for the months surrounding jd, where * m is the month-of-year for the current month and f is index into d_w[] * of the first day of this month and nd is number of days of month `m'. */ static void mm_nfmoon (jd, tzone, m, f, nd) double jd; double tzone; int m, f, nd; { static char nms[] = "NM", fms[] = "FM"; double jdn, jdf; /* mjd of new and full moons near jd */ int mm, ym; double dm; int ndays; int di; moonnf (jd, &jdn, &jdf); if (pref_get(PREF_ZONE) == PREF_LOCALTZ) { jdn -= tzone/24.0; jdf -= tzone/24.0; } mjd_cal (jdn, &mm, &dm, &ym); if ((mm == m - 1) || (mm == 12 && m == 1)) { mjd_dpm (jdn, &ndays); dm -= ndays; } else if ((mm == m + 1) || (mm == 1 && m == 12)) { dm += nd; } else if (mm != m) dm = -f; di = (int)floor(dm + f - 1); if (di >= 0 && di < CAL_ROWS*CAL_COLS) f_showit (d_w[di], nms); mjd_cal (jdf, &mm, &dm, &ym); if ((mm == m - 1) || (mm == 12 && m == 1)) { mjd_dpm (jdf, &ndays); dm -= ndays; } else if ((mm == m + 1) || (mm == 1 && m == 12)) { dm += nd; } else if (mm != m) dm = -f; di = (int)floor(dm + f - 1); if (di >= 0 && di < CAL_ROWS*CAL_COLS) f_showit (d_w[di], fms); }