Package edu.mit.blocks.workspace.typeblocking

Source Code of edu.mit.blocks.workspace.typeblocking.TypeBlockManager

package edu.mit.blocks.workspace.typeblocking;

import java.awt.Container;
import java.awt.Point;

import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import edu.mit.blocks.codeblocks.Block;
import edu.mit.blocks.codeblocks.BlockConnector;
import edu.mit.blocks.codeblocks.BlockLink;
import edu.mit.blocks.codeblocks.BlockLinkChecker;
import edu.mit.blocks.renderable.BlockNode;
import edu.mit.blocks.renderable.BlockUtilities;
import edu.mit.blocks.renderable.RenderableBlock;
import edu.mit.blocks.renderable.TextualFactoryBlock;
import edu.mit.blocks.workspace.BlockCanvas;
import edu.mit.blocks.workspace.PageChangeEventManager;
import edu.mit.blocks.workspace.Workspace;
import edu.mit.blocks.workspace.WorkspaceEvent;
import edu.mit.blocks.workspace.WorkspaceWidget;

/**
* The TypeBlockManager primary serves to help users drop
* blocks manually into the bock canvas through the keyboard. 
* To achieve this, the TypeBlockManager commands three
* distinct phases: Interfacing, Searching, Dropping.
*/
public class TypeBlockManager {
   
    private final Workspace workspace;
   
    /**Directional Pad values*/
    protected static enum Direction {

        UP, DOWN, LEFT, RIGHT, ESCAPE, ENTER
    };
    /**TypeBlockmanager graphical view*/
    private final AutoCompletePanel autoCompletePanel;
    /**Helper Controller that manages the transition between blocks D-PAD*/
    private FocusTraversalManager focusManager;
    /**Current canvas with focus*/
    private BlockCanvas blockCanvas;
    /** plus operations string constants**/
    static final String PLUS_OPERATION_LABEL = "+";
    static final String NUMBER_PLUS_OPERATION_LABEL = "+ [number]";
    static final String TEXT_PLUS_OPERATION_LABEL = "+ [text]";
    /**empty string for labels that already exist and shouldn't be altered to user's preference**/
    static final String EMPTY_LABEL_NAME = "";
    /**quote string for string blocks**/
    static final String QUOTE_LABEL = "\"";
    JFrame frame;
   
    /** Whether keyboard support is enabled or not */
    private boolean enabled = false;

    /**
     * TypeBlockManager Constructor
     */
    public TypeBlockManager(Workspace workspace, BlockCanvas component) {
        this.workspace = workspace;
       
        // turned off the automated block placements
        KeyInputMap.enableDefaultKeyMapping(false);
        this.autoCompletePanel = new AutoCompletePanel(workspace);
        this.blockCanvas = component;
        this.focusManager = workspace.getFocusManager();
    }
   
    /**
     * Enables/disables the keyboard support
     * @param enabled
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
        if (enabled) {
            blockCanvas.getCanvas().addMouseListener(focusManager);
            blockCanvas.getCanvas().addKeyListener(focusManager);
            workspace.addWorkspaceListener(focusManager);
        }
        else {
            blockCanvas.getCanvas().removeMouseListener(focusManager);
            blockCanvas.getCanvas().removeKeyListener(focusManager);
            workspace.removeWorkspaceListener(focusManager);
        }
    }
   
    /**
     * Whether keyboard support is enabled or not
     * @return {@code true}/{@code false}
     */
    public boolean isEnabled() {
        return enabled;
    }

    /*----------------------------------------------------------*
     * Convenience Methods                    *
    -----------------------------------------------------------*/
    /**
     * @return true if and only if block is invalid (null or ID==-1)
     */
    private static boolean invalidBlockID(Long blockID) {
        if (blockID == null) {
            return true;
        } else if (blockID.equals(Block.NULL)) {
            return true;
        } else {
            return false;
        }
    }

    private boolean isNullBlockInstance(Long blockID) {
        if (blockID == null) {
            return true;
        } else if (blockID.equals(Block.NULL)) {
            return true;
        } else if (workspace.getEnv().getBlock(blockID) == null) {
            return true;
        } else if (workspace.getEnv().getBlock(blockID).getBlockID() == null) {
            return true;
        } else if (workspace.getEnv().getBlock(blockID).getBlockID().equals(Block.NULL)) {
            return true;
        } else if (workspace.getEnv().getRenderableBlock(blockID) == null) {
            return true;
        } else if (workspace.getEnv().getRenderableBlock(blockID).getBlockID() == null) {
            return true;
        } else if (workspace.getEnv().getRenderableBlock(blockID).getBlockID().equals(Block.NULL)) {
            return true;
        } else {
            return false;
        }
    }

    ///////////////////////
    //Automation Handlers//
    ///////////////////////
    /**
     * @requires the current block with focus must exist with non-null
     *        ID in a non-null widget with a non-null parent
     * @modifies the current block with focus
     * @effects  removes the current block with focus and all
     *        its children from the GUI and destroys the link
     *        between the block with focus and it's parent
     *        block if one exists
     */
    protected void automateBlockDeletion(Workspace workspace) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMateBlockDeletion invoked but typeBlockManager is disabled.");
            return;
        }
        if (!isNullBlockInstance(typeBlockManager.focusManager.getFocusBlockID())) {
            typeBlockManager.deleteBlockAndChildren();
            PageChangeEventManager.notifyListeners();
        }
    }

    /**
     * @requires the current block with focus must exist with non-null
     *        ID in a non-null widget with a non-null parent
     * @modifies the current block with focus
     * @effects  removes the current block with focus and children
     *        from the GUI and destroys the link
     *        between the block with focus and it's parent
     *        block if one exist and children blocks
     *        if it has childrens.
     */
    private void deleteBlockAndChildren() {
//    ====================>>>>>>>>>>>>>>>>>>>>>>>>>
//    ====================focus coming in>>>>>>>>>>TODO
//    ====================>>>>>>>>>>>>>>>>>>>>>>>>>

        //Do not delete null block references.  Otherwise, get Block and RenderableBlock instances.
        if (isNullBlockInstance(focusManager.getFocusBlockID())) {
            throw new RuntimeException("TypeBlockManager: deleting a null block references.");
        }
        Block block = workspace.getEnv().getBlock(focusManager.getFocusBlockID());
        RenderableBlock renderable = workspace.getEnv().getRenderableBlock(block.getBlockID());

        //get workspace widget associated with current focus
        WorkspaceWidget widget = renderable.getParentWidget();
        //do not delete block instances in null widgets
        if (widget == null) {
            throw new RuntimeException("TypeBlockManager: do not delete blocks with no parent widget.");
            //return;
        }
        //get parent container of this graphical representation
        Container container = renderable.getParent();
        //do not delete block instances in null parents
        if (container == null) {
            throw new RuntimeException("TypeBlockManager: do not delete blocks with no parent container.");
            //return;
        }
        //get the Block's location on the canvas
        Point location = SwingUtilities.convertPoint(
                renderable, new Point(0, 0), this.blockCanvas.getCanvas());

        //for every valid and active connection, disconnect it.
        Long parentID = null;
        if (validConnection(block.getPlug())) {
            parentID = block.getPlugBlockID();
            this.disconnectBlock(block, widget);
            if (validConnection(block.getAfterConnector())) {
                disconnectBlock(workspace.getEnv().getBlock(block.getAfterBlockID()), widget);
            }
        } else if (validConnection(block.getBeforeConnector())) {
            parentID = block.getBeforeBlockID();
            BlockConnector parentConnectorToBlock = workspace.getEnv().getBlock(parentID).getConnectorTo(block.getBlockID());
            this.disconnectBlock(block, widget);
            if (validConnection(block.getAfterConnector())) {
                Long afterBlockID = block.getAfterBlockID();
                disconnectBlock(workspace.getEnv().getBlock(afterBlockID), widget);
                if (parentID != null) {
                    BlockLink link = BlockLinkChecker.canLink(
                            workspace,
                            workspace.getEnv().getBlock(parentID),
                            workspace.getEnv().getBlock(afterBlockID),
                            parentConnectorToBlock,
                            workspace.getEnv().getBlock(afterBlockID).getBeforeConnector());
                    if (link != null) {
                        link.connect();
                        workspace.notifyListeners(new WorkspaceEvent(
                                workspace,
                                workspace.getEnv().getRenderableBlock(link.getPlugBlockID()).getParentWidget(),
                                link, WorkspaceEvent.BLOCKS_CONNECTED));
                        workspace.getEnv().getRenderableBlock(link.getPlugBlockID()).repaintBlock();
                        workspace.getEnv().getRenderableBlock(link.getPlugBlockID()).repaint();
                        workspace.getEnv().getRenderableBlock(link.getPlugBlockID()).moveConnectedBlocks();
                        workspace.getEnv().getRenderableBlock(link.getSocketBlockID()).repaintBlock();
                        workspace.getEnv().getRenderableBlock(link.getSocketBlockID()).repaint();

                    }
                }
            }
        } else if (validConnection(block.getAfterConnector())) {
            parentID = block.getAfterBlockID();
        }

        //remove form widget and container
        this.removeChildrenBlock(renderable, widget, container);

//    <<<<<<<<<<<<<<<<<<<<<<<<<<==========================
//    <<<<<<<<<<<<<<<<<<<<<<<<<<focus changing, coming out TODO
//    <<<<<<<<<<<<<<<<<<<<<<<<<<==========================
        //If the deleted block had a parent, give the parent the focus,
        //Otherwise, give the focus to the canvas (NOT BLOCK CANVAS)
        if (invalidBlockID(parentID)) {
            this.focusManager.setFocus(location, Block.NULL);
            this.blockCanvas.getCanvas().requestFocus();
            return;
        } else {
            this.focusManager.setFocus(parentID);
            this.blockCanvas.getCanvas().requestFocus();
            return;
        }
    }

    private void removeChildrenBlock(RenderableBlock renderable, WorkspaceWidget widget, Container container) {
        widget.removeBlock(renderable);
        container.remove(renderable);
        container.validate();
        container.repaint();
        renderable.setParentWidget(null);
        //Workspace.getInstance().notifyListeners(new WorkspaceEvent(widget, renderable.getBlockID(), WorkspaceEvent.BLOCK_REMOVED));
        for (BlockConnector child : workspace.getEnv().getBlock(renderable.getBlockID()).getSockets()) {
            if (child == null || child.getBlockID().equals(Block.NULL)) {
                continue;
            }
            RenderableBlock childRenderable = workspace.getEnv().getRenderableBlock(child.getBlockID());
            if (childRenderable == null) {
                continue;
            }
            removeBlock(childRenderable, widget, container);
        }
        // If it is a procedure block, we want to delete the entire stack
        if (workspace.getEnv().getBlock(renderable.getBlockID()).isProcedureDeclBlock()) {
            if (workspace.getEnv().getBlock(renderable.getBlockID()).getAfterBlockID() != Block.NULL) {
                removeAfterBlock(workspace.getEnv().getRenderableBlock(workspace.getEnv().getBlock(renderable.getBlockID()).getAfterBlockID()),
                        widget, container);
                this.disconnectBlock(workspace.getEnv().getBlock(workspace.getEnv().getBlock(renderable.getBlockID()).getAfterBlockID()), widget);
            }
        }
        if (renderable.hasComment()) {
            renderable.removeComment();
        }
        workspace.notifyListeners(new WorkspaceEvent(workspace, widget, renderable.getBlockID(), WorkspaceEvent.BLOCK_REMOVED));
    }

    /**
     * Helper method that recursively finds and removes all the blocks connected to the bottom of
     * this block, including this block.
     *
     * @param afterBlock - RenderableBlock we start removing at
     * @param widget - WorkspaceWidget that the block is using
     * @param container - Container the block is stored in
     *
     */
    private void removeAfterBlock(RenderableBlock afterBlock, WorkspaceWidget widget, Container container) {
        if (workspace.getEnv().getBlock(afterBlock.getBlockID()).getAfterBlockID() != Block.NULL) {
            removeAfterBlock(workspace.getEnv().getRenderableBlock(workspace.getEnv().getBlock(afterBlock.getBlockID()).getAfterBlockID()),
                    widget, container);
        }
        removeChildrenBlock(afterBlock, widget, container);
    }

    /**
     * Checks if a connection is a valid and ACTIVE connection.
     *
     * @param connection - BlockConnector in question
     *
     * @requires none
     * @return true if and only if connection != null && connection.hasBlock() == true
     */
    private boolean validConnection(BlockConnector connection) {
        if (connection != null) {
            Long blockID = connection.getBlockID();
            if (!isNullBlockInstance(blockID)) {
                if (connection.hasBlock()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     *
     * @param renderable
     * @param widget
     * @param container
     *
     * @requires renderable != null && renderable.blockID != null && renderable.blockID != Block.NULL
     *        && widget != null && container != null
     * @modifies renderable && children blocks connected to renderable
     * @effects removes renderable from container and widget and re-renders
     *       renderable block, widget, and container appropriately.
     *       Repeats for all of renderable's children.
     */
    private void removeBlock(RenderableBlock renderable, WorkspaceWidget widget, Container container) {
        widget.removeBlock(renderable);
        container.remove(renderable);
        container.validate();
        container.repaint();
        renderable.setParentWidget(null);
        //Workspace.getInstance().notifyListeners(new WorkspaceEvent(widget, renderable.getBlockID(), WorkspaceEvent.BLOCK_REMOVED));
        for (BlockConnector child : BlockLinkChecker.getSocketEquivalents(workspace.getEnv().getBlock(renderable.getBlockID()))) {
            if (child == null || child.getBlockID().equals(Block.NULL)) {
                continue;
            }
            RenderableBlock childRenderable = workspace.getEnv().getRenderableBlock(child.getBlockID());
            if (childRenderable == null) {
                continue;
            }
            removeBlock(childRenderable, widget, container);
        }
        if (renderable.hasComment()) {
            renderable.removeComment();
        }
        workspace.notifyListeners(new WorkspaceEvent(workspace, widget, renderable.getBlockID(), WorkspaceEvent.BLOCK_REMOVED));
    }

    /**
     * @param childBlock
     * @param widget
     *
     * @requires widget != null
     * @modifies
     * @effects Does nothing if: childBlock is invalid (null)
     *       Otherwise, remove childBlock from it's parent block
     *       if the childBlock has a parent.  If it does not have
     *       a parent, do nothing.
     */
    private void disconnectBlock(Block childBlock, WorkspaceWidget widget) {
        if (childBlock == null || invalidBlockID(childBlock.getBlockID())) {
            return;
        }
        BlockConnector childPlug = BlockLinkChecker.getPlugEquivalent(childBlock);
        if (childPlug == null || !childPlug.hasBlock() || isNullBlockInstance(childPlug.getBlockID())) {
            return;
        }
        Block parentBlock = workspace.getEnv().getBlock(childPlug.getBlockID());
        BlockConnector parentSocket = parentBlock.getConnectorTo(childBlock.getBlockID());
        if (parentSocket == null) {
            return;
        }
        //disconector if child connector exists and has a block connected to it
        BlockLink link = BlockLink.getBlockLink(workspace, childBlock, parentBlock, childPlug, parentSocket);
        if (link == null) {
            return;
        }

        link.disconnect();

        RenderableBlock parentRenderable = workspace.getEnv().getRenderableBlock(parentBlock.getBlockID());
        if (parentRenderable == null) {
            throw new RuntimeException("INCONSISTANCY VIOLATION: "
                    + "parent block was valid, non-null, and existed.\n\tBut yet, when we get it's renderable"
                    + "representation, we recieve a null instance.\n\tIf the Block instance of an ID is non-null"
                    + "then its graphical RenderableBlock should be non-null as well");
        }
        parentRenderable.blockDisconnected(parentSocket);
        workspace.notifyListeners(new WorkspaceEvent(workspace, widget, link, WorkspaceEvent.BLOCKS_DISCONNECTED));
    }
    /**
     * @requires none
     * @modifies bufferedBlock (the block that is copied)
     * @effects change bufferedBlock such that it points
     *       to the block with current focus
     */
    private BlockNode bufferedBlock = null;

    public static void copyBlock(Workspace workspace) {
        TypeBlockManager.automateCopyBlock(workspace);
    }

    public static void pasteBlock(Workspace workspace) {
        TypeBlockManager.automatePasteBlock(workspace);
    }

    protected static void automateCopyBlock(Workspace workspace) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMateCopyBlock invoked but typeBlockManager is disabled.");
            return;
        }
        typeBlockManager.bufferedBlock =
                BlockUtilities.makeNodeWithChildren(workspace, typeBlockManager.focusManager.getFocusBlockID());
    }

    protected static void automateCopyAll(Workspace workspace) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMatePasteBlock invoked but typeBlockManager is disabled.");
            return;
        }
        typeBlockManager.bufferedBlock =
                BlockUtilities.makeNodeWithStack(workspace, typeBlockManager.focusManager.getFocusBlockID());
    }

    /**
     * @param workspace
     * @requires whatever is requires for AutomatedBlockInsertion
     *
     */
    protected static void automatePasteBlock(Workspace workspace) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMatePasteBlock invoked but typeBlockManager is disabled.");
            return;
        }

        typeBlockManager.pasteStack(typeBlockManager.bufferedBlock);
    }

    private void pasteStack(BlockNode node) {
//    ====================>>>>>>>>>>>>>>>>>>>>>>>>>
//    ====================focus coming in>>>>>>>>>> TODO
//    ====================>>>>>>>>>>>>>>>>>>>>>>>>>
        if (node == null) {
            return;
        }
        WorkspaceWidget widget = null;
        Iterable<WorkspaceWidget> widgets = null;
        Point spot = null;
        if (invalidBlockID(focusManager.getFocusBlockID())) {
            //canvas has focus
            Point location = SwingUtilities.convertPoint(
                    this.blockCanvas.getCanvas(),
                    this.focusManager.getCanvasPoint(),
                    workspace);
            widget = workspace.getWidgetAt(location);
            spot = SwingUtilities.convertPoint(
                    this.blockCanvas.getCanvas(),
                    this.focusManager.getCanvasPoint(),
                    widget.getJComponent());
        } else {
            RenderableBlock focusRenderable = workspace.getEnv().getRenderableBlock(focusManager.getFocusBlockID());
            widget = focusRenderable.getParentWidget();
            spot = focusRenderable.getLocation();
        }

        if (widget == null) {
            // TODO: To be examined and fixed, occurs on macs
            JOptionPane.showMessageDialog(frame, "Please click somewhere on the canvas first.",
                    "Error", JOptionPane.PLAIN_MESSAGE);
            //throw new RuntimeException("Why are we adding a block to a null widget?");
        } else {
            // checks to see if the copied block still exists
            if (BlockUtilities.blockExists(workspace, node)) {
                //create mirror block and mirror childrens
                spot.translate(10, 10);
                RenderableBlock mirror = BlockUtilities.makeRenderable(workspace, node, widget);
                mirror.setLocation(spot);
                mirror.moveConnectedBlocks(); // make sure the childrens are placed correctly
            } else {
                //TODO: future version, allow them to paste
                JOptionPane.showMessageDialog(frame, "You cannot paste blocks that are currently NOT on the canvas."
                        + "\nThis function will be available in a future version.\n", "Error", JOptionPane.PLAIN_MESSAGE);
            }

        }
    }

    /**
     * Traverses the block tree structure to move
     * in the direction of the input argument.
     * @param workspace
     * @param dir
     */
    protected static void automateFocusTraversal(Workspace workspace, Direction dir) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMateFocusTraversal invoked but typeBlockManager is disabled.");
            return;
        }
        typeBlockManager.traverseFocus(dir);
    }

    private void traverseFocus(Direction dir) {
        if (isNullBlockInstance(focusManager.getFocusBlockID())) {
            if (dir == Direction.UP) {
                blockCanvas.getVerticalModel().setValue(blockCanvas.getVerticalModel().getValue() - 5);
            } else if (dir == Direction.DOWN) {
                blockCanvas.getVerticalModel().setValue(blockCanvas.getVerticalModel().getValue() + 5);
            } else if (dir == Direction.LEFT) {
                blockCanvas.getHorizontalModel().setValue(blockCanvas.getHorizontalModel().getValue() - 5);
            } else if (dir == Direction.RIGHT) {
                blockCanvas.getHorizontalModel().setValue(blockCanvas.getHorizontalModel().getValue() + 5);
            } else if (dir == Direction.ESCAPE) {
                //according to the focus manager, the canvas already
                //has focus. So, just request focus again.
                this.blockCanvas.getCanvas().requestFocus();
            } else if (dir == Direction.ENTER) {
            }
        } else {
            if (dir == Direction.UP) {
                focusManager.focusBeforeBlock();
            } else if (dir == Direction.DOWN) {
                focusManager.focusAfterBlock();
            } else if (dir == Direction.LEFT) {
                focusManager.focusPrevBlock();
            } else if (dir == Direction.RIGHT) {
                focusManager.focusNextBlock();
            } else if (dir == Direction.ESCAPE) {
                RenderableBlock block = workspace.getEnv().getRenderableBlock(
                        focusManager.getFocusBlockID());
                Point location = SwingUtilities.convertPoint(block, new Point(0, 0), this.blockCanvas.getCanvas());
                this.focusManager.setFocus(location, Block.NULL);
                this.blockCanvas.getCanvas().requestFocus();
            } else if (dir == Direction.ENTER) {
              workspace.getEnv().getRenderableBlock(focusManager.getFocusBlockID()).switchToLabelEditingMode(true);
            }
        }
    }

    /**
     * Displays an assisting AutoCompletePanel.
     * @param workspace
     * @param character
     */
    protected void automateAutoComplete(Workspace workspace, char character) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMateAutoComplete invoked but typeBlockManager is disabled.");
            return;
        }
        typeBlockManager.displayAutoCompletePanel(character);
    }

    /**
     * @requires this.blockCanvas.getCanvas() != null
     * @param character
     */
    private void displayAutoCompletePanel(char character) {
//    ====================>>>>>>>>>>>>>>>>>>>>>>>>>
//    ====================focus coming in>>>>>>>>>> TODO
//    ====================>>>>>>>>>>>>>>>>>>>>>>>>>
        if (invalidBlockID(focusManager.getFocusBlockID())) {
            //canvas has focus
            this.blockCanvas.getCanvas().add(autoCompletePanel, JLayeredPane.DRAG_LAYER);
            autoCompletePanel.setLocation(this.focusManager.getCanvasPoint());
            autoCompletePanel.setVisible(true);
            autoCompletePanel.requestFocus();
        } else {
            //renderableblock has focus
            this.blockCanvas.getCanvas().add(autoCompletePanel, JLayeredPane.DRAG_LAYER);
            RenderableBlock block = workspace.getEnv().getRenderableBlock(focusManager.getFocusBlockID());
            Point location = SwingUtilities.convertPoint(
                    block,
                    this.focusManager.getBlockPoint(),
                    this.blockCanvas.getCanvas());
            location.translate(10, 10);
            autoCompletePanel.setLocation(location);
            autoCompletePanel.setVisible(true);
            autoCompletePanel.requestFocus();
        }
        autoCompletePanel.setText(String.valueOf(character));
    }

    /**
     * assumes number and differen genus exist and number genus has ediitabel lable
     */
    protected void automateNegationInsertion(Workspace workspace) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMateNegationInsertion invoked but typeBlockManager is disabled.");
            return;
        }

//    ====================>>>>>>>>>>>>>>>>>>>>>>>>>
//    ====================focus coming in>>>>>>>>>> TODO
//    ====================>>>>>>>>>>>>>>>>>>>>>>>>>

        //get focus block
        Long parentBlockID = typeBlockManager.focusManager.getFocusBlockID();
        if (isNullBlockInstance(parentBlockID)) {
            //focus on canvas
            automateBlockInsertion(workspace, "number", "-");

        } else {
            Block parentBlock = workspace.getEnv().getBlock(parentBlockID);
            if (parentBlock.isDataBlock()) {
                //focus on a data block
                automateBlockInsertion(workspace, "difference", null);
            } else {
                //focus on a non-data block
                automateBlockInsertion(workspace, "number", "-");
            }
        }
    }

    protected void automateMultiplication(Workspace workspace, char character) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMateMultiplication invoked but typeBlockManager is disabled.");
            return;
        }
        if (!isNullBlockInstance(typeBlockManager.focusManager.getFocusBlockID())) {
            Block parentBlock = workspace.getEnv().getBlock(typeBlockManager.focusManager.getFocusBlockID());
            if (parentBlock.getGenusName().equals("number")) {
                automateBlockInsertion(workspace, "product", null);
                return;
            }
        }
        automateAutoComplete(workspace, character);
        return;
    }

    protected void automateAddition(Workspace workspace, char character) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMateMultiplication invoked but typeBlockManager is disabled.");
            return;
        }
        //get focus block
        Long parentBlockID = typeBlockManager.focusManager.getFocusBlockID();
        if (isNullBlockInstance(parentBlockID)) {
            //focus on canvas
            automateBlockInsertion(workspace, "sum", null);
        } else {
            Block parentBlock = workspace.getEnv().getBlock(parentBlockID);
            if (parentBlock.getGenusName().equals("string")) {
                //focus on string block
                automateBlockInsertion(workspace, "string-append", null);
            } else if (parentBlock.getGenusName().equals("string-append")) {
                //focus on string append block
                automateBlockInsertion(workspace, "string-append", null);
            } else {
                //focus on any other block
                automateBlockInsertion(workspace, "sum", null);
            }
        }
    }

    /**
     * @param workspace The workspace in use
     * @param genusName
     * @param label
     *
     * @requires if (label != null) then associated block.isLabelEditable() should return true
     * @modifies   focusManager.focusblock &&
     *         focusManager.focuspoint &&
     *         blockCanvas
     * @effects Do nothing if "genusName" does not map to a valid block.
     *       Otherwise, create and add a new block with matching genus
     *       and label properties to one of the following:
     *         1. the current block with focus at (0,0)
     *            relative to that block.
     *         2. the current block with focus at next
     *            applicable socket location
     *         3. the canvas at the last mouse click point.
     *       Then update any focus and block connections.
     */
    protected void automateBlockInsertion(Workspace workspace, String genusName, String label) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMateBlockInsertion invoked but typeBlockManager is disabled.");
            return;
        }
        //if genus is null, DO NOT insert a new block, DO NOT change the focus
        if (genusName == null) {
            return;
        }
        //get matching textual Block
        RenderableBlock createdRB = BlockUtilities.getBlock(workspace, genusName, null);
        if (createdRB == null) {
            return;
        } else {
            //change name of block IF AN DONLY IFF a label was passed
            //and the block's label was editable and the block
            //does not need to have a unique label
            if (label != null && workspace.getEnv().getBlock(createdRB.getBlockID()).isLabelEditable() && !workspace.getEnv().getBlock(createdRB.getBlockID()).labelMustBeUnique()) {
              workspace.getEnv().getBlock(createdRB.getBlockID()).setBlockLabel(label);
            }
            //add block
            typeBlockManager.addBlock(createdRB);
        }
    }

    /**
     * @requires none
     * @modifies   focusManager.focusblock &&
     *         focusManager.focuspoint &&
     *         blockCanvas
     * @effects Do nothing if "genusName" does not map to a valid block.
     *       Otherwise, create and add a new block with matching genus
     *       and label properties to one of the following:
     *         1. the current block with focus at (0,0)
     *            relative to that block.
     *         2. the current block with focus at next
     *            applicable socket location
     *         3. the canvas at the last mouse click point.
     *       Then update any focus and block connections.
     */
    protected void automateBlockInsertion(Workspace workspace, TextualFactoryBlock block) {
        /*Passing in an empty label name means that the block should already have
        a predetermined label name that does not need to be altered to the user's preference*/
        automateBlockInsertion(workspace, block, EMPTY_LABEL_NAME);
    }

    /**
     * @requires none
     * @modifies   focusManager.focusblock &&
     *         focusManager.focuspoint &&
     *         blockCanvas
     * @effects Do nothing if "genusName" does not map to a valid block.
     *       Otherwise, create and add a new block with matching genus
     *       and label properties to one of the following:
     *         1. the current block with focus at (0,0)
     *            relative to that block.
     *         2. the current block with focus at next
     *            applicable socket location
     *         3. the canvas at the last mouse click point.
     *       If label is not an empty string, then set the block label
     *       to that string.
     *       Then update any focus and block connections.
     */
    protected void automateBlockInsertion(Workspace workspace, TextualFactoryBlock block, String label) {
        TypeBlockManager typeBlockManager = workspace.getTypeBlockManager();
        if (!typeBlockManager.isEnabled()) {
            System.err.println("AutoMateBlockInsertion invoked but typeBlockManager is disabled.");
            return;
        }
        RenderableBlock createdRB = createRenderableBlock(block);
        // sets the label of the block to whatever the user typed (should only be numbers)
        if (label != EMPTY_LABEL_NAME) {
            createdRB.getBlock().setBlockLabel(label);
        }
        // changes the plus number labels back to +
        if (label.equals(NUMBER_PLUS_OPERATION_LABEL)) {
            createdRB.getBlock().setBlockLabel(PLUS_OPERATION_LABEL);
        }
        // changes the plus text labels back to +
        if (label.equals(TEXT_PLUS_OPERATION_LABEL)) {
            createdRB.getBlock().setBlockLabel(PLUS_OPERATION_LABEL);
        }
        if (createdRB == null) {
            return;
        } else {
            typeBlockManager.addBlock(createdRB);
        }
    }

    /**
     * @param block - the textual block from which a new RenderableBlock will be constructed
     *
     * @requires
     * @modifies nothing
     * @effects none
     * @return new RenderableBlock instance from the TextualFactoryBlock
     *        or null if not possible.
     */
    private RenderableBlock createRenderableBlock(TextualFactoryBlock block) {
        //if textual wrapper is null, return a null instance of RenderableBlock.
        if (block == null) {
            return null;
        }
        //if FactoryBlock wrapped in textual wrapper is invalid, return null RenderableBlock instance.
        if (block.getfactoryBlock() == null || block.getfactoryBlock().getBlockID().equals(Block.NULL)) {
            return null;
        }
        //create and get the RenderableBloc instance associated with the Textual wrapper's FactoryBlock
        RenderableBlock createdRB = block.getfactoryBlock().createNewInstance();
        //if the above instance of RenderableBlock is invalid (null or points to null)
        //then DO NOT insert a new block, DO NOT change the focus.
        if (createdRB == null || isNullBlockInstance(createdRB.getBlockID())) {
            throw new RuntimeException("Invariant Violated:"
                    + "May not drop null instances of Renderable Blocks");
        }
        //Please keep the above check rep because it does not
        //make any sense to have an exisitn valid
        //FactoryRenderableBlock point to some non-existing
        //block.  In other words, why would you have a factory
        //that churns out invalid products?
        return createdRB;
    }

    /**
     * @param block
     *
     * @requires   block must be a valid block.  That is, block may not be such that
     *         block == null || block.getBlockID() == null ||
     *        block.getBlockID() == Block.NULL || block.getBlockID() == -1 ||
     *        Block.getBlock(block.getBlockID()) == null ||
     *        Block.getBlock(block.getBlockID()).getGenusName() == null ||
     *        Block.getBlock(block.getBlockID()).getGenusName().length() == 0 ||
     *        Block.getBlock(block.getBlockID()).getBlockLabel() == null
     * @modifies Objects modified by this method is undefined
     * @effects The effects of this method is unknown
     */
    private void addBlock(RenderableBlock block) {
        //check invariant
        if (block == null || block.getBlockID() == null
                || block.getBlockID().equals(Block.NULL)
                || workspace.getEnv().getBlock(block.getBlockID()) == null
                || workspace.getEnv().getBlock(block.getBlockID()).getGenusName() == null
                || workspace.getEnv().getBlock(block.getBlockID()).getGenusName().length() == 0
                || workspace.getEnv().getBlock(block.getBlockID()).getBlockLabel() == null) {
            throw new RuntimeException("Invariant Violated: may not pass an invalid instance of renderabel block");
        }

        //ignore default arguments
        block.ignoreDefaultArguments();
        this.blockCanvas.getCanvas().add(block, 0);
        block.setLocation(0, 0);
        Long parentBlockID = this.focusManager.getFocusBlockID();
        if (invalidBlockID(parentBlockID)) {
            new BlockDropAnimator(
                    workspace,
                    this.focusManager.getCanvasPoint(),
                    block,
                    workspace.getEnv().getRenderableBlock(parentBlockID));
        } else {
            RenderableBlock parentBlock = workspace.getEnv().getRenderableBlock(parentBlockID);
            new BlockDropAnimator(
                    workspace,
                    SwingUtilities.convertPoint(parentBlock,
                    this.focusManager.getBlockPoint(),
                    this.blockCanvas.getCanvas()),
                    block,
                    workspace.getEnv().getRenderableBlock(parentBlockID));
        }
        this.focusManager.setFocus(block.getBlockID());
    }
}
TOP

Related Classes of edu.mit.blocks.workspace.typeblocking.TypeBlockManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.