;+
; NAME:
;     XMOVIE
;
; PURPOSE:
;
;     This program is a simplified version of XINTERANIMATE. It is written
;     to illustrate the proper way to write an animation loop in a widget
;     program using the WIDGET_TIMER functionality and pixmaps.
;
; AUTHOR:
;
;       FANNING SOFTWARE CONSULTING
;       David Fanning, Ph.D.
;       1645 Sheely Drive
;       Fort Collins, CO 80526 USA
;       Phone: 970-221-0438
;       E-mail: davidf@dfanning.com
;       Coyote's Guide to IDL Programming: http://www.dfanning.com
;
; CATEGORY:
;
;     Widgets.
;
; CALLING SEQUENCE:
;
;      XMOVIE, image3d
;
; INPUTS:
;
;     image3d: A three-dimensional image array. The animation occurs over
;              the third dimension.
;
; KEYWORD PARAMETERS:
;
;     GROUP:   The group leader of the program. When the group leader dies,
;              this program dies as well.
;
;     TITLE:   The window title of the program. The default is "Animation
;              Example...".
;
; COMMON BLOCKS:
;
;     None.
;
; SIDE EFFECTS:
;
;     None.
;
; EXAMPLE:
;
;     To open the abnormal heart data and animate it, type:
;
;        filename = FILEPATH(SUBDIR=['examples', 'data'], 'abnorm.dat')
;        OPENR, lun, filename, /GET_LUN
;        data = BYTARR(64, 64, 15)
;        READU, lun, data
;        FREE_LUN, lun
;        data = REBIN(data, 256, 256, 15)
;
;        XMOVIE, data
;
; MODIFICATION HISTORY:
;
;      Written by: David W. Fanning, June 96.
;      Added slider for controlling animation speed. 30 June 99. DWF
;      Added pixmap operations. 15 May 2002. DWF
;-
;
;###########################################################################
;
; LICENSE
;
; This software is OSI Certified Open Source Software.
; OSI Certified is a certification mark of the Open Source Initiative.
;
; Copyright © 1996-2002 Fanning Software Consulting.
;
; This software is provided "as-is", without any express or
; implied warranty. In no event will the authors be held liable
; for any damages arising from the use of this software.
;
; Permission is granted to anyone to use this software for any
; purpose, including commercial applications, and to alter it and
; redistribute it freely, subject to the following restrictions:
;
; 1. The origin of this software must not be misrepresented; you must
;    not claim you wrote the original software. If you use this software
;    in a product, an acknowledgment in the product documentation
;    would be appreciated, but is not required.
;
; 2. Altered source versions must be plainly marked as such, and must
;    not be misrepresented as being the original software.
;
; 3. This notice may not be removed or altered from any source distribution.
;
; For more information on Open Source Software, visit the Open Source
; web site: http://www.opensource.org.
;
;###########################################################################



PRO XMOVIE_CLEANUP, tlb

   ; Come here when the TLB is destroyed so you can clean up.

Widget_Control, tlb, Get_UValue=info
IF N_Elements(info) NE 0 THEN WDelete, info.pixid

END ; ***************************** of XMOVIE_CLEANUP ***********************



PRO XMOVIE_DELAY_EVENTS, event

   ; Get the INFO structure.

WIDGET_CONTROL, event.top, GET_UVALUE=info, /NO_COPY

   ; Set the delay.

info.delay = (100 - event.value) / 200.0 ; Max delay 0.5 seconds.

      ; Put INFO structure back.

WIDGET_CONTROL, event.top, SET_UVALUE=info, /NO_COPY


END ; ************************ of XMOVIE_DELAY_EVENTS ***********************



PRO XMOVIE_EVENT, event

   ; Get the INFO structure.

WIDGET_CONTROL, event.top, GET_UVALUE=info, /NO_COPY

   ; What kind of event is this?

eventName = TAG_NAMES(event, /STRUCTURE_NAME)

   ; Code for BUTTON events.

IF eventName EQ 'WIDGET_BUTTON' THEN BEGIN

   CASE event.id OF

      info.start: BEGIN

            ; If stop flag is 0, then display next frame.

         IF info.stopflag EQ 0 THEN BEGIN
            WSET, info.wid
            Device, Copy=[info.xsize*info.thisFrame, 0, info.xsize, info.ysize, 0, 0, info.pixid]
         ENDIF

            ; Update frame number.

         info.thisFrame = info.thisFrame + 1
         IF info.thisFrame GT (info.nframes - 1) THEN info.thisFrame = 0

            ; Set a timer event to get back into this event handler.

         WIDGET_CONTROL, event.ID, TIMER=0

            ; Update stop flag.

         info.stopflag = 0
      END ; of START button CASE.

      info.stop: BEGIN
         info.stopflag = 1
         info.thisFrame = info.thisFrame - 1  ; Subtract a frame.
         END

      info.quit: WIDGET_CONTROL, event.top, /DESTROY
   ENDCASE

      ; Put INFO structure back.

   IF WIDGET_INFO(event.top, /VALID_ID) THEN $
      WIDGET_CONTROL, event.top, SET_UVALUE=info, /NO_COPY
   RETURN
ENDIF ; of BUTTON code

   ; Code for TIMER events.

IF eventName EQ 'WIDGET_TIMER' THEN BEGIN

   IF info.stopflag EQ 0 THEN BEGIN

      WSET, info.wid

         ; Delay for a moment.

      Wait, info.delay

         ; Display the image by copying from the pixmap.

      Device, Copy=[info.xsize*info.thisFrame, 0, info.xsize, info.ysize, 0, 0, info.pixid]

         ; Update frame number.

      info.thisFrame = info.thisFrame + 1
      IF info.thisFrame GT (info.nframes - 1) THEN info.thisFrame = 0

         ; Set a timer event to get back into this event handler.

      IF info.stopflag EQ 0 THEN WIDGET_CONTROL, event.ID, TIMER=0
   ENDIF

      ; Put INFO structure back.

   WIDGET_CONTROL, event.top, SET_UVALUE=info, /NO_COPY
ENDIF ; of TIMER code

END ; ******************************** of XMOVIE_EVENT ***********************



PRO XMOVIE, data, GROUP=group, TITLE=title

ON_ERROR, 1

   ; Read some 3D example data for animation if none passed in.

IF N_PARAMS() EQ 0 THEN BEGIN
   filename = FILEPATH(SUBDIR=['examples', 'data'], 'abnorm.dat')
   OPENR, lun, filename, /GET_LUN
   data = BYTARR(64, 64, 15)
   READU, lun, data
   FREE_LUN, lun
   data = REBIN(data, 256, 256, 15)
ENDIF

IF N_ELEMENTS(title) EQ 0 THEN title='Animation Example...'

  ; Collect information about the dimensions of the data.

dataSize = SIZE(data)
IF dataSize(0) NE 3 THEN MESSAGE, 'Data parameter must be 3D.'

   ; Create the widgets.

tlb = WIDGET_BASE(COLUMN=1, TITLE=title)
start = WIDGET_BUTTON(tlb, Value='Start Animation')
stop = WIDGET_BUTTON(tlb, Value='Stop Animation')
drawID = WIDGET_DRAW(tlb, XSIZE=dataSize(1), YSIZE=dataSize(2))
delayID = WIDGET_SLIDER(tlb, Max=100, Min=0, /Suppress_Value, $
   Title='Frame Delay', Value=50, Event_Pro='XMovie_Delay_Events')
quit = WIDGET_BUTTON(tlb, Value='Exit Program')

   ; Realize and make draw widget active graphics window.

WIDGET_CONTROL, tlb, /REALIZE
WIDGET_CONTROL, drawID, GET_VALUE=wid
WSET, wid

   ; Load color table and first frame.

Device, Decomposed=0
LOADCT, 3
TVSCL, data(*,*,0)
XYOUTS, 15, 15, /DEVICE, 'Frame 0'

   ; Create a pixmap and load the images onto it for fast copy
   ; to the display.

Window, /Free, /Pixmap, XSize=datasize(1)*datasize(3), YSize=datasize(2)
pixID = !D.Window
Widget_Control, Hourglass=1
FOR j=0,datasize(3) - 1 DO BEGIN
   TVSCL, data(*,*,j), j*datasize(1), 0
   XYOUTS, 15 + (datasize(1)*j), 15, /Device, 'Frame ' + StrTrim(j,2)
ENDFOR
Widget_Control, Hourglass=0

   ; Create an "INFO" structure for program info. Store in TLB.

info = { start:start, $         ; ID of START button.
         stop:stop, $           ; ID of STOP button.
         quit:quit, $           ; ID of QUIT button.
         pixid:pixid, $         ; The window index number of the pixmap.
         wid:wid, $             ; Graphics window index number of draw widget.
         delay:0.25, $          ; The amount of time (seconds) to delay between frames.
         stopflag:0, $          ; A stop animation flag.
         xsize:dataSize(1), $   ; The X size of the image.
         ysize:dataSize(2), $   ; The Y size of the image.
         nframes:dataSize(3), $ ; The total number of animation frames.
         thisFrame:0 }          ; The current animation frame.

WIDGET_CONTROL, tlb, SET_UVALUE=info, /NO_COPY

   ; Run the program.

XMANAGER, 'xmovie', tlb, GROUP_LEADER=group, CLEANUP='XMovie_Cleanup'
END
