/*****************************************************************************
*   A windows NT interface for animation (based on the motif interface).     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Written by:  Michael Plavnik			       Ver 0.1, Sept. 1995.  *
* Written by:  Iris Steinvarts			       Ver 0.1, March 1995.  *
*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "irit_sm.h"
#include "iritgrap.h"
#include "windows.h"
#include "windowsx.h"
#include "wntdrvs.h"
#include "commctrl.h"

#define FRAC		5
#define SCALEMULT	100
#define BUTTONSNUM	20
#define BUFSIZE		100
#define OFFSET		1

typedef enum {
    MOVIE_MIN,
    MOVIE_MAX,
    MOVIE_DT,
    MOVIE_FAST_DT
}
MovieScaleType;

static double
    MovieDt = 0.01,
    MovieFastDt = 0.05;
static int
    MovieSingleStep = FALSE;

static HWND 
    AnimationForm, 
    AnimationScale, 
    AnimationButtons[BUTTONSNUM];

void MovieRewindCb(void);
void MovieSaveCb(HWND Parent);
void MovieDismissCb(void);
void MoviePlayBackCb(void);
void MovieStopCb(void);
void MoviePlayCb(void);
void MovieFastForwardCb(void);
void MovieRestartCb(void);
void MovieFastDtCb(void);
void MovieDtCb(void);
void MovieScaleCb(float ScaleValue);

static void ScaleCB(HWND Scale, PVOID ClientData, PVOID CallData);
static void InitButtons(void);
static HWND CreateButton(int idButton, int idBitmap);
static void ButtonsCallBack(HWND hWnd,
			    PVOID ClientData,
			    PVOID *cbs);
static void IntervalDtCB(HWND Parent);
static void SingleStepCB(HWND Parent);
static void FastIntervalDtCB(HWND Parent);
static void MinTimeCB(HWND Parent);
static void MaxTimeCB(HWND Parent);
static void ReadValue(HWND HWND,
		      PVOID ClientData,
		      PVOID CallData);
static void NewValue(HWND HWND,
		     PVOID ClientData,
		     PVOID CallData);
static void DisplayErrorMsg(void);
static void DisplayWrnMsg(void);
static void DisplayErrValue(char *Msg);
static int  IsNumber(char *Buf, double *Value);
static void DoSingleStepAnimation(AnimationStruct *Anim, IPObjectStruct *PObj);

/* USER INTERFACE (WINDOWS NT). */

#define WINVER 0x0400

typedef struct SlideBar {
    HWND  ValueText;
    SHORT DecimalPoints;
    SHORT Scale;
} SlideBar;

typedef struct PromptDialogStruct {
    const char *Title;
    const char *SelectionLabel;
    void (*OkCB)(HWND, PVOID, PVOID);
    PVOID ClientData;
} PromptDialogStruct;

static LRESULT CALLBACK ImageButtonWndProc(HWND hWnd,
					   UINT msg,
					   WPARAM wParam,
					   LPARAM lParam);
static BOOL CALLBACK AnimationFormDlgProc(HWND hDlg,
					  UINT uMsg,
					  WPARAM wParam,
					  LPARAM lParam);
static int PromptDialogBox(HWND Parent,
                           const char *Title,
                           const char *SelectionLabel,
                           void (*OkCB)(HWND, PVOID, PVOID),
                           PVOID ClientData);
static SlideBar* CreateSlidebar(HWND hTrackbar, 
                                SHORT Min, SHORT Max, SHORT Value, 
                                SHORT decimalPoints, SHORT scale);
static void DestroySlidebar(HWND hTrackbar);
static LONG SlidebarGetValue(HWND hTrackbar);
static LONG SlidebarGetMinimum(HWND hTrackbar);
static LONG SlidebarGetMaximum(HWND hTrackbar);
static void SlidebarSetValue(HWND hTrackbar, int Value, BOOL Redraw);
static void SlidebarSetMinimum(HWND hTrackbar, int Value, BOOL Redraw);
static void SlidebarSetMaximum(HWND hTrackbar, int Value, BOOL Redraw);
static void UpdateSlidebar(HWND hTrackbar);

/*****************************************************************************
* DESCRIPTION:								     M
*   creates the main animation window				   	     M
*									     *
* PARAMETERS:								     M
*   IGTopLevel: The shell HWND (top level shell) 			     M
*									     *
* RETURN VALUE:								     M
*   void								     M
*****************************************************************************/
void CreateAnimation(HWND IGTopLevel)
{
    AnimResetAnimStruct(&IGAnimation);
    AnimFindAnimationTime(&IGAnimation, IGGlblDisplayList);
    IGAnimation.TextInterface = FALSE;

    /* Animation is child of IGTopLevel and gets WM_DESTROY message from it */
    AnimationForm = CreateDialog(GetWindowInstance(IGTopLevel),
                                 MAKEINTRESOURCE(IDD_ANIMATION_FORM),
                                 IGTopLevel,
                                 AnimationFormDlgProc);
    if (!AnimationForm) {
	/* For some reason this fails on Win 95! */
        /*
	IGIritError("CreateAnimation Error", 
                    "Unable to CreateDialog.\nCode %d.", 
                    GetLastError());
	*/
        return;
    }

    AnimationScale = GetDlgItem(AnimationForm, IDC_TRACKBAR_SCALE);
    CreateSlidebar(AnimationScale, 
                   (short) (100.0 * IGAnimation.StartT),
		   (short) (100.0 * IGAnimation.FinalT),
		   (short) (100.0 * IGAnimation.StartT),
                   2,
                   100);
                                 
    InitButtons();
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Initialize the buttons.                                                  *
*                                                                            *
* PARAMETERS:                                                                *
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void InitButtons(void)
{
    unsigned int
	n = 0;

    AnimationButtons[n++] = CreateButton(IDC_BUTTON_REWIND, IDB_BITMAP_REWIND);
    AnimationButtons[n++] = CreateButton(IDC_BUTTON_PLAYBACKWARD,
					 IDB_BITMAP_PLAYBACKWARD);
    AnimationButtons[n++] = CreateButton(IDC_BUTTON_STOP, IDB_BITMAP_STOP);
    AnimationButtons[n++] = CreateButton(IDC_BUTTON_PLAY, IDB_BITMAP_PLAY);
    AnimationButtons[n++] = CreateButton(IDC_BUTTON_FORWARD,
					 IDB_BITMAP_FORWARD);
}

static HWND CreateButton(int idButton, int idBitmap)
{
    HINSTANCE
        hInst = GetWindowInstance(AnimationForm);
    HWND
	button = GetDlgItem(AnimationForm, idButton);    
    HBITMAP
	bitmap = LoadBitmap(hInst, MAKEINTRESOURCE(idBitmap));

    SubclassWindow(button, ImageButtonWndProc);
    SetWindowLong(button, GWL_USERDATA, (long) bitmap);
    return button;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Call back function of scale.                                             *
*                                                                            *
* PARAMETERS:                                                                *
*   Scale:       The HWND to handle.                                         *
*   ClientData:  Not used.                                                   *
*   CallData:    Holds the scale's value.                                    *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void ScaleCB(HWND Scale, PVOID ClientData, PVOID CallData)
{
    LONG
	value = SlidebarGetValue(Scale);

    MovieScaleCb((float) value / 100.0f);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Call back function of all buttons. Invokes the proper action.            *
*                                                                            *
* PARAMETERS:                                                                *
*   w:            Of button to handle.                                       *
*   ClientData:   To get the Name of button.                                 *
*   cbs:          Not used.                                                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void ButtonsCallBack(HWND hWnd,
			    PVOID ClientData,
			    PVOID *cbs)
{
    DWORD 
	Name = (DWORD) ClientData;

    if (Name == IDCANCEL)
	MovieDismissCb();
    else if (Name == IDC_BUTTON_INTERVALDT)
        IntervalDtCB(hWnd);
    else if (Name == IDC_BUTTON_FASTINTERVALDT)
        FastIntervalDtCB(hWnd);
    else if (Name == IDC_BUTTON_SINGLESTEP)
        SingleStepCB(hWnd);
    else if (Name == IDC_BUTTON_MINTIME)
        MinTimeCB(hWnd);
    else if (Name == IDC_BUTTON_MAXTIME)
        MaxTimeCB(hWnd);
    else if (Name == IDC_BUTTON_PLAYBACKWARD)
        MoviePlayBackCb();
    else if (Name == IDC_BUTTON_REWIND)
        MovieRewindCb();
    else if (Name == IDC_BUTTON_SAVE)
        MovieSaveCb(hWnd);
    else if (Name == IDC_BUTTON_STOP)
        MovieStopCb();
    else if (Name == IDC_BUTTON_PLAY)
        MoviePlayCb();
    else if (Name == IDC_BUTTON_FORWARD)
        MovieFastForwardCb();
    else if (Name == IDC_BUTTON_RESTART)
        MovieRestartCb();
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   The call back function of the interval button.                           *
*                                                                            *
* PARAMETERS:                                                                *
*   Parent:        HWND.                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void IntervalDtCB(HWND Parent)
{
    char Buf[BUFSIZ];

    sprintf(Buf, "Current Dt value is %0.4f\nEnter New Interval Dt Value:",
	    MovieDt); 
    PromptDialogBox(Parent, "New Dt", Buf, ReadValue, (PVOID) MOVIE_DT);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   The call back function of the fast interval button.                      *
*                                                                            *
* PARAMETERS:                                                                *
*   Parent:        HWND.                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void FastIntervalDtCB(HWND Parent)
{
    char Buf[BUFSIZ];
    sprintf(Buf, "Current Dt value is %0.4f\nEnter New Fast Interval Dt Value:",
	    MovieFastDt); 
    PromptDialogBox(Parent, "New Fast Dt", Buf, ReadValue,
		    (PVOID) MOVIE_FAST_DT);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Call back function for single step button.	 	                     *
*                                                                            *
* PARAMETERS:                                                                *
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*                                                                            *
* KEYWORDS:                                                                  *
*   SingleStepCB                                                             *
*****************************************************************************/
static void SingleStepCB(HWND Parent)
{
    if (MovieSingleStep = !MovieSingleStep) {
	SetWindowText(Parent, "SStp");
    }
    else {
	SetWindowText(Parent, "Cont");
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   The call back function of the minimum time button.                       *
*                                                                            *
* PARAMETERS:                                                                *
*   Parent:        HWND.                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void MinTimeCB(HWND Parent)
{
    int ScaleValue;
    char Buf[BUFSIZ];

    ScaleValue = SlidebarGetMinimum(AnimationScale);
    sprintf(Buf, "Minimum time is %0.2f\nEnter new minimum time value:",
	    ScaleValue / 100.0); 
    PromptDialogBox(Parent, "Update minimum time", Buf, NewValue,
		    (PVOID) MOVIE_MIN);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   The call back function of the maximum time button.                       *
*                                                                            *
* PARAMETERS:                                                                *
*   Parent:        HWND.                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void MaxTimeCB(HWND Parent)
{
    int ScaleValue;
    char Buf[BUFSIZ];

    ScaleValue = SlidebarGetMaximum(AnimationScale);
    sprintf(Buf, "Maximum time is %0.2f\nEnter new maximum time value:",
	    ScaleValue / 100.0); 
    PromptDialogBox(Parent, "Update maximum time",  Buf, NewValue,
		    (PVOID) MOVIE_MAX);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Gets one real value from user.                                           *
*                                                                            *
* PARAMETERS:                                                                *
*   HWND:         Not used.                                                  *
*   ClientData:   Is this for fast or regular motion!?                       *
*   CallData:     To get a handle on string.                                 *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void ReadValue(HWND widget, PVOID ClientData, PVOID CallData)
{
    char Buf[BUFSIZ];
    double Value;

    if (!GetDlgItemText((HWND) CallData, IDC_EDIT_SELECTION, Buf, BUFSIZ-1)) {
        IGIritError("Read Value Error", "Can't convert compound String");
        return;
    }

    if (IsNumber(Buf, &Value)) {
	if (((int) ClientData) == MOVIE_FAST_DT) {
	    MovieFastDt = Value;
	    MovieFastDtCb();
	}
	else {
	    MovieDt = Value;
	    MovieDtCb();
	}
    }
    else
        DisplayErrorMsg();
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Gets one real value from user.                                           *
*                                                                            *
* PARAMETERS:                                                                *
*   HWND:         Not used.                                                  *
*   ClientData:   Is this for fast or regular motion!?                       *
*   CallData:     To get a handle on string.                                 *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void NewValue(HWND widget, PVOID ClientData, PVOID CallData)
{
    int ScaleValue;
    double Value;
    char Buf[BUFSIZ];

    if (!GetDlgItemText((HWND)CallData, IDC_EDIT_SELECTION, Buf, BUFSIZ-1)) {
        IGIritError("Read Value Error", "Can't convert compound String");
        return;
    }

    if (!IsNumber(Buf, &Value)) {
        DisplayErrorMsg();
    }
    else {
	Value = 100 * Value;
	if (Value != (int) Value) {
	    DisplayWrnMsg();
	    Value = (int) Value;
	}

	if (((int) ClientData) == MOVIE_MIN) {
            ScaleValue = SlidebarGetMaximum(AnimationScale);
	    if ((double)ScaleValue <= Value) 
	        DisplayErrValue("Max");
	    else {
		IGAnimation.StartT = Value / 100.0;
		if (IGAnimation.RunTime < IGAnimation.StartT) {
		    IGAnimation.RunTime = IGAnimation.StartT;
                    SlidebarSetValue(AnimationScale, (int) Value, FALSE);
		}
                SlidebarSetMinimum(AnimationScale, (int) Value, TRUE);    
	    }
	}
	else {
            ScaleValue = SlidebarGetMinimum(AnimationScale);    
	    if ((double)ScaleValue >= Value)
	        DisplayErrValue("Min");
	    else {
		IGAnimation.FinalT = Value / 100.0;
		if (IGAnimation.RunTime > IGAnimation.FinalT) {
		    IGAnimation.RunTime = IGAnimation.FinalT;
                    SlidebarSetValue(AnimationScale, (int) Value, FALSE);
		}
                SlidebarSetMaximum(AnimationScale, (int) Value, TRUE);    
	    }
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Error messages for input of numbers.                                     *
*                                                                            *
* PARAMETERS:                                                                *
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DisplayErrorMsg(void)
{
    MessageBox(IGhTopLevel, "Value is not a number.", "Error",
	       MB_OK | MB_ICONSTOP);    
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Warning messages for input of numbers.                                   *
*                                                                            *
* PARAMETERS:                                                                *
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DisplayWrnMsg(void)
{
    MessageBox(IGhTopLevel, 
               "Only 2 digits after decimal point. Value will be rounded.", 
               "Warning", 
               MB_OK | MB_ICONEXCLAMATION);    
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Error messages for input of numbers.                                     *
*                                                                            *
* PARAMETERS:                                                                *
*   Msg:   To print.                                                         *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DisplayErrValue(char *Msg)
{
    Msg = (strcmp(Msg, "Max") == 0) 
                    ? "Maximum time >= new given minimum"
                    : "Minimum time <= new given maximum";
    MessageBox(IGhTopLevel, Msg, "Error", MB_OK | MB_ICONSTOP);    
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Verifies and converts a string into a numeric value.                     *
*                                                                            *
* PARAMETERS:                                                                *
*   Buf:    To convert to a number.                                          *
*   Value:  Numeric result.                                                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   int:   TRUE is is a number, FALSE otherwise.                             *
*****************************************************************************/
static int IsNumber(char *Buf, double *Value)
{
    return sscanf(Buf, "%lf", Value);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Should we stop this animation? Senses the event queue of X.              M
*                                                                            *
* PARAMETERS:                                                                M
*   Anim:     The animation to abort.                                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      TRUE if we needs to abort, FALSE otherwise.                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   AnimCheckInterrupt                                                       M
*****************************************************************************/
int AnimCheckInterrupt(AnimationStruct *Anim)
{
    MSG Event;

    UpdateWindow(IGhWndView);
    while (PeekMessage(&Event, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE) ||
	   PeekMessage(&Event, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) {
	TranslateMessage(&Event);
	DispatchMessage(&Event);    
    }

    return Anim -> StopAnim;
}

/*****************************************************************************
* DESCRIPTION:								     M
*   Getting input values from the user.					     M
*									     *
* PARAMETERS:								     M
*   None                                 				     M
*									     *
* RETURN VALUE:								     M
*   void 								     M
*****************************************************************************/
void AnimationCB(void)
{  
    BOOL rc;
    RECT wr, dr, cr;      

    GetWindowRect(GetDesktopWindow(), &dr); 
    GetWindowRect(IGhTopLevel, &wr);    
    GetClientRect(AnimationForm, &cr);  /* Get form size */
    rc = SetWindowPos(AnimationForm, 
                      HWND_TOP,
                      min(wr.right+3, dr.right-cr.right-3),
                      wr.top,
                      0, 
                      0,
                      SWP_NOSIZE | SWP_SHOWWINDOW);

#ifdef DEBUG_ERROR_ANIMATION
    if (!rc)
        IGIritError("Error in AnimationCB", 
                    "Unable to SetWindowPos.\nCode %d.", 
                    GetLastError());
#endif /* DEBUG_ERROR_ANIMATION */
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Executes a single animation step according to Anim info an PObj geom.    *
*                                                                            *
* PARAMETERS:                                                                *
*   Anim:    Prescription of the animation step to perform.                  *
*   PObj:    Current geometry to display.                                    *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DoSingleStepAnimation(AnimationStruct *Anim, IPObjectStruct *PObj)
{
    IGAnimation.StopAnim = FALSE;

    AnimDoSingleStep(Anim, PObj);

    if (IGAnimation.RunTime > IGAnimation.FinalT)
	IGAnimation.RunTime = IGAnimation.FinalT;
    if (IGAnimation.RunTime < IGAnimation.StartT)
	IGAnimation.RunTime = IGAnimation.StartT;

    SlidebarSetValue(AnimationScale, (int) (IGAnimation.RunTime * 100.0), TRUE);    
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for play back button.                                 M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MoviePlayBackCb                                                          M
*****************************************************************************/
void MoviePlayBackCb(void)
{
    if (MovieSingleStep) {
	IGAnimation.RunTime -= MovieDt;
	DoSingleStepAnimation(&IGAnimation, IGGlblDisplayList);
    }
    else {
	IGAnimation.StopAnim = FALSE;

        for ( ;
	     IGAnimation.RunTime >= IGAnimation.StartT + IRIT_EPS &&
	     !IGAnimation.StopAnim;
	     IGAnimation.RunTime -= MovieDt) {
    	    DoSingleStepAnimation(&IGAnimation, IGGlblDisplayList);
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for rewind button. 	                             M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MovieRewindCb                                                            M
*****************************************************************************/
void MovieRewindCb(void)
{
    if (MovieSingleStep) {
	IGAnimation.RunTime -= MovieFastDt;
	DoSingleStepAnimation(&IGAnimation, IGGlblDisplayList);
    }
    else {
	IGAnimation.StopAnim = FALSE;

        for ( ;
	     IGAnimation.RunTime >= IGAnimation.StartT-IRIT_EPS &&
	     !IGAnimation.StopAnim;
	     IGAnimation.RunTime -= MovieFastDt) {
    	    DoSingleStepAnimation(&IGAnimation, IGGlblDisplayList);
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for save button. 	                             M
*                                                                            *
* PARAMETERS:                                                                M
*   Parent:        HWND.                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MovieSaveCb                                                              M
*****************************************************************************/
void MovieSaveCb(HWND Parent)
{
    if (IGAnimation.SaveAnimation = !IGAnimation.SaveAnimation) {
	SetWindowText(Parent, "Save");
    }
    else {
	SetWindowText(Parent, "No Save");
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for stop button. 	                             M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MovieStopCb                                                              M
*****************************************************************************/
void MovieStopCb(void) 
{
    IGAnimation.StopAnim = TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for play button. 	                             M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MoviePlayCb                                                              M
*****************************************************************************/
void MoviePlayCb(void) 
{
    if (MovieSingleStep) {
	IGAnimation.RunTime += MovieDt;
	DoSingleStepAnimation(&IGAnimation, IGGlblDisplayList);
    }
    else {
	IGAnimation.StopAnim = FALSE;

        for ( ;
	     IGAnimation.RunTime <= IGAnimation.FinalT + IRIT_EPS &&
	     !IGAnimation.StopAnim;
	     IGAnimation.RunTime += MovieDt) {
    	    DoSingleStepAnimation(&IGAnimation, IGGlblDisplayList);
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for fast forward button. 	                     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MovieFastForwardCb                                                       M
*****************************************************************************/
void MovieFastForwardCb(void) 
{
    if (MovieSingleStep) {
	IGAnimation.RunTime += MovieFastDt;
	DoSingleStepAnimation(&IGAnimation, IGGlblDisplayList);
    }
    else {
	IGAnimation.StopAnim = FALSE;

        for ( ;
	     IGAnimation.RunTime <= IGAnimation.FinalT + IRIT_EPS &&
	     !IGAnimation.StopAnim;
	     IGAnimation.RunTime += MovieFastDt) {
    	    DoSingleStepAnimation(&IGAnimation, IGGlblDisplayList);
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for restart button.	 	                     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MovieRestartCb                                                           M
*****************************************************************************/
void MovieRestartCb(void) 
{
    IGAnimation.RunTime = IGAnimation.StartT;
    MoviePlayCb();
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for dismiss button.	 	                     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MovieDismissCb                                                           M
*****************************************************************************/
void MovieDismissCb(void) 
{
    ShowWindow(AnimationForm, SW_HIDE);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for set fast Dt button.	 	                     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MovieFastDtCb                                                            M
*****************************************************************************/
void MovieFastDtCb(void)
{
    IGAnimation.Dt = MovieFastDt;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for set Dt button.	 	                     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MovieDtCb                                                                M
*****************************************************************************/
void MovieDtCb(void)
{
    IGAnimation.Dt = MovieDt;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Call back function for set scale animation value. 	                     M
*                                                                            *
* PARAMETERS:                                                                M
*   ScaleValue:     Value of scale to update animation with.                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   MovieScaleCb                                                             M
*****************************************************************************/
void MovieScaleCb(float ScaleValue)
{
    IGAnimation.RunTime = ScaleValue;

    DoSingleStepAnimation(&IGAnimation, IGGlblDisplayList);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Creates slider that emulates Motif slider. Parameters are subset of      *
*   resources that Motif slider accepts.                                     *
*                                                                            *
* PARAMETERS:                                                                *
*   hTrackbar: Win32 trackbar common control handle.                         *
*   minimum: minimum value slider can reach.                                 *
*   maximum: maximum value slider can reach.                                 *
*   value:   inital value of the slider.                                     *
*   decimalPoints: number of decimal points in slider value.                 *
*   scale: real fraction.                                                    *
*                                                                            *
* RETURN VALUE:                                                              *
*   SliderBar *:  pointer to the allocated object.                           *
*****************************************************************************/
static SlideBar* CreateSlidebar(HWND hTrackbar, 
				SHORT minimum, 
				SHORT maximum, 
				SHORT value, 
				SHORT decimalPoints,
				SHORT scale)
{
     SlideBar
	*w = calloc(1, sizeof(SlideBar));

     w -> DecimalPoints = decimalPoints;
     w -> Scale = scale;    

     /* InitCommonControls(); */
     SetWindowLong(hTrackbar, GWL_USERDATA, (LONG)w);
     SendMessage(hTrackbar, TBM_SETRANGE, TRUE, MAKELONG(minimum, maximum));
     w -> ValueText = CreateWindow("STATIC",
				   "", 
				   WS_CHILD | SS_LEFT | WS_CLIPSIBLINGS,  
				   0, 0, 40, 20, 
				   GetParent(hTrackbar),
				   NULL,
				   NULL,
				   NULL);
     UpdateSlidebar(hTrackbar);
     ShowWindow(w -> ValueText, SW_SHOW);
      
     return w;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Destroys sliderbar object allocated by CreateSlidebar function call.     *
*                                                                            *
*                                                                            *
* PARAMETERS:                                                                *
*   hTrackbar: Win32 trackbar common control handle (the same on creation)   *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DestroySlidebar(HWND hTrackbar)
{
    SlideBar
	*w = (SlideBar*) GetWindowLong(hTrackbar, GWL_USERDATA);

    if (w) 
        free(w);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Returns current value of the slider.                                     *
*                                                                            *
* PARAMETERS:                                                                *
*   hTrackbar: Win32 handle to the trackbar common control.                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   LONG: current value of the slider.                                       *
*****************************************************************************/
static LONG SlidebarGetValue(HWND hTrackbar)
{
    return SendMessage(hTrackbar, TBM_GETPOS, 0, 0);    
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Modifies value of the slider and postion of its thumb.                   *
*                                                                            *
* PARAMETERS:                                                                *
*   hTrackbar: Win32 handle to the trackbar common control.                  *
*   Value: new value of the slider in (minimum. maximum interval)            *
*   Redraw: redraws slider when true.                                        *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SlidebarSetValue(HWND hTrackbar, int Value, BOOL Redraw)
{
    SendMessage(hTrackbar, TBM_SETPOS, Redraw, Value);    
    UpdateSlidebar(hTrackbar);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Returns minimum value of the slider.                                     *
*                                                                            *
* PARAMETERS:                                                                *
*   hTrackbar: Win32 handle to the trackbar common control.                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   LONG: minimum value of the slider.                                       *
*****************************************************************************/
static LONG SlidebarGetMinimum(HWND hTrackbar)
{
    return SendMessage(hTrackbar, TBM_GETRANGEMIN, 0, 0);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Modifies minimum value of the slider and when current value occures to   *
*   be less then minimum adjusts it accordanly.                              *
*                                                                            *
* PARAMETERS:                                                                *
*   hTrackbar: Win32 handle to the trackbar common control.                  *
*   Value: new minimum value.                                                *
*   Redraw: redraws slider when true.                                        *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*                                                                            *
*****************************************************************************/
static void SlidebarSetMinimum(HWND hTrackbar, int Value, BOOL Redraw)
{
    SendMessage(hTrackbar, TBM_SETRANGEMIN, Redraw, (int) Value); 
    /* Not efficient, but reasonable on modern systems. */
    UpdateSlidebar(hTrackbar);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Returns maximum value of the slider.                                     *
*                                                                            *
* PARAMETERS:                                                                *
*   hTrackbar: Win32 handle to the trackbar common control.                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   LONG: maximum value of the slider.                                       *
*****************************************************************************/
static LONG SlidebarGetMaximum(HWND hTrackbar)
{
    return SendMessage(AnimationScale, TBM_GETRANGEMAX, 0, 0);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Modifies maximum value of the slider and when current value occures to   *
*   be less then maximum adjusts it accordanly.                              *
*                                                                            *
* PARAMETERS:                                                                *
*   hTrackbar: Win32 handle to the trackbar common control.                  *
*   Value: new maximum value.                                                *
*   Redraw: redraws slider when true.                                        *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SlidebarSetMaximum(HWND hTrackbar, int Value, BOOL Redraw)
{
    SendMessage(hTrackbar, TBM_SETRANGEMAX, Redraw, (int) Value); 
    /* Not efficient, but reasonable on modern systems. */
    UpdateSlidebar(hTrackbar);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Causes slider to redraw itself. That is necessary to implement Motif     *
*   emulation (with numeric display under the thumb).                        *
*                                                                            *
* PARAMETERS:                                                                *
*   hTrackbar: Win32 handle to the trackbar common control.                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void UpdateSlidebar(HWND hTrackbar)
{
    RECT r1, r2;
    LONG value;
    int dec, sign;
    char *buf;
    SlideBar 
	*w = (SlideBar*) GetWindowLong(hTrackbar, GWL_USERDATA);

    GetWindowRect(hTrackbar, &r1);
#if (WINVER >= 0x0400)
    SendMessage(hTrackbar, TBM_GETTHUMBRECT, 0, (LPARAM)&r2);
#else
    r2.left = 0;
    r2.bottom = r1.bottom - r1.top;
#endif
    ScreenToClient(GetParent(hTrackbar), (LPPOINT)&r1);
    SetWindowPos(w->ValueText, 
                 hTrackbar, 
                 r1.left + r2.left, r1.top + r2.bottom + 5, 0, 0,
                 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);     

    value = SendMessage(hTrackbar, TBM_GETPOS, 0, 0);    
    buf = _fcvt((double)value/(double)w->Scale, w->DecimalPoints, &dec, &sign);
    if (dec < 0) {
        memmove(&buf[-dec+1], buf, w->DecimalPoints+1+dec);
        memset(buf, '0', 1-dec);
        *buf = '.';
    }
    else {
        memmove(&buf[dec+1], &buf[dec], w->DecimalPoints+1);
        buf[dec] = '.';
    }
    SetWindowText(w->ValueText, buf);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Implements emulation of Motif prompt dialog.                             *
*   Is Win32 dialog callback function.                                       *
*                                                                            *
* PARAMETERS:                                                                *
*   hDlg:   handle of the dialog that calls the function.                    *
*   uMsg:   message sent by the dialog.                                      *
*   wParam: first parameter.                                                 *
*   lParam: second parameter.                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   BOOL: returns true when message is processed completely.                 *
*****************************************************************************/
static BOOL CALLBACK PromptDlgProc(HWND hDlg,
				   UINT uMsg,
				   WPARAM wParam,
				   LPARAM lParam)
{
    const PromptDialogStruct* p;

    switch (uMsg) {
	case WM_INITDIALOG:
            if (lParam) {
	        p = (const PromptDialogStruct*) lParam;
		if (p -> Title)
		    SetWindowText(hDlg, p -> Title);
		if (p -> SelectionLabel)
		    SetDlgItemText(hDlg, IDC_STATIC_SELCTIONLABEL,
				   p -> SelectionLabel);
		SetWindowLong(hDlg, GWL_USERDATA, lParam);
		SetFocus(GetDlgItem(hDlg, IDC_EDIT_SELECTION));
	    }
	    return FALSE;
	case WM_COMMAND:
	    switch (LOWORD(wParam)) {
	        case IDOK:
		    p = (const PromptDialogStruct*)
				        GetWindowLong(hDlg, GWL_USERDATA); 
		    (*p -> OkCB)((HWND) lParam, p -> ClientData, hDlg);
		case IDCANCEL:
		    EndDialog(hDlg, LOWORD(wParam));
		    break;
	    }
	    return TRUE;
	default:
	    return FALSE;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Runs emulation of Motif prompt dialog.                                   *
*   Is Win32 dialog callback function.                                       *
*                                                                            *
* PARAMETERS:                                                                *
*   Parent:     handle to the parent window.                                 *
*   Title:      title of the prompt dialog.                                  *
*   SelectionLable: label that marks edit field.                             *
*   OkCB:       function pointer to OK button handler.                       *
*   ClientData: pointer to the data passed to OkCB.                          *
*                                                                            *
* RETURN VALUE:                                                              *
*   BOOL: returns true when message is processed completely.                 *
*****************************************************************************/
static int PromptDialogBox(HWND Parent,
			   const char *Title,
			   const char *SelectionLabel,
			   void (*OkCB)(HWND, PVOID, PVOID),
			   PVOID ClientData)
{
    PromptDialogStruct args;

    args.Title = Title;
    args.SelectionLabel = SelectionLabel;
    args.OkCB = OkCB;
    args.ClientData = ClientData;
    return DialogBoxParam(NULL, 
                          MAKEINTRESOURCE(IDD_PROMPT), 
                          Parent, 
                          PromptDlgProc, 
                          (LPARAM) &args);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Implements emulation of Motif animation dialog.                          *
*   Is Win32 dialog callback function.                                       *
*                                                                            *
* PARAMETERS:                                                                *
*   hDlg:  handle of the dialog that calls the function.                     *
*   uMsg:  message sent by the dialog.                                       *
*   wParam: first parameter.                                                 *
*   lParam: second parameter.                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   BOOL: returns true when message is processed completely.                 *
*****************************************************************************/
static BOOL CALLBACK AnimationFormDlgProc(HWND hDlg,
					  UINT uMsg,
					  WPARAM wParam,
					  LPARAM lParam)
{
    HWND hWnd;
    PVOID clientId;
        
    switch (uMsg) {
	case WM_INITDIALOG:
            return TRUE;
	case WM_DESTROY:
	    DestroySlidebar(AnimationScale);
	    return FALSE;
	case WM_COMMAND:
	    hWnd = (HWND) lParam;                 /* Handle of control HWND. */
	    if (hWnd != AnimationScale) {
	        clientId = (PVOID) LOWORD(wParam);        /* ID of the item. */
		ButtonsCallBack(hWnd, clientId, NULL);
	    }
	    return TRUE;
	case WM_HSCROLL:
            hWnd = (HWND) lParam;                 /* Handle of control HWND. */
	    if (hWnd == AnimationScale) {
	        UpdateSlidebar(hWnd);
		clientId = (PVOID) LOWORD(wParam);        /* ID of the item. */
		ScaleCB(hWnd, clientId, NULL);    
	    }
	    return TRUE;
	default:
	    return FALSE;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Implements Motif button with image on it. (We could not use OCX).        *
*   That button is a subclassed version of standard button.                  *
*                                                                            *
* PARAMETERS:                                                                *
*   hWnd: handle of the button.                                              *
*   msg: message recieved.                                                   *
*   wParam: 1st parameter.                                                   *
*   lParam: 2nd parameter.                                                   *
*                                                                            *
* RETURN VALUE:                                                              *
*   LRESULT: true when function completely processes the message.            *
*****************************************************************************/
static LRESULT CALLBACK ImageButtonWndProc(HWND hWnd,
					   UINT msg,
					   WPARAM wParam,
					   LPARAM lParam)
{
    HDC hdcWin, hdcMem;
    HGDIOBJ hOld;
    HBITMAP hBitmap;
    BITMAP bmpData;
    RECT rect;
    LRESULT result;
    WNDPROC
	wndproc = (WNDPROC) GetClassLong(hWnd, GCL_WNDPROC);
    unsigned
	offset = 0;

    switch (msg) {
	case WM_DESTROY:
	    DeleteBitmap(GetWindowLong(hWnd, GWL_USERDATA));
	    return CallWindowProc(wndproc, hWnd, msg, wParam, lParam);
	case BM_SETSTATE:
            if (wParam) 
                offset = 1;
	case WM_PAINT:
            result = CallWindowProc(wndproc, hWnd, msg, wParam, lParam);
	    hdcWin = GetDC(hWnd);
	    hdcMem = CreateCompatibleDC(hdcWin); 
	    hBitmap = (HBITMAP) GetWindowLong(hWnd, GWL_USERDATA);
	    hOld = SelectBitmap(hdcMem, hBitmap);
	    GetClientRect(hWnd, &rect);
	    GetObject(hBitmap, sizeof(bmpData), &bmpData);
	    rect.left = max(0, (rect.right - bmpData.bmWidth) / 2) + offset;
	    rect.top = max(0, (rect.bottom - bmpData.bmHeight) / 2) + offset;
	    BitBlt(hdcWin, rect.left, rect.top, bmpData.bmWidth,
		   bmpData.bmHeight, hdcMem, 0, 0, SRCAND);
	    SelectObject(hdcMem, hOld);
	    DeleteDC(hdcMem);
	    ReleaseDC(hWnd, hdcWin);
	    return result;       
    };
    return CallWindowProc(wndproc, hWnd, msg, wParam, lParam);
}
