function charpanel(propeditor,h,charstruct,varargin)   

import com.mathworks.mwswing.*;
import javax.swing.*;
import javax.swing.table.*
import java.awt.*;
import com.mathworks.page.utils.VertFlowLayout;
import com.mathworks.toolbox.timeseries.*;

%% Method which builds/populates the char panel on the Property Editor

%% Assemble the char table data by traversing each characteristic
charlist = charstruct.charlist;
additionalDataProps = charstruct.additionalDataProps;
additionalDataPropDefaults = charstruct.additionalDataPropDefaults;
additionalHeadings = charstruct.additionalHeadings;
waves = h.allwaves;

%% Build char table data 
tableData = cell(0,length(additionalHeadings)+2);
for row=1:size(charlist,1)
    if localfindchar(h,charlist{row,:})           
        varchar = find(waves(1).Characteristics,'Identifier',charlist{row,1});
        if ~isempty(varchar)
            additionalCells = cell(1,length(additionalDataProps)); % Adds cells for start/end time st
            for k=1:length(additionalDataProps)
                additionalCells{k} = sprintf('%0.2g',get(varchar.Data,additionalDataProps{k}));
            end
            tableData = [{strcmp(varchar.Visible,'on'),charlist{row,1}} additionalCells];
        else 
            tableData = [{false,charlist{row,1}} additionalDataPropDefaults];
        end
    else
        tableData = [tableData; ...
                     {java.lang.Boolean(0),charlist{row,1}} additionalDataPropDefaults];
    end
end

%% Find the char tab
thisTab = propeditor.findtab('Characteristics');

%% (Re)Create the table model
if ~isempty(thisTab) && isfield(thisTab.Handles,'CharTable') && ...
        ~isempty(thisTab.Handles.CharTable)
    if thisTab.Handles.CharTable.getModel.getDataVector.equals(...
            thisTab.Handles.CharTable.getModel.getDataVector)
        return
    else
        for row=0:size(tableData,1)-1
            for col=0:size(tableData,2)-1
                awtinvoke(tableModel,'setValueAt(Ljava.lang.Object;II)',...
                    tableData{row+1,col+1},row,col);
            end
        end
    end
else
    tableModel = tsCharTableModel(tableData,[{'Show (y/n)','Name'} additionalHeadings]);
    if nargin<=3
        set(handle(tableModel,'callbackproperties'),'TableChangedCallback',...
            {@localDataChange tableModel h additionalDataProps charlist});
    else % Custom parser for additional char props in the table (e.g. dates)
        set(handle(tableModel,'callbackproperties'),'TableChangedCallback',...
            {@localDataChange tableModel h additionalDataProps charlist varargin{1}});
    end
end
       
%% If necessary build the Characteristics tab
if isempty(thisTab)
    % Build the char table
    thisTab.Handles.CharTable = MJTable(tableModel);
    thisTab.Handles.CharTable.getColumnModel.getColumn(0).setCellEditor(...
        DefaultCellEditor(MJCheckBox));
    thisTab.Handles.CharTable.getColumnModel.getColumn(0).setCellRenderer(...
        tsCheckBoxRenderer);    
    thisTab.Handles.CharTable.setRowSelectionAllowed(true);
    thisTab.Handles.CharTable.setColumnSelectionAllowed(false);
    thisTab.Handles.CharTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
    sPanel = MJScrollPane(thisTab.Handles.CharTable);    
    
    % Add the char panel container
    propeditor.Handles.TabPane.add('Define Statistical Annotations',sPanel);
    
    % Update the Tabs structure
    thisTab.Name = 'Characteristics';
    propeditor.Tabs = [propeditor.Tabs; thisTab];
end


function localDataChange(eventSrc,eventData,model,h,additionalDataProps,charlist,varargin)

%% Callback for changes in the characteristic table. Additional arg
%% specifies a parsing function to be used to interpret char property
%% values defining in the char table

%% If the event indicates the table is locked abort to prevent recursion
if isa(eventData,'com.mathworks.toolbox.timeseries.tsCharTableModel$mlTableModelEvent') && ...
        eventData.locked
    return
end

%% Find the data which was changed
startRow = eventData.getFirstRow;
endRow = eventData.getLastRow;
col = eventData.getColumn;

%% Find the char
ind = find(strcmp(char(model.getValueAt(startRow,1)),charlist(:,1)));
if isempty(ind)
    return
end
charid = charlist{ind(1),1};
chardataclass = charlist{ind(1),2};
charviewclass = charlist{ind(1),3};

%% Update the plot 

%% Make sure a char exists
thischar = [];
waves = h.allwaves;
if ~isempty(waves) && ~isempty(waves(1).Characteristics)
    thischar = find(waves(1).Characteristics,'Identifier',charid);
end
if model.getValueAt(startRow,0) && (isempty(thischar) || strcmp(thischar.Visible,'off')) 
    h.addchar(charid,chardataclass,charviewclass,'Visible','on'); 
end


%% Parse additonal char data props
for k=1:length(additionalDataProps)
    if nargin<=6
        adddataprop{k} = eval(char(model.getValueAt(startRow,1+k)),'[]');
    else % Custom parser for additional char props
        adddataprop{k} = feval(varargin{1},h,char(model.getValueAt(startRow,1+k)));
    end
end
waves = h.allwaves;

%% Set the char properties based on the char table
for k=1:length(waves)
    if ~isempty(waves(k).Characteristics)
        thischar = find(waves(k).Characteristics,'Identifier',charid);
        if ~isempty(thischar)
            for j=1:length(additionalDataProps)
                if ~isempty(adddataprop{j}) % Do not use invalid entries
                   set(thischar.Data,additionalDataProps{j},adddataprop{j})
                end
            end
            % Update the char visibility and the char menu checked status
            if model.getValueAt(startRow,0)
                set(thischar,'Visible','on')   
            else
                set(thischar,'Visible','off') 
            end
            thischar.draw
        end
    end     
end

%% Invalid entries trigger a ViewChange to refresh the char table
if length(additionalDataProps)>0 && any(cellfun('isempty',adddataprop))
    h.AxesGrid.send('ViewChange');
end

%% Update the char menus
if model.getValueAt(startRow,0)
    set(h.axesgrid.findMenu(charid),'Checked','on')
else
    set(h.axesgrid.findMenu(charid),'Checked','off')
end

function charexists = localfindchar(h,id,dataclass,viewclass)

%% Looks for any existing visible characteristics with identifier id.
%% If one is found chars are created for all waveforms. If one is visible
%% they are all set to visible.

%% Does a char of this type exist?
thischar = [];
waves = h.allwaves;
for k=1:length(waves)
    if ~isempty(waves(k).Characteristics)
         thischar = [thischar(:); ...
             find(waves(k).Characteristics,'Identifier',id)];
    end
end

%% If any chars of this type exist syncronize their visibility
if ~isempty(thischar)
    if any(strcmp(get(thischar,{'Visible'}),'on'))
        h.addchar(id,dataclass,viewclass,'Visible','on'); 
    else
        h.addchar(id,dataclass,viewclass,'Visible','off');
    end
    charexists = true;
else
    charexists = false;
end
