Source-code for CW_FIELD_ARROWS.

Note that some of the formatting may be messed up because my web-page creation program does not recognize "tabs", so the indentaion is sporadic and comments are difficult to read.

Subroutines:

CW_FIELD_ARROWS GUI Creator.
CW_FIELD_ARROWS_EVENT Main event-handler for GUI.

CW_FIELD_ARROWS_FILL_DRAW NOTIFY_REALIZE program for draw-window.

CW_FIELD_ARROWS_GET Get the value of a CW_FIELD_ARROWS
CW_FIELD_ARROWS_SET Set the value of a CW_FIELD_ARROWS

CW_FIELD_ARROWS_UPDOWN_EVENT Event-handler for arrows.

;CW_FIELD_ARROWS.PRO
;
;Purpose:
;Wrapper for IDL's CW_FIELD, to add up/down arrows to
;increment/decrement the value. This is a more compact
;version of a slider.
; Abstract:
;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 below 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:
;if (event.update NE 1) then RETURN
;
;KEYWORDS:(in addition to the standard CW_FIELD ones):
;INCREMENTSet 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.
;MULTIPLESet 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 MULTIPLE.
;MINIMUMMinimum value allowed.
;MAXIMUMMaximum value allowed.
;
;------------------------------------------------------------------
;--- CW_FIELD_ARROWS_FILL_DRAW
;--- NOTIFY_REALIZE program for draw-window.
;--- Draws the arrow design in the window.
;--- Keywords:
;---FROM_PARENTUse 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
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=any,1=other
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
}

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, $
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 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) THENType= 2
IF KEYWORD_SET(Long) THENType= 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
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'

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 )

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, $
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