; $Id: cw_field_arrows.pro,v 1.1 2004/03/11 17:00:00 dimeo Exp $
;CW_FIELD_ARROWS.PRO
;+
;CW_FIELD_ARROWS.PRO
;@ Terry Oakes 2000.
;
;Purpose:
;  Compound widget to change the value of a CW_FIELD using arrow buttons.
;
; Abstract:
;  This is a wrapper for IDL's CW_FIELD, to add up/down arrows to
;  increment/decrement a value. This is a more compact version of
;  a slider. The following data types are permitted: INT, LONG, FLOAT.
;
;  Sometimes the arrows do not get drawn in properly. This seems to happen
;  all the time in some widgets, and never in others. The solution is to
;  insert the following snippet of code after the widget has been realized:
;  ---
;;;     CW_FIELD_ARROWS_FILL_DRAW, cf_field_arrow_wid, /FROM_PARENT
;  ---
;  See comments in code for more details.
;
;  To avoid acting on two events, one for a button press and the other for
;  a button release, you should use the following line at the beginning of
;  the event-handler for each CW_FIELD_ARROWS:
;  ---
;  if (event.update NE 1) then RETURN
;  ---
;
;KEYWORDS:  (in addition to the standard CW_FIELD keywords):
;  INCREMENT Set to a number by which you want to increment/decrement
;      the current value when an up- or down-arrow is pressed.
;      Default is 1 or 1.0.
;      Over-rides MULTIPLE.
;  MULTIPLE  Set to a factor by which you want to multiply/divide
;      the current value when an up- or down-arrow is pressed.
;      Default is 1 or 1.0.
;      Over-ridden by INCREMENT.
;  MINIMUM     Minimum value allowed.
;  MAXIMUM     Maximum value allowed.
;
;Returns:
;  CW_FIELD_ARROWS returns the same structure as the standard CW_FIELD
;  structure:
;  Return_struct = {CW_FIELD_ARROWS_STRUCTURE,   $
;   ID:   0L, $
;   TOP:  0L,   $
;   HANDLER:0L, $
;   VALUE:   0.0,   $
;   TYPE:    0,   $ ;must be INT, LONG, or FLOAT.
;   UPDATE: 0}
;
;History:
;  2000 May  Written by Terry Oakes, Univ. Wisconsin-Madison.
;-
;------------------------------------------------------------------
;--- CW_FIELD_ARROWS_FILL_DRAW
;--- NOTIFY_REALIZE program for draw-window.
;--- Draws the arrow design in the window.
;--- Keywords:
;---  FROM_PARENT  Use this keyword if, for some unknown reason,
;---   this routine does not get called when the widget gets realized.
;---   Pass the widget ID of the parent (i.e. CW_FIELD_ARROWS)
;---   and this routine can then figure out the widget ID of the
;---   draw-window.
;---   Call after the widget has been realized, e.g.:
;---     cw_f_arr=CW_FIELD_ARROWS(...)
;---     WIDGET_CONTROL,tlb,/REALIZE
;---     CW_FIELD_ARROWS_FILL_DRAW, cw_f_arr, /FROM_PARENT
;---
;
PRO  cw_field_arrows_fill_draw, draw_w_id, FROM_PARENT=FROM_PARENT

DRAW_id=draw_w_id
if KEYWORD_SET(FROM_PARENT) then begin
  CW_id = WIDGET_INFO(draw_w_id, /CHILD)
  DRAW_id = WIDGET_INFO(CW_id, /SIBLING)
endif

WIDGET_CONTROL, DRAW_id, GET_UVALUE=State_Arrow, GET_VALUE=win_id
WSET, win_id
TVLCT,r,g,b,/GET
TVLCT, 0,0,0,0
;TVLCT, 255,255,255,!MAX_COL
TVLCT, 255,255,255,255
TV, State_Arrow.arrows
TVLCT,r,g,b
END


;----------------------------------------------------------------
;  Procedure to set the value of a CW_FIELD_ARROWS
;
PRO CW_FIELD_ARROWS_SET, Base, Value

   sValue  = Value     ; Prevent alteration from reaching back to caller
   Sz   = SIZE(sValue)
   IF Sz[0] NE 7 THEN sValue = STRTRIM(Value,2)

   ;*** find widget ID of the CW_FIELD, which is the first child: ***
   CW_id= WIDGET_INFO(Base, /CHILD)
   WIDGET_CONTROL, CW_id, SET_VALUE=sValue
END

;----------------------------------------------------------------
;  Function to get the value of a CW_FIELD_ARROWS
;
FUNCTION CW_FIELD_ARROWS_GET, Base

   ;*** find widget ID of the CW_FIELD, which is the first child: ***
   CW_id = WIDGET_INFO(Base, /CHILD)
   WIDGET_CONTROL, CW_id, GET_VALUE=Value
   RETURN, Value
END


;------------------------------------------------------------------
; event-handler for draw window.
; determines if the user selected an "Up" or "Down" arrow.
;
FUNCTION CW_FIELD_ARROWS_UPDOWN_EVENT, event
prog_str='   (cw_field_arrows_updown_event)'

widget_id = event.id
WIDGET_CONTROL, widget_id, GET_UVALUE=State_Arrow, GET_VALUE=win_id

;*** Find the value of the widget: ***
;*** The value is stored in the Uvalue of the CW_FIELD widget: ***
CW_id = WIDGET_INFO(State_Arrow.Field_id, /CHILD)
WIDGET_CONTROL, CW_id, GET_UVALUE=State
WIDGET_CONTROL, State.TextId, GET_VALUE=RValue
RValue_orig = RValue

;*** only accept presses: ***
if (event.press NE 1) then GOTO, END_OF_ARROW_EVENT


;*** determine if event happened in the top or bottom half of the draw-window:
sz=SIZE(State_Arrow.arrows)
d_x = sz(1)
d_y = sz(2)
midline = (FIX(d_y)/2 - 1) > 1
y_pos = event.y


;*** Decide whether to increment or multiply.     ***
;*** Only multiply if State_Arrow.Mult is larger than 1. ***
incr = State_Arrow.Incr
mult = State_Arrow.Mult
if (mult GT 1) then begin
  ;*** Do not do anything if user hit midline.        ***
  ;*** Increment value if user clicked above midline: ***
  if (y_pos GT midline) then RValue = RValue * mult
  ;*** Decrement value if user clicked above midline: ***
  if (y_pos LT midline) then RValue = RValue / mult
endif else begin
  ;*** Do not do anything if user hit midline.        ***
  ;*** Increment value if user clicked above midline: ***
  if (y_pos GT midline) then RValue = RValue + incr
  ;*** Decrement value if user clicked above midline: ***
  if (y_pos LT midline) then RValue = RValue - incr
endelse

;*** see if value is greater or less than max, min values; ***
if (STRTRIM(STRING(State_Arrow.Min_val),2) NE 'None') then begin
    CASE State.Type OF
   1:min_val=FLOAT(State_Arrow.Min_val)
   2:min_val=FIX(State_Arrow.Min_val)
   3:min_val=LONG(State_Arrow.Min_val)
    ENDCASE
    RValue = RValue > min_val
endif
if (STRTRIM(STRING(State_Arrow.Max_val),2) NE 'None') then begin
    CASE State.Type OF
   1:max_val=FLOAT(State_Arrow.Max_val)
   2:max_val=FIX(State_Arrow.Max_val)
   3:max_val=LONG(State_Arrow.Max_val)
    ENDCASE
    RValue = RValue < max_val
endif

;*** make sure RValue has the correct type: ***
CASE State.Type OF
   1:RValue=FLOAT(RValue)
   2:RValue=FIX(RValue)
   3:RValue=LONG(RValue)
ENDCASE

WIDGET_CONTROL, State.TextId, SET_VALUE=STRTRIM(STRING(RValue),2)
WIDGET_CONTROL, CW_id, SET_UVALUE=State, /NO_COPY

END_OF_ARROW_EVENT:

RValue = RValue[0]
;*** set update value: ***
update_val = (RValue_orig NE RValue)     ; 0=no change,1=different
update_val = update_val[0]

Ret   = {CW_FIELD_ARROWS_STRUCTURE, $
     ID: Event.Handler, $
     TOP: Event.Top,    $
     HANDLER: 0L,    $
     VALUE: RValue,   $
     TYPE: State_arrow.Type,  $
     UPDATE:update_val  $
   }

RETURN, Ret
END



;------------------------------------------------------------------
FUNCTION CW_FIELD_ARROWS_EVENT, event
prog_str='   (cw_field_arrows_event)'

;We only need to handle events from the draw-window.
;Otherwise (if the CW_FIELD value was changed by typing)
;just return the event.

widg_type = WIDGET_INFO(event.id, /TYPE)

CASE widg_type OF
 4:BEGIN ;*** event came from a draw-window ***
   ;*** replace the wid of the CW_FIELD (which is its own top base)
   ;*** with the wid of the CW_FIELD_ARROWS base, and return this
   ;*** structure to the top-level event-handler:
   ;*** the current "event" structure is not really an event,
   ;*** but rather the returned structure from CW_FIELD.
   parent_id = WIDGET_INFO(Event.id, /PARENT)
   Return_struct = {CW_FIELD_ARROWS_STRUCTURE, $
     ID: parent_id,  $
     TOP: Event.Top,    $
     HANDLER: Event.handler,   $
     VALUE: Event.value,     $
     TYPE: Event.Type,  $
     UPDATE: Event.update  $   ; 0=any,1=other
     }
   if (event.update EQ 1) then RETURN, Return_struct
   END

 ELSE:BEGIN
   ;*** check to see if the value is outside of boundaries: ***
   ;* find current value: *
   WIDGET_CONTROL, event.id, GET_VALUE=RValue

   ;* see if there are limits set: *
   DRAW_id = WIDGET_INFO(event.id, /SIBLING)
   WIDGET_CONTROL, DRAW_id, GET_UVALUE=State_Arrow
   CASE State_Arrow.Type OF
     1:RValue=FLOAT(RValue)
     2:RValue=FIX(RValue)
     3:RValue=LONG(RValue)
   ENDCASE
   if (STRTRIM(STRING(State_Arrow.Min_val),2) NE 'None') then begin
       CASE State_Arrow.Type OF
     1:min_val=FLOAT(State_Arrow.Min_val)
     2:min_val=FIX(State_Arrow.Min_val)
     3:min_val=LONG(State_Arrow.Min_val)
       ENDCASE
       RValue = RValue > min_val
   endif
   if (STRTRIM(STRING(State_Arrow.Max_val),2) NE 'None') then begin
       CASE State_Arrow.Type OF
     1:max_val=FLOAT(State_Arrow.Max_val)
     2:max_val=FIX(State_Arrow.Max_val)
     3:max_val=LONG(State_Arrow.Max_val)
       ENDCASE
       RValue = RValue < max_val
   endif
   event.value = Rvalue
   RValue=STRTRIM(STRING(RValue),2)
   WIDGET_CONTROL, event.id, SET_VALUE=RValue


   ;*** replace the wid of the CW_FIELD (which is its own top base)
   ;*** with the wid of the CW_FIELD_ARROWS base, and return this
   ;*** structure to the top-level event-handler:
   ;*** the current "event" structure is not really an event,
   ;*** but rather the returned structure from CW_FIELD.
   parent_id = WIDGET_INFO(Event.id, /PARENT)
   Return_struct = {CW_FIELD_ARROWS_STRUCTURE, $
     ID: parent_id,  $
     TOP: Event.Top,    $
     HANDLER: Event.handler,   $
     VALUE: Event.value,     $
     TYPE: Event.Type,  $
     UPDATE: Event.update  $   ; 0=any,1=other
     }
   RETURN, Return_struct
   END

ENDCASE

END


;====================================================================
FUNCTION CW_FIELD_ARROWS, Parent, COLUMN=Column, ROW=Row, $
   FLOATING=Float, INTEGER=Int, LONG=Long, $
   FONT=LabelFont, FRAME=Frame, TITLE=Title, UVALUE=UValue, VALUE=Value, $
   RETURN_EVENTS=ReturnEvents, ALL_EVENTS=AllUpdates, $
   FIELDFONT=FieldFont, NOEDIT=NoEdit, TEXT_FRAME=TextFrame, $
   XSIZE=XSize, UNAME = uname,   $
   MINIMUM=v_min, MAXIMUM=v_max, INCREMENT=increment, MULTIPLE=MULTIPLE

prog_str='   (cw_field_arrows)'


Column   = KEYWORD_SET(Column)
Row     = 1 - Column
AllEvents   = 1 - KEYWORD_SET(NoEdit)

;*** Enum Update { None, All, CRonly }
Update     = 0
IF KEYWORD_SET(AllUpdates) THEN Update = 1
IF KEYWORD_SET(ReturnEvents) THEN Update  = 2

IF N_ELEMENTS(UNAME) EQ 0 then UNAME = ''
IF KEYWORD_SET(FieldFont) EQ 0 THEN FieldFont=''
IF KEYWORD_SET(Frame) EQ 0 THEN Frame=0
IF KEYWORD_SET(LabelFont) EQ 0 THEN LabelFont=''
IF KEYWORD_SET(Title) EQ 0 THEN Title="Input Field:"
IF N_Elements(Value) EQ 0 THEN Value=FIX(1)
IF KEYWORD_SET(UValue) EQ 0 THEN UValue=0
IF KEYWORD_SET(XSize) EQ 0 THEN XSize=0
TextFrame   = KEYWORD_SET( TextFrame )

         Type = 2   ; INT is default
IF KEYWORD_SET(Float) THEN    Type = 1
IF KEYWORD_SET(Int) THEN   Type = 2
IF KEYWORD_SET(Long) THEN  Type   = 3
CASE Type OF
      1:BEGIN
   Float=1 & Int=0 & Long=0
   END
      2:BEGIN
   Float=0 & Int=1 & Long=0
   END
      3:BEGIN
   Float=0 & Int=0 & Long=1
   END
      ELSE:BEGIN
        print,'Warning- This compound widget can only use data types of:'
   print,'   INT, FLOAT, LONG.+prog_str
   RETURN, LONG(-1)
   END
ENDCASE

;*** convert min, max values (if any) to strings: ***
if KEYWORD_SET(v_min) then v_min_str=STRTRIM(v_min[0],2) else v_min_str='None'
if KEYWORD_SET(v_max) then v_max_str=STRTRIM(v_max[0],2) else v_max_str='None'
;* if min is same as max, inactivate the min and max limits: *
if (v_min_str EQ v_max_str) then begin
  v_min_str='None'
  v_max_str='None'
endif

CASE Type OF
     1:if KEYWORD_SET(INCREMENT) then v_incr=FLOAT(increment) else v_incr=FLOAT(1.0)
     2:if KEYWORD_SET(INCREMENT) then v_incr=FIX(increment)   else v_incr=FIX(1)
     3:if KEYWORD_SET(INCREMENT) then v_incr=LONG(increment)  else v_incr=LONG(1)
ENDCASE

;*** Only allow use of MULTIPLE if INCREMENT is not set,  ***
;*** otherwise v_mult is 1.0 and will be ignored later on.***
if KEYWORD_SET(INCREMENT) then begin
   v_mult=v_incr/v_incr    ;1, with correct type.
endif else begin
  v_mult=v_incr/v_incr   ;1, with correct type
  if KEYWORD_SET(MULTIPLE) then begin
    CASE Type OF
   1:v_mult=FLOAT(MULTIPLE) > 1.0
   2:v_mult=FIX(MULTIPLE) > 1
   3:v_mult=LONG(MULTIPLE) > 1L
    ENDCASE
  endif
endelse



;*** Make Widget: ***
tlb = WIDGET_BASE(Parent, ROW=Row, COLUMN=Column, UVALUE=UValue, $
      EVENT_FUNC='CW_FIELD_ARROWS_EVENT', $
      PRO_SET_VALUE='CW_FIELD_ARROWS_SET', $
      FUNC_GET_VALUE='CW_FIELD_ARROWS_GET', $
      /BASE_ALIGN_CENTER, $
      FRAME=Frame , UNAME = uname)

   Field_id = CW_FIELD(tlb, COLUMN=Column, ROW=Row, $
   FLOATING=Float, INTEGER=Int, LONG=Long, $
   FONT=LabelFont, FRAME=Frame, TITLE=Title, UVALUE=UValue, VALUE=Value, $
   RETURN_EVENTS=ReturnEvents, ALL_EVENTS=AllUpdates, $
   XSIZE=XSize, $
   FIELDFONT=FieldFont, NOEDIT=NoEdit, TEXT_FRAME=TextFrame)

    ;**** Make the arrow array and draw-window to display it: ***
    arrows = [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], $
     [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0], $
     [0,0,0,0,0,0,1,1,1,0,0,0,0,0,0], $
     [0,0,0,0,0,1,1,0,1,1,0,0,0,0,0], $
     [0,0,0,0,1,1,0,0,0,1,1,0,0,0,0], $
     [0,0,0,1,1,0,0,0,0,0,1,1,0,0,0], $
     [0,0,0,1,0,0,0,0,0,0,0,1,0,0,0], $
     [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], $
     [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], $
     [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], $
     [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], $
     [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], $
     [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], $
     [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], $
     [0,0,0,1,0,0,0,0,0,0,0,1,0,0,0], $
     [0,0,0,1,1,0,0,0,0,0,1,1,0,0,0], $
     [0,0,0,0,1,1,0,0,0,1,1,0,0,0,0], $
     [0,0,0,0,0,1,1,0,1,1,0,0,0,0,0], $
     [0,0,0,0,0,0,1,1,1,0,0,0,0,0,0], $
     [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0], $
     [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]
    arrows=BYTSCL(arrows)
    sz=SIZE(arrows)
    d_x = sz(1)
    d_y = sz(2)
    ;*** save arrow-related info in user state: ***
    State_Arrow={ $
   Field_id:Field_id,  $
   Type:Type,    $
   Incr:v_incr,     $
   Mult:v_mult,     $
   Min_val:v_min_str,  $
   Max_val:v_max_str,  $
   arrows:arrows    $
   }

    Draw_UpDown = WIDGET_DRAW(tlb, /BUTTON_EVENTS, $
      XSIZE=d_x, YSIZE=d_y, $
      NOTIFY_REALIZE='CW_FIELD_ARROWS_FILL_DRAW', $
      UVALUE=State_Arrow, $
      RETAIN=2, $
      EVENT_FUNC='CW_FIELD_ARROWS_UPDOWN_EVENT')

    ;print, Parent, tlb, Field_id, Draw_UpDown

    ;** Do NOT save anything in Uvalue of Base, since this is
    ;** how the calling routine will look for the widget!
    RETURN, tlb
END
