/* handle the favorites list. * independent copies of all favorites are kept here but pointers returned are * from main database if entry with same name exists. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xephem.h" #include "lilxml.h" /* pointer to object if loaded, else just name. also on/off control. * N.B. don't point op at &o because the list gets realloced and can move. */ typedef struct { Obj o; /* defn */ Obj *op; /* db object else NULL for our o */ int on; /* whether to use */ } Favorite; static Favorite * favGrow (void); static void fav_up_cb (Widget w, XtPointer client, XtPointer call); static void fav_dn_cb (Widget w, XtPointer client, XtPointer call); static void fav_del_cb (Widget w, XtPointer client, XtPointer call); static void fav_on_cb (Widget w, XtPointer client, XtPointer call); static void fav_load_cb (Widget w, XtPointer client, XtPointer call); static void fav_save_cb (Widget w, XtPointer client, XtPointer call); static void fav_add_cb (Widget w, XtPointer client, XtPointer call); static void fav_help_cb (Widget w, XtPointer client, XtPointer call); static void fav_close_cb (Widget w, XtPointer client, XtPointer call); static void fav_save_cb (Widget w, XtPointer client, XtPointer call); static void fav_create (void); static void showFavorites (void); static void setup1Row (Widget rc_w, int i); static void loadFavs (); static void saveFavs (char *filename); static void favRmAll(void); static void bldExport(void); static Obj *srchRealDB (char *name); static void del_i(void); static void chg_i(void); /* the list */ static Favorite *favs; /* malloced list of all favorites */ static int nfavs; /* n in favs list */ static Obj **xfavs; /* malloced list of fav Obj to export */ static int nxfavs; /* n in xfavs list */ static Widget favshell_w; /* main form shell */ static Widget sw_w; /* list scrolled window */ static Widget save_w; /* TF for name of file to save favs */ static Widget add_w; /* TF for edb dfn of new favorite */ static int internal_scan; /* flag to temp disable fav_scan */ static char favcategory[] = "Favorites"; /* Save category */ static char fav_suffix[] = ".fav"; /* suffix of Favorites files */ static char deffavfn[] = "favorites.fav"; /* default favorites to load */ /* used by query callbacks */ static int deli; static Obj newo; /* show, create if first time, the favorites window */ void fav_manage() { if (!favshell_w) fav_create(); XtPopup (favshell_w, XtGrabNone); set_something (favshell_w, XmNiconic, (XtArgVal)False); } /* add a new object to the favorites list if not already */ void fav_add (Obj *op) { Favorite *addfp; int i; watch_cursor(1); if (!favshell_w) fav_create(); /* replace if name is dup, else add */ i = fav_already (op->o_name); if (i >= 0) { addfp = &favs[i]; memcpy (&addfp->o, op, sizeof(Obj)); addfp->op = NULL; /* local copy now for sure */ } else { addfp = favGrow(); memcpy (&addfp->o, op, sizeof(Obj)); addfp->op = (op->o_flags & FLDSTAR) ? NULL : op; } addfp->on = 1; /* display */ bldExport(); showFavorites(); all_newfavs(); watch_cursor(0); } /* return index into favs entry if given name is on favorites list, else -1 */ int fav_already (char *name) { int i; for (i = 0; i < nfavs; i++) if (strcmp (favs[i].o.o_name, name) == 0) return (i); return (-1); } /* return a list of Favorite Obj* to be used. * each Obj * will point into db if loaded, else to our private copy. * oppp can be NULL if just want count. * N.B. caller should never free the list. */ int fav_get_loaded (Obj ***oppp) { if (!favshell_w) fav_create(); if (oppp) *oppp = xfavs; return (nxfavs); } /* called when the database changes. * goal is to decide whether the copies we have here are now also in the db */ void fav_newdb() { int i; if (!favshell_w) fav_create(); /* update db pointer if really in db */ for (i = 0; i < nfavs; i++) favs[i].op = srchRealDB (favs[i].o.o_name); bldExport(); showFavorites(); } /* used by db_scan() to collect Favorites set on and not in the main database. * called with index into favs[] from which to scan and mask of desired types. * return next Obj with index set for next scan or NULL when no more. * N.B. internal calls from here can disable by setting internal_scan. */ Obj * fav_scan (int *np, int typemask) { int i; if (internal_scan) return (NULL); for (i = *np; i < nfavs; i++) { if (favs[i].on && !favs[i].op && (OBJTYPE2MASK(favs[i].o.o_type) & typemask)) { *np = i+1; return (&favs[i].o); } } return (NULL); } /* called to put up or remove the watch cursor. */ void fav_cursor (Cursor c) { Window win; if (favshell_w && (win = XtWindow(favshell_w)) != 0) { Display *dsp = XtDisplay(favshell_w); if (c) XDefineCursor (dsp, win, c); else XUndefineCursor (dsp, win); } } /* create the favorites window */ static void fav_create() { static struct { char *label; XtCallbackProc cb; char *tip; } btns[] = { {"Close", fav_close_cb, "Close this window"}, {"Help", fav_help_cb, "Learn more"}, }; Widget w, bf_w, pb_w, sfm_w; Widget favform_w; Arg args[20]; char *s[1]; int i; int n; /* create the Favories shell and form */ n = 0; XtSetArg (args[n], XmNallowShellResize, False); n++; XtSetArg (args[n], XmNcolormap, xe_cm); n++; XtSetArg (args[n], XmNtitle, "Favorites"); n++; XtSetArg (args[n], XmNiconName, "Favorites"); n++; XtSetArg (args[n], XmNdeleteResponse, XmUNMAP); n++; favshell_w = XtCreatePopupShell ("Favorites", topLevelShellWidgetClass, toplevel_w, args, n); setup_icon (favshell_w); set_something (favshell_w, XmNcolormap, (XtArgVal)xe_cm); sr_reg (favshell_w, "XEphem*Favorites.x", favcategory, 0); sr_reg (favshell_w, "XEphem*Favorites.y", favcategory, 0); sr_reg (favshell_w, "XEphem*Favorites.width", favcategory, 0); sr_reg (favshell_w, "XEphem*Favorites.height", favcategory, 0); n = 0; XtSetArg (args[n], XmNverticalSpacing, 10); n++; XtSetArg (args[n], XmNmarginHeight, 10); n++; XtSetArg (args[n], XmNmarginWidth, 10); n++; favform_w = XmCreateForm (favshell_w, "FMForm", args, n); XtAddCallback (favform_w, XmNhelpCallback, fav_help_cb, 0); XtManageChild (favform_w); /* controls attached across the bottom */ n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNfractionBase, 5*XtNumber(btns)-1); n++; bf_w = XmCreateForm (favform_w, "FBForm", args, n); XtManageChild (bf_w); for (i = 0; i < XtNumber(btns); i++) { n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 5*i); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 5*(i+1)-1); n++; w = XmCreatePushButton (bf_w, btns[i].label, args, n); wtip (w, btns[i].tip); XtAddCallback (w, XmNactivateCallback, btns[i].cb, 0); XtManageChild (w); } /* favorites add control */ n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNbottomWidget, bf_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; pb_w = XmCreatePushButton (favform_w, "FATB", args, n); set_xmstring (pb_w, XmNlabelString, "Add edb:"); wtip (pb_w, "Add or change a Favorite defined by the edb format entry at right"); XtAddCallback (pb_w, XmNactivateCallback, fav_add_cb, NULL); XtManageChild (pb_w); n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNbottomWidget, bf_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, pb_w); n++; XtSetArg (args[n], XmNleftOffset, 5); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; add_w = XmCreateTextField (favform_w, "edb", args, n); XtAddCallback (add_w, XmNactivateCallback, fav_add_cb, NULL); sr_reg (add_w, NULL, favcategory, 0); wtip (add_w, "edb format definition of new or changed Favorite"); XtManageChild (add_w); /* favorites file save control */ n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNbottomWidget, add_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; pb_w = XmCreatePushButton (favform_w, "FSPB", args, n); set_xmstring (pb_w, XmNlabelString, "Save to:"); wtip (pb_w, "Save above Favorites in file named at right"); XtAddCallback (pb_w, XmNactivateCallback, fav_save_cb, NULL); XtManageChild (pb_w); n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNbottomWidget, add_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, pb_w); n++; XtSetArg (args[n], XmNleftOffset, 5); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; save_w = XmCreateTextField (favform_w, "File", args, n); XtAddCallback (save_w, XmNactivateCallback, fav_save_cb, NULL); sr_reg (save_w, NULL, favcategory, 0); defaultTextFN (save_w, 0, deffavfn, NULL); wtip (save_w, "File in which to save current Favorites"); XtManageChild (save_w); /* favorites load control */ s[0] = fav_suffix; sfm_w = createFSM (favform_w, s, 1, "auxil", fav_load_cb); wtip (sfm_w, "Select existing Favorites file to load"); set_xmstring (sfm_w, XmNlabelString, "Load file: "); n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNbottomWidget, save_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetValues (sfm_w, args, n); /* SW for RC of entries stretches between top and row of controls */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNbottomWidget, sfm_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNscrollingPolicy, XmAUTOMATIC); n++; sw_w = XmCreateScrolledWindow (favform_w, "SW", args, n); XtManageChild (sw_w); /* add a dummy RC just so it can be replaced when add entries */ n = 0; w = XmCreateRowColumn (sw_w, "Dummy", args, n); XtManageChild (w); /* init list */ loadFavs(NULL); showFavorites(); } /* display each of the favs entries in the dialog. */ static void showFavorites() { Widget ww; Arg args[20]; int i, n; /* replace workWindow with a fresh RC */ get_something (sw_w, XmNworkWindow, (XtArgVal)&ww); XtDestroyWidget (ww); n = 0; XtSetArg (args[n], XmNspacing, 0); n++; ww = XmCreateRowColumn (sw_w, "FavRC", args, n); set_something (sw_w, XmNworkWindow, (XtArgVal)ww); /* fill with info */ for (i = 0; i < nfavs; i++) setup1Row (ww, i); /* ok */ XtManageChild (ww); } /* using info from favs[i] create its row in the favories window */ static void setup1Row (rc_w, i) Widget rc_w; int i; { Widget f_w, on_w, del_w, lbl_w, up_w, dn_w; Arg args[20]; char buf[2048]; int n; /* create the row's form */ n = 0; XtSetArg (args[n], XmNhorizontalSpacing, 5); n++; f_w = XmCreateForm (rc_w, "FF", args, n); XtManageChild (f_w); /* PB to delete */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; del_w = XmCreatePushButton (f_w, "Del", args, n); XtAddCallback (del_w, XmNactivateCallback, fav_del_cb, (XtPointer)(long int)i); wtip (del_w, "Delete this Favorite"); XtManageChild (del_w); /* arrow buttons for up and down */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, del_w); n++; XtSetArg (args[n], XmNarrowDirection, XmARROW_UP); n++; XtSetArg (args[n], XmNsensitive, i > 0); n++; up_w = XmCreateArrowButton (f_w, "Fup", args, n); XtAddCallback (up_w, XmNactivateCallback, fav_up_cb, (XtPointer)(long int)i); wtip (up_w, "Move this Favorite up one row"); XtManageChild (up_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, up_w); n++; XtSetArg (args[n], XmNarrowDirection, XmARROW_DOWN); n++; XtSetArg (args[n], XmNsensitive, i < nfavs-1); n++; dn_w = XmCreateArrowButton (f_w, "Fdn", args, n); XtAddCallback (dn_w, XmNactivateCallback, fav_dn_cb, (XtPointer)(long int)i); wtip (dn_w, "Move this Favorite down one row"); XtManageChild (dn_w); /* TB for on/off */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, dn_w); n++; XtSetArg (args[n], XmNset, favs[i].on); n++; on_w = XmCreateToggleButton (f_w, "Use", args, n); XtAddCallback (on_w, XmNvalueChangedCallback, fav_on_cb,(XtPointer)(long int)i); wtip (on_w, "Whether to use this entry"); XtManageChild (on_w); /* label for name */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, on_w); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; lbl_w = XmCreateLabel (f_w, "FL", args, n); db_write_line (favs[i].op ? favs[i].op : &favs[i].o, buf); set_xmstring (lbl_w, XmNlabelString, buf); XtManageChild (lbl_w); } /* callback from the overall Help button. */ /* ARGSUSED */ static void fav_help_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { static char *msg[] = { "Collection of favorite objects", }; hlp_dialog ("Favorites", msg, sizeof(msg)/sizeof(msg[0])); } /* callback from the Close PB */ /* ARGSUSED */ static void fav_close_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { XtPopdown (favshell_w); } /* callback to load favorites list from file. * file name is lable of htsi widget */ /* ARGSUSED */ static void fav_load_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { char *fn; get_xmstring (w, XmNlabelString, &fn); loadFavs(fn); showFavorites(); all_newfavs(); XtFree (fn); } /* callback to Save the current favorites to file named in save_w * N.B. do not use call arg, this is used both by PB and TF */ /* ARGSUSED */ static void fav_save_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { char buf[1024], *fn; char *txt; /* get file name */ fn = txt = XmTextFieldGetString (save_w); if (!strstr (txt, fav_suffix)) { sprintf (fn = buf, "%s%s", txt, fav_suffix); XmTextFieldSetString (save_w, fn); } /* save */ saveFavs (fn); /* clean up */ if (confirm()) xe_msg (1, "%s saved", fn); XtFree (txt); } /* callback to Add a new favorite or possibly modify an existing fav. * N.B. do not use call arg, this is used both by PB and TF */ /* ARGSUSED */ static void fav_add_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { char buf[1024]; char *txt; /* get edb dfn, crack and add/change if ok */ txt = XmTextFieldGetString (add_w); if (db_crack_line (txt, &newo, NULL, 0, buf) < 0) { /* unknown */ xe_msg (1, buf); } else if (srchRealDB (newo.o_name)) { /* already in db */ xe_msg (1, "%s is already in loaded database", newo.o_name); } else if ((deli = fav_already (newo.o_name)) >= 0) { /* already in favs list */ if (confirm()) { (void) sprintf (buf, "Change \"%s\"?", favs[deli].o.o_name); query (favshell_w, buf, "Yes -- change", "No -- ignore", NULL, chg_i, NULL, NULL); } else chg_i(); } else { /* add to favs */ Favorite *newfp = favGrow(); newfp->o = newo; newfp->op = NULL; newfp->on = 1; /* display */ bldExport(); showFavorites(); all_newfavs(); } XtFree (txt); } /* used to delete favorite entry deli */ static void del_i() { /* remove deli from favs list */ memmove (&favs[deli], &favs[deli+1], (--nfavs-deli)*sizeof(Favorite)); /* show list now */ watch_cursor (1); bldExport(); showFavorites(); all_newfavs(); watch_cursor (0); } /* used to change favorite[deli] to newo */ static void chg_i() { /* update entry */ memcpy (&favs[deli].o, &newo, sizeof(Obj)); favs[deli].op = NULL; /* local copy now for sure */ /* update display */ watch_cursor (1); bldExport(); showFavorites(); all_newfavs(); watch_cursor (0); } /* called when a Use toggle changes. * client is index into favs[] */ /* ARGSUSED */ static void fav_on_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { int wanton = XmToggleButtonGetState (w); int i = (long int)client; watch_cursor(1); favs[i].on = wanton; bldExport(); all_newfavs(); watch_cursor(0); } /* called when an Up arrow button is hit in the favorites list. * client is index into favs[] */ /* ARGSUSED */ static void fav_up_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { int i = (long int)client; Favorite tmp = favs[i-1]; favs[i-1] = favs[i]; favs[i] = tmp; bldExport(); showFavorites(); all_newfavs(); } /* called when a Down arrow button is hit in the favorites list. * client is index into favs[] */ /* ARGSUSED */ static void fav_dn_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { int i = (long int)client; Favorite tmp = favs[i+1]; favs[i+1] = favs[i]; favs[i] = tmp; bldExport(); showFavorites(); all_newfavs(); } /* called when a Delete button is hit in the favorites window. * client is index into favs[] */ /* ARGSUSED */ static void fav_del_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { /* save index */ deli = (long int)client; /* confirm */ if (confirm()) { char buf[1024]; (void) sprintf (buf, "Delete \"%s\"?", favs[deli].o.o_name); query (favshell_w, buf, "Yes -- delete", "No -- keep it", NULL, del_i, NULL, NULL); } else del_i(); } /* add one more to favs and return ponter to new */ static Favorite * favGrow() { Favorite *newfp; favs = (Favorite *) XtRealloc ((char*)favs, (nfavs+1)*sizeof(Favorite)); newfp = &favs[nfavs++]; zero_mem (newfp, sizeof(*newfp)); return (newfp); } static void favRmAll() { XtFree ((char *)favs); favs = NULL; nfavs = 0; } /* build xfavs, list of Obj* to be exported as the current set of favs */ static void bldExport() { int i; xfavs = (Obj **) XtRealloc ((char *)xfavs, nfavs*sizeof(Obj*)); /* WC */ for (nxfavs = i = 0; i < nfavs; i++) if (favs[i].on) xfavs[nxfavs++] = favs[i].op ? favs[i].op : &favs[i].o; } /* set the current set of favorites from fn them build the export obj list. * if !fn, use save_w */ static void loadFavs(char *fn) { Favorite *newfp; char msg[1024]; XMLEle *root, *ep; LilXML *xp; FILE *fp; char buf[1024]; /* get file name */ if (!fn) { char *txt = XmTextFieldGetString (save_w); if (!strstr (txt, fav_suffix)) sprintf (buf, "%s%s", txt, fav_suffix); else strcpy (buf, txt); XtFree (txt); fn = buf; } /* open file */ fp = fopend (fn, "auxil", "r"); if (!fp) return; /* already informed user */ /* read */ xp = newLilXML(); root = readXMLFile (fp, xp, msg); delLilXML (xp); fclose (fp); if (!root) { xe_msg (1, "%s: %s", fn, msg[0] ? msg : "malformed xml"); return; } if (strcmp (tagXMLEle(root), "Favorites")) { xe_msg (1, "%s: not a Favorites file", fn); delXMLEle (root); return; } /* remove current set */ favRmAll(); /* add new from file */ for (ep = nextXMLEle(root,1); ep != NULL; ep = nextXMLEle(root,0)) { Obj o; if (strcmp (tagXMLEle(ep), "favorite")) continue; if (db_crack_line (pcdataXMLEle(ep), &o, NULL, 0, msg) < 0) { xe_msg (1, "Bad favorite: %s: %s", pcdataXMLEle(ep), msg); continue; } newfp = favGrow(); newfp->o = o; newfp->op = srchRealDB (newfp->o.o_name); newfp->on = strcmp (findXMLAttValu (ep, "on"), "true") == 0; } /* clean up */ delXMLEle (root); /* build export list */ bldExport(); } /* save the current set of favorites to the given file. */ static void saveFavs(char *fn) { char line[1024]; FILE *fp; int i; /* create */ fp = fopend (fn, NULL, "w"); if (!fp) return; /* already informed user */ /* write */ fprintf (fp, "\n"); for (i = 0; i < nfavs; i++) { db_write_line (favs[i].op ? favs[i].op : &favs[i].o, line); fprintf (fp, " %s\n", favs[i].on ? "true" : "false", line); } fprintf (fp, "\n"); /* finished */ fclose (fp); } /* scan just the db (not favs) for entry with given name and return * pointer else NULL * N.B. this basically removes from db_scan() what fav_scan() adds */ static Obj * srchRealDB (char *name) { DBScan dbs; Obj *op; /* flag to disable fav_scan() while we use db_scan() */ internal_scan = 1; for (db_scaninit (&dbs,ALLM,NULL,0); (op = db_scan(&dbs)) != NULL; ) if (!strcmp (op->o_name, name)) break; internal_scan = 0; return (op); }