function hp = imdisplayrange(varargin)
%IMDISPLAYRANGE Display range tool.
%   IMDISPLAYRANGE creates a display range tool in the current figure. 
%   The display range tool shows the display range of the intensity image
%   or images in the figure.
%
%   The tool is a uipanel object, positioned in the lower-right corner of
%   the figure, that contains the text string "Display Range:" followed
%   by the display range values for the image. For an indexed, truecolor,
%   or binary image, the display range is not applicable and is set 
%   to empty ([]).
%
%   IMDISPLAYRANGE(H) creates a display range tool in the figure specified
%   by the handle H, where H is an image, axes, uipanel, or figure
%   object. Axes, uipanel, or figure objects must contain at
%   least one image object.
%
%   IMDISPLAYRANGE(HPARENT,HIMAGE) creates a tool in HPARENT that shows 
%   the display range of HIMAGE. HIMAGE is a handle to an image object or 
%   an array of image object handles. HPARENT is handle to the figure or
%   uipanel object that contains the display range tool.
%
%   HPANEL = IMDISPLAYRANGE(...) returns a handle to the display range tool
%   uipanel.
%
%    Examples
%    --------
%    
%        imshow('bag.png');
%        imdisplayrange
% 
%        dcm = dicomread('CT-MONO2-16-ankle.dcm');
%        subplot(1,2,1), imshow(dcm);
%        subplot(1,2,2), imshow(dcm,[]);
%        imdisplayrange
% 
%    See also IMTOOL.

%   Copyright 1993-2004 The MathWorks, Inc.
%   $Revision: 1.1.8.1 $  $Date: 2004/08/10 01:49:03 $

[h,parent] = parseInputs(varargin{:});

if strcmp(get(parent,'Type'),'figure')
    parentIsFigure = true;
else
    parentIsFigure = false;
end

imageHandles = imhandles(h);
if isempty(imageHandles)
    eid = sprintf('Images:%s:noImageInFigure',mfilename);
    msg = sprintf('%s expects a figure to contain an image.',upper(mfilename));
    error(eid,'%s',msg);
end

hFig = ancestor(h,'Figure');
if iscell(hFig)  %hFig will be a cell array if imageHandles is an array,
    %even though imageHandles may belong to the same figure.
    hFig = hFig{1};
end

hPanel = createPanel;

% Make sure imdisplayrange can update itself if an image has been added to
% hFig.
%reactToImageChangesInFig(imageHandles,@deleteFcn,@recreateFcn);
reactToImageChangesInFig(imageHandles,@deleteFcn);

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function deleteFcn(obj,eventData)

        if ishandle(hPanel)
            delete(hPanel);
        end
    end

    %%%%%%%%%%%%%%%%%%%%
    function recreateFcn

        imageHandles = findobj(hFig,'type','image');
        hPanel = createPanel;
        reactToImageChangesInFig(imageHandles,@deleteFcn);
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function hPanel = createPanel

        units = 'Pixels';
        parentPos = getpixelposition(parent);
        visibility = 'on';
        posPanel = [1 1 parentPos(3) 20];

        if parentIsFigure
            backgrndColor = get(parent,'Color');
        else
            backgrndColor = get(parent,'BackgroundColor');
        end

        fudge = 2;

        %%%
        hPanel = uipanel('Parent',parent,...
                         'Units',units, ...
                         'Visible',visibility,...
                         'Bordertype','none',...
                         'Position',posPanel,...
                         'BackgroundColor', backgrndColor);

        %%%
        labelString = 'Display range:';
        hLabel = uicontrol('Parent',hPanel,...
                           'Style','text',...
                           'String',labelString, ...
                           'Units','pixels',...
                           'Visible',visibility,...
                           'BackgroundColor',backgrndColor);
        labelExtent = get(hLabel,'Extent');
        posLabel = [posPanel(1) posPanel(2) labelExtent(3) ...
            labelExtent(4)];
        set(hLabel,'Position',posLabel);

        %%%
        defaultString = '[black white]';
        
        hRange = uicontrol('Parent',hPanel,...
                           'Style','text',...
                           'Units','pixels',...
                           'HorizontalAlignment','left',...
                           'BackgroundColor',backgrndColor,...
                           'BusyAction','queue',...
                           'enable', 'inactive',...
                           'visible',visibility,...
                           'Interruptible','off');
        
        imageModels = getimagemodel(imageHandles);

        if numel(imageModels) == 1
          createStaticDisplayRange;
          
        else
          createDynamicDisplayRange;
          set(hRange,'String',defaultString); %do this for display purposes
        end
        
        % link visibility of hPanel and its children
        hlink = linkprop([hPanel hLabel hRange],...
            'Visible');
        setappdata(hPanel,'linkToChildren',hlink);

        setPanelPosition
        set(hPanel,'Position', [panelLeft 1 panelWidth panelHeight]);
        set(hPanel,'Visible','on');  % must be in this function.

        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function createStaticDisplayRange

            imageClass = getClassType(imageModels);

            switch imageClass  
             case 'uint16'
              formatNumber = @(value) sprintf('%d',round(value));
              defaultString = '[00000 00000]'; 
             case 'uint8'
              formatNumber = @(value) sprintf('%d',round(value));
              defaultString = '[000 000]'; 
             case 'logical'
              formatNumber = @(value) sprintf('%d',value);
              defaultString = '[]'; 
             case {'double','single'}
              img = get(imageHandles,'CData');
              
              if isequal(floor(img),img) && all(isfinite(img(:)))
                formatNumber = @(value) sprintf('%d',round(value));
                defaultString = '[-00000 -00000]';                 
              else
                absMaxVal = abs(max(img(:)));
                if (absMaxVal >= 10^4 || absMaxVal < 10^-2)
                  formatNumber = @(value) sprintf('%1.2E', value);
                  defaultString = '[0.00E-000 0.00E-000]';                 

                elseif absMaxVal > 1 || absMaxVal < 0
                  formatNumber = @(value) sprintf('%2.2f', value);
                  defaultString = '[0.0000 0.0000]';                 
                else
                  % double intensity image in range [0 1]
                  formatNumber = @(value) sprintf('%1.2f',value);        
                  defaultString = '[0.00 0.00]';                 
                end
              end
             
            end
            
            set(hRange,'String',defaultString);
            sethRangePosition;
            setDisplayRange;
               
            %%%%%%%%%%%%%%%%%%%%%%%%
            function setDisplayRange
              range = getDisplayRange(imageModels);
              if ~isempty(range)
                rangestr = sprintf('[%s %s]',formatNumber(range(1)), ...
                                   formatNumber(range(2)));
              else
                rangestr = '[ ]';
              end
              set(hRange,'String',rangestr);

            end
            
            %update val if axes clim, image cdata, or image
            %cdatamapping changes
            
            axesHandle = handle(ancestor(imageHandles,'Axes'));
            clim = axesHandle.findprop('CLim');
            climListener = handle.listener(axesHandle, clim, ...
                                           'PropertyPostSet',@updateVal);
            
            setappdata(axesHandle,'climListener',climListener);
            
            hIm = handle(imageHandles);
            cdata = hIm.findprop('Cdata');
            cdataMapping = hIm.findprop('Cdatamapping');
            cdataListener = handle.listener(hIm, cdata, ...
                                            'PropertyPostSet',@updateVal);
            cdataMappingListener = handle.listener(hIm, ...
                                                   cdataMapping, ...
                                                   'PropertyPostSet',@ ...
                                                   updateVal);
            
            setappdata(hIm,'imageTypeListeners',...
                           [cdataListener cdataMappingListener]);

            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
            function updateVal(obj,eventData)

              setDisplayRange;
              extent = get(hRange,'Extent');
              oldPos = get(hRange,'Position');
              if extent(3) > oldPos(3)
                sethRangePosition;
                setPanelPosition;
              end

            end 
        end %createStaticDisplayRange
        
        
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function createDynamicDisplayRange

            axesHandles = ancestor(imageHandles,'Axes');
            if iscell(axesHandles)
              axesHandles = [axesHandles{:}]';
            end
            
            callbackID = iptaddcallback(hFig,'WindowButtonMotionFcn', ...
                                       @showDisplayRange);
            set(hRange,'DeleteFcn',{@removeCallback,hFig,callbackID});
            
            % set string of hRange so it will display nicely 
            % for common cases.
            set(hRange,'String','[0.00E-000 0.00E-000]');

            sethRangePosition;

            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
            function showDisplayRange(obj,eventdata)

                axesCurPt = get(axesHandles,{'CurrentPoint'});
                overAnImage = false;
                im = [];

                % determine which image the cursor is over.
                for k = 1:length(axesHandles)
                    pt = axesCurPt{k};
                    x = pt(1,1);
                    y = pt(1,2);
                    xlim = get(axesHandles(k),'Xlim');
                    ylim = get(axesHandles(k),'Ylim');

                    if x >= xlim(1) && x <= xlim(2) && y >= ylim(1) && y <= ylim(2)
                        im = imageModels(k);
                        overAnImage = true;
                        break;
                    end
                end

                if overAnImage
                    rangestr = num2str(getDisplayRange(im));
                    if ~isempty(rangestr)
                      rangestr = sprintf('[%s]',rangestr);
                    else
                      %target image is not an intensity image
                      rangestr = '[ ]';
                    end

                else
                    rangestr = defaultString;
                end

                set(hRange,'String',rangestr);

            end %showDisplayRange
        end %createDynamicDisplayRange
   
        %%%%%%%%%%%%%%%%%%%%%%%%%%
        function sethRangePosition
  
            rangeExtent = get(hRange,'Extent');
            posRange = [posLabel(1)+posLabel(3) posPanel(2) rangeExtent(3) ...
                        rangeExtent(4)];
            set(hRange,'Position',posRange);
        end

        %%%%%%%%%%%%%%%%%%%%%%%%%
        function setPanelPosition
          parentPos = getpixelposition(parent);
          posRange = get(hRange,'Position');
          panelWidth = posLabel(3)+posRange(3)+fudge;
          panelHeight = max([posLabel(4) posRange(4)]);
          panelLeft = parentPos(3)-panelWidth;
          setpixelposition(hPanel,[panelLeft 1 panelWidth panelHeight]);
        end
          
    end %createPanel

if isequal(parent,hFig)
  set(hFig,'resizefcn',@resizePanel);
  
  if strcmp(get(parent, 'Visible'),'on')
    figure(parent);
  end
end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function resizePanel(obj,evt)

        hfigPos = get(hFig,'Position');

        if ishandle(hPanel)
            oldUnits = get(hPanel,'Units');
            set(hPanel,'Units','pixels');

            panelPos = get(hPanel,'Position');
            set(hPanel,'Position', [hfigPos(3)-panelPos(3) 1 panelPos(3) ...
                panelPos(4)]);
            set(hPanel,'Units',oldUnits);
        end
    end

if nargout > 0
    hp = hPanel;
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function removeCallback(obj,eventData,hFig,callbackID)
      
  if ishandle(hFig)
    iptremovecallback(hFig,'WindowButtonMotionFcn',callbackID);
  end
end 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [h,parent] = parseInputs(varargin)

iptchecknargin(0,2,nargin,mfilename);

switch nargin
    case 0
        %IMDISPLAYRANGEPANEL
        h = get(0, 'CurrentFigure');
        if isempty(h)
            eid = sprintf('Images:%s:noCurrentFigure',mfilename);
            msg1 = sprintf('%s expects a current figure', ...
                upper(mfilename));
            msg2  = ' that contains an image.';
            error(eid,'%s%s',msg1,msg2);
        end
        parent = h;

    case 1
        %IMDISPLAYRANGEPANEL(H)
        h = varargin{1};
        iptcheckhandle(h,{'image','axes','figure'},mfilename,'H',1);
        parent = ancestor(h,'Figure');

    case 2
        %IMDISPLAYRANGEPANEL(HPARENT,HIMAGE)
        parent = varargin{1};
        iptcheckhandle(parent,{'figure','uipanel','uicontainer'},mfilename, ...
            'HPARENT',1);
        h = varargin{2};
        checkImageHandleArray(h,mfilename);
end

end
