function dtPlotUIGroundtruth(Filename, detected_tonals, Start_s, Stop_s,...
    varargin)
% dtPlotUIGroundtruth(Filename, detected_tonals, Start_s, Stop_s, ...
%   OptionalArgs)
% Build an interface for user to generate the ground truth tonals.
% Ground tonals can be generated from scratch or it can generated by using
% the detected tonals detected by the detector dtTonalsTracking.
%
% Filename - Example - 'palmyra092007FS192-071011-230000.wav'
% detected_tonals - [] or
%                   List of tonals detected.
% Start_s - start time in s relative to the start of the recording
% Stop_s - stop time in s relative to the start of the recording
% Optional arguments in any order:
%   'Framing', [Advance_ms, Length_ms] - frame advance and length in ms
%       Defaults to 2 and 8 ms respectively
%   'Noise', method
%       Method for noise compensation in spectrogram plots.
%       It is recommended that the same noise compensation as
%       used for creating the tonal set and plotting them be used.  See
%       dtSpectrogramNoiseComp for valid methods. (default 'median')
%   'ParameterSet', String or struct
%       Default set of parameters.  May either be a string
%       which is passed to dtThresh or a parameter structure
%       that has been loaded from dtThresh and possibly modified.
%       This argument is processed before any other argument, and other
%       arguments may override these values.
%   'Range, [LowCutoff, HighCutoff] - Sepcify low and high cutoffs in Hz
%       Defaults to values specified in dtThresh().
%
% Example call:
% 1. dtPlotUIGroundtruth(File, [], t0, t1);
% 2. Using detected tonals
% [tonals graphs] = dtTonalsTracking(File, t0, t1);
% dtPlotUIGroundtruth(File, tonals, t0, t1);


% Defaults

% The threshold set is processed before any other argument as other
% arguments override the parameter set.
thr = dtParseParameterSet(varargin{:});  % retrieve parameters

NoiseMethod = {'median'};
scale = 1000; % kHz

if isempty(Filename)
    [Filename, FileDir] = uigetfile('.wav', 'Develop ground truth for file');
    if strcmp(num2str(Filename), '0')
        fprintf('User abort\n');
        return
    else
        Filename = fullfile(FileDir, Filename);
        cd(FileDir);
    end
end

warning off;

hdr = ioReadWavHeader(Filename);
Stop_s = min(Stop_s, hdr.Chunks{hdr.dataChunk}.nSamples/hdr.fs);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% processs arguments
%
k = 1;
while k <= length(varargin)
    switch varargin{k}
        case 'Framing'
            if length(varargin{k+1}) ~= 2
                error('%s must be [Advance_ms, Length_ms]', varargin{k});
            else
                thr.advance_ms = varargin{k+1}(1);
                thr.length_ms = varargin{k+1}(2);
            end
            k=k+2;
        case 'Noise'
            NoiseMethod = varargin{k+1}; k=k+2;
            if ~ iscell(NoiseMethod)
                NoiseMethod = {NoiseMethod};
            end
        case 'ParameterSet'
            k=k+2;  % handled earlier
        case 'Range'
            if length(varargin{k+1}) ~= 2 || diff(varargin{k+1}) <= 0
                error('%s must be [LowCutoff_Hz, HighCutoff_Hz]', varargin{k});
            else
                thr.low_cutoff_Hz = varargin{k+1}(1);
                thr.high_cutoff_Hz = varargin{k+1}(2);
            end
            k=k+2;
        otherwise
            try
                if isnumeric(varargin{k})
                    errstr = sprintf('Bad option %f', varargin{k});
                else
                    errstr = sprintf('Bad option %s', char(varargin{k}));
                end
            catch e
                errstr = sprintf('Bad optional arg position %d', k);
            end
            error('%s', errstr);
    end
end
ms_per_s = 1000;
thr.advance_s = thr.advance_ms / ms_per_s;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Variables
%
MarkerSize = 9;
LineStyle = '-';
trim_flag = false;  % keeps the track of trim operation
% Track the previous popup option before the figure is redrawn
popup_value = 1;
sel_tonal = []; % vector for selected tonals
ground_truth_tonals = []; % ground truth tonal

% defaults for user interface
highCutoffStr_Hz = sprintf('%.1f', thr.high_cutoff_Hz);
thresholdStr_dB = '10';
lengthStr_s = '5';
stepStr_s = '4';
polyStr_degree = '3';
% Borrowed brightness and contrast defaults from dtPlotSpecgram.m
brightStr_dB = '10';
contrastStr_dB = '200';
thr_on = false;
thr_off = true;
% Structure for accessing data among different callback
DATA.high_cutoff_Hz = str2double(highCutoffStr_Hz);
DATA.bright_dB = str2double(brightStr_dB);
DATA.contrast_dB = str2double(contrastStr_dB);
DATA.minbright_dB = -300;
DATA.maxbright_dB = 300;
DATA.mincontrast_dB = 0;
DATA.maxcontrast_dB = 1000;
DATA.plot_len = str2double(lengthStr_s);
DATA.time_step = str2double(stepStr_s);
DATA.poly_deg = str2double(polyStr_degree);
DATA.thr_energy_dB = str2double(thresholdStr_dB);
DATA.threshold = false;
% Required for redrawing the points (Add/Merge -> Undo Operation)
DATA.time_pt = []; % Time co-ordinate of a point
DATA.freq_pt = []; % Frequency co-ordinate of a point

% Handles
tonalH = [];  % selected tonals
pointH = [];  % draggable points
tonal_markerH = [];  % tonal end points markers
callbackH = []; % Save the callback
proposedH = []; % curve under construction
new_tonalsH = []; % New tonal handles

% Vectors for undo operation
orig_sel_tonal = []; % selected tonals before being modified
new_tonal = [];      % modified or new tonals

% Plot only between certain specific time
blkstart_s = Start_s;
blkstop_s = min(blkstart_s + DATA.plot_len, Stop_s);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Create and then hide the GUI as it is being constructed.
HANDLES.fig = figure('name', 'Ground truth GUI', ...
    'Visible', 'off', 'Position', [360, 400, 800, 500],...
    'CloseRequestFcn', @closerqst_CallBack);

% Track the motion of a cursor
set(HANDLES.fig, 'WindowButtonMotionFcn', @motion_Callback);
set(HANDLES.fig, 'WindowButtonUpFcn', @buttonup_Callback);

bgColor = [.75 .875 1]; % light blue

% Initialize the ground truth tonals using detected tonals, as we are
% building ground truth tonal from detected tonals. Build begin and end
% time tree.
init_ground_truth_tonal;

% Create GUI components
gui_components;

% Initial call to plot spectrogram and tonals
plotSpec_detected_tonal;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Setting various properties of figure
%
% Move the GUI to the center of the screen
movegui(HANDLES.fig, 'center')
% Add standard toolbar to the GUI
set(HANDLES.fig, 'toolbar', 'figure')
% Make the GUI visible.
set(HANDLES.fig, 'Visible', 'on');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    function gui_components
        
        % get figures position
        pos = get(HANDLES.fig,'Position');
        
        HANDLES.textoption = uicontrol(...
            'Style', 'text',...
            'String', 'Options', ...
            'FontSize', 12,...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.82 pos(4)*0.86 pos(3)*0.1 pos(4)*0.04],...
            'Visible', 'on');
        
        HANDLES.popup = uicontrol(...
            'Style', 'popup', ...
            'String', 'Add/Merge|Delete Tonals|Trim Tonal|Smooth|Spline Fit',...
            'Position', [pos(3)*0.80 pos(4)*0.75 pos(3)*0.15 pos(4)*0.08],...
            'Callback', {@popup_Callback});
        
        % Set the popup to previous option that was selected.
        if ~(popup_value == 1)
            set(HANDLES.popup, 'Value', popup_value);
        end
        
        HANDLES.add = uicontrol(...
            'Style', 'pushbutton',...
            'String', 'Add/Merge',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Add points and merge tonals',...
            'Position', [pos(3)*.80 pos(4)*0.63 pos(3)*.15 pos(4)*0.07],...
            'Visible', 'off', ...
            'Callback', {@addMergeButton_Callback});
        
        HANDLES.delete = uicontrol(...
            'Style', 'pushbutton',...
            'String', 'Delete Tonals',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Delete selected tonals',...
            'Position', [pos(3)*0.80 pos(4)*0.63 pos(3)*0.15 pos(4)*.07],...
            'Visible', 'off', ...
            'Callback', {@deleteButton_Callback});
        
        HANDLES.trimLeft = uicontrol(...
            'Style', 'pushbutton',...
            'String', 'Left',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Trim selected tonal towards left',...
            'Position', [pos(3)*0.78 pos(4)*0.66 pos(3)*.0875 pos(4)*0.05],...
            'Visible', 'off', ...
            'Callback', {@trimLeftButton_Callback});
        
        HANDLES.trimRight = uicontrol(...
            'Style', 'pushbutton',...
            'String', 'Right',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Trim selected tonal towards right',...
            'Position', [pos(3)*.8875 pos(4)*0.66 pos(3)*0.0875 pos(4)*0.05],...
            'Visible', 'off', ...
            'Callback', {@trimRightButton_Callback});
        
        HANDLES.texttrim = uicontrol(...
            'Style', 'text',...
            'String', 'Trim Tonal', ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.8313 pos(4)*0.71 pos(3)*0.1 pos(4)*0.03],...
            'Visible', 'off');
        
        HANDLES.smooth = uicontrol(...
            'Style', 'pushbutton',...
            'String', 'Smooth Tonals',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Smooth selected tonals',...
            'Position', [pos(3)*.80 pos(4)*0.63 pos(3)*.15 pos(4)*0.07],...
            'Visible', 'off', ...
            'Callback', {@smoothButton_Callback});
        
        HANDLES.textsmooth = uicontrol(...
            'Style', 'text',...
            'String', {'Polynomial', 'degree'}, ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.81 pos(4)*0.705 pos(3)*0.1 pos(4)*0.06],...
            'Visible', 'off');
        
        HANDLES.editsmooth = uicontrol(...
            'Style', 'edit',...
            'String', polyStr_degree, ...
            'BackgroundColor', [1 1 1],...
            'Position', [pos(3)*0.91 pos(4)*0.705 pos(3)*0.025 pos(4)*0.06],...
            'Visible', 'off',...
            'Callback', {@editsmooth_Callback});
        
        HANDLES.splineFit = uicontrol(...
            'Style', 'pushbutton',...
            'String', 'Spline fit',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Compute spline fit of selected tonals',...
            'Position', [pos(3)*.80 pos(4)*0.63 pos(3)*.15 pos(4)*0.07],...
            'Visible', 'off', ...
            'Callback', {@splineFitButton_Callback});
        
        % Set the property "visible" to "on" of the appropriate push button
        val = get(HANDLES.popup, 'Value');
        switch val
            case 1 % Add/Merge
                set(HANDLES.add, 'Visible', 'on');
            case 2 % Delete
                set(HANDLES.delete, 'Visible', 'on');
            case 3 % Trim
                set(HANDLES.trimLeft, 'Visible', 'on');
                set(HANDLES.trimRight, 'Visible', 'on');
                set(HANDLES.texttrim, 'Visible', 'on');
            case 4 % Smooth
                set(HANDLES.smooth, 'Visible', 'on');
                set(HANDLES.textsmooth, 'Visible', 'on');
                set(HANDLES.editsmooth, 'Visible', 'on');
            case 5 % Spline fit
                set(HANDLES.splineFit, 'Visible', 'on');
            otherwise
                % won't get here
        end
        
        HANDLES.undo = uicontrol(...
            'Style', 'pushbutton',...
            'String', 'Undo Operation',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Undo previous operation',...
            'Position', [pos(3)*.80 pos(4)*0.53 pos(3)*.15 pos(4)*0.05],...
            'Visible', 'on', ...
            'Callback', {@undoButton_Callback});
        
        HANDLES.clear = uicontrol(...
            'Style', 'pushbutton',...
            'String', 'Clear',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Clear the selection',...
            'Position', [pos(3)*.78 pos(4)*.45 pos(3)*0.0625 pos(4)*0.05],...
            'Callback', {@clearButton_Callback});
        
        HANDLES.save = uicontrol(...
            'Style', 'pushbutton',...
            'String', 'Save',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Save the ground truth tonals',...
            'Position', [pos(3)*.8462 pos(4)*.45 pos(3)*0.0625 pos(4)*0.05],...
            'Callback', {@saveButton_Callback});
        
        HANDLES.reset = uicontrol(...
            'Style', 'pushbutton',...
            'String', 'Reset',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Reset the ground truth tonals',...
            'Position', [pos(3)*.9113 pos(4)*0.45 pos(3)*0.0625 pos(4)*0.05],...
            'Callback', {@resetButton_Callback});
        
        HANDLES.textCutoff = uicontrol(...
            'Style', 'text',...
            'String', {'High Cutoff (Hz)'}, ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.763 pos(4)*0.38 pos(3)*0.1188 pos(4)*0.04],...
            'Visible', 'on');
        
        HANDLES.editcutoff = uicontrol(...
            'Style', 'edit',...
            'String', highCutoffStr_Hz, ...
            'BackgroundColor', [1 1 1],...
            'Position', [pos(3)*0.8813 pos(4)*0.38 pos(3)*0.1188 pos(4)*0.04],...
            'Visible', 'on',...
            'Callback', {@editcutoff_Callback});
        
        HANDLES.textthr = uicontrol(...
            'Style', 'text',...
            'String', {'Threshold', '(dB)'}, ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.763 pos(4)*0.31 pos(3)*0.1188 pos(4)*0.06],...
            'Visible', 'on');
        
        HANDLES.editthr = uicontrol(...
            'Style', 'edit',...
            'String', thresholdStr_dB, ...
            'BackgroundColor', [1 1 1],...
            'Position', [pos(3)*0.8813 pos(4)*0.31 pos(3)*0.06 pos(4)*0.06],...
            'Visible', 'on',...
            'Callback', {@editthr_Callback});
        
        HANDLES.radiothrOn = uicontrol(...
            'Style', 'radiobutton',...
            'String', {'ON'}, ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.9413 pos(4)*0.34 pos(3)*0.06 pos(4)*0.03],...
            'Value', thr_on,...
            'Visible', 'on',...
            'Callback', {@thronRadio_Callback});
        
        HANDLES.radiothrOff = uicontrol(...
            'Style', 'radiobutton',...
            'String', {'OFF'}, ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.9413 pos(4)*0.31 pos(3)*0.06 pos(4)*0.03],...
            'Value', thr_off,...
            'Visible', 'on',...
            'Callback', {@throffRadio_Callback});
        
        HANDLES.textplotLength = uicontrol(...
            'Style', 'text',...
            'String', {'Plot Length (s)'}, ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.763 pos(4)*0.26 pos(3)*0.1188 pos(4)*0.04],...
            'Visible', 'on');
        
        HANDLES.editplotLength = uicontrol(...
            'Style', 'edit',...
            'String', lengthStr_s, ...
            'BackgroundColor', [1 1 1],...
            'Position', [pos(3)*0.763 pos(4)*0.22 pos(3)*0.1188 pos(4)*0.04],...
            'Visible', 'on',...
            'Callback', {@editplotLength_Callback});
        
        HANDLES.texttimeStep = uicontrol(...
            'Style', 'text',...
            'String', {'Time Step (s)'}, ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.8813 pos(4)*0.26 pos(3)*0.1188 pos(4)*0.04],...
            'Visible', 'on');
        
        HANDLES.edittimeStep = uicontrol(...
            'Style', 'edit',...
            'String', stepStr_s, ...
            'BackgroundColor', [1 1 1],...
            'Position', [pos(3)*0.8813 pos(4)*0.22 pos(3)*0.1188 pos(4)*0.04],...
            'Visible', 'on',...
            'Callback', {@edittimeStep_Callback});
        
        HANDLES.start = uicontrol(...
            'Style', 'pushbutton',...
            'String', '|<',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Start',...
            'Position', [pos(3)*0.763 pos(4)*0.17 pos(3)*0.0625 pos(4)*0.05],...
            'Visible', 'on',...
            'Enable', 'on',...
            'Callback', {@startButton_Callback});
        
        HANDLES.prev = uicontrol(...
            'Style', 'pushbutton',...
            'String', '<',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Prev time period',...
            'Position', [pos(3)*0.8225 pos(4)*0.17 pos(3)*0.0625 pos(4)*0.05],...
            'Visible', 'on',...
            'Enable', 'on',...
            'Callback', {@prevButton_Callback});
        
        HANDLES.next = uicontrol(...
            'Style', 'pushbutton',...
            'String', '>',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'Next time period',...
            'Position', [pos(3)*0.8813 pos(4)*0.17 pos(3)*0.0625 pos(4)*0.05],...
            'Visible', 'on', ...
            'Callback', {@nextButton_Callback});
        
        HANDLES.stop = uicontrol(...
            'Style', 'pushbutton',...
            'String', '>|',...
            'BackgroundColor', bgColor,...
            'TooltipString', 'End',...
            'Position', [pos(3)*0.9413 pos(4)*0.17 pos(3)*0.0625 pos(4)*0.05],...
            'Visible', 'on', ...
            'Callback', {@stopButton_Callback});
        
        HANDLES.textStartTime = uicontrol(...
            'Style', 'text',...
            'String', {'Start time (s)'}, ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.763 pos(4)*0.12 pos(3)*0.1188 pos(4)*0.04],...
            'Visible', 'on');
        
        blkstartStr_s = num2str(blkstart_s);
        HANDLES.editstartTime = uicontrol(...
            'Style', 'edit',...
            'String', blkstartStr_s, ...
            'BackgroundColor', [1 1 1],...
            'Position', [pos(3)*0.8813 pos(4)*0.12 pos(3)*0.1188 pos(4)*0.04],...
            'Visible', 'on',...
            'Callback', {@editstartTime_Callback});
        
        HANDLES.textBright = uicontrol(...
            'Style', 'text',...
            'String', {'Brightness (dB)'}, ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.703 pos(4)*0.07 pos(3)*0.1188 pos(4)*0.04],...
            'Visible', 'on');
        
        HANDLES.scrollBright = uicontrol(...
            HANDLES.fig,'Style','slider', ...
            'Max', 300, 'Min', -300, 'Value', DATA.bright_dB,...
            'SliderStep', [2/600 5/600],...
            'Position', [pos(3)*0.8213 pos(4)*0.07 pos(3)*0.12 pos(4)*0.04],...
            'Callback', {@scrollBright_Callback});
        
        HANDLES.editbright = uicontrol(...
            'Style', 'edit',...
            'String', brightStr_dB, ...
            'BackgroundColor', [1 1 1],...
            'Position', [pos(3)*0.9413 pos(4)*0.07 pos(3)*0.06 pos(4)*0.04],...
            'Visible', 'on',...
            'Callback', {@editBright_Callback});
        
        HANDLES.textContrast = uicontrol(...
            'Style', 'text',...
            'String', {'Contrast (%dB)'}, ...
            'BackgroundColor', bgColor,...
            'Position', [pos(3)*0.703 pos(4)*0.03 pos(3)*0.1188 pos(4)*0.04],...
            'Visible', 'on');
        
        HANDLES.scrollContrast = uicontrol(...
            HANDLES.fig, 'Style', 'slider', ...
            'Max', 1000, 'Min', 0, 'Value', DATA.contrast_dB,...
            'SliderStep', [16/1000 20/1000],...
            'Position', [pos(3)*0.8213 pos(4)*0.03 pos(3)*0.12 pos(4)*0.04],...
            'Callback', {@scrollContrast_Callback});
        
        HANDLES.editcontrast = uicontrol(...
            'Style', 'edit',...
            'String', contrastStr_dB, ...
            'BackgroundColor', [1 1 1],...
            'Position', [pos(3)*0.9413 pos(4)*0.03 pos(3)*0.06 pos(4)*0.04],...
            'Visible', 'on',...
            'Callback', {@editContrast_Callback});
        
        % Change units to normalized so components resize automatically.
        set([HANDLES.textoption, HANDLES.popup, HANDLES.add, HANDLES.delete,...
            HANDLES.trimLeft, HANDLES.trimRight, HANDLES.smooth,...
            HANDLES.splineFit, HANDLES.undo, HANDLES.clear, HANDLES.save,...
            HANDLES.reset, HANDLES.textCutoff, HANDLES.editcutoff,...
            HANDLES.textBright, HANDLES.scrollBright, HANDLES.editbright,...
            HANDLES.textContrast, HANDLES.scrollContrast,...
            HANDLES.editcontrast, HANDLES.textthr, HANDLES.editthr...
            HANDLES.radiothrOn, HANDLES.radiothrOff,...
            HANDLES.textplotLength, HANDLES.editplotLength,...
            HANDLES.texttimeStep, HANDLES.edittimeStep,...
            HANDLES.start, HANDLES.prev, HANDLES.next, HANDLES.stop,...
            HANDLES.textStartTime, HANDLES.editstartTime, HANDLES.texttrim,...
            HANDLES.textsmooth, HANDLES.editsmooth], 'Units', 'normalized');
        
        % Change axis position to accomodate GUI components.
        ax = get(gca, 'Position');
        ax(1) = ax(1) - ax(1)*2/5;    % Left
        ax(2) =  ax(2) + ax(2)*2/5;   % Bottom
        ax(3) = ax(3) - ax(3)*1/5;    % Right
        ax(4) = ax(4) - ax(4)*1/10;   % Top
        set(gca, 'Position', ax);
        
    end

    function init_ground_truth_tonal
        % Create a new copy of list of detected tonal.
        
        % Empty detected tonals
        if isempty(detected_tonals)
            detected_tonals = java.util.LinkedList();
            ground_truth_tonals = java.util.LinkedList();
        else
            ground_truth_tonals = javaArray('tonals.tonal', ...
                detected_tonals.size());
            detectedIt = detected_tonals.iterator();
            idx = 1;
            while detectedIt.hasNext()
                a_tonal = detectedIt.next();
                % add a copy to the set of ground truth tonals
                ground_truth_tonals(idx) = a_tonal.clone();
                idx = idx + 1;
            end
        end
    end

    function plotSpec_detected_tonal
        % Plot spectrogram and detected tonals
        
        % Plot Spectrogram
        colormap(bone);
        if (DATA.threshold == true)
            RenderOpts = {'Render', 'floor', ...
                'Threshold', DATA.thr_energy_dB};
        else
            RenderOpts = {};
        end

        [HANDLES.axisH HANDLES.imageH] = ...
            dtPlotSpecgram(Filename, blkstart_s, blkstop_s, ...
            'ParameterSet', thr, ...
            'Noise', NoiseMethod, ...
            'Range', [thr.low_cutoff_Hz, DATA.high_cutoff_Hz], ...
            RenderOpts{:});
        
        set(HANDLES.imageH, 'ButtonDownFcn', @selectPoint_Callback);
        hold on;
        
        % Plot detected tonals
        %
        % fileparts is Platform dependent.
        [pathstr, fname, ext] = fileparts(Filename);
        filenm = strcat(fname, ext);
        title(filenm, 'fontname', 'helvetica', 'fontsize', 12,...
            'fontweight', 'b');
        % handles cell array
        handles = cell(1, 1);
        hidx = 1;
        % The hue, saturation, value color map is used.
        colorsN = 20;
        colorMap = hsv(colorsN);
        colorMap = colorMap(randperm(colorsN), :);
        cidx = 1;
        
        % Iterate over ground truth tonals
        for idx = 1 : length(ground_truth_tonals)
            if ~ ground_truth_tonals(idx).isEmpty() % skip the empty tonals
                tonal = ground_truth_tonals(idx);
                start_tm = tonal.getFirst.time;
                end_tm = tonal.getLast.time;
                if ((start_tm <= blkstart_s) && (end_tm >= blkstart_s)) || ...
                        (start_tm >= blkstart_s && start_tm <= blkstop_s)
                    % Tonals with start time less then the start time of
                    % current block or tonal with start time within the current
                    % block. Some part of tonal is in the current block
                    % and some may be outside current block. Matlab figure
                    % only shows the part of tonal that is within current
                    % block.
                    
                    time = tonal.get_time();
                    freq = tonal.get_freq() / scale;
                    handles{hidx} = plot(time, freq, 'LineStyle', LineStyle,...
                        'Color', colorMap(cidx, :), 'LineWidth', 2);
                    set(handles{hidx}, 'UserData', tonal);
                    % Tonal selection callback
                    set(handles{hidx}, 'ButtonDownFcn', @selectTonal_Callback);
                    hidx = hidx + 1;
                    % Change color for the next edge
                    cidx = mod(cidx, colorsN) + 1;
                end
            end
        end
        
        % Start time display
        startStr  = num2str(Start_s);
        startStr_s = strcat('File start time', ' :', startStr, ' s');
        text('Position', [0, -0.09], 'String', startStr_s,...
            'Units', 'normalized', 'FontSize', 10);
        
        % Stop time display
        stopStr = num2str(Stop_s);
        stopStr_s = strcat('File stop time', ' :', stopStr, ' s');
        text('Position', [0, -0.13], 'String', stopStr_s,...
            'Units', 'normalized', 'FontSize', 10);
        
    end

%  Following callbacks automatically have access to component handles and
%  data because they are nested at a lower level.

% Motion callback -------------------------------------------

    function motion_Callback(hObject, eventdata)
        % Detect motion of a cursor by drawing full crosshair.
        
        currentAxisPoint = get(HANDLES.axisH, 'CurrentPoint');
        x = currentAxisPoint(1, 1);
        y = currentAxisPoint(1, 2);
        xlim = get(HANDLES.axisH, 'Xlim');
        ylim = get(HANDLES.axisH, 'Ylim');
        needCrosshair = ( (xlim(2) >= x && xlim(1) <= x) && ...
            (ylim(2) >= y && ylim(1) <= y) );
        pointer = get(HANDLES.fig, 'pointer');
        if (needCrosshair && ~ strcmp(pointer , 'fullcrosshair'))
            set(HANDLES.fig, 'pointer', 'fullcrosshair');
        elseif(~ needCrosshair && strcmp(pointer , 'fullcrosshair'))
            set(HANDLES.fig, 'pointer', 'arrow');
        end
        
    end

% Button up callback -------------------------------------------

    function buttonup_Callback(hObject, eventdata)
        
        % If more than one existing draggable point has same time
        % co-ordiante then we get rid of the first. Sometimes due to rapid
        % execution of point select and button up callbacks, points with
        % same time may have not been deleted.
        delete_duplicate_timept();
        
        if length(pointH)>1
            current_pt = get(HANDLES.axisH, 'CurrentPoint');
            matches = [];
            for idx = 1 : length(pointH)
                existing_pt = getPosition(pointH(idx));
                if (existing_pt(1) == current_pt(1,1))
                    % compared to the dragged point, the time
                    % is identical.
                    matches(end+1) = idx;
                end
            end
            if ~ isempty(matches)
                if length(matches)>=2
                    % same time, get rid of the first one.
                    delete (pointH(matches(1)));
                    pointH(matches(1)) = [];
                end
            end
        end
        
    end

% Popup callback --------------------------------------------

    function popup_Callback(hObject, eventdata)
        val = get(hObject, 'Value');
        
        switch val
            case 1
                % Add points and Merge tonals
                set(HANDLES.add, 'Visible', 'on');
                set(HANDLES.delete, 'Visible', 'off');
                set(HANDLES.trimRight, 'Visible', 'off');
                set(HANDLES.trimLeft, 'Visible', 'off');
                set(HANDLES.smooth, 'Visible', 'off');
                set(HANDLES.texttrim, 'Visible','off');
                set(HANDLES.textsmooth, 'Visible','off');
                set(HANDLES.editsmooth, 'Visible', 'off');
                set(HANDLES.splineFit, 'Visible', 'off');
                
                % Turn on the hit test of selected tonals if they were
                % turned off during trim operation.
                if trim_flag == true
                    if ~isempty (tonalH)
                        val = get(tonalH, 'HitTest');
                        if strcmp(val, 'off')
                            set(tonalH, 'HitTest', 'on');
                        end
                    end
                    trim_flag = false;
                end
            case 2
                % Delete tonals
                set(HANDLES.delete, 'Visible', 'on');
                set(HANDLES.trimRight, 'Visible', 'off');
                set(HANDLES.trimLeft, 'Visible', 'off');
                set(HANDLES.add, 'Visible', 'off');
                set(HANDLES.smooth, 'Visible', 'off');
                set(HANDLES.texttrim, 'Visible', 'off');
                set(HANDLES.textsmooth, 'Visible', 'off');
                set(HANDLES.editsmooth, 'Visible', 'off');
                set(HANDLES.splineFit, 'Visible', 'off');
                
                % Turn on the hit test of selected tonals if they were
                % turned off during trim operation.
                if trim_flag == true
                    if ~isempty (tonalH)
                        val = get(tonalH, 'HitTest');
                        if strcmp(val, 'off')
                            set(tonalH, 'HitTest', 'on');
                        end
                    end
                    trim_flag = false;
                end
            case 3
                % Trim tonal
                set(HANDLES.texttrim, 'Visible','on');
                set(HANDLES.trimRight, 'Visible', 'on');
                set(HANDLES.trimLeft, 'Visible', 'on');
                set(HANDLES.delete, 'Visible', 'off');
                set(HANDLES.smooth, 'Visible', 'off');
                set(HANDLES.add, 'Visible', 'off');
                set(HANDLES.textsmooth, 'Visible', 'off');
                set(HANDLES.editsmooth, 'Visible', 'off');
                set(HANDLES.splineFit, 'Visible', 'off');
                trim_flag = true;
                
                if length(sel_tonal) >= 2
                    errordlg('Only one tonal can be trimmed at a time',...
                        'Tonal error');
                    clear_data;
                    return;
                end
                
                % Turn the hit test off so we can select point on tonal. If
                % hit test is on and we click on it than the tonal object
                % would be selected and not the point.
                set(tonalH, 'HitTest', 'off');
            case 4
                % Smooth tonals
                set(HANDLES.smooth, 'Visible', 'on');
                set(HANDLES.textsmooth, 'Visible','on');
                set(HANDLES.editsmooth, 'Visible', 'on');
                set(HANDLES.add, 'Visible', 'off');
                set(HANDLES.delete, 'Visible', 'off');
                set(HANDLES.trimRight, 'Visible', 'off');
                set(HANDLES.trimLeft, 'Visible', 'off');
                set(HANDLES.texttrim, 'Visible', 'off');
                set(HANDLES.splineFit, 'Visible', 'off');
                
                % Turn on the hit test of selected tonals if they were
                % turned off during trim operation.
                if trim_flag == true
                    if ~isempty (tonalH)
                        val = get(tonalH, 'HitTest');
                        if strcmp(val, 'off')
                            set(tonalH, 'HitTest', 'on');
                        end
                    end
                    trim_flag = false;
                end
            case 5
                % Spline Fit
                set(HANDLES.splineFit, 'Visible', 'on');
                set(HANDLES.delete, 'Visible', 'off');
                set(HANDLES.trimRight, 'Visible', 'off');
                set(HANDLES.trimLeft, 'Visible', 'off');
                set(HANDLES.smooth, 'Visible', 'off');
                set(HANDLES.texttrim, 'Visible','off');
                set(HANDLES.textsmooth, 'Visible','off');
                set(HANDLES.editsmooth, 'Visible', 'off');
                
                % Turn on the hit test of selected tonals if they were
                % turned off during trim operation.
                if trim_flag == true
                    if ~isempty (tonalH)
                        val = get(tonalH, 'HitTest');
                        if strcmp(val, 'off')
                            set(tonalH, 'HitTest', 'on');
                        end
                    end
                    trim_flag = false;
                end
            otherwise
                % In case of popup menu first value is selected by default.
                % Won't get here.
        end
    end

% Selection callbacks ---------------------------------------

    function selectPoint_Callback(hObject, eventdata)
        %  Point selection callback. Gets x(i.e. time) and y(i.e. freq)
        %  co-ordinates of the point and creates a draggable point.
        
        % Get time and frequency co-ordinates
        current_pt = get(HANDLES.axisH, 'CurrentPoint');
        time_pt = current_pt(1, 1);
        freq_pt = current_pt(1, 2);
        
        % If more than one existing draggable point has same time
        % co-ordiante then we get rid of the first. Sometimes due to rapid
        % execution of point select and button up callbacks, points with
        % same time may have not been deleted.
        delete_duplicate_timept();
        
        % Special case: Trim operation
        if trim_flag
            if length(pointH) >= 1
                errordlg(['Select only one point.Tonal is trimmed towards '...
                    'left/right from the point'], 'Point Error');
                clear_data;
                return;
            end
            if isempty(sel_tonal)
                errordlg('Tonal not selected', 'Tonal Error');
                clear_data;
                return;
            end
            if time_pt < sel_tonal.getFirst.time || ...
                    time_pt > sel_tonal.getLast.time
                errordlg(['Point not selected correctly. It must be on the'...
                    ' tonal so that left or right part from the point can '...
                    'be deleted.'], 'Point Error');
                clear_data;
                return;
            end
        end
        % Draw draggable points
        draw_point(hObject, eventdata, time_pt, freq_pt);
    end

    function draw_point(hObject, eventdata, time_pt, freq_pt)
        % Draw points
        
        % Draggable point
        pointerH = impoint(HANDLES.axisH, time_pt, freq_pt);
        setColor(pointerH, 'White');
        % Draggable point handle
        pointH = [pointH pointerH];
        % Hijack the callback so we see it before impoint
        % save old callback in user data
        callbackH = get(pointerH, 'ButtonDownFcn');
        set(pointerH, 'ButtonDownFcn', {@existingPoint_CallBack, ...
            callbackH, pointerH});
        addNewPositionCallback(pointerH, @previewSplineFit);
        
        previewSplineFit(hObject, eventdata, pointerH);
        
    end

    function existingPoint_CallBack(hObject, eventdata, varargin)
        % Check the type of a click on existing point. If its shift
        % click than we unselect the point otherwise we set callback back
        % to impoint.
        
        img_point_ButtonDownFcn = varargin{1};
        pointerH = varargin{2};
        if strcmp(get(HANDLES.fig, 'SelectionType'), 'extend')
            % delete on shift click
            idx = find(pointH == pointerH);  % which one to delete
            pointH(idx) = [];
            delete(pointerH); % unselect
            previewSplineFit(hObject, eventdata);
        else
            % let impoint handle its own callback
            img_point_ButtonDownFcn(hObject, eventdata);
        end
        1;
    end

    function [t, f] = previewSplineFit(hObject, eventdata, varargin)
        % preview spline fit - user selected points are treated as knots
        
        if ishandle(proposedH)
            delete(proposedH);
        end
        % Assuming a set of impoints, preview their spline fit
        if length(pointH) > 1
            if nargin > 2
                % Pass handle(pointerH) of new impoint
                [t,f] = getSpline(pointH, varargin{1});
            else
                % Pass hObject (new position of existing impoint)
                [t,f] = getSpline(pointH, hObject);
            end
            % preview the spline fit
            % make it unselectable to continue selecting knots
            proposedH = plot(t, f / scale, 'r:', 'HitTest', 'off');
        end
    end

    function [ntime, nfreq] = getSpline(points, current_point)
        % Fit a spline to data that is assumed to be without noise
        % Each point is assumed to be a knot
        % if current_point is not [], delete any nodes from the points
        % that occur at the same time.
        
        if nargin < 2
            current_point = [];
        end
        % return points sorted by time
        [t, f] = get_points(points, current_point);
        
        if length(t) > 1
            polynomial = spline(t, f);
            % User clicked at a specific point
            advance_s = thr.advance_ms / 1000;
            start = floor(t(1)/advance_s)*advance_s;
            stop = floor(t(end)/advance_s)*advance_s;
            N = round((stop - start)/advance_s)+ 1;
            ntime = linspace(start, stop, N);
            nfreq = ppval(polynomial, ntime);
        else
            ntime = t;  % degenerate case
            nfreq = f;
        end
    end


    function selectTonal_Callback(hObject, eventdata)
        % Tonal selection callback. Gets the selected tonals data from the
        % plot and stores it in the vector. Handles of the selected tonal
        % is also stored.
        
        % Check the type of a click on existing tonal. If its shift
        % click than we unselect the tonal.
        if strcmp(get(HANDLES.fig, 'SelectionType'), 'extend')
            % delete on shift click
            
            % delete tonal from selected tonal vector
            curr_tonal =  get(hObject, 'UserData');
            for idx = 1 : length(sel_tonal)
                if (sel_tonal(idx) == curr_tonal)
                    if length(sel_tonal) == 1
                        sel_tonal = [];
                    else
                        sel_tonal(idx) = [];
                    end
                end
            end
            % Update the sel_tonal by removing the empty entries
            if ~ isempty(sel_tonal)
                cnt = 1;
                for idx = 1 : length(sel_tonal)
                    if ~ isempty (sel_tonal(idx))
                        temp_tonal(cnt) = sel_tonal(idx);
                        cnt = cnt + 1;
                    end
                end
                sel_tonal = temp_tonal;
            end
            
            % Remove the tonal handle from the list of tonal handles.
            idx = find(tonalH == hObject);  % which one to delete
            if trim_flag
                % Before deleting turn the HitTest property to 'on' which
                % may have been turned 'off' during selection of tonal
                % for trimming.
                val = get(tonalH(idx), 'HitTest');
                if strcmp(val,'off')
                    set(tonalH, 'HitTest', 'on');
                end
            end
            tonalH(idx) = [];
            
            % delete tonal markers
            idx1 = idx + (idx - 1);
            delete(tonal_markerH(idx1));
            delete(tonal_markerH(idx1+1));
            % Remove the selected tonal marker handles from the list of
            % marker handles.
            tonal_markerH(idx1) = [];
            tonal_markerH(idx1) = [];
            return;
        end
        
        % Special case: Trim operation
        if trim_flag
            if isempty(sel_tonal)
                set(hObject, 'HitTest', 'off');
            else
                errordlg('Only one tonal can be trimmed at a time',...
                    'Tonal error');
                clear_data;
                return;
            end
        end
        
        curr_tonal =  get(hObject, 'UserData');
        if ~ isempty(tonalH) && ~ isempty(find(tonalH == hObject))
            return;
        else
            sel_tonal = [sel_tonal curr_tonal];
        end
        tonalH = [tonalH hObject]; % Track selected tonal handles
        
        % Plot endpoints of the selected tonal
        tonal_markerH = [tonal_markerH plot(curr_tonal.getFirst.time, ...
            curr_tonal.getFirst.freq/scale, 'r+', ...
            'MarkerSize', MarkerSize)];
        tonal_markerH = [tonal_markerH plot(curr_tonal.getLast.time,...
            curr_tonal.getLast.freq/scale, 'r+', ...
            'MarkerSize', MarkerSize)];
        
    end

% Push button callbacks -------------------------------------

    function addMergeButton_Callback(hObject, eventdata)
        % Add Points:
        % Selected point/points is/are added to the selected tonal.
        % OR
        % If tonal is not selected, new tonal is created from the selected
        % points and added at the end of ground truth tonals.
        %
        % Merge tonals:
        % If points and tonals are selected.
        % Points are added to the selected tonal and extended tonal is than
        % merged with previously selected tonal in time. New extened tonal
        % is added at the end of ground truth tonals.
        % OR
        % If only tonals are selected.
        % Say, if tonals A,B and C are selected than Tonal A,B and C will
        % be merged to form new tonal D and this new tonal is added at the
        % end of ground truth tonals.
        
        if ~ isempty(DATA.time_pt)
            % Empty the co-ordinate record of points
            DATA.time_pt = [];
            DATA.freq_pt = [];
        end
        
        time_vec = [];
        t =[];
        f = [];
        if ~ isempty(pointH)
            % Get position of the points i.e x(time) and y(frequency)
            % co-ordinates by using the point's handle saved earlier.
            [time_vec] = get_points(pointH);
            
            % fit a spline to the selected points
            [t, f] = getSpline(pointH);
        end
        
        if length(time_vec) < 2 && isempty(sel_tonal)
            errordlg(['Minimum two points needs to be selected to generate '...
                'a tonal OR tonals and points must be selected to add '...
                'points to tonals'], 'Point/Tonal Error');
            return;
        end
        
        if length(sel_tonal) == 1 && isempty(time_vec)
            errordlg(['Minimum two tonals needs to be selected to merge '...
                'tonals OR tonals and points must be selected to add '...
                'points to tonals'], 'Point/Tonal Error');
            clear_data;
            return;
        end
        
        if ~ isempty(sel_tonal)
            % Sort tonals on the basis of start time of tonal
            for x = 1 : length(sel_tonal)
                tstarts(x) = sel_tonal(x).getFirst.time;
            end
            [tstarts torder] = sort(tstarts);
            sel_tonal =  sel_tonal(torder);
            
            % Check for overlap
            for idx = 1 : length(sel_tonal) - 1
                if sel_tonal(idx).getLast.time > ...
                        sel_tonal(idx+1).getFirst.time
                    errordlg('Overlapping tonals cannot be merged', ...
                        'Tonal error');
                    clear_data;
                    return;
                end
            end
            
            % Check whether points can be added to the tonal
            for point_idx =  1 : length(time_vec)
                for tonal_idx = 1 : length(sel_tonal)
                    if time_vec(point_idx) > ...
                            sel_tonal(tonal_idx).getFirst.time && ...
                            time_vec(point_idx) < ...
                            sel_tonal(tonal_idx).getLast.time
                        
                        errordlg(['Point/Points wrongly selected. Must be '...
                            'before or after the selected tonal in time'],...
                            'Point Error');
                        return;
                    end
                end
            end
            
        end
        
        import tonals.*;
        
        tonal_idx = 1;
        new_tonal = [];
        orig_sel_tonal = [];
        new_tonalsH = [];
        prev_endTime = 0.0;
        prev_tone = []; % previous tonal
        
        if isempty(sel_tonal)
            % New tonal created from selected points
            
            % Initilization of new tonal
            node = tfnode.create(t(1), f(1), 0.0, 0.0);
            new_tonal = tonal(node);
            
            for idx = 2 : length(t)
                node = tfnode.create(t(idx), f(idx), 0.0, 0.0);
                new_tonal.add(node);
            end
            
            % Append new tonal to ground truth tonals
            ground_truth_tonals = [ground_truth_tonals new_tonal];
        else
            while tonal_idx <= length(sel_tonal)
                % Add points and merge tonals while iterating over tonals.
                
                % Required for undo opertation
                orig_sel_tonal = ...
                    [orig_sel_tonal sel_tonal(tonal_idx).clone()];
                
                % ---------------------------------------------------------
                % Prepend points
                %
                t_vec = t(t < sel_tonal(tonal_idx).getFirst.time);
                f_vec = f(t < sel_tonal(tonal_idx).getFirst.time);
                prepend_time_vec = t_vec(t_vec > prev_endTime);
                prepend_freq_vec = f_vec(t_vec > prev_endTime);
                prev_endTime = sel_tonal(tonal_idx).getLast.time;
                % Sort the points in descending order of time
                [prepend_time_sort t_index] = ...
                    sort(prepend_time_vec, 'descend');
                prepend_freq_sort = prepend_freq_vec(t_index);
                for idx = 1 : length(prepend_time_sort)
                    node = tfnode.create(prepend_time_sort(idx),...
                        prepend_freq_sort(idx), 0.0, 0.0);
                    sel_tonal(tonal_idx).addFirst(node);
                end
                % ---------------------------------------------------------
                
                % Merge the tonals
                if ~ isempty(prev_tone)
                    % Prepend previous tonal to current tonal.
                    sel_tonal(tonal_idx).merge_prepend(prev_tone);
                end
                prev_tone = sel_tonal(tonal_idx).clone();
                % clear the selected tonal
                sel_tonal(tonal_idx).clear();
                tonal_idx = tonal_idx + 1;
            end
            
            % -------------------------------------------------------------
            % Append points
            %
            append_time_vec = t(t > prev_tone.getLast.time);
            append_freq_vec = f(t > prev_tone.getLast.time);
            % Sort the points in ascending order of time
            [append_time_sort t_index] = sort(append_time_vec);
            append_freq_sort = append_freq_vec(t_index);
            for idx = 1 : length(append_time_sort)
                node = tfnode.create(append_time_sort(idx), ...
                    append_freq_sort(idx), 0.0, 0.0);
                prev_tone.add(node);
            end
            % -------------------------------------------------------------
            
            % Required for undo opertation
            new_tonal = [new_tonal prev_tone];
            
            % Append new tonal to ground truth tonals
            ground_truth_tonals = [ground_truth_tonals new_tonal];
            
        end
        
        % Record the co-ordinates of points. Required for redrawing points
        % when Undo Operation is performed after Add/Merge.
        [DATA.time_pt DATA.freq_pt] = get_points(pointH);
        
        % Delete current tonal objects from the plot. Plot extended
        % tonal OR Plot new tonal created by adding just points.
        
        if ~ isempty(tonalH)
            % Get the current color of the tonal.
            color = get(tonalH(end), 'Color');
        else
            % Since new tonal is created by adding points assign specific
            % color to it.
            color = 'cyan';
        end
        % Delete tonal object from the plot
        delete(tonalH);
        tonalH = [];
        % Plot new tonal
        new_tonalsH = [new_tonalsH plot_tonal(new_tonal, color)];
        
        clear_data;
    end

    function deleteButton_Callback(hObject, eventdata)
        % Delete selected tonals
        
        if ~ isempty(DATA.time_pt)
            % Empty the co-ordinate record of points
            DATA.time_pt = [];
            DATA.freq_pt = [];
        end
        
        if isempty(sel_tonal)
            errordlg('Tonal/Tonals not selected to delete', 'Tonal Error');
            clear_data;
            return;
        elseif ~ isempty(pointH)
            errordlg(['Point/Points cannot be deleted, only tonal/tonals '...
                'can be deleted'], 'Point/Tonal Error');
            clear_data;
            return;
        end
        
        tonal_idx = 1;
        new_tonal = [];
        orig_sel_tonal =[];
        while tonal_idx <= length(sel_tonal)
            
            % Required for undo opertation
            orig_sel_tonal = [orig_sel_tonal sel_tonal(tonal_idx).clone()];
            
            % clear the selected tonal
            sel_tonal(tonal_idx).clear();
            
            tonal_idx = tonal_idx + 1;
        end
        
        % Delete the selected tonals object from the plot
        delete(tonalH);
        tonalH = [];
        
        clear_data;
    end

    function trimRightButton_Callback(hObject, eventdata)
        % Trim the selected tonal towards right from selected point.
        
        if ~ isempty(DATA.time_pt)
            % Empty the co-ordinate record of points
            DATA.time_pt = [];
            DATA.freq_pt = [];
        end
        
        if isempty(sel_tonal)
            errordlg('Tonal not selected.', 'Tonal Error');
            clear_data;
            return;
        else
            if length(sel_tonal) >= 2
                errordlg('Only one tonal can be trimmed at a time',...
                    'Tonal error');
                clear_data;
                return;
            end
            
            if isempty(pointH)
                errordlg('Point not selected on the tonal.', 'Point Error');
                clear_data;
                return;
            end
        end
        
        new_tonal = [];
        orig_sel_tonal =[];
        new_tonalsH = [];
        % Get the position of a point using the point handle saved earlier
        pos = getPosition(pointH(1));
        
        found_flag = false; % default
        nodeIt = sel_tonal.iterator();
        while nodeIt.hasNext() && found_flag ~= true
            node = nodeIt.next();
            if node.time >= pos(1)
                found_flag = true;
            end
        end
        
        if found_flag == true
            
            temp_tonal = sel_tonal.clone(); % tonal before trimming
            nodeIt = temp_tonal.iterator();
            while nodeIt.hasNext()
                node = nodeIt.next();
                if node.time >= pos(1)
                    sel_tonal.remove(node);
                end
            end
            
            % -----------------------------------------------------------
            % Required for undo opertation
            %
            orig_sel_tonal = [orig_sel_tonal temp_tonal];
            % Modified tonal
            new_tonal = [new_tonal sel_tonal.clone()];
            % -----------------------------------------------------------
            
            % clear the selected tonal
            sel_tonal.clear();
            % Append new tonal to ground truth tonals
            ground_truth_tonals = [ground_truth_tonals new_tonal];
            
            % Get the color of tonal
            color = get(tonalH(1), 'Color');
            % Delete tonal object from the plot
            delete(tonalH);
            tonalH = [];
            % Plot new trimmed tonal
            new_tonalsH = [new_tonalsH plot_tonal(new_tonal, color)];
        else
            set(tonalH, 'HitTest', 'on');
        end
        
        clear_data;
    end

    function trimLeftButton_Callback(hObject, eventdata)
        % Trim the selected tonal towards left from selected point.
        
        if ~ isempty(DATA.time_pt)
            % Empty the co-ordinate record of points
            DATA.time_pt = [];
            DATA.freq_pt = [];
        end
        
        if isempty(sel_tonal)
            errordlg('Tonal not selected.', 'Tonal Error');
            clear_data;
            return;
        else
            if length(sel_tonal) >= 2
                errordlg('Only one tonal can be trimmed at a time',...
                    'Tonal error');
                clear_data;
                return;
            end
            
            if isempty(pointH)
                errordlg('Point not selected on the tonal.', 'Point Error');
                clear_data;
                return;
            end
        end
        
        new_tonal = [];
        orig_sel_tonal =[];
        new_tonalsH = [];
        % Get the position of a point using the point handle saved earlier
        pos = getPosition(pointH(1));
        
        found_flag = false; % default
        nodeIt = sel_tonal.iterator();
        while nodeIt.hasNext() && found_flag ~= true
            node = nodeIt.next();
            if node.time <= pos(1)
                found_flag = true;
            end
        end
        
        if found_flag == true
            
            temp_tonal = sel_tonal.clone(); % tonal before trimming
            nodeIt = temp_tonal.iterator();
            while nodeIt.hasNext()
                node = nodeIt.next();
                if node.time <= pos(1)
                    sel_tonal.remove(node);
                end
            end
            
            % -----------------------------------------------------------
            % Required for undo opertation
            %
            orig_sel_tonal = [orig_sel_tonal temp_tonal];
            % Modified tonal
            new_tonal = [new_tonal sel_tonal.clone()];
            % -----------------------------------------------------------
            
            % clear the selected tonal
            sel_tonal.clear();
            % Append new tonal to ground truth tonals
            ground_truth_tonals = [ground_truth_tonals new_tonal];
            
            % Get the color of tonal
            color = get(tonalH(1), 'Color');
            % Delete tonal object from the plot
            delete(tonalH);
            tonalH = [];
            % Plot new trimmed tonal
            new_tonalsH = [new_tonalsH plot_tonal(new_tonal, color)];
        else
            set(tonalH, 'HitTest', 'on');
        end
        
        clear_data;
    end

    function smoothButton_Callback(hObject, eventdata)
        % Smooth selected tonals
        
        if ~ isempty(DATA.time_pt)
            % Empty the co-ordinate record of points
            DATA.time_pt = [];
            DATA.freq_pt = [];
        end
        
        if isempty(sel_tonal)
            errordlg('Tonal/Tonals not selected to smooth', 'Tonal Error');
            clear_data;
            return;
        elseif ~ isempty(pointH)
            errordlg(['Point/Points cannot be smoothed, only tonal/tonals '...
                'can be smoothed'], 'Point/Tonal Error');
            clear_data;
            return;
        end
        
        set(HANDLES.fig, 'Pointer', 'watch');
        import tonals.*;
        
        tonal_idx = 1;
        new_tonal = [];
        new_tonalsH = [];
        orig_sel_tonal =[];
        while tonal_idx <= length(sel_tonal)
            
            % Required for undo opertation
            orig_sel_tonal = [orig_sel_tonal sel_tonal(tonal_idx).clone()];
            
            % New tonal
            tone = tonal();
            grdIt = sel_tonal(tonal_idx).iterator();
            while grdIt.hasNext()
                node = grdIt.next();
                % New node created
                new_node = tfnode.create(node.time, node.freq, 0.0, ...
                    node.phase);
                tone.add(new_node);
            end
            
            % Get time and frequency of tonal.
            t = tone.get_time();
            f = tone.get_freq();
            poly_freq = polyval(polyfit(t, f, DATA.poly_deg), t);
            
            for freq_idx = 1 : length(poly_freq)
                node = tone.get(freq_idx - 1);
                node.freq = poly_freq(freq_idx);
            end
            
            % -----------------------------------------------------------
            % Required for undo opertation
            %
            % New tonal
            new_tonal = [new_tonal tone];
            % -----------------------------------------------------------
            
            % Clear the selected tonal
            sel_tonal(tonal_idx).clear();
            % Append new tonal to ground truth tonals
            ground_truth_tonals = [ground_truth_tonals tone];
            
            % Plot new smoothened tonal
            
            % Get the color of tonal
            color = get(tonalH(tonal_idx), 'Color');
            % Delete the selected tonals object from the plot
            delete(tonalH(tonal_idx));
            % Plot the tonal
            new_tonalsH = [new_tonalsH plot_tonal(tone, color)];
            tonal_idx = tonal_idx + 1;
        end
        
        tonalH = [];
        set(HANDLES.fig, 'Pointer', 'arrow');
        clear_data;
        
    end

    function splineFitButton_Callback(hObject, eventdata)
        % Spline fit.
        
        if ~ isempty(DATA.time_pt)
            % Empty the co-ordinate record of points
            DATA.time_pt = [];
            DATA.freq_pt = [];
        end
        
        if isempty(sel_tonal)
            errordlg('Tonal/Tonals not selected to compute spline fit',...
                'Tonal Error');
            clear_data;
            return;
        elseif ~ isempty(pointH)
            errordlg('Spline fit cannot be computed on point/points',...
                'Point/Tonal Error');
            clear_data;
            return;
        end
        
        set(HANDLES.fig, 'Pointer', 'watch');
        import tonals.*;
        
        tonal_idx = 1;
        new_tonal = [];
        orig_sel_tonal =[];
        new_tonalsH = [];
        while tonal_idx <= length(sel_tonal)
            
            % Required for undo opertation
            orig_sel_tonal = [orig_sel_tonal sel_tonal(tonal_idx).clone()];
            
            % Get time and frequency of tonal.
            t = sel_tonal(tonal_idx).get_time();
            f = sel_tonal(tonal_idx).get_freq();
            
            % Fit a cubic spline to noisy data
            knots = 6;
            c_splineNoise = splinefit(t, f, knots);
            tt = t(1):thr.advance_s:t(end);
            ff = ppval(c_splineNoise, tt);
            
            % New tonal
            tone = tonal();
            for idx = 1 : length(tt);
                % New node created
                new_node = ...
                    tfnode.create(tt(idx), ff(idx), 0.0, 0.0);
                tone.add(new_node);
            end
            
            % -----------------------------------------------------------
            % Required for undo opertation
            %
            % New tonal
            new_tonal = [new_tonal tone];
            % -----------------------------------------------------------
            
            % clear the selected tonal
            sel_tonal(tonal_idx).clear();
            % Append new tonal to ground truth tonals
            ground_truth_tonals = [ground_truth_tonals tone];
            
            % Plot new spline fitted tonal
            
            % Get the color of tonal
            color = get(tonalH(tonal_idx), 'Color');
            % Delete the selected tonals object from the plot
            delete(tonalH(tonal_idx));
            % Plot the tonal
            new_tonalsH = [new_tonalsH plot_tonal(tone, color)];
            tonal_idx = tonal_idx + 1;
        end
        
        tonalH = [];
        set(HANDLES.fig, 'Pointer', 'arrow');
        clear_data;
        
    end

    function undoButton_Callback(hObject, eventdata)
        % Undo's the last operation performed.
        
        % clear the selection
        clear_data;
        
        if isempty(orig_sel_tonal) && ~ isempty(new_tonal)
            % New tonal was created by just adding points, so clear
            % the last entry in ground truth tonal as we had added the
            % newly created tonal at the end.
            
            delete(new_tonalsH);
            ground_truth_tonals(end).clear();
        end
        
        if ~ isempty(orig_sel_tonal)
            
            % The hue, saturation, value color map is used.
            colorsN = 20;
            colorMap = hsv(colorsN);
            colorMap = colorMap(randperm(colorsN), :);
            cidx = 1;
            
            if isempty(new_tonal)
                % undo delete operation
                
                % Add back the original tonals
                for tonal_idx = 1 : length(orig_sel_tonal)
                    ground_truth_tonals = [ground_truth_tonals orig_sel_tonal(tonal_idx)];
                    
                    % Plot the tonal
                    plot_tonal(orig_sel_tonal(tonal_idx), colorMap(cidx, :));
                    % Change color for the next edge
                    cidx = mod(cidx, colorsN) + 1;
                end
            elseif length(new_tonal) == 1
                % undo Add/Merge; Trim; Smooth; Splinefit operation
                
                delete(new_tonalsH);
                % Remove the recently added tonal
                ground_truth_tonals(end).clear();
                
                % Add back the original tonals
                for tonal_idx = 1 : length(orig_sel_tonal)
                    ground_truth_tonals = [ground_truth_tonals orig_sel_tonal(tonal_idx)];
                    
                    % Plot the tonal
                    plot_tonal(orig_sel_tonal(tonal_idx), colorMap(cidx, :));
                    % Change color for the next edge
                    cidx = mod(cidx, colorsN) + 1;
                end
            elseif length(new_tonal) > 1
                % undo Smooth/Splinefit operation
                
                delete(new_tonalsH);
                % Remove the recently modified tonals
                for tonal_idx = length(orig_sel_tonal)-1:-1:0
                    ground_truth_tonals(end - tonal_idx).clear();
                end
                
                % Add back the original tonals
                for tonal_idx = 1 : length(orig_sel_tonal)
                    ground_truth_tonals = [ground_truth_tonals orig_sel_tonal(tonal_idx)];
                    
                    % Plot the tonal
                    plot_tonal(orig_sel_tonal(tonal_idx), colorMap(cidx, :));
                    % Change color for the next edge
                    cidx = mod(cidx, colorsN) + 1;
                end
            end
        end
        
        if ~ isempty(DATA.time_pt)
            for idx = 1:length(DATA.time_pt)
                % Redraw points
                draw_point(HANDLES.imageH, eventdata, DATA.time_pt(idx),...
                    DATA.freq_pt(idx)/scale);
            end
        end
        DATA.time_pt = [];
        DATA.freq_pt = [];
        
        new_tonal = [];
        orig_sel_tonal = [];
        new_tonalsH = [];
        
    end

    function saveButton_Callback(hObject, eventdata)
        % Save tonals
        
        import tonals.*;
        % build linked list to pass to save function
        whistles = java.util.LinkedList();
        for tonal_idx = 1:length(ground_truth_tonals)
            if ~ ground_truth_tonals(tonal_idx).isEmpty()
                whistles.add(ground_truth_tonals(tonal_idx));
            end
        end
        filename = strrep(Filename, '.wav', '.bin');
        dtTonalsSave(filename, whistles, true);
    end

    function resetButton_Callback(hObject, eventdata)
        % Reset the ground truth tonal
        
        clear_data;
        set(HANDLES.fig, 'Pointer', 'watch');
        
        if ~ isempty(ground_truth_tonals)
            ground_truth_tonals = [];
        end
        
        % Initialize ground truth tonals from detected tonals
        init_ground_truth_tonal;
        
        % Get the current popup selection and save it so that we can use it
        % when we redraw the figure.
        popup_value = get(HANDLES.popup, 'Value');
        
        new_tonal = [];
        orig_sel_tonal = [];
        new_tonalsH = [];
        clf;
        gui_components;
        % Plot
        plotSpec_detected_tonal;
        dtBrightContrast(HANDLES.imageH, DATA.bright_dB, DATA.contrast_dB);
        set(HANDLES.fig, 'Pointer', 'arrow');
    end

    function clearButton_Callback(hObject, eventdata)
        % Clears the data
        clear_data;
    end

    function startButton_Callback(hObject, eventdata)
        % Start of the audio recording
        
        set(HANDLES.fig, 'Pointer', 'watch');
        blkstart_s = Start_s;
        blkstop_s = min(blkstart_s + DATA.plot_len, Stop_s);
        
        % Get the current popup selection and save it so that we can use it
        % when we redraw the figure.
        popup_value = get(HANDLES.popup, 'Value');
        % Get the position of points before they are deleted, so that we
        % can redraw the points once the image is redrawn.
        [time freq] = get_points(pointH);
        
        new_tonal = [];
        orig_sel_tonal = [];
        new_tonalsH = [];
        clear_data;
        clf;
        gui_components;
        plotSpec_detected_tonal;
        dtBrightContrast(HANDLES.imageH, DATA.bright_dB, DATA.contrast_dB);
        % Redraw the points
        for idx=1:length(time)
            draw_point(HANDLES.imageH, eventdata, time(idx),...
                freq(idx)/scale);
        end
        set(HANDLES.fig, 'Pointer', 'arrow');
    end

    function prevButton_Callback(hObject, eventdata)
        % Prev time period
        
        if (blkstart_s - DATA.time_step) >= Start_s
            set(HANDLES.fig, 'Pointer', 'watch');
            blkstart_s = blkstart_s - DATA.time_step;
            blkstop_s = min(blkstart_s + DATA.plot_len, Stop_s);
            
            % Get the current popup selection and save it so that we can
            % use it when we redraw the figure.
            popup_value = get(HANDLES.popup, 'Value');
            % Get the position of points before they are deleted,so that we
            % can redraw the points once the image is redrawn.
            [time freq] = get_points(pointH);
            
            new_tonal = [];
            orig_sel_tonal = [];
            new_tonalsH = [];
            clear_data;
            clf;
            gui_components;
            plotSpec_detected_tonal;
            dtBrightContrast(HANDLES.imageH, DATA.bright_dB,...
                DATA.contrast_dB);
            % Redraw the points
            for idx=1:length(time)
                draw_point(HANDLES.imageH, eventdata, time(idx),...
                    freq(idx)/scale);
            end
            set(HANDLES.fig, 'Pointer', 'arrow');
        end
    end

    function nextButton_Callback(hObject, eventdata)
        % Next time period
        
        if (blkstart_s + DATA.time_step) < Stop_s
            set(HANDLES.fig, 'Pointer', 'watch');
            blkstart_s = blkstart_s + DATA.time_step;
            blkstop_s = min(blkstart_s + DATA.plot_len, Stop_s);
            DATA.plot_len = blkstop_s - blkstart_s;
            lengthStr_s = num2str(DATA.plot_len);
            % Get the current popup selection and save it so that we can
            % use it when we redraw the figure.
            popup_value = get(HANDLES.popup, 'Value');
            % Get the position of points before they are deleted, so that we
            % can redraw the points once the image is redrawn.
            [time freq] = get_points(pointH);
            
            new_tonal = [];
            orig_sel_tonal = [];
            new_tonalsH = [];
            clear_data;
            clf;
            gui_components;
            plotSpec_detected_tonal;
            dtBrightContrast(HANDLES.imageH, DATA.bright_dB,...
                DATA.contrast_dB);
            % Redraw the points
            for idx=1:length(time)
                draw_point(HANDLES.imageH, eventdata, time(idx),...
                    freq(idx)/scale);
            end
            set(HANDLES.fig, 'Pointer', 'arrow');
        end
    end

    function stopButton_Callback(hObject, eventdata)
        % End of the audio recording
        
        set(HANDLES.fig, 'Pointer', 'watch');
        blkstop_s = Stop_s;
        blkstart_s = max(blkstop_s - DATA.plot_len, Start_s);
        
        % Get the current popup selection and save it so that we can use it
        % when we redraw the figure.
        popup_value = get(HANDLES.popup, 'Value');
        % Get the position of points before they are deleted, so that we
        % can redraw the points once the image is redrawn.
        [time freq] = get_points(pointH);
        
        new_tonal = [];
        orig_sel_tonal = [];
        new_tonalsH = [];
        clear_data;
        clf;
        gui_components;
        plotSpec_detected_tonal;
        dtBrightContrast(HANDLES.imageH, DATA.bright_dB, DATA.contrast_dB);
        % Redraw the points
        for idx=1:length(time)
            draw_point(HANDLES.imageH, eventdata, time(idx),...
                freq(idx)/scale);
        end
        set(HANDLES.fig, 'Pointer', 'arrow');
    end

%  Edit Callback ----------------------------------------

    function editcutoff_Callback(hObject, eventdata)
        % Edit box for specifying the high end cutoff (Hz).
        
        user_entry = str2double(get(hObject, 'string'));
        if isnan(user_entry)
            errordlg('You must enter a numeric value', 'Bad Input')
            set(hObject, 'String', DATA.high_cutoff_Hz);
            return;
        else
            if (user_entry <= thr.low_cutoff_Hz)
                errordlg(['High end cutoff cannot be less than lowest '...
                    'frequency'], 'Bad Input');
                set(hObject, 'String', DATA.high_cutoff_Hz);
                return;
            end
            DATA.high_cutoff_Hz = user_entry;
            highCutoffStr_Hz = num2str(user_entry); % default
            % Get the position of points before they are deleted, so
            % that we can redraw the points once the image is redrawn.
            [time freq] = get_points(pointH);
            clear_data;
            % Get the current popup selection and save it so that we
            % can use it when we redraw the figure.
            popup_value = get(HANDLES.popup, 'Value');
            
            new_tonal = [];
            orig_sel_tonal = [];
            new_tonalsH = [];
            clf;
            gui_components;
            plotSpec_detected_tonal;
            dtBrightContrast(HANDLES.imageH, DATA.bright_dB,...
                DATA.contrast_dB);
            % Redraw the points
            for idx=1:length(time)
                draw_point(HANDLES.imageH, eventdata, time(idx),...
                    freq(idx)/scale);
            end
        end
    end

    function editsmooth_Callback(hObject, eventdata)
        % Edit box for specifying the polynomial degree.
        
        user_entry = str2double(get(hObject, 'string'));
        if isnan(user_entry)
            errordlg('You must enter a numeric value', 'Bad Input')
            set(hObject, 'String', DATA.poly_deg);
            return;
        else
            DATA.poly_deg = user_entry;
            polyStr_degree = num2str(user_entry); % default
        end
    end

    function editplotLength_Callback(hObject, eventdata)
        % Edit box for specifying the plot length.
        
        user_entry = str2double(get(hObject, 'string'));
        if isnan(user_entry)
            errordlg('You must enter a numeric value', 'Bad Input')
            set(hObject, 'String', DATA.plot_len);
            return;
        else
            if user_entry == 0
                errordlg('Plot Length must be greater than 0', 'Bad Input')
                set(hObject, 'String', DATA.plot_len);
                return;
            end
            if user_entry < 0
                user_entry = user_entry * -1;
            end
            set(hObject, 'String', user_entry);
            DATA.plot_len = user_entry;
            lengthStr_s = num2str(user_entry);
            if (blkstart_s + DATA.plot_len) <= Stop_s
                blkstop_s = blkstart_s + DATA.plot_len;
                % Get the position of points before they are deleted, so
                % that we can redraw the points once the image is redrawn.
                [time freq] = get_points(pointH);
                clear_data;
                
                % Get the current popup selection and save it so that we
                % can use it when we redraw the figure.
                popup_value = get(HANDLES.popup, 'Value');
                
                new_tonal = [];
                orig_sel_tonal = [];
                new_tonalsH = [];
                clf;
                gui_components;
                plotSpec_detected_tonal;
                dtBrightContrast(HANDLES.imageH, DATA.bright_dB,...
                    DATA.contrast_dB);
                % Redraw the points
                for idx=1:length(time)
                    draw_point(HANDLES.imageH, eventdata, time(idx),...
                        freq(idx)/scale);
                end
            end
        end
    end

    function edittimeStep_Callback(hObject, eventdata)
        % Edit box for specifying the time step. Move N sec. forward or
        % backward from current start time of the block.
        
        user_entry = str2double(get(hObject, 'string'));
        if isnan(user_entry)
            errordlg('You must enter a numeric value', 'Bad Input')
            set(hObject, 'String', DATA.time_step);
            return;
        else
            if user_entry < 0
                user_entry = user_entry * -1;
            end
            set(hObject, 'String', user_entry);
            DATA.time_step = user_entry;
            stepStr_s = num2str(user_entry);
        end
    end

    function editthr_Callback(hObject, eventdata)
        % Edit box for specifying the SNR (threshold energy).
        
        user_entry = str2double(get(hObject, 'string'));
        if isnan(user_entry)
            errordlg('You must enter a numeric value', 'Bad Input')
            set(hObject, 'String', DATA.thr_energy_dB);
            return;
        else
            if user_entry < 0
                user_entry = user_entry * -1;
            end
            set(hObject, 'String', user_entry);
            DATA.thr_energy_dB = user_entry;
            thresholdStr_dB = num2str(user_entry);
            if get(HANDLES.radiothrOn, 'Value') == ...
                    get(HANDLES.radiothrOn, 'Max');
                
                % Get the position of points before they are deleted, so
                % that we can redraw the points once the image is redrawn.
                [time freq] = get_points(pointH);
                clear_data;
                
                % Get the current popup selection and save it so that we
                % can use it when we redraw the figure.
                popup_value = get(HANDLES.popup, 'Value');
                
                new_tonal = [];
                orig_sel_tonal = [];
                new_tonalsH = [];
                clf;
                gui_components;
                plotSpec_detected_tonal;
                dtBrightContrast(HANDLES.imageH, DATA.bright_dB,...
                    DATA.contrast_dB);
                % Redraw the points
                for idx=1:length(time)
                    draw_point(HANDLES.imageH, eventdata, time(idx),...
                        freq(idx)/scale);
                end
            end
        end
    end

    function editBright_Callback(hObject, eventdata)
        % Edit box for Brightness
        
        user_entry = str2double(get(hObject, 'string'));
        if isnan(user_entry)
            errordlg('You must enter a numeric value', 'Bad Input')
            set(hObject, 'String', num2str(DATA.bright_dB));
            return;
        else
            if (DATA.minbright_dB > user_entry ||...
                    DATA.maxbright_dB < user_entry)
                errordlg('You have crossed the limit', 'Bad Input')
                set(hObject, 'String', num2str(DATA.bright_dB));
                return;
            end
            DATA.bright_dB = user_entry;
            brightStr_dB = num2str(user_entry);
            dtBrightContrast(HANDLES.imageH, DATA.bright_dB, DATA.contrast_dB);
            if DATA.bright_dB ~= get(HANDLES.scrollBright, 'Value')
                set(HANDLES.scrollBright, 'Value', DATA.bright_dB);
            end
        end
    end

    function editContrast_Callback(hObject, eventdata)
        % Edit box for Contrast
        
        user_entry = str2double(get(hObject, 'string'));
        if isnan(user_entry)
            errordlg('You must enter a numeric value', 'Bad Input')
            set(hObject, 'String', num2str(DATA.contrast_dB));
            return;
        else
            if (DATA.mincontrast_dB > user_entry ||...
                    DATA.maxcontrast_dB < user_entry)
                errordlg('You have crossed the limit', 'Bad Input')
                set(hObject, 'String', num2str(DATA.contrast_dB));
                return;
            end
            DATA.contrast_dB = user_entry;
            contrastStr_dB = num2str(user_entry);
            dtBrightContrast(HANDLES.imageH, DATA.bright_dB, DATA.contrast_dB);
            if DATA.contrast_dB ~= get(HANDLES.scrollContrast, 'Value')
                set(HANDLES.scrollContrast, 'Value', DATA.contrast_dB);
            end
        end
    end

    function editstartTime_Callback(hObject, eventdata)
        % Edit start time of the plot.
        % User can move to arbitrary point in the recording.
        
        start_time = str2double(get(hObject, 'string'));
        if isnan(start_time)
            errordlg('You must enter a numeric value', 'Bad Input')
            set(hObject, 'String', num2str(blkstart_s));
            return;
        else
            if Start_s <= start_time && start_time < Stop_s
                blkstart_s = start_time;
                blkstop_s = min(start_time + DATA.plot_len, Stop_s);
                DATA.plot_len = blkstop_s - blkstart_s;
                lengthStr_s = num2str(DATA.plot_len);
                
                % Get the current popup selection and save it so that we
                % can use it when we redraw the figure.
                popup_value = get(HANDLES.popup, 'Value');
                % Get the position of points before they are deleted, so
                % that we can redraw the points once the image is redrawn.
                [time freq] = get_points(pointH);
                
                new_tonal = [];
                orig_sel_tonal = [];
                new_tonalsH = [];
                clear_data;
                clf;
                gui_components;
                plotSpec_detected_tonal;
                dtBrightContrast(HANDLES.imageH, DATA.bright_dB,...
                    DATA.contrast_dB);
                % Redraw the points
                for idx=1:length(time)
                    draw_point(HANDLES.imageH, eventdata, time(idx),...
                        freq(idx)/scale);
                end
            end
        end
    end

%   Radio Button callback -------------------------------

    function thronRadio_Callback(hObject, eventdata)
        % Redraw the image spectrogram with threshold on
        
        if get(hObject, 'Value') == get(hObject, 'Max')
            if DATA.threshold == true
                return;
            else
                set(HANDLES.fig, 'Pointer', 'watch');
                set(HANDLES.radiothrOff, 'Value', 0);
                thr_on = true;
                thr_off = false;
                DATA.threshold = true;
                hold off;
                % Get the position of points before they are deleted, so
                % that we can redraw the points once the image is redrawn.
                [time freq] = get_points(pointH);
                clear_data;
                
                % Get the current popup selection and save it so that we
                % can use it when we redraw the figure.
                popup_value = get(HANDLES.popup, 'Value');
                
                new_tonal = [];
                orig_sel_tonal = [];
                new_tonalsH = [];
                clf;
                gui_components;
                plotSpec_detected_tonal;
                dtBrightContrast(HANDLES.imageH, DATA.bright_dB,...
                    DATA.contrast_dB);
                % Redraw the points
                for idx=1:length(time)
                    draw_point(HANDLES.imageH, eventdata, time(idx),...
                        freq(idx)/scale);
                end
                set(HANDLES.fig, 'Pointer', 'arrow');
            end
        else
            % Radio button is not selected.
        end
    end

    function throffRadio_Callback(hObject, eventdata)
        % Redraw the image spectrogram with threshold off
        
        if get(hObject, 'Value') == get(hObject, 'Max')
            if DATA.threshold == false
                return;
            else
                set(HANDLES.fig, 'Pointer', 'watch');
                thr_off = true;
                thr_on = false;
                DATA.threshold = false;
                hold off;
                % Get the position of points before they are deleted, so
                % that we can redraw the points once the image is redrawn.
                [time freq] = get_points(pointH);
                clear_data;
                
                % Get the current popup selection and save it so that we
                % can use it when we redraw the figure.
                popup_value = get(HANDLES.popup, 'Value');
                
                new_tonal = [];
                orig_sel_tonal = [];
                new_tonalsH = [];
                clf;
                gui_components;
                plotSpec_detected_tonal;
                dtBrightContrast(HANDLES.imageH, DATA.bright_dB,...
                    DATA.contrast_dB);
                % Redraw the points
                for idx=1:length(time)
                    draw_point(HANDLES.imageH, eventdata, time(idx),...
                        freq(idx)/scale);
                end
                set(HANDLES.fig, 'Pointer', 'arrow');
            end
        else
            % Radio button is not selected.
        end
    end

    function closerqst_CallBack(hObject, eventdata)
        % User-defined close request function
        
        user_choice = questdlg('Close This Figure?',...
            'Close Request Function', 'Yes', 'No', 'Yes');
        switch user_choice
            case 'Yes'
                delete(hObject)
            case 'No'
                return
        end
    end

%   Scroll bar callback ----------------------------------

    function scrollBright_Callback(hObject, eventdata)
        % Control brightness of the spectrogram
        
        DATA.bright_dB = round(get(hObject, 'Value'));
        dtBrightContrast(HANDLES.imageH, DATA.bright_dB, DATA.contrast_dB);
        if DATA.bright_dB ~= str2double(get(HANDLES.editbright, 'String'))
            set(HANDLES.editbright, 'String', num2str(DATA.bright_dB));
            brightStr_dB = num2Str(DATA.bright_dB);
        end
    end

    function scrollContrast_Callback(hObject, eventdata)
        % Control contrast of the spectrogram
        
        DATA.contrast_dB = round(get(hObject, 'Value'));
        dtBrightContrast(HANDLES.imageH, DATA.bright_dB, DATA.contrast_dB);
        if DATA.contrast_dB ~= str2double(get(HANDLES.editcontrast, 'String'))
            set(HANDLES.editcontrast, 'String', num2str(DATA.contrast_dB));
            contrastStr_dB = num2Str(DATA.contrast_dB);
        end
    end

%   Utility functions --------------------------------------------

    function [time, freq] = get_points(points, current_point)
        % convert points to time X freq nodes
        % The optional current_point is assumed to be a member of points.
        % When it is present, any other node in points that occurs at the
        % same time as current_point will not be returned in the
        % list of times and frequencies.
        
        % get list of times and frequencies
        N = length(points);
        time = zeros(N,1);
        freq = zeros(N,1);
        for idx=1:N
            posn = getPosition(points(idx));
            time(idx) = posn(1);
            freq(idx) = posn(2) * scale;
        end
        
        if nargin > 1 && ~ isempty(current_point)
            % get a list of all points at the current time
            
            if ~ isnumeric(current_point)
                % current_point is handle of impoint. Get the position
                % using the handle and update the current_point with the
                % position of the point.
                current_point = getPosition(current_point);
            end
            
            duplicates = find(time == current_point(1));
            if length(duplicates) > 1
                current_idx = find(freq(duplicates) == current_point(2)*scale);
                if length(current_idx) >= 2
                    for idx = 1 : length(current_idx) - 1
                        time(duplicates(current_idx(idx))) = [];
                        freq(duplicates(current_idx(idx))) = [];
                    end
                else
                    duplicates(current_idx) = [];
                    % remove the duplicates
                    time(duplicates) = [];
                    freq(duplicates) = [];
                end
            end
        end
        % sort points
        [time time_order] = sort(time);
        freq = freq(time_order);
    end

    function delete_duplicate_timept()
        % Delete first draggable point with same time. Only first match is
        % considered.
        
        time = [];
        if length(pointH) > 1
            for idx = 1 : length(pointH)
                existing_pt = getPosition(pointH(idx));
                time = [time existing_pt(1)];
            end
            for idx = 1 : length(time)
                duplicates  = find(time == time(idx));
                if length(duplicates) > 1
                    delete (pointH(duplicates(1)));
                    pointH(duplicates(1)) = [];
                    break;
                end
            end
        end
    end

    function new_tonalH = plot_tonal(tonal, color)
        % Plot the tonal and set button down function on the tonal
        
        time_newT = tonal.get_time();
        freq_newT = tonal.get_freq() / scale;
        new_tonalH = plot(time_newT, freq_newT, 'LineStyle', LineStyle,...
            'Color', color, 'LineWidth', 2);
        set(new_tonalH, 'UserData', tonal);
        % Tonal selection callback
        set(new_tonalH, 'ButtonDownFcn', @selectTonal_Callback);
    end

    function clear_data
        % Clears all the data related to the selection
        
        % erase temporary curves
        if ishandle(proposedH)
            delete(proposedH);
        end
        
        % empty the vector of selected tonals
        if ~isempty(sel_tonal)
            sel_tonal = [];
        end
        
        % Delete tonal_markerH object from plot
        delete(tonal_markerH);
        tonal_markerH = [];
        
        % Delete recently selected point objects from the plot
        if ~ isempty(pointH)
            delete(pointH);
            pointH = [];
        end
        
        % Turn the HitTest property to 'on' which may have been turned
        % 'off' during selection of tonal for trimming.
        if trim_flag
            if ~ isempty(tonalH)
                val = get(tonalH, 'HitTest');
                if strcmp(val,'off')
                    set(tonalH, 'HitTest', 'on');
                end
            end
        end
        
        % Empty the vector of selected tonal handles
        if ~isempty(tonalH)
            tonalH = [];
        end
    end

end
