function splinetool(x,y)
%SPLINETOOL Experiment with some spline approximation methods.
%
%   SPLINETOOL(X,Y) starts the GUI for experimenting with spline fitting to the
%   data  X, Y  (if X and Y are numerical vectors of the same length>1).
%
%   SPLINETOOL  (without any arguments) prompts you for some data  X, Y,
%   and then both  X  and  Y  may be expressions. In fact,
%   Y  may be the name of a function whose values at  X   are to be used.
%   More than that, the string  X  then supplied may be the name of an M-file 
%   (like 'titanium') whose TWO-argument output provides numerical values 
%   for  X  and  Y . 

%   cb: 5jun99, 13-28jun99, 30jul-15aug99
%   helped by: Penny Anderson, Karen Gondoly, Rob Henson, Maria Karra,
%   Trudi Leone, Cleve Moler, Diana DeMarco, Joe Regensburger, John Wright,
%   and many others at Mathworks.
%   Copyright 1987-2000 by C. de Boor and The MathWorks, Inc.
%   $Revision: 1.18 $  $Date: 2000/08/31 04:07:17 $

updated = '30aug00'; testing = 0;
abouttext = {'Spline Toolbox 3.0'; ...
            'Copyright 1987-2000 C. de Boor and The Mathworks, Inc.'};

if nargin<1  % import data

   if ~isempty(findobj(allchild(0),'name','Spline Tool'))
      answer = questdlg({'SPLINETOOL is already running.';''; ...
               'Do you want to terminate it?';''}, ...
               'No need to start another one..','No','Yes','No');
      if answer(1)=='N'
         return
      else
         action = 'finish';
      end 
   else
      action = 'start';
   end
    
else
   if isstr(x)
      action = x;  hand = get(gcbf,'Userdata');
   
   else 
      lx = length(x);
      if lx<2
         error('There should be at least two data points.')
      end
      if lx~=length(y)
         error(['The number ',num2str(lx),' of data sites should match',...
                ' the number ',num2str(length(y)),' of data values.'])
      end
      if any(diff(x)<0), [x,index] = sort(x); y = y(index); end
      xname = 'x'; yname = 'y';
      isf = 0;
      action = 'start';
   end
end
  
switch action

case 'add_item'    % add an item
   
   switch get(hand.data_etc,'Value')
   case 1
      [xx,yy] = ask_for_add(hand);
      x = get(hand.dataline,'Xdata');
      if any(x==xx)
         errordlg('Data sites must be distinct.')
      else
         if xx<x(1)|xx>x(end)
            errordlg('I will only let you add interior data sites.', ...
                'The end data sites are forever','modal')
         else 
            set(hand.undo,'Enable','on')
            insert_and_mark(x,get(hand.dataline,'Ydata'),xx,yy,hand)
            undoud = get(hand.undo,'Userdata');
            undoud.lineud = get(hand.nameline,'Userdata');
            set(hand.undo,'Userdata',undoud);
   
            get_approx(hand)
         end
      end
      if isequal(get(hand.undo,'Enable'),'off')
         V = get(hand.params(1,3),'Value');
         set(hand.params(2,3),'String',x(V))
         if isequal(get(hand.params(2,5),'Visible'),'on')
            y = get(hand.dataline,'Ydata');
            set(hand.params(2,5),'String',y(V))
         end
      end

   case 2
      switch get(hand.method,'Value')
      case 3
         xx = ask_for_add(hand);
         lineud = get(hand.nameline,'Userdata');
         [lineud.knots,i] = sort([lineud.knots,xx]);
         placed = find(i==length(lineud.knots));
         if placed==1|placed==length(lineud.knots)
            errordlg('I will only let you add interior knots.', ...
                'The end knots are forever','modal')
            lineud.knots(placed)=[];
            set(hand.nameline,'Userdata',lineud)
            set(hand.params(2,4),'String', ...
            lineud.knots(get(hand.params(1,4),'Value')))
            
         else
            set(hand.params(1,4),'String',lineud.knots(:),'Value', placed)
            set(hand.nameline,'Userdata',lineud)
            % update knots display
            markb(hand), set_bdisplay(lineud.knots,hand)
         
            get_approx(hand)
         end
      end %switch get(hand.method,'Value')
   case 3

   end %switch get(hand.data_etc,'Value')

case 'axesclick'  % action when the axes is clicked

   % only react here to LEFT clicks
   tmp = get(hand.figure,'SelectionType');
   if tmp(1)=='n'
   
      % get the location of the latest click in data coordinates:
      clickp = get(hand.Axes(1),'CurrentPoint');
   
      switch get(hand.data_etc,'Value')
      case {1,3} % highlight the nearest data point
         highlightn(clickp,hand)
   
      case 2
         switch get(hand.method,'Value')
         case {1,3,4}
   
            % find the nearest break
            breaks = str2num(get(hand.params(1,4),'String'));
            [ignored,V]=min(abs(breaks(:)-repmat(clickp(1),length(breaks),1)));
            if get(hand.params(1,4),'Value')~=V
               set(hand.params(1,4),'Value',V), markb(hand)
            end
         case 2
            highlightn(clickp,hand)

         end %switch get(hand.method,'Value')
   
      end %switch get(hand.data_etc,'Value')
   end %if tmp(1)=='n'

case 'change_name' % change the name of the current spline

   names = get_names(get(hand.list_names,'String'));
   V = get(hand.list_names,'Value');
   oldname = names(V,:);  oldname = oldname(6:end);
   answer = inputdlg('Give a name for the current spline:',...
    'Please provide a new name for the current spline ...', 1,{oldname});
   if isempty(answer)|strcmp(answer{:},oldname) return, end
   name = full_name(answer{1},hand);
   hand.name = name; set(hand.figure,'Userdata',hand)
   if get(hand.ask_show,'Value')==1
      names(V,:) = ['v || ',name];
   else
      names(V,:) = ['  || ',name];
   end
   set(hand.list_names,'String',{names})
   lineud = get(hand.nameline,'Userdata');
   lineud.bottom(1:11) = [' ',name];
   set(hand.bottomlinetext, 'String',lineud.bottom ) 
   set(hand.nameline,'Tag',name,'Userdata',lineud)
   set_view_label(name,hand)
   if names(V,1)=='v'
      legend(hand.Axes(1),'off'), set_legend(hand,hand.Axes(1))
   end

case 'change_order' % call_back from the order selector in approximation field
   
   M = get(hand.method,'Value');
   switch M

   case 2 % we are smoothing, hence now must change the edit fields
      set_edits(2,2,hand)

   case {3,4}
      lineud = get(hand.nameline,'Userdata');
      lineud.k = get(hand.order,'Value'); 
      if M==3  % we are doing least-squares
         lineud.knots = augknt(lineud.knots,lineud.k);
      else     % we are doing interpolation
         [lineud.knots,lineud.k] = aptknt(get(hand.dataline,'Xdata'),lineud.k);
      end
      if get(hand.data_etc,'Value')==2  %  need to update the knot display
         set(hand.params(1,4),'String',lineud.knots(:),'Value',1+lineud.k)
         markb(hand), set_bdisplay(lineud.knots,hand) 
      end
      set(hand.nameline,'Userdata',lineud)

   end %switch M

   get_approx(hand)

case 'data_etc'  % callback from 'data_etc' to set up for present edit choice

   if nargin>1  hand = y; end
   % start clean
   set([reshape(hand.params(:,3:5),15,1); hand.buttonm; hand.piecem; ...
        hand.piecetext; ...
        hand.highlightxy;hand.highlightb;hand.breaks(:);hand.rep_knot(:); ...
        hand.add_item(:);hand.del_item(:)], 'Visible','off')
   set(hand.breaks(:),'Userdata','off')
   set(hand.undo,'Enable','off')

   switch get(hand.data_etc,'Value')
   case 1  % turn on the data display
      set(hand.add_item,'Label','&Add Point ...','Visible','on')
      set(hand.del_item,'Label','&Delete Point','Visible','on')
      set(hand.Axes(1),'UiContextMenu',hand.context)
      x = get(hand.dataline,'Xdata');
      set(hand.params(1,3),'String',x, ...
         'Visible','on', ...
         'Backgroundcolor',[1 1 1], ...
         'Callback','splinetool ''highlightxy''', ...
         'TooltipString', ...
         'mark a point, for editing below; also rightclick in graph')
      set(hand.params(2,3),'String',x(1),'Visible','on')
      set(hand.params([3 5],3), 'Visible','on')
      set(hand.params(4,3),'Visible','on','Value',0)
      if ~get(hand.params(4,3),'Value')
        set(hand.params(4,3),'String',(x(end)-x(1))/(10*length(x)))
      end

      y = get(hand.dataline,'Ydata');
      if get(hand.dataline,'Userdata')
         set(hand.params(1,5),'String',y,'Visible','on', ...
            'Backgroundcolor',.9*[1 1 1], ...
            'Callback','splinetool ''highlightxy''', ...
            'TooltipString','')
      else  
         set(hand.params(1,5),'String',y, 'Visible','on', ...
            'Backgroundcolor',[1 1 1], ...
            'Callback','splinetool ''highlightxy''', ...
            'TooltipString','mark a point; rightclick in graph for edit')
         set(hand.params(2,5),'String',y(1),'Visible','on')
         set(hand.params([3 5],5), 'Visible','on')
         set(hand.params(4,5),'Visible','on','Value',0)
         if ~get(hand.params(4,5),'Value')
           set(hand.params(4,5),'String',(max(y)-min(y))/(10*length(y)))
         end
      end %if get(hand.dataline,'Userdata')
      set(hand.highlightxy,'Visible','on')
      
      markxy(hand) % also sets the editclkr field

   case 2  
      M = get(hand.method,'Value');
      switch M
      case 1   % turn on the breaks display on the middle
         set(hand.Axes(1),'UiContextMenu',[])
         lineud = get(hand.nameline,'Userdata');
         breaks = fnbrk(lineud.cs,'breaks');
         set(hand.params(1,4),'String',breaks(:), ...
         'Visible','on', ...
         'TooltipString','mark a break', ...
         'Callback','splinetool ''highlightb''')
         set_bdisplay(breaks,hand), markb(hand)
         set(hand.highlightb,'Visible','on')

      case 2   % turn on the sites/weight display
         set(hand.Axes(1),'UiContextMenu',[])
         set(hand.params(1,3),'String',get(hand.dataline,'Xdata'), ...
            'Visible','on', ...
            'Backgroundcolor',.9*[1 1 1], ...
            'Callback','splinetool ''highlightxy''', ...
            'TooltipString','')
         lineud = get(hand.nameline,'Userdata');
         set(hand.params(1,5),'String',lineud.w(:), ...
            'Visible','on', ...
            'Enable','on', ...
            'Backgroundcolor',[1 1 1], ...
            'Callback','splinetool ''highlightw''', ...
            'TooltipString','mark a weight, for editing below')
         set(hand.params(2,5),'String',lineud.w(1),'Visible','on')    
         set([hand.highlightxy;hand.params([3 5],5)], ...
            'Visible','on')
         set(hand.params(4,5),'Visible','on','Value',0,'String',.1)

         markxy(hand)

      case {3,4}  % turn on the knots display in the middle
         lineud = get(hand.nameline,'Userdata');
         knots = fnbrk(lineud.cs,'knots'); npk = length(knots);
         set(hand.params(1,4),'String',knots(:), ...
            'Visible','on', ...
            'TooltipString', ...
            'mark a knot, for editing below; also rightclick in graph',...
            'Callback','splinetool ''highlightb''', ...
            'Value',1+lineud.k)
         markb(hand), set_bdisplay(knots,hand)
         if M==3 
            set(hand.Axes(1),'UiContextMenu',hand.context)
            set(hand.add_item,'Label','&Add Knot','Visible','on')
            set(hand.del_item,'Label','&Delete Knot','Visible','on')
            set(hand.rep_knot,'Visible','on')
                % show the newknt button and piecem window
            set([hand.buttonm,hand.piecem,hand.piecetext],'Visible','on')
         end
         set([hand.highlightb;hand.params([2,3,5],4)],'Visible','on')
         set(hand.params(4,4),'Visible','on','Value',0)
         if ~get(hand.params(4,4),'Value')
            set(hand.params(4,4), ...
             'String',(knots(end)-knots(1))/(10*length(knots)))
         end

      end %switch M

   case 3 %  so far, these concern a weight display

      set(hand.Axes(1),'UiContextMenu',[])
      set(hand.params(1,3),'String',get(hand.dataline,'Xdata'), ...
         'Visible','on', ...
         'Backgroundcolor',.9*[1 1 1], ...
         'Callback','splinetool ''highlightxy''', ...
         'TooltipString','')
      lineud = get(hand.nameline,'Userdata');
      set([hand.highlightxy; hand.params([2 3 5],5)], 'Visible','on')
      set(hand.params(4,5),'Visible','on','Value',0,'String',.1)

      switch get(hand.method,'Value')

      case 2 % turn on the sites/jump in weights display
         tols = lineud.tol;
         if length(tols)==1 
            tols = [tols,ones(1,length(get(hand.dataline,'Xdata'))-1)];
            lineud.tol = tols;
            set(hand.nameline,'Userdata',lineud)
         end
         tols = [tols(2),diff(tols(2:end)),NaN];

         set(hand.params(1,5),'String',tols, ...
            'Visible','on', ...
            'Enable','on', ...
            'Backgroundcolor',[1 1 1], ...
            'Callback','splinetool ''highlightw''', ...
            'TooltipString', ...
                ['mark a jump in roughness weight, for editing below'])

      case 3 % turn on the sites/weight display
         set(hand.params(1,5),'String',lineud.w(:), ...
            'Visible','on', ...
            'Enable','on', ...
            'Backgroundcolor',[1 1 1], ...
            'Callback','splinetool ''highlightw''', ...
            'TooltipString','mark a weight, for editing below')
      end %switch get(hand.method,'Value')

      markxy(hand) % also sets the editclkr field
    
   end %switch get(hand.data_etc,'Value')

case 'del' % delete the current spline

   % the Value in list_names points to the current spline in that list
   listud = get(hand.list_names,'Userdata');
   V = get(hand.list_names,'Value');
   if V>0  % otherwise there is nothing left to delete
      delete(listud.handles(V))
      listud.length = listud.length-1;
      listud.handles(V) = [];
      names = get_names(get(hand.list_names,'String'));
      set(hand.list_names, ...
          'String',{names([1:V-1 V+1:end],:)}, ...
          'Value',min(V,listud.length), ... 
          'Userdata',listud);
   end
   hand = set_current(hand);
   set_displays(hand)
   set_legend(hand,hand.Axes(1))

case 'del_item'    % delete the current item

   switch get(hand.data_etc,'Value')
   case 1
      V = get(hand.params(1,3),'Value');
      x = get(hand.dataline,'Xdata');
      if V==1|V==length(x)
        errordlg('I won''t let you delete the extreme data points.', ...
                 'For simplicity, ...')
      else
         y = get(hand.dataline,'Ydata'); x(V) = []; y(V)=[];
         set(hand.params(1,3),'Value',min(V,length(x)));
         set(hand.params(1,5),'Value',min(V,length(y)));
         set(hand.undo,'Enable','on')
         set_data(hand,x,y)
         undoud = get(hand.undo,'Userdata');
         undoud.lineud = get(hand.nameline,'Userdata');
         set(hand.undo,'Userdata',undoud);
            % re-initialize the weights, if any
         lineud = get(hand.nameline,'Userdata');
         if isfield(lineud,'tol'), lineud.tol(2:end)=[]; end
         if isfield(lineud,'w'), lineud = rmfield(lineud,'w'); end
         if isfield(lineud,'knots'), lineud = rmfield(lineud,'knots'); end
         set(hand.nameline,'Userdata',lineud);
         get_approx(hand)
         set(hand.highlightxy,'Xdata',[get(hand.highlightxy,'Xdata'),x(V)])
         set(hand.highlightxy,'Ydata',[get(hand.highlightxy,'Ydata'),y(V)])
      end %if V==1|V==length(x)
   case 2
      V = get(hand.params(1,4),'Value');
      lineud = get(hand.nameline,'Userdata'); npk = length(lineud.knots);
      if V<=lineud.k|V>npk-lineud.k
         errordlg('I won''t let you delete an end knot.')
      else
         lineud.knots(V)=[];
         tmp = get(hand.params(1,4),'String'); tmp(V,:)=[];
         set(hand.params(1,4),'String',tmp)
         set(hand.params(1,4),'Value',min(V,npk-lineud.k));
         set(hand.nameline,'Userdata',lineud)
         markb(hand), set_bdisplay(lineud.knots,hand)

         get_approx(hand)

      end
   
   end %switch get(hand.data_etc,'Value')

case 'export_data'

   xname = 'x'; yname = 'y'; accepted = 0;
   while ~accepted
      answer = inputdlg({'Give a name for the data sites:',...
        'Give a name for the data values:'},...
        'To copy current data to the base workspace ...', 1,{xname;yname});
      if ~isempty(answer)
         xname = answer{1}; yname = answer{2};
         if okvar(xname)&okvar(yname)
            accepted = 1;
         end
      else, break
      end
   end

   if accepted
         assignin('base',xname, get(hand.dataline','Xdata'));
         assignin('base',yname, get(hand.dataline','Ydata'));
   %      workspace -refresh
   %      % needed now to make certain the workspace browser gets updated,
   %      % but this may be fixed soon.  
   end

case 'export_spline'

   namewob = hand.name; namewob(find(namewob==' '))=[];
   accepted = 0;
   while ~accepted
      answer = inputdlg(['Give a name for the spline:'], ...
               'To copy current spline to the base workspace ...',1,{namewob});
      if isempty(answer), return, end
      if length(answer{1})==0|~isempty(find(answer{1}==' '))
         errordlg('You need to specify a name (without any blanks).')
      end
      namewob = answer{1}; if okvar(namewob), accepted = 1; end
   end      
      
   lineud = get(hand.nameline,'Userdata');
   assignin('base',answer{1}, lineud.cs)
%       workspace -refresh 
%       % needed now to make certain the workspace browser gets updated,
%       % but this may be fixed soon.  

case {'close','exit','finish','quit'} % terminate splinetool

   answer = questdlg('Do you really want to quit the SPLINE TOOL?', ...
                     'Do you want to quit?','OK','Cancel','OK');
   if answer(1)=='C', return, end
   close(findobj(allchild(0),'name','Spline Tool'))

case 'ginputmove'
   pt = get(gca,'CurrentPoint');
   xlim = get(gca,'Xlim');
   if xlim(1)<=pt(1,1) & pt(1,1)<=xlim(2)
      set(hand(1),'String',pt(1,1))
      if length(hand)>1, set(hand(2),'String',pt(1,2)), end
   end

case 'ginputdone'
    set(gcbf,'WindowButtonMotionFcn','','WindowButtonDownFcn','',...
         'Pointer','arrow');

case 'help'
   titlestring = ['Explanation: ',y];
   switch y

   case 'about'
      titlestring = 'About the Spline Toolbox';
      mess = abouttext;

   otherwise
      mess = spterms(y);
   end %switch y

   msgbox(mess,titlestring)

case 'highlightb'
      markb(hand)

case 'highlightw'   % mark a weight (and the corresponding point)

      V = get(hand.params(1,5),'Value'); set(hand.params(1,3),'Value',V);
      markxy(hand)
      tmp = get(hand.params(1,5),'String'); 
      set(hand.params(2,5),'String',tmp(V,:))

case 'highlightxy' % highight the marked item
      markxy(hand)
     
case 'increment'   % convert expression to a value, checking validity
   
   tag = get(gcbo,'Tag');
   j = find('telmr'==tag(end));
   output = convert(get(hand.params(4,j),'String'));
   if ischar(output)
      bad_edit(get(hand.params(4,j),'String'),output)
      set(hand.params(4,j),'String', ...
         shortstr(abs(eval(get(hand.params(2,j),'String')))/10,1))
      return
   else
       set(hand.params(4,j),'String',shortstr(output,1),'Value',1)
   end

case 'labels'  % change Xlabel and/or Ylabel

   fory = hand.Axes(1); forx = hand.Axes(2);
   tmp = get(forx,'Visible'); if tmp(2)=='f', forx = fory; end

   answer = inputdlg({['Give the x-label'], ...
                  ['Give the y-label; if the y-label is the name of',...
                   ' an m-file or a built-in function, that function',...
                   ' will be compared against in the error calculation.']}, ...
                   'Please provide labels', 1, ...
                   {get(get(forx,'Xlabel'),'String');
                    get(get(fory,'Ylabel'),'String')});

   if ~isempty(answer)
      set(get(forx,'Xlabel'),'String',answer{1})
      set(get(fory,'Ylabel'),'String',answer{2})
      % If the new y-label is the name of an m-file or a built-in function,
      % it will be used in the error calculation to provide the exact values.
      set(hand.dataline,'Userdata',get_isf(answer{2}))
      if find(get(hand.viewmenu,'Userdata')==1)==3 % if the error is on view,
         set_view(hand)                            % update it now
      end
   end

case 'make_current'
   hand = set_current(hand);
   set_displays(hand)
   set_legend(hand,hand.Axes(1))

case 'method' % set things up for a particular method

   method(hand)

case 'move_item'     % move an item to the position suggested by its editbox

   switch get(hand.data_etc,'Value')
   case 1
      move_point(hand)
   case 2
      switch get(hand.method,'Value')
      case 2  
        change_weight(hand) 
      case {3,4}
        if ~move_knot(hand), return, end
        get_approx(hand)
      end % switch get(hand.method,'Value')

   case 3
      switch get(hand.method,'Value')
      case 2
        get_approx(hand)
      case 3
        change_weight(hand)
      end %switch get(hand.method,'Value') 
   end %switch get(hand.data_etc,'Value')

case 'new' % start a new spline fit, using the not-a-knot end conditions

   new(hand);

case 'newknt' % redo the knot sequence according to the current approximation

   get_approx(hand)

case 'parameters'     % change to the newly specified parameters

   parameters(hand)

case 'pieces' % use the number of pieces specified

   get_approx(hand)

case 'pm'  % one of the increment/decrement guys has been clicked

   handle = gcbo; tag = get(handle,'Tag');
   % find out whether to increment or decrement, ...
   pm = '+';  if tag(1)=='m', pm = '-'; end

   % ... then change the numerical value of its edit field accordingly ...
   j = find('telmr'==tag(end));
   set(hand.params(2,j),'String', eval([get(hand.params(2,j),'String'),pm, ...
      get(hand.params(4,j),'String')]));

   % ... and invoke its callback
   eval(get(hand.params(2,j),'Callback'))

case 'print_graph'  % print the current axes

   % setting the windowstyle to modal will remove the toolbar???
   set(gcbf,'Visible','off')
   printfig = figure('Toolbar','figure',...
    'numbertitle','off',  ...
    'toolbar','figure', ...
    'DeleteFcn','set(findobj(''name'',''Spline Tool''),''Visible'',''on'')',...
    'HandleVisibility','callback', ...
    'name','Print Graph (temporary window)');
   copyobj(hand.Axes(1), printfig);
   if any(get(hand.viewmenu,'Userdata'))
      set(findobj(printfig,'Tag','Axes1'), ...
        'ButtonDownFcn','', ...
        'Position',[ 0.1300    0.5000    0.7750    0.4950]) 
      copyobj(hand.Axes(2),printfig);
      set(findobj(printfig,'Tag','Axes2'), ...
        'ButtonDownFcn','', ...
        'Position',[ 0.1300    0.1300    0.7750    0.3100]) 
   else
      set(findobj(printfig,'Tag','Axes1'), ...
       'ButtonDownFcn','', ...
       'Position',[ 0.1300    0.2600    0.7750    0.7150]) 
   end
   set_legend(hand,findobj(printfig,'Tag','Axes1'))
    bp =  uicontrol('Parent', printfig, 'ListboxTop',0, ...
      'Units','normalized', ...
      'Position',[.6, .01, .15, .09], ...
      'String','Print', ...
      'Style','pushbutton', ...
      'Callback',['delete(findobj(gcbf,''Tag'',''gofirst'')),',...
            'temp = findobj(''name'',''Print Graph (temporary window)'');', ...
            'temp1 = printdlg(temp);', ...
            'if ~isempty(temp1), waitfor(temp1), end, delete(temp),', ...
            'set(findobj(''name'',''Spline Tool''),''Visible'',''on'')'],...
            'Tag','gofirst');
     bc = copyobj(bp,printfig);
    set(bc, ...
      'Position', [.8, .01, .15, .09], ...
      'String','Cancel', ...
      'Callback', ['delete(gcbf),', ...
         'set(findobj(''name'',''Spline Tool''),''Visible'',''on'')'], ...
      'Tag','gofirst');

case 'rep' % start a new spline as a replica of the current spline

   % set up a new line as current line, updating the list
   oldnameline = hand.nameline;
   [currentname, hand.nameline] = add_to_list(hand);
   hand.name = currentname; set(gcbf,'Userdata',hand)

   % copy the stuff from the old current line to the new line
     lineud = get(oldnameline,'Userdata');
     lineud.bottom(1:11) = [' ',currentname];
     set(hand.bottomlinetext, 'String',lineud.bottom ) 
   set(hand.nameline, ...
      'Xdata',get(oldnameline,'Xdata'),...
      'Ydata',get(oldnameline,'Ydata'),...
      'Userdata',lineud);
   
   % no need to update endconds, model, rest of display, except for
   set_view_label(currentname,hand)
   set_legend(hand,hand.Axes(1))

case 'rep_knot' % replicate the marked knot

   switch get(hand.method,'Value')
   case 3
      V = get(hand.params(1,4),'Value');
      lineud = get(hand.nameline,'Userdata');
      index = find(lineud.knots==lineud.knots(V));
      if length(index)>=lineud.k
         errordlg(['There is no point in having knots of multiplicity',...
                   ' greater than the order of the spline.']), return
      end 
      lineud.knots = lineud.knots([1:V,V:end]);
      set(hand.nameline,'Userdata',lineud)
      set(hand.params(1,4),'String',lineud.knots(:), ...
         'Value', V+1 )
      % update knots display
      markb(hand), set_bdisplay(lineud.knots,hand)
      
      get_approx(hand)

   end %switch get(hand.method,'Value')

case 'restart'

   answer = questdlg('Do you really want to restart the SPLINE TOOL?', ...
                     'Do you want to restart?','OK','Cancel','OK');
   if answer(1)=='C', return, end
   delete(hand.figure), splinetool

case 'start'   

   load splinetool

   % the basic figure
   hand.figure = figure('Color',[0.8 0.8 0.8], ...
	'Colormap',mat0, ...
	'FileName','splinetool.m', ...
	'PaperUnits','normalized', ...
        'PaperPosition',[.25 .10 .6 .75], ...
        'Units','normalized', ...
        'Position',[.25 .10 .6 .75], ...
     'numbertitle','off', ...
     'name','Spline Tool', ...
	'Tag','splinetoolfig', ...
        'doublebuffer','on', ...
     'Menubar','none', ...
	'ToolBar','none');

   if nargin<1 % if there are no data yet, ask for some:
     while isempty(get(hand.figure,'Userdata'))

       isf = 0; errortit = 'Error in input to the SPLINE TOOL';
      try
         % now call on my own version of the menu command
       switch menu({'Choose some data to work on with the';
                     ' SPLINE TOOL:'}, ...
          'Provide your own data', ...
          'Specify an m-file that provides the data', ...
          'Titanium heat data', ...
          'Noisy values of a smooth function', ...
          'The function sin(x) on [0 .. pi/2]', ...
          'Census data', ...
          'Richard Tapia''s drag race data');
       case 1
          [x,y,xname,yname,isf] = ask_for_data;
          if isf<0 % the user hit Cancel, so get ready to ask again for data
             set(hand.figure,'Userdata',[])
          end
       case 2
        answer = inputdlg(...
           'Give the name of a function whose output is [x,y]:',...
           'Please provide data', 1, ...
           {'titanium'});
          if ~isempty(answer)
             [x,y] = evalin('base',answer{1});
             if any(diff(x)<0), [x,index] = sort(x); y = y(index); end
             xname = 'x'; yname = 'y';
          else
             % the user hit Cancel, so get ready to ask again for data
             set(hand.figure,'Userdata',[])
          end
          
       case 4
          x = linspace(0,2*pi,101); y = sin(x)+(rand(size(x))-.5)*.2;
        xname = 'x= linspace(0,2*pi,101)'; yname = 'sin'; isf = 2;
       case 5
          x = linspace(0,pi/2,31); y = sin(x);
          xname = 'x = linspace(0,pi/2,31)'; yname = 'sin'; isf = 2;
       case 6 % census data, taken from the matlab demo CENSUS
          x = (1900:10:1990)';
          y = [75.995 91.972 105.711 123.203 131.669 ...
               150.697 179.323 203.212 226.505 249.633]';
          xname = 'year'; yname = 'population';
       case 7 % Richard Tapia's drag race data; 
              % try to estimate the initial acceleration
          x = [ 0.000 0.857 2.142 3.074 3.862 4.4052 4.544];
          y = [ 0 60 330 660 1000 1254 1320];
          xname = 'time in seconds'; yname = 'distance in feet';
          messh = ...
          msgbox({'The task here is to estimate the initial acceleration.'; ...
                  'One way: use a clamped cubic spline'; ...
                  'and take the initial speed to be 0.'}, 'Your mission: ');
          figure(hand.figure)

       otherwise
          [x,y] = titanium; xname = 'temperature'; yname = 'titanium property';
       end %switch menu'Choose some data to work on with the';
      catch
         cf = findobj(allchild(0),'name','Spline Tool');
         if ~isempty(cf)
            errordlg('Something went wrong during input.',errortit)
            close(cf)
         end
         return
      end %try
     end %while
      if ~exist('x')
         errordlg('Data sites are not defined.',errortit), return, end
      if ~exist('y')
         errordlg('Data values are not defined.',errortit), return, end
      if length(x)~=length(y)
        errordlg('Sites and values must be vectors of the same length',errortit)
         return
      end
   end %if nargin<1 

   % finish the rest of the gui figure:
   backgrey = repmat(0.752941176470588,1,3);
   breakcolor =  backgrey*.8; % would have liked just backgrey
   currentcolor = [0 0 1];
   highcolor =  [1 1 1];
   units = 'normalized';

    %  make up your own menubar items:
    h1 = uimenu(gcf,'Label','&File','Tag','file');
    h2 = uimenu(h1,'Label','&Restart', ...
         'Callback','splinetool ''restart''', ...
         'Tag','restart');
    h2 = uimenu(h1,'Label','Export &Data ...', ...
         'Callback','splinetool ''export_data''', ...
         'Separator','on',...
         'Tag','export_data');
    h2 = uimenu(h1,'Label','Export &Spline ...', ...
         'Callback','splinetool ''export_spline''', ...
         'Tag','export_spline');
    h2 = uimenu(h1,'Label','&Print Graph', ...
         'Callback','splinetool ''print_graph''', ...
         'Separator','on',...
         'Tag','print_graph');
    h2 = uimenu(h1,'Label','&Close', 'Callback', 'splinetool ''finish''', ...
         'Separator','on',...
         'Tag','exit');
    h1 =  uimenu(gcf,'Label','&Edit',...
         'Tag','editmenu');
    hand.editmenu = h1;
    hand.undo = uimenu(h1,'Label','&Undo','Userdata',[], 'Enable','off' ,...
                   'Callback','splinetool ''undo''','Tag','undo');
    hand.add_item = uimenu(h1,'Label','&Add', ...
         'Callback', 'splinetool ''add_item''', ...
         'Tag','add_item');
    hand.rep_knot = uimenu(h1,'Label','&Replicate Knot', ...
                   'Callback','splinetool ''rep_knot''','Tag','rep_knot');
    hand.del_item = uimenu(h1,'Label','&Delete', ...
         'Callback','splinetool ''del_item''',...
         'Tag','del_item');
    hlab = uimenu(h1,'Label','&Labels ...', 'Callback', ...
                     'splinetool ''labels''', 'Tag','labels');
    viewud = [0 0 0];
    h1 = uimenu(gcf,'Label','&View','Userdata',viewud, ...
                   'Tag','viewmenu');
    hand.view(1) = uimenu(h1,'Label','Show &1st Derivative', ...
                                    'Callback','splinetool ''view''', ...
                    'Tag','1view');
    hand.view(2) = uimenu(h1,'Label','Show &2nd Derivative', ...
                                    'Callback','splinetool ''view''', ...
                    'Tag','2view');
    hand.view(3) = uimenu(h1,'Label','Show &Error', ...
                                    'Callback','splinetool ''view''', ...
                    'Tag','3view');
    hand.viewmenu = h1;
    h1 = uimenu(gcf,'Label','&Help');
    h2 = uimenu(h1,'Label','Splinetool &Help', ...
                           'Callback','doc splines/splinetool');
    h2 = uimenu(h1,'Label','&Spline Toolbox Help', ...
                           'Callback','doc splines/');
    h2 = uimenu(h1,'Label','&Demos', ...
                           'Separator','on', ...
                           'Callback','demo toolbox spline');
%%%%%%%%%%%%%%%%%%   all specific help removed on Penny's advice
   h2 = uimenu(h1,'Label','&About the Spline Toolbox',...
                           'Separator','on', ...
                       'Callback','splinetool(''help'',''about'')');


   % also provide a context menu, to be active in data mode, and to provide
   % the same capability as the edit commands
   hand.context = uicontextmenu('Parent',gcf, ...
              'Position',[.35 .46], ...
              'Tag','context');
     hand.add_item(2) = copyobj(hand.add_item, hand.context);
     hand.rep_knot(2) = copyobj(hand.rep_knot, hand.context);
     hand.del_item(2) = copyobj(hand.del_item, hand.context);

    
   %%%%%%%%%%%%%%%%%%%%% the axes on which to draw data and splines
   mat2 = [       0         0    1.0000     
                  0    0.5000         0
             1.0000         0         0
                  0    0.7500    0.7500
             0.7500         0    0.7500
             0.7500    0.7500         0
             0.2500    0.2500    0.2500];
   xaxes = .35; dxaxes = .63;
  
   %%%%%%%%%%%%%%%%%%%%% axes on which to draw derivatives and such
   hand.Axes(2) = axes('Parent',gcf, ...
   	'CameraUpVector',[0 1 0], ...
   	'CameraUpVectorMode','manual', ...
   	'Color',[1 1 1], ...
   	'ColorOrder', mat2, ...
   	'Position',[xaxes .11 dxaxes .316], ...
        'Visible','off', ...
   	'Tag','Axes2', ...
        'Fontsize',8, ...
        'ButtonDownFcn','splinetool ''axesclick''', ...
   	'XColor',[0 0 0], ...
   	'YColor',[0 0 0], ...
   	'ZColor',[0 0 0]);
         xlabel(xname), ylabel([])

   hand.zeroline = line('Xdata',[NaN NaN],'Ydata',[0 0], ...
        'Linewidth',1.5, ...
        'Color',breakcolor, ...
        'Visible','off', ...
        'Tag','zeroline');
   hand.viewline = line('Xdata',NaN,'Ydata',NaN, ...
        'Color',currentcolor, ...
        'Linewidth',2, ...
        'erasemode','normal', ...
        'Tag','viewline');
    % put break/knot lines (also) on second graphic
   hand.breaks(2) = line('Xdata',NaN, 'Ydata',NaN,...
              'Linewidth',1.5, ...
              'erasemode','normal', ...
              'Color',breakcolor, ...
              'visible','off', ...
              'Userdata','off', ...
              'ButtonDownFcn', 'splinetool ''axesclick''', ...
              'Tag','breaks');
   
   %%%%%%%%%%%%%%%%%%%%% axes on which to draw data and approximations
   hand.Axes(1) = axes('Parent',gcf, ...
   	'CameraUpVector',[0 1 0], ...
        'CameraUpVectorMode','manual', ...
   	'Color',[1 1 1], ...
   	'ColorOrder', mat2, ...
        'Units', units, ...
   	'Position',[xaxes 0.47 dxaxes 0.497], ...
   	'Tag','Axes1', ...
        'Fontsize',8, ...
        'ButtonDownFcn','splinetool ''axesclick''', ...
        'UiContextMenu',hand.context, ...
   	'XColor',[0 0 0], ...
   	'YColor',[0 0 0], ...
   	'ZColor',[0 0 0]);
        xlabel(xname), ylabel(yname)

   %%%%%%%%%%%%%%%%%%  set up bottom line for showing toolbox commands used:
   hand.bottomlinetext = uicontrol('Parent',gcf, ...
   	'BackgroundColor',highcolor, ...
   	'ListboxTop',0, ...
   	'Units',units, ...
   	'Position',[.114 .003 .90 .046], ...
   	'Style','text', ...
        'HorizontalAlignment','left', ...
        'TooltipString', 'shows toolbox command used', ...
   	'Tag','bottomlinetext');
   uicontrol('Parent',gcf, ...
   	'BackgroundColor',get(gcf,'Color'), ...
   	'ListboxTop',0, ...
   	'Units',units, ...
   	'Position',[.014 .023 .10 .026], ...
   	'String',' Bottomline:', ...
   	'Style','text')

   %  %%%%%%%%%%%%%%%%%%%%%%%%%%%  the spline/name manager
        mmpos = [.015 .77-.007, .101, .18]; marg = .008; 
        framecolor = get(gcf,'Color');
   uicontrol('Parent',gcf, ...
        'BackgroundColor',framecolor, ...
        'Units',units, ...
        'Position',[mmpos(1:2)-marg,.25+2*marg,mmpos(4)+3.5*marg], ...
        'Style','frame', ...
        'Tag','nameframe')
   uicontrol('Parent',gcf, ...
        'BackgroundColor',framecolor, ...
         'Units',units, ...
         'Position',[mmpos(1)+.005, mmpos(2)+mmpos(4),.19, 4*marg], ...
         'HorizontalAlignment','left', ...
         'String','List of approximations', ...
         'Style','text')

   sl = .032; rw = .15;
   lnpx = rw-sl; lnpy = mmpos(2); wc = .09; ww = lnpx-mmpos(1);
        wh = .045; ph = 3*wh;
   hand.ask_show = uicontrol('Parent',gcf, ...
   	'BackgroundColor',backgrey, ...
   	'ListboxTop',0, ...
   	'Units',units, ...
   	'Position',[lnpx+.002 lnpy rw wh], ...
   	'Style','checkbox', ...
        'String','shown in graph', ...
        'Callback', 'splinetool ''toggle_show''', ...
        'TooltipString', 'Show current spline in graph?', ...
   	'Tag','ask_show');

   % list of names
   listud.handles = []; listud.length=0; listud.untitleds=0;
   hand.list_names = uicontrol('Parent',gcf, ...
   	'BackgroundColor',[1 1 1], ...
   	'Units',units, ...
   	'Position',[lnpx lnpy+wh rw ph], ...
   	'String',{''}, ...
   	'Style','listbox', ...
   	'Tag','list_names', ...
        'Callback','splinetool ''make_current''', ...
        'TooltipString', 'choose the current spline', ...
        'Userdata',listud, ...
   	'Value',1);
        %'Fontname','FixedWidth', ...
        %'Fontsize',6,...
     %%%%%%%%%%%%%  Pushbutton NEW : start another curve with the default fit
   h1 = uicontrol('Parent',gcf, ...
   	'Units',units, ...
   	'BackgroundColor',backgrey, ...
   	'ListboxTop',0, ...
   	'Position',[mmpos(1),mmpos(2)+3*wh,ww wh], ...
   	'String','New', ...
        'TooltipString', 'start a new spline', ...
        'Callback','splinetool ''new''', ...
   	'Tag','Pushnew');
     %%%%%%%%%%%%%  Pushbutton REP : start another curve, a copy of current one
   h1 = uicontrol('Parent',gcf, ...
   	'Units',units, ...
   	'BackgroundColor',backgrey, ...
   	'ListboxTop',0, ...
   	'Position',[mmpos(1),mmpos(2)+2*wh,ww wh], ...
   	'String','Replicate', ...
        'TooltipString', 'replicate the current spline', ...
        'Callback','splinetool ''rep''', ...
   	'Tag','Pushrep');
     %%%%%%%%%%%%%  Pushbutton DEL : delete the current curve
   h1 = uicontrol('Parent',gcf, ...
   	'Units',units, ...
   	'BackgroundColor',backgrey, ...
   	'ListboxTop',0, ...
   	'Position',[mmpos(1),mmpos(2)+wh,ww wh], ...
   	'String','Delete', ...
        'TooltipString', 'delete the current spline', ...
        'Callback','splinetool ''del''', ...
   	'Tag','Pushdel');
     %%%%%%%%%%%%%  Pushbutton RENAME : rename current spline
  hand.ask_name = uicontrol('Parent',gcf, ...
   	'Units',units, ...
   	'BackgroundColor',backgrey, ...
   	'Position',[mmpos(1),mmpos(2),ww wh], ...
        'String','Rename ...', ...
        'Callback', 'splinetool ''change_name''', ...
        'TooltipString', 'change the name of the current spline', ...
   	'Tag','ask_name');
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%  the method manager
   topy = mmpos(2)-.07; edy = .0013; wh = .036;
   mpx = mmpos(1); mpy = topy-wh; mw=.25;
   bottomy = mpy-4*(wh+edy)-.03; 
   uicontrol('Parent',gcf, ...
        'BackgroundColor',framecolor, ...
        'Units',units, ...
        'Position',[[mpx,bottomy]-marg,mw+2*marg,topy-bottomy+3.5*marg], ...
        'Style','frame', ...
        'Tag','nameframe')
  uicontrol('Parent',gcf, ...
        'BackgroundColor',framecolor, ...
         'Units',units, ...
         'Position',[mmpos(1)+.005, topy,.19, 4*marg], ...
         'HorizontalAlignment','left', ...
         'String','Approximation method', ...
         'Style','text')
  hand.method = uicontrol('Parent',gcf, ...
   	'Units',units, ...
   	'BackgroundColor',highcolor, ...
   	'ListboxTop',0, ...
   	'Position',[mpx mpy mw wh], ...
   	'HorizontalAlignment','center', ...
   	'Style','popupmenu', ...
    	'String',{'Cubic Spline Interpolation';
                  'Smoothing Spline';
                  'Least-Squares Approximation';
                  'Spline Interpolation'}, ...
        'Callback','splinetool ''method''', ...
        'TooltipString', 'choose a method', ...
   	'Tag','method', ...
        'Interruptible','off', ...
        'Enable', 'on', ...
   	'Value',1);

   %%%%%%%%%%%%%%%%%%%%%%%%%  additional parameters
        ew = .12;  
   hand.endtext = uicontrol('Parent',gcf, ...
        'Style','text', ...
   	'Units',units, ...
   	'Position',[mpx mpy-wh-4*edy-.007 ew wh], ...
   	'BackgroundColor',get(gcf,'Color'), ...
        'String','End conditions:', ...
   	'Tag','endtext')   ;
        %'FontName','CourierNew', ...
        %'Fontsize',8, ...

                 %%%%%%%%%%%  ... for cubic spline interpolation
   hand.endconds = uicontrol('Parent',gcf, ...
   	'Units',units, ...
   	'BackgroundColor',highcolor, ...
   	'ListboxTop',0, ...
   	'Position',[mpx+(mw-ew) mpy-wh-4*edy ew wh], ...
   	'String',{'not-a-knot';'clamped';'complete';'second';'periodic'; ...
                     'variational';'''natural''';'Lagrange';'Custom'}, ...
   	'Style','popupmenu', ...
        'Callback','splinetool ''parameters''', ...
        'TooltipString', 'choose end conditions', ...
           'Visible','off', ...
   	'Tag','endconds', ...
   	'Value',1);

                 %%%%%%%%%%%  ... for everything else, we have order
    endpos = get(hand.endconds,'Position');
    currlist = {'1';'2';'3';'4';'5';'6';'7';'8';'9';'10';'11';'12';'13';'14'};
    hand.order = uicontrol('Parent',gcf, ...
         'BackgroundColor',highcolor, ...
         'Units',units, ...
         'Position',get(hand.endconds,'Position'), ...
         'HorizontalAlignment','center',...
         'Style','popupmenu',...
         'String',currlist, ...
         'Callback','splinetool ''change_order''', ...
         'Userdata',currlist, ...
         'Value',4, ...
         'ToolTipString','choose the order', ...
         'Visible','off','Tag','order');

              %%%%  set up the the remaining parameter display
     centerx = mpx+mw/2;  wid = mw/2;

     %%%%%%%%%%%%  left end toggle
   hand.partext(1,1) = uicontrol('Parent',gcf, ...
        'Units',units, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'Position',[centerx-wid bottomy+3*(wh+edy)-.015 wid-.001 wh], ...
        'Style','text', ...
        'Visible','on', ...
        'String','left end');
   hand.partext(2,1) = copyobj(hand.partext(1,1),gcf);
   set(hand.partext(2,1), ...
        'Position',[centerx-wid bottomy+2*(wh+edy) wid-.001 wh], ...
        'Visible','off', ...
        'String','parameter')
   hand.params(1,1) = uicontrol('Parent',gcf, ...
        'Units',units, ...
   	'BackgroundColor',backgrey, ...
        'ListboxTop',0, ...
        'Position',get(hand.partext(2,1),'Position'), ...
        'Userdata','pushend', ...
        'Tag','pushbuttonleft', ...
        'String','1st deriv.', ...
        'Callback', 'splinetool(''toggle_ends'',''left'')', ...
        'Value',1);
   hand.params(2:5,1) = ...
         clickable([centerx-wid,bottomy], wid, 'left','parameters','endconds');
    
     %%%%%%%%%%%%  rite end toggle
   hand.partext(1,2) = copyobj(hand.partext(1,1),gcf);
   set(hand.partext(1,2), ...
        'Position',[centerx+.001 bottomy+3*(wh+edy)-.015 wid-.001 wh], ...
        'String','right end')
   hand.partext(2,2) = copyobj(hand.partext(1,2),gcf);
   set(hand.partext(2,2), ...
        'Position',[centerx+.001 bottomy+2*(wh+edy) wid-.001 wh], ...
        'String','tolerance')
   hand.params(1,2) = copyobj(hand.params(1,1),gcf);
      set(hand.params(1,2), ...
        'Position',get(hand.partext(2,2),'Position'), ...
        'String','1st deriv.', ...
        'Callback', 'splinetool(''toggle_ends'',''rite'')', ...
        'Tag','pushbuttonrite')
   hand.params(2:5,2) = ...
            clickable([centerx,bottomy], wid, 'rite','parameters','endconds');
   set(hand.params(2:5,1:2),'Enable','off')
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%   popup re lists
   dkpx = mmpos(1); dkpy = .111; dkw=.125; dkh = .265;
   datatop = dkpy+dkh+.01+wh;
   databottom = dkpy+wh-(2*wh+.002);
   uicontrol('Parent',gcf, ...
        'BackgroundColor',framecolor, ...
        'Units',units,'Position', ...
        [[dkpx,databottom]-marg,2*(dkw+marg),(datatop-databottom)+3.5*marg], ...
        'Style','frame', ...
        'Tag','nameframe')
   uicontrol('Parent',gcf, ...
        'BackgroundColor',framecolor, ...
         'Units',units, ...
         'Position',[dkpx+.005, datatop,.25, 4*marg], ...
         'String','Data, breaks/knots, weights, ...', ...
         'HorizontalAlignment','left', ...
         'Style','text')


   hand.data_etc = uicontrol('Parent',gcf, ...
   	'Units',units, ...
        'Position',[dkpx dkpy+dkh+.01 2*dkw wh], ...
   	'BackgroundColor',highcolor, ...
   	'ListboxTop',0, ...
   	'HorizontalAlignment','left', ...
   	'Style','popupmenu', ...
        'Callback','splinetool ''data_etc''', ...
   	'Tag','data_etc', ...
        'Interruptible','off', ...
        'Enable', 'on', ...
   	'Value',1);
        %'Fontname','FixedWidth', ...
        %'FontUnits','Normalized','FontSize',.01, ...
 
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% also make the three lists and their
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% clickable and edit field, but keep
    %%%%%%%%%%%%%%%%%%%%%%%%%% them all invisible, to be turned on as needed

   poss = 'lmr'; shift = [0 0 1]*dkw;

   for j=3:5 
      hand.params(1,j) = uicontrol('Parent',gcf, ...
         	'Units',units, ...
         	'BackgroundColor','w', ...
         	'Position',[dkpx+shift(j-2),dkpy+wh, dkw, dkh-wh], ...
         	'String',[], ...
         	'Style','listbox', ...
         	'Tag',['list',poss(j-2)], ...
              'Enable','on', ...
              'Visible','off', ...
              'Value',1);
      
      % put a clickable underneath:
      hand.params(2:5,j) = ...
                 clickable([dkpx+shift(j-2),databottom],dkw,...
                        ['clk',poss(j-2)],'move_item','listlmr');
   
      if j==4  % also provide a button to the right of the top:
         hand.buttonm = uicontrol('Parent',gcf, ...
           'Units',units, 'Position', ...
                [dkpx+shift(j-2)+dkw+.02,dkpy+dkh-wh-.001, .083, wh], ...
           'String','adjust', ...
           'Callback','splinetool ''newknt''', ...
           'TooltipString','sometimes, this improves the knot distribution',...
           'Tag','buttonm', ...
           'Visible','off');
               % ... and show the number of pieces in an edit box:
         hand.piecem = uicontrol('Parent',gcf, ...
           'Backgroundcolor',highcolor, ...
           'Units',units, 'Position', ...
                   [dkpx+shift(j-2)+dkw+.028,databottom, .075, wh], ...
           'Style','edit', ...
           'String',' ', ...
           'Callback','splinetool ''pieces''', ...
           'TooltipString','choose the number of pol.pieces to use',...
           'Tag','piecem', ...
           'Visible','off');
               % ... and label that edit box:
          hand.piecetext = uicontrol('Parent',gcf, ...
           'BackgroundColor',framecolor, ...
           'Units',units, 'Position', ...
                get(hand.piecem,'Position')+[0,wh,0,0], ...
           'Style','text', ...
           'String','# pieces', ...
           'Tag','piecetext', ...
           'Visible','off');
      end

   end %for j=3:5 
   
   hand.highlightxy = line('Xdata',NaN, 'Ydata',NaN,...
              'Marker','o','Linestyle','none',...
              'Linewidth',3, ...
              'erasemode','normal', ...
              'Color','r', ...
              'Visible','off', ...
              'Tag','highlightxy');

   hand.breaks(1) = copyobj(hand.breaks(2),hand.Axes(1));
   hand.highlightb = line('Xdata',NaN, 'Ydata',NaN,...
              'Linewidth',2, ...
              'erasemode','normal', ...
              'Color','r', ...
              'visible','off', ...
              'Tag','highlightb');

   % set up the dataline and data display
   hand.dataline = line('Xdata',NaN,'Ydata',NaN,...
              'erasemode','normal', ...
              'Marker','o','Linestyle','none',...
              'ButtonDownFcn', 'splinetool ''axesclick''', ...
              'Tag','dataline');
   set_data(hand,x,y,isf)

   hand.currentline = line('Xdata',NaN,'Ydata',NaN, ...
                 'linewidth',2, ...
                 'erasemode','normal', ...
                 'Color',currentcolor, ...
                 'ButtonDownFcn', 'splinetool ''axesclick''', ...
                 'Tag','currentline');

   hand = new(hand);
   view(hand.view(3),3,hand)
   if testing, set(hand.figure, 'Userdata',hand)
   else set(hand.figure, 'HandleVisibility','callback','Userdata',hand)
   end

   % if any data provided a special message, show it now:
   if exist('messh'), figure(messh), end

case 'toggle_ends'  % toggle the left/rite endcondition button

   if strcmp(y,'left'), j=1; else j=2; end
   toggle_ends(j,hand)

case 'toggle_show'  % toggle whether the current spline is plotted

   C = get(hand.list_names,'Value'); 
   names = get_names(get(hand.list_names,'String'));
   if get(hand.ask_show,'Value')==0  % need to turn off the plotting
      set([hand.currentline,hand.nameline],'Visible','off')
      names(C,1) = ' ';
   else     % need to turn on the plotting
      set([hand.currentline,hand.nameline],'Visible','on')
      names(C,1) = 'v';
   end
   
   set(hand.list_names,'String',{names})
   set_legend(hand,hand.Axes(1))

case 'undo'       % undo the most recent change, so far only in data
  
   set(hand.undo,'Enable','off')
   undoud = get(hand.undo,'Userdata');
   if isfield(undoud,'lineud')
      set(hand.nameline,'Userdata',undoud.lineud)
   end
   set_data(hand,undoud.xy(1,:),undoud.xy(2,:))
   get_approx(hand)

case 'view'

   clicked = gcbo;
   tag = get(clicked,'Tag'); ip = eval(tag(1));
   view(clicked,ip,hand)

otherwise
   errordlg(['we have not yet covered the string:', x], ...
               'We must work harder!','modal') 
end % switch action

function [name,handle] = add_to_list(hand,name)
%ADD_TO_LIST Add name to list of splines, marking it shown and current.
% If no name is given, take the next default one.

listud = get(hand.list_names,'Userdata'); 
names = get_names(get(hand.list_names,'String'));
if isempty(names), listud.untitleds = 1; end

if nargin<2  % make up a name
   while 1
      name = ['spline',num2str(listud.untitleds),'   '];
      if listud.untitleds>9, name = name(1:10); end
      listud.untitleds = listud.untitleds+1;
      if isempty(findobj('Tag',name)), break, end
   end
else % make sure the name is new and is exactly 10 characters long
   name = full_name(name,hand);
end

names = [names;['v || ',name]];
listud.length = listud.length+1;

% generate a new line, with its linestyle
styles = {'-';'--';':';'-.'}; nstyles = length(styles);
handle = line('Xdata',NaN, 'Ydata',NaN, ...
   'Parent',hand.Axes(1), ...
   'linestyle', styles{nstyles-rem(listud.length,nstyles)}, ...
   'erasemode','normal', ...
   'ButtonDownFcn', 'splinetool ''axesclick''', ...
   'Tag',name);
listud.handles = [listud.handles;handle];

% save the updated userdata listud:
set(hand.list_names,'String', {names}, ...
    'Value', listud.length, ...
    'Userdata',listud);

%  ... and set the line as showing in the graph:
set(hand.ask_show,'Value',1), set(hand.currentline,'Visible','on')

function [xx,yy] = ask_for_add(hand)
%ASK_FOR_ADD

switch get(gcbo,'Tag')
case 'add_item'
   [xx,yy] = ginput(hand);
case 'mov_item'
   [xx,yy] = get(hand.Axes(1),'CurrentPoint');
end % switch get(gcbo,'Tag')

if nargin>0&get(hand.dataline,'Userdata')
   yy = given(xx,hand);
end

function [x,y,xname,yname,isf] = ask_for_data
%ASK_FOR_DATA
% will return negative ISF in case user hits Cancel

answer = inputdlg({['Give a value or an expression for the data sites (',...
      'or the name of a function whose output is [x,y]):'],...
      ['Give a corresponding label for the x-axis (default is data',...
      ' sites expression)'], ...
  ['Give a value or an expression for the corresponding data values (',...
  'or the name of a function to be evaluated at the data sites you ',...
  'specified):'], ...
  ['Give a corresponding label for the y-axis (default is data values',...
  ' expression)']},...
  'Please provide data', 1, ...
   {'linspace(0,2*pi,31)','','cos',''});
if ~isempty(answer)&~isempty(answer{1})
   xname = answer{1}; yname = answer{3};
   % check out the answers. Lack of knowledge restricts me to merely check 
   % whether (a) x is a function, in which case I expect it to return both
   % x and y.
   if any(0+xname<48) % looks like a formula
      x = evalin('base',xname); 
   else
      a = which(xname);   
      if length(a)>1&a(end-1:end)=='.m'  
                             % this is the name of an m-file, let's hope it
                             % supplies both x and y
         [x,y] = evalin('base',xname);
         xname = 'x'; yname = 'y';
      else
         x = evalin('base',xname); 
      end
   end      
   
   isf = 0;
   if ~exist('y')
      if any(0+yname<48) % looks like a formula
         y = evalin('base',yname); 
      else
         isf = get_isf(yname);
         if isf>0, y = feval(yname,x); 
         else,     y = evalin('base',yname);
         end
      end %if any(0+yname<48) % looks like a formula
   end %if ~exist('y')

   % check that x is nondecreasing
   if any(diff(x)<0), [x,index] = sort(x); y = y(index); end

   if length(answer{2}>0), xname = answer{2}; end
   if ~isf&length(answer{4}>0), yname = answer{4}; end
else   % take default data, but with  isf < 0
   isf = -1;
   [x,y] = titanium; xname = 'temperature'; yname = 'titanium property';
end %if ~isempty(answer)

function bad_edit(editstring,lasterr)
%BAD_EDIT report failure of change in edit field

errordlg({'Evaluation of the expression you entered:';...
          ['''',editstring,''''];...
          'produced the following error message:';'';lasterr}, ...
            'Unusable expression supplied ...','modal')

function change_weight(hand)
%CHANGE_WEIGHT

V = get(hand.params(1,5),'Value'); lineud = get(hand.nameline,'Userdata');
output = convert(get(hand.params(2,5),'String')); 
if ischar(output)
   bad_edit(get(hand.params(2,5),'String'),output)
   set(hand.params(2,5),'String',lineud.w(V))
   return
else
   w = output;
end

if w<0
   ermsg = ...
       errordlg(['I won''t let you use negative weights, so have taken', ...
             ' this value to be zero.'], 'For simplicity, ...','modal');
   % in anticipation of the next error message, ...
   set(ermsg,'Position',get(ermsg,'Position')+[0,60,0,0])
   w = 0; set(hand.params(2,5),'String','0')
end
lineud.w(V) = w;
%check that there are still at least two positive weights
if w==0&length(find(lineud.w>0))<2
   errordlg({['Setting this weight to zero would leave you with only one',...
              ' positive weight.']}, ...
             'There must be at least two positive weights ...','modal')
   return
end 
   
lineud.wchanged = 1; set(hand.nameline,'Userdata',lineud)
set(hand.params(1,5),'String',lineud.w(:));

get_approx(hand)


function h = clickable(corner,ewid,id,ecall,userdata)
%CLICKABLE a setup for editing and repeatedly incrementing

% be sure to make id exactly 4 characters long

units = 'normalized';
if nargin<5, userdata==[]; end
edy = .0016; wh = .036; lx = .018; h = zeros(4,1);
h(1) = uicontrol('Parent',gcf, ...
	'Units',units, ...
	'BackgroundColor',[1 1 1], ...
	'ListboxTop',0, ...
	'Position',[corner(1) corner(2)+wh+3*edy ewid wh], ...
     'Userdata',userdata,...
	'Style','edit', ...
        'HorizontalAlignment','left', ...
     'Callback',['splinetool ''',ecall,''''], ...
     'TooltipString', 'change the current value', ...
	'Tag',['edit',id]);
     % much prefer the following triple to a slider
h(2)= uicontrol('Parent',gcf, ...
     'Units',units, ...
     'Position',[corner(1), corner(2),lx,wh], ...
        'Userdata',userdata,...
     'Style','pushbutton', ...
     'String','-', ...
     'TooltipString','decrease above value by increment', ...
     'Callback','splinetool ''pm''',...
     'Tag',['minus',id]);
h(3) = uicontrol('Parent',gcf, ...
     'Units',units, ...
     'Backgroundcolor',[1 1 1], ...
     'Position',[corner(1)+lx,corner(2),ewid-2*lx,wh], ...
     'Userdata',userdata,...
     'Style','edit', ...
     'HorizontalAlignment','left', ...
     'String','.1', ...
     'TooltipString','increment (changeable)', ...
     'Callback','splinetool ''increment''', ...
     'Tag',['increment',id]);
h(4)  = uicontrol('Parent',gcf, ...
     'Units',units, ...
     'Position',[corner(1)+ewid-lx,corner(2),lx,wh], ...
     'Userdata',userdata,...
     'Style','pushbutton', ...
     'String','+', ...
     'TooltipString','increase above value by increment', ...
     'Callback','splinetool ''pm''',...
     'Tag',['plus',id]) ;

function mess = concat(mess1,mess2)
%CONCAT  two cell arrays containing strings

mess = cell(length(mess1)+length(mess2),1);
[mess{:}] = deal(mess1{:},mess2{:}); 

function output = convert(string)
%CONVERT evaluate the given string; else return the error message generated

lastwarn(''), warning off
try
   output = eval(string);
catch
   output = lasterr;
end
lastw = lastwarn;
if ~isempty(lastw), output = lastw; end

function name = full_name(name,hand)
%FULL_NAME make name exactly 10 characters long, without any embedded blanks

lname = length(name);
if lname>10      % truncate the name
   name = name(1:10);
   errordlg('I can handle only names of up to 10 characters.','Sorry, ...', ...
            'modal')
elseif lname<10  % pad the name with blanks
   name = [name,repmat(' ',1,10-lname)];
end
if ~isempty(findobj('Tag',name))
   errordlg({['the name ''',name,''' is already in use']}, ...
             'Choose another name, please ...','modal')
   % recover the current name and return it
   name = hand.name;
end

function get_approx(hand)
%GET_APPROX    compute the current spline
 
% pick up the userdata of the current line 
lineud = get(hand.nameline,'Userdata');

% get the data
x = get(hand.dataline,'Xdata');
y = get(hand.dataline,'Ydata');

lineud.method = get(hand.method,'Value');
switch lineud.method
case 1
   % pick up what end condition was chosen.
   V = get(hand.endconds,'Value');
   
   lineud.endconds(1) = V;
   
   if V==2|V==3|V==4|V==9 % pick up the numerical endcondition information

      tag = get(gcbo,'Tag'); j = 1; if tag(end)=='e', j=2; end
      output = convert(get(hand.params(2,j),'String'));
      if ischar(output)
         bad_edit(get(hand.params(2,j),'String'),output)
         set(hand.params(2,1),'String',lineud.valconds(j))
         return
      else
         lineud.valconds(j) = output;
         lineud.valconds(3-j) = convert(get(hand.params(2,3-j),'String'));
      end
   end
    
   switch V
   case 1 %  'not-a-knot'
      lineud.cs = csapi(x,y);
   case {2,3} %  'clamped', 'complete'
      lineud.cs = csape(x,y,'complete',lineud.valconds);
      lineud.bottom = [' ',hand.name,' = csape(x,y,''complete'',', ...
      sprintf('[%g,%g]);',lineud.valconds),...
         '    % same as  ', ...
       sprintf('spline(x,[%g,y,%g])',lineud.valconds)];
   case 4 %  'second'
      lineud.cs = csape(x,y,'second',lineud.valconds);
      lineud.bottom = [' ',hand.name,' = csape(x,y,''second'',', ...
       sprintf('[%g,%g]);',lineud.valconds),...
         '    % same as  ', ...
       sprintf('csape(x,y,[2 2],[%g,%g])',lineud.valconds)];
   case 5 %  'periodic'
      lineud.cs = csape(x,y,'periodic');
   case {6,7} %  'variational', ''natural''
      lineud.cs = csape(x,y,'variational');
   case 8 %  'Lagrange'
      lineud.cs = csape(x,y);
   case 9 %  'custom'
      tmp = get(hand.params(1,1),'String'); l = str2num(tmp(1));
      tmp = get(hand.params(1,2),'String'); r = str2num(tmp(1));
      lineud.cs = csape(x,y,[l,r],lineud.valconds);
      lineud.endconds([2 3]) = [l,r];
      lineud.bottom = [' ',hand.name,' = csape(x,y,',...
       sprintf('[%g,%g],[%g,%g]);',...
         l,r,lineud.valconds),' % here we can specify the slope', ...
         ' at one end and the second derivative at the other.'];
   end %switch V

case 2 % combine both cubic and quintic smoothing spline.

   % decide on whether to use csaps or spaps; it will depend on how we got here.
   % if the smoothing parameter was reset, we use 
   %    csaps for order 4,  spaps for order 6
   % if the smoothing tolerance was reset, we use spaps
   % if a datum was added, deleted, or changed, or a weight was changed,
   %           we use the previously used one.
   % But, to begin with, we use   csaps(x,y,[],[],w)

   tag = get(gcbo,'Tag');  % find out what button was pushed

      % the default weights are the trapezoidal weights (as in spaps)
   if ~isfield(lineud,'w')|any(tag=='_')|tag(end-3:end)=='clkl'
      dx = diff(x); lineud.w = ([dx 0]+[0 dx])/2; lineud.wchanged = 0;
   end

   if ~strcmp(tag,'method') % find out what call was previously made
      whichcall = lineud.bottom(15:16);
   end

   switch tag

   case {'method','undo'} % we start from scratch

      lineud.par = -1; whichcall = 'cs'; lineud.tol = 0;

   case {'minusleft','editleft','plusleft'}% we specified the smoothing par ...
   
      output = convert(get(hand.params(2,1),'String'));
      if ischar(output)
         bad_edit(get(hand.params(2,1),'String'),output)
         set(hand.params(2,1),'String',shortstr(lineud.par))
         return
       else % make sure this value is in the interval [0 .. 1]
         lineud.par = max(0,min(1,output));
      end

      switch get(hand.order,'Value')
      case 1
         whichcall = 'cs';
      case 2
         whichcall = 'sp';
            % we must translate the parameter into a rho or tol
         if lineud.par==1
            lineud.tol(1) = 0;
         elseif lineud.par==0
            lineud.tol(1) = sum((lineud.w).*(y.*y));
         else
            lineud.tol(1) = lineud.par/(lineud.par-1);
         end
      end

   case {'add_item','del_item', 'minusclkl','editclkl','plusclkl'}

      lineud.tol(2:end) = [];
      
   case {'minusclkr','editclkr','plusclkr'}
   
      if get(hand.data_etc,'Value')==3
                    % we must update the add'l part of tolerance
         V = get(hand.params(1,5),'Value');
         if V==length(x) % attempt to change the NaN entry
             set(hand.params(2,5),'String',NaN)
             errordlg('Cannot change the last entry in this column', ...
                      'Error when modifying diff(roughness weights)','modal')
             return
         end
         output = convert(get(hand.params(2,5),'String'));
         if ischar(output) 
            bad_edit(get(hand.params(2,5),'String'),output)
            temp = get(hand.params(1,5),'String');
            set(hand.params(2,5),'String',eval(temp(V,:)))
            return
         else  % check that this new jump value is ok; if it is less than
               % the previous value, it may have to be raised
            change = output - lineud.tol(V+1);
            if V>1  change = change + lineud.tol(V); end
            if change<0
               [change,ich] = max([change, (1e-10)-min(lineud.tol(V+1:end))]);
                      % note the positive lower bound (1e-10) imposed on lambda
               if ich>1
                 errordlg(['In order to keep the roughness weight positive',...
                  ', I had to modify the jump, ', num2str(output), ...
                  ', you proposed.'],'Sorry, ...','modal')
               end
            end
            lineud.tol(V+1:end) = lineud.tol(V+1:end)+change;
            set(hand.params(1,5),'String', ...
                 [lineud.tol(2), diff(lineud.tol(2:end)),NaN])
         end

      end 

   case {'minusrite','editrite','plusrite'} % the tolerance was modified

      output = convert(get(hand.params(2,2),'String'));
      if ischar(output) 
         bad_edit(get(hand.params(2,2),'String'),output)
         set(hand.params(2,2),'String',0)
         return
      else
         lineud.tol(1) = output;
      end


      % for the time being, do not allow explicit specification of rho
      if lineud.tol(1) < 0
         lineud.tol(1) = 0; set(hand.params(2,2),'String','0');
         errordlg({'I won''t deal with a negative value for the tolerance.';...
               '';['(A negative value of the tolerance would be taken', ...
               ' by SPAPS as specification of its smoothing parameter.)']},...
               'For simplicity, ...','modal')
      end

      whichcall = 'sp';
      
   case 'order'

      if get(hand.order,'Value')==2, whichcall = 'sp'; end

   otherwise
      errordlg(['in smoothing, we have not yet covered the button:', ...
                 tag], 'We must work harder!','modal') 
   end %switch tag

   switch whichcall

   case 'cs'
      [lineud.cs,p_used] =  ...
            csaps(x,y,[lineud.par,lineud.tol(2:end)],[],lineud.w);
      lineud.tol(1) = sum((lineud.w).*((y-fnval(lineud.cs,x)).^2)); 
      parinfo = num2str(lineud.par);  dlaminfo = '';
      moreparinfo = ...
           ' %   csaps is used to enforce specified smoothing parameter;';
      if lineud.par<0
         moreparinfo = ' %  csaps chooses the smoothing parameter;';
      end
      lineud.par = p_used;
      weightinfo = ' error weights are those for trapezoidal rule';
      if isfield(lineud,'wchanged')&lineud.wchanged
         weightinfo = ' weights as listed in Data display on left';
      end
      if length(lineud.tol)>1 
         parinfo = ['[',parinfo,',cumsum(dlam)]',];
         dlaminfo = ['; dlam are the jumps in roughness weight',...
                       ' as shown in Data display on left.']; 
      end
      lineud.bottom = [' ',hand.name,' = csaps(x,y,',parinfo, ...
                       ', [], weights);', moreparinfo,weightinfo,dlaminfo];

   case 'sp'

      maintext = ' % spaps is used to enforce specified tolerance;';
      tolinfo = num2str(lineud.tol(1)); 
   
      lineud.bottom = [];
      switch get(hand.order,'Value')
      case 1
         minfo = '';
         [lineud.cs,ignored,rho] = spaps(x,y,lineud.tol,lineud.w);
         if isfinite(rho), lineud.par = rho/(1+rho);
         else, lineud.par = 1; end
      case 2
         minfo = ',3';
         if length(x)<3
            errordlg(['A 6th order smoothing spline requires',...
                   ' at least 3 data points.'])
         else
            [lineud.cs,ignored,rho] = spaps(x,y,lineud.tol,lineud.w,3);
            if isfinite(rho), lineud.par = rho/(1+rho);
            else, lineud.par = 1; end
         end

         if tag(end-3:end)=='left' % we must update lineud.tol, and the text
            lineud.tol(1) = sum((lineud.w).*((y-fnval(lineud.cs,x)).^2)); 
            maintext = ...
              ' % spaps is used to enforce specified smoothing parameter;';
         end
      end %switch get(hand.order,'Value')
   
      if isempty(lineud.bottom)
         lastinfo = ' ';
         if length(lineud.tol)>1
            tolinfo = ['[',tolinfo,',cumsum(dlam)]'];
            lastinfo = [' dlam are the jumps in roughness weight',...
                       ' shown in Data display on left.']; 
         end
         if isfield(lineud,'wchanged')&lineud.wchanged
            tolinfo = [tolinfo,',weights'];
            lastinfo = [' weights as shown in Data display on left;',lastinfo];
         end
         lineud.bottom = [' ',hand.name,' = spaps(x,y,',tolinfo,minfo,');',...
         maintext, lastinfo]; 
      end
   otherwise
      errordlg(['in smoothing, we have not yet covered the call:', ...
                 whichcall], 'We must work harder!','modal') 
   end %switch whichcall

case 3  % least-squares approximation

   moved = 0;
   tag = get(gcbo,'Tag');  % find out what button was pushed
   switch tag
   case {'method' , 'order' , ...
        'minusclkl','editclkl','plusclkl','minusclkr','editclkr','plusclkr',...
        'add_item','rep_knot','del_item', 'undo', ...
        'minusclkm','editclkm','plusclkm'}

      if ~isfield(lineud,'knots')
         % we've just switched into l2; start with no interior knots
         lineud.k = min(4,length(x)); strk = num2str(lineud.k);
         lineud.bottom =  ...
         [' ',hand.name,' = spap2(1,',...
          strk,',x,y); %   we are starting off with the least squares',...
         ' polynomial approximation of order ',strk];
         lineud.knots = augknt(x([1 end]),lineud.k); 
         set(hand.order,'Value',lineud.k)
         lineud.w = ones(size(x)); 
      elseif ~isfield(lineud,'w')
         lineud.w = ones(size(x)); 
         lineud.bottom = [' ',hand.name,' = spap2(knots,',num2str(lineud.k),...
                          ',x,y);'];
         moved = 1;
      elseif  all(lineud.w==1)
         lineud.bottom = [' ',hand.name,' = spap2(knots,',num2str(lineud.k),...
                          ',x,y);'];
         moved = 1;
      else
         lineud.bottom = [' ',hand.name,' = spap2(knots,',num2str(lineud.k),...
                         ',x,y,weights);'];
         moved = 1;
      end

     
   case 'buttonm'  % we are to get a new set of knots via newknt
     
        lineud.knots = newknt(lineud.cs); 
        set(hand.params(1,4),'String',lineud.knots(:)) 
        markb(hand), set_bdisplay(lineud.knots,hand) 
        lineud.bottom = [' ',hand.name,' = spap2(newknt(',...
                          deblank(hand.name),'), ',...
                         num2str(lineud.k),',x,y,weights);'];
        moved = 1;
   
   case 'piecem' % we are to choose knots for a given number of pieces

      lineud.knots = eval(get(hand.piecem,'String'));
      moved = 1;

   otherwise
    errordlg(['in least-squares, we have not yet covered the button:', tag], ...
               'We must work harder!','modal') 
   end %switch tag

   if moved
      lastwarn(''), warning off
      try
         newcs = spap2(lineud.knots,lineud.k,x,y,lineud.w);
      catch
         newcs = lasterr;
      end
      lastw = lastwarn; 
      if ~isempty(lastw)
         errordlg({['Foiled attempt to use too large a number of pieces', ...
                   ' for the given data'];...
                   ['and order, as indicated by the following warning ',...
                    '(from SPAP2):'];'';lastw},'Bad order','modal')
      end
      if ischar(newcs)
         if strcmp(tag,'order')
            lineud.k = fnbrk(lineud.cs,'order');
            set(hand.order,'Value',lineud.k)
            errmss = ['Foiled attempt to use too high an order for', ...
                      ' the given knots and data,'];
         else
            errmss = ['Foiled attempt to use a knot sequence that fails to',...
                  ' satisfy the Schoenberg-Whitney conditions of the', ...
                  ' given order wrto any subset of the data sites,'];
         end
         errordlg({'';[errmss, ...
                 ' as indicated by the following warning or error:'];'';...
                 newcs},'Bad move...','modal')         
         lineud.knots = fnbrk(lineud.cs,'knots');
         set(hand.params(1,4),'String',lineud.knots(:),'Value',1+lineud.k)
         markb(hand), set_bdisplay(lineud.knots,hand)
      else
         lineud.cs = newcs;
         if length(lineud.knots)==1 % we only specified the number of pieces
            lineud.bottom = [' ',hand.name,...
                            ' = spap2(',num2str(lineud.knots),',',...
                            num2str(lineud.k),',x,y,weights);'];
            lineud.knots = fnbrk(newcs,'knots');
            set(hand.params(1,4),'String',lineud.knots(:)) 
            markb(hand), set_bdisplay(lineud.knots,hand) 
         end
      end
   else
      lineud.cs = spap2(lineud.knots,lineud.k,x,y);
   end

   % set the number of pieces in the piecem display
   set(hand.piecem,'String',length(find(diff(lineud.knots))>0))

case 4  % spline interpolation

   moved = 0;
   if ~isfield(lineud,'knots')
      % we've just switched into spline interpolation;
      %  start with the default knot choice
      lineud.bottom =  ...
      [' ',hand.name,' = spapi(4,x,y);', ...
      ' %   we are starting off with the cubic spline interpolant,', ...
      ' with knots supplied by aptknt(x,4).'];
      lineud.knots = aptknt(x,4); 
      lineud.k = length(lineud.knots) - length(x); 
      set(hand.order,'Value',lineud.k)
      set(hand.params(1,4),'String',lineud.knots(:),'Value',1+lineud.k)
      markb(hand)
   else
      switch get(gcbo,'Tag')
      case {'order','method','add_item','del_item'}
         [lineud.knots, lineud.k] = aptknt(x,lineud.k); 
         strk = num2str(get(hand.order,'Value')); 
         lineud.bottom = [' ',hand.name,' = spapi(',strk,',x,y);', ...
                       ' %  knots supplied by aptknt(x,',strk,')'];
         set(hand.order,'Value',lineud.k)
      otherwise
         moved = 1;
         lineud.bottom = [' ',hand.name,' = spapi(knots,x,y);', ...
         ' %  knots as shown in the Data display to the left '];
      end
   end

   if moved
      lastwarn(''), warning off
      try
         newcs = spapi(lineud.knots,x,y);
      catch
         newcs = lasterr;
      end
      lastw = lastwarn; if ~isempty(lastw), newcs = lasterr; end
      if ischar(newcs)
          errordlg({['Foiled attempt to use a knot sequence that violates',...
                  ' the Schoenberg-Whitney conditions wrto the data sites,',...
                  ' as indicated by the following warning or error:'];'';...
                  newcs },'Bad move...','modal')         
         lineud.knots = fnbrk(lineud.cs,'knots');
         set(hand.params(1,4),'String',lineud.knots(:))
         markb(hand)
      else
         lineud.cs = newcs;
      end
   else
      lineud.cs = spapi(lineud.knots,x,y);
   end
end %switch lineud.method

currentplot = fnplt(lineud.cs); 
set(hand.currentline, ...
   'Xdata',currentplot(1,:),'Ydata',currentplot(2,:))
% generate the first two derivatives and their end values
lineud.dcs = fnder(lineud.cs);
lineud.ddcs = fnder(lineud.dcs);
ends = fnbrk(lineud.cs,'interval');
lineud.ends = [fnval(lineud.dcs,ends);fnval(lineud.ddcs,ends)];
set(hand.nameline, ...
    'Xdata',currentplot(1,:), 'Ydata',currentplot(2,:), ...
    'Userdata',lineud);

% also take care of second view
set_view(hand), set_view_label(hand.name,hand)

set_displays(hand)

function isf = get_isf(yname)
%GET_ISF check whether yname is a legal function, returning 
%        1   for an m-file
%        2   for a built-in function
%        0   otherwise

b = which(yname);
if length(b)>1&b(end-1)=='.'&(b(end)=='m'|b(end)=='M')
                     % this is the name of an m-file; let's hope it
                     % supplies y for given x.
   isf = 1; 
else 
   if length(b)>7&b(1:8)=='built-in' 
               % it is a built-in function, of one variable I hope
      isf = 2;
   else
      isf = 0;
   end
end

function names = get_names(cnames)
%GET_NAMES  temporary way for recovering the names

if length(cnames{1})==0
   names = [];
else
   for j=size(cnames,1):-1:1, names(j,:) = cnames{j}; end
end

function [x,y] = ginput(hand)
%GINPUT local version of ginput, to display coordinates

% set up things
if get(hand.data_etc,'Value')==1
   hands = hand.params(2,[3 5]);
else
   hands = hand.params(2,4);
end
cf = hand.figure;
savehand = hand; % temporarily use the figure's userdata to store the handle
               % of the edit window(s) that should show the currentpoint coords
state = uisuspend(cf);
set(cf,'Backingstore','off', 'Userdata', hands, ...
        'WindowButtonMotionFcn','splinetool(''ginputmove'')',...
        'WindowButtonDownFcn','splinetool(''ginputdone'')', ...
        'Pointer','fullcrosshair')

while ishandle(cf)
   if isequal(get(cf,'Pointer'),'arrow')
      x = eval(get(hands(1),'String'));
      if length(hands)>1
         y = eval(get(hands(2),'String'));
      else y = 0; end
      set(cf,'Userdata',savehand)
      uirestore(state)
      return
    end
   pause(.001)
end

function values = given(x,hand)
%GIVEN   evaluate the given function

values = feval(get(get(hand.Axes(1),'Ylabel'),'String'),x);

function highlightn(clickp,hand)
%HIGHLIGHTN  highlight the point nearest CLICKP

% find the nearest data point
x = get(hand.dataline,'Xdata'); y = get(hand.dataline,'Ydata');
% since the user only sees the window as is, the data point 
% closest to the clicked point should be determined in the
% physical coordinates rather than those of the data.
[ignored,V] = ...
 min((([x(:) y(:)]-repmat(clickp(1,1:2),length(x),1)).^2)*...
            reshape(([2 1]./diff(reshape(axis,2,2))).^2,2,1));
if get(hand.params(1,3),'Value')~=V | length(get(hand.highlightxy,'Xdata'))>1 
   set(hand.params(1,3),'Value',V), markxy(hand)
end

function insert_and_mark(x,y,xx,yy,hand)
%INSERT_AND_MARK

[x,i] = sort([x xx]); tmp = [y yy]; y = tmp(i);

  % make the added point the current one
V = find(i==length(x));
set(hand.params(1,3),'Value',V);

   % re-initialize the weights, if any
lineud = get(hand.nameline,'Userdata');
if isfield(lineud,'tol'), lineud.tol(2:end)=[]; end
if isfield(lineud,'w'), lineud = rmfield(lineud,'w'); end
   % re-initialize the knots if necessary
if lineud.method==4|(lineud.method==3&(V==1|V==length(x)))
   lineud = rmfield(lineud,'knots');
end
set(hand.nameline,'Userdata',lineud);

set_data(hand,x,y)
markxy(hand)

function markb(hand)
%MARKB highlight a break in the figure

V = get(hand.params(1,4),'Value');

breaks = get(hand.params(1,4),'String'); 
ylim = get(hand.Axes(1),'Ylim');
set(hand.params(2,4),'String',breaks(V,:))
breaks = str2num(breaks);
set(hand.highlightb, 'Xdata', breaks([V V]), 'Ydata', ylim);

function markxy(hand)
%MARKXY highlight the marked data point

tag = get(gcbo,'Tag');
if length(tag)==5&tag(5)=='r'
   listt = hand.params(1,5); listu = hand.params(1,3);
else
   listt = hand.params(1,3); listu = hand.params(1,5);
end

V = get(listt,'Value'); set(listu,'Value',V);
set(listu,'ListboxTop', get(listt,'ListboxTop'));
x = get(hand.dataline,'Xdata');  y = get(hand.dataline,'Ydata'); 
set(hand.highlightxy, 'Xdata',x(V), 'Ydata',y(V))

   % also update the edit fields
tmp = get(hand.params(1,3),'String'); set(hand.params(2,3),'String',tmp(V,:))
tmp = get(hand.params(1,5),'String'); set(hand.params(2,5),'String',tmp(V,:))

function out = menu(varargin)
%MENU my own much more pliable version of MATLAB's menu command

cf = gcf; depth = .9; width = .6; margo2 = (1-width)/2; inter = .01;
bh = min(.09,(depth/nargin)-inter); handles = zeros(1,nargin);

handles(1) = uicontrol('Parent',cf,...
             'Style','text', ...
             'BackgroundColor',get(cf,'Color'),...
             'String',varargin{1}, ...
             'Units','normalized', ...
             'Position',[.01,.98-(bh+inter), .98, bh]);
             
for j=2:nargin
   callb = ['set(findobj(''name'',''Spline Tool''),''Userdata'',', ...
                num2str(j-1),')'];
   handles(j) = uicontrol('Parent',cf, ...
            'Units','normalized', ...
            'BackgroundColor',[1 1 1], ...
            'Position',[margo2, .98-j*(bh+inter), width, bh], ...
            'String',varargin{j}, ...
            'Callback', callb);
end

while ishandle(cf)
   pause(.01)
   out = get(cf,'Userdata');
   if ~isempty(out)
      delete(handles), return, end
end

function method(hand)
%METHOD

if nargin>1 hand = y; end
M = get(hand.method,'Value');
if M==2 % initialize the order display
   set(hand.order,'Value',1)
end
lineud = get(hand.nameline,'Userdata');
if isempty(lineud)
   M0 = 0;
else
   M0 = lineud.method;
end

if M~=M0, set_edits(M0,M,hand), end

parameters(hand)

function moved = move_knot(hand)
%MOVE_KNOT

moved = 0;

V = get(hand.params(1,4),'Value'); lineud = get(hand.nameline,'Userdata');
if V<=lineud.k | V>length(lineud.knots)-lineud.k
   errordlg('I will only let you modify an interior knot.', ...
            'The end knots are forever','modal')
   set(hand.params(2,4),'String',lineud.knots(V))
   return
end

output = convert(get(hand.params(2,4),'String'));
if ischar(output)
   bad_edit(get(hand.params(2,4),'String'),output)
   set(hand.params(2,4),'String',lineud.knots(V))
   return
else 
   knot = output;
end

% make sure this knot is still interior
if lineud.knots(1)>= knot| lineud.knots(end)<=knot
 errordlg('I won''t let you move a knot outside the basic interval.',...
      'The knot must remain interior.','modal')
   return
end

lineud.knots(V) = [];
[lineud.knots,i] = sort([lineud.knots, knot]);
set(hand.params(1,4),'Value',find(i==length(lineud.knots)),...
     'String',lineud.knots(:)) 
set(hand.nameline,'Userdata',lineud)
markb(hand), set_bdisplay(lineud.knots,hand) 
moved = 1;

function move_point(hand)
%MOVE_POINT % replace the current point by the point specified in the edit

x = get(hand.dataline,'Xdata');
V = get(hand.params(1,3),'Value');
output = convert(get(hand.params(2,3),'String'));
if ischar(output)
   bad_edit( get(hand.params(2,3),'String'),output)
   set(hand.params(2,3),'String',x(V))
   return
else
   xx = output;
end
if (V==1&x(1)~=xx)|(V==length(x)&x(end)~=xx)
   errordlg('I won''t let you change the extreme data sites.', ...
            'For simplicity, ...','modal')
elseif xx<x(1)|xx>x(end)
   errordlg('I won''t let you move data sites beyond the extreme ones.', ...
            'For simplicity, ...','modal')
else   
   x(V)=[]; y = get(hand.dataline,'Ydata'); yold = y(V); y(V) = [];
   if any(x==xx)
      errordlg('Data sites must be distinct.','Error','modal')
   else
      if get(hand.dataline,'Userdata')
         yy = given(xx,hand);
      else
         output = convert(get(hand.params(2,5),'String'));
         if ischar(output)
            bad_edit( get(hand.params(2,5),'String'),output)
            set(hand.params(2,5),'String',yold)
            return
         else
            yy = output;
         end
      end
      insert_and_mark(x,y,xx,yy,hand)
      get_approx(hand)
   end
end

function hand = new(hand)
%NEW start off a new function

[hand.name, hand.nameline] = add_to_list(hand);
set(hand.figure,'Userdata',hand)
set(hand.nameline,'Userdata',[])

set([hand.method,hand.endconds],'Value',1)
method(hand)

set_legend(hand,hand.Axes(1))

function ok = okvar(varname)
%OKVAR ok to overwrite the variable VARNAME in base workspace?

   ok = 1;
   if evalin('base',['exist(''',varname,''',''var'')'])
      answer = questdlg({'Ok to overwrite the variable';''; ...
                        ['     ',varname];'';'in the base workspace?';''}, ...
               'This variable already exists ...','No','Yes','No');
      if answer(1)=='N', ok = 0;
      end
   end

function parameters(hand)
%PARAMETERS

switch get(hand.method,'Value')
case 1

   % (re?)-enable the edit pushbuttons
   set(hand.params(1,1:2),'Enable','on')

   % pick up what end condition was chosen.
   V = get(hand.endconds,'Value');
   lineud = get(hand.nameline,'Userdata');
   lineud.endconds(1) = V;
   
   % for some of these, we need some data input before we can proceed
      set(hand.params(2:5,1:2),'Enable','off')
      if (V==2|V==3|V==4|V==9)
           % then we must turn on the clickable stuff, and ask for input
         set(hand.params(2:5,1:2),'Enable','on')
   
         switch V
   
         case {2,3}
           if get(hand.params(1,1),'String')=='2nd deriv.',
              toggle_ends(1,hand), end
           if get(hand.params(1,2),'String')=='2nd deriv.',
              toggle_ends(2,hand), end
         case 4
           if get(hand.params(1,1),'String')=='1st deriv.',
              toggle_ends(1,hand), end
           if get(hand.params(1,2),'String')=='1st deriv.',
              toggle_ends(2,hand), end
         end % switch V
      end
    
      % store endcondition details in lineud
   switch V
   case 1 %  'not-a-knot'
      lineud.endconds([2 3]) = [0 0];
      lineud.bottom = [' ',hand.name,' = csapi(x,y);', ...
         '    % same as csape(x,y,''not-a-knot'') or spline(x,y)'];
   case {2,3} %  'clamped', 'complete'
      lineud.endconds([2 3]) = [1 1];
   case 4 %  'second'
      lineud.endconds([2 3]) = [2 2];
   case 5 %  'periodic'
      lineud.endconds([2 3]) = [0 0];
      lineud.bottom = [' ',hand.name,' = csape(x,y,''periodic'');'];
   case {6,7} %  'variational', ''natural''
      lineud.endconds([2 3]) = [2 2];
      lineud.valconds = [0 0];
      lineud.bottom = [' ',hand.name,' = csape(x,y,''variational'');', ...
         '    % same as csape(x,y,[2 2],[0,0]), or csaps(x,y,1)', ...
         ', i.e., sets end second derivative to 0.'];
   case 8 %  'Lagrange'
      lineud.endconds([2 3]) = [0 0];
      lineud.bottom = [' ',hand.name,' = csape(x,y);',...
         '    % i.e., the default for  csape;',...
         ' supplies endslopes by local cubic interpolation.'];
   case 9 %  'custom'
       % everything about it possibly changes with endcond input
   end %switch V

   set(hand.nameline,'Userdata',lineud)

case 2
   % all is done directly in get_approx

case {3,4} 
   % nothing so far

end %switch get(hand.method,'Value')

get_approx(hand)


function segsout = segplot(s,arg2,arg3)
%SEGPLOT plot a collection of segments
%
%        segsout = segplot(s,arg2,arg3)
%
%  returns the appropriate sequences  SEGSOUT(1,:) and  SEGSOUT(2,:)
% (containing the segment endpoints properly interspersed with NaN's)
% so that PLOT(SEGSOUT(1,:),SEGSOUT(2,:)) plots the straight-line 
% segment(s) with endpoints  (S(1,:),S(2,:))  and  (S(d+1,:),S(d+2,:)) ,
% with S of size  [2*d,:].
%
%  If there is no output argument, the segment(s) will be plotted in
% the current figure (and nothing will be returned),
% using the linestyle and linewidth optionally specified by ARG2 and ARG3 
% as a string and a number, respectively.

% cb dec96, mar97, apr99; DIRFIELD is a good test for it.

[twod,n] = size(s); d = twod/2;
if d<2, error('the input must be of size (2d)-by-n with d>1.'), end
if d>2, s = s([1 2 d+1 d+2],:); end

tmp = [s; repmat(NaN,1,n)];
segs = [reshape(tmp([1 3 5],:),1,3*n);
        reshape(tmp([2 4 5],:),1,3*n)];

if nargout==0
   symbol=[]; linewidth=[];
   for j=2:nargin
      eval(['arg = arg',num2str(j),';'])
      if isstr(arg), symbol=arg;
      else
         [ignore,d]=size(arg);
         if ignore~=1, error(['arg',num2str(j),' is incorrect.'])
         else
            linewidth = arg;
         end
      end
   end
   if isempty(symbol) symbol='-'; end
   if isempty(linewidth) linewidth=1; end
   plot(segs(1,:),segs(2,:),symbol,'linewidth',linewidth)
   % plot(s([1 3],:), s([2 4],:),symbol,'linewidth',linewidth)
   % would also work, without all that extra NaN, but is noticeably slower.
else
   segsout = segs;
end

function set_bdisplay(breaks,hand)
%SET_BDISPLAY

if nargin==2
   lp1 = length(breaks);
   for j=1:2
      ylim = get(hand.Axes(j),'Ylim');
      xy = segplot([breaks;repmat(ylim(1),1,lp1); ...
                    breaks;repmat(ylim(2),1,lp1)]);
      set(hand.breaks(j),'Xdata', xy(1,:),'Ydata', xy(2,:),'Visible','on', ...
                         'Userdata','on');
   end
else % refresh the knot/break lines in second graph
   n = size(get(breaks.breaks(2),'Ydata'),2)/3; 
   ylim = get(breaks.Axes(2),'Ylim');
   set(breaks.breaks(2),'Ydata',...
      reshape([repmat(ylim(:),1,n);repmat(NaN,1,n)],1,3*n))
end

function hand = set_current(hand)
%SET_CURRENT update the current setting according to the present value
%   of Value in list_names . If that value is 0, generate the default fit.

V = get(hand.list_names,'Value');

if V<1
   hand = new(hand);
else %make the V-th guy the current one
   names = get_names(get(hand.list_names,'String'));
   name = names(V,:); view = name(1); name = name(6:end);
   listud = get(hand.list_names,'Userdata');
   nameline = listud.handles(V);
   set(hand.currentline, ...
      'Xdata',get(nameline,'Xdata'), ...
      'Ydata',get(nameline,'Ydata'));
   hand.name = name;
   if view=='v'
      set(hand.ask_show,'Value',1)
      set(hand.currentline,'Visible','on')
   else
      set(hand.ask_show,'Value',0)
      set(hand.currentline,'Visible','off')
   end
   hand.nameline = nameline; set(gcbf,'Userdata',hand)
   set_view(hand), set_view_label(hand.name,hand)
end
   
function set_data(hand,x,y,isf)
%SET_DATA  (re)set the data

if nargin<4 
   % before change, preserve current data and setup in undoud
   undoud.xy = [get(hand.dataline,'Xdata');get(hand.dataline,'Ydata')];
   set(hand.undo,'Userdata',undoud)

   set(hand.dataline,'Xdata',x,'Ydata',y)
   hxlabel = get(hand.Axes(2),'Xlabel');
   if isempty(get(hxlabel,'Userdata'))
      set(hxlabel,'Userdata',1, ...
      'String',[get(hxlabel,'String'),'  (modified data)'])
      if ~any(get(hand.viewmenu,'Userdata'))
         set(get(hand.Axes(1),'Xlabel'),'String', ...
         get(hxlabel,'String')) 
      end
   end
   set(hand.params(1,3),'String',x(:))
   set(hand.params(1,5),'String',y(:))
   markxy(hand)
else
   set(hand.dataline,'Xdata',x,'Ydata',y,'Userdata',isf);
end

function set_displays(hand)
%SET_DISPLAYS updates model, endconditions, bottomline, data/breaks, etc

%  extract all the line detail from nameline
lineud = get(hand.nameline,'Userdata');

%  Set the bottom line:
set(hand.bottomlinetext, 'String',lineud.bottom) 

%  reset the edit fields according to the current method
M = get(hand.method,'Value');
M0 = lineud.method; 
    % what should happen next depends on how we got here
tag = get(gcbo,'Tag');
if ~isempty(tag)&(strcmp(tag,'list_names')|strcmp(tag,'Pushdel'))
   % either list_names or pushdel was clicked, hence  M  is actually the old
   % method and M0 is the new, hence we switch them now: 
   M0 = M; M = lineud.method;
      % we are switching back to an old approx, hence must update the displays 
      % concerning order and knots
   switch M
   case 2
      set(hand.order,'Value',max(1,fnbrk(lineud.cs,'order')/2-1))
      % This fails to recover the fact that smoothing order was 3 in case
      % the smoothing spline is of order < 6, e.g., if P was 0.
   case {3,4}
      set(hand.order,'Value',lineud.k)
      if get(hand.data_etc,'Value')==2 % must also update the knot display
         set(hand.params(1,4),'String',lineud.knots(:), ...
         'Value',lineud.k+1)
         markb(hand), set_bdisplay(lineud.knots,hand) 
      end
   end
end
if M~=M0
   set(hand.method,'Value',M);
   set_edits(M0,M,hand)
end

% fill editleft/rite etc appropriately
switch M
case 1   %  Set the end conditions:
   
      % initialize 'String' and 'Userdata' for editleft and editrite to contain
      % slope and second derivative of the current spline, toggling these only
      % when the endconds is 2
   
   set(hand.endconds, 'Value',lineud.endconds(1) ) 
   
   for j=1:2
      if lineud.endconds(j+1)<1  % make edit and incrementers inactive
         enable = 'off'; disable = 'on';
      else
         enable = 'on'; disable = 'off';
      end
      set(hand.params(2,j), ...
         'Enable',enable, ...
         'String',lineud.ends(1,j),...
         'CreateFcn',num2str(lineud.ends(2,j)));
      set(hand.params(1,j), ...
         'String','1st deriv.');
      if lineud.endconds(j+1)==2, toggle_ends(j,hand), end
      if enable(2) == 'n'
         set(hand.params(4,j),'Enable',enable, 'String',...
         shortstr(abs(eval(get(hand.params(2,j),'String')))/10,1))
      else
         set(hand.params(4,j),'Enable',enable,'String',0)
      end
      set(hand.params([3 5],j),'Enable',enable)
   end

case 2 % set parameter and tolerance
   set(hand.params(2,1),'String',shortstr(lineud.par))
   if ~get(hand.params(4,1),'Value')
      set(hand.params(4,1),'String',...
      shortstr(min(lineud.par, 1-lineud.par)/10,1))
   end
   set(hand.params(2,2),'String',shortstr(lineud.tol(1)))
   if ~get(hand.params(4,2),'Value')
      set(hand.params(4,2),'String',shortstr(lineud.tol(1)/10,1))
   end

case {3,4}
   for j=1:2
      set(hand.params(2,j),'String',lineud.ends(1,j),...
                        'CreateFcn',num2str(lineud.ends(2,j)))
   end
   set(hand.params(4,1:2),'String',0)
   set(hand.params(1,1:2),'String','1st deriv.')

end %switch M

function set_edits(M0,M,hand)
%SET_EDITS  set up the fields for the viewing and editing of params and data

% Here, M0 is the previously used method, and M is the current method.
% Start by trying to define the labels in the popup in dependence on the
% gui's window size.

tmp = get(hand.figure,'Position'); dkw = fix(tmp(3)*20);

sitesvals =    '  sites   and   values';
breakshead =   '  breaks  ';
knotshead =    '  knots  ';
sitesweights = '  sites   and   error weights';
sitesjumps =   '  sites   and  roughness weight';

%  start with clean slate
set([hand.endconds; hand.partext(:); hand.params(1,:).'],'Visible','off')

set(hand.endtext,'String','Order:')

switch M
case 1 % cubic spline interpolation

   set(hand.order, 'Visible','off')
   set([hand.endconds,hand.params(1,:),hand.partext(1,:)],'Visible','on')
   set(hand.endtext,'String','End conditions:')

   set(hand.params(2,1:2),'Enable','on')
    %  also indicate that increment fields have not yet been edited,
    %  by setting their 'Value' to 0
   set(hand.params(4,1:2),'Value',0)
   
   % also choose the menu for data_etc and start it off with Value 1
   set(hand.data_etc, ...
      'Value',1, ...
      'String',{sitesvals; breakshead}, ...
      'TooltipString', 'work with data or breaks')
   
case 2 % smoothing spline

   set(hand.order,'String',{'4';'6'},'Visible','on')
   set(hand.partext(2,:),'Visible','on')
   set(hand.params(1:5,1:2),'Enable','on')
     %%%%%%%%%%%%  clickables
   set(hand.params(2:5,1:2),'Enable','on')
   set(hand.params(4,1:2),'Value',0)
    
   % also choose the menu for data_etc and start it off with Value 1
   set(hand.data_etc, ...
      'Value',1, ...
      'String',{sitesvals; sitesweights; sitesjumps}, ...
      'TooltipString', 'work with data or weights')
   
case {3,4} %  least-squares approximation  and spline interpolation

   % start off with a clean slate concerning knots
   lineud = get(hand.nameline,'Userdata');
   if isfield(lineud,'knots')
      set(hand.nameline,'Userdata',rmfield(lineud,'knots'))
   end
   
   if isfield(lineud,'k'),k=lineud.k; else, k=4; end
   set(hand.order,'String',get(hand.order,'Userdata'),'Value',k,'Visible','on')
   set([hand.partext(1,:),hand.params(1,1:2)],'Visible','on')
   set(hand.params(2:5,1:2),'Enable','off')
   set(hand.params(1,1:2),'Enable','on')
   
   % also choose the menu for data_etc and start it off with Value 1
   if M==3
      set(hand.data_etc, 'Value',1, ...
      'String',{sitesvals;knotshead;sitesweights}, ...
      'TooltipString', 'work with data,  knots, or weights')
   else
      set(hand.data_etc, 'Value',1, ...
      'String',{sitesvals;knotshead}, ...
      'TooltipString', 'work with data or knots')
   end

end % switch M

splinetool('data_etc',hand)

function set_legend(hand,ax)
%SET_LEGEND (re)generate the legend

% get the list of visible handles
names = get_names(get(hand.list_names,'String'));
listud = get(hand.list_names,'Userdata'); handles = listud.handles;
% the following complication is needed when set_legend is used for printing
tmp = hand.currentline; handles(get(hand.list_names,'Value')) = tmp(1);
listshown = find(names(:,1)=='v');
handles = handles(listshown);
names = ['data      ';names(listshown,6:end)];
legend(ax,[findobj(hand.figure,'Tag','dataline');handles], names,0)

function set_view(hand)
%SET_VIEW   update the view graph

viewud = get(hand.viewmenu,'Userdata');

ip = find(viewud==1);
if ~isempty(ip)
   lineud = get(hand.nameline,'Userdata');
   switch ip
   case 1
      xy = fnplt(lineud.dcs);
      set(hand.viewline,'Xdata',xy(1,:),'Ydata',xy(2,:))
   case 2
      xy = fnplt(lineud.ddcs);
      set(hand.viewline,'Xdata',xy(1,:),'Ydata',xy(2,:))
   case 3
      if get(hand.dataline,'Userdata')
         xx = get(hand.nameline,'Xdata');
         set(hand.viewline,'Xdata', xx, ...
          'Ydata', get(hand.nameline,'Ydata')- given(xx,hand))
      else
         x = get(hand.dataline,'Xdata');
         y = get(hand.dataline,'Ydata');
         set(hand.viewline,'Xdata',x,'Ydata',y-fnval(lineud.cs,x));
      end
      tmp = get(hand.Axes(2),'Xtick'); 
      set(hand.zeroline,'Xdata',tmp([1 end]))
   end %switch ip
   if strcmp(get(hand.breaks(2),'Userdata'),'on');
      set(hand.breaks(2),'Visible','off')  
      set_bdisplay(hand), set(hand.breaks(2),'Visible','on')  
   end
end

function set_view_label(name,hand)
%SET_VIEW_LABEL update Ylabel in second axes to given name

if any(get(hand.viewmenu,'Userdata'))
   hylabel = get(hand.Axes(2),'Ylabel');
   tmp = get(hylabel,'String'); tmp(end-9:end)=name; set(hylabel,'String',tmp)
end

function output = shortstr(varargin)
%SHORTSTR formated string

if nargin<2, varargin{2} = '%-0.5g'; end % else, form = '%-0.1g'; end
output = num2str(varargin{:});

function toggle_ends(j,hand)
%TOGGLE_ENDS toggle between 1st and 2nd derivative value display

pushbutton = hand.params(1,j);
tmp = get(pushbutton,'String');
if tmp(1)=='1'
   deriv=2; set(pushbutton,'String','2nd deriv.');
else
   deriv=1; set(pushbutton,'String','1st deriv.');
end
h = hand.params(2,j); 
enable = 'off'; 
if get(hand.method,'Value')==1
   V = get(hand.endconds,'Value');
   if(deriv==1&(V==2|V==3))|(deriv==2&V==4)|V==9
      enable = 'on';
   end
end
other = get(h,'CreateFcn'); ither = get(h,'String');
set(h,'String',other,'CreateFcn',ither,'Enable',enable)

function view(clicked, ip, hand)
%VIEW

set(hand.zeroline,'Visible','off')
viewud = get(hand.viewmenu,'Userdata');
Axes1 = hand.Axes(1); Axes2 = hand.Axes(2);
if viewud(ip)==1  % turn off the view
   set(clicked,'Checked','off')
   set(get(Axes1,'Xlabel'),'String',get(get(Axes2,'Xlabel'),'String'))
   set(Axes1,'Xticklabel', get(Axes2,'Xticklabel'))
   set([Axes2;allchild(Axes2)],'Visible','off')
   viewud(ip)=0;
else
   set(clicked,'Checked','on')
   otherv = find(viewud==1);
   if isempty(otherv) % turn on the view
      set([Axes2,hand.viewline],'Visible','on')
      set(get(Axes1,'Xlabel'),'String',[])
      set(Axes1,'Xticklabel',[])
   else  % toggle the label currently shown
      viewud([ip,otherv])=[1 0];
      set(hand.view(otherv),'Checked','off')
   end

   viewud(ip)=1;
   switch ip
   case 1 
      set(get(Axes2,'Ylabel'),'String',['1st deriv. of ',hand.name])
   case 2 
      set(get(Axes2,'Ylabel'),'String',['2nd deriv. of ',hand.name])
   case 3 
      set(get(Axes2,'Ylabel'),'String',['error in ',hand.name])
      set(hand.zeroline,'Visible','on')
   end %switch ip
end %if viewud(ip)==1 
set(hand.viewmenu,'Userdata',viewud);
set_view(hand)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%   the end
