coyote: XCOLORS

Description
The purpose of this program is to interactively change color tables
in a manner similar to XLoadCT. No common blocks are used so
multiple copies of XColors can be on the display at the same
time (if each has a different `Title`). XColors has the ability
to notify a widget event handler, an object method, or an IDL
procedure if and when a new color table has been loaded. Brewer
color tables can also be accessed from this program, if the file
fsc_brewer.tbl can be found somewhere in your IDL path.
Events are sent to widgets if the `NotifyID` keyword is used. Object 
methods are called if the `NotifyObj` keyword is used. This program 
is a non-blocking widget unless the `Block` keyword is set.
.. image:: xcolors.png
Categories
Graphics
Examples
To load a color table into 100 colors, starting at color index
 50 and send an event to the widget identified at info.drawID
 in the widget heirarchy of the top-level base event.top, type::
    XCOLORS, NCOLORS=100, BOTTOM=50, NOTIFYID=[info.drawID, event.top]
Author
FANNING SOFTWARE CONSULTING::
    David W. Fanning 
    1645 Sheely Drive
    Fort Collins, CO 80526 USA
    Phone: 970-221-0438
    E-mail: david@idlcoyote.com
    Coyote's Guide to IDL Programming: http://www.idlcoyote.com
History
Change History::
   Written by David W. Fanning, 15 April 97. 
   Added OBJECT_DATA keyword so that I can get additional information
       about the state of the color table tool into object methods. 21 October 2008. DWF.
   Add REVERSE keyword and Reverse Color Table button. 12 April 2009. DWF.
   In looking for a Brewer color table file, I replaced all FILE_WHICH 
       commands with cgFindPathTo commands. 28 April 2009. DWF.
   Made sure all "NOTIFY" data structures have both a "REVERSED" and 
       "BREWER" field in them to indicate the status of the XCOLORS program. Also 
       inproved the documentation and made it more accurate. 20 Sept 2009. DWF.
   Still a few problems getting the Brewer color tables completely integrated. 
       Fixed several bugs with updating color table names and type. 14 Oct 2009. DWF.
   Modified the program to work correctly with a user-supplied color table file. 29 Sept 2010. DWF.
   Fixed a problem I noticed when starting the program with reversed color tables. The 
        initial colors were incorrect on subsequent calls. Also made a modification so that 
        color index -1 as input is handled properly (ignored). 26 November 2010. DWF.
   Added WINDOW and WINID keywords. 26 January 2011. DWF.
   Changed several Get_Decomposed calls to the more generic cgSetColorState. 15 Jan 2012. DWF.
Copyright
Copyright (c) 1997-2012, Fanning Software Consulting, Inc.
 routines is identical to the IDL Congrid command, except that it 
les a problem with floating divides by zero properly.
Params
tlb: in, required
    The widget identifier of the widget that just died.
 is a procedure to load color tables into a restricted color range of the physical 
r table. It is a highly simplified, but much more powerful, version of XLoadCT.
Keywords
block: in, optional, type=boolean, default=0
   If this keyword is set, the program will try to block the
   IDL command line. Note that this is only possible if no other
   widget program is currently blocking the IDL command line. It
   is much more reliable to make XCOLORS a modal widget (see the MODAL
   keyword), although this can generally only be done when XCOLORS
   is called from another widget program.
brewer: in, optional, type=boolean, default=0
   Set this keyword if you wish to use the Brewer Colors, as explained
   in this reference: http://www.idlcoyote.com/color_tips/brewer.html. The
   file, `fsc_brewer.tbl `
   must be found somewhere in your IDL path for this option to be available. 
   Note that if this file is found, the Brewer colors are automatically added
   to the program as an option. In this case, the BREWER keyword just makes sure
   this is the initial user choice.
bottom: in, optional, type=integer, default=0
   The lowest color index of the colors to be changed.
colorinfo: out, optional
   This output keyword will return either a pointer to a color information structure 
   (if the program is called in a non-modal fashion) or a color information structure 
   (if the program is called in modal or blocking fashion). The color information
    structure is an anonymous structure defined like this::
        struct = { R: BytArr(!D.Table_Size), $ ; The current R color vector.
                   G: BytArr(!D.Table_Size), $ ; The current G color vector.
                   B: BytArr(!D.Table_Size), $ ; The current B color vector.
                   NAME: "", $                 ; The name of the current color table.
                   INDEX: 0, $                 ; The index number of the current color table.
                   TYPE: "", $                 ; The type of color table (e.g, BREWER or IDL).
                   BREWER: 0, $                ; Set to 1 if using BREWER color tables, else to 0.
                   REVERSED: 0B }              ; Set to 1 if the color table is reversed.
   If a pointer to the structure is obtained, you will be responsible
   for freeing it to prevent memory leakage::
        XColors, ColorInfo=colorInfoPtr
        Print, "Color Table Name: ", (*colorInfoPtr).Name
        Ptr_Free, colorInfoPtr
   Note that that Name field will be "Unknown" and the Index field will
   be -1 until a color table is actually selected by the user. You are
   responsible for checking this value before you use it.
   When called in modal or blocking fashion, you don't have to worry about freeing
   the pointer, since no pointer is involved::
        XColors, /Block, ColorInfo=colorInfoData
        Help, colorInfoData, /Structure
        Print, "Color Table Name: ", colorInfoData.Name
data: in, optional
   This keyword can be set to any valid IDL variable. If
   the variable is defined, the specified object method or notify
   procedure will be passed this variable via a DATA keyword. This
   keyword is defined primarily so that Notify Procedures are compatible
   with the XLOADCT way of passing data. It is not strictly required,
   since the _EXTRA keyword inheritance mechanism will allow passing
   of *any* keyword parameter defined for the object or procedure that is
   to be notified.
drag: in, optional, type=boolean, default=0
   Set this keyword if you want colors loaded as you drag
   the sliders. Default is to update colors only when you release
   the sliders. Use of this keyword is greatly discouraged.
file: in, optional, type=string, default="colors1.tbl"
   A name of a color table file. The supplied colors1.tbl file is used by default.
group_leader: in, optional, type=long
   The group leader identifier for this program. When the group
     leader is destroyed, this program will be destroyed.
index: in, optional, type=integer
   The index of the color table to start up. If provided, a color
   table of this index number is loaded prior to display. Otherwise,
   the current color table is used. Set this keyword if you wish
   to have the index number of the event structure correct when
   the user CANCELs out of the progam.
modal: in, optional, type=boolean, default=0
   Set this keyword (along with the GROUP_LEADER keyword) to
   make the XCOLORS dialog a modal widget dialog. Note that NO
   other events can occur until the XCOLORS program is destroyed
   when in modal mode.
ncolors: in, optional, type=integer, default=256
   Set this keyword to the number of colors to load when a color table
   is selected.
nosliders: in, optional, type=boolean, default=0
   If this keyword is set, the color stretch and color gamma
   sliders are not displayed. This would be appropriate, for example,
   for programs that just load pre-defined color tables.
notifyid: in, optional
   A 2-column by n-row array that contains the IDs of widgets
   that should be notified when XCOLORS loads a color table. The first
   column of the array is the widgets that should be notified. The
   second column contains IDs of widgets that are at the top of the
   hierarchy in which the corresponding widgets in the first column
   are located. (The purpose of the top widget IDs is to make it
   possible for the widget in the first column to get the "info"
   structure of the widget program.) An XCOLORS_LOAD event will be
   sent to the widget identified in the first column. The event
   structure is defined like this::
      event = {XCOLORS_LOAD, ID:0L, TOP:0L, HANDLER:0L, $
         R:BytArr(!D.TABLE_SIZE < 256), G:BytArr(!D.TABLE_SIZE < 256), $
         B:BytArr(!D.TABLE_SIZE < 256), INDEX:0, NAME:"", $
         TYPE:"", BREWER:0, REVERSED:0}
   The ID field will be filled out with NOTIFYID[0, n] and the TOP
   field will be filled out with NOTIFYID[1, n]. The R, G, and B
   fields will have the current color table vectors, obtained by
   exectuing the command TVLCT, r, g, b, /Get. The INDEX field will
   have the index number of the just-loaded color table. The name
   field will have the name of the currently loaded color table.
   The TYPE field with be "BREWER" if a Brewer color table was loaded,
   or "IDL" otherwise. The BREWER field will be set to 1 if a Brewer 
   color table was loaded, or to 0 otherwise. The REVERSED field will
   be set to 1 if the color table is reversed, or to 0 otherwise.
   Note that XCOLORS can't initially tell *which* color table is
   loaded, since it just uses whatever colors are available when it
   is called. Thus, it stores a -1 in the INDEX field to indicate
   this "default" value. Programs that rely on the INDEX field of
   the event structure should normally do nothing if the value is
   set to -1. This value is also set to -1 if the user hits the
   CANCEL button. (Note the NAME field will initially be "Unknown").
   Typically the XCOLORS button will be defined like this::
        xcolorsID = Widget_Button(parentID, Value='Load New Color Table...', $
           Event_Pro='Program_Change_Colors_Event')
   The event handler will be written something like this::
        PRO Program_Change_Colors_Event, event
           ; Handles color table loading events. Allows colors be to changed.
        Widget_Control, event.top, Get_UValue=info, /No_Copy
        thisEvent = Tag_Names(event, /Structure_Name)
        CASE thisEvent OF
           'WIDGET_BUTTON': BEGIN
                ; Color table tool.
              XColors, NColors=info.ncolors, Bottom=info.bottom, $
                 Group_Leader=event.top, NotifyID=[event.id, event.top]
              ENDCASE
           'XCOLORS_LOAD': BEGIN
                ; Update the display for 24-bit displays.
              Device, Get_Visual_Depth=thisDepth
              IF thisDepth GT 8 THEN BEGIN
              WSet, info.wid
               ...Whatever display commands are required go here. For example...
               TV, info.image
            ENDIF
            ENDCASE
         ENDCASE
         Widget_Control, event.top, Set_UValue=info, /No_Copy
         END
notifyobj: in, optional, type=structure
   A vector of structures (or a single structure), with each element of the vector 
   defined as follows::
        struct = {XCOLORS_NOTIFYOBJ, object:Obj_New(), method:''}
   where the Object field is an object reference, and the Method field
   is the name of the object method that should be called when XCOLORS
   loads its color tables::
        ainfo = {XCOLORS_NOTIFYOBJ, a, 'Draw'}
        binfo = {XCOLORS_NOTIFYOBJ, b, 'Display'}
        XColors, NotifyObj=[ainfo, binfo]
   Note that the XColors program must be compiled before these structures
   are used. Alternatively, you can put this program, named
   "xcolors_notifyobj__define.pro" (*three* underscore characters in this
   name!) in your PATH::
        PRO XCOLORS_NOTIFYOBJ__DEFINE
         struct = {XCOLORS_NOTIFYOBJ, OBJECT:Obj_New(), METHOD:''}
        END
   Or, you can simply define this structure as it is shown here in your code.
   "Extra" keywords added to the XCOLORS call are passed along to
   the object method, which makes this an alternative way to get information
   to your methods. If you expect such keywords, your methods should be defined
   with an _Extra keyword.
   If you set the /OBJECT_DATA keyword, the same structure defined for the 
   COLORINFO keyword above will be passed to your object method via an 
   XCOLORS_DATA keyword that you will have to define for the method.
notifypro: in, optional, type=string
   The name of a procedure to notify or call when the color
   tables are loaded. If the DATA keyword is also defined with a valid
   IDL variable, it will be passed to this program via an DATA keyword. 
   But note that *any* keyword appropriate for the procedure can be used 
   in the call to XCOLORS. For example, here is a procedure that re-displays 
   an image in the current graphics window::
        PRO REFRESH_IMAGE, Image=image, _Extra=extra, WID=wid
        IF N_Elements(wid) NE 0 THEN WSet, wid
        cgImage, image, _Extra=extra
        END
   This program can be invoked with this series of commands::
        IDL> Window, /Free
        IDL> cgImage, image, Position=[0.2, 0.2, 0.8, 0.8]
        IDL> XColors, NotifyPro='Refresh_Image', Image=image, WID=!D.Window
   Note that "extra" keywords added to the XCOLORS call are passed along to
   your procedure, which makes this an alternative way to get information
   to your procedure. If you expect such keywords, your procedure should
   be defined with an _Extra keyword as illustrated above.
object_data: in, optional, type=boolean, default=0        
   Set this keyword if you wish color information to be
   supplied to your object notification method via an XCOLORS_DATA
   keyword. This keyword is ignored unless the NOTIFYOBJ keyword is
   also used. The color information is supplied as a structure and is
   defined in the COLORINFO keyword definition above.
reverse: in, optional, type=boolean, default=0      
   If this keyword is set, the color table is reversed and the
   Reverse Color Table button is set on.
title: in, optional, type='string'
   This is the window title. It is "Load Color Tables" by default. The program 
   is registered with the name 'XCOLORS:' plus the TITLE string. The "register 
   name" is checked before the widgets are defined. If a program with that name 
   has already been registered you cannot register another with that name. This 
   means that you can have several versions of XCOLORS open simultaneously as long 
   as each has a unique title or name. For example, like this::
       IDL> XColors, NColors=100, Bottom=0, Title='First 100 Colors'
       IDL> XColors, NColors=100, Bottom=100, Title='Second 100 Colors'
window: in, optional, type=boolean, default=0
   Set this keyword to send the colors to a Coyote Graphics cgWindow program.
winid: in, optional, type=integer   
   The window index number of a Coyote Graphics cgWindow program to receive the color vectors.
xoffset: in, optional, type=integer
   This is the X offset of the program on the display. The program will be placed 
   approximately in the middle of the display by default.
yoffset: in, optional, type=integer
   This is the Y offset of the program on the display. The program will be placed 
   approximately in the middle of the display by default.
_extra: in, optional
   The keyword inheritance mechanism will pick up and pass along to any method or procedure 
   to be notified any keywords that are defined for that procedure. Note that you should be 
   sure that keywords are spelled correctly. Any mis-spelled keyword will be ignored.