; CLASS_NAME:
;   kb_tab__define
;
; PURPOSE:
;   This defines the kb_tab plugin module for the Sodasurf package
;   in Solarsoft
;
; CATEGORY:
;   Main Module
;
; SUPERCLASSES:
;       Inherits from the plugin class
;
; SUBCLASSES:
;       List classes that inherit from this class, if any.
;
; CREATION:
;       Note how an object of this class is created.
;       Generally, this is just a reference to the class'
;       Init method.
;
; METHODS:
;       List the methods intrinsic to this class. There is no
;       need to list the inherited methods.
;
;   Update: A procedure for updating the tab in the Display Window belonging
;     to this plugin. This procedure is only called when this tab
;     is the currently selected one in the Display Window.
;
;   Init: For the creation of the tab.
;
;   GetProperty: retrieve information about the kb_tab object.
;
; MODIFICATION HISTORY:
;   Written by:  Mark Cheung, April 2007.
;   April 16th 2007 - Mark Cheung - removed hard coding of
;                     Feature/Event types

PRO kb_table_events, event

   case event.type of
      0: BEGIN
	 widget_control, event.id, get_uvalue=state
	 state.kb_tab_obj->GetProperty, evstruct_ptr = ev_ptr
	 widget_control, state.kb_table_required, get_value=required_values
	 widget_control, state.kb_table_optional, get_value=optional_values
	 tags = tag_names((*ev_ptr).required)
	 FOR I=0,N_ELEMENTS(tags)-1 DO BEGIN
	     (*ev_ptr).required.(I) = required_values[I]
	 ENDFOR
	 tags = tag_names((*ev_ptr).optional)
	 FOR I=0,N_ELEMENTS(tags)-1 DO BEGIN
	     (*ev_ptr).optional.(I) = optional_values[I]
	 ENDFOR

      ENDCASE
      else: BEGIN

      ENDCASE
   END
end


PRO kb_tab_events, event

   widget_control, event.id, get_uvalue = info

   case event.id of        

	info.kb_xml_button:begin
	   widget_control, info.kb_tab, get_uvalue = state

           kml_web_base = './'
           kml_dir      = './'
           xml_web_base = './'
           xml_dir      = './'
           ; Attempt to load template
           file = "~/.kb_template"
           IF file_exist(file) THEN BEGIN
               Nlines = file_lines(file)
               lines = strarr(Nlines)
               openr, lun, file, /get_lun
               readf, lun, lines
               free_lun, lun
   
               FOR I=0,Nlines-1 DO BEGIN
                   pair = strtrim(strsplit(lines[I],'=',/extract),2)

                   IF strcmp(pair[0],'xml_dir', /fold_case) EQ 1 THEN BEGIN
                       xml_dir = strtrim(pair[1], 2)
                       IF strmid(xml_dir,0,/reverse) NE '/' THEN xml_dir = xml_dir + '/'
                   ENDIF
                   IF strcmp(pair[0],'xml_web_base', /fold_case) EQ 1 THEN BEGIN
                       xml_web_base = strtrim(pair[1], 2)
                       IF strmid(xml_web_base,0,/reverse) NE '/' THEN xml_web_base = xml_web_base + '/'
                   ENDIF
                   IF strcmp(pair[0],'kml_dir', /fold_case) EQ 1 THEN BEGIN
                       kml_dir = strtrim(pair[1], 2)
                       IF strmid(kml_dir,0,/reverse) NE '/' THEN kml_dir = kml_dir + '/'
                   ENDIF
                   IF strcmp(pair[0],'kml_web_base', /fold_case) EQ 1 THEN BEGIN
                       kml_web_base = strtrim(pair[1], 2)
                       IF strmid(kml_web_base,0,/reverse) NE '/' THEN kml_web_base = kml_web_base + '/'
                   ENDIF
               ENDFOR
           ENDIF
           info.kb_tab_obj->GetProperty, evstruct_ptr=evstruct_ptr
           prefix  = (strsplit((*(evstruct_ptr)).required.event_type,':',/extract))[0]+ '_' + time2file(!stime,/sec)
           xmlfile = xml_dir+prefix+'.xml'
           kmlfile = kml_dir+prefix+'.kml'
           xml_url = xml_web_base + prefix + '.xml'
           kml_url = kml_web_base + prefix + '.kml'
	   state.kb_tab_obj->WriteXML, outdir=xml_dir, kml_url = kml_url, prefix=prefix
	   ;state.kb_tab_obj->WriteKML, outdir=kml_dir, xml_url = xml_url, prefix=prefix, kml_web_base=kml_web_base
        end
	info.kb_xml_disp_button:begin
	   widget_control, info.kb_tab, get_uvalue = state
	   state.kb_tab_obj->WriteXML, /display, /relax
        end
	info.kb_kml_button:begin
	   widget_control, info.kb_tab, get_uvalue = state
	   state.kb_tab_obj->WriteKML
        end
        info.kb_update_button:begin
	   widget_control, info.kb_tab, get_uvalue = state
	   state.kb_tab_obj->Relist
        end
        info.kb_autofill_button:begin
	   widget_control, info.kb_tab, get_uvalue = state
	   state.kb_tab_obj->Autofill
       end
	info.kb_type_droplist: begin
		; User selected a new Feature/Event type
		widget_control, event.id, get_value=feature_types
		index = widget_info(event.id, /droplist_select)
		evstruct = struct4event(feature_types[index])
		tags        = tag_names(evstruct.required)
		nrows       = N_ELEMENTS(tags)
		row_labels  = strarr(1,nrows)
		row_labels[0,*] = tags
		row_values  = strarr(1,nrows)
		FOR I=0,nrows-1 DO row_values[0,I] = string(evstruct.required.(I))
		editable    = bytarr(1,nrows)
		FOR I=0,nrows-1 DO row_values[0,I] = string(evstruct.required.(I))
		FOR I=0,nrows-1 DO BEGIN
   		   editable[0,I] =  1-strcmp('KB', strtrim((strsplit(tags[I],'_',/extract))[0],2),/fold_case)
   		   IF strcmp(tags[I],'EVENT_TYPE', /fold_case) THEN editable[0,I] = 0
		ENDFOR
		widget_control, info.kb_table_required, table_ysize=nrows
		widget_control, info.kb_table_required, row_labels=tags
		widget_control, info.kb_table_required, set_value=row_values
		widget_control, info.kb_table_required, editable=editable
		tags        = tag_names(evstruct.optional)
		nrows       = N_ELEMENTS(tags)
		row_labels  = strarr(1,nrows)
		row_labels[0,*] = tags
		row_values  = strarr(1,nrows)
		FOR I=0,nrows-1 DO row_values[0,I] = string(evstruct.optional.(I))
		widget_control, info.kb_table_optional, table_ysize=nrows
		widget_control, info.kb_table_optional, row_labels=tags
		widget_control, info.kb_table_optional, set_value=row_values
		widget_control, info.kb_tab, get_uvalue = state
		state.kb_tab_obj->GetProperty, evstruct_ptr=evstruct_ptr
                ptr_free, evstruct_ptr
		state.kb_tab_obj->SetProperty, evstruct_ptr=Ptr_new(evstruct)
	end
	else: begin
	end
   endcase


END
;==================================================================
; METHODNAME:
;       kb_tab::WriteKML
;
; PURPOSE:
;   Export movie frames of bounding box region and creat
;   KML file for use with Google Earth
; SEQUENCE:
;   kb_tab->WriteKML
;
; OPTIONAL INPUTS:
;
; RESTRICTIONS:
;   Describe any "restrictions" here.  Delete this section if there are
;   no important restrictions.
;
; PROCEDURE:
;   You can describe the foobar superfloatation method being used here.
;   You might not need this section for your routine.
;
; EXAMPLE:
;   The following example creates a Display_Window object named DW and retrives the
;   widget ID of the Display Window using the GetProperty function of this object.
;
;   kb_tab = Obj_New("kb_tab", DW=DW)
;   kb_tab->WriteKML
;
; MODIFICATION HISTORY:
;   Written by:  Mark Cheung, April 2007
;   April, 2007   Any additional mods get described here.  Remember to
;      change the stuff above if you add a new keyword or
;      something!

PRO kb_tab::WriteKML, outdir=outdir, prefix=prefix, xml_url=xml_url, kml_web_base=kml_web_base
   evstruct_ptr=self.evstruct_ptr
   self.data_obj->GetProperty,  cube_ptr=cube_ptr, filenames=filenames_ptr
   self.DW->GetProperty, bookmarks=bookmarks, bounding_box=bb
   zaxis   = self.data_obj->axis('z')
   filearr = (*filenames_ptr)

  IF N_ELEMENTS(kml_web_base) EQ 0 THEN kml_web_base = ''
  IF N_ELEMENTS(outdir) EQ 0 THEN outdir = './'
  
  IF N_ELEMENTS(prefix) NE 0 THEN BEGIN
     kmlfile = prefix+'.kml'
  ENDIF ELSE BEGIN
     prefix = (strsplit((*(self.evstruct_ptr)).required.event_type,':',/extract))[0]$
	 + '_' + time2file(!stime,/sec) 
     kmlfile =  prefix+ '.kml'
  ENDELSE
   
   IF bookmarks[2] GT bookmarks[0] THEN BEGIN
       startframe = bookmarks[0]
       endframe = bookmarks[2]
   ENDIF ELSE BEGIN
       startframe = 0
       endframe = N_ELEMENTS((*cube_ptr)[0,0,*])-1
   ENDELSE
   Nframes = endframe - startframe + 1


   ; Determine size of image from bounding box values
   IF bb[0] EQ bb[2] THEN BEGIN
       bb[0] = 0
       bb[2] = N_ELEMENTS((*cube_ptr)[*,0,0])
   ENDIF
   IF bb[1] EQ bb[3] THEN BEGIN
       bb[1] = 0
       bb[3] = N_ELEMENTS((*cube_ptr)[0,*,0])
   ENDIF

   self.data_obj->GetProperty, colormin=colormin, colormax=colormax
   time = anytim(self.data_obj->axis('z'))
   time = time-time[0]
   xaxis = reform((self.data_obj->axis('x'))[0,*])
   yaxis = reform((self.data_obj->axis('y'))[0,*])
   ; Linear fit to x and y axis offsets (time array) to remove jitter
   xlin  = linfit(time,xaxis)
   ylin  = linfit(time,yaxis)   
   smooth_x = xlin[1]*time + xlin[0]
   smooth_y = ylin[1]*time + ylin[0]
   jitter_x = xaxis - smooth_x
   jitter_y = yaxis - smooth_y
   jitter_x[*] = 0.
   jitter_y[*] = 0.
   buff = ['<?xml version="1.0" encoding="UTF-8"?>',$
           '<kml xmlns="http://earth.google.com/kml/2.1">',$
           '<Folder>',$
           '        <name>'+(*(self.evstruct_ptr)).required.event_type+'</name>',$
           '        <description><![CDATA[']

   ; Use event structure entries to make a description of the event
   desc = ['             Start: '+(*(self.evstruct_ptr)).required.event_starttime+'<br>',$
	   '             End  : '+(*(self.evstruct_ptr)).required.event_endtime+'<br>',$
	   '             Instrument: '+(*(self.evstruct_ptr)).required.obs_instrument+'<br>',$
	   '             Observatory: '+(*(self.evstruct_ptr)).required.obs_observatory+'<br>',$
	   '             ChannelID: '+(*(self.evstruct_ptr)).required.obs_channelid+'<br>',$
	   '             FRM Name : '+(*(self.evstruct_ptr)).required.frm_name+'<br>']

   ; Accompanying XML file
   IF N_ELEMENTS(xml_url) NE 0 THEN BEGIN
      desc = [desc,$
           '             Heliophysics Knowledge Base: <a href="'+xml_url+'">VOEvent file</a><br>']

   ENDIF

   buff = [buff,desc,$
	   '                     ]]>',$
	   '        </description>']

   FOR M=startframe,endframe DO BEGIN
      image = (*cube_ptr)[bb[0]:bb[2],bb[1]:bb[3],M]
      ; File name for jpeg file
      splittext = strsplit(filearr[M], '/', /extract)
      fitsfilename = splittext[N_ELEMENTS(splittext)-1]
      firstpart = strsplit(fitsfilename, 'fits', /extract)
      file = prefix + '.' +string(M,format="(I04)") + ".jpg"
      fileppm = prefix + '.' +string(M,format="(I04)") + ".ppm"
      image_url  = kml_web_base+'images/'+file
      ; Scale image by user specified range
      image = bytscl(image,min=(colormin), max=(colormax))
      write_jpeg, outdir+'images/'+file, image
      write_ppm, "/Users/cheung/hinode/movies/"+fileppm, reverse(image,2)
      north = (self.data_obj->coord('y', bb[3], M)-jitter_y[M])/60.
      south = (self.data_obj->coord('y', bb[1], M)-jitter_y[M])/60.
      east  = (self.data_obj->coord('x', bb[2], M)-jitter_x[M])/60.
      west  = (self.data_obj->coord('x', bb[0], M)-jitter_x[M])/60.
      ll = arcmin2hel(west,south, date=strtrim(zaxis[M],2))
      ur = arcmin2hel(east,north, date=strtrim(zaxis[M],2))
      west = ll[1]
      south= ll[0]
      east = ur[1]
      north= ur[0]
      if M EQ 0 THEN placemark = [west,south]
      buff = [buff,$
             '   <GroundOverlay>',$
                     '<altitudeMode>absolute</altitudeMode>',$
                     '<altitude>1000.0</altitude>',$
                     '<name>'+firstpart+'</name>',$
                     '<description>'+firstpart+'</description>',$
                     '<TimeStamp>',$
                     '     <when>'+strtrim(zaxis[M],2)+'</when>',$
                     '</TimeStamp>',$
                     '<Icon>',$
                     '     <href>'+image_url+'</href>',$
                     '</Icon>',$
                     '<LatLonBox id="Heliophysics KB Feature/Event">',$
                     '    <north>'+string(north,format="(e11.4)")+'</north>',$
                     '    <south>'+string(south,format="(e11.4)")+'</south>',$
                     '    <east>'+string(east,format="(e11.4)")+'</east>',$
                     '    <west>'+string(west,format="(e11.4)")+'</west>',$
                     '</LatLonBox>',$
             '   </GroundOverlay>']
  ENDFOR
  buff = [buff,$
          '</Folder>']

;  Create Placemark for Google Earth with Descriptive HTML
;  evptr = self.evstruct_ptr
;  buff = [buff,$
;	  '<Placemark>',$
;	  '    <name>'+(*evptr).required.Event_Type+'</name>',$
;	  '    <description>',$
;	  '        <![CDATA[',$],$
;	  '           <h1>'+(*evptr).required.Event_Type+'</h1>',$
;	  '           <p><font color="red">Active region</font></p>',$
;	  '        ]]>',$
;	  '    </description>']
;  ; Position of placemark
;  placemark = strtrim(string(placemark, format='(e11.4)'),2)
;  placemark = placemark[1]+','+placemark[0]
;  buff = [buff,$
;	  '     <Point>',$
;	  '          <coordinates>' +placemark+ '</coordinates>',$
;	  '     </Point>',$
;	  '</Placemark>']

  buff = [buff,$
          '</kml>']

  ; Now write out KML file.
  file_append, outdir+kmlfile, buff, /newfile
  ;url = kml_web_base+kmlfile
END

;==================================================================
; METHODNAME:
;       kb_tab::WriteXML
;
; PURPOSE:
;   Output event structure to XML file for Heliophysics Knowledge Base
;
; CALLING SEQUENCE:
;   kb_tab->WriteXML
;
; OPTIONAL INPUTS:
;
; RESTRICTIONS:
;   Describe any "restrictions" here.  Delete this section if there are
;   no important restrictions.
;
; PROCEDURE:
;   You can describe the foobar superfloatation method being used here.
;   You might not need this section for your routine.
;
; EXAMPLE:
;   The following example creates a Display_Window object named DW and retrives the
;   widget ID of the Display Window using the GetProperty function of this object.
;
;   kb_tab = Obj_New("kb_tab", DW=DW)
;   kb_tab->WriteXML
;
; MODIFICATION HISTORY:
;   Written by:  Mark Cheung, April 2007
;   April, 2007   Any additional mods get described here.  Remember to
;      change the stuff above if you add a new keyword or
;      something!

PRO kb_tab::WriteXML, display=display, relax=relax, outdir=outdir, kml_url=kml_url, prefix=prefix
   self->GetProperty, evstruct_ptr=evstruct_ptr
   widget_control, self.tab_ID, get_uvalue=state
   widget_control, state.kb_xml_text, get_value=directory

   

   type_vec = ['string' ,'byte','integer','long' ,'float'  ,'double' ,'undefined']
   val_vec  = ['"blank"','0b'  ,'-9999'  ,'-999999l','!VALUES.F_Infinity','!VALUES.D_Infinity','"-"'      ]
   idl_type_vec=[7,1,2,3,4,5,0]

   ; Check that all required tags have indeed been filled in (default
   ; values from template not acceptable unless relax keyword is specified)
   error_buff=['']
   these_tags = tag_names((*(evstruct_ptr)).required)
   for I=0,n_elements(these_tags)-1 do begin
       temp_index = where( (size((*(evstruct_ptr)).required.(I)))[1] EQ idl_type_vec) 
       var_type = type_vec[temp_index]
       case (size((*(evstruct_ptr)).required.(I)))[1] of
           1: if (*(evstruct_ptr)).required.(i) EQ 0b then error_buff=[error_buff, string(these_tags[I])+ "=" +string((*(evstruct_ptr)).required.(I))]
           2: if (*(evstruct_ptr)).required.(i) EQ -9999 then error_buff=[error_buff, string(these_tags[I])+ "=" +string((*(evstruct_ptr)).required.(I))]
           3: if (*(evstruct_ptr)).required.(i) EQ -999999l then error_buff=[error_buff, string(these_tags[I])+ "=" +string((*(evstruct_ptr)).required.(I))]
           4: if (*(evstruct_ptr)).required.(i) EQ !VALUES.F_Infinity then error_buff=[error_buff, string(these_tags[I])+ $
                                                                           "= " +string((*(evstruct_ptr)).required.(I))]
           5: if (*(evstruct_ptr)).required.(i) EQ !VALUES.D_Infinity then error_buff=[error_buff, string(these_tags[I])+ $
                                                                           "= " +string((*(evstruct_ptr)).required.(I))]
           7: if (*(evstruct_ptr)).required.(i) EQ "blank" then error_buff=[error_buff, string(these_tags[I])+ "=" +string((*(evstruct_ptr)).required.(I))]
           else: begin
           endelse
       endcase
   endfor

   IF N_ELEMENTS(error_buff) GT 1 THEN BEGIN
       IF KEYWORD_SET(relax) EQ 0 THEN BEGIN
           error_buff = ["Error: The following required tags must be given proper values.",error_buff]
           error_buff = ["---------------------------------------------------------------------",error_buff]
           error_buff = [error_buff,"---------------------------------------------------------------------"]
           error_buff = [error_buff,"No xml files were produced."]
           prstr, error_buff
           return
       ENDIF ELSE BEGIN
           error_buff = ["Warning: The following required tags must be given proper values.",error_buff]
           error_buff = ["---------------------------------------------------------------------",error_buff]
           error_buff = [error_buff,"---------------------------------------------------------------------"]
           error_buff = [error_buff,"Nevertheless, the XML code was displayed and/or written as requested."]
           prstr, error_buff
       ENDELSE
   ENDIF

   IF KEYWORD_SET(display) THEN BEGIN
        export_event, *(self.evstruct_ptr), /display, /relax
        return
    ENDIF

   ;Check if an accompanying kml_url is available, if so, get that to Event.optional.Event_MapURL
   IF N_ELEMENTS(kml_url) NE 0 THEN (*(self.evstruct_ptr)).optional.event_mapurl = kml_url

   ; KlugeStart:
   IF N_ELEMENTS(prefix) NE 0 THEN xmlfile = prefix+'.xml'

   ;IF N_ELEMENTS(xmlfile) EQ 0 THEN xmlfile =  (strsplit((*(self.evstruct_ptr)).required.event_type,':',/extract))[0]$
   ;	 + '_' + time2file(!stime,/sec) + '.xml'
   
   ;IF strcmp(directory, 'Automatic', /fold_case) EQ 0 THEN BEGIN
      export_event, *(self.evstruct_ptr), outdir=outdir, /write, outfil=xmlfile
   ;ENDIF ELSE BEGIN
   ;   export_event, *(self.evstruct_ptr), /write, outfil=xmlfile
   ;ENDELSE
END

;==================================================================
; METHODNAME:
;       kb_tab::Autofill

;
; PURPOSE:
;   Try to automatically fill in values for the form
;
; CALLING SEQUENCE:
;   kb_tab->Autofill
;
; OPTIONAL INPUTS:
;
; RESTRICTIONS:
;   Describe any "restrictions" here.  Delete this section if there are
;   no important restrictions.
;
; PROCEDURE:
;   You can describe the foobar superfloatation method being used here.
;   You might not need this section for your routine.
;
; EXAMPLE:
;   The following example creates a Display_Window object named DW and retrives the
;   widget ID of the Display Window using the GetProperty function of this object.
;
;   kb_tab = Obj_New("kb_tab", DW=DW)
;   kb_tab->Autofill
;
; MODIFICATION HISTORY:
;   Written by:  Mark Cheung, April 2007
;   April, 2007   
;   23rd Oct 2007 - Added "COMMON SODASURF_BLOCK, evstr" line
;                 - Save event structure to evstr

PRO kb_tab::Autofill

COMMON SODASURF_BLOCK, evstr

ev_ptr = self.evstruct_ptr
self.DW->GetProperty, bounding_box=bounding_box, bookmarks=bookmarks
self.data_obj->GetProperty, cat_ptr=cat_ptr

; Update bounding box values
IF (bounding_box[0] NE bounding_box[2]) AND (bounding_box[1] NE bounding_box[3]) THEN BEGIN
   (*(self.evstruct_ptr)).required.boundbox_c1ll = (self.data_obj)->coord('x',bounding_box[0],0)
   (*(self.evstruct_ptr)).required.boundbox_c2ll = (self.data_obj)->coord('y',bounding_box[1],0)
   (*(self.evstruct_ptr)).required.boundbox_c1ur = (self.data_obj)->coord('x',bounding_box[2],0)
   (*(self.evstruct_ptr)).required.boundbox_c2ur = (self.data_obj)->coord('y',bounding_box[3],0)
ENDIF

time = strsplit(systime(), ' ', /extract)

; Attempt to load template
file = "~/.kb_template"
IF file_exist(file) THEN BEGIN
   Nlines = file_lines(file)
   lines = strarr(Nlines)
   openr, lun, file, /get_lun
   readf, lun, lines
   free_lun, lun
   
   FOR I=0,Nlines-1 DO BEGIN
	pair = strtrim(strsplit(lines[I],'=',/extract),2)
	tags = tag_names((*ev_ptr).required)
	struct_match = where(strcmp(tags,pair[0],/fold_case) EQ 1)
	IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = pair[1]
	tags = tag_names((*ev_ptr).optional)
	struct_match = where(strcmp(tags,pair[0],/fold_case) EQ 1)
	IF struct_match[0] NE -1 THEN (*ev_ptr).optional.(struct_match) = pair[1]

        IF strcmp(pair[0],'xml_directory', /fold_case) EQ 1 THEN BEGIN
	   widget_control, self.tab_ID, get_uvalue=state
           widget_control, state.kb_xml_text, set_value = pair[1]
        ENDIF
    ENDFOR
ENDIF



tags = tag_names((*ev_ptr).required)
; FRM_DateRun
match = where(strcmp(tags,'FRM_DateRun',/fold_case) EQ 1)
IF match[0] NE -1 THEN (*ev_ptr).required.FRM_DateRun = anytim(!stime, /ccsds);time[4]+'-'+time[1]+'-'+time[2]

; FRM_DateRun
match = where(strcmp(tags,'FRM_HumanFlag',/fold_case) EQ 1)
IF match[0] NE -1 THEN (*ev_ptr).required.(match) = 'True'

; OBS_Observatory
struct_match = where(strcmp(tags,'Obs_Observatory',/fold_case) EQ 1)
fits_match  = where(strcmp(tag_names(*cat_ptr), 'Telescop', /fold_case) EQ 1)
if fits_match[0] NE -1 AND struct_match NE -1 THEN $
	(*ev_ptr).required.(struct_match) = ((*cat_ptr)[0]).(fits_match)

; OBS_Instrument
struct_match = where(strcmp(tags,'Obs_Instrument',/fold_case) EQ 1)
fits_match  = where(strcmp(tag_names(*cat_ptr), 'Instrume', /fold_case) EQ 1)
if fits_match[0] NE -1 AND struct_match NE -1 THEN $
	(*ev_ptr).required.(struct_match) = ((*cat_ptr)[0]).(fits_match)

; OBS_CHANNELID
channelid=''
struct_match = where(strcmp(tags,'OBS_ChannelID',/fold_case) EQ 1)
fits_match  = where(strcmp(tag_names(*cat_ptr), 'WAVE', /fold_case) EQ 1)
if fits_match[0] NE -1 THEN channelid = ((*cat_ptr)[0]).(fits_match)+':'
fits_match = where(strcmp(tag_names(*cat_ptr), 'OBS_TYPE', /fold_case) EQ 1)
if fits_match[0] NE -1 THEN channelid = channelid+((*cat_ptr)[0]).(fits_match)
if struct_match[0] NE -1 AND channelid NE '' THEN $
	(*ev_ptr).required.(struct_match) = channelid

; OBS_MeanWaveL
struct_match = where(strcmp(tags,'OBS_MeanWaveL',/fold_case) EQ 1)
fits_match  = where(strcmp(tag_names(*cat_ptr), 'WAVE', /fold_case) EQ 1)
if fits_match[0] NE -1 AND struct_match[0] NE -1 THEN BEGIN
    channel = strsplit(((*cat_ptr)[0]).(fits_match),' ', /extract)
    channel = channel[N_ELEMENTS(channel)-1]
    (*ev_ptr).required.(struct_match) = channel
endif

; Event_Starttime
struct_match = where(strcmp(tags,'Event_Starttime',/fold_case) EQ 1)
fits_match  = where(strcmp(tag_names(*cat_ptr), 'Date_Obs', /fold_case) EQ 1)
if fits_match[0] NE -1 AND struct_match NE -1 THEN BEGIN
    time = strsplit(((*cat_ptr)[bookmarks[0]]).(fits_match),' ', /extract)
    ;time = time[N_ELEMENTS(time)-1]
    ;time = strsplit(time, 'T', /extract)
    ;time = time[0] + ' ' + time[1]
    (*ev_ptr).required.(struct_match) = time
endif

; Event_Endtime
struct_match = where(strcmp(tags,'Event_Endtime',/fold_case) EQ 1)
fits_match  = where(strcmp(tag_names(*cat_ptr), 'Date_Obs', /fold_case) EQ 1)
if fits_match[0] NE -1 AND struct_match NE -1 THEN BEGIN
    time = strsplit(((*cat_ptr)[bookmarks[2]]).(fits_match),' ', /extract)
    ;time = time[N_ELEMENTS(time)-1]
    ;time = strsplit(time, 'T', /extract)
    ;time = time[0] + ' ' + time[1]
    (*ev_ptr).required.(struct_match) = time
endif

; Event_Peaktime
struct_match = where(strcmp(tags,'Event_Peaktime',/fold_case) EQ 1)
fits_match  = where(strcmp(tag_names(*cat_ptr), 'Date_Obs', /fold_case) EQ 1)
if fits_match[0] NE -1 AND struct_match NE -1 THEN BEGIN
    time = strsplit(((*cat_ptr)[bookmarks[1]]).(fits_match),' ', /extract)
    ;time = time[N_ELEMENTS(time)-1]
    ;time = strsplit(time, 'T', /extract)
    ;time = time[0] + ' ' + time[1]
    (*ev_ptr).required.(struct_match) = time
endif

; Update bounding box values
IF (bounding_box[0] NE bounding_box[2]) AND (bounding_box[1] NE bounding_box[3]) THEN BEGIN
   fovx = 0
   fovy = 0
   xcen = ((*(self.evstruct_ptr)).required.boundbox_c1ll+(*(self.evstruct_ptr)).required.boundbox_c1ur)/2
   ycen = ((*(self.evstruct_ptr)).required.boundbox_c2ll+(*(self.evstruct_ptr)).required.boundbox_c2ur)/2
   struct_match = where(strcmp(tags,'Event_Coord1',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = xcen
   struct_match = where(strcmp(tags,'Event_Coord2',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = ycen
   struct_match = where(strcmp(tags,'boundbox_c1ll',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = self.data_obj->coord('x',round(bounding_box[0]),0)
   struct_match = where(strcmp(tags,'boundbox_c2ll',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = self.data_obj->coord('y',round(bounding_box[1]),0)
   struct_match = where(strcmp(tags,'boundbox_c1ur',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = self.data_obj->coord('x',round(bounding_box[2]),0)
   struct_match = where(strcmp(tags,'boundbox_c2ur',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = self.data_obj->coord('y',round(bounding_box[3]),0)
   struct_match = where(strcmp(tags,'event_c1error',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = $
     (self.data_obj->coord('x',round(bounding_box[2]),0)-self.data_obj->coord('x',round(bounding_box[0]),0))/2.
   struct_match = where(strcmp(tags,'event_c2error',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = $
     (self.data_obj->coord('y',round(bounding_box[3]),0)-self.data_obj->coord('y',round(bounding_box[1]),0))/2.
ENDIF ELSE BEGIN
   fovx = 0
   fovy = 0
   ;xcen = ((*(self.evstruct_ptr)).required.boundbox_c1ll+(*(self.evstruct_ptr)).required.boundbox_c1ur)/2
   ;ycen = ((*(self.evstruct_ptr)).required.boundbox_c2ll+(*(self.evstruct_ptr)).required.boundbox_c2ur)/2
   fits_match  = where(strcmp(tag_names(*cat_ptr), 'XCEN', /fold_case) EQ 1)
   IF fits_match[0] NE -1 THEN xcen = ((*cat_ptr)[bookmarks[0]]).(fits_match)
   fits_match  = where(strcmp(tag_names(*cat_ptr), 'YCEN', /fold_case) EQ 1)
   IF fits_match[0] NE -1 THEN ycen = ((*cat_ptr)[bookmarks[0]]).(fits_match)
   struct_match = where(strcmp(tags,'Event_Coord1',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = xcen
   struct_match = where(strcmp(tags,'Event_Coord2',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = ycen
   struct_match = where(strcmp(tags,'boundbox_c1ll',/fold_case) EQ 1)
   fits_match  = where(strcmp(tag_names(*cat_ptr), 'FOVX', /fold_case) EQ 1)
   IF fits_match[0] NE -1 THEN fovx = ((*cat_ptr)[bookmarks[0]]).(fits_match)
   fits_match  = where(strcmp(tag_names(*cat_ptr), 'FOVY', /fold_case) EQ 1)
   IF fits_match[0] NE -1 THEN fovy = ((*cat_ptr)[bookmarks[0]]).(fits_match)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = xcen - fovx/2
   struct_match = where(strcmp(tags,'boundbox_c2ll',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = ycen - fovy/2
   struct_match = where(strcmp(tags,'boundbox_c1ur',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = xcen + fovx/2
   struct_match = where(strcmp(tags,'boundbox_c2ur',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = ycen + fovy/2
   struct_match = where(strcmp(tags,'event_c1error',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = fovx/2
   struct_match = where(strcmp(tags,'event_c2error',/fold_case) EQ 1)
   IF struct_match[0] NE -1 THEN (*ev_ptr).required.(struct_match) = fovy/2

ENDELSE

self->Relist
; Save to common variable evstr
evstr = *self.evstruct_ptr

END


;==================================================================
; METHODNAME:
;       kb_tab::Relist
;
; PURPOSE:
;   Relist the values for structure
;
; CALLING SEQUENCE:
;   kb_tab->Relist
;
; OPTIONAL INPUTS:
;
; RESTRICTIONS:
;   Describe any "restrictions" here.  Delete this section if there are
;   no important restrictions.
;
; PROCEDURE:
;   You can describe the foobar superfloatation method being used here.
;   You might not need this section for your routine.
;
; EXAMPLE:
;   The following example creates a Display_Window object named DW and retrives the
;   widget ID of the Display Window using the GetProperty function of this object.
;
;   kb_tab = Obj_New("kb_tab", DW=DW)
;   kb_tab->Relist
;
; MODIFICATION HISTORY:
;   Written by:  Mark Cheung, April 2007
;   April, 2007   Any additional mods get described here.  Remember to
;      change the stuff above if you add a new keyword or
;      something!

PRO kb_tab::Relist


tags        = tag_names((*(self.evstruct_ptr)).required)
nrows       = N_ELEMENTS(tags)
row_labels  = strarr(1,nrows)
row_labels[0,*] = tags
row_values  = strarr(1,nrows)
FOR I=0,nrows-1 DO row_values[0,I] = string((*(self.evstruct_ptr)).required.(I))
editable    = bytarr(1,nrows)
FOR I=0,nrows-1 DO BEGIN
  editable[0,I] =  1-strcmp('KB', strtrim((strsplit(tags[I],'_',/extract))[0],2),/fold_case)
   IF strcmp(tags[I],'EVENT_TYPE', /fold_case) THEN editable[0,I] = 0
ENDFOR
widget_control, self.table_required, table_ysize=nrows
widget_control, self.table_required, row_labels=tags
widget_control, self.table_required, set_value=row_values
widget_control, self.table_required, editable=editable

tags        = tag_names((*(self.evstruct_ptr)).optional)
nrows       = N_ELEMENTS(tags)
row_labels  = strarr(1,nrows)
row_labels[0,*] = tags
row_values  = strarr(1,nrows)
FOR I=0,nrows-1 DO row_values[0,I] = string((*(self.evstruct_ptr)).optional.(I))
widget_control, self.table_optional, table_ysize=nrows
widget_control, self.table_optional, row_labels=tags
widget_control, self.table_optional, set_value=row_values


end


;==================================================================
; METHODNAME:
;       kb_tab::Refresh
;
; PURPOSE:
;   For Refreshing the tab. This method is usually called upon
;   events generated by tabs in the Display Window.
;   For example, when the user selects a different tab,
;   the Refresh procedure for this tab is called.
;
;   In contrast to kb_tab::Refresh, kb_tab::Update is usually called
;   upon movie_player events like moving the slider.
;
; CALLING SEQUENCE:
;   kb_tab->Refresh
;
; OPTIONAL INPUTS:
;
; RESTRICTIONS:
;   Describe any "restrictions" here.  Delete this section if there are
;   no important restrictions.
;
; PROCEDURE:
;   You can describe the foobar superfloatation method being used here.
;   You might not need this section for your routine.
;
; EXAMPLE:
;   The following example creates a Display_Window object named DW and retrives the
;   widget ID of the Display Window using the GetProperty function of this object.
;
;   kb_tab = Obj_New("kb_tab", DW=DW)
;   kb_tab->Update
;
; MODIFICATION HISTORY:
;   Written by:  Mark Cheung, April 2007
;   April, 2007   Any additional mods get described here.  Remember to
;      change the stuff above if you add a new keyword or
;      something!

PRO kb_tab::Refresh, resize=resize

IF N_ELEMENTS(resize) EQ 0 THEN resize=0

; Error handling for all trapped errors.
Catch, theError
IF theError NE 0 THEN BEGIN
   Catch, /Cancel
   ok = Error_Message(kbback=1, /Error)
   RETURN
ENDIF

self->Update

END


;==================================================================
; METHODNAME:
;       kb_tab::Update
;
; PURPOSE:
;   For updating the kb tab in the Display Window if it is currently selected
;   and some event (user-driven or not) occurs.
;
; CALLING SEQUENCE:
;   kb_tab->Update
;
; OPTIONAL INPUTS:
;
; RESTRICTIONS:
;   Describe any "restrictions" here.  Delete this section if there are
;   no important restrictions.
;
; PROCEDURE:
;   You can describe the foobar superfloatation method being used here.
;   You might not need this section for your routine.
;
; EXAMPLE:
;   The following example creates a Display_Window object named DW and retrives the
;   widget ID of the Display Window using the GetProperty function of this object.
;
;   kb_tab = Obj_New("kb_tab", DW=DW)
;   kb_tab->Update
;
; MODIFICATION HISTORY:
;   Written by:  Mark Cheung, April 2007
;   April, 2007   Any additional mods get described here.  Remember to
;      change the stuff above if you add a new keyword or
;      something!

PRO kb_tab::Update

; Error handling for all trapped errors.
Catch, theError
IF theError NE 0 THEN BEGIN
   Catch, /Cancel
   ok = Error_Message(kbback=1, /Error)
   RETURN
ENDIF

END

; METHODNAME:
;       kb_tab::SetProperty
;
; PURPOSE:
;   For modifying the attributes of a kb_tab class object
;
; CALLING SEQUENCE:
;   kb_tab = Obj_New("kb_tab", DW=DW)
;   kb_tab->SetProperty, plugin_name=plugin_name
;
; OPTIONAL INPUTS:
;   plugin_name : The name of this plugin
;   profiles_window: The widget ID of the any profiles window associated
;   with this kb tab.
;
; RESTRICTIONS:
;   Describe any "restrictions" here.  Delete this section if there are
;   no important restrictions.
;
; PROCEDURE:
;   You can describe the foobar superfloatation method being used here.
;   You might not need this section for your routine.
;
; EXAMPLE:
;   The following example creates a Display_Window object named DW and retrives the
;   widget ID of the Display Window using the GetProperty function of this object.
;
;   kb_tab = Obj_New("kb_tab", DW=DW)
;   kb_tab->SetProperty, plugin_name="New kb Tab"
;
; MODIFICATION HISTORY:
;   Written by:  Mark Cheung, April 2007
;   April, 2007   Any additional mods get described here.  Remember to
;      change the stuff above if you add a new keyword or
;      something!

PRO kb_tab::SetProperty, $
    plugin_name = plugin_name, $
    tab_size=tab_size, $
    evstruct_ptr = evstruct_ptr
;Error Handling
Catch, theError
IF theError NE 0 THEN BEGIN
   Catch, Cancel=1
   ok = Dialog_Message('Error in SetProperty Method. Returning...')
   Print, ''
   Print, 'kb_tab::SetProperty Method: ' + !Error_State.Msg
ENDIF

IF N_ELEMENTS(plugin_name) NE 0 THEN self.plugin_name=plugin_name
IF N_ELEMENTS(tab_size) EQ 2 THEN self.tab_size=tab_size
IF N_ELEMENTS(evstruct_ptr) NE 0 THEN self.evstruct_ptr = evstruct_ptr

END

; METHODNAME:
;       kb_tab::GetProperty
;
; PURPOSE:
;   For the retrieval of information about the kb_tab object
;
; CALLING SEQUENCE:
;   kb_tab = Obj_New("kb_tab", DW=DW)
;   kb_tab->GetProperty, plugin_name=plugin_name, ...
;
; OPTIONAL INPUTS:
;   plugin_name : Returns the name of this plugin
;   tab_ID: Returns the widget ID of the tab associated with this plugin
;   drawID: Returns the widget ID of the draw widget contained in the kb tab.
;   DW : Returns an object pointer to the Display Window object to which
;     this plugin is attached.
;
; RESTRICTIONS:
;   Describe any "restrictions" here.  Delete this section if there are
;   no important restrictions.
;
; PROCEDURE:
;   You can describe the foobar superfloatation method being used here.
;   You might not need this section for your routine.
;
; EXAMPLE:
;   The following example creates a Display_Window object named DW and retrives the
;   widget ID of the Display Window using the GetProperty function of this object.
;
;   kb_tab = Obj_New("kb_tab", DW=DW)
;   kb_tab->GetProperty, tab_ID=tab_ID, plugin_name=name
;
; MODIFICATION HISTORY:
;   Written by:  Mark Cheung, April 2007
;   April, 2007   Any additional mods get described here.  Remember to
;      change the stuff above if you add a new keyword or
;      something!

PRO kb_tab::GetProperty, $
    plugin_name = plugin_name, $
    tab_ID = tab_ID, $
    DW = DW, $
    tab_size=tab_size, $
    rank=rank, $
    table_required=table_required, $
    table_optional=table_optional, $
    evstruct_ptr = evstruct_ptr

;Error Handling
Catch, theError
IF theError NE 0 THEN BEGIN
   Catch, Cancel=1
   ok = Dialog_Message('Error in GetProperty Method. Returning...')
   Print, ''
   Print, 'kb_tab::GetProperty Method: ' + !Error_State.Msg
ENDIF

plugin_name = self.plugin_name
tab_ID = self.tab_ID
DW = self.DW
tab_size=self.tab_size
data_obj=self.data_obj
rank    = self.rank
table_required  = self.table_required
table_optional  = self.table_optional
evstruct_ptr    = self.evstruct_ptr
END

; METHODNAME:
;       kb_tab::Init
;
; PURPOSE:
;   For the creation of a the kb_tab plugin and it's tab in a Display Window
;
; CALLING SEQUENCE:
;   kb_tab = Obj_New("kb_tab", DW=DW)
;
; INPUTS:
;   DW: The Display Window Object to which this plugin is attached.
;
; OPTIONAL INPUTS:
;
; KEYWORD PARAMETERS:
;   KEY1: Document keyword parameters like this. Note that the keyword
;     is shown in ALL CAPS!
;
; OPTIONAL OUTPUTS:
;   Describe optional outputs here.  If the routine doesn't have any,
;   just delete this section.
;
; COMMON BLOCKS:
;   BLOCK1:   Describe any common blocks here. If there are no COMMON
;     blocks, just delete this entry. Object methods probably
;               won't be using COMMON blocks.
;
; SIDE EFFECTS:
;   Describe "side effects" here.  There aren't any?  Well, just delete
;   this entry.
;
; RESTRICTIONS:
;   Describe any "restrictions" here.  Delete this section if there are
;   no important restrictions.
;
; PROCEDURE:
;   You can describe the foobar superfloatation method being used here.
;   You might not need this section for your routine.
;
; EXAMPLE:
;   The following example creates a Display_Window object named DW and retrives the
;   widget ID of the Display Window using the GetProperty function of this object.
;
;   kb_tab = Obj_New("kb_tab", DW=DW)
;
; MODIFICATION HISTORY:
;   Written by:  Mark Cheung, April 2007
;   April, 2007   Any additional mods get described here.  Remember to
;      change the stuff above if you add a new keyword or
;      something!

FUNCTION kb_tab::INIT, $
    DW=DW, rank=rank

;Error Handling
Catch, theError
IF theError NE 0 THEN BEGIN
   Catch, Cancel=1
   ok = Dialog_Message('Error in INIT Method. Returning...')
   Print, ''
   Print, 'data_set::INIT Method: ' + !Error_State.Msg
   RETURN, 0
ENDIF

IF N_ELEMENTS(tab_ID) EQ 0 THEN tab_ID=0L
IF N_ELEMENTS(rank) EQ 0 THEN rank=0

green = [209,255,119]
red   = [255,97,97]

;=====================kb tab widget========================
DW->GetProperty, xsize=xsize, $
    ysize=ysize, $
    DW_ID=DW_ID, $
    DW_tabs=DW_tabs, $
    mbarID=mbarID, data_obj=data_obj

(data_obj[rank])->GetProperty, variable_name=variable_name
kb_tab = widget_base(title='HelioKB Report: '+variable_name, DW_tabs, xsize=xsize, $
    ysize=ysize, xoffset=0, yoffset=0, /context_events)

kb_tab_context_menu = widget_base(kb_tab, /context_menu)

defevent='VOEvent_Spec.txt'
local=concat_dir(curdir(),defevent)
sswdef=concat_dir('$SSW_ONTOLOGY_DATA',defevent)

case 1 of
   data_chk(infil_params,/string):   ; user supplied
   file_exist(local): infil_params = local
   file_exist(sswdef): infil_params = sswdef
   else: begin 
      box_message,'Cannot find event file> ' + sswdef
      return,-1
   endcase
endcase

; Read in Spec Table
lines = strarr(file_lines(infil_params))
openr, lun,/get_lun, infil_params
readf, lun, lines
free_lun, lun
lines = lines[2:*]
ncols = n_elements(strsplit(lines[0],',',/extract))
buff0 = strarr(ncols, n_elements(lines))
FOR I=0,N_ELEMENTS(lines)-1 DO buff0[*,I] = strsplit(lines[i],',',/extract)
buff1 = buff0[*,2:*]
feat_code_vec = strtrim(buff0[*,0],2)
feat_code_arr = feat_code_vec
feat_name_arr = strtrim(buff0[*,1],2)
correct_code  = where(strlen(feat_code_vec) EQ 2)
feat_name_arr = feat_name_arr[correct_code]
feat_code_arr = feat_code_arr[correct_code]

kb_type_label       = widget_label(kb_tab, value="Feature/Event Type", xoffset=10, yoffset=20)
kb_type_droplist    = widget_droplist(kb_tab, value=feat_name_arr, xsize=250, xoffset=120, yoffset=10,$
			event_pro="kb_tab_events")
kb_update_button    = widget_button(kb_tab, value="Refresh", xoffset=400, yoffset=15, xsize=80,$
			event_pro="kb_tab_events")
kb_autofill_button    = widget_button(kb_tab, value="Autofill", xoffset=300, yoffset=15, xsize=80,$
			event_pro="kb_tab_events")
kb_xml_label      = widget_label(kb_tab, value="XML output dir:", xoffset=10, yoffset=60)
kb_xml_text       = widget_text(kb_tab, value="Automatic", xoffset=120, yoffset=50, xsize=40, /editable)
kb_xml_button     = widget_button(kb_tab, value="Generate XML file", xoffset=400, yoffset=50,$
			event_pro="kb_tab_events")
kb_xml_disp_button     = widget_button(kb_tab, value="Display XML file", xoffset=530, yoffset=50,$
			event_pro="kb_tab_events")
kb_kml_button     = widget_button(kb_tab, value="Export KML file", xoffset=660, yoffset=50,$
			event_pro="kb_tab_events")

evstruct    = struct4event('AR')
tags        = tag_names(evstruct.required)
nrows       = N_ELEMENTS(tags)
row_values  = strarr(1,nrows)
editable    = bytarr(1,nrows)
FOR I=0,nrows-1 DO row_values[0,I] = string(evstruct.required.(I))
FOR I=0,nrows-1 DO BEGIN
   editable[0,I] =  1-strcmp('KB', strtrim((strsplit(tags[I],'_',/extract))[0],2),/fold_case)
   IF strcmp(tags[I],'EVENT_TYPE', /fold_case) THEN editable[0,I] = 0
ENDFOR
longest_label_len = max(strlen(tags))

yoffset = 100
kb_table_required = widget_table(kb_tab, value=row_values,$
	row_labels=tags, column_labels=["Required attributes"], column_widths=250,$
	xsize=1, scr_ysize=ysize-yoffset-100, scr_xsize=400, yoffset=yoffset, /resizeable_columns,$
	background_color=red, /scroll, editable=editable, event_pro='kb_table_events')

tags        = tag_names(evstruct.optional)
nrows       = N_ELEMENTS(tags)
row_labels  = strarr(1,nrows)
row_labels[0,*] = tags
row_values = strarr(1,nrows)
FOR I=0,nrows-1 DO row_values[0,I] = string(evstruct.optional.(I))
longest_label_len = max(strlen(tags))

kb_table_optional = widget_table(kb_tab, value=row_values,$
	row_labels=tags, column_labels=["Optional attributes"], column_widths=250,$
	xsize=1, scr_ysize=ysize-yoffset-100, scr_xsize=400, xoffset=400, yoffset=yoffset, /resizeable_columns,$
	background_color=green, /scroll, /editable, event_pro='kb_table_events')


kb_tab_state = {kb_tab:kb_tab, kb_tab_obj:self, kb_table_required:kb_table_required,$
		kb_table_optional:kb_table_optional, tab_object:self,$
		kb_type_droplist:kb_type_droplist, kb_update_button:kb_update_button,$
		kb_autofill_button:kb_autofill_button, $
		kb_xml_text:kb_xml_text, kb_xml_button:kb_xml_button, $
                kb_xml_disp_button:kb_xml_disp_button, kb_kml_button:kb_kml_button}

widget_control, kb_tab, set_uvalue = kb_tab_state
widget_control, kb_type_droplist, set_uvalue = kb_tab_state
widget_control, kb_update_button, set_uvalue = kb_tab_state
widget_control, kb_autofill_button, set_uvalue = kb_tab_state
widget_control, kb_xml_text, set_uvalue = kb_tab_state
widget_control, kb_xml_button, set_uvalue = kb_tab_state
widget_control, kb_xml_disp_button, set_uvalue = kb_tab_state
widget_control, kb_kml_button, set_uvalue = kb_tab_state
widget_control, kb_table_required, set_uvalue = kb_tab_state
widget_control, kb_table_optional, set_uvalue = kb_tab_state

self.plugin_name='KnowledgeBase Tab'
self.tab_ID         = kb_tab
self.DW             = DW
self.table_required = kb_table_required
self.table_optional = kb_table_optional
self.rank           = rank
self.data_obj       = data_obj[self.rank] ; data_obj is pointer to data obj, which is carried by the Display Window. 
self.tab_size       = [xsize, ysize]
self.evstruct_ptr   = Ptr_new(evstruct, /no_copy)


RETURN, 1
END

PRO kb_tab__define

struct = {kb_tab, $
    table_required:0l,$              ; The widget ID of the table widget with required tags as rows.
    table_optional:0l,$              ; The widget ID of the table widget with optional tags as rows.
    rank:0l,$		    ; Rank within Display Window. Determines which data_obj to access.
    data_obj:obj_new(), $   ; Data_obj
    evstruct_ptr:Ptr_new(), $   ; Pointer to Knowledge Base event structure (ontology package)
    INHERITS plugin $
    }
END
