function setupplotbrowser (fig, plotbrowser)
% This is a utility function used by the plot browser.

% Copyright 2005 The MathWorks, Inc.

% "setupplotbrowser" is called by the PlotBrowser constructor.  First it
% walks the HG hierarchy for the given figure, picking out axes,
% data-related axes children, and UIPanels, and building a tree structure
% on the Java side that roughly mirrors the HG hierarchy.
% 
% Then it sets up event listeners on those HG objects -- additions,
% deletions, and property changes.  These listeners maintain the Java-side
% tree structure to keep it in sync with the HG hierarchy.  There are some
% other odd events it listens to as well, to account for vagaries in the
% way HG works:  axes title changes, color changes to bar and area plots,
% PlotDone events, and paste events.


% allPropNames:  This table maps various HG object types to the properties
% that the plotbrowser is interested in.  We'll use this table to set up
% property post-set listeners as the HG objects are created.
allPropNames = ...
    {{'axes', ...
        {'Visible', 'HandleVisibility', 'Title'}}, ...
     {'tsguis.uitspanel', ...
        {'Visible', 'HandleVisibility', 'Name'}}, ...
     {'uipanel', ...
        {'Visible', 'HandleVisibility', 'Title'}}, ...
     {'graph2d.lineseries', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'Color', 'LineWidth', 'Linestyle',  ...
         'Marker', 'MarkerEdgeColor', 'MarkerFaceColor'}}, ...
     {'specgraph.stemseries', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'Color', 'LineWidth', 'Linestyle',  ...
         'Marker', 'MarkerEdgeColor', 'MarkerFaceColor'}}, ...
     {'specgraph.stairseries', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'Color', 'LineWidth', 'Linestyle',  ...
         'Marker', 'MarkerEdgeColor', 'MarkerFaceColor'}}, ...
     {'specgraph.barseries', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'FaceColor', 'EdgeColor'}}, ...
     {'specgraph.areaseries', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'FaceColor', 'EdgeColor'}}, ...
     {'specgraph.errorbarseries', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'Color', 'LineWidth', 'LineStyle'}}, ...
     {'specgraph.scattergroup', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'Marker', 'MarkerEdgeColor', 'MarkerFaceColor'}}, ...
     {'specgraph.contourgroup', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'LineColor', 'Fill'}}, ...
     {'specgraph.quivergroup', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'Color', 'LineWidth', 'LineStyle', 'ShowArrowHead'}}, ...
     {'graph3d.surfaceplot', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'FaceColor', 'EdgeColor'}}, ...
     {'line', ...
        {'Visible', 'HandleVisibility', ...
         'Color', 'LineWidth', 'Linestyle',  ...
         'Marker', 'MarkerEdgeColor', 'MarkerFaceColor'}}, ...
     {'patch', ...
        {'Visible', 'HandleVisibility', ...
         'FaceColor', 'EdgeColor'}}, ...
     {'surface', ...
        {'Visible', 'HandleVisibility', ...
         'FaceColor', 'EdgeColor'}}, ...
     {'image', ...
        {'Visible', 'HandleVisibility'}}, ...
     {'graph2d.functionline', ...
        {'Visible', 'HandleVisibility', 'DisplayName', ...
         'Color', 'LineWidth', 'Linestyle',  ...
         'Marker', 'MarkerEdgeColor', 'MarkerFaceColor'}}, ...
     {'graph2d.constantlineseries', ...
        {'Visible', 'HandleVisibility', 'Tag', ...
         'Color', 'LineWidth', 'Linestyle',  ...
         'Marker', 'MarkerEdgeColor', 'MarkerFaceColor'}}};

    % The initial batch of listeners are structural.  They listen for added and
    % removed axes and uipanels, plots being made, and pastes being performed:
    createListener (fig, 'ObjectChildAdded', {@figChildAddedCallback}, 'FigChildAddedLsnr');
    createListener (fig, 'ObjectChildRemoved', {@figChildRemovedCallback}, 'FigChildRemovedLsnr');
    plotmgr = getappdata (fig, 'PlotManager');
    createListener (plotmgr, 'PlotFunctionDone', {@figPastePlotDoneCallback}, 'PlotFunctionDoneLsnr');
    createListener (plotmgr, 'PlotEditPaste', {@figPastePlotDoneCallback}, 'PlotEditPasteLsnr');

    % Other listeners will be dynamically created and assigned to the figure
    % children (axes and uipanels).  That's done in the callbacks which are
    % subfunctions of this function, such as "figChildAddedCallback."

    % Walk through the HG containment tree (if there are any children
    % already in the figure) and build the PlotBrowser's tree model.  And
    % that's it.  The rest of this function is all subfunction definitions.
    
    buildUpModel (fig);
    
    
    
    
    %-------------------------------
    function buildUpModel (parent)
    % Recursively constructs the subtree of the PlotBrowser's tree model
    % rooted at this parent, *not* including the parent itself.
    children = get (parent, 'children');
    for i = length(children):-1:1  % walk backwards through children for correct order
        child = children(i);
        if (isa (handle(child), 'scribe.colorbar')) || (isa (handle(child), 'scribe.legend'))
            continue;
            
        elseif (isa (handle(child), 'hg.uipanel'))
            if (strcmpi (get (child, 'HandleVisibility'), 'off') == 1)
                continue;
            end 
            panel = child;
            % then add figure-like listeners to catch axes additions
            plotbrowser.addUIPanelProxy (java(handle(panel)), java(handle(parent)));
            updatePropertiesForObject (handle(panel));
            createListener (panel, 'ObjectChildAdded', {@figChildAddedCallback}, 'ObjectChildAddedLsnr');
            createListener (panel, 'ObjectChildRemoved', {@figChildRemovedCallback}, 'ObjectChildRemovedLsnr');
            createPropertyListenersForObject (handle(panel));
            buildUpModel (panel);
            
        elseif (isa (handle(child), 'hg.axes'))
            if (strcmpi (get (child, 'HandleVisibility'), 'off') == 1)
                continue;
            end
            axes = child;
            createListener (axes, 'ObjectChildAdded', {@axesChildAddedCallback}, 'ObjectChildAddedLsnr');
            createListener (axes, 'ObjectChildRemoved', {@axesChildRemovedCallback}, 'ObjectChildRemovedLsnr');
            createPropertyListenersForObject (handle(axes));
            title = get (axes, 'Title');
            createPropertyListener (title, 'String', {@titlePropertySetCallback, axes});
            plotbrowser.addAxesProxy (java(handle(axes)), java(handle(parent)), java(handle(title)));
            updatePropertiesForObject (handle(axes));
            
            allChildren = graph2dhelper ('get_legendable_children', axes, true);
            for j = 1:length(allChildren)
                axesChild = allChildren(j);
                createPropertyListenersForObject (handle(axesChild));
                plotbrowser.addSeriesProxy ...
                    (java(handle(axesChild)), ...
                     java(handle(axes)), ...
                     getNearestKnownParentClass(axesChild));
                updatePropertiesForObject (handle(axesChild));
            end
        end
    end
    end
        


    % Callback functions start here.

    %-------------------------------
    function figChildAddedCallback (parent, childEventData)
    child = childEventData.child;
    parent = childEventData.source;
    childType = class (child);
    if (isa (child, 'scribe.colorbar')) || (isa (child, 'scribe.legend'))
        return;
        
    elseif isa (child, 'hg.axes')
        if (strcmpi (get (child, 'HandleVisibility'), 'off') == 1)
            return;
        end
        axes = child;
        if isa (parent, 'hg.uipanel')
            plotbrowser.clearAll;
            buildUpModel (fig);   % too complicated; just reconstruct it from scratch!
        else
            createListener (axes, 'ObjectChildAdded', {@axesChildAddedCallback}, 'ObjectChildAddedLsnr');
            createListener (axes, 'ObjectChildRemoved', {@axesChildRemovedCallback}, 'ObjectChildRemovedLsnr');
            createPropertyListenersForObject (handle(axes));
            title = get (axes, 'Title');
            createPropertyListener (title, 'String', {@titlePropertySetCallback, axes});
            plotbrowser.addAxesProxy (java(handle(axes)), java(handle(parent)), java(handle(title)));
            updatePropertiesForObject (handle(axes));
        end
        
    elseif isa (child, 'hg.uipanel')
        if (strcmpi (get (child, 'HandleVisibility'), 'off') == 1)
            return;
        end
        panel = child;
        plotbrowser.addUIPanelProxy (java(handle(panel)), java(handle(parent)));
        updatePropertiesForObject (handle(panel));
        createListener (panel, 'ObjectChildAdded', {@figChildAddedCallback}, 'ObjectChildAddedLsnr');
        createListener (panel, 'ObjectChildRemoved', {@figChildRemovedCallback}, 'ObjectChildRemovedLsnr');
        % go depth-first through its children and see if it has any nested
        % uipanels or axes
        buildUpModel (panel);
    end
    end
    %-- Checked for axes via command line:  OK
    %-- Checked for axes via figure palette:  OK
    %-- Checked for uipanels:  OK

    %-------------------------------
    function figChildRemovedCallback (fig, childEventData)
    child = childEventData.child;
    if (max (strcmpi (class(child), {'axes', 'uipanel'})) == 1)
        plotbrowser.removeProxy (java(handle(child)));
    end
    end
    %-- Checked for axes:  OK
    %-- Checked for uipanels:  OK

    %-------------------------------
    function figPastePlotDoneCallback (fig, eventData)
    objsCreated = eventData.objectsCreated;
    for i = 1:length(objsCreated)
        if (strcmpi (class(objsCreated(i)), 'axes') == 1)
            continue;  
            % ...because we're only interested in axes children!
            % If it's an axes, a figChildAdded has already fired.
        end
        axesChild = objsCreated(i);
        createPropertyListenersForObject (axesChild);
        if (strcmpi (class(axesChild), 'specgraph.barseries') == 1)
            createListener (axesChild, 'BarColorChanged', {@barColorSetCallback}, 'BarColorChangedLsnr');
        elseif (strcmpi (class(axesChild), 'specgraph.areaseries') == 1)
            createListener (axesChild, 'AreaColorChanged', {@barColorSetCallback}, 'AreaColorChangedLsnr');
        end
        plotbrowser.addSeriesProxy ...
            (java(handle(axesChild)), ...
             java(handle(get(axesChild,'parent'))), ...
             getNearestKnownParentClass(axesChild));
        updatePropertiesForObject (handle(axesChild));
    end
    end
    %-- Checked for plot command:   OK
    %-- Checked for paste:  OK

    %-------------------------------
    function axesChildAddedCallback (axes, childEventData)
    axesChild = childEventData.child;
    testedTypes = {'image', 'line', 'patch', 'graph2d.functionline', 'graph2d.constantlineseries'};
    if (max (strcmpi (class(axesChild), testedTypes)) == 1)
        if (strcmpi (get (axesChild, 'HandleVisibility'), 'off') == 1)
            return;
        end
        createPropertyListenersForObject (axesChild);
        plotbrowser.addSeriesProxy ...
            (java(handle(axesChild)), ...
            java(handle(get(axesChild,'parent'))), ...
            getNearestKnownParentClass(axesChild));
        updatePropertiesForObject (handle(axesChild));
    end
    end
    %-- Checked for image:  OK
    %-- Checked for line:  OK
    %-- Checked for patch:  OK
    %-- Checked for functionline:  OK
    %-- Checked for constantlineseries:  OK
    %-- Checked for ordinary lineseries (negative test):  OK

    %-------------------------------
    function axesChildRemovedCallback (axes, childEventData)
    child = childEventData.child;
    if (strcmpi (class(child), 'text') == 0)
        plotbrowser.removeProxy (java(handle(child)));
    end
    end
    %-- Checked for lineseries:  OK
    %-- Checked for patch:  OK
    %-- Checked for text (negative test):  OK
    %-- Checked for arrow (negative test):  OK

    %-------------------------------
    function propertySetCallback (obj, propertyEventData)
    whichProperty = propertyEventData.source.name;
    affectedObj = propertyEventData.AffectedObject;
    plotbrowser.updateProperty (java(handle(affectedObj)), whichProperty);
    if (strcmpi (whichProperty, 'Title') == 1)
        axesTitle = get (affectedObj, 'Title');  % PostSet event, so we can do this
        titleString = get (axesTitle, 'String');
        plotbrowser.updateAxesTitle (java(handle(affectedObj)), titleString);
        createPropertyListener (axesTitle, 'String', {@titlePropertySetCallback, affectedObj});
    end
    end
    %-- Checked for axes visible/invisible:  OK
    %-- Checked for title reference:  (how can I test this?)
    %-- Checked for lineseries visible/invisible:  OK 
    %-- Checked for lineseries color:  OK

    %-------------------------------
    function barColorSetCallback (barseries, colorEventData)
    val = colorEventData.NewValue;
    plotbrowser.updateProperty (java(handle(barseries)), 'FaceColor');
    end
    %-- Checked for barseries:  OK
    %-- Checked for areaseries:  OK
    %-- Checked for lineseries (negative test):  OK

    %-------------------------------
    function titlePropertySetCallback (obj, propertyEventData, axes)
    val = propertyEventData.NewValue;
    plotbrowser.updateAxesTitle (java(handle(axes)), val);
    end
    %-- Checked for title string:  OK

    
    
    
    % Here's a bunch of helper functions.
    
    %-------------------------------
    function createListener (obj, event, callback, uniqueName)
    % Creates the listener and stores it in the object for safekeeping.
    lsnr = handle.listener (obj, event, callback);
    % This guarantees that the event handler will not pass out of scope
    % and be garbage-collected:
    if ~isprop (handle(obj), uniqueName)
        p = schema.prop (handle(obj), uniqueName, 'MATLAB array');
        p.AccessFlags.Serialize = 'off';
    end
    set (handle(obj), uniqueName, lsnr);
    end

    %-------------------------------
    function createPropertyListener (obj, propName, callback)
    % Very similar to createListener, but specialized for property post-set
    % listeners.
    lsnr = handle.listener (obj, findprop(handle(obj), propName), 'PropertyPostSet', callback);
    uniqueName = strcat (propName, 'Lsnr');
    if ~isprop (handle(obj), uniqueName)
        p = schema.prop (handle(obj), uniqueName, 'MATLAB array');
        p.AccessFlags.Serialize = 'off';
    end
    set (handle(obj), uniqueName, lsnr);
    end

    %-------------------------------
    function createPropertyListenersForObject (obj)
    % Uses the allPropNames table to create a set of property listeners for
    % this object.  What properties get listened to depends on the type of
    % object.  The PlotBrowser does not need to know about every change,
    % only a few presentation-related changes.
    
    propNames = {'Visible', 'HandleVisibility'};   
    % These two properties will be listened to for any HG obj, so they're
    % good default propNames to start with (in case we don't find this obj
    % in the allPropNames table).
    objType = class(obj);
    for i = 1:length(allPropNames)
        entry = allPropNames{i};
        if (strcmpi (objType, entry{1}) == 1)
            propNames = entry{2};
            break;
        end
    end
    for i = 1:length(propNames)
        if isprop (handle(obj), propNames{i})
            createPropertyListener (obj, propNames{i}, {@propertySetCallback});
        end
    end
    end

    %-------------------------------
    function updatePropertiesForObject (obj)
    % Called whenever an HG object is detected and added to the PlotBrowser
    % tree model.  It collects all the relevant property values from the HG
    % object and initializes the new proxy object (on the Java side) with 
    % those values.
    objType = class(obj);
    propNames = [];
    for i = 1:length(allPropNames)
        entry = allPropNames{i};
        if (strcmpi (objType, entry{1}) == 1)
            propNames = entry{2};
            break;
        end
    end
    for i = 1:length(propNames)
        plotbrowser.updateProperty (java(handle(obj)), propNames(i));
    end
    if (strcmpi (objType, 'axes') == 1)
        title = get (obj, 'Title');
        str = get (title, 'String');
        plotbrowser.updateAxesTitle (java(handle(obj)), str);
    end
    end

    %% --------------------------------------------
    function objType = getNearestKnownParentClass (obj)
    knownClasses = {'figure', 'axes', 'graph2d.lineseries', ...
                    'specgraph.barseries', 'specgraph.stemseries', ...
                    'specgraph.areaseries', 'specgraph.errorbarseries', ...
                    'specgraph.scattergroup', 'specgraph.contourgroup', ...
                    'specgraph.quivergroup', 'graph3d.surfaceplot', ...
                    'image', 'uipanel', 'uicontrol,' ...
                    'scribe.line', 'scribe.arrow', 'scribe.doublearrow', ...
                    'scribe.textarrow', 'scribe.textbox', 'scribe.scriberect', ...
                    'scribe.scribeellipse', 'scribe.legend', 'scribe.colorbar', ...
                    'line', 'text', 'rectangle', 'patch', 'surface'};
    objType = class(obj);
    for i = 1:length(knownClasses)
        if isa (handle(obj), knownClasses{i})
            objType = knownClasses{i};
            return;
        end
    end
    end
    
    
end
