function sp_h = impixelregionpanel(parent, hImage)
%IMPIXELREGIONPANEL Pixel Region tool panel.
%   HPANEL = IMPIXELREGIONPANEL(HPARENT, HIMAGE) creates a Pixel Region tool
%   panel associated with the image specified by the handle HIMAGE, called the
%   target image. This is the image whose pixels are to be displayed. HPARENT
%   is the handle to the figure or uipanel object that will contain the Pixel
%   Region tool panel.
% 
%   The Pixel Region tool is a uipanel object that contains an extreme close-up
%   view of a small region of pixels in the target image. The tool superimposes
%   the numeric value of the pixel over each pixel. To define the region being
%   examined, the tool overlays a rectangle on the target image, called the
%   pixel region rectangle. To view pixels in a different region, click and
%   drag the rectangle over the target image.
%  
%   HPANEL is the handle to the Pixel Region tool scroll panel. 
% 
%   Note
%   ----    
%   To open the Pixel Region Tool in a separate figure window, use
%   IMPIXELREGION.
%    
%   Example
%   -------
%
%      himage = imshow('peppers.png');
%      hfigure = figure;
%      hpanel = impixelregionpanel(hfigure, himage);
%
%      % Set the panel's position to the lower-left quadrant of the
%      % figure.
%      set(hpanel, 'Position', [0 0 .5 .5])
%
%   See also IMPIXELREGION, IMPOSITIONRECT, IMSCROLLPANEL.

%   Copyright 1993-2004 The MathWorks, Inc.
%   $Revision: 1.1.8.3 $  $Date: 2004/12/18 07:36:09 $

iptchecknargin(2, 2, nargin, mfilename);
iptcheckhandle(parent,{'figure','uipanel','uicontainer'},mfilename,'HPARENT',1)
iptcheckhandle(hImage,{'image'},mfilename,'HIMAGE',2)

% Set default 'HitTest','off' as workaround to HG issue
% We only want to manually turn on 'HitTest' for objects that will have
% a 'ButtonDownFcn' set.
set(parent,'HitTest','off',...
           'DefaultUipanelHitTest','off',...
           'DefaultAxesHitTest','off',...
           'DefaultHggroupHitTest','off',...           
           'DefaultImageHitTest','off',...
           'DefaultLightHitTest','off',...
           'DefaultLineHitTest','off',...
           'DefaultPatchHitTest','off',... 
           'DefaultRectangleHitTest','off',...
           'DefaultSurfaceHitTest','off',...
           'DefaultTextHitTest','off')

hAxes   = ancestor(hImage, 'axes');
hFigure = ancestor(hAxes, 'figure');

hPixelRegionAx = axes('Parent', parent, ...
                      'YDir', 'reverse', ...
                      'TickDir', 'out', ...
                      'XGrid', 'off', ...
                      'YGrid', 'off', ...
                      'DataAspectRatio', [1 1 1], ...
                      'PlotBoxAspectRatioMode', 'auto', ...
                      'Visible', 'on');
hPixelRegionFig = ancestor(hPixelRegionAx, 'figure');

if ndims(get(hImage, 'CData')) == 2
  set(hPixelRegionFig, 'Colormap', get(hFigure, 'Colormap'));
end

clim = get(hAxes, 'CLim');
if ~isempty(clim)
    set(hPixelRegionAx, 'CLim', clim);
end

hPixelRegionIm = copyobj(hImage, hPixelRegionAx);
set(hPixelRegionIm,'ButtonDownFcn',[],... % Don't want to copy this
                   'HitTest','off');      % Turn off by default to allow
                                          % rect to be draggable

pixRegionImModel = getimagemodel(hPixelRegionIm);
image_height = getImageHeight(pixRegionImModel);
image_width  = getImageWidth(pixRegionImModel);

set(hPixelRegionIm, ...
    'BusyAction', 'cancel', ...
    'Interruptible', 'off');

sp_h   = imscrollpanel(parent,hPixelRegionIm);

set(hPixelRegionFig, ...
    'DefaultTextFontSize', 8, ...
    'DefaultTextFontName', 'none', ...
    'DefaultTextInterpreter', 'none', ...
    'DefaultTextHorizontalAlignment', 'center', ...
    'DefaultTextHitTest', 'off');
h = text(1,1,getDefaultPixelRegionString(pixRegionImModel), ...
         'Parent', hPixelRegionAx);
te = get(h,'Extent');
delete(h);
text_gutter_space = 3;
min_spip = ceil(max(te(3:4))) + 2 * text_gutter_space;

sp_api = iptgetapi(sp_h);
sp_api.setMagnification(min_spip)
h_rect = impositionrect(hAxes, sp_api.getVisibleImageRect());
rect_api = iptgetapi(h_rect);
rect_api.addNewPositionCallback(@rectDragged);
rect_api.setDragConstraintCallback(@(pos) constrainPositionRect(pos, ...
                                                  image_width, image_height));

hScrollable = handle(get(hPixelRegionAx, 'Parent'));
getPixelRegionString = getPixelRegionFormatFcn(pixRegionImModel);
scrollable_listener = handle.listener(hScrollable, ...
                                      hScrollable.findprop('Position'), ...
                                      'PropertyPostSet', @updatePosition);

handleAxes = handle(hAxes);
clim_listener = handle.listener(handleAxes, ...
                                handleAxes.findprop('CLim'), ...
                                'PropertyPostSet', @updateCLim);

handleFigure = handle(hFigure);
colormap_listener = handle.listener(handleFigure, ...
                                    handleFigure.findprop('Colormap'), ...
                                    'PropertyPostSet', @updateColormap);

setappdata(hPixelRegionIm, 'Listeners', [scrollable_listener colormap_listener ...
                   clim_listener]);

imgridlines(hPixelRegionIm);

show_pixel_values = true;
api.setShowPixelValues       = @setShowPixelValues;
api.isValueDisplayPossible   = @isValueDisplayPossible;
setappdata(sp_h, 'impixelregionpanelAPI', api);

setappdata(sp_h, 'impositionrectAPI', rect_api);

set(sp_h, ...
    'DeleteFcn', @deleteTool, ...
    'Tag', 'PixelRegionPanel');

text_handles = [];
updatePosition();

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function updateColormap(varargin)
    if ishandle(hPixelRegionFig)
      set(hPixelRegionFig, 'Colormap', get(hFigure, 'Colormap'));
      updatePosition();
    end
  end
  %---------------------------------------------------------------------

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function updateCLim(varargin)
    if ishandle(hPixelRegionAx)
      set(hPixelRegionAx, 'CLim', get(hAxes, 'CLim'));
      updatePosition();
    end
  end
  %---------------------------------------------------------------------

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function deleteTool(varargin)
    rect_api.delete();
  end
  %---------------------------------------------------------------------
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function rectDragged(varargin)
    rect_pos = rect_api.getPosition();
    sp_api.setVisibleLocation(rect_pos(1), rect_pos(2));
  end
  %---------------------------------------------------------------------

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function setShowPixelValues(new_show_pixel_values)
    show_pixel_values = new_show_pixel_values;
    if show_pixel_values && (sp_api.getMagnification() < min_spip)
      sp_api.setMagnification(min_spip);
    end
    
    updatePosition();
  end
  %---------------------------------------------------------------------

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function tf = isValueDisplayPossible()

    tf = (sp_api.getMagnification() >= min_spip);

  end
  %---------------------------------------------------------------------

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function updatePosition(varargin)
    updateRect();
    updateText();

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function updateRect(varargin)
      rect_api.setPosition(sp_api.getVisibleImageRect());
    end
    %-------------------------------------------------------------------
    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function updateText(varargin)

      if ~show_pixel_values && ~isempty(text_handles)
        set(text_handles, 'Visible', 'off');
        return
      end

      if sp_api.getMagnification() >= min_spip
          [xx,yy] = visiblePixelCenters(sp_api.getVisibleImageRect());
          num_new_text_objects_needed = numel(xx) - numel(text_handles);
          if num_new_text_objects_needed > 0
              text_handles = [text_handles; zeros(num_new_text_objects_needed, 1)];
              for k = 1:num_new_text_objects_needed
                  text_handles(end - k + 1) = text('Parent', hPixelRegionAx);
              end
          end
          
          screen_pixel_colors = getScreenPixelRGBValue(pixRegionImModel, yy(:), xx(:));
          gray_screen_pixel_colors = screen_pixel_colors * [0.2989; 0.5870; 0.1140];
          pixel_value_strings = getPixelRegionString(yy(:), xx(:));
          
          for k = 1:numel(xx)
              if (gray_screen_pixel_colors(k) > 0.5)
                  text_color = 'k';
              else
                  text_color = 'w';
              end
              
              set(text_handles(k), 'Position', [xx(k) yy(k)], ...
                                'String', pixel_value_strings{k}, ...
                                'Color', text_color);
          end
          
          set(text_handles(1:numel(xx)), 'Visible', 'on');
          set(text_handles(numel(xx)+1:end), 'Visible', 'off');
          
      else
          set(text_handles, 'Visible', 'off');
      end
      
    end
    %-------------------------------------------------------------------
    
    
  end
  %---------------------------------------------------------------------

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function [xx, yy] = visiblePixelCenters(im_rect)

    xmin = im_rect(1);
    xmax = xmin + im_rect(3);
    
    ymin = im_rect(2);
    ymax = ymin + im_rect(4);
    
    x1 = max(1, floor(xmin));
    x2 = min(image_width, ceil(xmax));
    
    y1 = max(1, floor(ymin));
    y2 = min(image_height, ceil(ymax));
    
    [xx,yy] = meshgrid(x1:x2,y1:y2);

  end
%-----------------------------------------------------------------------

end

