%MULTISELECT   Multiple selection dialog box.
%   [SELECTION,OK] = LISTDLG(PROMPTSTRING,LISTSTRING,INITIALVALUE)
%   creates a modal dialog box which allows you to select multiple
%   strings from a list.
%   SELECTION is a vector of indices of the selected strings. Can be
%   [], and will be [] when OK is 0.
%   OK is 1 if you push the OK button, or 0 if you push the Cancel
%   button or close the figure.
%   Double-clicking on an item or pressing <Enter> when multiple items
%   are selected has the same effect as clicking the OK button.
%
%   Input parameter:  Description:
%   PROMPTSTRING      String which appears as text above the list box.
%   LISTSTRING        Cell array of strings for the list box OR
%                     Struct array with l.name and l.description.
%   INITIALVALUE      Vector of indices of which items of the list box
%                     are initially selected.
%
%   LISTDLG(...,1) causes the LISTSTRING to be shown as fixed-width text.

% (C) Copyright 1999-2014               Pattern Recognition Group
%     All rights reserved               Faculty of Applied Physics
%                                       Delft University of Technology
%                                       Lorentzweg 1
%                                       2628 CJ Delft
%                                       The Netherlands
%
% Cris Luengo, November 2000.
% Blatantly based on LISTDLG:
%   T. Krauss, 12/7/95, P.N. Secakusuma, 6/10/97
%   Copyright (c) 1984-98 by The MathWorks, Inc.
% 30 March 2001: Added minimum dialog box size.
% 15 September 2001: Added keyboard callback.
% 18 September 2001: Fixed bug for MATLAB 6: 'CurrentCharacter' is []
%                    after pressing shift key.
% 12 June 2002: Added FIXEDWIDTH font option.
% 24 November 2002: Added LISTSTRING as a structure array. Substitute for FIXEDWIDTH.
% 12 August 2014: Fix for new graphics in MATLAB 8.4 (R2014b).

function [selection,value] = multiselect(promptstring,liststring,initialvalue,fixedwidth)

if nargin<3
   error('Invalid input.');
elseif nargin<4
   fixedwidth = 0;
end

% Create dialog box
fig = figure('name','DIPimage dialog', ...
             'units','pixels', ...
             'resize','off', ...
             'numbertitle','off', ...
             'windowstyle','modal', ...
             'KeyPressFcn',['DIP_Image_DLG_ch = double(get(gcbf,''CurrentCharacter''));',...
                            'if ~isempty(DIP_Image_DLG_ch),switch DIP_Image_DLG_ch,',...
                            'case 13,set(gcbf,''userdata'',''ok'');',...
                            'case 27,set(gcbf,''userdata'',''cancel'');',...
                            'end,end;clear DIP_Image_DLG_ch'],...
             'createfcn','', ...
             'closerequestfcn','set(gcbf,''userdata'',''cancel'')', ...
             'color',get(0,'defaultuicontrolbackgroundcolor'), ...
             'visible','off');

% These quantities are copied from DIPIMAGE:
leftmargin = 20;          % Space between left edge and controls
internalmargin = 10;      % Horizontal space between controls
rightmargin = 20;         % Space between right edge and controls
topmargin = 10;           % Space between top edge and controls
bottommargin = 10;        % Space between bottom edge and controls
vspacing = 6;             % Space between rows
vslack = 4;               % Extra space allotted to controls (around text)
hslack = 10;              % Extra space allotted to controls (around text)
mindlgsize = [300,200];

% Calculate sizes of uicontrol elements and create liststring from struct array.
h = uicontrol(fig,'Style','text','String','Select All','Visible','off');
extent = get(h,'Extent');
controlwidth = extent(3)+hslack;
textheight = extent(4);
controlheight = textheight+vslack;
set(h,'String',promptstring);
extent = get(h,'Extent');
listwidth = extent(3);
if fixedwidth
   set(h,'FontName','FixedWidth');
end
if isstruct(liststring)
   % struct array with .name and .description - create cell array of strings
   names = {liststring.name};
   descriptions = {liststring.description};
   set(h,'String',' ');
   extent = get(h,'Extent');
   spw = extent(3);
   set(h,'String','  ');
   extent = get(h,'Extent');
   spw = extent(3) - spw; % This is the width added by a single space
   liststring = names;
   set(h,'String',names);
   extent = get(h,'Extent');
   namew = extent(3) + spw; % This is the width of the widest name
   for ii=1:length(names)
      set(h,'String',names{ii});
      extent = get(h,'Extent');
      nspaces = round((namew - extent(3))/spw);
      liststring{ii} = [names{ii},repmat(' ',1,nspaces),'- ',descriptions{ii}];
   end
else
   % cell array of strings
   % all's correct.
end
set(h,'String',liststring);
extent = get(h,'Extent');
listwidth = max(listwidth,extent(3))+20+hslack;
listheight = extent(4)+vslack;
listwidth = max(2*controlwidth+internalmargin,listwidth);
listwidth = max(listwidth,mindlgsize(1)-leftmargin-rightmargin);
dlgheight = topmargin+textheight+3*vspacing+2*controlheight+bottommargin;
listheight = max(mindlgsize(2)-dlgheight,listheight);
dlgheight = dlgheight+listheight;
dlgwidth = leftmargin+rightmargin+listwidth;
delete(h);

% Position dialog
fp = get(fig,'position');
fp = [fp(1) fp(2)+fp(4)-dlgheight dlgwidth dlgheight];  % keep upper left corner fixed
set(fig,'position',fp);

% Create controls
uicontrol(fig,'style','text','string',promptstring,...
          'horizontalalignment','left','units','pixels',...
          'position',[leftmargin,fp(4)-topmargin-textheight,...
                      listwidth,controlheight]);
uicontrol(fig,'style','pushbutton','string','OK',...
          'position',[fp(3)-rightmargin-controlwidth,bottommargin,...
                      controlwidth,controlheight],...
          'callback','set(gcbf,''userdata'',''ok'')');
uicontrol(fig,'style','pushbutton','string','Cancel',...
          'position',[fp(3)-rightmargin-2*controlwidth-internalmargin,...
                      bottommargin,controlwidth,controlheight],...
          'callback','set(gcbf,''userdata'',''cancel'')');
selectall_btn = uicontrol(fig,'style','pushbutton','string','Select all',...
                          'position',[fp(3)-rightmargin-controlwidth,...
                                      bottommargin+controlheight+vspacing,...
                                      controlwidth,controlheight]);
listbox = uicontrol(fig,'style','listbox','string',liststring,...
                    'backgroundcolor','w','max',2,'value',initialvalue,...
                    'position',[leftmargin,bottommargin+2*controlheight+2*vspacing,...
                                listwidth,listheight]);
if fixedwidth
   set(listbox,'FontName','FixedWidth');
end
if matlabver_ge([8,4])
   set(selectall_btn,'callback',{@selectall_btn_callback,listbox,liststring});
   set(listbox,'callback',{@listbox_callback,selectall_btn,liststring});
else
   set(selectall_btn,'callback',['set(',handle2str(selectall_btn),...
                                 ',''enable'',''off''),set(',handle2str(listbox),...
                                 ',''value'',',mat2str(1:length(liststring)),')']);
   set(listbox,'callback',['if length(get(',handle2str(listbox),',''value''))==',...
                           mat2str(length(liststring)),...
                           ',str=''off'';else,str=''on'';end,set(',...
                           handle2str(selectall_btn),',''enable'',str),'...
                           'if(strcmp(get(gcbf,''SelectionType''),''open'')),',...
                           'set(gcbf,''userdata'',''ok''),end']);
end
if length(initialvalue) == length(liststring)
   set(selectall_btn,'enable','off')
end

% RUN!
set(fig,'visible','on');
waitfor(fig,'userdata')
switch get(fig,'userdata')
   case 'ok'
      value = 1;
      selection = get(listbox,'value');
   otherwise
      value = 0;
      selection = [];
end
delete(fig)

function selectall_btn_callback(selectall_btn,callbackdata,listbox,liststring)
set(selectall_btn,'enable','off');
set(listbox,'value',1:length(liststring));

function listbox_callback(listbox,callbackdata,selectall_btn,liststring)
if length(get(listbox,'value'))==length(liststring)
   str = 'off';
else
   str = 'on';
end
set(selectall_btn,'enable',str);
if(strcmp(get(gcbf,'SelectionType'),'open'))
   set(gcbf,'userdata','ok');
end
