function CBH = cbfreeze(varargin)
%CBFREEZE   Freezes the colormap of a colorbar.
%
%   SYNTAX:
%           cbfreeze
%           cbfreeze off
%           cbfreeze(CMAP)          % Freezes it with this colormap!
%           cbfreeze(CMAP,'off')
%           cbfreeze(H,...)
%     CBH = cbfreeze(...);
%
%   INPUTS:
%     CMAP  - Colormap matrix or name to be used at the colorbar.
%             DEFAULT: (uses the figure one)
%     H     - Handles of colorbars to be frozen, or from figures to search
%             for them or from peer axes (see COLORBAR).
%             DEFAULT: gcf (freezes all colorbars from the current figure)
%     'off' - Unfreezes the colorbars, other options are:
%               'on'    Freezes
%               'un'    same as 'off'
%               'del'   Deletes the colorbars.
%             DEFAULT: 'on' (of course)
%
%   OUTPUTS (all optional):
%     CBH - Color bar handle(s).
%
%   DESCRIPTION:
%     MATLAB works with a unique COLORMAP by figure which is a big
%     limitation. Function FREEZECOLORS by John Iversen allows to use
%     different COLORMAPs in a single figure, but it fails freezing the
%     COLORBAR. This program handles this problem.
%
%   NOTE:
%     * Optional inputs use its DEFAULT value when not given or [].
%     * Optional outputs may or not be called.
%     * If no colorbar is found, one is created on each of the axes.
%     * But, if you need to creat it with LCOLORBAR instead of COLORBAR,
%       use:
%        >> lcolorbar(...,'Tag','Colorbar')
%     * The new frozen colorbar is an axes object and does not behaves
%       as normally colorbars when resizing the peer axes. Although, some
%       time the normal behavior is not that good.
%     * Besides, it does not have the 'Location' property anymore.
%     * But, it does acts normally: no ZOOM, no PAN, no ROTATE3D and no
%       mouse selectable.
%     * No need to say that CAXIS and COLORMAP must be defined before using
%       this function. Besides, the colorbar location. Anyway, 'off' or
%       'del' may help.
%     * The 'del' functionality may be used whether or not the colorbar(s)
%       is(are) froozen. The peer axes are resized back. Try:
%        >> colorbar, cbfreeze del
%
%   EXAMPLE:
%     surf(peaks(30))
%     colormap jet
%     cbfreeze
%     colormap gray
%     title('What...?')
%
%   SEE ALSO:
%     COLORMAP, COLORBAR, CAXIS
%     and
%     FREEZECOLORS by John Iversen
%     at http://www.mathworks.com/matlabcentral/fileexchange
%
%
%   ---
%   MFILE:   cbfreeze.m
%   VERSION: 2.1 (Jul 03, 2014) (<a href="matlab:web('http://www.mathworks.com/matlabcentral/fileexchange/authors/11258')">download</a>)
%   MATLAB:  8.2.0.701 (R2013b)
%   AUTHOR:  Carlos Adrian Vargas Aguilera (MEXICO)
%   CONTACT: nubeobscura@hotmail.com

%   REVISIONS:
%   1.0      Released. (Jun 08, 2009)
%   1.1      Fixed BUG with image handle on MATLAB R2009a. Thanks to Sergio
%            Muniz. (Sep 02, 2009)
%   2.0      Fixed several BUGs about scientific notation, thanks to Rafa
%            and Jenny from the FileExchange. Changed application name to
%            'cbfreeze' on both, the colorbar and the peer axes. New
%            optional input CMAP. (Jun 05, 2014) 
%   2.1      Fixed BUGs about reading inputs, thanks to Maxime Desbiens. 
%            (Jul 03, 2014) 

%   DISCLAIMER:
%   cbfreeze.m is provided "as is" without warranty of any kind, under the
%   revised BSD license.

%   Copyright (c) 2009-2014 Carlos Adrian Vargas Aguilera


% INPUTS CHECK-IN
% -------------------------------------------------------------------------

% Parameters:
appName = 'cbfreeze';

% Set defaults:
OPT  = 'on';
H    = get(get(0,'CurrentFigure'),'CurrentAxes');
CMAP = [];

% Checks inputs:
assert(nargin<=3,'CAVARGAS:cbfreeze:IncorrectInputsNumber',...
    'At most 3 inputs are allowed.')
assert(nargout<=1,'CAVARGAS:cbfreeze:IncorrectOutputsNumber',...
    'Only 1 output is allowed.')

% Checks from where CBFREEZE was called:
if (nargin~=2) || (isempty(varargin{1}) || ...
        ~all(reshape(ishandle(varargin{1}),[],1)) ...
        || ~isempty(varargin{2}))
    % CBFREEZE called from Command Window or M-file:

    % Reads H in the first input: Version 2.1
    if ~isempty(varargin) && ~isempty(varargin{1}) && ...
            all(reshape(ishandle(varargin{1}),[],1)) 
        H = varargin{1};
        varargin(1) = [];
    end
    
    % Reads CMAP in the first input: Version 2.1
    if ~isempty(varargin) && ~isempty(varargin{1})
        if isnumeric(varargin{1}) && (size(varargin{1},2)==3) && ...
                (size(varargin{1},1)==numel(varargin{1})/3)
            CMAP = varargin{1};
            varargin(1) = [];
        elseif ischar(varargin{1}) && ...
                (size(varargin{1},2)==numel(varargin{1}))
            temp = figure('Visible','off');
            try
                CMAP = colormap(temp,varargin{1});
            catch
                close temp
                error('CAVARGAS:cbfreeze:IncorrectInput',...
                    'Incorrrect colormap name ''%s''.',varargin{1})
            end
            close temp
            varargin(1) = [];
        end
    end
    
    % Reads options: Version 2.1
    while ~isempty(varargin)
        if isempty(varargin{1}) || ~ischar(varargin{1}) || ...
                (numel(varargin{1})~=size(varargin{1},2))
            varargin(1) = [];
            continue
        end
        switch lower(varargin{1})
            case {'off','of','unfreeze','unfreez','unfree','unfre', ...
                    'unfr','unf','un','u'}
                OPT = 'off';
            case {'delete','delet','dele','del','de','d'}
                OPT = 'delete';
            otherwise
                OPT = 'on';
        end
    end
    
    % Gets colorbar handles or creates them:
    CBH = cbhandle(H,'force');
    
else
    
    % Check for CallBacks functionalities:
    % ------------------------------------
    
    varargin{1} = double(varargin{1});
    
    if strcmp(get(varargin{1},'BeingDelete'),'on')
        % CBFREEZE called from DeletFcn:
        
        if (ishandle(get(varargin{1},'Parent')) && ...
                ~strcmpi(get(get(varargin{1},'Parent'),'BeingDeleted'),'on'))
            % The handle input is being deleted so do the colorbar:
            OPT = 'delete';
            
            if strcmp(get(varargin{1},'Tag'),'Colorbar')
                % The frozen colorbar is being deleted:
                H = varargin{1};
            else
                % The peer axes is being deleted:
                H = ancestor(varargin{1},{'figure','uipanel'});
            end
        else
            % The figure is getting close:
            return
        end
        
    elseif ((gca==varargin{1}) && ...
            (gcf==ancestor(varargin{1},{'figure','uipanel'})))
        % CBFREEZE called from ButtonDownFcn:
        
        cbdata = getappdata(varargin{1},appName);
        if ~isempty(cbdata)
            if ishandle(cbdata.peerHandle)
                % Sets the peer axes as current (ignores mouse click-over):
                set(gcf,'CurrentAxes',cbdata.peerHandle);
                return
            end
        else
            % Clears application data:
            rmappdata(varargin{1},appName)
        end
        H = varargin{1};
    end
    
    % Gets out:
    CBH = cbhandle(H);
    
end

% -------------------------------------------------------------------------
% MAIN
% -------------------------------------------------------------------------

% Keeps current figure:
cfh = get(0,'CurrentFigure');

% Works on every colorbar:
for icb = 1:length(CBH)
    
    % Colorbar handle:
    cbh = double(CBH(icb));
    
    % This application data:
    cbdata = getappdata(cbh,appName);
    
    % Gets peer axes handle:
    if ~isempty(cbdata)
        peer = cbdata.peerHandle;
        if ~ishandle(peer)
            rmappdata(cbh,appName)
            continue
        end
    else
        % No matters, get them below:
        peer = [];
    end
    
    % Choose functionality:
    switch OPT
        
        case 'delete'
            % Deletes:
            if ~isempty(peer)
                % Returns axes to previous size:
                oldunits = get(peer,'Units');
                set(peer,'Units','Normalized');
                set(peer,'Position',cbdata.peerPosition)
                set(peer,'Units',oldunits)
                set(peer,'DeleteFcn','')
                if isappdata(peer,appName)
                    rmappdata(peer,appName)
                end
            end
            if strcmp(get(cbh,'BeingDelete'),'off')
                delete(cbh)
            end
            
        case 'off'
            % Unfrozes:
            if ~isempty(peer)
                delete(cbh);
                set(peer,'DeleteFcn','')
                if isappdata(peer,appName)
                    rmappdata(peer,appName)
                end
                oldunits = get(peer,'Units');
                set(peer,'Units','Normalized')
                set(peer,'Position',cbdata.peerPosition)
                set(peer,'Units',oldunits)
                CBH(icb) = colorbar(...
                    'Peer'    ,peer,...
                    'Location',cbdata.cbLocation);
            end
            
        case 'on'
            % Freezes:

            % Gets colorbar axes properties:
            cbprops = get(cbh);
            
            % Current axes on colorbar figure:
            fig = ancestor(cbh,{'figure','uipanel'});
            cah = get(fig,'CurrentAxes');
            
            % Gets colorbar image handle. Fixed BUG, Sep 2009
            himage = findobj(cbh,'Type','image');
            
            % Gets image data and transforms them to RGB:
            CData = get(himage,'CData');
            if size(CData,3)~=1
                % It's already frozen:
                continue
            end
            
            % Gets image tag:
            imageTag = get(himage,'Tag');
            
            % Deletes previous colorbar preserving peer axes position:
            if isempty(peer)
                peer = cbhandle(cbh,'peer');
            end
            oldunits = get(peer,'Units');
            set(peer,'Units','Normalized')
            position = get(peer,'Position');
            delete(cbh)
            oldposition = get(peer,'Position');
            
            % Seves axes position
            cbdata.peerPosition = oldposition;
            set(peer,'Position',position)
            set(peer,'Units',oldunits)
            
            % Generates a new colorbar axes:
            % NOTE: this is needed because each time COLORMAP or CAXIS
            %       is used, MATLAB generates a new COLORBAR! This
            %       eliminates that behaviour and is the central point
            %       on this function.
            cbh = axes(...
                'Parent'  ,cbprops.Parent,...
                'Units'   ,'Normalized',...
                'Position',cbprops.Position...
                );
            
            % Saves location for future calls:
            cbdata.cbLocation = cbprops.Location;
            
            % Move ticks because IMAGE draws centered pixels:
            XLim = cbprops.XLim;
            YLim = cbprops.YLim;
            if     isempty(cbprops.XTick)
                % Vertical:
                X = XLim(1) + diff(XLim)/2;
                Y = YLim    + diff(YLim)/(2*length(CData))*[+1 -1];
            else % isempty(YTick)
                % Horizontal:
                Y = YLim(1) + diff(YLim)/2;
                X = XLim    + diff(XLim)/(2*length(CData))*[+1 -1];
            end
            
            % Gets colormap:
            if isempty(CMAP)
                cmap = colormap(fig);
            else
                cmap = CMAP;
            end
            
            % Draws a new RGB image:
            image(X,Y,ind2rgb(CData,cmap),...
                'Parent'            ,cbh,...
                'HitTest'           ,'off',...
                'Interruptible'     ,'off',...
                'SelectionHighlight','off',...
                'Tag'               ,imageTag)
            
            % Moves all '...Mode' properties at the end of the structure,
            % so they won't become 'manual':
            % Bug found by Rafa at the FEx. Thanks!, which also solves the
            % bug found by Jenny at the FEx too. Version 2.0
            cbfields = fieldnames(cbprops);
            indmode  = strfind(cbfields,'Mode');
            temp     = repmat({'' []},length(indmode),1);
            cont     = 0;
            for k = 1:length(indmode)
                % Removes the '...Mode' properties:
                if ~isempty(indmode{k})
                    cont = cont+1;
                    temp{cont,1} = cbfields{k};
                    temp{cont,2} = getfield(cbprops,cbfields{k});
                    cbprops = rmfield(cbprops,cbfields{k});
                end
            end
            for k=1:cont
                % Now adds them at the end:
                cbprops = setfield(cbprops,temp{k,1},temp{k,2});
            end
            
            % Removes special COLORBARs properties:
            cbprops = rmfield(cbprops,{...
                'CurrentPoint','TightInset','BeingDeleted','Type',...       % read-only
                'Title','XLabel','YLabel','ZLabel','Parent','Children',...  % handles
                'UIContextMenu','Location',...                              % colorbars
                'ButtonDownFcn','DeleteFcn',...                             % callbacks
                'CameraPosition','CameraTarget','CameraUpVector', ...
                'CameraViewAngle',...
                'PlotBoxAspectRatio','DataAspectRatio','Position',...
                'XLim','YLim','ZLim'});
            
            % And now, set new axes properties almost equal to the unfrozen
            % colorbar:
            set(cbh,cbprops)
            
            % CallBack features:
            set(cbh,...
                'ActivePositionProperty','position',...
                'ButtonDownFcn'         ,@cbfreeze,...  % mhh...
                'DeleteFcn'             ,@cbfreeze)     % again
            set(peer,'DeleteFcn'        ,@cbfreeze)     % and again!
            
            % Do not zoom or pan or rotate:
            %if isAllowAxesZoom(fig,cbh)
            setAllowAxesZoom  (    zoom(fig),cbh,false)
            %end
            %if isAllowAxesPan(fig,cbh)
            setAllowAxesPan   (     pan(fig),cbh,false)
            %end
            %if isAllowAxesRotate(fig,cbh)
            setAllowAxesRotate(rotate3d(fig),cbh,false)
            %end
            
            % Updates data:
            CBH(icb) = cbh;
            
            % Saves data for future undo:
            cbdata.peerHandle = peer;
            cbdata.cbHandle   = cbh;
            setappdata(cbh ,appName,cbdata);
            setappdata(peer,appName,cbdata);
            
            % Returns current axes:
            if ishandle(cah)
                set(fig,'CurrentAxes',cah)
            end
            
    end % switch functionality
    
end  % MAIN loop

% Resets the current figure
if ishandle(cfh)
    set(0,'CurrentFigure',cfh)
end

% OUTPUTS CHECK-OUT
% -------------------------------------------------------------------------

% Output?:
if ~nargout
    clear CBH
else
    CBH(~ishandle(CBH)) = [];
end

end

% [EOF] CBFREEZE.M by Carlos A. Vargas A.