Viewing contents of file '../idllib/contrib/fanning/icon_ex.pro'
;+
; FUNCTION NAME:
;
;   ICON_BUTTON
;
; PURPOSE:
;
;   This is a compound widget to put up a small button with an image on it.
;   It is implemented as a draw widget, but acts like a button. The event
;   structure that is returned is a WIDGET_BUTTON event structure for a
;   SELECT event.
;
; MAJOR TOPICS:
;
;   Widget Programming
;
; FUNCTION:
;
;   Used almost identically to WIDGET_BUTTON.
;
; CALLING SEQUENCE:
;
;    buttonID = ICON_BUTTON(parent, Value='icon_button', Image=image)
;
; POSITIONAL PARAMETERS:
;
;    parent -- The identifier of the parent widget.
;
; KEYWORD PARAMETERS:
;
;    BOTTOM -- The starting color index of the image colors.
;
;    COLORINDEX -- A four-element array that gives the color indicies for the button
;       colors. COLORINDEX(0) is the button outline color. COLORINDEX(1) is the dark
;       edge color. COLORINDEX(2) is the light edge color.
;       COLORINDEX(3) is the button highlight color.
;
;    EVENT_FUNC -- The name of an event handler function for this button.
;
;    EVENT_PRO -- The name of an event handler procedure for this button.
;
;    IMAGE -- The 2D array to use as the icon display.
;
;    NCOLORS -- The number of colors to use for the image display.
;
;    VALUE -- The value of the icon. (Used to make it look like a button.)
;
;    UVALUE -- A user value. Used in the normal way.
;
;    XOFFSET -- The X offset of the icon on the display.
;
;    YOFFSET -- The Y offset of the icon on the display.
;
;    XSIZE -- This is the XSIZE of the icon.
;
;    YSIZE -- This is the YSIZE of the icon.
;
; EXAMPLE:
;
;  To make an icon button out of the CTSCAN.DAT image, do this:
;
;    Load colors for the icon button's boundaries.
;
;       TVLCT, [255, 70, 150, 255], [255, 70, 150, 255], $
;          [255, 70, 150, 255], 200
;
;    Read image data.
;
;       ctscan = GetImage()
;
;    Build widgets.
;
;       base = Widget_Base()
;       icon = Icon_Button(base, ColorIndex=Indgen(4)+200, $
;           Value='icon_button', Image=ctscan, NColors=200, Bottom=0)
;       Widget_Control, base, /Realize
;
; MAJOR FUNCTIONS and PROCEDURES:
;
;   None.
;
; EVENT STRUCTURE:
;
;   event = {WIDGET_BUTTON, ID:0L, TOP:0L, HANDLER:0L, SELECT:1}
;
; MODIFICATION HISTORY:
;
;   Written by: David Fanning, August 1996.
;   Given to attendees of IDL training courses.
;-



PRO XColors_Set, info

TVLCT, r, g, b, /Get
r(info.bottom:info.currentbottom) = info.bottomcolor(0)
g(info.bottom:info.currentbottom) = info.bottomcolor(1)
b(info.bottom:info.currentbottom) = info.bottomcolor(2)
r(info.currenttop:info.top) = info.topcolor(0)
g(info.currenttop:info.top) = info.topcolor(1)
b(info.currenttop:info.top) = info.topcolor(2)

red = info.r
green = info.g
blue = info.b
number = (info.currenttop-info.currentbottom) + 1

gamma = info.gamma

index = Findgen(info.ncolors)
distribution = index^gamma
index = Round(distribution * (info.ncolors-1) / Max(distribution))

IF info.reverse EQ 0 THEN BEGIN
   r(info.currentbottom:info.currenttop) = Congrid(red(index), number)
   g(info.currentbottom:info.currenttop) = Congrid(green(index), number)
   b(info.currentbottom:info.currenttop) = Congrid(blue(index), number )
ENDIF ELSE BEGIN
   r(info.currentbottom:info.currenttop) = $
      Reverse(Congrid(red(index), number))
   g(info.currentbottom:info.currenttop) = $
      Reverse(Congrid(green(index), number))
   b(info.currentbottom:info.currenttop) = $
      Reverse(Congrid(blue(index), number))
ENDELSE

TVLct, r, g, b
WSet, info.windowindex
TV, info.colorimage

   ; Are there widgets to notify?

s = SIZE(info.notifyID)
IF s(0) EQ 1 THEN count = 0 ELSE count = s(2)-1
FOR j=0,count DO BEGIN
   colorEvent = { XCOLORS_LOAD, $
                  ID:info.notifyID(0,j), $
                  TOP:info.notifyID(1,j), $
                  HANDLER:0L, $
                  R:r, $
                  G:g, $
                  B:b }
   IF Widget_Info(info.notifyID(0,j), /Valid_ID) THEN $
      Widget_Control, info.notifyID(0,j), Send_Event=colorEvent
ENDFOR

END ; ***************************************************************



PRO XCOLORS_GAMMA_SLIDER, event

   ; Get the info structure from storage location.

Widget_Control, event.top, Get_UValue=info, /No_Copy

   ; Get the gamma value from the slider.

Widget_Control, event.id, Get_Value=gamma
gamma = 10^((gamma/50.0) - 1)
info.gamma = gamma

   ; Update the gamma label.

Widget_Control, info.gammaID, Set_Value=String(gamma, Format='(F6.3)')

   ; Make a pseudo structure.

IF info.currentBottom GT info.currentTop THEN $
   pseudo = {currenttop:info.currentbottom, currentbottom:info.currenttop, $
      reverse:1, bottomcolor:info.topcolor, topcolor:info.bottomcolor, $
      gamma:info.gamma, top:info.top, bottom:info.bottom, $
      ncolors:info.ncolors, r:info.r, g:info.g, b:info.b, $
      notifyID:info.notifyID, colorimage:info.colorimage, $
      windowindex:info.windowindex} $
ELSE $
   pseudo = {currenttop:info.currenttop, currentbottom:info.currentbottom, $
      reverse:0, bottomcolor:info.bottomcolor, topcolor:info.topcolor, $
      gamma:info.gamma, top:info.top, bottom:info.bottom, $
      ncolors:info.ncolors, r:info.r, g:info.g, b:info.b, $
      notifyID:info.notifyID, colorimage:info.colorimage, $
      windowindex:info.windowindex}

   ; Load the colors.

XColors_Set, pseudo

   ; Put the info structure back in storage location.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END ; ************************************************************************



PRO XCOLORS_COLORTABLE, event

   ; Get the info structure from storage location.

Widget_Control, event.top, Get_UValue=info, /No_Copy

LoadCt, event.index, File=info.file, /Silent, $
   NColors=info.ncolors, Bottom=info.bottom

TVLct, r, g, b, /Get
info.r = r(info.bottom:info.top)
info.g = g(info.bottom:info.top)
info.b = b(info.bottom:info.top)
info.topcolor = [r(info.top), g(info.top), b(info.top)]
info.bottomcolor = [r(info.bottom), g(info.bottom), b(info.bottom)]

   ; Update the slider positions and values.

Widget_Control, info.botSlider, Set_Value=0
Widget_Control, info.topSlider, Set_Value=info.ncolors-1
Widget_Control, info.gammaSlider, Set_Value=50
Widget_Control, info.gammaID, Set_Value=String(1.0, Format='(F6.3)')
info.currentBottom = info.bottom
info.currentTop = info.top
info.gamma = 1.0

   ; Create a pseudo structure.

pseudo = {currenttop:info.currenttop, currentbottom:info.currentbottom, $
reverse:info.reverse, windowindex:info.windowindex, $
   bottomcolor:info.bottomcolor, topcolor:info.topcolor, gamma:info.gamma, $
   top:info.top, bottom:info.bottom, ncolors:info.ncolors, r:info.r, $
   g:info.g, b:info.b, notifyID:info.notifyID, colorimage:info.colorimage}

   ; Update the colors.

XColors_Set, pseudo

   ; Put the info structure back in storage location.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END ; ************************************************************************



PRO XCOLORS_BOTTOM_SLIDER, event

   ; Get the info structure from storage location.

Widget_Control, event.top, Get_UValue=info, /No_Copy

   ; Update the current bottom value of the slider.

info.currentBottom = info.bottom + event.value

   ; Error handling. Is currentBottom = currentTop?

IF info.currentBottom EQ info.currentTop THEN BEGIN
   info.currentBottom = info.currentTop
   Widget_Control, info.botSlider, Set_Value=(info.currentBottom-info.bottom)
ENDIF

   ; Error handling. Is currentBottom > currentTop?

IF info.currentBottom GT info.currentTop THEN BEGIN

   bottom = info.currentTop
   top = info.currentBottom
   bottomcolor = info.topColor
   topcolor = info.bottomColor
   reverse = 1

ENDIF ELSE BEGIN

   bottom = info.currentBottom
   top = info.currentTop
   bottomcolor = info.bottomColor
   topcolor = info.topColor
   reverse = 0

ENDELSE

   ; Create a pseudo structure.

pseudo = {currenttop:top, currentbottom:bottom, reverse:reverse, $
   bottomcolor:bottomcolor, topcolor:topcolor, gamma:info.gamma, $
   top:info.top, bottom:info.bottom, ncolors:info.ncolors, r:info.r, $
   g:info.g, b:info.b, notifyID:info.notifyID, colorimage:info.colorimage, $
   windowindex:info.windowindex}

   ; Update the colors.

XColors_Set, pseudo

   ; Put the info structure back in storage location.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END ; ************************************************************************




PRO XCOLORS_PROTECT_COLORS, event

   ; Get the info structure from storage location.

IF event.enter NE 1 THEN RETURN

Widget_Control, event.top, Get_UValue=info, /No_Copy

   ; Create a pseudo structure.

pseudo = {currenttop:info.currenttop, currentbottom:info.currentbottom, $
   reverse:info.reverse, $
   bottomcolor:info.bottomcolor, topcolor:info.topcolor, gamma:info.gamma, $
   top:info.top, bottom:info.bottom, ncolors:info.ncolors, r:info.r, $
   g:info.g, b:info.b, notifyID:info.notifyID, colorimage:info.colorimage, $
   windowindex:info.windowindex}

   ; Update the colors.

XColors_Set, pseudo

   ; Put the info structure back in storage location.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END ; ************************************************************************



PRO XCOLORS_TOP_SLIDER, event

   ; Get the info structure from storage location.

Widget_Control, event.top, Get_UValue=info, /No_Copy

   ; Update the current top value of the slider.

info.currentTop = info.bottom + event.value

   ; Error handling. Is currentBottom = currentTop?

IF info.currentBottom EQ info.currentTop THEN BEGIN
   info.currentBottom = (info.currentTop - 1) > 0
   thisValue = (info.currentBottom-info.bottom)
   IF thisValue LT 0 THEN BEGIN
      thisValue = 0
      info.currentBottom = info.bottom
   ENDIF
   Widget_Control, info.botSlider, Set_Value=thisValue
ENDIF

   ; Error handling. Is currentBottom > currentTop?

IF info.currentBottom GT info.currentTop THEN BEGIN

   bottom = info.currentTop
   top = info.currentBottom
   bottomcolor = info.topColor
   topcolor = info.bottomColor
   reverse = 1

ENDIF ELSE BEGIN

   bottom = info.currentBottom
   top = info.currentTop
   bottomcolor = info.bottomColor
   topcolor = info.topColor
   reverse = 0

ENDELSE

   ; Create a pseudo structure.

pseudo = {currenttop:top, currentbottom:bottom, reverse:reverse, $
   bottomcolor:bottomcolor, topcolor:topcolor, gamma:info.gamma, $
   top:info.top, bottom:info.bottom, ncolors:info.ncolors, r:info.r, $
   g:info.g, b:info.b, notifyID:info.notifyID, colorimage:info.colorimage, $
   windowindex:info.windowindex}

   ; Update the colors.

XColors_Set, pseudo

   ; Put the info structure back in storage location.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END ; ************************************************************************



PRO XCOLORS_CANCEL, event
Widget_Control, event.top, Get_UValue=info, /No_Copy

   ; Create a pseudo structure.

pseudo = {currenttop:info.currenttop, currentbottom:info.currentbottom, $
reverse:info.reverse, windowindex:info.windowindex, $
   bottomcolor:info.bottomcolor, topcolor:info.topcolor, gamma:info.gamma, $
   top:info.top, bottom:info.bottom, ncolors:info.ncolors, r:info.rstart, $
   g:info.gstart, b:info.bstart, notifyID:info.notifyID, colorimage:info.colorimage}

   ; Update the colors.

XColors_Set, pseudo
Widget_Control, event.top, /Destroy
END ; ************************************************************************



PRO XCOLORS_DISMISS, event
Widget_Control, event.top, /Destroy
END ; ************************************************************************



PRO XCOLORS, NColors=ncolors, Bottom=bottom, Title=title, File=file, $
   Group=group, XOffset=xoffset, YOffset=yoffset, Just_Reg=jregister, $
   NotifyID=notifyID

   ; This is a procedure to load color tables into a
   ; restricted color range of the physical color table.
   ; It is a highly simplified version of XLoadCT.

On_Error, 1

   ; Make sure colors are initiated.

thisWindow = !D.Window
Window, /Pixmap, /Free, XSize=10, YSize=10
WDelete, !D.Window
IF thisWindow GE 0 THEN WSet, thisWindow

   ; Check keyword parameters. Define defaults.

IF N_Elements(ncolors) EQ 0 THEN ncolors = 256 < !D.N_Colors
IF N_Elements(bottom) EQ 0 THEN bottom = 0
top = bottom + (ncolors-1)
IF N_Elements(title) EQ 0 THEN title = 'Load Color Tables'
IF N_ELements(file) EQ 0 THEN $
   file = Filepath(SubDir=['resource','colors'], 'colors1.tbl')
IF N_Elements(notifyID) EQ 0 THEN notifyID = [-1L, -1L]

   ; Find the center of the display.

DEVICE, GET_SCREEN_SIZE=screenSize
xCenter = FIX(screenSize(0) / 2.0)
yCenter = FIX(screenSize(1) / 2.0)

IF N_ELEMENTS(xoffset) EQ 0 THEN xoffset = xCenter - 150
IF N_ELEMENTS(yoffset) EQ 0 THEN yoffset = yCenter - 200

IF N_Elements(xoffset) EQ 0 THEN xoffset = 100
IF N_Elements(yoffset) EQ 0 THEN yoffset = 100
registerName = 'XCOLORS:' + title

   ; Only one XCOLORS with this title.

IF XRegistered(registerName) THEN RETURN

   ; Create the top-level base. No resizing.

tlb = Widget_Base(Column=1, Title=title, TLB_Frame_Attr=1, $
   XOffSet=xoffset, YOffSet=yoffset, Base_Align_Center=1, Tracking=1)

   ; Create a draw widget to display the current colors.

draw = Widget_Draw(tlb, XSize=256, YSize=40, Tracking=1)

   ; Create sliders to control stretchs and gamma correction.

sliderbase = Widget_Base(tlb, Column=1, Frame=1)
botSlider = Widget_Slider(sliderbase, Value=0, Min=0, $
   Max=ncolors-1, XSize=256, /Drag, Event_Pro='XColors_Bottom_Slider', $
   Title='Stretch Bottom')
topSlider = Widget_Slider(sliderbase, Value=ncolors-1, Min=0, $
   Max=ncolors-1, XSize=256, /Drag, Event_Pro='XColors_Top_Slider', $
   Title='Stretch Top')
gammaID = Widget_Label(sliderbase, Value=String(1.0, Format='(F6.3)'))
gammaSlider = Widget_Slider(sliderbase, Value=50.0, Min=0, Max=100, $
   /Drag, XSize=256, /Suppress_Value, Event_Pro='XColors_Gamma_Slider', $
   Title='Gamma Correction')

   ; Get the colortable names for the list widget.

colorNames=''
LoadCt, Get_Names=colorNames
FOR j=0,N_Elements(colorNames)-1 DO $
   colorNames(j) = StrTrim(j,2) + ' - ' + colorNames(j)
filebase = Widget_Base(tlb, Column=1, /Frame)
listlabel = Widget_Label(filebase, Value='Select Color Table...')
list = Widget_List(filebase, Value=colorNames, YSize=8, Scr_XSize=256, $
   Event_Pro='XColors_ColorTable')

   ; Dialog Buttons

dialogbase = WIDGET_BASE(tlb, Row=1)
cancel = Widget_Button(dialogbase, Value='Cancel', $
   Event_Pro='XColors_Cancel', UVALUE='CANCEL')
dismiss = Widget_Button(dialogbase, Value='Accept', $
   Event_Pro='XColors_Dismiss', UVALUE='ACCEPT')
Widget_Control, tlb, /Realize

   ; Get window index number of the draw widget.

Widget_Control, draw, Get_Value=windowIndex
WSet, windowIndex

   ; Put a picture of the color table in the window.

colorImage = BIndgen(256,40)
colorRow = BIndgen(ncolors)
colorRow = Congrid(colorRow, 256)
FOR j=0,39 DO colorImage(*,j) = colorRow
colorImage = BytScl(colorImage, Top=ncolors-1) + bottom
Tv, colorImage

   ; Get the colors that make up the current color table
   ; in the range that this program deals with.

TVLCT, rr, gg, bb, /Get
r = rr(bottom:top)
g = gg(bottom:top)
b = bb(bottom:top)

topColor = [rr(top), gg(top), bb(top)]
bottomColor = [rr(bottom), gg(bottom), bb(bottom)]

   ; Create an info structure to hold information to run the program.

info = {  windowIndex:windowIndex, $   ; The WID of the draw widget.
          botSlider:botSlider, $       ; The widget ID of the bottom slider.
          currentBottom:bottom, $      ; The current bottom slider value.
          currentTop:top, $            ; The current top slider value.
          topSlider:topSlider, $       ; The widget ID of the top slider.
          gammaSlider:gammaSlider, $   ; The widget ID of the gamma slider.
          gammaID:gammaID, $           ; The widget ID of the gamma label
          ncolors:ncolors, $           ; The number of colors we are using.
          gamma:1.0, $                 ; The current gamma value.
          file:file, $                 ; The name of the color table file.
          bottom:bottom, $             ; The bottom color index.
          top:top, $                   ; The top color index.
          topcolor:topColor, $         ; The top color in this color table.
          bottomcolor:bottomColor, $   ; The bottom color in this color table.
          reverse:0, $                 ; A reverse color table flag.
          notifyID:notifyID, $         ; Notification widget IDs.
          r:r, $                       ; The red color vector.
          g:g, $                       ; The green color vector.
          b:b, $                       ; The blue color vector.
          rstart:r, $                  ; The original red color vector.
          gstart:g, $                  ; The original green color vector.
          bstart:b, $                  ; The original blue color vector.
          colorimage:colorimage }      ; The color table image.

   ; Store the info structure in the user value of the top-level base.

Widget_Control, tlb, Set_UValue=info, /No_Copy

thisRelease = StrMid(!Version.Release, 0, 1)
IF thisRelease EQ '5' THEN $
   XManager, registerName, tlb, Group=group, Just_Reg=Keyword_Set(jregister), $
      /No_Block, Event_Handler='XCOLORS_PROTECT_COLORS' ELSE $
   XManager, registerName, tlb, Group=group, Just_Reg=Keyword_Set(jregister), $
      Event_Handler='XCOLORS_PROTECT_COLORS'
END ; ************************************************************************


PRO INTEGER_ONLY, event

; This event handler for text widgets only allows integers to be entered
; in the text widget. A maximum of four digits are allowed.

   ; Deal with simple one-character insertion events.

IF event.type EQ 0 THEN BEGIN

      ; Get the current text in the widget and find its length.

   Widget_Control, event.id, Get_Value=text
   text = text(0)
   length = StrLen(text)

      ; Only react if the insertion character is a number and there
      ; are less than four characters already in the widget.

   IF Byte(event.ch) GE 48B AND Byte(event.ch) LE 57B $
      AND length LT 4 THEN BEGIN

         ; Get the current text selection.

      selection = Widget_Info(event.id, /Text_Select)

         ; Insert the character at the proper location.

      Widget_Control, event.id, /Use_Text_Select, Set_Value=String(event.ch)

         ; Update the current insertion point in the text widget.

      Widget_Control, event.id, Set_Text_Select=event.offset + 1

   ENDIF

ENDIF ; of insertion event

   ; Deal with deletion events.

IF event.type EQ 2 THEN BEGIN

      ; Get the current text in widget.

   Widget_Control, event.id, Get_Value=text
   text = text(0)
   length = StrLen(text)

      ; Put it back with the deletion subtracted.

   Widget_Control, event.id, Set_Value=StrMid(text, 0, length-event.length)

      ; Reset the text insertion point in the text widget.

   Widget_Control, event.id, Set_Text_Select=event.offset
ENDIF

END ; of INTEGER_ONLY event handler *****************************************



PRO NULL_EVENTS, event

   ; The purpose of this event handler is to do nothing
   ;and ignore all events that come to it.

END ; of NULL_EVENTS event handler *******************************************



PRO GETIMAGE_EVENT, event

   ; The only events that can come here are button events.

   ; Get the info structure out of the user value of the top-level base.

Widget_Control, event.top, Get_UValue=info

   ; There may be errors we can't anticipate. Catch them here, alert the
   ; user as to what the error was, and exit the event handler without
   ; doing any damage.

Catch, error
IF error NE 0 THEN BEGIN
   ok = Widget_Message(!Err_String)
   RETURN
ENDIF

   ; Which button caused this event?

Widget_Control, event.id, Get_Value=buttonValue

CASE buttonValue OF

   'Pick Filename': BEGIN

         ; Start in the directory listed in the directory text widget.
         ; Convert the text value to a scalar.

      Widget_Control, info.dirnameID, Get_Value=startDirectory
      startDirectory = startDirectory(0)

         ; If this directory doesn't exist, use the current directory.

      test = Findfile(startDirectory, Count=foundfile)
      IF foundfile NE 1 THEN CD, Current=startDirectory

         ; Use PICKFILE to pick a name.

      pick = Pickfile(Path=startDirectory, /NoConfirm, $
         Get_Path=path, Filter='*.dat')

         ; Set the directory text widget with the name of the directory.
         ; Make sure the user didn't cancel out of PICKFILE.

      IF pick NE '' THEN BEGIN

            ; Find the lengths of the PICK and the PATH.

         pathLen = StrLen(path)
         picklen = StrLen(pick)

           ; Shorten the PATH to take off last file separator.

         path = StrMid(path,0,pathLen-1)

            ; Put the PATH in the directory location.
         Widget_Control, info.dirnameID, Set_Value=path

            ; Set the filename text widget with the name of the file.

         filename = StrMid(pick, pathlen, picklen-pathlen)
         Widget_Control, info.filenameID, Set_Value=filename

      ENDIF

      END ; of the Pick Filename button case

    'Cancel': BEGIN

         ; Have to exit here gracefully. Null filename means "CANCEL".

      formdata = {filename:''}
      Handle_Value, info.ptrToFormData, formdata, /Set

         ; Out of here!

      Widget_Control, event.top, /Destroy
      END ; of the Cancel button case

    'Accept': BEGIN  ; Gather the form information.

          ; Put the directory and filename together to make a path.

       Widget_Control, info.dirnameID, Get_Value=directory
       Widget_Control, info.filenameID, Get_Value=file

       filename = Filepath(Root_Dir=directory(0),file(0))

          ; Get the size and header info. Remember these are STRINGS!

       Widget_Control, info.headerID, Get_Value=header
       Widget_Control, info.xsizeID, Get_Value=xsize
       Widget_Control, info.ysizeID, Get_Value=ysize
       Widget_Control, info.frameID, Get_Value=frames

       header = Fix(header(0))
       xsize =  Fix(xsize(0))
       ysize =  Fix(ysize(0))
       frames =  Fix(frames(0))

          ; Get the data type from the droplist widget.

       listIndex = Widget_Info(info.droplistID, /Droplist_Select)
       datatype = info.datatypes(listIndex)

          ; Get the format index from the formatlist widget.

       formatIndex = Widget_Info(info.formatlistID, /Droplist_Select)


          ; Create the formdata structure from the information you collected.

       formdata = {header:header, xsize:xsize, ysize:ysize, frames:frames, $
          filename:filename, datatype:datatype, formatIndex:formatIndex}

          ; Store the formdata in the pointer location.

       Handle_Value, info.ptrToFormData, formdata, /Set

         ; Out of here!

      Widget_Control, event.top, /Destroy
      END ; of the Accept button case

ENDCASE

END ; of GETIMAGE_EVENT event handler ***************************************




FUNCTION GETIMAGE, filename, Directory=directory, XSize=xsize, YSize=ysize, $
   Frames=frames, Header=header, XDR=xdr, XOffSet=xoffset, YOffSet=yoffset

   ; This is a function to specify the size, data type, and header information
   ; about an image that you would like to read. It reads the data and returns
   ; it as the result of the function. If an error occurs, the function returns
   ; a 0.

   ; Check for parameters and keywords.

IF N_Params() EQ 0 THEN filename='ctscan.dat'

   ; If DIRECTORY keyword is not used, use the "coyote" directory.
   ; If that is not found, use the current directory.

IF N_Elements(directory) EQ 0 THEN BEGIN

   startDirectory = Coyote()
   IF startDirectory EQ '' THEN CD, Current=startDirectory

ENDIF ELSE startDirectory = directory

   ; Check for size and header keywords. These probably come in as
   ; numbers and you need strings to put them into text widgets.

IF N_Elements(xsize) EQ 0 THEN xsize='256' ELSE xsize=StrTrim(xsize,2)
IF N_Elements(ysize) EQ 0 THEN ysize='256' ELSE ysize=StrTrim(ysize,2)
IF N_Elements(frames) EQ 0 THEN frames='0' ELSE frames=StrTrim(frames,2)
IF N_Elements(header) EQ 0 THEN header='0' ELSE header=StrTrim(header,2)

   ; Find the center of the display.

Device, Get_Screen_Size=screenSize
xCenter = Fix(screenSize(0) / 2.0)
yCenter = Fix(screenSize(1) / 2.0)

IF N_Elements(xoffset) EQ 0 THEN xoffset = xCenter - 275
IF N_Elements(yoffset) EQ 0 THEN yoffset = yCenter - 150


   ; Create a top-level base. The fake TLB is used with IDL 5.
   ; This is probably not a good idea in general, but I am doing
   ; it to maintain IDL 4 compatibility.

thisRelease = StrMid(!Version.Release, 0, 1)
IF thisRelease EQ '5' THEN BEGIN
   fakeTLB = Widget_Base()
   tlb = Widget_Base(Column=1, Title='Read Image Data', XOffSet=xoffset, $
   YOffSet=yoffset, /Modal, Group_Leader=fakeTLB)
ENDIF ELSE BEGIN
   tlb = Widget_Base(Column=1, Title='Read Image Data', XOffSet=xoffset, $
      YOffSet=yoffset)
ENDELSE

   ; Create the directory widgets.

dirnamebase = Widget_Base(tlb, Row=1)
   dirnamelabel = Widget_Label(dirnamebase, Value='Directory:')
   dirnameID = Widget_Text(dirnamebase, Value=startDirectory, /Editable, $
      Event_Pro='NULL_EVENTS', XSize=Fix(2.0*StrLen(startDirectory) > 50))

   ; Create the filename widgets.

filenamebase = Widget_Base(tlb, Row=1)
   filenamelabel = Widget_Label(filenamebase, Value='Filename:')
   filenameID = Widget_Text(filenamebase, Value=filename, /Editable, $
      Event_Pro='NULL_EVENTS', XSize=2*StrLen(filename) > 20)

   ; Create a button to allow user to pick a filename.

pickbutton = Widget_Button(filenamebase, Value='Pick Filename')

   ; Create a droplist widget to select file data types.

database = Widget_Base(tlb, Row=1)
   datatypes = ['Byte', 'Integer', 'Long', 'Float']
   droplistID = Widget_Droplist(database, Value=datatypes, $
      Title='Data Type: ', Event_Pro='NULL_EVENTS')

   ; Create a droplist widget to select file formats.

   formatlistID = Widget_Droplist(database, Value=['None', 'XDR'], $
      Title='File Format: ', Event_Pro='NULL_EVENTS')

   ; Create a text widget to accept a header size.

   headlabel = Widget_Label(database, Value='Header Size:')
   headerID = Widget_Text(database, Value=header, All_Events=1, Editable=0, $
      Event_Pro='INTEGER_ONLY', XSize=8)

   ; Create widgets to gather the required file sizes.

sizebase = Widget_Base(tlb, Row=1)
   xlabel = Widget_Label(sizebase, Value='X Size:')
   xsizeID = Widget_Text(sizebase, Value=xsize, All_Events=1, Editable=0, $
      Event_Pro='INTEGER_ONLY', XSize=8)
   ylabel = Widget_Label(sizebase, Value='Y Size:')
   ysizeID = Widget_Text(sizebase, Value=ysize, All_Events=1, Editable=0, $
      Event_Pro='INTEGER_ONLY', XSize=8)
   zlabel = Widget_Label(sizebase, Value='Frames:')
   frameID = Widget_Text(sizebase, Value=frames, All_Events=1, Editable=0, $
      Event_Pro='INTEGER_ONLY', XSize=8)

   ; Create cancel and accept buttons.

cancelbase = Widget_Base(tlb, Column=2, Frame=1)
   cancel = Widget_Button(cancelbase, Value='Cancel')
   accept = Widget_Button(cancelbase, Value='Accept')

   ; Realize the program.

Widget_Control, tlb, /Realize

   ; Create a pointer to store the information collected from the form.

ptrToFormData = Handle_Create()

   ; Set the correct file format in the format droplist widget.

Widget_Control, formatlistID, Set_Droplist_Select=Keyword_Set(xdr)

   ; Set the text insertion point at the end of the filename text widget.

tip = [StrLen(filename),0]
Widget_Control, filenameID, Input_Focus=1
Widget_Control, filenameID, Set_Text_Select=tip

   ; Create an info structure with program information.

info = { filenameID:filenameID, $
         dirnameID:dirnameID, $
         xsizeID:xsizeID, $
         ysizeID:ysizeID, $
         frameID:frameID, $
         headerID:headerID, $
         droplistID:droplistID, $
         formatlistID:formatlistID, $
         datatypes:datatypes, $
         ptrToFormData:ptrToFormData}

  ; Store the info structure in the user value of the top-level base.

Widget_Control, tlb, Set_UValue=info

   ; The form will be a MODAL widget. This means we STOP at the
   ; following XManager call until the widget is destroyed.

thisRelease = StrMid(!Version.Release, 0, 1)
IF thisRelease EQ '5' THEN $
   XManager, 'getimage', tlb, Event_Handler='GETIMAGE_EVENT', /No_Block ELSE $
   XManager, 'getimage', tlb, Event_Handler='GETIMAGE_EVENT', /Modal

   ; Get the form data that was collected by the form.

Handle_Value, ptrToFormData, formdata

   ; If there is nothing here. Free the pointer and return.

IF N_Elements(formdata) EQ 0 THEN BEGIN
   Handle_Free, ptrToFormData
   RETURN, 0
ENDIF

   ; Did the user cancel out of the form? If so, return a 0.

IF formdata.filename EQ '' THEN BEGIN
   Handle_Free, ptrToFormData
   RETURN, 0
ENDIF

   ; Make the proper sized image array. Check for success.

image = 0
IF STRUPCASE(formdata.datatype) EQ 'INTEGER' THEN formdata.datatype = 'INT'
IF formdata.frames EQ 0 THEN $
   command = 'image = Make_Array(formdata.xsize, formdata.ysize, ' + $
      formdata.datatype + '=1)' ELSE $
   command = 'image = Make_Array(formdata.xsize, ' + $
      'formdata.ysize, formdata.frames, ' + formdata.datatype + '=1)'

check = Execute(command)
IF check EQ 0 THEN BEGIN
   ok = Widget_Message("Problem making image array. Returning 0.")
   RETURN, 0
ENDIF

   ; We can have all kinds of trouble reading data. Let's catch all
   ; input and output errors and alert user without crashing the program!

Catch, error
IF error NE 0 THEN BEGIN

      ; If we can't read the file for some reason, let the user know
      ; why, free the pointer and its information, check the logical
      ; unit number back in if it is checked out, and return a 0.

   ok = Widget_Message(!Err_String)
   Handle_Free, ptrToFormData
   IF N_ELements(lun) NE 0 THEN Free_Lun, lun
   RETURN, 0
ENDIF

   ; Read the data file.

IF formdata.header GT 0 THEN header = BytArr(formdata.header)
Get_Lun, lun
OpenR, lun, formdata.filename, XDR=formdata.formatIndex
IF formdata.header EQ 0 THEN ReadU, lun, image $
   ELSE ReadU, lun, header, image
Free_Lun, lun

   ; Free the pointer.

Handle_Free, ptrToFormData

   ; Free up the fake TLB, if necessary.

IF N_Elements(fakeTLB) NE 0 THEN Widget_Control, fakeTLB, /Destroy

RETURN, image

END ; of GETIMAGE program ***************************************************



FUNCTION TYPEVAR, variable

   ; Use SIZE function to get variable info.

varInfo = Size(variable)

   ; The next to last element in varInfo has the data type.

typeIndex = varInfo(varInfo(0) + 1)
dataTypes = ['UNDEFINED', 'BYTE', 'INTEGER', 'LONG', 'FLOATING', $
     'DOUBLE', 'COMPLEX', 'STRING', 'STRUCTURE', 'DCOMPLEX']
thisType = dataTypes(typeIndex)

RETURN, thisType
END ; *******************************************************************


FUNCTION ICON_BUTTON_EVENTS, event

   ; Get the info structure.

child = WIDGET_INFO(event.handler, /Child)
WIDGET_CONTROL, child, Get_UValue=info, /No_Copy

   ; Is this a button press event?

IF event.type EQ 0 THEN BEGIN
   temp = info.index(1)
   info.index(1) = info.index(2)
   info.index(2) = temp
   WIDGET_CONTROL, child, Set_UValue=info, /No_Copy
   ICON_BUTTON_REALIZE, event.handler
   RETURN, 0
ENDIF

   ; Pretend this is a button event. Make up a BUTTON event structure.

returnEvent = {WIDGET_BUTTON, ID:event.handler, TOP:event.top, HANDLER:0L, SELECT:1}
temp = info.index(1)
info.index(1) = info.index(2)
info.index(2) = temp
WIDGET_CONTROL, child, Set_UValue=info
ICON_BUTTON_REALIZE, event.handler

   ; If there is alternate event handler, send event to it.

IF (info.altPro NE '') THEN BEGIN
   Call_Procedure, info.altPro, returnEvent
   WIDGET_CONTROL, child, Set_UValue=info, /No_Copy
   RETURN, 0
ENDIF
IF (info.altFunc NE '') THEN BEGIN
   result = CALL_FUNCTION(info.altFunc, returnEvent)
   WIDGET_CONTROL, child, Set_UValue=info, /No_Copy
   RETURN, 0
ENDIF

   ; Send it to the regular event handler.

WIDGET_CONTROL, child, Set_UValue=info, /No_Copy
RETURN, returnEvent
END ; *******************************************************************


PRO ICON_BUTTON_SET, id, thisValue

   ; (No error checking!) You can pass in a button value
   ; directly as a string, or you can pass a button value
   ; and an image for the icon as a structure defined like
   ; this: struct = {value:'', image:yourImage}

child = WIDGET_INFO(id, /Child)
WIDGET_CONTROL, child, Get_UValue=info, /No_Copy
IF TYPEVAR(thisValue) EQ 'STRUCTURE' THEN BEGIN
   info.value = thisValue.value
   icon = CONGRID(thisValue.image, info.xsize-10, info.ysize-10)
   icon = BYTSCL(icon, TOP=info.ncolors-1) + BYTE(info.bottom)
   info.icon = icon
ENDIF ELSE info.value = thisValue
WIDGET_CONTROL, child, Set_UValue=info, /No_Copy

   ; Rebuild the button in the draw widget.

ICON_BUTTON_REALIZE, id
END ; *******************************************************************


FUNCTION ICON_BUTTON_GET, id

   ; The value of the button is the value text.

child = WIDGET_INFO(id, /Child)
WIDGET_CONTROL, child, Get_UValue=info, /No_Copy
retvalue = info.value
WIDGET_CONTROL, child, Set_UValue=info, /No_Copy
RETURN, retvalue
END ; *******************************************************************


PRO ICON_BUTTON_REALIZE, id

   ; This is where the button is drawn in the draw widget.

child = WIDGET_INFO(id, /Child)
WIDGET_CONTROL, child, Get_UValue=info, /No_Copy

   ; Make a pixmap window to draw into.

oldID = !D.Window
Window, /Free, XSize=info.xsize, YSize=info.ysize, /Pixmap
pixID = !D.Window

   ; Draw the button.

x1 = 5
x2 = info.xsize - 6
y1 = 5
y2 = info.ysize - 6
xs = info.xsize
ys = info.ysize
buttonx = [x1, x1, x2, x2, x1]
buttony = [y1, y2, y2, y1, y1]
lightx = [0, 0, xs, x2, x1, x1, 0]
lighty = [0, ys-1, ys-1, y2, y2, y1,0]
darkx = [0, x1, x2, x2, xs-1, xs-1, 0]
darky = [0, y1, y1, y2, ys-1,  0, 0]

POLYFILL, darkx, darky, Color=info.index(1), /Device
POLYFILL, lightx, lighty, Color=info.index(2), /Device
TV, info.icon, 5, 5
PLOTS, buttonx, buttony, Color=info.index(3), /Device
PLOTS, [0, x1], [0, y1], Color=info.index(3), /Device
PLOTS, [x1, 0], [y2, ys], Color=info.index(3), /Device
PLOTS, [x2, xs], [y2, ys], Color=info.index(3), /Device
PLOTS, [x2, xs], [y1, 0], Color=info.index(3), /Device
PLOTS, [0, xs-1, xs-1, 0, 0], [0, 0, ys-1, ys-1, 0], Color=info.index(0), /Device

   ; Make the draw widget the current graphics window.

WIDGET_CONTROL, info.drawID, Get_Value=wid
info.wid = wid
WSET, wid

   ; Copy the pixmap window and delete it.

WAIT, 0.1
DEVICE, COPY=[0, 0, info.xsize, info.ysize, 0, 0, pixID]
WDELETE, pixID

WSET, oldID
WIDGET_CONTROL, child, Set_UValue=info, /No_Copy
END ; *******************************************************************


FUNCTION ICON_BUTTON, parent, ColorIndex=index, XSize=xsize, $
   YSize=ysize, Value=value, UValue=uvalue, YOffset=yoffset, $
   Event_Pro=newprocedure, Event_Func=newfunction, XOffset=xoffset, $
   NColors=ncolors, Bottom=bottom, Image=image

ON_ERROR, 1

   ; Check for keywords and assign defaults if necessary.

IF N_PARAMS() NE 1 THEN MESSAGE, 'Must pass parent ID as argument.'
IF N_ELEMENTS(image) EQ 0 THEN image = DIST(256)
IF N_ELEMENTS(value) EQ '' THEN value = 'BUTTON VALUE'
IF N_ELEMENTS(yoffset) EQ 0 THEN yoffset = 0
IF N_ELEMENTS(xoffset) EQ 0 THEN xoffset = 0
IF N_ELEMENTS(uvalue) EQ 0 THEN uvalue = 'ICON BUTTON'
IF N_ELEMENTS(newprocedure) EQ 0 THEN newprocedure = ''
IF N_ELEMENTS(ncolors) EQ 0 THEN ncolors = !D.N_Colors - 4
IF N_ELEMENTS(bottom) EQ 0 THEN bottom = 0
IF N_ELEMENTS(newfunction) EQ 0 THEN newfunction = ''
IF N_ELEMENTS(xsize) EQ 0 THEN xsize = 40
IF N_ELEMENTS(ysize) EQ 0 THEN ysize = 40
IF N_ELEMENTS(index) EQ 0 THEN BEGIN
   index = INDGEN(4) + ncolors
   TVLCT, [255, 70, 150, 255], [255, 70, 150, 255], $
      [255, 70, 150, 255], ncolors
ENDIF

; Make the icon from the image data.

icon = CONGRID(image, xsize-10, ysize-10)
icon = BYTSCL(icon, TOP=ncolors-1) + BYTE(bottom)

cw_tlb = WIDGET_BASE(parent, UValue=uvalue, Event_Func='ICON_BUTTON_EVENTS', $
   Notify_Realize='ICON_BUTTON_REALIZE', Pro_Set_Value='ICON_BUTTON_SET', $
   Func_Get_Value='ICON_BUTTON_GET', XOffset=xoffset, YOffset=yoffset)
drawID = WIDGET_DRAW(cw_tlb, XSize=xsize, YSize=ysize, Button_Events=1)

info = { drawID:drawID, $ ;        Draw widget ID.
         wid:-1, $ ;               Window index number of draw widget.
         xsize:xsize, $ ;          XSIZE of draw widget.
         ysize:ysize, $ ;          YSIZE of draw widget.
         ncolors:ncolors, $ ;      The number of colors for the image icon.
         bottom:bottom, $          The starting index of the image icon colors.
         icon:icon, $ ;            The image that is the button icon.
         value:value, $ ;          The pseudo button "value".
         altPro:newprocedure, $ ;  The name of an alternate procedure to send event to.
         altFunc:newfunction, $ ;  The name of an alternate function to send event to.
         index:index} ;            The index array that specifies button colors values.

WIDGET_CONTROL, drawID, Set_UValue=info, /No_Copy
returnEvent = {WIDGET_BUTTON, ID:-1L, TOP:-1L, HANDLER:0-1L, SELECT:-1}
RETURN, cw_tlb
END ; *******************************************************************


PRO MakeIcon_Event, event

   ; Get the ID of the real application.

Widget_Control, event.top, Get_UValue=mapID

   ; Map the real application and destroy this icon.

Widget_Control, event.top, /Destroy
Widget_Control, mapID, Map=1

END ; ***************************************************************



PRO MakeIcon, image, mapID, NColors=ncolors, Bottom=bottom

   ; A procedure to make an icon out of an application.

;  image -- The image that goes on the icon. It can be any size.
;  mapID -- The ID of the top-level base of the application to be
;    iconified.
;  ncolors -- The number of colors used for the icon.
;  bottom -- The starting color index of the colors.

   ; Check for default values of keywords.

IF N_Elements(ncolors) EQ 0 THEN ncolors=!D.N_Colors-1
IF N_Elements(bottom) EQ 0 THEN bottom=0

   ; Unmap the application.

Widget_Control, mapID, Map=0

   ; Get the display size.

Device, Get_Screen_Size=srcSize

   ; Create the widgets. Use the compound widget ICON_BUTTON to
   ; create the icon.

tlb = Widget_Base(UValue=mapID, XOffset=20, YOffset=srcSize(1) - 60, $
   TLB_Frame_Attr=11, Title='Icon')
button = Icon_Button(tlb, Image=image, NColors=ncolors, Bottom=bottom)
Widget_Control, tlb, /Realize
XManager, 'makeicon', tlb
END ; ***************************************************************


PRO Example_Quit, event
Widget_Control, event.top, /Destroy
END ; ***************************************************************



PRO Example_Iconify, event

   ; Iconify the application.

Widget_Control, event.top, Get_UValue=info, /No_Copy
MakeIcon, info.currentImage, event.top, NColors=info.currentColors, $
    Bottom=info.currentBottom
Widget_Control, event.top, Set_UValue=info, /No_Copy
END ; ***************************************************************



PRO Example_Controls, event

   ; Map the control droplist widget in the window.

Widget_Control, event.top, Get_UValue=info, /No_Copy
Widget_Control, info.controlID, Map=1
Widget_Control, event.top, Set_UValue=info, /No_Copy
END ; ***************************************************************



PRO Example_Droplist_Events, event

   ; Respond to the droplist events. Unmap controls after
   ; each event.

Widget_Control, event.top, Get_UValue=info, /No_Copy
Widget_Control, event.id, Get_UValue=choices

   ; Draw into display window.

WSet, info.wid

CASE choices(event.index) OF
   'Sobel': BEGIN
      data = Sobel(info.currentImage)
      data = BytScl(data, Top=info.currentColors-1) + Byte(info.currentBottom)
      TV, data
      END

   'Roberts': BEGIN
      data = Roberts(info.currentImage)
      data = BytScl(data, Top=info.currentColors-1) + Byte(info.currentBottom)
      TV, data
      END

   'Median': BEGIN
      data = Median(info.currentImage, 5)
      data = BytScl(data, Top=info.currentColors-1) + Byte(info.currentBottom)
      TV, data
      END

   'Smooth 5': BEGIN
      data = Smooth(info.currentImage, 5)
      data = BytScl(data, Top=info.currentColors-1) + Byte(info.currentBottom)
      TV, data
      END

   'Original': TV, info.currentImage

ENDCASE

   ; Unmap controls in display window.

Widget_Control, info.controlID, Map=0
Widget_Control, event.top, Set_UValue=info, /No_Copy
END ; ***************************************************************



PRO Example_Load, event

   ; Load a new image. Requires the training program GETIMAGE.PRO.

Widget_Control, event.top, Get_UValue=info, /No_Copy

   ; Let user select image file.

image = GetImage()

   ; Anything go wrong reading the data? If "yes", out of here!

s = Size(image)
IF s(0) NE 2 THEN BEGIN
   Widget_Control, event.top, Set_UValue=info, /No_Copy
   RETURN
ENDIF

  ; Update the icon for the new image.

Widget_Control, info.id, Set_Value={value:'newimage', image:image}

  ; Make the display window active.

WSet, info.wid

   ; Display the new image after sizing and scaling.

data = Congrid(image, 350, 350)
data = BytScl(data, Top=info.currentColors) + Byte(info.currentBottom)
TV, data

   ; Update info structure with new image.

info.currentImage = data
Widget_Control, event.top, Set_UValue=info, /No_Copy
END ; ***************************************************************



PRO Example_Colors, event

   ; Load colors for images. Requires training program XCOLORS.PRO.

Widget_Control, event.top, Get_UValue=info, /No_Copy
XColors, NColors=info.currentColors, Bottom=info.currentBottom
Widget_Control, event.top, Set_UValue=info, /No_Copy

END ; ***************************************************************



PRO Example_Icon_Events, event

  ; Handles events from the image icons. Get information.

Widget_Control, event.top, Get_UValue=info, /No_Copy
Widget_Control, event.id, Get_UValue=buttonUValue, /No_Copy

   ; Display the icon image.

TV, buttonUValue.image

   ; Update the info structure.

info.currentImage = buttonUValue.image
info.currentColors = buttonUValue.ncolors
info.currentBottom = buttonUValue.bottom
info.id = event.id

Widget_Control, event.id, Set_UValue=buttonUValue, /No_Copy
Widget_Control, event.top, Set_UValue=info, /No_Copy
END ; ***************************************************************


PRO Example

   ; An example program for placing free-form buttons on interface.

ON_ERROR, 1
tlb = Widget_Base(Title='Free-Form Button Example', Column=1)

   ; Create the button panel.

buttonbase = Widget_Base(tlb, Row=1)
quitter = Widget_Button(buttonbase, Value='Quit', $
   Event_Pro='Example_Quit')
iconify = Widget_Button(buttonbase, Value='Iconify', $
   Event_Pro='Example_Iconify')
loader = Widget_Button(buttonbase, Value='Load', $
   Event_Pro='Example_Load')
controls = Widget_Button(buttonbase, Value='Controls', $
   Event_Pro='Example_Controls')
colors = Widget_Button(buttonbase, Value='Colors', $
   Event_Pro='Example_Colors')

   ; Create draw base as a bulletin board base.

drawbase = Widget_Base(tlb)

   ; Create the control panel droplist widget.

controlbase = Widget_Base(drawbase, XOffSet=10, YOffset=300, Map=0)
values = ['Sobel', 'Roberts', 'Median', 'Smooth 5', 'Original']
controls = Widget_DropList(controlbase, Value=values, UValue=values, $
   Event_Pro='Example_Droplist_Events')

   ; Load the files for the various icons. These should be in the
   ; EXAMPLES/DATA directory of the main IDL distribution. Build
   ; icons as you go with the compound widget ICON_BUTTON. Store
   ; icon information in the button's user value.

CATCH, thisError
IF thisError NE 0 THEN BEGIN
   ok = Widget_Message(['Problem reading data. Check image location', $
      'Should be: !DIR/examples/data'])
   RETURN
ENDIF

file = Filepath(Subdir=['examples', 'data'], 'cereb.dat')
OpenR, lun, file, /Get_Lun
image = Bytarr(512,512)
ReadU, lun, image
image = BytScl(Congrid(image, 350, 350), Top=39)
LoadCT, 3, NColors=40, Bottom=0
Free_Lun, lun
button = Icon_Button(drawbase, Image=image, Value='cereb.dat', $
   XOffset=375, YOffset=15, NColors=40, $
   ColorIndex=Indgen(4) + 200, Bottom=0)
uval = {image:image, ncolors:40, bottom:0, id:button}
Widget_Control, button, Set_UValue=uval
currentID = button

file = Filepath(Subdir=['examples', 'data'], 'galaxy.dat')
OpenR, lun, file, /Get_Lun
image = Bytarr(256,256)
ReadU, lun, image
image = BytScl(Congrid(image, 350, 350), Top=39) + 40B
LoadCT, 26, NColors=40, Bottom=40
Free_Lun, lun
button = Icon_Button(drawbase, Image=image, Value='galaxy.dat', $
  XOffset=375, YOffset=85, NColors=40, $
   ColorIndex=Indgen(4) + 200, Bottom=40)
uval = {image:image, ncolors:40, bottom:40, id:button}
Widget_Control, button, Set_UValue=uval

file = Filepath(Subdir=['examples', 'data'], 'people.dat')
OpenR, lun, file, /Get_Lun
image = Bytarr(192,192)
ReadU, lun, image
image = BytScl(Congrid(image, 350, 350), Top=39) + 80B
LoadCT, 0, NColors=40, Bottom=80
Free_Lun, lun
button = Icon_Button(drawbase, Image=image, Value='people.dat', $
   XOffset=375, YOffset=155, NColors=40, $
   ColorIndex=Indgen(4) + 200, Bottom=80)
uval = {image:image, ncolors:40, bottom:80, id:button}
Widget_Control, button, Set_UValue=uval

file = Filepath(Subdir=['examples', 'data'], 'abnorm.dat')
OpenR, lun, file, /Get_Lun
image = Bytarr(64,64)
ReadU, lun, image
image = BytScl(Congrid(image, 350, 350), Top=39) + 120B
LoadCT, 11, NColors=40, Bottom=120
Free_Lun, lun
button = Icon_Button(drawbase, Image=image, Value='abnorm.dat', $
   XOffset=375, YOffset=225, NColors=40, $
   ColorIndex=Indgen(4) + 200, Bottom=120)
uval = {image:image, ncolors:40, bottom:120, id:button}
Widget_Control, button, Set_UValue=uval

file = Filepath(Subdir=['examples', 'data'], 'worldelv.dat')
OpenR, lun, file, /Get_Lun
image = Bytarr(360,360)
ReadU, lun, image
image = BytScl(Congrid(image, 350, 350), Top=39) + 160B
LoadCT, 4, NColors=40, Bottom=160
Free_Lun, lun
button = Icon_Button(drawbase, Image=image, Value='worldelv.dat', $
   XOffset=375, YOffset=295, NColors=40, $
   ColorIndex=Indgen(4) + 200, Bottom=160)
uval = {image:image, ncolors:40, bottom:160, id:button}
Widget_Control, button, Set_UValue=uval

drawID = Widget_Draw(drawbase, XSize=450, YSize=350)
file = Filepath(Subdir=['examples', 'data'], 'cereb.dat')
OpenR, lun, file, /Get_Lun
image = Bytarr(512,512)
ReadU, lun, image
Free_Lun, lun
image = Congrid(image, 350,350)

   ; Cancel error handling.

Catch, /Cancel

   ; Load colors for the icon button's boundaries.

TVLCT, [255, 70, 150, 255], [255, 70, 150, 255], $
   [255, 70, 150, 255], 200

Widget_Control, tlb, /Realize
Widget_Control, drawID, Get_Value=wid
WSet, wid
image = BytScl(image, Top=39)
TV, image

info = { wid:wid, $               ; Window index number of display.
         currentImage:image, $    ; The current image on display.
         currentColors:40, $      ; Number of colors used by current image.
         currentBottom:0, $       ; Index where current colors are loaded.
         id:currentID, $          ; Icon button ID.
         controlID:controlbase }  ; ID of control base for mapping controls.

Widget_Control, tlb, Set_UValue=info, /No_Copy
XManager, 'example', tlb, Event_Handler='Example_Icon_Events'
END ; ***********************************************************************