XEphem/GUI/xephem/moviemenu.c

912 lines
24 KiB
C

/* code to manage movie loop display.
*
* N.B. in order to keep the frame scale happy, we keep the min set to 0 and
* never set the max below 1 even if there are no images. always use npixmaps
* to tell how many frames are in the movie, not the max of the frame scale.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <ctype.h>
#include <math.h>
#include <sys/types.h>
#include <unistd.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Frame.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/DrawingA.h>
#include <Xm/CascadeB.h>
#include <Xm/SelectioB.h>
#include <Xm/Scale.h>
#include <Xm/ToggleB.h>
#include <Xm/ScrolledW.h>
#include <Xm/Separator.h>
#include <Xm/Text.h>
#include <Xm/RowColumn.h>
#include "xephem.h"
#include "png.h"
static void ml_create_shell (void);
static void ml_create_prepd_w(void);
static void ml_help_cb (Widget w, XtPointer client, XtPointer data);
static void ml_close_cb (Widget w, XtPointer client, XtPointer data);
static void ml_frame_cb (Widget w, XtPointer client, XtPointer data);
static void ml_fwrd_cb (Widget w, XtPointer client, XtPointer data);
static void ml_back_cb (Widget w, XtPointer client, XtPointer data);
static void ml_rate_cb (Widget w, XtPointer client, XtPointer data);
static void ml_delone_cb (Widget w, XtPointer client, XtPointer data);
static void ml_delall_cb (Widget w, XtPointer client, XtPointer data);
static void ml_save_cb (Widget w, XtPointer client, XtPointer data);
static void ml_exp_cb (Widget w, XtPointer client, XtPointer data);
static void ml_prefix_cb (Widget w, XtPointer client, XtPointer call);
static void ml_timer_cb (XtPointer client, XtIntervalId *id);
static void getWH (Drawable d, unsigned int *wp, unsigned int *hp);
static void delall(void);
static void showFrame(int n);
static void stopLooping(void);
static void saveMovie(char *prefix);
static int addPixmap (Drawable pm, Widget timestamp);
static int getPMPix (Pixmap pm, unsigned char **raster, unsigned int *wp,
unsigned int *hp);
static int writePNG (char *fn, unsigned char *raster, unsigned int w,
unsigned int h);
static Widget mlshell_w; /* main shell */
static Widget prepd_w; /* prefix name prompt dialog */
static Widget mlda_w; /* main drawing area */
static Widget mlsw_w; /* scrolled window for image */
static Widget bounce_w; /* TB whether to bounce */
static Widget frame_w; /* scale to show/control frame number */
static Widget rate_w; /* scale to control movie rate */
static int bouncing; /* whether currently moving backwards */
static Pixmap *pixmaps; /* malloced list of pixmaps in movie */
static int npixmaps; /* number of pixmaps in movie */
static XtIntervalId ml_id; /* movie timer */
static char mlcategory[] = "Movie";
/* bring up the movie loop tool */
void
ml_manage()
{
/* create shell if first time */
if (!mlshell_w)
ml_create_shell();
/* insure visible */
XtPopup (mlshell_w, XtGrabNone);
set_something (mlshell_w, XmNiconic, (XtArgVal)False);
}
/* add a pixmap to the movie loop, creating if first time */
void
ml_add (Drawable pm, Widget timestamp)
{
/* avoid misuse */
if (!pm)
return;
/* create shell if first time */
if (!mlshell_w)
ml_create_shell();
/* add */
if (timestamp)
XmUpdateDisplay (timestamp);
if (addPixmap (pm, timestamp) < 0)
return;
/* up and show */
ml_manage();
showFrame (npixmaps-1);
}
/* convenience function to add Ctrl-m accelerator to the given PB args at n.
* return number of entries added to args[]
*/
int
ml_addacc (Arg args[], int n)
{
XtSetArg (args[n], XmNacceleratorText,
XmStringCreate("Ctrl+m", XmSTRING_DEFAULT_CHARSET)); n++;
XtSetArg (args[n], XmNaccelerator, "Ctrl<Key>m"); n++;
return (2);
}
/* called to put up or remove the watch cursor. */
void
ml_cursor (Cursor c)
{
Window win;
if (mlshell_w && (win = XtWindow(mlshell_w)) != 0) {
Display *dsp = XtDisplay(mlshell_w);
if (c)
XDefineCursor (dsp, win, c);
else
XUndefineCursor (dsp, win);
}
}
/* create main shell */
static void
ml_create_shell ()
{
Widget pd_w, cb_w, mb_w;
Widget w, f_w, ff_w, rf_w;
Arg args[20];
int n;
/* create outter shell and form */
n = 0;
XtSetArg (args[n], XmNcolormap, xe_cm); n++;
XtSetArg (args[n], XmNtitle, "xephem Movie"); n++;
XtSetArg (args[n], XmNiconName, "Movie"); n++;
XtSetArg (args[n], XmNdeleteResponse, XmUNMAP); n++;
mlshell_w = XtCreatePopupShell ("Movie", topLevelShellWidgetClass,
toplevel_w, args, n);
set_something (mlshell_w, XmNcolormap, (XtArgVal)xe_cm);
setup_icon (mlshell_w);
sr_reg (mlshell_w, "XEphem*Movie.x", mlcategory, 0);
sr_reg (mlshell_w, "XEphem*Movie.y", mlcategory, 0);
sr_reg (mlshell_w, "XEphem*Movie.height", mlcategory, 0);
sr_reg (mlshell_w, "XEphem*Movie.width", mlcategory, 0);
n = 0;
XtSetArg (args[n], XmNverticalSpacing, 10); n++;
XtSetArg (args[n], XmNhorizontalSpacing, 10); n++;
f_w = XmCreateForm (mlshell_w, "MovieF", args, n);
XtAddCallback (f_w, XmNhelpCallback, ml_help_cb, 0);
XtManageChild(f_w);
/* menu bar */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
mb_w = XmCreateMenuBar (f_w, "SMB", args, n);
XtManageChild (mb_w);
/* Control */
n = 0;
pd_w = XmCreatePulldownMenu (mb_w, "CPD", args, n);
n = 0;
XtSetArg (args[n], XmNmnemonic, 'D'); n++;
w = XmCreatePushButton (pd_w, "DelOne", args, n);
set_xmstring (w, XmNlabelString, "Delete current frame");
wtip (w, "Delete the current frame from the movie");
XtAddCallback (w, XmNactivateCallback, ml_delone_cb, 0);
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNmnemonic, 'a'); n++;
w = XmCreatePushButton (pd_w, "DelAll", args, n);
set_xmstring (w, XmNlabelString, "Delete all frames");
wtip (w, "Delete the entire movie");
XtAddCallback (w, XmNactivateCallback, ml_delall_cb, 0);
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNmnemonic, 'S'); n++;
w = XmCreatePushButton (pd_w, "Save", args, n);
set_xmstring (w, XmNlabelString, "Save frames ...");
wtip (w, "Save each movie frame to individual files");
XtAddCallback (w, XmNactivateCallback, ml_save_cb, 0);
XtManageChild (w);
n = 0;
w = XmCreateSeparator (pd_w, "Sep", args, n);
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNmnemonic, 'C'); n++;
w = XmCreatePushButton (pd_w, "Close", args, n);
XtAddCallback (w, XmNactivateCallback, ml_close_cb, 0);
wtip (w, "Close this window");
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNmnemonic, 'C'); n++;
XtSetArg (args[n], XmNsubMenuId, pd_w); n++;
cb_w = XmCreateCascadeButton (mb_w, "Control", args, n);
set_something (mb_w, XmNmenuHelpWidget, (XtArgVal)cb_w);
XtManageChild (cb_w);
/* help */
n = 0;
pd_w = XmCreatePulldownMenu (mb_w, "HPD", args, n);
n = 0;
XtSetArg (args[n], XmNmnemonic, 'H'); n++;
w = XmCreatePushButton (pd_w, "Help", args, n);
wtip (w, "Get more help on making and viewing movies");
XtAddCallback (w, XmNactivateCallback, ml_help_cb, 0);
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNmnemonic, 'H'); n++;
XtSetArg (args[n], XmNsubMenuId, pd_w); n++;
cb_w = XmCreateCascadeButton (mb_w, "Help", args, n);
set_something (mb_w, XmNmenuHelpWidget, (XtArgVal)cb_w);
XtManageChild (cb_w);
/* frame control */
n = 0;
XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNrightPosition, 45); n++;
XtSetArg (args[n], XmNverticalSpacing, 10); n++;
XtSetArg (args[n], XmNmarginWidth, 10); n++;
XtSetArg (args[n], XmNmarginHeight, 10); n++;
ff_w = XmCreateForm (f_w, "FF", args, n);
XtManageChild (ff_w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
w = XmCreateLabel (ff_w, "SelLbl", args, n);
set_xmstring (w, XmNlabelString, "Frame selection:");
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++;
XtSetArg (args[n], XmNshowValue, True); n++;
XtSetArg (args[n], XmNscaleMultiple, 1); n++;
XtSetArg (args[n], XmNvalue, 0); n++;
XtSetArg (args[n], XmNmaximum, 1); n++;
frame_w = XmCreateScale (ff_w, "FSScale", args, n);
wtip (frame_w, "Displays and sets the movie frame to view");
XtAddCallback (frame_w, XmNdragCallback, ml_frame_cb, NULL);
XtAddCallback (frame_w, XmNvalueChangedCallback, ml_frame_cb, NULL);
XtManageChild (frame_w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, frame_w); n++;
XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
bounce_w = XmCreateToggleButton (ff_w, "Bounce", args, n);
wtip (bounce_w,"Whether to flow forward-backward or forward-reset");
XtManageChild (bounce_w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, frame_w); n++;
XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
w = XmCreatePushButton (ff_w, "Fwrd", args, n);
wtip (w, "Move to next frame, wrap to first from last");
set_xmstring (w, XmNlabelString, "Step +1");
XtAddCallback (w, XmNactivateCallback, ml_fwrd_cb, NULL);
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, frame_w); n++;
XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNrightWidget, w); n++;
XtSetArg (args[n], XmNrightOffset, 10); n++;
w = XmCreatePushButton (ff_w, "Back", args, n);
wtip (w, "Move to previous frame, wrap to last from first");
set_xmstring (w, XmNlabelString, "Step -1");
XtAddCallback (w, XmNactivateCallback, ml_back_cb, NULL);
XtManageChild (w);
/* rate control */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, ff_w); n++;
XtSetArg (args[n], XmNtopOffset, 0); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNleftPosition, 55); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNverticalSpacing, 10); n++;
XtSetArg (args[n], XmNmarginWidth, 10); n++;
XtSetArg (args[n], XmNmarginHeight, 10); n++;
rf_w = XmCreateForm (f_w, "RF", args, n);
XtManageChild (rf_w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
w = XmCreateLabel (rf_w, "RateLbl", args, n);
set_xmstring (w, XmNlabelString, "Frames per second:");
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++;
XtSetArg (args[n], XmNshowValue, True); n++;
XtSetArg (args[n], XmNmaximum, 10); n++;
rate_w = XmCreateScale (rf_w, "FRScale", args, n);
wtip (rate_w, "Set the number of frames displayed per second");
XtAddCallback (rate_w, XmNvalueChangedCallback, ml_rate_cb, NULL);
XtAddCallback (rate_w, XmNdragCallback, ml_rate_cb, NULL);
XtManageChild (rate_w);
/* remainder is scrolled window for images */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, mb_w); n++;
XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNbottomWidget, ff_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNscrollingPolicy, XmAUTOMATIC); n++;
XtSetArg (args[n], XmNshadowThickness, 1); n++;
mlsw_w = XmCreateScrolledWindow (f_w, "MovieSW", args, n);
XtManageChild (mlsw_w);
/* workarea is a drawing area */
n = 0;
XtSetArg (args[n], XmNwidth, 500); n++;
XtSetArg (args[n], XmNheight, 500); n++;
mlda_w = XmCreateDrawingArea (mlsw_w, "MovieMap", args, n);
XtAddCallback (mlda_w, XmNexposeCallback, ml_exp_cb, NULL);
XtManageChild (mlda_w);
/* SW assumes work is its child but just to be tidy about it .. */
set_something (mlsw_w, XmNworkWindow, (XtArgVal)mlda_w);
}
/* ARGSUSED */
static void
ml_help_cb (Widget w, XtPointer client, XtPointer data)
{
static char *msg[] = {
"This tool creates and controls a movie loop of pixmaps"
};
hlp_dialog ("Movie", msg, XtNumber(msg));
}
/* ARGSUSED */
static void
ml_close_cb (Widget w, XtPointer client, XtPointer data)
{
XtPopdown (mlshell_w);
}
/* called to fill in the main drawing area with the current frame, if any */
/* ARGSUSED */
static void
ml_exp_cb (Widget wid, XtPointer client, XtPointer data)
{
GC gc = DefaultGC(XtD,DefaultScreen(XtD));
Window dawin = XtWindow(wid);
unsigned int w, h;
Pixmap pm;
int i;
/* might be called before any pixmaps defined */
if (npixmaps <= 0) {
XClearWindow (XtD, dawin);
return;
}
/* get current pixmap.
* N.B. beware case of keeping XmNmax at least 1 even when only 1 frame.
*/
XmScaleGetValue (frame_w, &i);
if (i == npixmaps) {
i = 0;
} else if (i > npixmaps) {
printf ("movie frame %d > number of pixmaps %d\n", i, npixmaps);
abort();
}
pm = pixmaps[i];
getWH (pm, &w, &h);
XCopyArea (XtD, pm, dawin, gc, 0, 0, w, h, 0, 0);
}
/* ARGSUSED */
static void
ml_delone_cb (Widget w, XtPointer client, XtPointer data)
{
int i;
if (npixmaps == 0)
return;
stopLooping();
XmScaleGetValue (frame_w, &i);
XFreePixmap (XtD, pixmaps[i]);
memmove (&pixmaps[i], &pixmaps[i+1], (npixmaps-i-1)*sizeof(Pixmap));
if (i == --npixmaps) {
i--;
XmScaleSetValue (frame_w, i >= 0 ? i : 0);
}
if (npixmaps > 0)
set_something (frame_w, XmNmaximum, npixmaps); /* keep max > 0 */
showFrame (i);
}
/* ARGSUSED */
static void
ml_save_cb (Widget w, XtPointer client, XtPointer data)
{
if (!prepd_w)
ml_create_prepd_w();
if (npixmaps == 0) {
xe_msg (1, "Movie contains no frames");
return;
}
XtManageChild (prepd_w);
}
/* ARGSUSED */
static void
ml_delall_cb (Widget w, XtPointer client, XtPointer data)
{
if (npixmaps == 0)
return;
stopLooping();
if (confirm())
query (mlshell_w, "Delete entire movie?", "No", "Yes", NULL,
NULL, delall, NULL);
else
delall();
}
/* delete entire movie */
static void
delall()
{
int i;
for (i = 0; i < npixmaps; i++)
XFreePixmap (XtD, pixmaps[i]);
npixmaps = 0;
XmScaleSetValue (frame_w, 0);
set_something (frame_w, XmNmaximum, 1); /* keep max > 0 */
showFrame (-1);
}
/* callback from the rate control */
static void
ml_rate_cb (Widget w, XtPointer client, XtPointer data)
{
int r;
XmScaleGetValue (w, &r);
if (r == 0 && ml_id != 0) {
XtRemoveTimeOut (ml_id);
ml_id = 0;
} else if (r > 0 && ml_id == 0 && npixmaps >= 2) {
ml_id = XtAppAddTimeOut (xe_app, 0, ml_timer_cb, 0);
}
}
/* callback to move one frame forward */
static void
ml_fwrd_cb (Widget w, XtPointer client, XtPointer data)
{
int i;
if (npixmaps == 0) {
XmScaleSetValue (frame_w, 0);
return;
}
stopLooping();
XmToggleButtonSetState (bounce_w, False, True);
XmScaleGetValue (frame_w, &i);
i = (i+1)%npixmaps;
XmScaleSetValue (frame_w, i);
showFrame (i);
}
/* callback to move one frame backward */
static void
ml_back_cb (Widget w, XtPointer client, XtPointer data)
{
int i;
if (npixmaps == 0) {
XmScaleSetValue (frame_w, 0);
return;
}
stopLooping();
XmToggleButtonSetState (bounce_w, False, True);
XmScaleGetValue (frame_w, &i);
i = (i-1+npixmaps)%npixmaps;
XmScaleSetValue (frame_w, i);
showFrame (i);
}
/* callback from the frame control scale */
static void
ml_frame_cb (Widget w, XtPointer client, XtPointer data)
{
int i;
if (npixmaps == 0) {
XmScaleSetValue (frame_w, 0);
return;
}
stopLooping();
XmScaleGetValue (frame_w, &i);
/* since we keep max at least 1, beware when fewer than 2 frames */
if (i == 1 && npixmaps < 2) {
XmScaleSetValue (frame_w, 0);
return;
}
showFrame (i);
}
/* function called from the interval timer used to implement the movie loop.
* N.B. this assumes there are at least 2 images to show.
*/
/* ARGSUSED */
static void
ml_timer_cb (XtPointer client, XtIntervalId *id)
{
int i, r;
/* check assumption */
if (npixmaps < 2) {
printf ("movie timer called with %d pixmaps\n", npixmaps);
abort();
}
/* get current image number */
XmScaleGetValue (frame_w, &i);
/* advance to next image */
if (bouncing) {
if (--i < 0) {
bouncing = 0;
i = 1;
}
} else {
if (++i == npixmaps) {
if (XmToggleButtonGetState(bounce_w)) {
bouncing = 1;
i = npixmaps-2;
} else
i = 0;
}
}
XmScaleSetValue (frame_w, i);
/* show it */
showFrame (i);
/* repeat */
XmScaleGetValue (rate_w, &r);
ml_id = XtAppAddTimeOut (xe_app, 1000/r, ml_timer_cb, 0);
}
/* show frame i.
* if i < 0, show a blank image.
*/
static void
showFrame (int i)
{
if (i >= npixmaps) {
printf ("Bad pixmap number: %d of %d\n", i, npixmaps);
abort();
}
/* establish size */
if (i >= 0) {
Pixmap pm = pixmaps[i];
Window dawin = XtWindow(mlda_w);
GC gc = DefaultGC(XtD,DefaultScreen(XtD));
unsigned int w, h;
getWH (pm, &w, &h);
set_something (mlda_w, XmNwidth, (XtArgVal)w);
set_something (mlda_w, XmNheight, (XtArgVal)h);
XCopyArea(XtD, pm, dawin, gc, 0, 0, w, h, 0, 0);
} else {
/* N.B. setting to 0,0 crashes */
set_something (mlda_w, XmNwidth, (XtArgVal)1);
set_something (mlda_w, XmNheight, (XtArgVal)1);
}
}
/* get the width and height of the specified drawable */
static void
getWH (Drawable dr, unsigned int *wp, unsigned int *hp)
{
Window root;
unsigned int b, d;
int x, y;
XGetGeometry (XtD, dr, &root, &x, &y, wp, hp, &b, &d);
}
/* add the given pixmap to the movie.
* if defined, put timestamp just below.
* return 0 if ok else -1
*/
static int
addPixmap (Drawable pm, Widget timestamp)
{
Window tswin = timestamp ? XtWindow(timestamp) : 0;
GC gc = DefaultGC(XtD,DefaultScreen(XtD));
unsigned int pw, ph, tw, th;
unsigned int b, d;
Window root;
int x, y;
Pixmap newpm;
/* get sizes */
XGetGeometry (XtD, pm, &root, &x, &y, &pw, &ph, &b, &d);
if (tswin)
getWH (tswin, &tw, &th);
else
tw = th = 0;
/* dup pm with room for timestamp below, if any */
newpm = XCreatePixmap (XtD, pm, pw, ph+th, d);
if (!newpm) {
xe_msg (1, "Can not duplicate pixmap");
return (-1);
}
XCopyArea(XtD, pm, newpm, gc, 0, 0, pw, ph, 0, 0);
/* put timestamp beneath bottom center, if any */
if (tswin) {
Pixel p;
get_something (timestamp, XmNbackground, (XtArgVal)&p);
XSetForeground (XtD, gc, p);
XFillRectangle (XtD, newpm, gc, 0, ph, pw, th);
XCopyArea (XtD, tswin, newpm, gc, 0, 0, tw, th, (pw-tw)/2, ph);
}
/* add newpm to list */
pixmaps = (Pixmap *) XtRealloc ((char*)pixmaps,
(npixmaps+1)*sizeof(Pixmap));
pixmaps[npixmaps++] = newpm;
/* grow frame scale -- always keep XmNmax > 0 */
if (npixmaps > 1)
set_something (frame_w, XmNmaximum, (XtArgVal)(npixmaps-1));
XmScaleSetValue (frame_w, npixmaps-1);
return (0);
}
/* stop looping (ok if not) */
static void
stopLooping()
{
if (ml_id != 0) {
XtRemoveTimeOut (ml_id);
ml_id = 0;
XmScaleSetValue (rate_w, 0);
}
}
/* create the movie file prefix prompt */
static void
ml_create_prepd_w()
{
Widget t_w;
Arg args[20];
int n;
n = 0;
XtSetArg (args[n], XmNcolormap, xe_cm); n++;
XtSetArg(args[n], XmNmarginWidth, 10); n++;
XtSetArg(args[n], XmNmarginHeight, 10); n++;
prepd_w = XmCreatePromptDialog (mlshell_w, "MoviePrefix", args,n);
set_something (prepd_w, XmNcolormap, (XtArgVal)xe_cm);
set_xmstring (prepd_w, XmNdialogTitle, "xephem Movie prefix");
set_xmstring (prepd_w,XmNselectionLabelString,"Movie files prefix:");
t_w = XmSelectionBoxGetChild(prepd_w, XmDIALOG_TEXT);
defaultTextFN (t_w, 1, "mymovie", NULL);
wtip (t_w,
"Enter the prefix for the set of files comprising this movie");
XtUnmanageChild (XmSelectionBoxGetChild(prepd_w, XmDIALOG_HELP_BUTTON));
XtAddCallback (prepd_w, XmNokCallback, ml_prefix_cb, NULL);
XtAddCallback (prepd_w, XmNmapCallback, prompt_map_cb, NULL);
}
/* called when the Ok button is hit in the file prefix prompt */
/* ARGSUSED */
static void
ml_prefix_cb (Widget w, XtPointer client, XtPointer call)
{
char *prefix;
get_xmstring(w, XmNtextString, &prefix);
if (strlen(prefix) == 0) {
xe_msg (1, "Please enter a prefix for the frame files.");
XtFree (prefix);
return;
}
saveMovie (prefix);
XtFree (prefix);
}
/* save each frame to <privatedir>/<prefix><nnn>.png */
static void
saveMovie(char *prefix)
{
unsigned char *raster;
char fn[1024];
unsigned int w, h;
int i;
for (i = 0; i < npixmaps; i++) {
/* get pixmap as rgb raster */
if (getPMPix (pixmaps[i], &raster, &w, &h) < 0)
break;
/* create frame file */
sprintf (fn, "%s/%s%03d.png", getPrivateDir(), prefix, i);
/* write raster as png */
if (writePNG (fn, raster, w, h) < 0) {
free (raster);
break;
}
/* done */
free (raster);
}
}
/* suck out the pixels from the given pixmap and return as a list of rgb bytes.
* return size of image and malloced raster of bytes.
* N.B. if return 0 caller must free *raster
* return 0 if ok else -1 with xe_msg
*/
static int
getPMPix (Pixmap pm, unsigned char **raster, unsigned int *wp, unsigned int *hp)
{
unsigned int w, h;
int x, y;
XColor xc;
XImage *xim;
unsigned char *rp;
/* get server pixmap as a local image */
getWH (pm, &w, &h);
xim = XGetImage (XtD, pm, 0, 0, w, h, ~0, ZPixmap);
if (!xim) {
xe_msg (1, "Can not create temp %dx%d image", w, h);
return (-1);
}
*raster = rp = malloc (w*h*3);
if (!rp) {
xe_msg (1, "Can not malloc %dx%d temp raster", w, h);
free (xim->data);
xim->data = NULL;
XDestroyImage (xim);
return (-1);
}
/* scan image to retrieve rgb values */
pixCache (NULL);
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
xc.pixel = XGetPixel (xim, x, y);
pixCache (&xc);
*rp++ = xc.red >> 8;
*rp++ = xc.green >> 8;
*rp++ = xc.blue >> 8;
}
}
/* free temp image */
free (xim->data);
xim->data = NULL;
XDestroyImage (xim);
/* return size */
*wp = w;
*hp = h;
return (0);
}
/* given an rgb raster, save to the given file name in png format
* return 0 if ok else -1 with xe_msg
*/
static int
writePNG (char *fn, unsigned char *raster, unsigned int w, unsigned int h)
{
FILE *fp;
png_structp png;
png_infop info;
png_bytepp row_pointers;
int i;
/* create file */
fp = fopen (fn, "wb");
if (!fp) {
xe_msg (1, "Can not create %s:\n%s", fn, syserrstr());
return (-1);
}
/* build list of pointers to each row */
row_pointers = (png_bytepp) malloc (h * sizeof(png_bytep));
if (!row_pointers) {
xe_msg (1, "Can not malloc %d PNG row pointers", h);
fclose (fp);
return (-1);
}
for (i = 0; i < h; i++)
row_pointers[i] = raster + i*(w*3);
/* write png */
png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info = png_create_info_struct (png);
if (setjmp (png_jmpbuf (png))) {
xe_msg (1, "PNG error");
png_destroy_write_struct (&png, &info);
free (row_pointers);
fclose (fp);
return (-1);
}
png_init_io(png, fp);
png_set_IHDR (png, info, w, h, 8, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info (png, info);
png_write_image (png, row_pointers);
png_write_end (png, info);
png_destroy_write_struct (&png, &info);
free (row_pointers);
/* check for io error */
if (ferror(fp)) {
xe_msg (1, "Write error %s:\n%s", fn, syserrstr());
fclose (fp);
return (-1);
}
/* ok */
fclose (fp);
return (0);
}
/* For RCS Only -- Do Not Edit */
static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: moviemenu.c,v $ $Date: 2009/01/05 20:55:54 $ $Revision: 1.10 $ $Name: $"};