function [hcomponent, hcontainer] = javacomponent(component, position, parent)
%JAVACOMPONENT Create a Java AWT Component and put it in a figure
%
% JAVACOMPONENT(HCOMPONENT) places the Java component HCOMPONENT on the
% current figure PARENT or creates a new figure if one is not available.
% The default position is [20 20 60 20].
%
% JAVACOMPONENT(HCOMPONENT, POSITION, PARENT) places the Java component
% HCOMPONENT on the figure PARENT at position POSITION. POSITION is in
% pixel units with the format [left, bottom, width, height].
%
% [HCOMPONENT, HCONTAINER] = JAVACOMPONENT(HCOMPONENT, POSITION, PARENT)
% places the Java component HCOMPONENT on the figure PARENT at position
% POSITION. It returns the handles to the Java component and its HG
% container in HCONTAINER. HCONTAINER is only returned when pixel
% positioning is used. It can be used to change the units, position,
% and visibility of the Java component after it is added to the figure.
%
% JAVACOMPONENT(HCOMPONENT, CONSTRAINT, PARENT) places the Java component
% HCOMPONENT next to the figure's drawing area using CONSTRAINT. CONSTRAINT
% that can be NORTH, SOUTH, EAST, OR WEST placement - following Java AWT's
% BorderLayout rules. The handle to the Java component is returned on
% success, empty is returned on error. If parent is a uitoolbar handle,
% CONSTRAINT is ignored and the component is placed last in the child list
% for the given toolbar.
%
%   Examples:
%
%   f = figure('WindowStyle', 'docked');
%   b1 = javacomponent(javax.swing.JButton('Hi!'));
%   set(b1,'ActionPerformedCallback','disp Hi!');
%
%   [comp, container] = javacomponent(javax.swing.JSpinner);
%   set(container,'Position', [100, 100, 100, 40]);
%   set(container,'Units', 'normalized');
%
%   f = gcf;
%   tree = javacomponent(javax.swing.JTree, java.awt.BorderLayout.WEST, f);
%   set(container,'Position', [100, 100, 100, 40]);
%
%   f = figure('WindowStyle', 'docked');
%   table = javacomponent(javax.swing.JTable(3,10), ...
%   java.awt.BorderLayout.SOUTH, f);
%
%   tb = uitoolbar(gcf);
%   b2 = javacomponent(javax.swing.JButton('Hi again!'),[],tb); % Note: Position is ignored.
%   set(b2,'ActionPerformedCallback','disp(''Hi again!'')');

%   Copyright 1984-2004 The MathWorks, Inc.
%   $Revision: 1.1.6.11 $ $Date: 2004/11/29 23:33:23 $

if ~isempty(nargchk(1,3,nargin))
    error(usage);
end

if nargin < 3
    parent = gcf;
end

if nargin < 2
    position = [20 20 60 20];
end

parentIsFigure = false;

if isa(handle(parent), 'figure')
    parentIsFigure = true;
    peer = get(parent,'JavaFrame');
elseif isa(handle(parent), 'uitoolbar')
    peer = get(parent,'JavaContainer');
    if isempty(peer)
        drawnow;
        peer = get(parent,'JavaContainer');
    end
else
    error(sprintf('Invalid parent handle\n%s', usage))
end

if isempty(peer)
    error('Java figures are not enabled')
end

hUicontainer = [];
hgp = [];
returnContainer = 1;

% promote the component to a handle object first.
% It seems once a java object is cast to a handle, you cannot get another
% handle with 'callbackproperties'. So, do want you need outright and use
% the same handle in the subsequent code.
if ~isjava(component)
    component =java(component);
end
hcomponent  = handle(component,'callbackProperties');

if nargin == 1
    hgp = handle(peer.addchild(component));
    % parent must be a figure, we default to gcf upstairs
    createPanel;
    hgp.setUIContainer(hUicontainer);
else
    if parentIsFigure
        if isnumeric(position)
            if isempty(position)
                position = [20 20 60 20];
            end
            % numeric position is not set here, rely on the uicontainer
            % listeners below.
            hgp = handle(peer.addchild(component));
            createPanel;
            hgp.setUIContainer(hUicontainer);
        elseif ...
                isequal(char(position),char(java.awt.BorderLayout.NORTH)) || ...
                isequal(char(position),char(java.awt.BorderLayout.SOUTH)) || ...
                isequal(char(position),char(java.awt.BorderLayout.EAST))  || ...
                isequal(char(position),char(java.awt.BorderLayout.WEST))
            hgp = handle(peer.addchild(component, position));
            returnContainer = 0;
            createPanel;
        else
            error(sprintf('Invalid component position\n%s', usage))
        end
    else
        % Adding component to the toolbar.
        % component position is ignored for now
        peer.add(component);
        hUicontainer = parent; % toolbar.
    end

    % make sure the component is on the screen so the
    % caller can interact with it right now.
    % drawnow;
end

configureComponent;

if (returnContainer == 1)
    hcontainer = hUicontainer;
else
    hcontainer = [];
end

    function createPanel
        % add delete listener
        hUicontainer = hgjavacomponent('Parent', parent, 'Units', 'Pixels');

        set(hUicontainer, 'UserData', char(component.getClass.getName)); % For findobj queries.
        if isa(java(hgp), 'com.mathworks.hg.peer.FigureChild')
            set(hUicontainer, 'FigureChild', hgp);
        end
        set(hUicontainer, 'JavaPeer', hcomponent);

        if (returnContainer == 1)
            % add resize listener to parent (parent must be a figure or this dies quietly)
            % this is for normalized units
            addlistener(hUicontainer, 'PixelBounds', 'PropertyPostSet', @handleResize);

            % add visible listener
            addlistener(hUicontainer, 'Visible', 'PropertyPostSet', @handleVisible);

            % add parent listener
            addlistener(hUicontainer, 'Parent', 'PropertyPreSet', @handlePreParent);

            % add parent listener
            addlistener(hUicontainer, 'Parent', 'PropertyPostSet', @handlePostParent);

            % force though 1st resize event
            set(hUicontainer,'Position', position);
        else
            % For the BorderLayout components, we dont really want the
            % hUicontainer to show. But, it provides a nice place for cleanup.
            set(hUicontainer,'Visible', 'off', 'Handlevisibility', 'off');
            % Set position out of the figure to work around a current bug
            % due to which invisible uicontainers show up when renderer is
            % OpenGL (G.
            set(hUicontainer, 'Position', [1 1 0.01 0.01]);
        end

        if isa(component,'com.mathworks.hg.peer.FigureChild')
            component.setUIContainer(hUicontainer);
        end

        function handleResize(obj, evd)
            hgp.setPixelPosition(getpixelposition(hUicontainer, true));
        end

        function handleVisible(obj, evd)
            hgp.setVisible(strcmp(get(hUicontainer,'Visible'),'on'))
        end

        function handlePreParent(obj, evd)
            oldfig = ancestor(parent, 'figure');
            newfig = ancestor(evd.NewValue, 'figure');
            if ~isempty(newfig) && ~isequal(oldfig, newfig)
                peer = get(oldfig,'JavaFrame');
                peer.remove(component);
            end
        end

        function handlePostParent(obj, evd)
            oldfig = ancestor(parent, 'figure');
            newfig = ancestor(evd.NewValue, 'figure');
            if ~isempty(newfig) && ~isequal(oldfig, newfig)
                peer = get(newfig,'JavaFrame');
                hgp= handle(peer.addchild(component));
                if isa(java(hgp), 'com.mathworks.hg.peer.FigureChild')
                    % used by the uicontainer C-code
                    setappdata(hUicontainer, 'FigureChild', java(hgp));
                end
                parent = newfig;
            end
            hgp.setPixelPosition(getpixelposition(hUicontainer, true));
        end
    end

    function configureComponent
        set(hUicontainer,'DeleteFcn', {@containerDelete, hcomponent});        
        addlistener(hcomponent, 'ObjectBeingDestroyed', {@componentDelete, hUicontainer, parentIsFigure});

    end

end

function containerDelete(obj, evd, hc)
    if ishandle(hc)
        delete(hc);
    end
end

function componentDelete(obj, evd, hUicontainer, parentIsFigure)
if (parentIsFigure)
    parent = ancestor(hUicontainer,'figure');
    peer = get(parent, 'JavaFrame');
    % if ~isempty(peer) & ishandle(obj)
    if ishandle(obj)
        % So this even if the figure is about to be deleted because?
        peer.remove(java(obj));
    end

    % delete container is it exists
    if ishandle(hUicontainer)
        delete(hUicontainer);
    end
else
    parent = hUicontainer; % toolbar
    peer = get(parent, 'JavaContainer');

    if ~isempty(peer) && ishandle(obj)
        peer.remove(java(obj));
    end
end
end


function str=usage
str = [sprintf('\n') ...
    'usage: javacomponent(hJavaComponent, position, parent) ' sprintf('\n') ...
    '- position can be [left bottom width height] in pixels ' sprintf('\n') ...
    '  or the string North, South, East, or West' sprintf('\n') ...
    '- parent can be a figure or a uitoolbar handle.'];
end
