; Copyright (c) 2007  Thomas Döring 6/1/2007   All rights reserved
;+
;NAME:
;	CW_RSLIDER
;
;LAST CHANGED: ----------------------------------- 	25-Dec-07
;
; PURPOSE:
;	This procedure and associated functions/procedures is a compound widget that generates a
; colored slider which defines a value range, with independent control
; of lower bound (left click & drag), upper bound (right click & drag)
; or position (left&right (or middle) click & drag)
;
; CATEGORY:
;	Utility
;   used in image~generate mask and stacks~RGB composite map
;
; CALLING SEQUENCE:
;  Result = CW_RSLIDER, base, uvalue = uvalue, uname = uname, color = color, bg_color = bg_color,  $
;    maximum = maximum, minimum = minimum, xsize = xsize, ysize =  ysize, $
;    xoffset = xoffset, yoffset = yoffset, value = value, reverse = reverse, $
;    vertical = vertical, event_pro = event_pro
;
; INPUTS:
; 	BASE    ID of parent widget (compound widgets must be contained in another widget)
;
; KEYWORDS:
;	uvalue
;	uname
;	color		color of slider as a 3-value byte array  e.g. [255,0,0] = red
; 	bg_color 	color of backgound of slider as a 3-value byte array
;	maximum 	maximum limit of slider (double)
;	minimum 	minimum limit of slider (double)
;	xsize		x-dimension in pixels
;	ysize		y-dimension in pixels
;	xoffset		x-offset relative to base
;	yoffset		y-offset relative to base
;	value		value of range of slider (2-value double array)
;	reverse		reverse clicks for lower / upper or left / right limits
;	vertical	orient slider vertically
;	event_pro
;
; OUTPUTS:
;	A slider is created in the base
;
; COMMON BLOCKS:
;	AXIS_COM	standard set of common blocks
;
; EXAMPLE:
;   see TEST_CW_RSLIDER.PRO for illustration of how this is used
;
;  RS_min = double(r_min - 0.05*(r_max-r_min))
;  RS_max = double(r_max + 0.05*(r_max-r_min))
;  Rvalue = [double(r_min), double(r_max)]
;  RS_base = WIDGET_BASE(RBG_BASE, UNAME='WID_BASE_R', TITLE="Red", XPAD = 5, YPAD = 5, $
;           XOFFSET=40, YOFFSET=40, SCR_YSIZE=200,SCR_XSIZE=70)
;  sliderR = CW_RSLIDER(RS_BASE, UNAME='SLIDER_R', color=[255,0,0], /vertical, $
;            XSIZE=20, YSIZE = 180, MINIMUM=RS_min, MAXIMUM=RS_max, VALUE=Rvalue )
;
; MODIFICATION HISTORY:
; (27-Dec-07 aph) AXIS standard header added
; (28-Dec-07 aph^2) min/max adjustment added
;-


PRO CW_RSLIDER_KILL_NOTIFY, id

    WIDGET_CONTROL, ID, GET_UVALUE=uvalue

    OBJ_DESTROY,uvalue.obj

END

PRO CW_RSLIDER_NOTIFY, id
    WIDGET_CONTROL, ID, GET_UVALUE=uvalue

    uvalue.obj->drawImage
END

FUNCTION CW_RSLIDER_GET_VALUE, id

    sliderID = WIDGET_INFO(id,/CHILD)

    WIDGET_CONTROL, sliderID, GET_UVALUE=uvalue

    values = uvalue.obj->getProperty(/VALUE)

    RETURN, values

END

PRO CW_RSLIDER_SET_VALUE, id, value

    sliderID = WIDGET_INFO(id,/CHILD)

    WIDGET_CONTROL, sliderID, GET_UVALUE=uvalue

    uvalue.obj->setProperty,DOUBLE(value), /VALUE
    uvalue.obj->updateImage
    uvalue.obj->drawImage

END


PRO CW_RSLIDER_SET_MINMAX, id, minmax

    sliderID = WIDGET_INFO(id,/CHILD)

    WIDGET_CONTROL, sliderID, GET_UVALUE=uvalue


    uvalue.obj->setProperty,DOUBLE(minmax), /MINMAX
    uvalue.obj->updateImage
    uvalue.obj->drawImage

END

PRO CW_RSLIDER::CLEANUP
    PTR_FREE,self.uvalue
    PTR_FREE,self.image
END

PRO CW_RSLIDER::updateImage

    SysColors = WIDGET_INFO(self.tlb, /SYSTEM_COLORS)

    (*(self.image)) *= 0B

    value = self.value-self.minimum

    FOR i = 0, 2 DO (*(self.image))[i,*,*] += self.bg_color[i]

    factor = DOUBLE((self.vertical ? self.ysize:self.xsize)-4)/ABS(self.maximum)

    IF ~self.vertical THEN BEGIN
        IF ~self.reverse THEN $
            (*(self.image))[*,2+FIX(factor*value[0]):2+FIX(factor*value[1])+1, *] *= 0B $
        ELSE $
            (*(self.image))[*,self.xsize-(2+FIX(factor*value[1])+1):self.xsize-(2+FIX(factor*value[0])), *] *= 0B
    ENDIF ELSE BEGIN
        IF self.reverse THEN $
            (*(self.image))[*, *,self.ysize-(2+FIX(factor*value[1])+1):self.ysize-(2+FIX(factor*value[0]))] *= 0B $
        ELSE $
            (*(self.image))[*, *,2+FIX(factor*value[0]):2+FIX(factor*value[1])+1] *= 0B
    ENDELSE

    IF ~self.vertical THEN BEGIN $
        IF ~self.reverse THEN $
            FOR i = 0, 2 DO (*(self.image))[i, 2+FIX(factor*value[0]):2+FIX(factor*value[1])+1, *] += self.color[i]   $
        ELSE $
            FOR i = 0, 2 DO (*(self.image))[i,self.xsize-(2+FIX(factor*value[1])+1):self.xsize-(2+FIX(factor*value[0])), *] += self.color[i]
    ENDIF ELSE BEGIN
        IF self.reverse THEN $
            FOR i = 0, 2 DO (*(self.image))[i, *,self.ysize-(2+FIX(factor*value[1])+1):self.ysize-(2+FIX(factor*value[0]))] += self.color[i] $
        ELSE $
            FOR i = 0, 2 DO (*(self.image))[i, *,2+FIX(factor*value[0]):2+FIX(factor*value[1])+1] += self.color[i]
    ENDELSE

    FOR i = 0, 2 DO BEGIN
        (*(self.image))[i,*,self.ysize-2] = SysColors.Dark_Shadow_3D[i]
        (*(self.image))[i,1,*] = SysColors.Dark_Shadow_3D[i]

        (*(self.image))[i,self.xsize-2,*] = SysColors.Light_3D[i]
        (*(self.image))[i,*,1] = SysColors.Light_3D[i]

        (*(self.image))[i,*,self.ysize-1] = SysColors.Shadow_3D[i]
        (*(self.image))[i,0,*] = SysColors.Shadow_3D[i]
        (*(self.image))[i,*,0] = SysColors.Light_Edge_3D[i]
        (*(self.image))[i,self.xsize-1,*] = SysColors.Light_Edge_3D[i]
    ENDFOR
END

PRO CW_RSLIDER::drawImage

    WIDGET_CONTROL, self.win_id, GET_VALUE=wID
    WSET, wID

    TV, (*(self.image)), TRUE=1
END

FUNCTION CW_RSLIDER::drawWidget, obj

    base = WIDGET_BASE( self.base, $
                 PRO_SET_VALUE = "CW_RSLIDER_SET_VALUE", $
                 FUNC_GET_VALUE = "CW_RSLIDER_GET_VALUE", $
                 XPAD = 0, $
                 YPAD = 0, $
                 uname = self.uname, $
                 uvalue = self.uvalue, $
                 scr_xsize = self.xsize, $
                 scr_ysize = self.ysize, $
                 xoffset = self.xoffset, $
                 yoffset = self.yoffset, $
                 EVENT_PRO = "CW_RSLIDER_EVENT" )


    tlb = WIDGET_DRAW( base, $
                 scr_xsize = self.xsize, $
                 scr_ysize = self.ysize, $
                 notify_realize = "CW_RSLIDER_NOTIFY", $
                 kill_notify = 'CW_RSLIDER_KILL_NOTIFY', $
                 /MOTION_EVENTS, $
                 /BUTTON_EVENTS, $
                 /WHEEL_EVENTS)

    self.tlb = base
    self.win_id = tlb

    WIDGET_CONTROL, tlb, SET_UVALUE={obj: obj, RSlider: tlb}

    image = BYTARR(3,self.xsize,self.ysize)
    self.image = PTR_NEW(image)

    self->updateImage

    RETURN,base
END

FUNCTION CW_RSLIDER::init,  base,                   $
                            uvalue = uvalue,        $
                            uname = uname,          $
                            color = color,          $
                            bg_color = bg_color,    $
                            maximum = maximum,      $
                            minimum = minimum,      $
                            xsize = xsize,          $
                            ysize =  ysize,         $
                            xoffset = xoffset,      $
                            yoffset = yoffset,      $
                            value = value,          $
                            reverse = reverse,      $
                            vertical = vertical,    $
                            event_pro = event_pro


    IF N_PARAMS() EQ 0 THEN RETURN, 0B

    self.uvalue =   (N_ELEMENTS(uvalue) EQ 0)       ? PTR_NEW("")       : PTR_NEW(uvalue)
    self.uname =    (N_ELEMENTS(uname) EQ 0)        ? ""                : uname
    self.color =    (N_ELEMENTS(color) NE 3)        ? [192B, 192B, 192B]: color
    self.bg_color = (N_ELEMENTS(bg_color) NE 3)     ? [255B, 255B, 255B]: bg_color
    self.minimum =  (N_ELEMENTS(minimum) EQ 0)      ? 0d                : DOUBLE(minimum)
    self.maximum =  (N_ELEMENTS(maximum) EQ 0)      ? 100d              : DOUBLE(maximum-self.minimum)

    self.xoffset =  (N_ELEMENTS(xoffset) EQ 0)      ? 0L                : xoffset
    self.yoffset =  (N_ELEMENTS(yoffset) EQ 0)      ? 0L                : yoffset
    self.value =    (N_ELEMENTS(value) NE 2)        ? [25D, 75D]        : DOUBLE(value)
    self.event_pro =(N_ELEMENTS(event_pro) EQ 0)    ? ""                : event_pro

    self.reverse = KEYWORD_SET(REVERSE)
    self.vertical = KEYWORD_SET(VERTICAL)

    IF (self.vertical) THEN BEGIN
        self.ysize =    (N_ELEMENTS(ysize) EQ 0)        ? 100               : ysize
        self.xsize =    (N_ELEMENTS(xsize) EQ 0)        ? 15                : xsize
    ENDIF ELSE BEGIN
        self.xsize =    (N_ELEMENTS(xsize) EQ 0)        ? 100               : xsize
        self.ysize =    (N_ELEMENTS(ysize) EQ 0)        ? 15                : ysize
    ENDELSE

    self.base = base
    self.scroll = 0.2

    RETURN, 1B
END

PRO CW_RSLIDER__define

    void = {   CW_RSLIDER,               $
              uname:     '',           $
              color: BYTARR(3),       $
              bg_color: BYTARR(3),     $
              minimum: 0D,          $
              maximum: 0D,          $
              base:  0L,           $
              xsize:     0L,              $
              ysize:     0L,              $
              xoffset:0L,              $
              yoffset:0L,              $
              uvalue: PTR_NEW(),        $
              value:     DBLARR(2),        $
              event_pro: "",          $
              win_id: 0L,              $
              image: PTR_NEW(),       $
              mouse: [0B,0B],          $
              reverse: 0B,       $
              vertical: 0B,        $
              scroll: 0d, $
              tlb:   0L             $
         }

END


FUNCTION CW_RSLIDER::getProperty,   LEFT_BUTTON = left_button, $
                        RIGHT_BUTTON = right_button, $
                        BUTTONS = buttons, $
                        EVENT_PRO  = event_pro, $
                        MAXIMUM = maximum, $
                        MINIMUM = minimum, $
                        XSIZE = xsize, $
                        YSIZE = ysize, $
                        VERTICAL = vertical, $
                        REVERSE = reverse, $
                        SCROLL = scroll, $
                        VALUE = value

    IF keyword_set(LEFT_BUTTON) THEN RETURN,self.mouse[0]
    IF keyword_set(RIGHT_BUTTON) THEN RETURN,self.mouse[1]
    IF keyword_set(BUTTONS) THEN RETURN,self.mouse

    IF keyword_set(EVENT_PRO) THEN RETURN,self.event_pro

    IF keyword_set(MAXIMUM) THEN RETURN,self.maximum+self.minimum
    IF keyword_set(MINIMUM) THEN RETURN,self.minimum
    IF keyword_set(XSIZE) THEN RETURN,self.xsize
    IF keyword_set(YSIZE) THEN RETURN,self.ysize

    IF keyword_set(VERTICAL) THEN RETURN,self.vertical
    IF keyword_set(REVERSE) THEN RETURN,self.reverse

    IF keyword_set(SCROLL) THEN RETURN,self.scroll

    IF keyword_set(VALUE) THEN RETURN,self.value

    RETURN, 0B

END

PRO CW_RSLIDER::setProperty, values, LEFT_BUTTON = left_button, $
                        RIGHT_BUTTON = right_button, $
                        BUTTONS = buttons, $
                        SCROLL = scroll, $
                        MINMAX = MINMAX, $
                        VALUE = value

    IF keyword_set(LEFT_BUTTON) THEN self.mouse[0] = values
    IF keyword_set(RIGHT_BUTTON) THEN self.mouse[1] = values
    IF keyword_set(BUTTONS) THEN self.mouse = values

    IF keyword_set(SCROLL) THEN self.scroll = scroll

	IF keyword_set(MINMAX) THEN BEGIN
		self.minimum = values(0)
		self.maximum = values(1)-values(0)
		delta = 1e-5*self.maximum
		if self.value(0) LE self.minimum then self.value(0) = self.minimum+delta
		if self.value(1) GE  values(1) then self.value(1) =  values(1)-delta
	ENDIF

    IF keyword_set(VALUE) THEN self.value = values

END


PRO CW_RSLIDER_EVENT, event

    WIDGET_CONTROL, event.ID, GET_UVALUE=uvalue

    IF (event.clicks) THEN BEGIN
       IF (event.press EQ 1) THEN uvalue.obj->setProperty, 1B, /LEFT_BUTTON
       IF (event.release EQ 1) THEN uvalue.obj->setProperty, 0B, /LEFT_BUTTON

       IF (event.press EQ 4) THEN uvalue.obj->setProperty, 1B, /RIGHT_BUTTON
       IF (event.release EQ 4) THEN uvalue.obj->setProperty, 0B, /RIGHT_BUTTON

       IF (event.press EQ 2) THEN uvalue.obj->setProperty, [1B,1B], /BUTTONS
       IF (event.release EQ 2) THEN uvalue.obj->setProperty, [0B,0B], /BUTTONS

        IF (event.type EQ 7) THEN BEGIN
            baseID = WIDGET_INFO(event.ID,/PARENT)
            WIDGET_CONTROL, baseID, GET_VALUE=old_value
            WIDGET_CONTROL, baseID, GET_VALUE=old_value
            old_value += event.clicks * [1d, -1d] * (old_value[1] - old_value[0]) * uvalue.obj->getProperty(/SCROLL)

            mx = uvalue.obj->getProperty(/MAXIMUM)
            mn = uvalue.obj->getProperty(/MINIMUM)

            old_value[0] = ((old_value[0] > mn) < mx) NE old_value[0] ? mn : old_value[0]
            old_value[1] = ((old_value[1] > mn) < mx) NE old_value[1] ? mx : old_value[1]

            uvalue.obj->setProperty, old_value, /VALUE
            uvalue.obj->updateImage
            uvalue.obj->drawImage

            event_pro = uvalue.obj->getProperty(/EVENT_PRO)

            IF event_pro EQ "" THEN event_pro = WIDGET_INFO(event.top, /EVENT_PRO)

            CALL_PROCEDURE, event_pro, {WIDGET, ID: baseID, TOP:event.TOP, HANDLER: event.HANDLER, VALUES: old_value}
        ENDIF

    ENDIF ELSE BEGIN

       mouseButtons = uvalue.obj->getProperty(/BUTTONS)

       IF ~ARRAY_EQUAL(mouseButtons,[0B, 0B]) THEN BEGIN
         xs = (uvalue.obj->getProperty(/VERTICAL) ? (uvalue.obj->getProperty(/YSIZE)) : (uvalue.obj->getProperty(/XSIZE))) - 4
         mx = uvalue.obj->getProperty(/MAXIMUM)
         mn = uvalue.obj->getProperty(/MINIMUM)
         mx -= mn
         diff = mx

         mv = uvalue.obj->getProperty(/VERTICAL) ? event.y : event.x
         mv = uvalue.obj->getProperty(/REVERSE) ? xs-mv : mv

         IF (mv GT 1 AND mv LT xs+2) THEN BEGIN

          baseID = WIDGET_INFO(event.ID,/PARENT)
          WIDGET_CONTROL, baseID, GET_VALUE=old_value
          old_value -= mn
;
          value = DOUBLE((mv - 2d)/xs)*diff

          IF ARRAY_EQUAL(mouseButtons,[1B, 0B]) THEN old_value[0] = value LE old_value[1] ? value:old_value[0]
          IF ARRAY_EQUAL(mouseButtons,[0B, 1B]) THEN old_value[1] = value GE old_value[0] ? value:old_value[1]
          IF ARRAY_EQUAL(mouseButtons,[1B, 1B]) THEN BEGIN
              mid = TOTAL(old_value)/2d

              IF (value-(old_value[1]-old_value[0])/2d GE 0 AND $
                 value+(old_value[1]-old_value[0])/2d LT mx) THEN $
                   old_value += value-mid
          ENDIF

          uvalue.obj->setProperty, old_value+mn, /VALUE

          uvalue.obj->updateImage
          uvalue.obj->drawImage

         event_pro = uvalue.obj->getProperty(/EVENT_PRO)

         IF event_pro EQ "" THEN event_pro = WIDGET_INFO(event.top, /EVENT_PRO)

         CALL_PROCEDURE, event_pro, {WIDGET, ID: baseID, TOP:event.TOP, HANDLER: event.HANDLER, VALUES: old_value+mn}

         ENDIF

       ENDIF

    ENDELSE

END


FUNCTION CW_RSLIDER, base,                $
              _EXTRA = extra

    obj = OBJ_NEW('CW_RSLIDER', base, _EXTRA = extra)
    ret = obj->drawWidget(obj)
    RETURN, ret

END