function varargout = flowBenchDataSelector(varargin)
% flowBenchDataSelector  Callback routines for GUI used to average raw data from Agilent 34970A
%                    This M-file supplies handlers for UI controls in flowBenchDataSelector.fig
%
%      FLOWBENCHDATASELECTOR, by itself, creates a new FLOWBENCHDATASELECTOR or raises the existing
%      singleton*.
%
%      H = FLOWBENCHDATASELECTOR returns the handle to a new FLOWBENCHDATASELECTOR or the handle to
%      the existing singleton*.
%
%      FLOWBENCHDATASELECTOR('Property','Value',...) creates a new FLOWBENCHDATASELECTOR using the
%      given property value pairs. Unrecognized properties are passed via
%      varargin to flowBenchDataSelector_OpeningFcn.  This calling syntax produces a
%      warning when there is an existing singleton*.
%
%      FLOWBENCHDATASELECTOR('CALLBACK') and FLOWBENCHDATASELECTOR('CALLBACK',hObject,...) call the
%      local function named CALLBACK in FLOWBENCHDATASELECTOR.M with the given input
%      arguments.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help flowBenchDataSelector

% Last Modified by GUIDE v2.5 06-Jun-2004 22:28:57

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @flowBenchDataSelector_OpeningFcn, ...
                   'gui_OutputFcn',  @flowBenchDataSelector_OutputFcn, ...
                   'gui_LayoutFcn',  [], ...
                   'gui_Callback',   []);
if nargin & isstr(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT


% --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++
% --- Executes just before flowBenchDataSelector is made visible.
function flowBenchDataSelector_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   unrecognized PropertyName/PropertyValue pairs from the
%            command line (see VARARGIN)

% Choose default command line output for flowBenchDataSelector
handles.output = hObject;

% +++ GWR code start
% 
%  Store inital values of persistant variables in handles
handles.inFileName = 'sampleRaw.dat';
handles.NchanInInfile = 6;
handles.monitorChanIndex = 1;       %  default channel used to select data for averaging
handles.samplesPerReading = 30;

% ---
handles.allNozzleDiameters = [0.75; 1.0; 1.6];
handles.currentNozzleID = 1;
handles.currentNozzleDiameter = handles.allNozzleDiameters(handles.currentNozzleID);

% ---
handles.NreadingsSaved = 0;     %  reset to zero every time the file was saved or new file is opened?
handles.fileWasSaved = false;
handles.aveDataExists = false;  %  hack bcause exist(handles.emfave) fails if handles.emfave does not exist!
handles.dataSelected = false;
% +++ GWR code end

guidata(hObject, handles);     %  update 'handles' object before return to event loop

% UIWAIT makes flowBenchDataSelector wait for user response (see UIRESUME)
% uiwait(handles.figure1);


% --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++
% --- Outputs from this function are returned to the command line.
function varargout = flowBenchDataSelector_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;


% --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++
% --- Executes on button press in keepSelectDataButton.
function keepSelectDataButton_Callback(hObject, eventdata, handles)
% hObject    handle to keepSelectDataButton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

doKeepAveData(hObject, handles);

% --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++
% --- Executes on button press in saveDatasetButton.
function saveDatasetButton_Callback(hObject, eventdata, handles)
% hObject    handle to saveDatasetButton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

try
  doSaveData(hObject, handles);
catch
  errordlg(sprintf('Error while trying to save data:\n\t %s',lasterr));
end

% --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++
% --- Executes during object creation, after setting all properties.
function popupMonitorChannel_CreateFcn(hObject, eventdata, handles)
% hObject    handle to popupMonitorChannel (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end


% --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++
% --- Executes on selection change in popupMonitorChannel.
function popupMonitorChannel_Callback(hObject, eventdata, handles)
% hObject    handle to popupMonitorChannel (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns popupMonitorChannel contents as cell array
%        contents{get(hObject,'Value')} returns selected item from popupMonitorChannel

contents = get(hObject,'String');          % returns popupMonitorChannel contents as cell array
menuVal = contents{get(hObject,'Value')};  % returns selected item from popupMonitorChannel

% --- Match channel number with the available list of channels
%     This allows discontinous channel lists, e.g. 101, 104, 105
chanString = menuVal(1:3);          %  first three characters are channel number 
for i=1:size(handles.channelList)
  if strcmp(handles.channelList(i,:),chanString);
    jmon = i;     %  row index of channelList that matches chanString
    break
  end
end
handles.DataToMonitor = handles.Data(:,jmon);  %  vector of measured values to display
handles.monitorChanIndex = jmon;               %  column in Data array for channel to be averaged

doPlotMonitorChannel(hObject, handles)  %  update the plot
guidata(hObject, handles);              %  update 'handles' object before return to event loop


% --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++
% --- Executes during object creation, after setting all properties.
function popupNsamplesPerAve_CreateFcn(hObject, eventdata, handles)
% hObject    handle to popupNsamplesPerAve (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end


% --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++
% --- Executes on selection change in popupNsamplesPerAve.
function popupNsamplesPerAve_Callback(hObject, eventdata, handles)
% hObject    handle to popupNsamplesPerAve (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

contents = get(hObject,'String');          % returns popupNsamplesPerAve contents as cell array
menuVal = contents{get(hObject,'Value')};  % returns selected item from popupNsamplesPerAve
if strcmp(lower(menuVal(1)),'o')           %  first letter of "other"
  nsamples = dialogGetNumberSamples(handles.samplesPerReading)
  doRebuildNsamplesMenu(hObject, handles, contents, nsamples);
else
  try
    nsamples = str2num(menuVal);
  catch
    error(sprintf('Could not extract "%s" as a number',menuVal))
  end
end
% --- Store new value for samplesPerReading, and update static text display
handles.samplesPerReading = nsamples;
set(handles.displayNsamples,'String',sprintf('%4d Samples',nsamples));

guidata(hObject, handles);      %  update 'handles' object before return to event loop

% ==============================================
function doRebuildNsamplesMenu(hObject, handles, current, newValue)
% doRebuildNsamplesMenu  Construct new string

% --- Get list of numeric values from menu string
knum = 0;
for i=1:length(current)
  v = str2num(current{i});   %  stores numeric value of menu item unless it is a string
  if ~isempty(v)             %  v is empty if str2num could not convert current{i}
    knum = knum + 1;         %  which will be the case if current{i} = 'Other ...'
    values(knum) = fix(v);
  end    
end
values(knum+1) = newValue;
values = sort(values);
iselected = find(values==newValue);
nnum = length(values);
for i=1:nnum
  newString{i} = sprintf('%-d',values(i));
end
newString{nnum+1} = 'Other ...';
set(handles.popupNsamplesPerAve,'String',newString);
set(handles.popupNsamplesPerAve,'Value',iselected);
guidata(hObject, handles);          %  update 'handles' object before return to event loop

% --------------------------------------------------------------------
function FileMenu_Callback(hObject, eventdata, handles)
% hObject    handle to FileMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)


% --------------------------------------------------------------------
function FileOpen_Callback(hObject, eventdata, handles)
% hObject    handle to FileOpen (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

try
  status = doLoadNewData(hObject, eventdata, handles);
catch
  errordlg(sprintf('Error while trying to load data:\t\t %s',lasterr));
end

% --------------------------------------------------------------------
function fileSave_Callback(hObject, eventdata, handles)
% hObject    handle to fileSave (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

try
  doSaveData(hObject, handles);
catch
  errordlg(sprintf('Error while trying to save data:\n\t %s',lasterr));
end

% --------------------------------------------------------------------
function FileQuit_Callback(hObject, eventdata, handles)
% hObject    handle to FileQuit (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

doQuit(hObject, eventdata, handles);



% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
% **********************************************************************
% vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

% +++++++++++++++++++++++++++++++++++++++++++++++++++
function status = doLoadNewData(hObject, eventdata, handles)
% doLoadNewData  Perform all tasks related to opening of a data file

status = 1;  %  assume positive outcome

% --- Prompt user for the input file
[fileName,pathName] = uigetfile('*.*','Select plain text file for import');
inFile = [pathName,fileName];
handles.inFileName = fileName;

% --- Try to detect the number of data columns in the file
ncol = countColumns(inFile);
nchan = round(ncol/2);
if ncol==0
  msg = {sprintf('No data columns detected in %s.',fileName),...
         'Please check that the file is a tab-delimited plain text file'};
  errordlg(msg);
  return;
elseif abs(ncol-2*nchan)>100*eps
  msg = {sprintf('Number of data columns in %s does not appear to be an even number.',fileName),...
         sprintf('ncol = %g,  nchan = %g',ncol,nchan),...
         ' ',...
         'Please check that the file is a tab-delimited plain text file'};
  errordlg(msg);
  return;
end
handles.NchanInInfile = nchan;
set(handles.displayNcolumns,'String',sprintf('%d Channels in the Input File',nchan));

try
  % ncol = 2*handles.NchanInInfile;   %  time and value for each channel
  nhead = 0;
  nrowlabel = 1;
  [t,D,labels] = loadColData(inFile,ncol,nhead,nrowlabel);
catch
  status = -2;
  errordlg(sprintf('Could not read %s with loadColData.  Please check that the file is plain text',inFile));
  return;
end
% --- Successful reading of file.  Update file name display and set flags for clean start
set(handles.displayFilename,'String',sprintf('Data file: %s',fileName));
handles.fileWasSaved = false;
handles.dataSelected = false;

% --- Copy imported data to 'handles' object for persistent storage
handles.DataTime = t;
handles.Data = D(:,1:2:end);     % Measured data is in columns 2, 4, 6, ... of matrix D
% --- Set monitor channel
if ~exist('handles.monitorChanIndex')
  handles.monitorChanIndex = 1;
end
handles.DataToMonitor = handles.Data(:,handles.monitorChanIndex);  %  this is the measured value to display

% --- Update menu of column labels based on column labels for this file
iend = size(labels,1);
for i=1:size(labels,1)
  if strcmp(deblank(labels(i,:)),'')
    iend = i-1;
    break;
  end
end
labels(iend+1:end,:) = [];                  %  Delete blank rows at end of 'labels' matrix
handles.DataLabels = labels(2:2:iend,:);    %  save column labels
set(handles.popupMonitorChannel,'String',{labels(2:2:iend,:)});    %  update menu items

% --- Build and save a channel list from the column labels
channelList = handles.DataLabels(1,1:3);     %  first three digits are always a number
for i=2:size(handles.DataLabels,1)
  channelList = strvcat(channelList,handles.DataLabels(i,1:3));     %  first three digits are always a number
end
handles.channelList = channelList;

% --- Delete saved data to prepare for next clean data set
if handles.aveDataExists
  msg = {'You have stored average data from a previous data set.     ',...
         'Do you want to keep this data, or discard it before opening',...
         ' the next raw data file?  If you keep the old data, any new',...
         ' averaged data will be combined with the current averaged  ',...
         ' data so that you can save it as a single data set.        '};
  button = questdlg(msg,'Append new data to current data?','Keep','Discard','Help','Keep');
	if strcmp(button,'Discard')
    handles.aveDataExists = false;
    handles.NreadingsSaved = 0;
		handles.NreadingsSaved = 0;
		handles.fileWasSaved = false;   %  reset because new data has not been saved to file
		handles.nave = [];
		handles.istart = [];
		handles.istop = [];
		handles.t = [];
		handles.Dave = [];   %  row vector of average data to keep
		handles.Dstd = [];   %  std of data
		handles.dn = [];
		handles.nid = [];
	end
end
guidata(hObject, handles);      %  update 'handles' object before return to event loop

doPlotMonitorChannel(hObject, handles)

% ---------------------------------------------------------------

% +++++++++++++++++++++++++++++++++++++++++++++++++++
function doPlotMonitorChannel(hObject, handles)

% --- Plot all data in the file
axes(handles.plotAllData);
hp = plot(handles.DataTime,handles.DataToMonitor,'.');
set(gca,'NextPlot','replacechildren');

% --- Check for data in the 'currentReading' plot.  If there is any, delete it
hcrChild = get(handles.plotSelectData,'Children');   %  Does the axes contain stuff: lines, userData?
if ~isempty(hcrChild)
  set(hcrChild,'Xdata',[],'Ydata',[]);   %  delete data from plot
	% ---  Reset the display of the mean and standard deviation
	set(handles.displayNsamples,'String','');
	set(handles.displayAveValue,'String','');
	set(handles.displayStdev,'String','');
end

guidata(hObject, handles);      %  update 'handles' object before return to event loop

% ---------------------------------------------------------------

% +++++++++++++++++++++++++++++++++++++++++++++++++++
function doQuit(hObject, eventdata, handles)
% doQuit  Performs file closing and clean up.  Called in more than one place

% --- Skip warning if user saved file before last data were selected
%     or if no data is in buffers to be saved
if handles.fileWasSaved | (handles.NreadingsSaved==0)
  close(gcbf);
  return;
end

answers = {'Save Data','Don''t Save Data','Cancel'};
ButtonName=questdlg('Before quitting ...', ...
                       'Save before quitting', ...
                       answers{1},answers{2},answers{3},answers{3});
if strcmp(ButtonName,answers{1})
	try
      doSaveData(hObject, handles);
	catch
      errordlg(sprintf('Error while trying to save data:\n\t %s',lasterr));
	end
  close(gcbf);
elseif strcmp(ButtonName,answers{2})
%   fprintf('\n\tDon''t save the data\n');
  close(gcbf);
elseif strcmp(ButtonName,answers{3})
  ;
%   fprintf('\n\tCancel:  return to data selection\n');
else
  error('Test failed: no match of button selection to prompts??');
end

% --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++
% --- Executes on button press in pushbuttonPickData.
function pushbuttonPickData_Callback(hObject, eventdata, handles)
% hObject    handle to pushbuttonPickData (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

axes(handles.plotAllData);      %  set current axes to handles.plotAllData
[xmouse,ymouse] = ginput(1);    %  get one (x,y) pair (turns cursor to cross-hair)
handles.dataSelected = true;
guidata(hObject, handles);    %  update 'handles' object before return to event loop
% --- Find index in time vector near the mouse click
istart = min(find(handles.DataTime>xmouse));
highlightData(hObject, eventdata, handles, istart);


% ==============================================
function doKeepAveData(hObject, handles)
% doKeepAveData  Compute ave and std. dev. of selected data.  Save in handles

if ~handles.dataSelected
  msgbox('Select data by clicking in upper axes before pushing "Keep Data"',...
         'No data selected','help');
  return;
end

% --- Define indices for range of data to keep
ikeep = handles.DataKeepIndex(1):handles.DataKeepIndex(2);

% --- Increment data counters and flags
i = handles.NreadingsSaved + 1;
handles.NreadingsSaved = i;
handles.fileWasSaved = false;   %  reset because new data has not been saved to file
handles.aveDataExists = true;

% --- Process and save the data
handles.nave(i) = handles.samplesPerReading;
handles.istart(i) = handles.DataKeepIndex(1);
handles.istop(i) = handles.DataKeepIndex(2);
handles.t(i) = mean(handles.DataTime(ikeep));
handles.Dave(i,:) = mean(handles.Data(ikeep,:));   %  row vector of average data to keep
handles.Dstd(i,:) = std(handles.Data(ikeep,:));    %  std of data
% --- Add nozzle diameter
handles.nid(i) = handles.currentNozzleID;
handles.dn(i) = handles.allNozzleDiameters(handles.currentNozzleID);

set(handles.displayNsamplesSaved,'String',sprintf('%d Averaged samples',i));

% hp = plot(handles.DataTime,handles.DataToMonitor,'.');
v = handles.DataToMonitor;   vrange = max(v) - min(v);
jmon = handles.monitorChanIndex;
vmax = handles.Dave(i,jmon) + 0.1*vrange;
vmin = handles.Dave(i,jmon) - 0.1*vrange;
axes(handles.plotAllData);
nextPlotProperty = get(gca,'nextPlot');
hold on
drawBox(handles.DataTime(handles.istart(i)),handles.DataTime(handles.istop(i)),vmin,vmax)
hold off
set(gca,'nextPlot',nextPlotProperty);
guidata(hObject, handles);      %  update 'handles' object before return to event loop
% fprintf('\tFinished keeping measurement number %d\n',i);

% ==============================================
function doSaveData(hObject, handles)
% doSaveData  Store average (and std dev) data to MAT file
%
%  Goal:  Create a MAT file with variables holding the average and standard
%         deviations of all measured quantities.
%
%  Steps to achieve the goal:
%    1. Create local variables like 'V101ave','V101std','R102ave','R102std'
%       where variables that begin with 'V' are voltages, and variables that
%       begin with 'R' are resistances.  These new variables store the data
%       in the columns of handles.Dave and handles.Dstd.
%    2. Construct varlist, a string variable containing a comma-separated list
%       of the *names* of the variables to be saved.  Each variable name is
%       surrounded by single quotes.  Thus, a lot of quote-escaping is
%       necessary.  A simple example:   varlist = [''myVarName'']
%    3. Prompt the user for the destination of the file to be saved
%    4. Save the local variables in a MAT file
%
%  The trickiest parts of the preceding steps are achieved with the
%  eval() command.  To create the variable x and assign to the value 3
%  use the command  eval('x=3').  Obviously that simple example is of
%  no intrinsic value.  However, it hints at the syntax of the more powerful
%  command eval(sprintf(''%s=%d'','x',3)).  Suppose now that D is a matrix
%  of values, and we wish to create a column vector from column j of D.
%  Here is one way to do it:
%
%     varname = 'd';       %  new variable to create
%     j = ...              %  column in D matrix
%     eval(sprintf(''%s=D(:,%d)'',varname,j))

% --- Use eval to create variables that are saved to MAT file
nchan = handles.NchanInInfile;
t = handles.t;
nave = handles.nave;
dn = handles.dn*2.54e-2;              % convert inches to m
nid = handles.nid;
for j = 1:nchan
  label = handles.DataLabels(j,:);
  kpstart = findstr('(',label);       %  position of starting parenthesis
  kpend = findstr(')',label);
  units = label(kpstart+1:kpend-1);   %  units of measurement
  channel = label(1:kpstart-1);
  % --- First character(s) of variable names are determined by units
  switch lower(units)
    case 'vdc'
      avename = sprintf('V%save',channel);  %  create variable name like 'V101ave'
      stdname = sprintf('V%sstd',channel);  %  create variable name like 'V101std'
    case 'ohm'
      avename = sprintf('R%save',channel);
      stdname = sprintf('R%sstd',channel);
    case 'c'
      avename = sprintf('T%save',channel);
      stdname = sprintf('T%sstd',channel);
    otherwise
      error(sprintf('Cannot create variable name for units = %s, channel = %s',units,channel));
  end
  % --- Create variables in this (subfunction) workspace.  The variables will
  %     then be saved to a mat file
  eval(sprintf('%s=handles.Dave(:,%d);',avename,j));
  eval(sprintf('%s=handles.Dstd(:,%d);',stdname,j));
  % --- Create string for used in save(...,'mat') command
  theseVars = sprintf('''%s'',''%s''',avename,stdname);   %  string like "'V101ave','V101std'"
  if j==1
    varlist = ['''t'',''nave'',''dn'',''nid'',',theseVars];      %  initialize list of variables
  else
    varlist = [varlist,',',theseVars];   %  append to list of variables
  end
end

% --- Prompt user for destination (directory and file name) for storing average data
[fileName,pathName] = uiputfile('*.mat','Save data file in MAT format');
[pathstr,name,ext,versn] = fileparts(fileName);
if isempty(ext)
  fileName = [fileName,'.mat'];
  ext = '.mat';
end
if ~strcmp(lower(ext),'.mat')
  errordlg('File name must end in .mat:  Data not saved!');
  return
end
theFile = [pathName,fileName];
try
  eval(sprintf('save(''%s'',%s,''-mat'')',theFile,varlist));
  status = 1;
catch
  errordlg('Saving data file was unsuccessful!  Call/send email to Gerry');
  status = -1;
end
guidata(hObject, handles);      %  update 'handles' object before return to event loop

% ==============================================
function highlightData(hObject, eventdata, handles, istart)
% highlightData  Draw box around data user has selected for averaging

% --- Find index of where to start the box
t = handles.DataTime;
nave = handles.samplesPerReading;
vshow = handles.DataToMonitor;

% --- Check to make sure the box fits the data
n = length(t);
istop = istart + nave - 1;
if nave>n   %  --- not enough data for nave
	msg = {sprintf('There are only %d readings in the input file, and you have',n),...
         sprintf('requested an average of %d values.  Please reset the number',nave),...
         sprintf('of readings to average to a number no greater than %d',n),...
         ' ',...
         'In the mean time, the number of readings to average will be set',...
         sprintf('to %d',n)};
	reply = questdlg(msg,'Error Handler','Continue','Quit','Continue');
	if (strcmp(reply,'Quit'))
    doQuit(hObject, eventdata, handles);
    return
  end
  istart = 1;
  istop = n;
  nave = n;
  handles.samplesPerReading = nave;
elseif istop>n   % --- test for selection too close to end of data set
  istop = n;
  istart = istop - nave + 1;
	msg = {'Your data selection is too close to the end of the data set.',...
           sprintf('If you consent, the selection will be moved so that %d data ',nave),...
           'points can still be included in the average',...
           ' ',...
           'If this is acceptable to you, click CONTINUE and then press the',...
           'middle mouse button in the plot window to accept the data.',...
           ' ',...
           'If continuing with the averaging is not acceptable, click QUIT',...
           'to terminate the program and save any pending data.'};
	reply = questdlg(msg,'Error Handler','Continue','Quit','Continue');
	if (strcmp(reply,'Quit'))
    doQuit(hObject, eventdata, handles);
    return
  end
end
% --- Set ilow and ihigh, indices of extra data to show outside of the selection box
ilow = max([istart-10, 1]);   %  max avoids ilow<1
ihigh = min([istop+10, n]);   %  min avoids ihigh>n

% --- Define indices for range of data to keep
ikeep = istart:istop;
axes(handles.plotSelectData);
hp = plot(t(ilow:ihigh),vshow(ilow:ihigh),'.');

% --- Compute values used to update display, not permanent
vave = mean(vshow(ikeep));
vstd = std(vshow(ikeep));
vrange = max(vshow) - min(vshow);
vlow = vave - vrange/10;
vhigh = vave + vrange/10;
% --- Draw a box around data
hold on
drawBox(t(istart),t(istop),vlow,vhigh);
plot([t(ilow) t(ihigh)],[vave vave],'r--');
hold off

% --- Save current limits
handles.DataKeepIndex = [istart istop];

% --- Turn off HitTest property of all children so that ButtonDownFcn selects the axes
ca = get(gca,'Children');
for i=1:length(ca)
  set(ca(i),'HitTest','off');
end

% ---  Update display of the mean and standard deviation
%      Only the display values need to be set here
set(handles.displayNsamples,'String',sprintf('%-4d Samples',nave));
set(handles.displayAveValue,'String',sprintf('Average: %s',floatFormat(vave)));
set(handles.displayStdev,'String',sprintf('Std. Dev.: %s',floatFormat(vstd)));

guidata(hObject, handles);      %  update 'handles' object before return to event loop

% ===========================================
function drawBox(tmin,tmax,vmin,vmax,linestyle)
% drawBox  Draw a red box in the current figure window
if nargin<5, linestyle='r-';  end
plot([tmin tmax tmax tmin tmin],[vmin vmin vmax vmax vmin],linestyle);

% ===========================================
function s = floatFormat(v)
% floatFormat  Select nice format for floating point value

e = fix(abs(log10(v))+0.5);   %  rounded, positive, log 10 exponent
if e<3
  s = sprintf('%6.3f',v);
elseif e<4
  s = sprintf('%8.5f',v);
else
  s = sprintf('%12.3e',v);
end
  
% +++++++++++++++++++++++++++++++++++++++++++++++++++
function nchan = dialogGetNumberChannels(default)
% dialogGetNumberChannels  Present dialog box for user to enter number of channels of data

prompt = {'Enter number of channels in the input data set (raw data):'};
dlg_title = 'Input number of channels';
num_lines = [1 35];
def     = {sprintf('%d',default)};
answer  = inputdlg(prompt,dlg_title,num_lines,def);
nchan = str2num(answer{1});

% +++++++++++++++++++++++++++++++++++++++++++++++++++
function nsamples = dialogGetNumberSamples(default)
% dialogGetNumberSamples  Present dialog box for enterring number of samples to average of data

prompt = {'Enter number of samples for each average value:'};
dlg_title = 'Input number of samples';
num_lines = [1 35];
def     = {sprintf('%d',default)};
answer  = inputdlg(prompt,dlg_title,num_lines,def);
nsamples = str2num(answer{1});

% +++++++++++++++++++++++++++++++++++++++++++++++++++
function n = countColumns(fname)
% countColumns  Count the number non-whitespace strings in first line of a text file
%
% Synopsis:  n = countColumns(fname)
%
% Input: fname = (string) full path to file.  Simple file name is OK if
%                the file is in the current director
%
% Output: n = number of continuous character strings separated by whitespace
%             in the first line of the file.  If the file is a tab-delimited
%             text file, n will be the number of columns in the first line.

try
  fp = fopen(fname,'r');
catch
  errordlg(sprintf('Could not open %s in countColumns()',fname));
end
% --- Read only the first line of the file, and close it.
theLine = fgetl(fp);
fclose(fp);

n = 0;                   % number of strings found
rem = theLine;           % initialize the remainder
while length(rem)>0
  [s,rem] = strtok(rem);  %  store the next string in s, remainder in rem
  n = n + 1;
end


% +++++++++++++++++++++++++++++++++++++++++++++++++
% --- Executes during object creation, after setting all properties.
function popupNozzleDia_CreateFcn(hObject, eventdata, handles)
% hObject    handle to popupNozzleDia (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

% --- Executes on selection change in popupNozzleDia.
function popupNozzleDia_Callback(hObject, eventdata, handles)
% hObject    handle to popupNozzleDia (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns popupNozzleDia contents as cell array
%        contents{get(hObject,'Value')} returns selected item from popupNozzleDia

contents = get(hObject,'String');     %  returns nozzleSize menu contents as cell array
id = get(hObject,'Value');            %  index of nozzle diameter selected
dn = str2double(contents{id});        %  get value of item selected
handles.currentNozzleDiameter = dn;
handles.currentNozzleID = id;
guidata(hObject, handles);   %  updates handles object

% +++++++++++++++++++++++++++++++++++++++++++++++++
% --- Executes on button press in pushbuttonChangeNozzleDia.
function pushbuttonChangeNozzleDia_Callback(hObject, eventdata, handles)
% hObject    handle to pushbuttonChangeNozzleDia (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% --- Use simple built-in 'inputdlg' function to allow editing of nozzle diameters
dn = handles.allNozzleDiameters;
% --- built cell arrays for prompts and current values
n = length(dn);
for i=1:n
  prompt{i} = sprintf('dn(%d)',i);
  default{i} = sprintf('%-8.4f',dn(i));
end
dlg_title = 'Nozzle diameters';
num_lines = repmat([1,30],n,1);     %  control width of input boxes
answer = inputdlg(prompt,dlg_title,num_lines,default);
% --- answer is {} if user clicks cancel
if ~isempty(answer)
  na = size(answer,1);
  vals = zeros(na,1);
  for i=1:na
    dn(i) = str2num(answer{i});
  end
	handles.allNozzleDia = dn;
  set(handles.popupNozzleDia,'String',answer);
	guidata(hObject, handles);   %  updates handles object
end

% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
% --- Executes on button press in pushbuttonLeftNudge.
function pushbuttonLeftNudge_Callback(hObject, eventdata, handles)
% hObject    handle to pushbuttonLeftNudge (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

istart = handles.DataKeepIndex(1) - 1;
highlightData(hObject, eventdata, handles, istart);

% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
% --- Executes on button press in pushbuttonLeftBigNudge.
function pushbuttonLeftBigNudge_Callback(hObject, eventdata, handles)
% hObject    handle to pushbuttonLeftBigNudge (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

istart = handles.DataKeepIndex(1) - 5;
highlightData(hObject, eventdata, handles, istart);

% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
% --- Executes on button press in pushbuttonRightNudge.
function pushbuttonRightNudge_Callback(hObject, eventdata, handles)
% hObject    handle to pushbuttonRightNudge (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

istart = handles.DataKeepIndex(1) + 1;
highlightData(hObject, eventdata, handles, istart);

% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
% --- Executes on button press in pushbuttonRightBigNudge.
function pushbuttonRightBigNudge_Callback(hObject, eventdata, handles)
% hObject    handle to pushbuttonRightBigNudge (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

istart = handles.DataKeepIndex(1) + 5;
highlightData(hObject, eventdata, handles, istart);


% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
% --- Executes on mouse press over figure background.
function figure1_ButtonDownFcn(hObject, eventdata, handles)
% hObject    handle to figure1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)


