All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.quinsoft.zeidon.objectbrowser.OiDisplay Maven / Gradle / Ivy

/**
    This file is part of the Zeidon Java Object Engine (Zeidon JOE).

    Zeidon JOE is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Zeidon JOE is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Zeidon JOE.  If not, see .

    Copyright 2009-2015 QuinSoft
 */

package com.quinsoft.zeidon.objectbrowser;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.Line2D;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.swing.JPanel;
import javax.swing.JScrollPane;

import com.quinsoft.zeidon.EntityCursor;
import com.quinsoft.zeidon.EntityCursor.CursorStatus;
import com.quinsoft.zeidon.EntityInstance;
import com.quinsoft.zeidon.View;
import com.quinsoft.zeidon.objectdefinition.EntityDef;
import com.quinsoft.zeidon.objectdefinition.LodDef;

/**
 * This is the graphical panel that paints the OI display.
 *
 * @author DG
 *
 */
class OiDisplay extends JPanel
{
    interface EntitySelectedListener
    {
        void entitySelected( EntityDef entityDef, EntityInstance ei );
        void scaleChanged(View view, Point newCorner);
    }

    private static final long serialVersionUID = 1L;
    private static final int  TOP_PADDING = 2;

    private final BrowserEnvironment env;
    private final DrawingPane  drawingPane;
    private final LodDef       lodDef;
    private final View         view;
    private final View         originalView;
    private       LodDefLayout lodDefLayout;
    private final Map entities;
    private       EntitySquare selectedEntity;
    private final JScrollPane scroller;
    private final EntitySelectedListener entitySelectedListener;
    private       Point mousePoint;

    /**
     * Used to keep track of other entity squares that will need to be
     * repainted when a new entity is selected.
     */
    final Set repaintSquares = new HashSet<>();

    /**
     * Used by up/down arrow processing to navigate entities in the display.
     */
    final Stack moveDown = new Stack<>();

    /**
     * True if we think the mouse is down.
     */
    private boolean mouseDown;

    /**
     * @param env
     */
    OiDisplay( BrowserEnvironment env, View view, Component parent, EntitySelectedListener listener )
    {
        super( new BorderLayout() );
        this.env = env;
        this.lodDef = view.getLodDef();
        originalView = view;
        this.view = view.newView();
        this.view.setInternal( true );  // So the browser doesn't display it.
        this.entitySelectedListener = listener;

        entities = new HashMap();
        drawingPane = new DrawingPane();
        drawingPane.setCursor( new Cursor( Cursor.MOVE_CURSOR ) );
        MouseHandler handler = new MouseHandler();
        drawingPane.addMouseMotionListener( handler );
        drawingPane.addMouseListener( handler );
        drawingPane.addMouseWheelListener( handler );

        scroller = new JScrollPane(drawingPane);
        add(scroller, BorderLayout.CENTER);
        scroller.setVisible( true );
        setup( parent );
        setVisible( true );
    }

    private void setup( Component parent )
    {
        EntityDef root = lodDef.getRoot();
        lodDefLayout = env.getOdLayout( lodDef );

        EntitySquare e = new EntitySquare( this, env, null ); // Create a dummy just to get the size.
        Dimension size = new Dimension( lodDefLayout.getWidth() * e.getPaddedSize().width,
                                        lodDefLayout.getHeight() * e.getPaddedSize().height );
        drawingPane.setPreferredSize( size );
        drawingPane.setVisible( true );

        e = addEntity( root, 0, size.width );
        setSelectedEntity( e );

        if ( scroller.getWidth() < size.width )
        {
            int mid = size.width / 2 - parent.getWidth() / 2 + 10; // 10 is for width of scroll bar.
            Point p = new Point( mid, 0 );
            scroller.getViewport().setViewPosition( p );
        }
    }

    private EntitySquare addEntity( EntityDef entityDef, int left, int right )
    {
        EntityDefLayout layout = lodDefLayout.getEntityDefLayout( entityDef );
        EntitySquare e = new EntitySquare( this, env, layout );

        int totalWidth = right - left - e.getWidth();
        int middle = totalWidth / 2;
        int topPadding = TOP_PADDING * env.getPainterScaleFactor();

        e.setBounds( left + middle, ( entityDef.getDepth() - 1 ) * e.getPaddedSize().height + topPadding,
                     e.getWidth(), e.getHeight() );
        drawingPane.add(  e );
        entities.put( entityDef, e );

        if ( entityDef.getChildCount() == 0 )
            return e;

        List children = entityDef.getChildren();
        int newLeft = left;
        for ( EntityDef child : children )
        {
            layout = lodDefLayout.getEntityDefLayout( child );
            int w = layout.getWidth() * e.getPaddedSize().width;
            addEntity( child, newLeft, newLeft + w );
            newLeft += w;
        }

        return e;
    }

    /**
     * This resets the view cursors to match the original view.
     */
    void restoreView()
    {
        view.copyCursors( originalView );
        revalidate();
    }

    /**
     * @return the selectedEntity
     */
    EntitySquare getSelectedEntity()
    {
        return selectedEntity;
    }

    void repaintEntities()
    {
        for ( EntitySquare square : repaintSquares )
            square.repaint();

        repaintSquares.clear();
    }

    void setForRepaint( EntitySquare square )
    {
        repaintSquares.add( square );
    }

    void setSelectedEntityFromEntityDef( EntityDef entityDef )
    {
        EntitySquare prevSelected = getSelectedEntity();

        EntitySquare square = entities.get( entityDef );
        setSelectedEntity( square );
        prevSelected.repaint();
        square.repaint();
        repaintEntities();

        // Check to see if the square is outside the viewable area on the scroll.
        Rectangle sqrec = square.getBounds();
        Rectangle scrollrec = scroller.getViewport().getViewRect();
        if ( ! scrollrec.contains( sqrec ) )
        {
            // Create a point that puts the middle of the square into the middle
            // of the scroll.
            Point p = new Point( (int) ( sqrec.getCenterX() - scrollrec.getWidth() / 2 ),
                                 (int) ( sqrec.getCenterY() + scrollrec.getHeight() / 2 ) );
            repositionScroll( p );
        }

        revalidate();
    }

    /**
     * @param selectedEntity the selectedEntity to set
     */
    void setSelectedEntity( EntitySquare selectedEntity )
    {
        repaintEntities();
        this.selectedEntity = selectedEntity;
        EntityDef entityDef = selectedEntity.getEntityDef();
        EntityCursor cursor = view.cursor( entityDef );
        if ( cursor.getStatus() == CursorStatus.SET )
            entitySelectedListener.entitySelected( entityDef, cursor.getEntityInstance() );
        else
            entitySelectedListener.entitySelected( entityDef, null );

        if ( entityDef.getRecursiveChild() != null )
        {
            // Indicate that this square will need to be repainted to remove the border
            // when another entity is selected.
            EntitySquare square = entities.get( entityDef.getRecursiveChild() );
            square.repaint();
        }
        else
        if ( entityDef.getRecursiveParent() != null )
        {
            // Indicate that this square will need to be repainted to remove the border
            // when another entity is selected.
            EntitySquare square = entities.get( entityDef.getRecursiveParent() );
            square.repaint();
        }
    }

    /**
     * @return the view
     */
    View getView()
    {
        return view;
    }

    class DrawingPane extends JPanel
    {
        private static final long serialVersionUID = 1L;

        public DrawingPane()
        {
            super( null );
        }

        @Override
        protected void paintComponent( Graphics g )
        {
            super.paintComponent( g );

            // Now draw the lines.
            Graphics2D g2 = (Graphics2D) g;

            // Set a nice thick black line.
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setPaint(Color.black);

            for ( EntityDef entityDef : lodDef.getEntityDefs() )
            {
                EntityDef parent = entityDef.getParent();
                if ( parent == null )  // If this entity is the root we don't need to draw a line.
                    continue;

                EntitySquare e = entities.get( entityDef );
                EntitySquare p = entities.get( parent );

                g2.draw(new Line2D.Double(p.getBottomAnchor(), e.getTopAnchor()));
            }
        }
    }

    void repositionScrollLater( final Point p )
    {
        javax.swing.SwingUtilities.invokeLater( new Runnable()
        {
            @Override
            public void run()
            {
                repositionScroll( p );
            }
        } );
    }

    void repositionScroll( Point p )
    {
        // Make sure we aren't scrolling off the screen
        Dimension s = getSize();
        Dimension ps = drawingPane.getPreferredSize();
        p.x = Math.min( ps.width - s.width, p.x );
        p.y = Math.min( ps.height - s.height, p.y );

        p.x = Math.max( 0, p.x );
        p.y = Math.max( 0, p.y );

        scroller.getViewport().setViewPosition( p );
        scroller.revalidate();
    }

    private class MouseHandler extends MouseAdapter
    {
        @Override
        public void mouseDragged( MouseEvent e )
        {
            int x = mousePoint.x - e.getX();
            int y = mousePoint.y - e.getY();

            final Point p = scroller.getViewport().getViewPosition();
            p.x += x;
            p.y += y;
            javax.swing.SwingUtilities.invokeLater( new Runnable()
            {
                @Override
                public void run()
                {
                    repositionScroll( p );
                }
            } );
        }

        @Override
        public void mousePressed( MouseEvent e )
        {
            mouseDown = true;
            mousePoint = new Point( e.getX(), e.getY() );
            super.mouseReleased( e );
        }

        /* (non-Javadoc)
         * @see java.awt.event.MouseAdapter#mouseReleased(java.awt.event.MouseEvent)
         */
        @Override
        public void mouseReleased( MouseEvent e )
        {
            mouseDown = false;
            super.mouseReleased( e );
        }

        private void changeScale( MouseEvent e, int scale )
        {
            int oldScale = env.getPainterScaleFactor();
            if ( env.setPainterScaleFactor( scale ) )
            {
                // The scale factor has bounds so we need to get it again.
                scale = env.getPainterScaleFactor();

                // We want the center of the scroll pane to remain in the middle once we resize.
                Point corner = scroller.getViewport().getViewPosition();
                int width = e.getX() - corner.x;
                int x = ( corner.x + width ) * scale / oldScale - width;
                int height = e.getY() - corner.y;
                int y = ( corner.y + height ) * scale / oldScale - height;
                Point newCorner = new Point( x, y );

                entitySelectedListener.scaleChanged( view, newCorner );
            }
        }

        @Override
        public void mouseWheelMoved( MouseWheelEvent e )
        {
            if ( e.isControlDown() )
            {
                int scale = env.getPainterScaleFactor() + - e.getWheelRotation();
                changeScale( e, scale );
            }
            if ( e.isAltDown() || mouseDown )
            {
                // Scroll horizontally
                final Point p = scroller.getViewport().getViewPosition();
                p.x += e.getWheelRotation() * env.getPainterScaleFactor() * EntitySquare.SMALLEST_WIDTH * 4;
                repositionScrollLater( p );
            }
            else
            {
                // Scroll vertically
                final Point p = scroller.getViewport().getViewPosition();
                p.y += e.getWheelRotation() * env.getPainterScaleFactor() * EntitySquare.SMALLEST_HEIGHT * 2;
                repositionScrollLater( p );
            }
        }

        @Override
        public void mouseClicked( MouseEvent e )
        {
            if ( e.getClickCount() == 2 )
            {
                if ( env.getPainterScaleFactor() == env.getDefaultPainterScale() )
                    changeScale( e, 1 );
                else
                    changeScale( e, env.getDefaultPainterScale() );
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy