;+
; NAME:
;	CW_PROGRESS
;
;
; PURPOSE:
;	Generate a progress bar.
;
;
; CATEGORY:
;	UTILS
;
;
; CALLING SEQUENCE:
;	wid = cw_progress(parent[, <options>])
;
;
; INPUTS:
;	parent	long	Widget ID of the base widget in which it is
;			located. If no parent is given, then the
;			widget appears as a pop-up.
;
;
; KEYWORD PARAMETERS:
;	title	string	A title for the progress bar
;	/column		If set, then any title is placed above the bar
;			and the text progress below it.
;	xsize	int	The x-size of the bar in pixels
;	ysize	int	The y-size of the bar in pixels
;	font	string	Font to use for the title and text progress.
;	/frame		If set, then put a frame round the whole compound
;	/box		If set, then put a frame round the
;			draw_widget. (default is set (use box=0 to remove it)
;	uvalue	any	A user value for the widget.
;	uname	string	A user name for the widget
;	value	float	An initial setting of the progress state.
;	text	int	The format of the textual display of the
;			progress, 0 -- no text display, 1 -- display
;                       as a fraction, 2 (default)  -- display as
;                       percentage, 3 -- display as N of Nmax (for
;                       this the maximum keyword must be given)
;	map	bool	Whether the widget is initially mapped or not
;			(since the widget you see is a BASE, it can be
;			mapped and unmapped directly).
;	maximum	int	The maximum value for the progress (implies
;			TEXT=3)
;	group_leader long A Group leader for the base widget (ignored
;			if a parent is specified)
;	/modal		If set, then the widget is modal (only
;			relevant if a group_leader is given and parent
;			is not).
;	ndigit	int	The maximum number of digits for the text
;			progress indication (ignored when text ne 3).
;
; OBSOLETE KEY
;	index	int	Formerly used to set colours, now ignored
;
; OUTPUTS:
;	wid	long	The widget ID of the resulting widget.
;
;
; SIDE EFFECTS:
;	A pixmap 2*xsize x ysize is created.
;
; RESTRICTIONS:
;	The size may not be greater than 32k. widget_control's
;	get_value key is supported but not very useful.
;	If it is created before any graphics windows, then unless you
;	explicitly create a window, your graphics will go to the
;	progress bar.
;
; EXAMPLE:
;	The widget is used by using widget_control to set its value to
;	the completed fraction. e.g.
;		progid = cw_progress(base, title='Progress')
;		.
;		.
;		.
;		for j =0, n-1 do begin
;		    .
;		    .
;		    widget_control, progid,set_value=float(j)/float(n-1)
;		endfor
;
;
; MODIFICATION HISTORY:
;	Original: 9/1/03; SJT
;	Redesigned internals: 10/1/03; SJT
;	Fixed so it doesn't crash with NaN inputs: 13/5/03; SJT
;	Allow it to run as a standalone: 18/7/03; SJT
;	Add "3-D look" and make /box the default: 4/11/03; SJT
;	Add TEXT=3 (N of M) mode: 5/11/03; SJT
;	Fixes to prevent it changing input keys: 20/11/03; SJT
;	Add group_leader keyword: 20/7/10: SJT
;	Use system widget colours (index key is kept but ignored):
;	13/8/10; SJT
;	Fix crash bug, when no initial value is given: 17/8/10; SJT
;	Add a routine to change the maximum & ndigit keyword: 7/3/10; SJT
;-

;=========================================
; KILL_NOTIFY -- handler, destroys the pixmap. N.B. this is associated
;                with the inner base as getting children isn't allowed
;                at this stage.
pro cw_progress_state__define

state = {cw_progress_state, $
         value: 0., $
         progid: 0l, $
         progidx: 0l, $
         proglab: 0l, $
         proglab2: 0l, $
         pixindex: 0l, $
         xsize: 0, $
         ysize: 0, $
         wold: 0l, $
         text: 0, $
         maximum: 0l, $
         format: ''}
end

pro cw_progress_set_max, id, maxval

cid = widget_info(id, /child)
widget_control, cid, get_uvalue = state

if state.text ne 3 then begin
    message, /continue, "Cannot change the maximum of a fractional or " + $
      "percentage bar"
    return
endif

state.maximum = maxval
widget_control, state.proglab2, set_value = ' of '+string(maxval, $
                                                          format = $
                                                          "(I0)")
widget_control, cid, set_uvalue = state
widget_control, id, set_value = state.value

end
pro cw_progress__kill_b, cid

widget_control, cid, get_uvalue = state
wdelete, state.pixindex

end

;=========================================
; VALUE SETTER. -- Has to be able to cope with setting a value before
;                  realization, when the draw widget is inaccessible.

pro cw_progress_set_v, id, value

if finite(value) ne 1 then value = 0.

cid = widget_info(id, /child)
widget_control, cid, get_uvalue = state
wold = !d.window

if state.text eq 3 then state.value = value $
else state.value = 0. > value < 1.0
case state.text of
    3: widget_control, state.proglab, set_value = string(value, $
      format = state.format)
    2: widget_control, state.proglab, set_value = $
      string(100*state.value, format = state.format)
    1:  widget_control, state.proglab, set_value = $
      string(state.value, format = state.format)
    0:
endcase

widget_control, cid, set_uvalue = state

if state.progidx eq -1 then return

nvalue = round(state.xsize*state.value/float(state.maximum))
ivalue = state.xsize - nvalue
jvalue = nvalue/2 < 2

wset, state.progidx

device, copy = [ivalue, 0, state.xsize, state.ysize, 0, 0, $
                state.pixindex]
if jvalue ne 0 then device, copy = [0, 0, jvalue, state.ysize, 0, 0, $
                                    state.pixindex] 
wset, wold

end

;=========================================
; VALUE GETTER -- Not really very useful, but we might as well have
;                 it, after all it's pretty trivial to do.

function cw_progress_get_v, id

cid = widget_info(id, /child)
widget_control, cid, get_uvalue = state
return, state.value

end

;=========================================
; REALIZATION CALLBACK -- used to get the window ID and to draw the
;                         intial bar on the realization of the widget.

pro cw_progress__init, id

cid = widget_info(id, /child)
widget_control, cid, get_uvalue = state

widget_control, state.progid, get_value = progidx
state.progidx = progidx
widget_control, cid, set_uvalue = state

widget_control, id, set_value = state.value
wset, state.wold


end

;=========================================
; MAIN ROUTINE -- Defines the widget.

function cw_progress, parent, $
                      title = title, $
                      column = column, $
                      xsize = xsize, $
                      ysize = ysize, $
                      index = index, $
                      font = font, $
                      frame = frame, $
                      box = box, $
                      uvalue = uvalue, $
                      uname = uname, $
                      value = value, $
                      text = text, $
                      map = map, $
                      maximum = maximum, $
                      group_leader = group_leader, $
                      modal = modal, $
                      ndigit = ndigit

; Default parameters settings

; if not keyword_set(index) then index1 = [192, 160] $
; else if n_elements(index) ne 2 then return, 0l $
; else index1 = fix(index)

if not keyword_set(xsize) then myxsize = 100 $
else myxsize = xsize
if not keyword_set(ysize) then myysize = 10 $
else myysize = ysize

if not keyword_set(value) then myvalue = 0.0 $
else myvalue = float(value)

if keyword_set(maximum) then begin
    mytext = 3
    mymaximum = long(maximum)
endif else begin
    mymaximum = 1l
    if n_elements(text) eq 0 then mytext = 2 $
    else mytext = text
endelse
if mytext eq 3 and mymaximum eq 0 then mytext = 2

if n_elements(box) eq 0 then box = 1


; The actual definition

if n_params() eq 1 then $
  base = widget_base(parent, $
                     uname = uname, $
                     uvalue = uvalue, $
                     pro_set_value = 'cw_progress_set_v', $
                     func_get_value = 'cw_progress_get_v', $
                     notify_realize = 'cw_progress__init', $
                     map = map) $
else base =  widget_base(uname = uname, $
                         pro_set_value = 'cw_progress_set_v', $
                         func_get_value = 'cw_progress_get_v', $
                         notify_realize = 'cw_progress__init', $
                         title = title, $
                         group_leader = group_leader, $
                         modal = modal)

ibase = widget_base(base, $
                    column = keyword_set(column), $
                    row = keyword_set(column) eq 0, $
                    kill_notify = 'cw_progress__kill_b', $
                    frame = frame, $
                    /base_align_center)

if keyword_set(title) then junk = widget_label(ibase, $
                                               value = title, $
                                               font = font)

sbase = widget_base(ibase, $
                    /row)

progid = widget_draw(sbase, $
                     xsize = myxsize, $
                     ysize = myysize, $
                     frame = box)

proglab2 = 0l
case mytext of
    0: begin
        proglab =  0l
        fmt = ''
    end
    1: begin
        proglab = widget_label(sbase, $
                               value = '0.000', $
                               font = font)
        fmt = "(F5.3)"
    end
    3: begin
        if ~keyword_set(ndigit) then ndigit = ceil(alog10(mymaximum+1))
        fmt = "(I"+string(ndigit, format = "(I0)")+")"
        proglab = widget_label(sbase, $
                               value = string(0, format = fmt), $
                               font = font)
        proglab2 = widget_label(sbase, $
                                value = ' of '+string(mymaximum, format = $
                                                  "(I0)"), $
                                font = font)
    end
    else: begin
        mytext = 2
        proglab = widget_label(sbase, $
                               value = '100%', $
                               font = font)
        fmt = "(I3,'%')"
    end
endcase

; Create the bar so we can copy it to the display window quickly.

wc = widget_info(base, /system_colors)
wold = !d.window
window, /free, /pixmap, xsize = 2*myxsize, ysize = myysize

pixindex = !d.window

im = bytarr(3, 2*myxsize, myysize)
for j = 0, 2 do begin
    im[j, 0:1, 0:myysize-3] = wc.light_edge_3d[j]
    im[j, 2:myxsize-3, 2:myysize-3] = wc.face_3d[j]
    im[j, 0:myxsize-1, myysize-2:myysize-1] = wc.light_edge_3d[j]
    im[j, myxsize-2:myxsize-1,  0:myysize-1] = wc.shadow_3d[j]
    im[j, 1:myxsize-2, 0:1] = wc.shadow_3d[j]
    im[j, myxsize-1, myysize-1] = wc.face_3d[j]
    im[j, myxsize-2, myysize-2] = wc.face_3d[j]
    im[j, myxsize-2, myysize-1] = wc.light_edge_3d[j]
    im[j, 1, 1] = wc.light_edge_3d[j]
;    im[j, myxsize:*, *] = wc.scrollbar[j]
    im[j, myxsize:*, *] = wc.face_3d[j]
endfor

tv, im, true = 1

wset,  wold

state = {cw_progress_state, $
         value: myvalue, $
         progid: progid, $
         progidx: -1l, $
         proglab: proglab, $
         proglab2: proglab2, $
         pixindex: pixindex, $
         xsize: myxsize, $
         ysize: myysize, $
         wold: !d.window, $
         text: mytext, $
         maximum: mymaximum, $
         format: fmt}

widget_control, ibase, set_uvalue = state

if n_params() eq 0 then widget_control, base, /real

return, base

end
