% DynaSim GUI
% Purpose: graphical interface for DynaSim model building and exploration.
% Usage:
%   dynasim; % load default model
%   dynasim(specification)
%
% Author: Jason Sherfey, PhD <jssherfey@gmail.com>
% Copyright (C) 2016 Jason Sherfey, Boston University, USA

function dynasim(spec)

% abort if not running in MATLAB
if ~strcmp(reportUI,'matlab')
  warning('DynaSim GUI is not supported in GNU Octave at this time.');
  return
end

global handles SPEC MODEL cfg LASTSPEC LASTCFG
handles=[];

if nargin==0 % default model
  %SPEC=dsCheckSpecification([]);
  ina={
    'INa(v,m,h) = -gNa.*m.^3.*h.*(v-50); gNa=120';  % sodium current
    'dm/dt = aM(v).*(1-m)-bM(v).*m; m(0)=.1*ones(1,N_pop)';       % - activation
    'dh/dt = aH(v).*(1-h)-bH(v).*h; h(0)=.1*ones(1,N_pop)';       % - inactivation
    'aM(v) = (2.5-.1*(v+65))./(exp(2.5-.1*(v+65))-1)';
    'bM(v) = 4*exp(-(v+65)/18)';
    'aH(v) = .07*exp(-(v+65)/20)';
    'bH(v) = 1./(exp(3-.1*(v+65))+1)';
    '@current+=INa';
  };
  ik={
    'IK(v,n) = -gK.*n.^4.*(v+77); gK=36';           % potassium current
    'dn/dt = aN(v).*(1-n)-bN(v).*n; n(0)=0*ones(1,N_pop)';        % - activation
    'aN(v) = (.1-.01*(v+65))./(exp(1-.1*(v+65))-1)';
    'bN(v) = .125*exp(-(v+65)/80)';
    '@current+=IK';
  };
  iampa={
    'gSYN=0.1; ESYN=0; tauD=2; tauR=0.4';
    'netcon=ones(N_pre,N_post)';
    'ISYN(X,s)=(gSYN.*(s*netcon).*(X-ESYN))';
    'ds/dt=-s./tauD+((1-s)/tauR).*(1+tanh(X_pre/10)); s(0)=.1*ones(1,N_pre)';
    '@isyn += -ISYN(X_post,s)';
  };
  igaba={
    'gSYN=0.25; ESYN=-80; tauD=10; tauR=0.4';
    'netcon=ones(N_pre,N_post)';
    'ISYN(X,s)=(gSYN.*(s*netcon).*(X-ESYN))';
    'ds/dt=-s./tauD+((1-s)/tauR).*(1+tanh(X_pre/10)); s(0)=.1*ones(1,N_pre)';
    '@isyn += -ISYN(X_post,s)';
  };
  input='Iapp=0; noise=0; @input+=Iapp+noise*randn(1,N_pop)';
  master_equations='dv/dt=@input+@current+@isyn; v(0)=-65';
  mechanism_list={'ina','ik','input1'};
  %master_equations='dv/dt=Iapp+@current+noise*randn(1,N_pop);';
  %mechanism_list={'ina','ik'};
  s=[];
  s.populations(1).name='E';
  s.populations(1).size=8;
  s.populations(1).equations=master_equations;
  s.populations(1).mechanism_list=mechanism_list;
  s.populations(1).parameters={'Iapp',5,'noise',40,'gNa',125};
  s.populations(2).name='I';
  s.populations(2).size=2;
  s.populations(2).equations=master_equations;
  s.populations(2).mechanism_list=mechanism_list;
  s.populations(2).parameters={'Iapp',0,'noise',40};
  s.connections(1).direction='I->E';
  s.connections(1).mechanism_list={'igaba'};%{'iGABAa'};
  s.connections(1).parameters={'tauD',10,'gSYN',.1};
  s.connections(2).direction='E->I';
  s.connections(2).mechanism_list={'iampa'};%{'iAMPA'};
  s.connections(2).parameters={'tauD',2,'gSYN',.1};
  s.mechanisms(1).name='ina';
  s.mechanisms(1).equations=ina;
  s.mechanisms(2).name='ik';
  s.mechanisms(2).equations=ik;
  s.mechanisms(3).name='iampa';
  s.mechanisms(3).equations=iampa;
  s.mechanisms(4).name='igaba';
  s.mechanisms(4).equations=igaba;
  s.mechanisms(5).name='input1';
  s.mechanisms(5).equations=input;
  spec=s;

end
% check specification
SPEC=dsCheckSpecification(spec);
% remove global population params (already applied to pop(#).mechanisms(#).equation)
[SPEC.populations.parameters]=deal([]);
if ~isempty(SPEC.connections)
  [SPEC.connections.parameters]=deal([]);
end
% generate model
MODEL=dsGenerateModel(SPEC);
% extract model ODEFUN and IC
try
  [ODEFUN,IC,elem_names]=dsDynasim2odefun(dsPropagateParameters(MODEL));
catch
  ODEFUN='';
  IC=[];
  elem_names={};
end
% txt=dsExtractModelStrings(MODEL,'model',0);
% ODEFUN=txt{1}; IC=txt{2}; elem_names=txt{3};

% app configuration
cfg.username='anonymous';
cfg.model_text='model equations ...';
cfg.V=linspace(-100,100,20e3); % default xdata for auxiliary function plot
cfg.linecolors  = 'kbrgmy';
cfg.linetype  = {'-',':','-.','--'};
cfg.max_num_plots=3;
cfg.num_xticks=5;
cfg.num_steps_per_plot=400; % number sim time steps per sim view update
cfg.sim_paused=-1;
cfg.sim_stopped=0;
cfg.ymin=-90*ones(1,cfg.max_num_plots);
cfg.ymax=50*ones(1,cfg.max_num_plots);
cfg.ModelFontName='Monospaced'; % 'Courier'
cfg.autoscale_charcode=5864;

cfg.BackgroundColor=[204 204 180]/255;
cfg.ButtonColor=[0 102 153]/255/1.75; % 'c',[51 204 204]/255
cfg.ButtonFontColor=[240 240 240]/255; % 'k'

% cfg.BackgroundColor=[204 204 180]/255;
% cfg.ButtonColor=[0 255 255]/255/1.25;
% cfg.ButtonFontColor='k';

% default data
cfg.ODEFUN=ODEFUN;
cfg.IC=IC;
cfg.elem_names=elem_names;
cfg.ntime=20e3+1;
cfg.dt=.01;
cfg.t0=0;
cfg.tf=200;
cfg.t=(0:cfg.ntime-1)'*cfg.dt;
cfg.t_plot_indices=1:cfg.ntime;
cfg.Y=zeros(cfg.ntime,max(1,length(cfg.IC)));
cfg.xtick=linspace(cfg.t(1),cfg.t(end),cfg.num_xticks);
cfg.xticklabel=linspace(cfg.t(1),cfg.t(end),cfg.num_xticks);

if nargin==0
  LASTSPEC=SPEC;
  LASTCFG=cfg;
end

% open figure for model designer
% figure_position=[245 145 1460 770]; % compact
% handles.fig_main = figure('position',figure_position,'color',cfg.BackgroundColor,'tag','designer','name','DynaSim Model Builder','NumberTitle','off','WindowScrollWheelFcn',@ZoomFunction,'CloseRequestFcn','delete(gcf); clear global H');
% Full screen figure
handles.fig_main = figure('units','normalized','outerposition',[0 0 1 1],'color',cfg.BackgroundColor,'tag','designer','name','DynaSim Model Builder','NumberTitle','off','WindowScrollWheelFcn',@ZoomFunction,'CloseRequestFcn','delete(gcf); clear global H');

% #####################################
% MENU NEEDS WORK!!!
% #####################################
% Set up Menu
set(handles.fig_main,'MenuBar','none');
file_m = uimenu(handles.fig_main,'Label','File');
uimenu(file_m,'Label','New model','Callback','global handles; close(handles.fig_main); dynasim(dsCheckSpecification([]));');%{@OpenModel,1,'file'});
uimenu(file_m,'Label','Open model','Callback',@OpenModel);%{@OpenModel,1,'file'});
% uimenu(file_m,'Label','Append model(s)','Callback',{@OpenModel,0,'file'});
uimenu(file_m,'Label','Save model','Callback',@SaveModel);
% uimenu(file_m,'Label','Upload model','Callback',@UploadModel);
% uimenu(file_m,'Label','Write solve file','Callback','global CURRSPEC; write_dnsim_script(CURRSPEC);');
% ws_m = uimenu(file_m,'Label','Interact');
% uimenu(ws_m,'Label','Pass model (''spec'') to command window','Callback','global CURRSPEC; assignin(''base'',''spec'',CURRSPEC);');
% uimenu(ws_m,'Label','Update model (''spec'') from base workspace','Callback',{@refresh,1});
% uimenu(ws_m,'Label','Pass ''sim_data'' (during interactive simulation) to command window','Callback','global cfg;cfg.publish=1;');
% import_m = uimenu(file_m,'Label','Import');
% uimenu(import_m,'Label','XPP (wip)','Callback','not implemented yet');
% export_m = uimenu(file_m,'Label','Export');
% uimenu(export_m,'Label','XPP (wip)','Callback','not implemented yet');
% uimenu(export_m,'Label','NEURON (wip)','Callback','not implemented yet');
% uimenu(export_m,'Label','CellML (wip)','Callback','not implemented yet');
uimenu(file_m,'Label','Refresh GUI','Callback','global SPEC handles; close(handles.fig_main); dynasim(SPEC);');
uimenu(file_m,'Label','Exit','Callback','global handles cfg; close(handles.fig_main); clear handles cfg; warning on');
% plot_m = uimenu(handles.fig_main,'Label','Plot');
% uimenu(plot_m,'Label','quick plot','Callback',['global CURRSPEC; if ismember(''sim_data'',evalin(''base'',''who'')), plotv(evalin(''base'',''sim_data''),CURRSPEC,''varlabel'',sprintf(''%s'',CURRSPEC.variables.global_oldlabel{1})); else disp(''load data to plot''); end']);
% uimenu(plot_m,'Label','plotpow','Callback','global CURRSPEC; if ismember(''sim_data'',evalin(''base'',''who'')), plotpow(evalin(''base'',''sim_data''),CURRSPEC,''spectrogram_flag'',0); else disp(''load data to plot''); end');
% uimenu(plot_m,'Label','plotspk','Callback','global CURRSPEC; if ismember(''sim_data'',evalin(''base'',''who'')), plotspk(evalin(''base'',''sim_data''),CURRSPEC,''window_size'',30/1000,''dW'',5/1000); else disp(''load data to plot''); end');
% uimenu(plot_m,'Label','visualizer','Callback','global CURRSPEC; if ismember(''sim_data'',evalin(''base'',''who'')), visualizer(evalin(''base'',''sim_data'')); else disp(''load data to plot''); end');

InitializeMainGUI;

%% Set up GUI Model Builder
function InitializeMainGUI % (todo: called by GUI Launcher)
global handles SPEC cfg

% extract specification content necessary for setting up app controls
pop_names={SPEC.populations.name};
active_model_component=SPEC.populations(1).name;
active_mechanism_list=SPEC.populations(1).mechanism_list;
if isfield(SPEC.populations(1).mechanisms,'equations')
  active_mechanism_text=SPEC.populations(1).mechanisms(1).equations;
else
  active_mechanism_text='';
end
active_mechanism_userdata=[];

bgcolor=cfg.BackgroundColor;
% % open figure for model designer
% figure_position=[50 80 1800 900]; % 50% aspect ratio
% figure_position=[245 145 1460 770]; % compact
% handles.fig_main = figure('position',figure_position,'color',bgcolor,'tag','designer','name','DynaSim Model Builder','NumberTitle','off','WindowScrollWheelFcn',@ZoomFunction,'CloseRequestFcn','delete(gcf); clear global H');

% ####################################################################
% Views: right panel
handles.pview=uipanel('parent',handles.fig_main,'title','','visible','on','units','normalized','position',[.5 0 .5 1]);
% Simulation View
handles.bsimview=uicontrol('parent',handles.pview,'style','pushbutton','tag','viewtab','units','normalized','position',[0 .95 .5 .05],'string','Simulation View','fontsize',11,'FontWeight','bold','backgroundcolor',[.7 .7 .7],'callback','set(findobj(''tag'',''viewtoggle''),''visible'',''off''); set(findobj(''tag'',''viewtab''),''backgroundcolor'',[1 1 1]); set(findobj(''userdata'',''handles.psimview''),''visible'',''on''); set(gcbo,''backgroundcolor'',[.7 .7 .7]);');
handles.psimview=uipanel('parent',handles.pview,'backgroundcolor','w','title','','visible','on','tag','viewtoggle','userdata','handles.psimview','units','normalized','position',[0 0 1 .95]);
% Equation View
handles.beqnview=uicontrol('parent',handles.pview,'style','pushbutton','tag','viewtab','units','normalized','position',[.5 .95 .5 .05],'string','Equation View','fontsize',11,'FontWeight','bold','backgroundcolor',[1 1 1],'callback','set(findobj(''tag'',''viewtoggle''),''visible'',''off''); set(findobj(''tag'',''viewtab''),''backgroundcolor'',[1 1 1]); set(findobj(''userdata'',''handles.peqnview''),''visible'',''on''); set(gcbo,''backgroundcolor'',[.7 .7 .7]);');
handles.peqnview=uipanel('parent',handles.pview,'backgroundcolor',[.9 .9 .9],'title','','visible','off','tag','viewtoggle','userdata','handles.peqnview','units','normalized','position',[0 0 1 .95]);
handles.txt_model = uicontrol('parent',handles.peqnview,'style','edit','units','normalized','tag','modeltext','position',[0 0 1 1],'string',cfg.model_text,'ForegroundColor','k','FontName',cfg.ModelFontName,'FontSize',9,'HorizontalAlignment','Left','Max',100,'BackgroundColor',[.95 .95 .95]);
% % enable horizontal scrolling
%   jEdit = findjobj(txt_model);
%   try
%     jEditbox = jEdit.getViewport().getComponent(0);
%     jEditbox.setWrapping(false);                % turn off word-wrapping
%     jEditbox.setEditable(false);                % non-editable
%     set(jEdit,'HorizontalScrollBarPolicy',30);  % HORIZONTAL_SCROLLBAR_AS_NEEDED
%     % maintain horizontal scrollbar policy which reverts back on component resize
%     hjEdit = handle(jEdit,'CallbackProperties');
%     set(hjEdit, 'ComponentResizedCallback','set(gcbo,''HorizontalScrollBarPolicy'',30)')
%   end

% ####################################################################
% set up global controls (i.e., always present in main figure in all views)
uicontrol('parent',handles.fig_main,'style','text','string','DynaSim Model Designer','fontsize',16,'units','normalized','position',[0 .95 .25 .04],'backgroundcolor',bgcolor,'FontWeight','bold','ForegroundColor',[0 0 0]);
uicontrol('parent',handles.fig_main,'style','pushbutton','units','normalized','position',[.35 .95 .15 .05],'string','SAVE MODEL','backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor,'ForegroundColor',cfg.ButtonFontColor,'FontWeight','bold','callback',@SaveModel,'FontSize',14);
uicontrol('parent',handles.fig_main,'style','pushbutton','units','normalized','position',[.3 .97 .04 .03],'string','undo','backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor,'FontWeight','bold','callback',@undo,'visible','on');
bhistory=uicontrol('parent',handles.fig_main,'style','pushbutton','units','normalized','position',[0 .01 .1 .03],'string','View history','backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor,'FontWeight','bold','callback',[],'Enable','off','Visible','off');
bversion=uicontrol('parent',handles.fig_main,'style','pushbutton','units','normalized','position',[.12 .01 .08 .03],'string','+ Version','backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor,'FontWeight','bold','callback',[],'Enable','off','Visible','off');
bsimstudy=uicontrol('parent',handles.fig_main,'style','pushbutton','units','normalized','position',[.4 .01 .09 .03],'string','NEW SWEEP','backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor,'FontWeight','bold','callback',@DrawStudyInfo,'Enable','on');
% ####################################################################

% Model Designer:
pcreate=uipanel('parent',handles.fig_main,'backgroundcolor',bgcolor,'title','','visible','on','units','normalized','position',[0 .05 .5 .9],'fontweight','normal');
bnet=uicontrol('parent',pcreate,'style','pushbutton','tag','tab2','units','normalized','position',[.21 .65 .22 .04],'string','connections','backgroundcolor',[1 1 1],'FontWeight','bold','callback','set(findobj(''tag'',''ptoggle2''),''visible'',''off''); set(findobj(''tag'',''tab2''),''backgroundcolor'',[1 1 1]); set(findobj(''userdata'',''pnet''),''visible'',''on''); set(gcbo,''backgroundcolor'',[.7 .7 .7]);');
bmech=uicontrol('parent',pcreate,'style','pushbutton','tag','tab2','units','normalized','position',[.43 .65 .22 .04],'string','mechanisms','backgroundcolor',[.7 .7 .7],'FontWeight','bold','callback','set(findobj(''tag'',''ptoggle2''),''visible'',''off''); set(findobj(''tag'',''tab2''),''backgroundcolor'',[1 1 1]); set(findobj(''userdata'',''pmech''),''visible'',''on''); set(gcbo,''backgroundcolor'',[.7 .7 .7]);');
bcell=uicontrol('visible','off','parent',pcreate,'style','pushbutton','tag','tab2','units','normalized','position',[.65 .65 .22 .04],'string','parameters','backgroundcolor',[1 1 1],'FontWeight','bold','callback','set(findobj(''tag'',''ptoggle2''),''visible'',''off''); set(findobj(''tag'',''tab2''),''backgroundcolor'',[1 1 1]); set(findobj(''userdata'',''pcell''),''visible'',''on''); set(gcbo,''backgroundcolor'',[.7 .7 .7]);');
pmech=uipanel('parent',pcreate,'backgroundcolor',bgcolor,'title','Mechanism Editor','visible','on','tag','ptoggle2','userdata','pmech','units','normalized','position',[0 0 1 .65],'fontweight','bold');
pnet=uipanel('parent',pcreate,'backgroundcolor',bgcolor,'title','connection mechanism lists','visible','off','tag','ptoggle2','userdata','pnet','units','normalized','position',[0 0 1 .65]);
pcell=uipanel('parent',pcreate,'backgroundcolor',bgcolor,'title','parameters','visible','off','tag','ptoggle2','userdata','pcell','units','normalized','position',[0 0 1 .65]);
% Model Designer: connections
handles.p_pop_spec  = uipanel('parent',pcreate,'BackgroundColor',bgcolor,'Position',[0 .7 1 .29],'BorderWidth',.2,'BorderType','line'); % cell morphology
handles.p_net_connect = uipanel('parent',pnet,'BackgroundColor',bgcolor,'Position',[0 .6 1 .4],'BorderWidth',.2,'BorderType','line','title','','fontweight','normal'); % cell specification
p_net_kernel  = uipanel('parent',pnet,'BackgroundColor',bgcolor,'Position',[0 0 1 .6],'BorderWidth',.2,'BorderType','line','title','view and edit connectivity matrices'); % cell specification
% Model Designer: population controls
handles.list_pops = uicontrol('parent',handles.p_pop_spec,'units','normalized','style','listbox','position',[0 0 .2 .9],'value',1:length(pop_names),'string',pop_names,'BackgroundColor',[.9 .9 .9],'Max',5,'Min',0,'Callback',@UpdatePopSelection,'ButtonDownFcn',@RenamePopulation,'TooltipString','Right-click to edit node name','FontName',cfg.ModelFontName);
% Model Designer: headers for cell info
uicontrol('parent',handles.p_pop_spec,'tag','nodecontrols','BackgroundColor',bgcolor,'units','normalized','style','text','position',[0 .91 .25 .09],'string','populations','ListboxTop',0,'HorizontalAlignment','left','fontsize',10,'fontweight','bold');
uicontrol('parent',handles.p_pop_spec,'tag','nodecontrols','BackgroundColor',bgcolor,'units','normalized','style','text','position',[.25 .91 .06 .09],'string','size','ListboxTop',0,'HorizontalAlignment','left','fontsize',10,'fontweight','normal');
uicontrol('parent',handles.p_pop_spec,'tag','nodecontrols','BackgroundColor',bgcolor,'units','normalized','style','text','position',[.7 .91 .29 .09],'string','intrinsic mechanism lists','ListboxTop',0,'HorizontalAlignment','left','fontsize',10,'fontweight','normal');
uicontrol('parent',handles.p_pop_spec,'tag','nodecontrols','BackgroundColor',bgcolor,'units','normalized','style','text','position',[.31 .91 .25 .09],'string','master equations','ListboxTop',0,'HorizontalAlignment','left','fontsize',10,'fontweight','normal');

% Model Designer: Mechanism Editor
% edit box with mech info
handles.list_mechs = uicontrol('units','normalized','position',[0 .42 .2 .58],'parent',pmech,'BackgroundColor',[.9 .9 .9],'style','listbox','value',1,'string',active_mechanism_list,'Max',1,'Callback',@UpdateMechanismEditor,'ButtonDownFcn',@RenameMechanism,'TooltipString','Right-click to edit mechanism name','FontName',cfg.ModelFontName);
handles.edit_mech_eqns = uicontrol('parent',pmech,'style','edit','units','normalized','BackgroundColor','w','callback',@UpdateMechanismEditor,'position',[.2 .42 .8 .58],'string',active_mechanism_text,'userdata',active_mechanism_userdata,'FontName',cfg.ModelFontName,'FontSize',12,'HorizontalAlignment','Left','Max',100);
% mech plots associated w/ this compartment
p_static_plots = uipanel('parent',pmech,'Position',[0 0 1 .4],'BackgroundColor','white','BorderWidth',.2,'BorderType','line','title','');
handles.list_functions = uicontrol('units','normalized','position',[0 0 .2 .95],'parent',p_static_plots,'BackgroundColor',[.9 .9 .9],'style','listbox','value',1:5,'string',{},'Max',50,'Callback',@UpdateMechanismFunctions,'FontName',cfg.ModelFontName);
handles.ax_static_plot = subplot('position',[.23 .1 .75 .78],'parent',p_static_plots,'linewidth',3,'color','w','fontsize',6); box on;
title('functions of one variable');
edit_static_lims=uicontrol('Style','edit', 'Units','normalized','Position',[0.9 0.1 0.1 0.1],'backgroundcolor','w',...
          'String',sprintf('[%g,%g]',min(cfg.V),max(cfg.V)),'Callback',{@DrawAuxFunctions,1},'parent',p_static_plots);
btn_static_autoscale=uicontrol('style','pushbutton','fontsize',10,'string','autoscale','parent',p_static_plots,'backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor,...
          'Units','normalized','Position',[0.9 0 0.1 0.1],'callback',@AutoscaleMechPlot);
uicontrol('style','text','parent',p_static_plots,'Units','normalized','Position',[0.88 .12 0.02 0.075],'string','x','backgroundcolor','w');
uicontrol('style','text','parent',p_static_plots,'Units','normalized','Position',[0.88 .02 0.02 0.075],'string','y','backgroundcolor','w');
% set up function list for active mechanism
set(handles.list_functions,'string',{});
set(handles.list_functions,'value',[]);

UpdatePopControls;
UpdateConControls;
UpdateMechanismEditor;
InitializeSimView;
UpdateSimView;
UpdateEqnView;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function UpdatePopControls(src,evnt)
% purpose: populate population controls
global handles SPEC cfg
c=1.5; dy=-.07*c; ht=.1;
sel = get(handles.list_pops,'value');
l={SPEC.populations(sel).name};
N=[SPEC.populations(sel).size];
mechs={SPEC.populations(sel).mechanism_list};
for i=1:length(sel)
  m=mechs{i};
  if isempty(m)
    str='';
  else
    [~,str]=fileparts(m{1});
    for j=2:length(m)
      [~,name]=fileparts(m{j});
      str=[str ', ' name];
    end
  end
  if ~isempty(SPEC.populations(sel(i)).equations)
    if iscell(SPEC.populations(sel(i)).equations)
      pop_equations=[SPEC.populations(sel(i)).equations{:}];
    else
      pop_equations=SPEC.populations(sel(i)).equations;
    end
  else
    pop_equations='';
  end
  userdata.index=sel(i);
  size_callback='global SPEC LASTSPEC; LASTSPEC=SPEC; u=get(gcbo,''userdata''); SPEC.populations(u.index).size=str2num(get(gcbo,''string''));';
  eqn_callback='global SPEC LASTSPEC; LASTSPEC=SPEC; u=get(gcbo,''userdata''); SPEC.populations(u.index).equations=get(gcbo,''string'');';
  %mechlist_callback='global SPEC LASTSPEC; LASTSPEC=SPEC; u=get(gcbo,''userdata''); SPEC.populations(u.index).mechanism_list=strtrim(regexp(get(gcbo,''string''),'','',''split'',''once''));';
  mechlist_callback='global SPEC LASTSPEC; LASTSPEC=SPEC; u=get(gcbo,''userdata''); tmp=strtrim(regexp(get(gcbo,''string''),'','',''split'',''once'')); if isempty(tmp{1}), tmp=[]; end; SPEC.populations(u.index).mechanism_list=tmp;';
  if ~isfield(handles,'btn_pop_delete') || length(handles.btn_pop_delete)<length(sel) || ~ishandle(handles.btn_pop_delete(i))
    handles.btn_pop_delete(i) = uicontrol('parent',handles.p_pop_spec,'units','normalized',...
      'style','pushbutton','fontsize',10,'string','-','callback',{@RemovePopulation,sel(i)},...
      'position',[.205 .8+dy*(i-1) .03 ht],'TooltipString',l{i});
    handles.edit_pop_size(i) = uicontrol('parent',handles.p_pop_spec,'units','normalized','userdata',userdata,...
      'style','edit','position',[.24 .8+dy*(i-1) .06 ht],'backgroundcolor','w','string',N(i),'FontName',cfg.ModelFontName,...
      'HorizontalAlignment','left','Callback',{@UpdateModel,size_callback},'TooltipString',l{i});
    handles.edit_pop_equations(i) = uicontrol('parent',handles.p_pop_spec,'units','normalized','userdata',userdata,...
      'style','edit','position',[.3 .8+dy*(i-1) .4 ht],'backgroundcolor','w','string',pop_equations,'FontName',cfg.ModelFontName,...
      'HorizontalAlignment','left','Callback',{@UpdateModel,eqn_callback},...
      'ButtonDownFcn',{@UpdateModel,eqn_callback},'fontsize',9,'TooltipString',l{i});
    handles.edit_pop_mechlist(i) = uicontrol('parent',handles.p_pop_spec,'units','normalized','userdata',userdata,...
      'style','edit','position',[.7 .8+dy*(i-1) .26 ht],'backgroundcolor','w','string',str,'FontName',cfg.ModelFontName,...
      'HorizontalAlignment','left','Callback',{@UpdateModel,mechlist_callback},...
      'ButtonDownFcn',{@UpdateModel,mechlist_callback},'fontsize',9,'TooltipString',l{i});
    handles.btn_pop_copy(i) = uicontrol('parent',handles.p_pop_spec,'units','normalized',...
      'style','pushbutton','fontsize',10,'string','+','callback',{@AddPopulation,sel(i)},...
      'position',[.965 .8+dy*(i-1) .03 ht],'TooltipString',l{i});
  else
    % update properties
    set(handles.edit_pop_equations(i),'string',pop_equations,'visible','on','Callback',{@UpdateModel,eqn_callback},'TooltipString',l{i});
    set(handles.edit_pop_size(i),'string',N(i),'visible','on','Callback',{@UpdateModel,size_callback},'TooltipString',l{i});
    set(handles.edit_pop_mechlist(i),'string',str,'visible','on','Callback',{@UpdateModel,mechlist_callback},'TooltipString',l{i});
    set(handles.btn_pop_copy(i),'callback',{@AddPopulation,sel(i)},'visible','on','TooltipString',l{i});
    set(handles.btn_pop_delete(i),'callback',{@RemovePopulation,sel(i)},'visible','on','TooltipString',l{i});
  end
  if length(handles.btn_pop_delete)>i
    set(handles.edit_pop_equations(i+1:end),'visible','off');
    set(handles.edit_pop_size(i+1:end),'visible','off');
    set(handles.edit_pop_mechlist(i+1:end),'visible','off');
    set(handles.btn_pop_copy(i+1:end),'visible','off');
    set(handles.btn_pop_delete(i+1:end),'visible','off');
  end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function UpdateMechanismEditor(src,evnt)
% purpose: populate mechanism list and edit box controls
global handles SPEC

% create list of mechanisms for editor and associated userdata
fields={'populations','connections'};
userdata=[]; mech_names={}; cnt=1;
for f=1:length(fields)
  object=fields{f};
  for i=1:length(SPEC.(object))
    for j=1:length(SPEC.(object)(i).mechanism_list)
      userdata(cnt).object_type=object;
      userdata(cnt).object_index=i;
      [~,mech_name]=fileparts(SPEC.(object)(i).mechanism_list{j});
      userdata(cnt).mechanism_name=mech_name;
      userdata(cnt).mechanism_index=j;
      if strcmp(object,'populations')
        userdata(cnt).object_name=SPEC.(object)(i).name;
      elseif strcmp(object,'connections')
        userdata(cnt).object_name=[SPEC.(object)(i).source '->' SPEC.(object)(i).target];
      end
      userdata(cnt).mechanism_list_name=sprintf('%s.%s',userdata(cnt).object_name,userdata(cnt).mechanism_name);
      mech_names{cnt}=userdata(cnt).mechanism_list_name;
      % add empty mechanism info if not found (i.e., if not defined yet, as when a new mechanism is created)
      if ~isfield(SPEC.(object),'mechanisms') || isempty(SPEC.(object)(i).mechanisms) || ~ismember(mech_name,{SPEC.(object)(i).mechanisms.name})
        SPEC.(object)(i).mechanisms(end+1).name=mech_name;
        SPEC.(object)(i).mechanisms(end).equations='';
      end
      cnt=cnt+1;
    end
    % sort *.mechanisms wrt *.mechanism_list:
    %[~,~,sort_ind]=intersect(SPEC.(object)(i).mechanism_list,{SPEC.(object)(i).mechanisms.name});
    %SPEC.(object)(i).mechanisms=SPEC.(object)(i).mechanisms(sort_ind);
  end
end

% callback for mechanism edit box
edit_callback='global SPEC handles LASTSPEC; LASTSPEC=SPEC; u=get(handles.list_mechs,''userdata''); if ~isempty(u), u=u(get(handles.list_mechs,''value'')); eqns=get(gcbo,''string''); idx=cellfun(@isempty,regexp(eqns,'';$'')); eqns(idx)=cellfun(@(x)[x '';''],eqns(idx),''uni'',0); SPEC.(u.object_type)(u.object_index).mechanisms(u.mechanism_index).equations=[eqns{:}]; end';
% callback for mechanism selection listbox
% ... list_callback='';

% update mech lists and selection index
old_mech_index=get(handles.list_mechs,'value');
old_mech_list=get(handles.list_mechs,'string');
new_mech_index=old_mech_index;
new_mech_list=mech_names;
set(handles.list_mechs,'string',new_mech_list,'userdata',userdata);

% extract mechanism details for the select mechanism
if ~isempty(userdata)
  u=userdata(new_mech_index);
  if ~isempty(u)
    eqns=SPEC.(u.object_type)(u.object_index).mechanisms(u.mechanism_index).equations;
    % add line breaks
    eqns=strtrim(regexp(eqns,';','split'));
  else
    eqns='';
  end
  % update mechanism equations in edit box
  set(handles.edit_mech_eqns,'string',eqns,'Callback',{@UpdateModel,edit_callback});

  % update auxiliary plots of functions of one variable
  UpdateMechanismFunctions;
else
  set(handles.edit_mech_eqns,'Callback',{@UpdateModel,edit_callback});
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function UpdateMechanismFunctions(src,evnt,limflag)
% purpose: plot single-variable functions of select mechanism
global handles cfg

% get list of single-variable functions in this mechanism
% all equations for the active mechanism
eqns=get(handles.edit_mech_eqns,'string');
  % userdata=get(handles.edit_mech_eqns,'userdata');
  % u=userdata(get(handles.list_mechs,'value'));
  % eqns=SPEC.(u.object_type)(u.object_index).mechanisms(u.mechanism_index).equations;
% split equations into cell array of strings listing separate equations
idx=cellfun(@isempty,regexp(eqns,';$')); % lines that need semicolons
eqns(idx)=cellfun(@(x)[x ';'],eqns(idx),'uni',0);
eqns=[eqns{:}];
eqns=regexp(eqns,';','split');
eqns=eqns(cellfun(@ischar,eqns)&(~cellfun(@isempty,eqns)));

% find single-variable functions
idx=(~cellfun(@isempty,regexp(eqns,'^\w+\([a-zA-Z]\w*\)\s*=')));
if ~any(idx)
  set(handles.list_functions,'string',{});
  set(handles.list_functions,'value',[]);
  return;
end
functions=eqns(idx);
LHS=regexp(functions,'^(\w+)\(','tokens','once');
LHS=[LHS{:}];
RHS=regexp(functions,'^\w+(\(.+)','tokens','once');
RHS=strrep([RHS{:}],'=','');
% update function list
set(handles.list_functions,'string',functions);
% clear axes
if isfield(handles,'static_traces')
  axislimits=[get(handles.ax_static_plot,'xlim') get(handles.ax_static_plot,'ylim')];
  try delete(handles.static_traces); end
  handles=rmfield(handles,'static_traces');
  cla(handles.ax_static_plot);
else
  axislimits='tight';
end
% evaluate function handles and plot curves
X=cfg.V; cnt=1;
for i=1:length(LHS)
  try % todo: support functions with parameters and embedded functions
    eval(sprintf('%s=@%s;',LHS{i},RHS{i}));
    eval(sprintf('Y=%s(X);',LHS{i}));
    warning('off','MATLAB:hg:EraseModeIgnored');
    handles.static_traces(cnt)=line('parent',handles.ax_static_plot,'color',cfg.linecolors(max(1,mod(i,length(cfg.linecolors)))),...
      'LineStyle',cfg.linetype{max(1,mod(i,length(cfg.linetype)))},'xdata',X,'ydata',Y,'zdata',[]);
%     handles.static_traces(cnt)=line('parent',handles.ax_static_plot,'color',cfg.linecolors(max(1,mod(i,length(cfg.linecolors)))),...
%       'LineStyle',cfg.linetype{max(1,mod(i,length(cfg.linetype)))},'erase','background','xdata',X,'ydata',Y,'zdata',[]);
    cnt=cnt+1;
  end
end
% add legend
if isfield(handles,'static_traces') && ~isempty(handles.static_traces)
  h=legend(handles.ax_static_plot,LHS); set(h,'fontsize',6,'location','EastOutside');
  if strcmp(axislimits,'tight')
    axes(handles.ax_static_plot); axis(axislimits);
  else
    set(handles.ax_static_plot,'xlim',axislimits(1:2),'ylim',axislimits(3:4));
  end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function UpdateConControls(src,evnt)
% purpose: populate connection controls
global SPEC handles cfg

if isempty(SPEC.connections)
  sources={};
  targets={};
else
  sources={SPEC.connections.source};
  targets={SPEC.connections.target};
end

dx=.15; x=.13; c=1.5; dy=-.1*c; ht=.14;
sel_pop_inds = get(handles.list_pops,'value');
num_sel_pops = length(sel_pop_inds);
pop_names={SPEC.populations(sel_pop_inds).name};

for i=1:num_sel_pops % targets
  for j=1:num_sel_pops % sources
    % prepare string listing mechanisms connecting source to target
    mech_names = '';
    con_index = find(ismember(sources,pop_names{j}) & ismember(targets,pop_names{i}));
    if ~isempty(con_index)
      for k=1:length(SPEC.connections(con_index).mechanism_list)
        [~,mech_name]=fileparts(SPEC.connections(con_index).mechanism_list{k});
        mech_names = [mech_names mech_name ', '];
      end
      mech_names=mech_names(1:end-2);
    end
    % prepare control location and metadata
    pos = [x+dx*(i-1) .8+dy*(j-1) .9*dx ht];
    userdata=[];
    userdata.source=pop_names{j};
    userdata.target=pop_names{i};
    userdata.index=con_index;
    con_name = [userdata.source '->' userdata.target];
    % create/update controls
    arrow_right_code=8658; % 8658, 8594, 9032
    mechlist_callback='global SPEC LASTSPEC; LASTSPEC=SPEC; u=get(gcbo,''userdata''); if isempty(u.index),u.index=length(SPEC.connections)+1; end; SPEC.connections(u.index).mechanism_list=strtrim(regexp(get(gcbo,''string''),'','',''split'',''once'')); SPEC.connections(u.index).source=u.source; SPEC.connections(u.index).target=u.target;';
    if ~isfield(handles,'txt_to') || i>length(handles.txt_from) || j>length(handles.txt_to) || ~ishandle(handles.edit_con_mechlist(i,j)) || handles.edit_con_mechlist(i,j)==0
      if i==1 % to
        this=zeros(max(sel_pop_inds),1);
        this(sel_pop_inds)=j;
        handles.txt_to(j) = uicontrol('parent',handles.p_net_connect,'units','normalized',...
          'style','text','position',[x+dx*(j-1) .88 .11 ht],'string',[char(arrow_right_code) ' ' pop_names{j}],...
          'callback',{@ShowClickMechList,this,'connections'},'backgroundcolor',cfg.BackgroundColor);
      end
      if j==1 % from
        this=ones(1,max(sel_pop_inds));
        this(sel_pop_inds)=i;
        handles.txt_from(i) = uicontrol('parent',handles.p_net_connect,'units','normalized',...
          'style','text','position',[.01 .8+dy*(i-1) .11 ht],'string',[pop_names{i} ' ' char(arrow_right_code)],...
          'callback',{@ShowClickMechList,this,'connections'},'backgroundcolor',cfg.BackgroundColor);
      end
      handles.edit_con_mechlist(i,j) = uicontrol('parent',handles.p_net_connect,'units','normalized',...
        'style','edit','position',pos,'backgroundcolor','w','userdata',userdata,...
        'string',mech_names,'HorizontalAlignment','left','userdata',userdata);
        set(handles.edit_con_mechlist(i,j),'Callback',{@UpdateModel,mechlist_callback},...
        'ButtonDownFcn',@RenameMechanism);
      handles.p_conn_mechs(i,j) = uipanel('parent',handles.p_net_connect,'units','normalized',...
      'position',pos,'visible','off');
    else
      set(handles.txt_to(i),'string',['--> ' pop_names{i}],'visible','on');
      set(handles.txt_from(i),'string',[pop_names{i} ' -->'],'visible','on');
      set(handles.p_conn_mechs(i,j),'visible','off');
      set(handles.edit_con_mechlist(i,j),'string',mech_names,'userdata',userdata,'Callback',{@UpdateModel,mechlist_callback},...
        'ButtonDownFcn',@RenameMechanism,'visible','on');
    end
  end
end

if isfield(handles,'txt_to') && length(handles.txt_to)>length(sel_pop_inds)
  set(handles.txt_to(i+1:end),'visible','off');
  set(handles.txt_from(i+1:end),'visible','off');
  set(handles.edit_con_mechlist(i+1:end,:),'visible','off');
  set(handles.edit_con_mechlist(:,i+1:end),'visible','off');
  set(handles.p_conn_mechs(i+1:end,:),'visible','off');
  set(handles.p_conn_mechs(:,i+1:end),'visible','off');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Update Model
function UpdateModel(src,evnt,aux_callback)
% Purpose: update DynaSim model after modifing the DynaSim specification
% evaluate caller-specific auxiliary commands
if nargin>=3
  eval(aux_callback);
end

global SPEC MODEL cfg LASTCFG handles

% check for special case where full model is inserted into mechanism editor
if nargin>0 && isequal(src,handles.edit_mech_eqns)
  if isempty(SPEC.populations(1).equations) && isempty(SPEC.populations(1).mechanisms) && isempty(SPEC.populations(1).mechanism_list)
    % prepare equation string
    eqns=get(gcbo,'string');
    if ischar(eqns)
      eqns=cellstr(eqns);
    end
    idx=cellfun(@isempty,regexp(eqns,';$'));
    eqns(idx)=cellfun(@(x)[x ';'],eqns(idx),'uni',0);
    % add default name to mechanism
    SPEC.populations.mechanisms.name='l';
    SPEC.populations.mechanisms.equations=[eqns{:}];
    SPEC.populations(1).mechanism_list={'l'};
  end
end

% Execute common update operations
% check specification
SPEC=dsCheckSpecification(SPEC);
[SPEC.populations.parameters]=deal([]);
if ~isempty(SPEC.connections)
  [SPEC.connections.parameters]=deal([]);
end
% generate model
MODEL=dsGenerateModel(SPEC);
% extract model ODEFUN and IC
try
  [ODEFUN,IC,elem_names]=dsDynasim2odefun(dsPropagateParameters(MODEL));
  % txt=dsExtractModelStrings(MODEL,'model',0);
  % ODEFUN=txt{1}; IC=txt{2}; elem_names=txt{3};

  % update model config for running simulation
  LASTCFG=cfg;
  if length(IC)~=length(cfg.IC)
    cfg.Y=zeros(cfg.ntime,length(IC));
  end
  cfg.ODEFUN=ODEFUN;
  cfg.IC=IC;
  cfg.elem_names=elem_names;
end

% update views
UpdateViews('model');
UpdateViews('selection');
UpdateMechanismEditor;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Update GUI based on listbox selections in Model Designer
function UpdatePopSelection(src,evnt)
% update designer controls:
UpdatePopControls;
UpdateConControls;
UpdateMechanismEditor;

% update sim view
UpdateViews('selection');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Update Views
function UpdateViews(update_what)
global handles
% Purpose: update equation and simulation views
switch (update_what)
  case 'model'
    %if strcmp('on',get(handles.peqnview,'visible'))
      UpdateEqnView;
    %end
  case 'selection'
    if strcmp('on',get(handles.psimview,'visible'))
      UpdateSimView;
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% EQUATION VIEW
function UpdateEqnView(src,evnt)
global MODEL cfg handles

% get equation string to display:
txt=dsExtractModelStrings(MODEL,'model',0);

% construct display text
txt{end+1}='';
txt{end+1}='% ##############################################################';
txt{end+1}='% Prepare ODEFUN for use with built-in Matlab solvers:';
txt{end+1}='% ##############################################################';
txt{end+1}=sprintf('ODEFUN = %s;',char(cfg.ODEFUN));
txt{end+1}=sprintf('IC = [%s];',num2str(cfg.IC'));
legs={};
for i=1:length(cfg.elem_names), legs{end+1}=['''' cfg.elem_names{i} ''',']; end
legs=[legs{:}];
txt{end+1}=sprintf('elem_names = {%s};',legs(1:end-1));
txt{end+1}='';
txt{end+1}='% Solve system using built-in Matlab solver:';
txt{end+1}='options=odeset(''RelTol'',1e-2,''AbsTol'',1e-4,''InitialStep'',.01);';
txt{end+1}='[t,y]=ode23(ODEFUN,[0 100],IC,options);';
txt{end+1}='figure; plot(t,y);';
txt{end+1}=sprintf('legend(%s,''Location'',''EastOutside'');',strrep(legs(1:end-1),'_','\_'));
cfg.model_text=txt;
% update equation view
set(handles.txt_model,'string',cfg.model_text);

% todo: append other display mode options ('specification','xpp')

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SIMULATION VIEW
function InitializeSimView(src,evnt)
global handles cfg

% initialize for max_num_plots rows (with 'visible','off'):
% panel 'p_sim_plots'
% - list_vars
% - list_cells
% - axes_data_trace
% - axes_data_image
% - axes_data_hist
% - edit_ymax
% - edit_ymin

% initialize simulation controls:
% - QuickSim (panel 'p_quicksim_settings'): button and edit boxes for t0,tf
% - Running sims (panel 'p_runsim_settings'):
%   - run control buttons (Start, Reset)
%   - edit boxes for numerics (dt, ntime)
%   - ylim maxmin buttons (by_pop, across_pops)

% plot options
dy=-1/cfg.max_num_plots;
yp=1-1/cfg.max_num_plots+.04; % .7
default_visible='on';

% Create panels (parent: psimview):
% p_sim_plots
handles.p_sim_plots=uipanel('parent',handles.psimview,'units','normalized','position',[0 .15 1 .85],'backgroundcolor','w','title','','visible','on');

% p_quicksim_settings
handles.p_quicksim_settings=uipanel('parent',handles.psimview,'units','normalized','position',[0 0 .25 .15],'backgroundcolor','w','title','','visible','on');

% p_runsim_settings
handles.p_runsim_settings=uipanel('parent',handles.psimview,'units','normalized','position',[.25 0 .75 .15],'backgroundcolor','w','title','','visible','on');


% Create controls for interactive plotting (parent: p_sim_plots)
for i=1:cfg.max_num_plots
  % list_vars: listbox for pop-specific variables
  handles.list_vars(i)=uicontrol('units','normalized','position',[.01 yp+(i-1)*dy .14 -.8*dy],'parent',handles.p_sim_plots,'BackgroundColor',[.9 .9 .9],'style','listbox','value',1,'string',[],'Max',100,'Callback',@UpdateSimView,'TooltipString','Left-click to select variable to plot','visible',default_visible);
  
  % list_cells: listbox for pop-specific cell indices
  handles.list_cells(i)=uicontrol('units','normalized','position',[.15 yp+(i-1)*dy .05 -.8*dy],'parent',handles.p_sim_plots,'BackgroundColor',[.9 .9 .9],'style','listbox','value',[],'string',[],'Max',1000,'Callback',@UpdateSimView,'TooltipString','Left-click to select cells to plot','visible',default_visible);
  
  % axes_data_image: axis for plotting images
  handles.axes_data_image(i)=subplot('position',[.23 yp+(i-1)*dy .72 -.8*dy],'parent',handles.p_sim_plots,'visible','off','tag','simview_image');
  handles.img_data(i) = imagesc(cfg.t,1:length(cfg.IC),cfg.Y); axis xy; %colorbar
  set(handles.img_data(i),'visible','off','tag','simview_image');
  
  % axes_data_trace: axis for plotting traces
  handles.axes_data_trace(i)=subplot('position',[.23 yp+(i-1)*dy .72 -.8*dy], 'parent',handles.p_sim_plots,'tag','simview_trace');
  
  % edit_ymax: max y-limits
  callback=sprintf('global handles; set(handles.axes_data_trace(%g),''ylim'',[str2double(get(handles.edit_ymin(%g),''string'')) str2double(get(gco,''string''))]); set(handles.axes_data_image(%g),''clim'',[str2double(get(handles.edit_ymin(%g),''string'')) str2double(get(gco,''string''))]); cfg.ymax(%g)=str2double(get(gco,''string''));',i,i,i,i,i);
  handles.edit_ymax(i)=uicontrol('style','edit','parent',handles.p_sim_plots,'tag','ymax','units','normalized','position',[.955 .95+dy*(i-1)-.01 .037 .03],'backgroundcolor','w','string',cfg.ymax(i),'HorizontalAlignment','left','Callback',callback,'fontsize',8);
  
  % edit_ymin: min y-limits
  callback=sprintf('global handles; set(handles.axes_data_trace(%g),''ylim'',[str2double(get(gco,''string'')) str2double(get(handles.edit_ymax(%g),''string''))]); set(handles.axes_data_image(%g),''clim'',[str2double(get(gco,''string'')) str2double(get(handles.edit_ymax(%g),''string''))]); cfg.ymin(%g)=str2double(get(gco,''string''));',i,i,i,i,i);
  handles.edit_ymin(i)=uicontrol('style','edit','parent',handles.p_sim_plots,'tag','ymin','units','normalized','position',[.955 .95+dy*(i-1)+.8*dy+.03-.01 .037 .03],'backgroundcolor','w','string',cfg.ymin(i),'HorizontalAlignment','left','Callback',callback,'fontsize',8);
  handles.btn_sim_autoscale(i)=uicontrol('style','pushbutton','parent',handles.p_sim_plots,'units','normalized','position',[.96 .95+dy*(i-1)+.8*dy/2 .027 .05],'fontsize',12,'fontweight','bold','fontname','Blue Highway','String',char(cfg.autoscale_charcode),'callback',{@AutoscaleSimPlot,i},'visible',default_visible);%,'backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor);
  
  % ref: how to display pic on button: https://www.mathworks.com/matlabcentral/newsreader/view_thread/51230
    % for charcode=1:10000,fprintf('%g: %s\n',charcode,char(charcode)); end
    % up/down arrows: 5864, 8597, 8645, 8661
    % arrow right: 8594, 8658, 9032
    % arrow left: 8592, 8656, 9031
    % 8592-8703: ARROWS
    % 991: lightning bolt
    % 9398: encircled "A"
    % 9786: smiley face
    % 9733: 5-point star
%   pic_arrow=imread('/home/jason/code/dynasim/functions/arrow_up_down.png');
%   pic_arrow=1-ind2rgb(pic_arrow,gray);
%   handles.btn_sim_autoscale(i)=uicontrol('style','pushbutton','parent',handles.p_sim_plots,'units','normalized','position',[.955 .95+dy*(i-1)+.8*dy/2 .037 .08],'fontsize',12,'fontweight','bold','Cdata',pic_arrow,'callback',@QuickSim);%,'backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor);
end

handles.line_data=[];
% Create controls with default settings for QuickSim (parent: p_quicksim_settings)
% btn_quicksim
handles.btn_quicksim=uicontrol('style','pushbutton','parent',handles.p_quicksim_settings,'units','normalized','position',[.15 .55 .7 .3],'fontsize',12,'fontweight','bold','string','QuickSim','callback',@QuickSim,'backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor);

% edit_t0
handles.edit_t0 = uicontrol('style','edit','parent',handles.p_quicksim_settings,'units','normalized','position',[.15 .15 .3 .3],'fontsize',12,'string','0','HorizontalAlignment','left','backgroundcolor','w','callback','global cfg; cfg.t0=str2num(get(gcbo,''string''));');
uicontrol('style','text','parent',handles.p_quicksim_settings,'units','normalized','position',[.05 .1 .1 .3],'fontsize',10,'string','t0','HorizontalAlignment','center','backgroundcolor','w','callback','global cfg; cfg.dt=str2num(get(gcbo,''string''));');

% edit_tf
handles.edit_tf = uicontrol('style','edit','parent',handles.p_quicksim_settings,'units','normalized','position',[.55 .15 .3 .3],'fontsize',12,'string','200','HorizontalAlignment','left','backgroundcolor','w','callback','global cfg; cfg.tf=str2num(get(gcbo,''string''));');
uicontrol('style','text','parent',handles.p_quicksim_settings,'units','normalized','position',[.45 .1 .1 .3],'fontsize',10,'string','tf','HorizontalAlignment','center','backgroundcolor','w','callback','global cfg; cfg.dt=str2num(get(gcbo,''string''));');
handles.check_compile = uicontrol('style','checkbox','parent',handles.p_quicksim_settings,'units','normalized','position',[.15 .05 .85 .1],'string','compile','value',0,'backgroundcolor','w');


% Create controls with default settings for running sims (parent: p_runsim_settings)
% btn_start (string: start/pause/resume)
handles.btn_start=uicontrol('style','pushbutton','parent',handles.p_runsim_settings,'units','normalized','position',[.05 .55 .15 .3],'fontsize',12,'fontweight','bold','string','Start','callback',@RunSim,'backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor);
% btn_pause
handles.btn_pause=uicontrol('style','pushbutton','parent',handles.p_runsim_settings,'units','normalized','position',[.05 .55 .15 .3],'fontsize',12,'fontweight','bold','string','Pause','callback','global cfg; cfg.sim_paused=-cfg.sim_paused;','visible','off','backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor);
% btn_stop
handles.btn_stop=uicontrol('style','pushbutton','parent',handles.p_runsim_settings,'units','normalized','position',[.05 .15 .15 .3],'fontsize',12,'fontweight','bold','string','Stop','callback','global cfg; cfg.sim_stopped=1; cfg.sim_paused=-1;','visible','off','backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor);
% edit_dt
handles.edit_dt = uicontrol('style','edit','parent',handles.p_runsim_settings,'units','normalized','position',[.23 .55 .07 .3],'fontsize',12,'string','.01','HorizontalAlignment','left','backgroundcolor','w','callback','global cfg; cfg.dt=str2num(get(gcbo,''string''));');
uicontrol('style','text','parent',handles.p_runsim_settings,'units','normalized','position',[.23 .4 .07 .15],'fontsize',10,'string','dt','HorizontalAlignment','center','backgroundcolor','w','callback','global cfg; cfg.dt=str2num(get(gcbo,''string''));');
% edit_ntime
handles.edit_ntime = uicontrol('style','edit','parent',handles.p_runsim_settings,'units','normalized','position',[.32 .55 .15 .3],'fontsize',12,'string','20001','HorizontalAlignment','left','backgroundcolor','w','callback','global cfg; cfg.ntime=str2num(get(gcbo,''string''));');
uicontrol('style','text','parent',handles.p_runsim_settings,'units','normalized','position',[.32 .4 .15 .15],'fontsize',10,'string','# times','HorizontalAlignment','center','backgroundcolor','w','callback','global cfg; cfg.dt=str2num(get(gcbo,''string''));');
% btn_auto_ylim_by_pop
handles.btn_auto_ylim_by_pop=uicontrol('style','pushbutton','parent',handles.p_runsim_settings,'units','normalized','position',[.75 .55 .23 .3],'fontsize',10,'fontweight','bold','string','auto_by_pop','callback',@UpdateSimView,'visible','off','backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor);
% btn_auto_ylim_across_pops
handles.btn_auto_ylim_by_pop=uicontrol('style','pushbutton','parent',handles.p_runsim_settings,'units','normalized','position',[.75 .15 .23 .3],'fontsize',10,'fontweight','bold','string','auto_all_pops','callback',@UpdateSimView,'visible','off','backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor);
% edit_max_num_plots
% ...

% radio buttons to select kind of plot
handles.radio_plot_type=uibuttongroup('visible','off','SelectionChangeFcn',@UpdateSimView,'units','normalized','Position',[.55 .07 .15 .85],'parent',handles.p_runsim_settings,'backgroundcolor','w','title','Display');
handles.radio_plot_type_trace=uicontrol('style','radiobutton','string','trace','units','normalized','pos',[.1 .6 .9 .3],'userdata','simview_trace','parent',handles.radio_plot_type,'HandleVisibility','on','backgroundcolor','w');
handles.radio_plot_type_image=uicontrol('style','radiobutton','string','image','units','normalized','pos',[.1 .2 .9 .3],'userdata','simview_image','parent',handles.radio_plot_type,'HandleVisibility','on','backgroundcolor','w');
set(handles.radio_plot_type,'SelectedObject',handles.radio_plot_type_trace,'Visible','on');

% special plot options
% handles.check_compile  = uibuttongroup('visible','off','SelectionChangeFcn',@UpdateSimView,'units','normalized','Position',[.55 .07 .15 .85],'parent',handles.p_runsim_settings,'backgroundcolor','w','title','Display');
handles.check_zscore = uicontrol('style','checkbox','parent',handles.p_runsim_settings,'callback',@UpdateSimView,'units','normalized','position',[.75 .5 .2 .15],'string','zscore','value',0,'backgroundcolor','w');

% autoscale simview
uicontrol('style','pushbutton','parent',handles.p_runsim_settings,'units','normalized','Position',[.94 .35 .04 .3],'fontsize',12,'fontweight','bold','fontname','Blue Highway','String',char(cfg.autoscale_charcode),'callback',{@AutoscaleSimPlot,0},'visible','on');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function UpdateSimView(src,evnt)
% Purpose: update Sim View controls (except plotted data)

global handles cfg MODEL

% update plot type
if nargin>0 && isequal(src,handles.radio_plot_type)
  % hide all sim view plot objects
  Children=get(handles.radio_plot_type,'Children');
  for i=1:length(Children)
    set(findobj('tag',get(Children(i),'userdata')),'visible','off');
  end
  
  % show plot objects for the select type
  SelectedObject=get(handles.radio_plot_type,'SelectedObject');
  set(findobj('tag',get(SelectedObject,'userdata')),'visible','on');
end

% get selection info
pop_names=get(handles.list_pops,'string');
sel_pop_inds=get(handles.list_pops,'value');
sel_pop_names=pop_names(sel_pop_inds);
num_plots=min(length(sel_pop_inds),cfg.max_num_plots);
all_state_vars=MODEL.state_variables;
if ~isempty(MODEL.monitors)
  all_state_vars=cat(2,all_state_vars,fieldnames(MODEL.monitors)');
end


for plot_index=1:num_plots
  pop_name=sel_pop_names{plot_index};
  % get vars for this pop
  pat=['^' pop_name '_'];
  sel_state_vars=all_state_vars(~cellfun(@isempty,regexp(all_state_vars,pat,'once')));
  % get cell indices for this pop
  num_cells=MODEL.parameters.([pop_name '_Npop']);
  str_cell_inds=cellfun(@num2str,num2cell(1:num_cells),'uni',0);
  sel_cell_inds=1:length(str_cell_inds);
  % update controls
  set(handles.list_vars(plot_index),'string',sel_state_vars);
  set(handles.list_cells(plot_index),'string',str_cell_inds);
  val=get(handles.list_vars(plot_index),'value');
  if isempty(val) || max(val)>length(sel_state_vars)
    set(handles.list_vars(plot_index),'value',1);
  end
  val=get(handles.list_cells(plot_index),'value');
  if isempty(val) || max(val)>length(str_cell_inds)
    set(handles.list_cells(plot_index),'value',sel_cell_inds);
  end
end

% display all axes with select pops to plot
for plot_index=1:num_plots
  switch get(get(handles.radio_plot_type,'SelectedObject'),'String')
    case 'trace'
      set(handles.axes_data_trace(plot_index),'visible','on');
      if size(handles.line_data,1)>=plot_index
        ind=handles.line_data(plot_index,:)~=0;
        set(handles.line_data(plot_index,ind),'visible','on');
      end
    case 'image'
      set(handles.axes_data_image(plot_index),'visible','on');
      set(handles.img_data(plot_index),'visible','on');
  end
  set(handles.edit_ymax(plot_index),'visible','on');
  set(handles.edit_ymin(plot_index),'visible','on');
  set(handles.list_vars(plot_index),'visible','on');
  set(handles.list_cells(plot_index),'visible','on');
  set(handles.btn_sim_autoscale(plot_index),'visible','on');
end

% hide all available axes without select pops to plot
for plot_index=num_plots+1:cfg.max_num_plots
  set(handles.edit_ymax(plot_index),'visible','off');
  set(handles.edit_ymin(plot_index),'visible','off');
  set(handles.list_vars(plot_index),'visible','off');
  set(handles.list_cells(plot_index),'visible','off');
  set(handles.axes_data_trace(plot_index),'visible','off');
  if size(handles.line_data,1)>=plot_index
    ind=find(handles.line_data(plot_index,:)~=0);
    set(handles.line_data(plot_index,ind),'visible','off');
  end
  set(handles.axes_data_image(plot_index),'visible','off');
  set(handles.img_data(plot_index),'visible','off');
  set(handles.btn_sim_autoscale(plot_index),'visible','off');
end

% update plotted data
UpdateSimPlots;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function UpdateSimPlots(src,evnt)
% Purpose: update plotted data (from cfg.Y given listbox selections)
global handles cfg
sel_pop_inds=get(handles.list_pops,'value');
num_plots=min(length(sel_pop_inds),cfg.max_num_plots);

% what kind of plot? (trace, image)
plot_type=get(get(handles.radio_plot_type,'SelectedObject'),'String');

% loop over populations to plot
for plot_index=1:num_plots
  % select data to plot for this population
  plot_Y=SelectPlotData(plot_index);
  num_elems=size(plot_Y,2);
  % update plots for this population
  switch plot_type
    case 'trace'
      % loop over cells to update
      for line_index=1:num_elems
        % lines in handles.axes_data_trace(plot_index)
        if size(handles.line_data,1)>=plot_index && size(handles.line_data,2)>=line_index && ishandle(handles.line_data(plot_index,line_index)) && handles.line_data(plot_index,line_index)>0
          set(handles.line_data(plot_index,line_index),'ydata',plot_Y(:,line_index),'xdata',(0:cfg.ntime-1)*cfg.dt,'visible','on');
        else
          try
            warning('off','MATLAB:hg:EraseModeIgnored');
            handles.line_data(plot_index,line_index)=line('parent',handles.axes_data_trace(plot_index),'color',cfg.linecolors(max(1,mod(line_index,length(cfg.linecolors)))),'LineStyle',cfg.linetype{max(1,mod(line_index,length(cfg.linetype)))},'erase','background','xdata',(0:cfg.ntime-1)*cfg.dt,'ydata',plot_Y(:,line_index),'zdata',[],'tag','simview_trace');
          catch
            handles.line_data(plot_index,line_index)=line('parent',handles.axes_data_trace(plot_index),'color',cfg.linecolors(max(1,mod(line_index,length(cfg.linecolors)))),'LineStyle',cfg.linetype{max(1,mod(line_index,length(cfg.linetype)))},'xdata',(0:cfg.ntime-1)*cfg.dt,'ydata',plot_Y(:,line_index),'zdata',[],'tag','simview_trace');
          end
        end
      end
      ax=handles.axes_data_trace(plot_index);
      ylims=[cfg.ymin(plot_index) cfg.ymax(plot_index)];
      % hide other lines
      if size(handles.line_data,2)>num_elems
        ind=num_elems+1:size(handles.line_data,2);
        ind=ind(handles.line_data(plot_index,ind)~=0);
        set(handles.line_data(plot_index,ind),'visible','off');
      end
    case 'image'
      set(handles.img_data(plot_index),'cdata',plot_Y','ydata',1:num_elems,'xdata',(0:cfg.ntime-1)*cfg.dt);
      ax=handles.axes_data_image(plot_index);
      set(ax,'clim',[cfg.ymin(plot_index) cfg.ymax(plot_index)]);
      if num_elems>1, ylims=[.5 num_elems+.5]; else ylims=[.5 1.5]; end
  end
  % generic axis settings (todo: move to UpdateSimView?)
  % y-limits:
  if ylims(1)~=ylims(2)
    set(ax,'ylim',ylims);
  end
  % x-limits:
  set(ax,'xlim',[0 cfg.ntime*cfg.dt]);
  % xticks:
  set(handles.axes_data_trace(plot_index),'xticklabel',cfg.xticklabel,'xtick',cfg.xtick);
  set(handles.axes_data_image(plot_index),'xticklabel',cfg.xticklabel,'xtick',cfg.xtick);
% end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function AutoscaleSimPlot(src,evnt,plot_index)
global handles cfg

% get max/min
if plot_index==0
  % autoscale all plots based on max/min of current limits across individual plots
  datmax=-inf;
  datmin=inf;
  for i=1:length(handles.edit_ymin)
    if strcmp('on',get(handles.axes_data_trace(i),'visible'))
      plot_Y=SelectPlotData(i);
      datmax=max(datmax,max(plot_Y(:)));
      datmin=min(datmin,min(plot_Y(:)));
    end
  end
  plot_index=1:length(handles.edit_ymin);
  %datmax=max(str2double(get(handles.edit_ymin,'string')));
  %datmin=min(str2double(get(handles.edit_ymax,'string')));
else
  % autoscale individual plot based on max/min of current data in the plot
  plot_Y=SelectPlotData(plot_index);
  datmax=max(plot_Y(:));
  datmin=min(plot_Y(:));
end
% update plots
for i=1:length(plot_index)
  % what kind of plot? (trace, image)
  switch get(get(handles.radio_plot_type,'SelectedObject'),'String')
    case 'trace'
      % if phaseplot
        % scale xlim and ylim
      % else
        set(handles.axes_data_trace(plot_index(i)),'ylim',[datmin datmax]);
      % end
    case 'image'
      set(handles.axes_data_image(plot_index(i)),'clim',[datmin datmax]);
  end
  set(handles.edit_ymin(plot_index(i)),'string',datmin);
  set(handles.edit_ymax(plot_index(i)),'string',datmax);
  cfg.ymin(plot_index(i))=datmin;
  cfg.ymax(plot_index(i))=datmax;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function AutoscaleMechPlot(src,evnt)
global handles
ymin=inf;
ymax=-inf;
if isfield(handles,'static_traces')
  for i=1:length(handles.static_traces)
    ymin=min(ymin,min(get(handles.static_traces(i),'ydata')));
    ymax=max(ymax,max(get(handles.static_traces(i),'ydata')));
  end
  set(handles.ax_static_plot,'ylim',[ymin ymax]);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ZoomFunction(src,evnt)
global handles cfg
% get info on caller plot
if ismember(gco,handles.axes_data_trace)
  plot_index=find(gco==handles.axes_data_trace);
  hplot=handles.axes_data_trace(plot_index);
  LIM=[cfg.ymin(plot_index) cfg.ymax(plot_index)];
  property='ylim';
elseif ismember(gco,handles.img_data)
  disp('asdf')
  plot_index=find(gco==handles.img_data);
  hplot=handles.axes_data_image(plot_index);
  LIM=[cfg.ymin(plot_index) cfg.ymax(plot_index)];
  property='clim';
elseif isequal(gco,handles.ax_static_plot)
  hplot=handles.ax_static_plot;
  LIM=get(hplot,'ylim');
  property='ylim';
else
  LIM=[];
end
% get new limits
if isempty(LIM) || LIM(1)>=LIM(2) || any(isnan(LIM)) || any(isinf(LIM)), return; end
if evnt.VerticalScrollCount < 0           % zoom in
  if LIM(1)>0, LIM(1)=LIM(1)*1.5; else LIM(1)=LIM(1)/1.5; end
  if LIM(2)>0, LIM(2)=LIM(2)/1.5; else LIM(1)=LIM(1)*1.5; end
else                                      % zoom out
  if LIM(1)>0, LIM(1)=LIM(1)/1.5; else LIM(1)=LIM(1)*1.5; end
  if LIM(2)>0, LIM(2)=LIM(2)*1.5; else LIM(1)=LIM(1)/1.5; end
end
% update plots and cfg
set(hplot,property,LIM);
if ismember(gco,handles.axes_data_trace) || ismember(gco,handles.img_data)
  set(handles.edit_ymin(plot_index),'string',LIM(1));
  set(handles.edit_ymax(plot_index),'string',LIM(2));
  cfg.ymin(plot_index)=LIM(1);
  cfg.ymax(plot_index)=LIM(2);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function plot_Y=SelectPlotData(plot_index)
% purpose: select sim data (from cfg.Y) to plot
global cfg handles
% collect listbox info
all_var_names=get(handles.list_vars(plot_index),'string');
if ~isempty(all_var_names)
  sel_var_names=all_var_names(get(handles.list_vars(plot_index),'value'));
else
  sel_var_names=all_var_names;
end
sel_cell_inds=get(handles.list_cells(plot_index),'value');

% get list of elements for select variables in this pop
vind=find(ismember(cfg.elem_names,sel_var_names));
elem_names=cfg.elem_names(vind);

% get indices into Y (elem_names) for select cells in this pop
yind=[];
for i=1:length(sel_var_names)
  cind=find(strcmp(sel_var_names{i},elem_names)); % all indices to this var
  if max(sel_cell_inds)>length(cind)
    sel_cell_inds=sel_cell_inds(sel_cell_inds<=length(cind));
  end
  yind=[yind vind(cind(sel_cell_inds))];
end
plot_Y=cfg.Y(cfg.t_plot_indices,yind);
sel_elem_names=cfg.elem_names(yind);

% calculate zscore
if get(handles.check_zscore,'value')==1
  uniq_elem_names=unique(sel_elem_names);
  for i=1:length(uniq_elem_names)
    idx=ismember(sel_elem_names,uniq_elem_names{i});
    tmp=plot_Y(:,idx);
    plot_Y(:,idx)=(tmp-mean(tmp(:)))/std(tmp(:));
  end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function RunSim(src,evnt)
% Purpose: control ongoing simulation that updates cfg.Y and cfg.t
global cfg handles t
set(handles.btn_start,'visible','off');
set(handles.btn_pause,'visible','on');
set(handles.btn_stop,'visible','on');
set(handles.btn_quicksim,'Enable','off');
set(handles.btn_start,'Enable','off');
% Initialize data for simulation
% cfg.ntime=str2num(get(handles.edit_ntime,'string'));
cfg.Y=nan(cfg.ntime,length(cfg.IC));
cfg.Y(1,:)=cfg.IC;
cfg.t=(0:cfg.ntime-1)'*cfg.dt;
X=cfg.IC;
t=0;
t_pointer=2;
cfg.xtick=linspace(cfg.t(1),cfg.t(end),cfg.num_xticks);
cfg.xticklabel=linspace(cfg.t(1),cfg.t(end),cfg.num_xticks);

% Run simulation
while cfg.sim_stopped~=1
  % Pause simulation
  if cfg.sim_paused==1
    set(handles.btn_pause,'string','Resume');
    while cfg.sim_paused==1
      pause(.1);
      %drawnow
    end
    set(handles.btn_pause,'string','Pause');
  end
  % Integration
  t=t+cfg.dt;
  X=X+cfg.dt*cfg.ODEFUN(t,X);
  % store new state
  cfg.Y(t_pointer,:)=X;
  cfg.t=cfg.t+cfg.dt;
  % increment time index
  t_pointer=t_pointer+1;
  if t_pointer>cfg.ntime
    t_pointer=1;
  end
  % draw sim data
  if mod(t_pointer,cfg.num_steps_per_plot)==0
    % update indices to select chronological cfg.Y to plot
    if t<(cfg.ntime*cfg.dt)
      cfg.t_plot_indices=1:cfg.ntime;
    else
      cfg.xticklabel=cfg.xticklabel+cfg.dt*cfg.num_steps_per_plot;
      cfg.t_plot_indices=[t_pointer:cfg.ntime 1:t_pointer-1];
    end
    % update plots
    UpdateSimPlots;
    drawnow;
  end
end
cfg.sim_stopped=0;
set(handles.btn_start,'visible','on');
set(handles.btn_pause,'visible','off');
set(handles.btn_stop,'visible','off');
set(handles.btn_quicksim,'Enable','on');
set(handles.btn_start,'Enable','on');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function QuickSim(src,evnt)
% purpose: quick simulation using dsSimulate, display results in GUI
global DATA SPEC cfg handles MODEL
set(handles.btn_quicksim,'Enable','off');
set(handles.btn_start,'Enable','off');
% start quick simulation
% if get(handles.check_compile,'value')
  verbose_flag=1;
% else
%   verbose_flag=0;
% end
try
  DATA=dsSimulate(SPEC,'time_limits',[cfg.t0 cfg.tf],'dt',cfg.dt,'solver','euler','mex_flag',get(handles.check_compile,'value'),'verbose_flag',verbose_flag);
catch
  set(handles.btn_quicksim,'Enable','on');
  set(handles.btn_start,'Enable','on');
end
% convert data to GUI state for updating sim view plots
cfg=data2cfg(DATA);
MODEL=dsGenerateModel(SPEC);
UpdateSimView;
set(handles.btn_quicksim,'Enable','on');
set(handles.btn_start,'Enable','on');
set(handles.edit_ntime,'string',num2str(cfg.ntime));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function cfg=data2cfg(data)
global cfg
elem_names={};
Y=[]; % TODO: speed up by preallocating full data matrix
for i=1:length(data.labels)
  Y=cat(2,Y,data.(data.labels{i}));
  elem_names=cat(2,elem_names,repmat(data.labels(i),[1 size(data.(data.labels{i}),2)]));
end
cfg.t=data.time;
cfg.Y=Y;
cfg.elem_names=elem_names;
cfg.ntime=length(data.time);
cfg.t_plot_indices=1:cfg.ntime;
cfg.dt=data.simulator_options.dt;
cfg.xtick=linspace(cfg.t(1),cfg.t(end),cfg.num_xticks);
cfg.xticklabel=linspace(cfg.t(1),cfg.t(end),cfg.num_xticks);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% function data=cfg2data(cfg)
% ...
% if nargin==0, global cfg; end
% data.labels=unique(cfg.elem_names);

% Menu Callback usage: PlotData(cfg2data,'plot_type','__');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function AddPopulation(src,evnt,base_pop_index)
global SPEC LASTSPEC handles
LASTSPEC=SPEC;
% create new population based on the one associated with the clicked "+" button
SPEC.populations(end+1)=SPEC.populations(base_pop_index);
% give a unique name to the new population
name=sprintf('%s%g',SPEC.populations(end).name,length(SPEC.populations));
SPEC.populations(end).name=name;
% update population controls
val = get(handles.list_pops,'value');
str = get(handles.list_pops,'string');
set(handles.list_pops,'value',[val length(SPEC.populations)]);
set(handles.list_pops,'string',{str{:} name});
% update GUI models/views
UpdateModel;
UpdatePopSelection;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function RemovePopulation(src,evnt,base_pop_index)
global SPEC LASTSPEC handles
LASTSPEC=SPEC;
name=SPEC.populations(base_pop_index).name;
% remove population
SPEC.populations(base_pop_index)=[];
% remove associated connections
if ~isempty(SPEC.connections)
  idx=ismember({SPEC.connections.source},name) | ismember({SPEC.connections.target},name);
  SPEC.connections(idx)=[];
  if isempty(SPEC.connections)
    SPEC.connections=[];
  end
end
% update population controls
val = get(handles.list_pops,'value');
str = get(handles.list_pops,'string');
new_str=setdiff(str,name,'stable');
new_val=find(ismember(new_str,str(val)));
set(handles.list_pops,'value',new_val);
set(handles.list_pops,'string',new_str);
% update GUI models/views
UpdateModel;
UpdatePopSelection;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function RenamePopulation(src,evnt)
global SPEC handles
val = get(handles.list_pops,'value');
str = get(handles.list_pops,'string');
if length(val)>1
  % cannot rename populations >1 are selected
  return;
end
new_name=inputdlg(['Rename Population: ' str{val}],'New name');
drawnow; pause(0.05);  % this innocent line prevents the Matlab hang
if isempty(new_name), return; end
new_name=new_name{1};
old_name=str{val};
% update population name
idx=ismember({SPEC.populations.name},old_name);
SPEC.populations(idx).name=new_name;
% update name in connections
if ~isempty(SPEC.connections)
  idx=ismember({SPEC.connections.source},old_name);
  [SPEC.connections(idx).source]=deal(new_name);
  idx=ismember({SPEC.connections.target},old_name);
  [SPEC.connections(idx).target]=deal(new_name);
end
% update population list
str{val}=new_name;
set(handles.list_pops,'string',str);
% update GUI models/views
UpdateModel;
UpdatePopSelection;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function RenameMechanism(src,evnt)
global SPEC handles
ud=get(src,'userdata');
v=get(src,'value');
s=get(src,'string');
if length(v)>1, return; end
this=ud(v);
newname=inputdlg(['Rename Mechanism: ' s{v}],'New name');
drawnow; pause(0.05);  % this innocent line prevents the Matlab hang
if isempty(newname), return; end
newname=newname{1};

% edit ().mechanisms and ().mechanism_list
SPEC.(this.object_type)(this.object_index).mechanism_list{this.mechanism_index}=newname;
idx=ismember({SPEC.(this.object_type)(this.object_index).mechanisms.name},this.mechanism_name);
SPEC.(this.object_type)(this.object_index).mechanisms(idx).name=newname;

% copy to .mechanisms
if ~isfield(SPEC,'mechanisms') || isempty(SPEC.mechanisms)
  SPEC.mechanisms=SPEC.(this.object_type)(this.object_index).mechanisms(idx);
elseif ~ismember(newname,{SPEC.mechanisms.name})
  SPEC.mechanisms(end+1)=SPEC.(this.object_type)(this.object_index).mechanisms(idx);
end

% update mech editor listbox
s{v}=[this.object_name '.' newname];
set(src,'string',s);

% update mech editor userdata
this.mechanism_name=newname;
this.mechanism_list_name = [this.object_name '.' newname];
ud(v)=this;
set(src,'userdata',ud);

% update intrinsic mechanism list
m=SPEC.(this.object_type)(this.object_index).mechanism_list;
[~,str]=fileparts(m{1});
for j=2:length(m)
  [~,name]=fileparts(m{j});
  str=[str ', ' name];
end
if strcmp(this.object_type,'populations')
  set(handles.edit_pop_mechlist(this.object_index),'string',str);
else
  % get (i,j) index into con mech controls from source/target of object_index
  % ...

end

UpdateModel;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function SaveModel(src,evnt)
% function Save_Spec(src,evnt)
[filename,pathname] = uiputfile({'*.mat;'},'Save as','model-specification.mat');
if isequal(filename,0) || isequal(pathname,0)
  return;
end
outfile = fullfile(pathname,filename);
[fpath,fname,fext] = fileparts(outfile);
global SPEC
specification=SPEC;
fprintf('Saving model ''specification'': %s\n',outfile);
save(outfile,'specification');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function OpenModel(src,evnt)
% Load and/or Append models

[filename,pathname] = uigetfile({'*.mat'},'Pick a model specification file.','MultiSelect','off');

if isequal(filename,0) || isequal(pathname,0), return; end
if iscell(filename)
  datafile = cellfun(@(x)fullfile(pathname,x),filename,'uniformoutput',false);
  filename = filename{1};
else
  datafile = [pathname filename];
end
if exist(datafile,'file')
  fprintf('Loading file: %s\n',datafile);
  try
    o=load(datafile); % load file
    fprintf('Looking for ''specification'' structure...\n');
    if isfield(o,'specification')
      fprintf('specification found.\n');
      global SPEC handles
      SPEC=o.specification;
      close(handles.fig_main);
      dynasim(SPEC);
      %InitializeMainGUI;
      %UpdateModel;
    else
      fprintf('specification not found\n');
    end
  catch
    fprintf('failed to load file. check that it is a valid matlab file: %s\n',datafile);
    return;
  end
end
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function eqns=dsExtractModelStrings(MODEL,display_mode,display_flag)
% Purpose: construct string to display DynaSim model equations:
%   ODEs
%   ICs
%   conditionals
%   functions
%   fixed_variables
%   parameters
%   monitors
%   comments
%
% display_mode {'model' (default),'specification','odefun','xpp'}
% OPTIONS (todo: add options 2 and 3):
% 1. Display resulting model equations from DynaSim model structure
% 2. Display ODEFUN (function handle string for ODE system: @(X,t)...)
%     tip: use fun=str2func(eqns{1}) to obtain function handle from output
%          and ic=eval(eqns{2}) to obtain initial condition vector.
% 3. Display script defining the DynaSim specification structure
% 4. Display XPP .ode model implementation (see notes below)
%
% Example: display DynaSim model equations
% dsExtractModelStrings(MODEL,'model',1);
%
% Example: display and use script defining DynaSim specification structure
% eqns=dsExtractModelStrings(MODEL,'specification',1);
% spec=eval(eqns); % note: equivalent to MODEL.specification
%
% Example: integrate system using built-in Matlab solver
%   eqns=dsExtractModelStrings(MODEL,'odefun',0);
%   fun=eval(eqns{1});
%   ic=eqns{2};
%   [t,y]=ode23(fun,[0 100],ic);
%   figure; plot(t,y);

if nargin<3
  display_flag=0;
end
if nargin<2 || isempty(display_mode)
  display_mode='model';
end

eqns={};
switch lower(display_mode)
  case 'model' % Display resulting model equations from DynaSim model structure
    % standardize DynaSim model structure
    MODEL=dsCheckModel(MODEL);
    % ODEs and ICs:
    if ~isempty(MODEL.state_variables)
      eqns{end+1}='% DIFFERENTIAL EQUATIONS:';
      vars=MODEL.state_variables;
      for i=1:length(vars)
        eqns{end+1}=sprintf('%%  %s'' = %s',vars{i},MODEL.ODEs.(vars{i}));
      end
      eqns{end+1}='%';
      eqns{end+1}='% Initial conditions:';
      for i=1:length(vars)
        eqns{end+1}=sprintf('%%  %s(0) = %s',vars{i},MODEL.ICs.(vars{i}));
      end
      eqns{end+1}='';
    end
    % conditionals
    if ~isempty(MODEL.conditionals)
      eqns{end+1}='% CONDITIONALS:';
      for i=1:length(MODEL.conditionals)
        str=sprintf('  if(%s)then(%s)',MODEL.conditionals(i).condition,MODEL.conditionals(i).action);
        if ~isempty(MODEL.conditionals(i).else)
          str=sprintf('%selse(%s)',str,MODEL.conditionals(i).else);
        end
        eqns{end+1}=sprintf('\t%s',str);
      end
      eqns{end+1}='';
    end
    types={'parameters','fixed_variables','functions'};%,'monitors'
    type_headers={'% PARAMETERS:','% FIXED VARIABLES:','% FUNCTIONS:','% MONITORS:'};
    for p=1:length(types)
      type=types{p};
      header=type_headers{p};
      if ~isempty(MODEL.(type))
        eqns{end+1}=header;
        fields=fieldnames(MODEL.(type));
        for i=1:length(fields)
          val=MODEL.(type).(fields{i});
          if ~ischar(val)
            val=toString(val,'compact');
          end
          eqns{end+1}=sprintf('  %s = %s',fields{i},val);
        end
      end
      eqns{end+1}='';
    end
  case 'odefun' % Display ODEFUN (function handle string for ODE system: @(X,t)...)
    % Approach:
    % 1. evaluate params -> fixed_vars -> funcs
    % 2. evaluate ICs to get (# elems) per state var
    % 3. prepare state vector X
    % 4. replace state vars in ODEs by X
    % 5. combine X ODEs into ODEFUN

    % evaluate params -> fixed_vars -> funcs
    types={'parameters','fixed_variables','functions'};
    for p=1:length(types)
      type=types{p};
      if ~isempty(MODEL.(type))
        fields=fieldnames(MODEL.(type));
        for i=1:length(fields)
          val=MODEL.(type).(fields{i});
          if ~ischar(val)
            val=toString(val,'compact');
          end
          % evaluate
          eval(sprintf('%s = %s;',fields{i},val));
        end
      end
    end

    % evaluate ICs to get (# elems) per state var and set up generic state var X
    num_vars=length(MODEL.state_variables);
    num_elems=zeros(1,num_vars);
    old_vars=MODEL.state_variables;
    new_vars=cell(1,num_vars);
    new_inds=cell(1,num_vars);
    all_ICs=cell(1,num_vars);
    IC_names={};
    state_var_index=0;
    for i=1:num_vars
      var=MODEL.state_variables{i};
      % evaluate ICs to get (# elems) per state var
      ic=eval([MODEL.ICs.(var) ';']);
      num_elems(i)=length(ic);
      % set state var indices a variables for generic state vector X
      all_ICs{i}=ic;
      IC_names{i}=repmat({var},[1 num_elems(i)]);
      new_inds{i}=state_var_index+(1:length(ic));
      new_vars{i}=sprintf('X(%g:%g)',new_inds{i}(1),new_inds{i}(end));
      state_var_index=state_var_index+length(ic);
    end

    % prepare ODE system (comma-separated ODEs)
    ODEs=strtrim(struct2cell(MODEL.ODEs));
    idx=cellfun(@isempty,regexp(ODEs,';$')); % lines that need semicolons
    ODEs(idx)=cellfun(@(x)[x ';'],ODEs(idx),'uni',0);
    ODEs=[ODEs{:}]; % concatenate ODEs into a single string
    ODEs=strrep(ODEs,';',','); % replace semicolons by commas

    % substitute in generic state vector X
    for i=1:num_vars
      ODEs=dynasim_strrep(ODEs,old_vars{i},new_vars{i});
    end

    % prepare outputs (function handle string, ICs, and element names for
    % mapping each X(i) to a particular state variable):
    ODEFUN = eval(['@(t,X) [' ODEs '];']);
    IC=cat(2,all_ICs{:});
    elem_names=cat(2,IC_names{:});

    eqns{1}=ODEFUN;
    eqns{2}=IC;
    eqns{3}=elem_names;

    %{
      % usage:

      eqns=dsExtractModelStrings(MODEL,'odefun',0);
      ODEFUN=eqns{1};
      IC=eqns{2};
      elem_names=eqns{3};

      dt=.01; t=0:dt:100;
      y=zeros(length(t),length(IC));
      y(1,:)=IC;
      for i=2:length(t)
        y(i,:)=y(i-1,:)+dt*ODEFUN(t,y(i-1,:));
      end
      figure; plot(t,y); legend(elem_names{:},'Location','EastOutside');

      y=IC;
      for i=1:1e4
        y=y+dt*ODEFUN(0,y);
      end;

    %}

  otherwise
    error('options ''specification'' and ''xpp'' not implemented yet.');
end

if display_flag
  cellfun(@disp,eqns);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function undo(src,evnt)
global SPEC LASTSPEC cfg LASTCFG handles MODEL
s=SPEC;
SPEC=LASTSPEC;
LASTSPEC=s;
c=cfg;
cfg=LASTCFG;
LASTCFG=c;
MODEL=dsGenerateModel(SPEC);

InitializeMainGUI;
UpdateModel;

% close(handles.fig_main);
% dynasim(SPEC);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SWEEPS
% function ShowSimStudy(src,evnt)
% if isempty(findobj('tag','fig_simstudy'))
%   DrawStudyInfo;
% else
%   figure(findobj('tag','fig_simstudy'));
% end

function DrawStudyInfo(src,evnt)
global cfg handles

bgcolor=[204 204 180]/255;
bgcolor2='w';

if isempty(findobj('tag','fig_simstudy'))
  handles.fig_simstudy=figure('tag','fig_simstudy','name','Batch Simulation Manager','units','normalized','outerposition',[.25 .1 .5 .8],'NumberTitle','off','color',bgcolor);
  handles.pbatchcontrols=uipanel('parent',handles.fig_simstudy,'backgroundcolor',bgcolor,'title','(1) Configure simulator options','units','normalized','position',[0 .8 1 .2],'fontweight','bold');
  handles.pbatchspace=uipanel('parent',handles.fig_simstudy,'backgroundcolor',bgcolor,'title','(2) Configure search space','units','normalized','position',[0 .3 1 .5],'fontweight','bold');
  handles.pbatchoutputs=uipanel('parent',handles.fig_simstudy,'backgroundcolor',bgcolor,'title','(3) Configure outputs','units','normalized','position',[0 0 1 .3],'fontweight','bold');
else
  figure(findobj('tag','fig_simstudy'));
end

if isfield(cfg,'study')
  study = cfg.study;
else
  study.scope = '';
  study.variable = '';
  study.values = '';
  cfg.study = study;
end
if ~isfield(handles,'text_scope') || ~ishandle(handles.text_scope)
  % controls
  yshift=-.05; ht=.17;
  uicontrol('parent',handles.pbatchcontrols,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.05 .75+yshift .13 .2],'string','machine',...
    'HorizontalAlignment','left');%,'backgroundcolor','w'
  handles.text_memory_limit=uicontrol('parent',handles.pbatchcontrols,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.5 .75+yshift .13 .2],'string','memory_limit',...
    'HorizontalAlignment','right','visible','off');%,'backgroundcolor','w'
  handles.chk_parfor_flag=uicontrol('style','checkbox','value',1,'parent',handles.pbatchcontrols,'backgroundcolor',bgcolor2,'units','normalized','position',[.5 .8+yshift .13 ht],'string','parfor_flag','visible','on');
  uicontrol('parent',handles.pbatchcontrols,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.05 .5+yshift .13 .2],'string','tspan',...
    'HorizontalAlignment','left');%,'backgroundcolor','w'
  uicontrol('parent',handles.pbatchcontrols,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.05 .3+yshift .13 .2],'string','solver',...
    'HorizontalAlignment','left');%,'backgroundcolor','w'
  uicontrol('parent',handles.pbatchcontrols,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.25 .3+yshift .13 .2],'string','dt',...
    'HorizontalAlignment','right');%,'backgroundcolor','w'
  uicontrol('parent',handles.pbatchcontrols,'units','normalized',...
    'style','text','position',[.05 .1+yshift .13 .2],'string','# realizations','backgroundcolor',bgcolor,...
    'HorizontalAlignment','left');%,'backgroundcolor','w'
  handles.chk_mex_flag=uicontrol('style','checkbox','value',0,'parent',handles.pbatchcontrols,'backgroundcolor',bgcolor2,'units','normalized','position',[.5 .55+yshift .13 ht],'string','mex_flag','visible','on'); % [.415 .13+yshift .13 ht]
  handles.rad_machine=uibuttongroup('visible','off','units','normalized','backgroundcolor',bgcolor2,'Position',[.18 .8+yshift .3 .2],'parent',handles.pbatchcontrols);
  handles.rad_machine_1=uicontrol('style','radiobutton','backgroundcolor',bgcolor2,'string','local','parent',handles.rad_machine,'HandleVisibility','off',...
    'units','normalized','pos',[0 0 .4 1],'Callback','global handles; set(handles.edit_memory_limit,''visible'',''off''); set(handles.text_memory_limit,''visible'',''off''); set(handles.chk_parfor_flag,''visible'',''on'');');
  handles.rad_machine_2=uicontrol('style','radiobutton','backgroundcolor',bgcolor2,'string','SGE cluster','parent',handles.rad_machine,'HandleVisibility','off',...
    'units','normalized','pos',[.45 0 .5 1],'Callback','global handles; set(handles.edit_memory_limit,''visible'',''on''); set(handles.text_memory_limit,''visible'',''on''); set(handles.chk_parfor_flag,''visible'',''off'');');
  set(handles.rad_machine,'SelectedObject',handles.rad_machine_1);  % No selection
  set(handles.rad_machine,'Visible','on');
  handles.edit_memory_limit = uicontrol('parent',handles.pbatchcontrols,'units','normalized',...
    'style','edit','position',[.65 .8+yshift .1 ht],'backgroundcolor','w','string','8G',...
    'HorizontalAlignment','left','visible','off');
  handles.edit_timelimits = uicontrol('parent',handles.pbatchcontrols,'units','normalized',...
    'style','edit','position',[.18 .55+yshift .15 ht],'backgroundcolor','w','string','[0 100]',...
    'HorizontalAlignment','left');
  handles.edit_solver = uicontrol('parent',handles.pbatchcontrols,'units','normalized',...
    'style','edit','position',[.18 .35+yshift .15 ht],'backgroundcolor','w','string','euler',...
    'HorizontalAlignment','left');
  handles.edit_dt = uicontrol('parent',handles.pbatchcontrols,'units','normalized',...
    'style','edit','position',[.4 .35+yshift .08 ht],'backgroundcolor','w','string','0.01',...
    'HorizontalAlignment','left');
  handles.edit_repeats = uicontrol('parent',handles.pbatchcontrols,'units','normalized',...
    'style','edit','position',[.18 .15+yshift .15 ht],'backgroundcolor','w','string','1',...
    'HorizontalAlignment','left');
  % search space
  handles.text_scope = uicontrol('parent',handles.pbatchspace,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.1 .9 .1 .05],'string','object',...
    'HorizontalAlignment','center');%,'backgroundcolor','w'
  handles.text_variable = uicontrol('parent',handles.pbatchspace,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.31 .9 .1 .05],'string','variable',...
    'HorizontalAlignment','center');%,'backgroundcolor','w'
  handles.text_values = uicontrol('parent',handles.pbatchspace,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.52 .9 .1 .05],'string','values',...
    'HorizontalAlignment','center'); %,'backgroundcolor','w'
  handles.btn_batch_help = uicontrol('parent',handles.pbatchspace,'units','normalized',...
    'style','pushbutton','fontsize',10,'string','help','callback','web(''https://github.com/DynaSim/DynaSim/wiki/DynaSim-Getting-Started-Tutorial#varying-parameters'',''-browser'');',...
    'position',[.85 .92 .1 .06]);
  % outputs
  uicontrol('parent',handles.pbatchoutputs,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.05 .85 .13 .1],'string','study_dir',...
    'HorizontalAlignment','left');%,'backgroundcolor','w'
  uicontrol('parent',handles.pbatchoutputs,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.75 .85 .15 .1],'string','downsample_factor',...
    'HorizontalAlignment','right');%,'backgroundcolor','w'
  uicontrol('parent',handles.pbatchoutputs,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.05 .7 .1 .1],'string','save',...
    'HorizontalAlignment','right');%,'backgroundcolor','w'
  uicontrol('parent',handles.pbatchoutputs,'units','normalized','backgroundcolor',bgcolor,...
    'style','text','position',[.3 .7 .1 .1],'string','plot',...
    'HorizontalAlignment','right');%,'backgroundcolor','w'
  handles.edit_study_dir = uicontrol('parent',handles.pbatchoutputs,'units','normalized',...
    'style','edit','position',[.15 .85 .55 .15],'backgroundcolor','w','string',pwd,...
    'HorizontalAlignment','left');
  handles.edit_dsfact = uicontrol('parent',handles.pbatchoutputs,'units','normalized',...
    'style','edit','position',[.92 .85 .05 .15],'backgroundcolor','w','string','10',...
    'HorizontalAlignment','left');
  handles.btn_run_simstudy = uicontrol('parent',handles.pbatchoutputs,'units','normalized',...
    'style','pushbutton','fontsize',20,'string','RUN SWEEP','callback',@RunSimStudy,...
    'position',[.67 .4 .3 .3],'backgroundcolor',cfg.ButtonColor,'ForegroundColor',cfg.ButtonFontColor);
  handles.chk_overwrite=uicontrol('style','checkbox','value',0,'parent',handles.pbatchoutputs,'backgroundcolor',bgcolor2,'units','normalized','position'   ,[.83 .75 .14 .08],'string','overwrite_flag');
  handles.chk_savedata=uicontrol('style','checkbox','value',0,'parent',handles.pbatchoutputs,'backgroundcolor',bgcolor2,'units','normalized','position'   ,[.13 .6 .15 .1],'string','raw data');
%   handles.chk_savesum=uicontrol('style','checkbox','value',0,'parent',handles.pbatchoutputs,'backgroundcolor',bgcolor2,'units','normalized','position'    ,[.13 .5 .15 .1],'string','pop average');
%   handles.chk_savespikes=uicontrol('style','checkbox','value',0,'parent',handles.pbatchoutputs,'backgroundcolor',bgcolor2,'units','normalized','position' ,[.13 .4 .15 .1],'string','spike times');
%   handles.chk_saveplots=uicontrol('style','checkbox','value',0,'parent',handles.pbatchoutputs,'backgroundcolor',bgcolor2,'units','normalized','position'  ,[.13 .3 .15 .1],'string','plots');
  handles.chk_saveplots=uicontrol('style','checkbox','value',0,'parent',handles.pbatchoutputs,'backgroundcolor',bgcolor2,'units','normalized','position'  ,[.13 .5 .15 .1],'string','plots');
  handles.chk_plottraces=uicontrol('style','checkbox','value',1,'parent',handles.pbatchoutputs,'backgroundcolor',bgcolor2,'units','normalized','position' ,[.38 .6 .17 .1],'string','state variables');
  handles.chk_plotrates=uicontrol('style','checkbox','value',0,'parent',handles.pbatchoutputs,'backgroundcolor',bgcolor2,'units','normalized','position'  ,[.38 .5 .17 .1],'string','raster plots');
  handles.chk_plotspectra=uicontrol('style','checkbox','value',0,'parent',handles.pbatchoutputs,'backgroundcolor',bgcolor2,'units','normalized','position',[.38 .4 .17 .1],'string','power spectrum');
end
if isfield(handles,'edit_scope')
  if ishandle(handles.edit_scope)
    delete(handles.edit_scope);
    delete(handles.edit_variable);
    delete(handles.edit_values);
    delete(handles.btn_simset_delete);
    delete(handles.btn_simset_copy);
  end
  handles = rmfield(handles,{'edit_scope','edit_variable','edit_values','btn_simset_delete','btn_simset_copy'});
end
for i=1:length(study)
  handles.edit_scope(i) = uicontrol('parent',handles.pbatchspace,'units','normalized',...
    'style','edit','position',[.1 .8-.1*(i-1) .2 .08],'backgroundcolor','w','string',study(i).scope,...
    'HorizontalAlignment','left','Callback',sprintf('global cfg; cfg.study(%g).scope=get(gcbo,''string'');',i));
  handles.edit_variable(i) = uicontrol('parent',handles.pbatchspace,'units','normalized',...
    'style','edit','position',[.31 .8-.1*(i-1) .2 .08],'backgroundcolor','w','string',study(i).variable,...
    'HorizontalAlignment','left','Callback',sprintf('global cfg; cfg.study(%g).variable=get(gcbo,''string'');',i));
  handles.edit_values(i) = uicontrol('parent',handles.pbatchspace,'units','normalized',...
    'style','edit','position',[.52 .8-.1*(i-1) .4 .08],'backgroundcolor','w','string',study(i).values,...
    'HorizontalAlignment','left','Callback',sprintf('global cfg; cfg.study(%g).values=get(gcbo,''string'');',i));
  handles.btn_simset_delete(i) = uicontrol('parent',handles.pbatchspace,'units','normalized',...
    'style','pushbutton','fontsize',10,'string','-','callback',{@DeleteSimSet,i},...
    'position',[.06 .8-.1*(i-1) .03 .08]);
  handles.btn_simset_copy(i) = uicontrol('parent',handles.pbatchspace,'units','normalized',...
    'style','pushbutton','fontsize',10,'string','+','callback',{@CopySimSet,i},...
    'position',[.93 .8-.1*(i-1) .03 .08]);
end
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function DeleteSimSet(src,evnt,index)
global cfg
cfg.study(index) = [];
DrawStudyInfo;
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function CopySimSet(src,evnt,index)
global cfg
cfg.study(end+1) = cfg.study(index);
DrawStudyInfo;

% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function RunSimStudy(src,evnt)
% Purpose: run simulation batch

% Note on schema for sim notes and history:
% notes(1).id='model2';
% notes(1).date='yyyymmdd-hhmmss';
% notes(1).text='batch sim note...';
% notes(1).changes{1}='E.n: 1 => 2';
% notes(1).changes{2}='+E2: {iNa,iK}';
% notes(1).isbatch = 1;
% notes(1).batch.space(1).scope = '(E,I)';
% notes(1).batch.space(1).variables = 'N';
% notes(1).batch.space(1).values = '[1 2 3]';
% notes(1).model = CURRSPEC;
% CURRSPEC.history(end+1)=notes;

disp('Running sweep varying model parameters...');

global cfg SPEC handles % BACKUPFILE BIOSIMROOT
if isempty(SPEC.populations), return; end
if isempty([cfg.study.scope]) && isempty([cfg.study.variable])
  cfg.study.scope=SPEC.populations(1).label;
  cfg.study.variable='size';
  cfg.study.values=sprintf('[%g]',SPEC.populations(1).size);
end
scope = {cfg.study.scope};
variable = {cfg.study.variable};
values = {cfg.study.values};
dir=get(handles.edit_study_dir,'string');
mem=get(handles.edit_memory_limit,'string');
dt=str2num(get(handles.edit_dt,'string'));
lims=str2num(get(handles.edit_timelimits,'string'));
dsfact=str2num(get(handles.edit_dsfact,'string'));

machine=get(get(handles.rad_machine,'SelectedObject'),'String');
if strcmp(machine,'local')
  cluster_flag = 0;
  set(handles.edit_memory_limit,'visible','off');
  set(handles.text_memory_limit,'visible','off');
  set(handles.chk_parfor_flag,'visible','on');
elseif strcmp(machine,'cluster')
  cluster_flag = 1;
  set(handles.edit_memory_limit,'visible','on');
  set(handles.text_memory_limit,'visible','on');
  set(handles.chk_parfor_flag,'visible','off');
end
nrepeats=str2num(get(handles.edit_repeats,'string'));
set(handles.btn_run_simstudy,'Enable','off'); drawnow
for i=1:nrepeats
  if nrepeats>1
    fprintf('Submitting batch iteration %g of %g...\n',i,nrepeats);
  end

  % Record note
%   if ~isfield(SPEC,'history') || isempty(SPEC.history)
%     id=1;
%   else
%     id=max([SPEC.history.id])+1;
%   end
%   note.id=id;
%   timestamp=datestr(now,'yyyymmdd-HHMMSS');
%   note.date=timestamp;
%   if strcmp(machine,'local')
%     note.text=sprintf('%s BATCH: t=[%g %g], dt=%g. ',machine,lims,dt);
%   else
%     note.text=sprintf('%s BATCH: t=[%g %g], dt=%g. ',machine,lims,dt);% study_dir=%s',machine,dir);
%   end
%   tmp=SPEC;
%   if isfield(tmp.model,'eval')
%     tmp.model=rmfield(tmp.model,'eval');
%   end
%   if isfield(tmp,'history')
%     tmp = rmfield(tmp,'history');
%   end
%   if id>1 && isequal(SPEC.history(end).spec.model,SPEC.model)
%     note.id=id-1;
%     note.spec=tmp;
%     note.changes={};
%   else
%     note.spec=tmp;
%     note.changes={'changes made'};
%   end
%   note.isbatch=1;
%   note.batch.space=cfg.study;
%   note.batch.study_dir = dir;
%   note.batch.machine = machine;
%   % update controls
%   s=get(handles.lst_notes,'string');
%   v=get(handles.lst_notes,'value');
%   s={s{:} num2str(note.id)};
%   v=[v length(s)];
%   set(handles.lst_notes,'string',s,'value',v);
%   set(handles.edit_notes,'string','');
%   note.batch.savedata = get(handles.chk_savedata,'value');
%   if id==1
%     SPEC.history = note;
%   else
%     SPEC.history(end+1) = note;
%   end
%   UpdateHistory;
%   tmpspec=rmfield(SPEC,'history');

  % Prepare 'vary'
  values=cellfun(@eval,values,'uni',0);
  vary=cat(1,scope,variable,values)';

  % Prepare plot options
  if cluster_flag || get(handles.chk_saveplots,'value')
    % prepare plot_functions and plot_options according to plot checkboxes
    plot_functions={};
    plot_options={};
    if get(handles.chk_plottraces,'value')
      plot_functions{end+1}=@dsPlot;
      plot_options{end+1}={'plot_type','waveform'};
    end
    if get(handles.chk_plotrates,'value')
      plot_functions{end+1}=@dsPlot;
      plot_options{end+1}={'plot_type','raster'};
    end
    if get(handles.chk_plotspectra,'value')
      plot_functions{end+1}=@dsPlot;
      plot_options{end+1}={'plot_type','power'};
    end
  else
    plot_functions=[];
    plot_options=[];
  end

  % Submit simulation batch
%   if cluster_flag
%     warndlg('Submitting sweep to SGE cluster. Check study_dir/plots and study_dir/data for results.');
%   else
%     warndlg('Running sweep locally. Check Command Window for status. Plots will pop up here when simulations are finished.');
%   end
  [data,studyinfo] = dsSimulate(SPEC,'vary',vary,'dt',dt,'study_dir',dir,'memory_limit',mem,...
    'tspan',lims,'downsample_factor',dsfact,'cluster_flag',cluster_flag, ...
    'save_flag',get(handles.chk_savedata,'value'),'verbose_flag',1,...
    'overwrite_flag',get(handles.chk_overwrite,'value'),'solver',get(handles.edit_solver,'string'),...
    'plot_functions',plot_functions,'plot_options',plot_options,...
    'parfor_flag',get(handles.chk_parfor_flag,'value'),'mex_flag',get(handles.chk_mex_flag,'value'));
    %'addpath',fullfile(BIOSIMROOT,'matlab'), 'timestamp',timestamp,...
    %'savepopavg_flag',get(handles.chk_savesum,'value'),'savespikes_flag',get(handles.chk_savespikes,'value'),...

    if ~cluster_flag
      % plot data according to plot checkboxes
      if get(handles.chk_plottraces,'value')
        dsPlot(data,'plot_type','waveform');
      end
      if get(handles.chk_plotrates,'value')
        dsPlot(data,'plot_type','raster');
      end
      if get(handles.chk_plotspectra,'value')
        dsPlot(data,'plot_type','power');
      end
    end

%   clear tmpspec
%   if isempty(rootoutdir)
%     note.batch.rootoutdir = {};
%   else
%     note.batch.rootoutdir = rootoutdir{1};
%   end
%   if id==1
%     SPEC.history = note;
%   else
%     SPEC.history(end+1) = note;
%   end
%   UpdateHistory;
end
set(handles.btn_run_simstudy,'Enable','on');
% autosave
% if 1
%   spec=SPEC;
%   save(BACKUPFILE,'spec');
% end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% function SPEC=ApplyPopulationParameters(spec)
% % apply global population parameters to pop-specific mechanism equations
% fields={'populations','connections'};
% % update population and connection mechanism lists
% for f=1:length(fields)
%   object=fields{f};
%   for i=1:length(spec.(object))
%     for j=1:length(spec.(object)(i).mechanism_list)
%       if ~isempty(spec.(object)(i).parameters)
%         % approach: set key=val for all keys in eqns
%         eqns=spec.(object)(i).mechanisms(j).equations;
%         keys=spec.(object)(i).parameters(1:2:end);
%         vals=spec.(object)(i).parameters(2:2:end);
%         % get list of parameters/variables/functions in population equations
%         words=unique(regexp(eqns,'[a-zA-Z]+\w*','match'));
%         % find words in user-supplied parameters (keys)
%         found_words=words(ismember(words,keys));
%         if ~isempty(found_words)
%           for ff=1:length(found_words)
%             found_word=found_words{ff};
%             % new parameter assignment
%             precision=8; % number of digits allowed for user-supplied values
%             found_value=toString(vals{strcmp(found_word,keys)},precision);
%             rep=sprintf(' %s=%s;',found_word,found_value);
%             % replace old parameter assignment in the middle of equations
%             pat=['([^\w]{1})' found_word '\s*=\s*\w+;']; % find in the middle
%             eqns=regexprep(eqns,pat,['$1' rep]);
%             % replace old parameter assignment at the beginning of equations
%             pat=['^' found_word '\s*=\s*\w+;']; % find at the beginning
%             eqns=regexprep(eqns,pat,rep);
%           end
%         end
%         spec.(object)(i).mechanisms(j).equations=eqns;
%         % remove global population parameters: spec.(object)(#).parameters=[];
%         spec.(object)(i).parameters=[];
%       end
%     end
%   end
% end