; $Id: cw_text_display.pro,v 1.9 2005/07/14 16:03:02 rdimeo Exp $
; +
; NAME:
;  CW_TEXT_DISPLAY
;
; PURPOSE:
;  Compound widget that can display text using colored fonts.
;  This is not a replacement for WIDGET_TEXT because it is
;  not editable.  It is just for display purposes only.
;
; CALLING SEQUENCE:
;  ID = CW_DISPLAY_TEXT(   PARENT,                          $
;                          OBJ_REF = obj_ref,               $
;                          UNAME = uname,                   $
;                          UVALUE = uvalue,                 $
;                          XSIZE = xsize,                   $
;                          TEXT = text,                     $
;                          COLOR = color,                   $
;                          YSIZE = ysize                    )
;
; RETURN VALUE:
;  Widget ID of the compound widget.
;
; PARAMETERS:  (required)
;
;  PARENT:     widget ID of the parent of this compound widget
;
; KEYWORDS:    (optional)
;
;  UNAME:      Set this keyword to a string that can be used to identify
;              the widget in your code.  You can associate a name with each
;              widget in a specific hierarchy, and then use that name to
;              query the widget hierarchy and get the correct widget ID.
;
;  UVALUE:     A user value to assign to the CW.  This value can be
;              of any type.
;  XSIZE:      Horizontal size of the draw widget (in pixels)
;  YSIZE:      Vertical size of the draw widget (in pixels)
;  TEXT:       String array containing the initial setting for the
;              text display.
;  COLOR:      String array of the same size as TEXT that contains
;              the identity of the colors of each of the elements in TEXT.
;              Valid colors are those accepted by FSC_COLOR (David Fanning).
;  OBJ_REF:    Reference to the object which allows more control over
;              the compound widget than WIDGET_CONTROL.
;
; SIDE EFFECTS:
;  -This widget generates no events.
;
; COMMON BLOCKS:
;  None
;
; REQUIRED PROGRAMS:
;  FSC_COLOR.PRO (From David Fanning's web site)
;
; REQUIRED RESOURCES:
;  NONE
;
; REQUIREMENTS:
;  24-Bit color required
;
; EXAMPLE USAGE: (appended to end of this file)
;  Compile this file then
;  IDL> TEXT_DISPLAY_TEST
;
; AUTHOR:
;  Robert M. Dimeo, Ph.D.
;  NIST Center for Neutron Research
;  100 Bureau Drive
;  Mail Stop 8562
;  Gaithersburg, MD 20899
;  Phone: (301) 975-8135
;  E-mail: robert.dimeo@nist.gov
;  http://www.ncnr.nist.gov/staff/dimeo
;
; MODIFICATION HISTORY:
;  Written 6/03/05
;  Corrected some problems with the ordering of text in text
;     arrays and also the colors -- RMD 06/06/05
;  Rewrote the ENLARGE method when I discovered that I did *not*
;     have to destroy and re-create the entire draw widget all over
;     again. -- RMD 06/06/05.
;  Added better color protection. -- RMD 06/15/05
;
; LICENSE:
;  The software in this file is written by an employee of
;  National Institute of Standards and Technology
;  as part of the DAVE software project.
;
;  The DAVE software package is not subject to copyright protection
;  and is in the public domain. It should be considered as an
;  experimental neutron scattering data reduction, visualization, and
;  analysis system. As such, the authors assume no responsibility
;  whatsoever for its use, and make no guarantees, expressed or
;  implied, about its quality, reliability, or any other
;  characteristic. The use of certain trade names or commercial
;  products does not imply any endorsement of a particular product,
;  nor does it imply that the named product is necessarily the best
;  product for the stated purpose. We would appreciate acknowledgment
;  if the DAVE software is used or if the code in this file is
;  included in another product.
; -
; ************************************************************ ;
pro rmd_text_display::cleanup
ptr_free,self.text_ptr,self.storage,self.color_ptr
end
; ************************************************************ ;
pro rmd_text_display::get_property, tlb = tlb,                 $
                                    uvalue = uvalue,           $
                                    value = value,             $
                                    xsize = xsize,             $
                                    ysize = ysize,             $
                                    winbase = winbase,         $
                                    color = color

winbase = self.winbase
tlb = self.tlb
if n_elements(*self.text_ptr) eq 0 then value = '' else $
   value = *self.text_ptr
if n_elements(*self.color_ptr) eq 0 then color = '' else $
   color = *self.color_ptr
xsize = self.xsize
ysize = self.ysize
uvalue = *self.storage
end
; ************************************************************ ;
pro rmd_text_display::set_property, win = win,                 $
                                    winvis = winvis,           $
                                    value = value

if n_elements(win) ne 0 then self.win = win
if n_elements(winvis) ne 0 then self.winvis = winvis
if n_elements(value) ne 0 then *self.text_ptr = value
end
; ************************************************************ ;
function rmd_text_display::eventhandler,event
; Handle the *internal* events
if event.id eq self.win then begin

   ; Handle viewport change events (i.e. scroll bar changes)
   if event.type eq 3 then begin
      n = n_elements(*self.text_ptr)
      if n eq 0 then return,0B
      wset,self.winvis
      tv,255B+bytarr(3,self.x_scroll_size,self.y_scroll_size),/true
      dy = float(!d.y_ch_size)
      ypos = 2.0*dy+1.5*dy*findgen(n)
      orig_font = !p.font
      !p.font = 0
      for i = 0,n-1 do begin
         xyouts,5-event.x,ypos[i]-event.y,(*self.text_ptr)[i],    $
            /device,color = fsc_color((*self.color_ptr)[i])
      endfor
      !p.font = orig_font
      return,0B
   endif
endif

return,0B
end
; ************************************************************ ;
function rmd_text_display::append_text,text
; Are there more than one element in text?
max_len = 128
n = n_elements(text)
if n eq 0 then begin
   ; Is this line too long?
   length = strlen(text)
   if length gt max_len then begin
      ; Split up the text value into a new text array
      ; whose elements do not exceed MAX_LEN
      nchar = 1 + length/max_len
      text_out = strarr(nchar)
      for i = 0,nchar-1 do begin
         init = i*max_len
         text_out[i] = strmid(text,init,max_len)
      endfor
   endif else begin
      text_out = text
   endelse
endif else begin
   for j = 0,n-1 do begin
      ; Is this line too long?
      length = strlen(text[j])
      if length gt max_len then begin
         ; Split up the text value into a new text array
         ; whose elements do not exceed MAX_LEN
         nchar = 1 + length/max_len
         output = strarr(nchar)
         for i = 0,nchar-1 do begin
            init = i*max_len
            output[i] = strmid(text[j],init,max_len)
         endfor
      endif else begin
         output = text[j]
      endelse

      if n_elements(text_out) eq 0 then begin
         text_out = output
      endif else begin
         text_out = [output,text_out]
      endelse
   endfor
endelse
text = reverse(text_out)

if n_elements(*self.text_ptr) eq 0 then begin
   *self.text_ptr = text
endif else begin
   *self.text_ptr = [text,*self.text_ptr]
endelse
return,1B
end
; ************************************************************ ;
function rmd_text_display::set_color,color
if n_elements(color) gt 1 then color = reverse(color)
if n_elements(*self.color_ptr) eq 0 then        $
   *self.color_ptr = color                      $
else                                            $
   *self.color_ptr = [color,*self.color_ptr]
return,1B
end
; ************************************************************ ;
function rmd_text_display::enlarge
device,get_decomposed = dc
device,decomposed = 1
n = n_elements(*self.text_ptr)
if (n+1) lt float(self.ysize)/(1.5*!d.y_ch_size) then y_enlarge = 0B else y_enlarge = 1B
;if y_enlarge then self.ysize = self.ysize + 100.*!d.y_ch_size
if y_enlarge then self.ysize = self.ysize + n*!d.y_ch_size

max_string = max(strlen(*self.text_ptr),imax)
strmax = (*self.text_ptr)[imax]
wset,self.winvis
xyouts,0.,0.,strmax,width = this_width,charsize = -1
win_geom = widget_info(self.win,/geometry)
this_width = this_width*(float(!d.x_size)/win_geom.draw_xsize)
xlen = win_geom.draw_xsize*this_width
x_enlarge = (xlen+5*!d.x_ch_size) gt self.xsize
if x_enlarge then self.xsize = xlen+10*!d.x_ch_size

if (x_enlarge or y_enlarge) then begin
   widget_control,self.win,   draw_xsize = self.xsize,            $
                              draw_ysize = self.ysize
   widget_control,self.win,set_draw_view = [0,0]
   wset,self.winvis
   tv,255B+bytarr(3,self.xsize,self.ysize),/true
endif
device,decomposed = dc
return,1B
end
; ************************************************************ ;
function rmd_text_display_get,id
stash = widget_info(id,/child)
widget_control,stash,get_uvalue = the_object
the_object->get_property,value = value
return,reverse(value)
end
; ************************************************************ ;
function rmd_text_display::clear_text
ptr_free,*self.text_ptr
self.text_ptr = ptr_new(/allocate_heap)
wset,self.winvis
tv,255B+bytarr(3,self.xsize,self.ysize),/true
return,1B
end
; ************************************************************ ;
function rmd_text_display::update_text,value
device,get_decomposed = dc
device,decomposed = 1
; Append the latest value of the text to the current text
; array.
ret = self->append_text(value)
; Determine if we need to enlarge the display window
ret = self->enlarge()
; Display the text in the window
n = n_elements(*self.text_ptr)
if n eq 0 then begin
   device,decomposed = dc
   return,0B
endif
wset,self.winvis
tv,255B+bytarr(3,self.xsize,self.ysize),/true
if n gt n_elements(*self.color_ptr) then begin
   nremaining = n - n_elements(*self.color_ptr)
   ret = self->set_color(replicate('black',nremaining))
endif
dy = float(!d.y_ch_size)
ypos = 2*dy+1.5*dy*indgen(n)
orig_font = !p.font
!p.font = 0
for i = 0,n-1 do begin
   xyouts,5.0,ypos[i],(*self.text_ptr)[i],/device, $
               color = fsc_color((*self.color_ptr)[i])
endfor
!p.font = orig_font
device,decomposed = dc
return,1B
end
; ************************************************************ ;
pro rmd_text_display_set,id,value
stash = widget_info(id,/child)
widget_control,stash,get_uvalue = the_object

n = n_elements(value)
for i = 0,n-1 do begin
   result = strsplit(value[i],string(10B),/extract)
   if n_elements(output) eq 0 then begin
      output = result
   endif else begin
      output = [output,result]
   endelse
endfor
if n_elements(output) gt 1 then output = reverse(output)
ret = the_object->update_text(output)
end
; ************************************************************ ;
pro rmd_text_display_kill,id
widget_control,id,get_uvalue = the_object
obj_destroy,the_object
end
; ************************************************************ ;
function rmd_text_display_event,event
stash = widget_info(event.id,/parent)
widget_control,stash,get_uvalue = the_object
the_event = the_object->eventhandler(event)
return,the_event
end
; ************************************************************ ;
pro rmd_text_display_notify_realize,id
device,get_decomposed = dc
device,decomposed = 1
stash = widget_info(id,/parent)
widget_control,stash,get_uvalue = the_object
widget_control,id,get_value = winvis
the_object->set_property,winvis = winvis
the_object->get_property,xsize = xsize,ysize = ysize,          $
            winbase = winbase
wset,winvis
tv,255B+bytarr(3,xsize,ysize),/true
widget_control,id,set_draw_view = [0,0]
device,/cursor_original
;widget_control,winbase,map = 1B
device,decomposed = dc
end
; ************************************************************ ;
function rmd_text_display::build_widget
tlb = widget_base(self.parent,uvalue = *self.storage,        $
                  uname = self.uname,                             $
                  pro_set_value = 'rmd_text_display_set',         $
                  func_get_value = 'rmd_text_display_get'         )
self.tlb = tlb
self.winbase = widget_base(self.tlb,uvalue = self,                $
               kill_notify = 'rmd_text_display_kill'              )

self.win = widget_draw(self.winbase,xsize = self.xsize,           $
            /app_scroll,                                          $
            /keyboard_events,                                     $
            notify_realize = 'rmd_text_display_notify_realize',   $
            event_func = 'rmd_text_display_event',                $
            retain = 2,                                           $
            ysize = self.ysize,                                   $
            x_scroll_size = self.x_scroll_size,                   $
            y_scroll_size = self.y_scroll_size                    )
return,1B
end
; ************************************************************ ;
function rmd_text_display::init, parent,                          $
                                 xsize = xsize,                   $
                                 ysize = ysize,                   $
                                 uvalue = uvalue,                 $
                                 uname = uname,                   $
                                 text = text,                     $
                                 color = color

if n_params() eq 0 then return,0B
self.parent = parent
self.text_ptr = ptr_new(/allocate_heap)
if n_elements(text) ne 0 then *self.text_ptr = text

self.x_scroll_size = (n_elements(xsize) ne 0) ? xsize:400
self.y_scroll_size = (n_elements(ysize) ne 0) ? ysize:200

self.xsize = 80.*!d.x_ch_size
self.ysize = self.y_scroll_size+10
self.color_ptr = (n_elements(color) ne 0) ? ptr_new(color):ptr_new(/allocate_heap)
self.storage = (n_elements(uvalue) ne 0) ? ptr_new(uvalue):ptr_new('')
self.uname = (n_elements(uname) ne 0) ? uname:''
return,1B
end
; ************************************************************ ;
pro rmd_text_display__define
void =   {  rmd_text_display,                                  $
            parent:0L,                                         $
            tlb:0L,                                            $
            win:0L,                                            $
            winvis:0L,                                         $
            xsize:0L,                                          $
            ysize:0L,                                          $
            x_scroll_size:0L,                                  $
            y_scroll_size:0L,                                  $
            winbase:0L,                                        $
            uname:'',                                          $
            color_ptr:ptr_new(),                               $
            storage:ptr_new(),                                 $
            text_ptr:ptr_new()                                 }

end
; ************************************************************ ;
function cw_text_display,  parent,                             $
                           obj_ref = obj_ref,                  $
                           _Extra = extra

obj_ref = obj_new('rmd_text_display',  parent,                 $
                                       _Extra = extra          )
ret = obj_ref->build_widget()
obj_ref->get_property, tlb = tlb

return,tlb
end
; ************************************************************ ;
; ***************** EXAMPLE IMPLEMENTATION ******************* ;
; ************************************************************ ;
pro text_display_test_event,event
case widget_info(event.id,/uname) of
'QUIT':     widget_control,event.top,/destroy
'DISPLAY':  $
   begin
      cmd_id = widget_info(event.top,find_by_uname = 'CMD')
      widget_control,cmd_id,set_value = event.value
   end
'CMD':   $
   begin
      ; If not a carriage return then get out
      cr = 10B
      up = 9B
      if event.type ne 0 then return
      if byte(event.ch) eq cr then begin
;         widget_control,event.top,get_uvalue = state,/no_copy
;         state.colors = shift(state.colors,-1)
         id = widget_info(event.top,find_by_uname = 'DISPLAY')
         widget_control,id,get_uvalue = o
;         ret = o->set_color(state.colors[0])
         ; Get the new value
         widget_control,event.id,get_value = value
         widget_control,id,set_value = value
         widget_control,event.id,set_value = ''
;         widget_control,event.top,set_uvalue = state,/no_copy
      endif
      if byte(event.ch) eq up then begin
         display = widget_info(event.top,find_by_uname = 'DISPLAY')
         widget_control,display,get_value = text_array
      endif
   end
else:
endcase
end
; ************************************************************ ;
pro text_display_test
tlb = widget_base(/col,/tlb_frame_attr,mbar = bar)
filemenu = widget_button(bar,value = 'File',/menu)
void = widget_button(filemenu,value = 'Quit',uname = 'QUIT')

id = cw_text_display(tlb,xsize = 400,ysize = 400,              $
   obj_ref = o,uname = 'DISPLAY')
void = widget_text(tlb,/editable,value = '',uname = 'CMD',     $
   /all_events)
widget_control,tlb,/realize
colors = ['red','green','blue','violet red','brown','tomato','purple']

text = ['This is an example implementation','of CW_TEXT_DISPLAY.','']
text = [text,'This compound widget is used for the display of']
text = [text,'text, possibly echoed from a command line.']
text = [text,'','The text can be displayed in different colors.','']
text = [text,'Programmatically the colors can be selected by']
text = [text,'using the SET_COLOR method with the object reference']
text = [text,'for this compound widget.']

text = [text,'','This widget is scrollable allowing more text than can ']
n = n_elements(text)
text[n-1] = text[n-1]+'fit in the window to be viewed.'
n = n_elements(text)
these_colors = replicate('black',n)
these_colors[6] = 'red'
these_colors[8:10] = 'blue'
these_colors[12] = 'purple'
;state = {colors:colors}
widget_control,tlb,set_uvalue = state,/no_copy
widget_control,id,set_uvalue = o
for i = 0,n-1 do ret = o->set_color(these_colors[i])
widget_control,id,set_value = text,set_uvalue = o



new_text = ['This is my new','message for you!']
ret = o->set_color(['blue','red'])
widget_control,id,set_value = new_text
text = strjoin(replicate('G',145))
widget_control,id,set_value = text
xmanager,'text_display_test',tlb,/no_block
end