function hout=imtool(varargin)
%IMTOOL Display image in the Image Tool.  
%   IMTOOL opens a new Image Tool in an empty state. Use the File menu options
%   "Open..." or "Import From Workspace..." to choose an image for display.
%
%   IMTOOL(I) displays the intensity image I.
%
%   IMTOOL(I,[LOW HIGH]) displays I as a grayscale intensity image,
%   specifying the display range for I. The value LOW (and any value less
%   than LOW) displays as black, the value HIGH (and any value greater than
%   HIGH) displays as white, and values in between display as intermediate
%   shades of gray. IMTOOL uses the default number of gray levels. If you
%   use an empty matrix ([]) for [LOW HIGH], IMTOOL uses [min(I(:))
%   max(I(:))]; the minimum value in I displays as black, and the maximum
%   value displays as white.
%
%   IMTOOL(RGB) displays the truecolor image RGB.
%
%   IMTOOL(BW) displays the binary image BW. Values of 0 display as
%   black, and values of 1 display as white.
%
%   IMTOOL(X,MAP) displays the indexed image X with colormap MAP.
%
%   IMTOOL(FILENAME) displays the image contained in the graphics file by
%   FILENAME.  The file must contain an image that can be read by IMREAD or
%   DICOMREAD. IMTOOL calls IMREAD or DICOMREAD to read the image from the
%   file, but the image data is not stored in the MATLAB workspace.  If the
%   file contains multiple images, the first one will be displayed. The file
%   must be in the current directory or on the MATLAB path.
%
%   HFIGURE = IMTOOL(...) returns a handle HFIGURE to the figure created by
%   IMTOOL. CLOSE(HFIGURE) closes the image tool.
%
%   IMTOOL CLOSE ALL closes all image tools.
%
%   IMTOOL(...,PARAM1,VAL1,PARAM2,VAL2,...)  specifies parameters and
%   corresponding values that control various aspects of the image display.
%   Parameter names can be abbreviated, and case does not matter.
%
%   Parameters include:
%
%   'DisplayRange'           Two-element vector [LOW HIGH] that controls 
%                            the display range of an intensity image. See 
%                            above for more details about how to set [LOW HIGH].
%
%                            Including the parameter name is optional, except
%                            when the image is specified by a filename. 
%                            The syntax IMTOOL(I,[LOW HIGH]) is equivalent to
%                            IMTOOL(I,'DisplayRange',[LOW HIGH]).
%                            The parameter name must be specified when 
%                            using IMTOOL with a filename, as in the syntax
%                            IMTOOL(FILENAME,'DisplayRange'[LOW HIGH]).
%
%   'InitialMagnification'   One of two text strings: 'adaptive' or 'fit',
%                            or a numeric scalar value that specifies the 
%                            initial magnification used to display the image. 
%
%                            When set to 'adaptive' then on initial display, 
%                            the entire image is visible. If the image is too
%                            large to display on the screen, IMTOOL displays
%                            the image at the largest magnification that fits 
%                            on the screen.
%
%                            When set to 'fit' IMTOOL scales the entire image
%                            to fit in the window.
%
%                            When set to 100, the image is displayed at 100%
%                            magnification. Similarly, for any other numeric
%                            scalar, so when set to 50, the image is displayed
%                            at 50% magnification. 
%
%                            By default, the initial magnification is set to
%                            the value returned by
%                            IPTGETPREF('ImtoolInitialMagnification').
%
%   Class Support
%   -------------  
%   A truecolor image can be uint8, uint16, double, or single. An indexed image
%   can be logical, uint8, double, or single.  An intensity image can be
%   logical, uint8, double, single, int16, or uint16. The input image must be
%   nonsparse.
% 
%   Related Toolbox Preferences
%   ---------------------------  
%   You can use the IPTSETPREF function to set toolbox preferences that
%   modify the behavior of IMTOOL:
%
%   - 'ImtoolInitialMagnification' controls the initial magnification for
%     image display, unless you override it in a particular call by
%     specifying IMTOOL(...,'InitialMagnification',INITIAL_MAG).
%
%   For more information about these preferences, see the reference entry for
%   IPTSETPREF.
%
%   Remarks
%   -------
%   IMSHOW is the toolbox's fundamental image display function, optimizing 
%   figure, axes, and image object property settings for image display. IMTOOL
%   provides all the image display capabilities of IMSHOW but also provides 
%   access to several other tools for navigating and exploring images, such as
%   the Pixel Region tool, Image Information tool, and the Adjust Contrast 
%   tool. IMTOOL presents an integrated environment for displaying images and
%   performing some common image processing tasks.
%
%   Examples
%   --------
%       % Display an image from a file
%       imtool('board.tif')  
%
%       % Display an indexed image
%       [X,map] = imread('trees.tif');
%       imtool(X,map) 
%
%       % Display an intensity image 
%       I = imread('cameraman.tif');
%       imtool(I) 
%
%       % Display an intensity image, adjust the diplay range
%       h = imtool(I,[0 80]);
%       close(h)
%
%   See also GETIMAGE, IMAGEINFO, IMCONTRAST, IMDISPLAYRANGE, IMGETFILE, 
%            IPTICONDIR, IMOVERVIEW, IMPIXELINFO, IMPIXELREGION, IMREAD, 
%            IMSHOW, IPTGETPREF, IPTSETPREF, IPTWINDOWALIGN.

%   Copyright 1993-2004 The MathWorks, Inc.  
%   $Revision $  $Date $

% Non-default XData and YData are not currently supported

%   'XData'                  Two-element vector X establishes a
%                            nondefault spatial coordinate system, by
%                            specifying the image XData. Note that X can
%                            have more than 2 elements, but only the first
%                            and last elements are actually used.
%
%   'YData'                  Two-element vector Y establishes a
%                            nondefault spatial coordinate system, by
%                            specifying the image YData. Note that Y can
%                            have more than 2 elements, but only the first
%                            and last elements are actually used.

  % short circuit for IMTOOL CLOSE ALL
  close_all =  nargin == 2 && ischar(varargin{1}) && ischar(varargin{2}) && ...
      strcmpi(varargin{1},'close') && ...
      strcmpi(varargin{2},'all');

  % short circuit for IMTOOL CLOSE ALL
  if close_all
     closeAll
     return
  end
  
  javaFigure = isJavaFigure;

  %Create invisible figure
  hFig = figure('Toolbar','none',...
                'Menubar','none',...
                'HandleVisibility','callback',...
                'IntegerHandle','off',...
                'NumberTitle','off',...
                'Tag','imtool',...
                'Visible','off',...
                'DeleteFcn',@deleteTool);

  % 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.
  turnOffDefaultHitTestFigChildren(hFig);
  
  % initialize for function scope
  hIm = [];
  imgModel = [];
  showDisplayRange = [];
  image_name = '';
  cb = [];
  sp_api = [];
  cb_api = [];
  cursor_check_callback_id = [];

  %Tools
  hOverviewFig = []; 
  hImageInfoFig = [];
  hCmapSelectFig = [];
  hBottomPanel = [];
  hPixelRegionFig = [];
  hScrollPanel = [];
  hRangePanel = [];
  hPixInfoPanel = [];
  hContrastFig = [];  
  
  navigationButtonGroup = [];
  navigationMenuGroup = [];
  
  %Keeps list of tools to enable
  menuOnOffList = [];
  toolbarOnOffList = [];
 
  minFigWidth  = 350; % keeps toolbar looking nice
  minFigHeight = 128; % don't scrunch too much
  lastFigPos = []; % used in the ResizeFcn
  imageWidth = [];
  imageHeight = [];

  % need by printToFig, showImageInfo e.t.c 
  [cdata, cdatamapping, clim, map, xdata, ydata,...
   initial_mag,style,filename] = deal([]);

  % mouse_is_off_image is a boolean used to indicate when the mouse
  % pointer has made a transition onto or off of the image.  It is
  % used by nested function cursorCheck().
  mouse_is_off_image = false;
  
  % saved_mouse_pointer is used to save the figure's mouse pointer before
  % changing it to arrow when the mouse moves off the image.  It is
  % used by nested function cursorCheck().
  saved_mouse_pointer = [];
  
  hToolbar = createToolbar; % must be called after declaring navigationButtonGroup
  createMenubar;
 
  % Computes the index number for this particular tool
  tool_number = getImtoolNumber();

  % Figure out variable name of image for use in naming of window in
  % nested function addImageToImtool().
  input_image_name = '';
  if nargin >= 1
      input_image_name = inputname(1);
  end
  
  is_imtool_empty = (nargin == 0 || isempty(varargin{1}));
      
  % checks that there is at least one non-empty input argument
  % this allows the call "imtools([])" to work the same as
  % "imtool()"  
  if ~is_imtool_empty
    addImageToImtool(varargin{:});
    
    % Turn on tools by default
    showOverview

  else
    createMagComboBox;
    set([toolbarOnOffList,menuOnOffList],'Enable','off');
    toolName = sprintf('Image Tool %d - (New)',tool_number);
    set(hFig,'Name',toolName);
    set(hFig,'Visible','on');
  end

  %showPixelRegionTool   % may need some kind of pause before calling this to
  % make sure that hFig is done painting itself
  figure(hFig) % Bring main tool to front

  if (nargout > 0)
    % Only return handle if caller requested it.
    hout = hFig;
  end
  
   %-----------------------------
   function cursorCheck(varargin)
      
     cp = get(hFig, 'CurrentPoint');
     cpx = cp(1,1,1);
     cpy = cp(1,2,1);
 
     over_panel = isOver(cpx,cpy,getpixelposition(hBottomPanel));
     over_scrollbars = isOverScrollbars(cpx,cpy,hScrollPanel);

     off_image = over_panel || over_scrollbars;
     
     [mouse_is_off_image,saved_mouse_pointer] = updateCursor(hFig,...
         off_image, mouse_is_off_image, saved_mouse_pointer, 'arrow');
          
   end
  
   %--------------------------
   function addImageToImtool(varargin)
    
     try
       % Opens the image
       [cdata, cdatamapping, clim, map, xdata, ydata, ...
        initial_mag, style, filename] = ...
           imageDisplayParseInputs(varargin{:});
       
       if isempty(initial_mag)
         initial_mag = iptgetpref('ImtoolInitialMagnification');
       else
         initial_mag = checkInitialMagnification(initial_mag,{'fit','adaptive'},...
                                                 mfilename,'INITIAL_MAG', ...
                                                 []);
       end
       
       if isempty(image_name)
         image_name = GetImageName(filename, input_image_name);
       end
       imtool_name = sprintf('Image Tool %d - %s',tool_number,image_name);
       
       set(hFig,'Name',imtool_name);
       
       
       % Check if XData or YData are non-default, warn and reset
       imageWidth  = size(cdata,2);
       imageHeight = size(cdata,1); 
       defaultXData = [1 imageWidth];
       defaultYData = [1 imageHeight];   
       
       isXDataDefault = isequal(xdata,defaultXData);
       isYDataDefault = isequal(ydata,defaultYData);
       if ~isXDataDefault || ~isYDataDefault
         wid =  sprintf('Images:%s:nonDefaultXDataOrYData',mfilename);
         msg1 = sprintf('IMTOOL currently requires default XData and YData.\n'); 
         
         if ~isXDataDefault
           xdata = [1 imageWidth];
           msgXData = sprintf('You specified non-default XData, resetting to [%d %d].\n',...
                              1,imageWidth);
         else
           msgXData = '';
         end
         
         if ~isYDataDefault
           ydata = [1 imageHeight];
           msgYData = sprintf('You specified non-default YData, resetting to [%d %d].',...
                              1,imageHeight);
         else
           msgYData = '';
         end
         
         warning(wid,'%s%s%s',msg1,msgXData,msgYData)
       end
       
       hAx = axes('Parent', hFig);
       
       warnstate = warning('off','Images:initSize:adjustingMag');
       hIm = basicImageDisplay(hFig,hAx,...
                               cdata,cdatamapping,clim,map,xdata,ydata);
       warning(warnstate)
       
       % Explicitly create an image model for the image.
       imgModel = getimagemodel(hIm);
     
       createPanels;
       
       cursor_check_callback_id = iptaddcallback(hFig, 'WindowButtonMotionFcn', ...
                                                 @cursorCheck);  
       
       
       % Enable all menus and toolbar buttons on list
       set([toolbarOnOffList,menuOnOffList],'Enable','on');
       
       if isempty(cb)
         createMagComboBox;
       end
       
       % Enable Magnification combo box and wire to scrollpanel
       if javaFigure
         cb_api.setScrollpanel(sp_api);
       end
       
       % Disable the open from file and open from WS Menu Items since 
       % they are only valid on new IMTOOL
%       openFromFileMenuItem = findobj(hFig,'Type','uimenu','tag','openFromFile');
%       importFromWSMenuItem = findobj(hFig,'Type','uimenu','tag','openFromWS');     
%       set([openFromFileMenuItem, importFromWSMenuItem],'Enable','off');
       is_imtool_empty = false;
       
       if (~isSupportedImcontrastImage)
         contrastAdj(1) = findobj([toolbarOnOffList,menuOnOffList],'tag',...
                                  'adjust contrast');      
         
         contrastAdj(2) = findobj([toolbarOnOffList,menuOnOffList],'tag',...
                                  'adjust contrast menu');
         set(contrastAdj, 'Enable', 'off')
       end
       
       if (~isSupportedImcolormap)
         colormap_sel_menu = findobj(menuOnOffList,'tag','choose colormap');
         set(colormap_sel_menu,'Enable','off');
       end
       
       % force a call to the resize function     
       resizeImtool;
       
       set(hFig,'Visible','on'); 
       set(hFig,'ResizeFcn',@resizeImtool);
     catch
       % in case of an error, close the figure
       close(hFig)
       rethrow(lasterror)
     end
     
   end %addImageToImtool
   
   %-------------------------------------------
   function createMagComboBox
      
     if javaFigure
       import com.mathworks.mwswing.MJPanel;
       import java.awt.BorderLayout;
       
       [cb,cb_api] = immagboxjava;
       
       panel = MJPanel(BorderLayout);
       panel.add(cb,BorderLayout.WEST)
       javacomponent(panel,0,hToolbar);
     end
    
   end
   
   %-------------------------------------------
   function createPanels
     % This function adds the necessary panels. e.g.
     %  imdisplayrange, imscrollpanel, impixelinfo

     hScrollPanel = imscrollpanel(hFig,hIm);
     sp_api = iptgetapi(hScrollPanel);
     
     if is_imtool_empty
       % If the image is being imported, we will use whichever is 
       % smaller: 100%  or fitMag
       
       initial_mag = 100;
       if (sp_api.findFitMag() < 1)
         initial_mag = 'fit';
       end
     end
     hFigPos = getpixelposition(hFig);
     
     % Create bottom panel
     hBottomPanel = uipanel('Parent',hFig,...
                            'Tag','BottomPanel',...
                            'BorderType','none',...
                            'Units','Pixels',...
                            'Position',[1 1 hFigPos(3) 21]);
     setChildColorToMatchParent(hBottomPanel,hFig);
     
     % Create tools in bottom panel
     hPixInfoPanel = impixelinfo(hBottomPanel,hIm);
     
     showDisplayRange = strcmp(getImageType(imgModel),'intensity');
     if showDisplayRange
       hRangePanel = imdisplayrange(hBottomPanel,hIm);
     end
     
     % Adjust height of bottom panel
     hPixInfoPos = get(hPixInfoPanel,'Position');
     hPixInfoH = hPixInfoPos(4);
     pos = getpixelposition(hBottomPanel);
     pos(4) = hPixInfoH;
     setpixelposition(hBottomPanel,pos);
     
     % Determine if image is too thin to fit; that is, the aspect ratio is
     % such that the image cannot be scaled to fit with either of its 
     % dimensions being at least 1 pixel.
     fp = figparams;
     min_width_to_height = 1/fp.ScreenHeight;
     min_height_to_width = 1/fp.ScreenWidth;
     image_aspect_ratio  = imageWidth/imageHeight;
     is_image_too_thin   = image_aspect_ratio < min_width_to_height || ...
                           1/image_aspect_ratio < min_height_to_width;
     if is_image_too_thin  && ~isnumeric(initial_mag)
       wid = sprintf('Images:%s:extremeAspectRatio',mfilename);
       warning(wid,'Image has an extreme aspect ratio. Displaying at 100%%.');
       initial_mag = 100;
     end
     
     switch initial_mag
      case 'fit'
       resizeImtool % force layout update, must be before call to findFitMag
       sp_api.setMagnification(sp_api.findFitMag())
       
      case 'adaptive'
       % Try for 100%
       mag = 1;
       sp_api.setMagnification(mag)
       
       is_image_small = imageWidth<=minFigWidth && imageHeight<=minFigHeight;
       
       if is_image_small
         % Set figure size to minimum, and don't bother calling initSize
         pos = getpixelposition(hFig);
         pos(3) = minFigWidth;
         pos(4) = minFigHeight;
         setpixelposition(hFig,pos)
         
       else
         % Adaptively adjust figure size
         isBorderTight = true;
         wid = 'Images:initSize:adjustingMag';
         state = warning('query',wid);
         warning('off',wid)
         initSize(hIm,mag,isBorderTight)
         warning(state)
         sp_api.setMagnification(sp_api.findFitMag()) % make image fit
         
         % Figure out if figure needs to grow to fit bottom panel
         % Must be called after call to initSize and findFitMag
         hFigPos = getpixelposition(hFig);
         onScreenImH = sp_api.getMagnification()*imageHeight;
         heightNeeded = onScreenImH + hPixInfoH;
         needMoreRoomForBottomPanel = heightNeeded > hFigPos(4);
         if needMoreRoomForBottomPanel
           hFigPos(4) = heightNeeded;
           setpixelposition(hFig,hFigPos)
         end
         
       end
       
       resizeImtool % force layout update            
       
      otherwise
        sp_api.setMagnification(initial_mag/100);
       
     end          
     
   end

   %---------------------------
   function createMenubar
   
     filemenu = uimenu(hFig, 'Label','&File','Tag','file menu');
     toolmenu = uimenu(hFig, 'Label','&Tools','Tag','tools menu');
     
     if javaFigure
       uimenu(hFig, 'Label', '&Window','Callback', winmenu('callback'),...
              'Tag','winmenu');
       %winmenu(hFig);
     end
     
     % File menu Items
     uimenu(filemenu,'Label','&New','Accelerator','N',...
            'Callback',@openNewTool);
   
       uimenu(filemenu,'Label','&Open...','Tag','openFromFile',...
              'Accelerator','O',...
              'Callback',@openFromFile);
       
     % disabling for compiler since there is no notion of base workspace
     % in a deployed application.
     if ~isdeployed
       uimenu(filemenu, 'Label','&Import from Workspace...',...
              'Callback',@openFromWS,'Tag','openFromWS');
       
       
       exportToWSItem = uimenu(filemenu,'Label','Export To Workspace...',...
                               'Callback',@callImExportToWorkspace,...
                               'Separator','on',...
                               'Tag','exportToWSItem');
     else
       exportToWSItem = [];
     end
          
     printToFigItem = uimenu(filemenu,'Label','&Print To Figure',...
                             'Callback',@printToFig,...
                             'Separator','on',...
                             'Tag','printToFigItem');
     
     uimenu(filemenu,'Label','&Close',...
            'Accelerator','W',...
            'Separator','on',...            
            'Callback',@closeImtool,'Tag','closeItem');
     
     uimenu(filemenu, 'Label','Close &All',...
            'Callback',@closeAll,'Tag','closeAllItem');
     
     
     % Tools menu Items
     zoomInItem = createToolsMenuItem(toolmenu,'&Zoom In','zoom in');
     
     zoomOutItem  = createToolsMenuItem(toolmenu,'Zoom &Out','zoom out');
     
     panItem = createToolsMenuItem(toolmenu,'Pa&n','drag image to pan');
     
     oviewItem = createToolsMenuItem(toolmenu,'Over&view','overview', ...
                                     'Separator','on');
     
     pixRegItem = createToolsMenuItem(toolmenu,'Pixel &Region',...
                                      'pixel region');
     
     imInfoItem = createToolsMenuItem(toolmenu,'&Image Information',...
                                      'image information');    
     
     conAdjItem = createToolsMenuItem(toolmenu,'Adjust &Contrast',...
                                      'adjust contrast');
     
     cmapItem = uimenu(toolmenu,'Label','Choose Color&map...',...
                       'Separator','on',....
                       'Callback',@showImColormapSelect,...
                       'Tag','choose colormap');

     navigationMenuGroup = [zoomInItem, zoomOutItem, panItem];
     
     % Help menu Items
     
     if ~isdeployed
       helpmenu = uimenu(hFig, 'Label','&Help','Tag','help menu');
       
       createToolsMenuItem(helpmenu,'&Image Tool Help','help');
       
       iptstandardhelp(helpmenu);
     end
       
     menuOnOffList = [navigationMenuGroup, printToFigItem, cmapItem, ...
                      exportToWSItem, oviewItem, pixRegItem,...
                      imInfoItem,conAdjItem];
   end
   
   %-----------------------------------------------  
   function callImExportToWorkspace(src,evt)
     
     imExportToWorkspace(hFig);
     
   end %callImExportToWorkspace
   
   
   %-----------------------------------------------  
   function menuH = createToolsMenuItem(parent,labelname,tag,varargin)
     % CREATETOOLMENUITEM Creates a Tool menu Item and connects its behavior
     %    to the behavior of the toolbar button with the same tag.
   
     menu_tag = [tag,' menu'];
     
     tbar = findobj(hFig,'tag',tag);
     if isempty(tbar)
       menuH = [];
       return;
     end
     menuH = uimenu(parent,'Label',labelname,'Tag',menu_tag,varargin{:});
     set(menuH,'Enable',get(tbar,'Enable'));     
     if strcmpi(get(tbar,'type'),'uipushtool') % this is for pushbuttons
       set(menuH,'Callback',get(tbar,'ClickedCallback'));
     elseif strcmpi(get(tbar,'type'),'uitoggletool') % this is for toggles
       set(menuH,'Callback',get(tbar,'OnCallback'));
     end
  end
  
  %-----------------------------------------------
  function closeImtool(obj,evt)
    close(hFig);
  end
  
  %-----------------------------------------------
  function openNewTool(varargin)
    imtool;
  end

  %-----------------------------------------------
  function openFromFile(varargin)
    [filename,user_canceled] = imgetfile;
    if user_canceled
      return
    else
      if is_imtool_empty
        addImageToImtool(filename,'initialmag','fit');
      else
        imtool(filename);
      end
    end
  end
  
  %-----------------------------------------------
  function openFromWS(varargin)
    [I,map,I_var_name,map_var_name,user_canceled] = imgetvar(hFig);
      
    if user_canceled
       return
    end
    
    if is_imtool_empty            
      image_name = I_var_name;
      if isempty(map)
        addImageToImtool(I,'initialmag','fit');
      else
        addImageToImtool(I,map,'initialmag','fit');
      end
    else
        if isempty(map)
            evalin('base',sprintf('imtool(%s);',I_var_name));
        else
            evalin('base',sprintf('imtool(%s,%s)',I_var_name,map_var_name));
        end
    end
    
  end

  %-----------------------------------------------
  function printToFig(varargin)
    
    printImageToFigure(hScrollPanel);
    
  end
  
  %-----------------------------------------------
  function closeAll(obj, evt)
     figs = findall(0,'Type','figure','Tag','imtool');
     close(figs)
  end
  
  %-------------------------------
  function toolbar = createToolbar
    toolbar =  uitoolbar(hFig);
    
    [iconRoot, iconRootMATLAB] = ipticondir;
    overviewIcon = makeToolbarIconFromGIF(fullfile(iconRoot, 'overview.gif'));
    overviewTool = createToolbarPushItem(toolbar,overviewIcon,...
                                         {@showOverview},...
                                         'Overview');

    pixelRegionIcon = makeToolbarIconFromGIF(fullfile(iconRoot, 'pixel_region.gif'));
    pixelRegionTool = createToolbarPushItem(toolbar,pixelRegionIcon,...
                                            @showPixelRegionTool,...
                                            'Pixel region');

    infoTool = [];
    if javaFigure                                    
        infoIcon = makeToolbarIconFromGIF(fullfile(iconRoot, 'info.gif'));
        infoTool = createToolbarPushItem(toolbar,infoIcon,...
                                         @showImageInfo,...
                                         'Image information');
    end
    
    contrastIcon = makeToolbarIconFromPNG(fullfile(iconRoot,'tool_contrast.png'));
    contrastTool = createToolbarPushItem(toolbar,contrastIcon,...
                                         @showImcontrast,...
                                         'Adjust contrast');
    
    if ~isdeployed
      helpIcon = makeToolbarIconFromGIF(fullfile(iconRootMATLAB, 'helpicon.gif'));
      createToolbarPushItem(toolbar,helpIcon,...
                            @showHelp,...
                            'Help');
    end

    zoomInIcon = makeToolbarIconFromGIF(fullfile(iconRootMATLAB, 'view_zoom_in.gif'));
    zoomInTool = createToolbarNavToggleItem(toolbar,zoomInIcon,...
                                            'Zoom in',...
                                            @makeZoomInModeCurrent,...
                                            []);

    set(zoomInTool,'Separator','on')  % separator actually appears before
                                      % zoomIn where we want it.  

    zoomOutIcon = makeToolbarIconFromGIF(fullfile(iconRootMATLAB, 'view_zoom_out.gif'));
    zoomOutTool = createToolbarNavToggleItem(toolbar,zoomOutIcon,...
                                             'Zoom out',...
                                             @makeZoomOutModeCurrent,...
                                             []);

    %    panIcon = makeToolbarIconFromGIF(fullfile(iconRoot, 'hand.gif');)
    panIcon = makeToolbarIconFromPNG(fullfile(iconRoot, 'tool_hand.png'));
    panTool = createToolbarNavToggleItem(toolbar,panIcon,...
                                         'Drag image to pan',...
                                         @makePanModeCurrent,...
                                         []);

    navigationButtonGroup = [zoomInTool zoomOutTool panTool];

    toolbarOnOffList = [navigationButtonGroup, overviewTool, infoTool,...
                        pixelRegionTool, contrastTool];
    
    
  end
  
  %------------------------------
  function showOverview(varargin)
  
    hOverviewFig = showChildTool(hFig,hOverviewFig, @imoverview, {hIm},...
                                 'Overview',...
                                 tool_number,...
                                 {'left','right'},...
                                 {'top','top'});
  end

  %-------------------------------
  function showImageInfo(varargin)

    if isempty(filename)
        args = {hIm};
    else
        args = {hIm,filename};
    end
  
    hImageInfoFig = showChildTool(hFig,hImageInfoFig, @imageinfo, args,...
                                 'Image Information',...
                                  tool_number,...
                                  {'left','left'},...
                                  {'bottom','top'});
  end

  %--------------------------------
  function showImcontrast(varargin)

    if isempty(hContrastFig) || ~ishandle(hContrastFig)
  
        % Turn off any navigation tools that may be on
        % so they don't interfere with contrast adjustment
        set(navigationButtonGroup,'State','off')
        set(navigationMenuGroup,'Checked','off');
        makeDefaultModeCurrent
    end

    hContrastFig = showChildTool(hFig,hContrastFig, @imcontrast, {hIm},...
                                 'Adjust Contrast',...
                                 tool_number,...
                                 {'left','left'},...
                                 {'bottom','top'});
  end
  
  %-------------------------------------
  function showPixelRegionTool(varargin)

    hPixelRegionFig = showChildTool(hFig,hPixelRegionFig, ...
                                    @impixelregion, {hIm},...
                                    'Pixel Region',...
                                    tool_number,...
                                    {'right','left'},...
                                    {'top','top'});
    centerPixelRegionCursor

    function centerPixelRegionCursor
      image_rect = sp_api.getVisibleImageRect();
      new_center_x = image_rect(1) + image_rect(3)/2;
      new_center_y = image_rect(2) + image_rect(4)/2;
      
      pixregion_sp = findobj(hPixelRegionFig, 'Tag', 'PixelRegionPanel');
      rect_api = getappdata(pixregion_sp, 'impositionrectAPI');
      pixregion_rect = rect_api.getPosition();
      old_center_x = pixregion_rect(1) + pixregion_rect(3)/2;
      old_center_y = pixregion_rect(2) + pixregion_rect(4)/2;
      
      delta_x = new_center_x - old_center_x;
      delta_y = new_center_y - old_center_y;
      
      rect_api.setPosition(pixregion_rect + [delta_x delta_y 0 0]);
    end
    
  end
  
  %-------------------------------------
  function showImColormapSelect(src,evt)
    
    hCmapSelectFig = showChildTool(hFig,hCmapSelectFig, ...
                                    @imchoosecmap, {hFig},...
                                    'Choose Colormap',...
                                    tool_number,...
                                    {'right','left'},...
                                    {'top','top'});
  end
    
  %-----------------------------------
  function showHelp(varargin)
  
    ipthelp('imageframe','Image Tool');

  end    
  
  %--------------------------------------
  function setCurrentModeHandler(fun,ptr)

    removeCurrentModeHandler      

    setptr(hFig,ptr)
    
    % Turn on HitTest so ButtonDownFcn will fire when image is clicked
    set(hIm,'HitTest','on')
    sp_api.setImageButtonDownFcn(fun)
    
  end

  %-------------------
  function removeCurrentModeHandler
    
    % Turn off HitTest as we're removing ButtonDownFcn
    set(hIm,'HitTest','off')  
    sp_api.setImageButtonDownFcn([])
    
  end
  
  %-------------------
  function makeZoomInModeCurrent(hSrc,event)

    fun = @imzoomin;
    ptr = 'glassplus';
  
    manageNavTools(hSrc,event,fun,ptr)

  end
  
  %-------------------
  function makeZoomOutModeCurrent(hSrc,event)

    fun = @imzoomout;
    ptr = 'glassminus';
    
    manageNavTools(hSrc,event,fun,ptr)

  end

  %-------------------
  function makePanModeCurrent(hSrc,event)

    fun = @impan;
    ptr = 'hand';
    
    manageNavTools(hSrc,event,fun,ptr)
  
  end

  %-------------------
  function makeDefaultModeCurrent(varargin)

    fun = '';
    ptr = 'arrow';
    
    setCurrentModeHandler(fun,ptr)

  end

  %----------------------------------
  function manageNavTools(hSrc,event,fun,ptr)
  
    % Manage mutually exclusive itmes on toolbar and menus
    type = lower(get(hSrc,'type'));

    if strcmp(type,'uimenu') 
        % caller is the menu item
        tg = strrep(get(hSrc,'tag'),' menu','');
        hSrcToolbarPeer = findall(navigationButtonGroup,'tag',tg);
        if strcmp(get(hSrcToolbarPeer,'State'),'on')
            set(hSrcToolbarPeer,'State','off');
        else
            set(hSrcToolbarPeer,'State','on');
            setCurrentModeHandler(fun,ptr)
        end
        navigationButtonChange(hSrcToolbarPeer,event);
    
    else 
        % caller is the toolbar button
        setCurrentModeHandler(fun,ptr)
        
    end    

  end
  
  %------------------------------------------
  function navigationButtonChange(hSrc,event)
    menu_tag = [get(hSrc,'tag'),' menu'];
    
    if strcmp(get(hSrc,'State'),'on')
      hSrcMenuPeer = findobj(navigationMenuGroup,'tag',menu_tag);
      otherMenu = navigationMenuGroup(navigationMenuGroup~=hSrcMenuPeer);
      set(navigationButtonGroup(navigationButtonGroup~=hSrc),'State','off')
      set(otherMenu,'Checked','off');
      set(hSrcMenuPeer,'Checked','on');
    else
      makeDefaultModeCurrent
      set(navigationMenuGroup,'Checked','off');
    end

  end
      
  %-------------------------------------------------------------------
  function item = createToolbarNavToggleItem(toolbar,icon,tooltip, ...
                                             oncallback,offcallback)
  
    item = uitoggletool(toolbar,...
                        'Cdata',icon,...
                        'TooltipString',tooltip,...
                        'Tag',lower(tooltip),...
                        'ClickedCallback',@navigationButtonChange,...
                        'OnCallback',oncallback,...
                        'OffCallback',offcallback);
  
  end  
  
  %------------------------------  
  function resizeImtool(src,event)
  
    figPos = getpixelposition(hFig);
    is_window_docked = strcmpi('docked',get(hFig,'WindowStyle'));
    
    if ~is_window_docked
      % Constrain minimum figure size
      if ~isempty(lastFigPos)
        figPos = constrainFigSize(figPos,lastFigPos,[minFigWidth minFigHeight]);
      end
      
      lastFigPos = figPos;
      setpixelposition(hFig,figPos);
    end
    
    if showDisplayRange
        % Make sure hRangePanel moves L/R as window shrinks/grows.
        rangePos = getpixelposition(hRangePanel);
        rangePos(1) = figPos(3)-rangePos(3);
        setpixelposition(hRangePanel,rangePos);
    end

    % Update hBottomPanel width to match figure width
    bottomPanelPos = getpixelposition(hBottomPanel);
    bottomPanelPos(3) = figPos(3);
    setpixelposition(hBottomPanel,bottomPanelPos);    

    % Adjust hScrollPanel position
    spB = bottomPanelPos(4)/figPos(4);
    spH = 1 - spB; 
    set(hScrollPanel,'position', [0 spB 1 spH]);

  end
  
  %------------------  
  function deleteTool(src,event)
  % Delete all child windows that the image tool may have created.
  
    if ~isempty(hOverviewFig) && ishandle(hOverviewFig)
        delete(hOverviewFig)
    end
    
    if ~isempty(hPixelRegionFig) && ishandle(hPixelRegionFig)
        delete(hPixelRegionFig);
    end

    if ~isempty(hImageInfoFig) && ishandle(hImageInfoFig)
        delete(hImageInfoFig);
    end

    if ~isempty(hContrastFig) && ishandle(hContrastFig)
        delete(hContrastFig);
    end

    if ~isempty(hFig) && ishandle(hFig)
        iptremovecallback(ancestor(src, 'figure'), ...
                         'WindowButtonMotionFcn', ...
                         cursor_check_callback_id);
    end
    
  end

  %--------------------------------------------
  function tf = isSupportedImcontrastImage
  
    if (~isequal(getImageType(imgModel), 'intensity'))
        tf = false;
        return
    end
    
    switch (getClassType(imgModel))
      case {'uint8', 'uint16', 'uint32'}
        tf = true;
      
      case {'int8', 'int16', 'int32'}
        tf = true;
      
      case {'logical'}
        tf = false;
      
      case {'double', 'single'}
        tf = true;
      
      otherwise
        tf = false;
      
    end
  
  end
  
  %--------------------------------------------        
  function tf = isSupportedImcolormap
  
    tf = ~strcmpi(getImageType(imgModel),'truecolor');
    
  end
  
  %--------------------------------------------      
  function tnum = getImtoolNumber()
  % Computes the index number for this particular tool
    allFigs = findall(0,'type','figure');
    d = zeros(size(allFigs));
    for n = length(allFigs): -1 : 1
      tmp = getappdata(allFigs(n),'imtoolnumber');
      if isempty(tmp), d(n) = []; else d(n) = tmp; end
    end
    
    % This is how we find the next index.  The rule is that we find the lowest
    % integer value (non-zero and positive) not yet prescribed to an image
    % tool.
    % This is the same way MATLAB figures behave.
    if isempty(d)
      tnum = 1;
    else
      tnum = setxor(d,1:max(d));
      if isempty(tnum) % there are no holes in number
        tnum = max(d)+1;
      end
    end
    setappdata(hFig,'imtoolnumber',tnum);
  end 

end %imtool

%-------------------------------------------------------------------
function item = createToolbarPushItem(toolbar,icon,callback,tooltip)

  item = uipushtool(toolbar,...
                    'Cdata',icon,...
                    'TooltipString',tooltip,...
                    'Tag',lower(tooltip),...
                    'ClickedCallback',callback,...
                    'Interruptible','off',... % Make buttons busy, cancel
                    'BusyAction','cancel');   % any repeated click events.
  
end

%----------------------------------------------------------------
function image_name = GetImageName(filename, input_variable_name)

  if isempty(filename)
    if isempty(input_variable_name)
      image_name = '(MATLAB Expression)';
    else
      image_name = input_variable_name;
    end
  else
    [path,name,ext] = fileparts(filename); 
    image_name = sprintf('%s%s',name,ext);
  end

end

%------------------------------------------------------------------
function hChildFig = showChildTool(hParentFig,hChildFig,...
                                   childFun,childArgs,...
                                   childToolName,...
                                   parentToolNumber,...
                                   parentChildLocHor,...
                                   parentChildLocVer)

  if isempty(hChildFig) || ~ishandle(hChildFig)
      hChildFig = feval(childFun,childArgs{:});

      childName = sprintf('%s (Image Tool %d)', ...
                          childToolName, parentToolNumber);
      set(hChildFig,'Name',childName)
      
      iptwindowalign(hParentFig, parentChildLocHor{1}, ...
                    hChildFig, parentChildLocHor{2});
      iptwindowalign(hParentFig, parentChildLocVer{1},...
                    hChildFig, parentChildLocVer{2});
      
  else
      figure(hChildFig);
  end
    
end
