Package ketUI.panel

Source Code of ketUI.panel.KetPanel

/*
* Copyright (C) 2011  Alasdair C. Hamilton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package ketUI.panel;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.*;
import javax.swing.JPanel;

import geom.Offset;
import geom.Position;
import ket.*;
import ket.display.*;
import ket.display.box.Box;
import ket.display.box.BoxList;
import ket.display.box.BoxText;
import ket.display.box.BoxTools;
import ket.display.box.BoxWord;
import ket.math.*;
import ket.math.purpose.Word;
import ketUI.*;
import ketUI.Document;
import ketUI.Ket;
import ketUI.MouseEventHandler;
import ketUI.chord.Chord;
import ketUI.chord.KeyboardEventHandler;
import ketUI.modes.*;
import ketUI.responder.*;

import ketUI.Ket; // NO!

/**
* The user interface panel consists of a series of box instances that each represent
* an equation or other user interface component such as a label, message or
* similar.
*/
public class KetPanel extends JPanel {

  static final boolean DEBUG = false;

  /**
   * An estimate of half the number of equations currently visible on the
   * screen used to determine the number of equations to move up/down
   * when page-up/page-down is pressed.
   */
  public static final int EQUATION_STEP_SIZE = 5;

  Graphics2D oldGraphics = null; // HACK

  public static final int LEFT_BORDER_SIZE = 15; // pixels
  public static final int TOP_BORDER_SIZE = 5; // pixels
  public static final int BOTTOM_BORDER_SIZE = 5; // pixels
  public static final int RIGHT_BORDER_SIZE = 5; // pixels

  public static final Position TOP_LEFT_BORDER = new Position(LEFT_BORDER_SIZE, TOP_BORDER_SIZE);
  public static final Offset BORDER_OFFSET = new Offset(
      LEFT_BORDER_SIZE+RIGHT_BORDER_SIZE,
      TOP_BORDER_SIZE+BOTTOM_BORDER_SIZE);


  final Document document;

  final MouseEventHandler mouseEventHandler;

  final PanelDecoration panelDecoration;

  final Vector<UpdateListener> listeners;

  final ScatterDisplay scatterDisplay;
  final AnimatedDisplay animatedDisplay;
  final GraphDisplay graphDisplay;
  final GridDisplay gridDisplay;
  final HistoryDisplay historyDisplay;
  final ListDisplay listDisplay;
  final NetworkDisplay networkDisplay;
  final PerspectiveDisplay perspectiveDisplay;

  Display display;

  /**
   * When animating, keep a record of the previous state before editing from which to transition. 
   */
  Argument before;
  Equation beforeEquation;
  boolean singleSelection;

  Boolean showBackground;
  boolean retainVisible;

  Position dragPosition;
  BufferedImage icon;

  public KetPanel(Document document) {
    this.document = document;

    ResizeListener resizeListener = new ResizeListener(this);
    this.addComponentListener(resizeListener);
   
    mouseEventHandler = new MouseEventHandler(document);
    this.addMouseListener(mouseEventHandler);
    if (MouseEventHandler.MOUSE_MOTION) {
      this.addMouseMotionListener(mouseEventHandler);
    }

    this.panelDecoration = new PanelDecoration(document); //! Moved above **
    setFocusable(true);

    MathCollection mathCollection = document.getMathCollection();
    this.scatterDisplay = new ScatterDisplay(mathCollection, panelDecoration);
    this.listDisplay = new ListDisplay(mathCollection);
    this.gridDisplay = new GridDisplay(mathCollection);
    this.animatedDisplay = new AnimatedDisplay(mathCollection);
    this.networkDisplay = new NetworkDisplay(mathCollection);
    this.graphDisplay = new GraphDisplay(document, mathCollection); // document is only used for font size: de-coupling.
    this.historyDisplay = new HistoryDisplay(mathCollection);
    this.perspectiveDisplay = new PerspectiveDisplay(mathCollection, mouseEventHandler);

    display = listDisplay;

    // **

    beforeEquation = null;
    before = null;
    singleSelection = true;

    // Unless set, default to the colourscheme's default choise.
    showBackground = null;
    retainVisible = false;

    listeners = new Vector<UpdateListener>();
  }

  /**
   * Unless explicitly set, the background is shown when specified in the colourscheme's settings file.
   */
  public void toggleShowBackground() {
    if (showBackground==null) {
      showBackground = !getShowBackground();
    } else {
      showBackground = !showBackground;
    }
  }

  public boolean getShowBackground() {
    if (showBackground==null) {
      return getColourScheme().getShowBackground();
    } else {
      return showBackground;
    }
  }


  public void setHold(boolean hold) {
    mouseEventHandler.setHold(hold);
  }

  public BoxList getMessageBoxList() {
    return panelDecoration.getMessageBoxList(); // TODO: Simplify?
  }

  public void setVisibleEquations(Vector<Equation> equations) {
    listDisplay.setVisibleEquations(equations); //?
  }

  public Vector<Equation> getVisibleEquations() {
    return listDisplay.getVisibleEquations(); //?
  }

  public Modes getModes() {
    return document.getModes();
  }

  public void moveViewDown() {
    if (display==listDisplay) {
      listDisplay.moveViewDown(); //?
    } else if (display==gridDisplay) {
      gridDisplay.moveViewDown();
    }
    setRetainVisible(); // TODO: Should this be here or in normalResponder.  If this is called within another call it may affect retainVisible.
  }
  public void moveViewUp() {
    if (display==listDisplay) {
      listDisplay.moveViewUp();
    } else if (display==gridDisplay) {
      gridDisplay.moveViewUp();
    }
    setRetainVisible(); // TODO: Should this be here or in normalResponder.  If this is called within another call it may affect retainVisible.
  }

  public void setRetainVisible() {
    retainVisible = true;
  }

  public void ensureSelectionVisibility() {
    if (retainVisible) {
      retainVisible = false;
      listDisplay.selectByVisible(document.getCursor(), document.getEquationList());
      gridDisplay.selectByVisible(document.getCursor(), document.getEquationList());
      scatterDisplay.selectByVisible(document.getCursor(), document.getEquationList()); //[new]
      updateAndRepaint(); // <--- !!! Drawn twice !!!
    } else {
      Equation equation = document.getCursor().getEquation();
      int index = equation.getEquationIndex();
      viewEquation(index);
    }
  }

  public void moveViewPageUp() {
    for (int i=0; i<EQUATION_STEP_SIZE; i++) {
      moveViewUp();
    }
  }
  public void moveViewPageDown() {
    for (int i=0; i<EQUATION_STEP_SIZE; i++) {
      moveViewDown();
    }
  }
  public void moveViewHalfPageDown() {
    for (int i=0; i<EQUATION_STEP_SIZE/2; i++) {
      moveViewDown();
    }
  }
  public void moveViewHalfPageUp() {
    for (int i=0; i<EQUATION_STEP_SIZE/2; i++) {
      moveViewUp();
    }
  }

  public PanelDecoration getPanelDecoration() {
    return panelDecoration;
  }

  public Equation pointToEquation(Position p) {
    return display.pointToEquation(p);
  }

  public Box findDeepestBox(Position p) {
    return display.findDeepestBox(p);
  }

  public Argument findDeepestArgument(Position p) {
    return display.findDeepestArgument(p);
  }

  public void fromTop() {
    fromTop(document.getCursor().getEquationIndex());
  }
  public void fromMiddle() {
    fromMiddle(document.getCursor().getEquationIndex());
  }
  public void fromBottom() {
    fromBottom(document.getCursor().getEquationIndex());
  }

  public void fromTop(int initialBoxIndex) {
    //                                                                      <---------------------
    // Why do they all keep a separate copy of super.topIndex, ... and why update animated but not network or graph display?
    listDisplay.fromTop(initialBoxIndex);
    animatedDisplay.fromTop();
    gridDisplay.viewEquation(initialBoxIndex);
    perspectiveDisplay.fromTop();
  }
  public void fromMiddle(int middleBoxIndex) {
    listDisplay.fromMiddle(middleBoxIndex);
    animatedDisplay.fromMiddle();
    gridDisplay.viewEquation(middleBoxIndex)//ish
    perspectiveDisplay.fromMiddle();
  }
  public void fromBottom(int bottomBoxIndex) {
    listDisplay.fromBottom(bottomBoxIndex);
    animatedDisplay.fromBottom();
    gridDisplay.viewEquation(bottomBoxIndex)//ish
    perspectiveDisplay.fromBottom();
  }

  public void updateForAnimatedDisplay() {
    Equation afterEquation = getCurrentEquation();
    // TODO: Animate undo and redo?
    /*-T
    Ket.out.println("before = " + before);
    Ket.out.println("beforeEquation = " + beforeEquation);
    Ket.out.println("afterEquation = " + afterEquation);
    Ket.out.println(" * old root = " + cloneOldRoot(beforeEquation));
    Ket.out.println(" * old root' = " + cloneOldRoot(afterEquation));
    */
    if (before!=null &&  (singleSelection && isSingleSelectionSet()) ) {
   
      // To avoid jumps during edits [why?] a more
      // fine-grained record is kept of the previous state
      // than that kept in the undo stack.
      if (beforeEquation==afterEquation) {
        display.noteChange(
            oldGraphics,
            getColourScheme(),
            before,
            afterEquation,
            getFontSize(),
            getPanelRectangle());
      } else {
        Argument oldRoot = cloneOldRoot(afterEquation);
        if (oldRoot!=null) {
          display.noteChange(
              oldGraphics,
              getColourScheme(),
              oldRoot,
              afterEquation,
              getFontSize(),
              getPanelRectangle());
        }
      }
    }
    beforeEquation = afterEquation;
    before = Argument.cloneArgument(afterEquation.getRoot());
    singleSelection = isSingleSelectionSet();
  }

  private boolean isSingleSelectionSet() {
    return document.getMathCollection().isSingleSelectionSet();
 


  /**
   * Look in the undo stack for the last 'save' state and return a clone of the root of the equation of the given index.
   */
  private Argument cloneOldRoot(Equation e) {
    if (e==null) return null;
    Undo undo = document.getMathCollection().getUndo();
    int equationIndex = e.getEquationIndex();
    return undo.cloneOldRoot(equationIndex);
  }

  private Equation getCurrentEquation() {
    return document.getCursor().getEquation();
  }

  /**
   * This is called in response to mouse events (including scrolls), key
   * events, clearing the screen (why?) opening and saving files (why?).
   */
  public void updateAndRepaint() {
    mark = null;
    //-? deepest = null; //?
    //-? clearDragIcon(); //?
    panelDecoration.update();
    ColourScheme colourScheme = getColourScheme();
    boolean ok = display.generateBoxes(oldGraphics, colourScheme, getFontSize(), getPanelRectangle());
    repaint();
    for (UpdateListener l : listeners) {
      l.updateEvent(document);
    }
  }

  public void addUpdateListener(UpdateListener l) {
    listeners.add(l);
  }

  public void removeUpdateListener(UpdateListener l) {
    listeners.remove(l);
  }

  public void paint(Graphics g) {
    Graphics2D g2D = (Graphics2D) g;
    oldGraphics = g2D; //?

    ImageTools.setHints(g2D);

    paintBackground(g2D);
    if (Box.displayBorders) {
      g2D.setColor(new Color(255, 128, 255));
      // Note: top, left, width, height
      g2D.drawRect(4, 4, this.getWidth()-8, this.getHeight()-8);
    }
    display.paint(g2D, getColourScheme(), getFontSize(), getPanelRectangle());
    Offset drawRectangle = getDrawRectangle();
    panelDecoration.paint(g2D, drawRectangle, getColourScheme());
    if (DEBUG) {
      mouseEventHandler.getMouseLoop().paint(g2D);
      repaint();
      //- return;
    }
    if (icon!=null) { // TODO: Clean up:
      if (dragPosition!=null) {
        g2D.drawImage(icon, (int) dragPosition.x-icon.getWidth()/2, (int) dragPosition.y-icon.getHeight()/2, null);
        g2D.drawOval((int) dragPosition.x-3, (int) dragPosition.y-3, 5, 5); // D
      }
      Position topLeft = deepest!=null ? deepest.getTopLeft() : null;
      if (topLeft!=null) {
        //- deepest.drawBorder(g2D, topLeft, getColourScheme());
        deepest.drawInnerBorder(g2D, topLeft, getColourScheme(), true);
      }
      repaint();
    } else if (mark!=null) {
      Position topLeft = mark!=null ? mark.getTopLeft() : null;
      if (topLeft!=null) {

        mark.drawInnerBorder(g2D, topLeft, getColourScheme(), highlightMark());
      }
      repaint();
     } else if (display.requiresRepaint()) {
      repaint();
    }
  }

  private boolean highlightMark() {
    // TODO: Clean
    if (mark==null) return false;
    Argument a = mark.getArgument();
    if (a==null) return false;
    Argument c = getCurrent();
    if (a==c) return true;
    //- if (a.hasAncestor(c)) return true;
    //- return false;
    boolean highlight = false;
    for (Branch b : a.getAncestors()) {
      if (b==c) return true;
    }
    return highlight;
  }

  private Offset getDrawRectangle() {
    double drawWidth = this.getWidth() - BORDER_OFFSET.width;
    double drawHeight = this.getHeight() - BORDER_OFFSET.height;
    return new Offset(drawWidth, drawHeight);
  }

  private void paintBackground(Graphics2D g2D) {
    int width = this.getWidth();
    int height = this.getHeight();
    if (getShowBackground()) {
      // Draw a background radial gradient pattern.
      float cx = width / 2.0f;
      float cy = height / 2.0f;
      float r = 8.0f * (float) Math.sqrt(cx*cx + cy*cy);
      float[] keyFrames = new float[]{0.0f, 1.0f};
     
      //- Color centreColour = getColourScheme().getBackgroundColour();
      //- Color plainTextColour = getColourScheme().getPlainTextColour();
      //- Color[] colours = new Color[]{centreColour, plainTextColour};
      Color[] colours = new Color[]{getColourScheme().getGradientFrom(), getColourScheme().getGradientTo()};
      RadialGradientPaint rgp = new RadialGradientPaint(cx, cy, r, keyFrames, colours);
      g2D.setPaint(rgp);
    } else {
      //- Color centreColour = getColourScheme().getBackgroundColour();
      //- g2D.setPaint(centreColour);
      g2D.setPaint(getColourScheme().getBackgroundColour());
    }
    g2D.fillRect(0, 0, width, height);
  }

  public Position getCentre() {
    Point p = getLocationOnScreen();
    return new Position(p.x + getWidth()/2, p.y + getHeight()/2);
  }

  public Position getTopLeft() {
    Point p = getLocationOnScreen();
    return new Position(p.x, p.y);
  }

  public boolean toggleListDisplay() {
    display = isListDisplayMode() ? animatedDisplay : listDisplay; //? <default> and listDisplay
    return isListDisplayMode();
  }

  public boolean toggleScatterDisplay() {
    display = isScatterDisplayMode() ? listDisplay : scatterDisplay;
    scatterDisplay.clear();
    return isScatterDisplayMode();
  }

  public boolean toggleNetworkDisplay() {
    display = isNetworkDisplayMode() ? listDisplay : networkDisplay;
    return isNetworkDisplayMode();
  }

  public boolean toggleGraphDisplay() {
    display = isGraphDisplayMode() ? listDisplay : graphDisplay;
    return isGraphDisplayMode();
  }

  public boolean toggleHistoryDisplay() {
    display = isHistoryDisplayMode() ? listDisplay : historyDisplay;
    return isHistoryDisplayMode();
  }

  public boolean toggleAnimatedDisplay() {
    display = isAnimatedDisplayMode() ? listDisplay : animatedDisplay;
    return isAnimatedDisplayMode();
  }

  public boolean toggleGridDisplay() {
    if (isGridDisplayMode()) {
      display = listDisplay;
    } else {
      display = gridDisplay;
      gridDisplay.viewEquation(0);
    }
    return isGridDisplayMode();
  }

  public boolean togglePerspectiveDisplay() {
    display = isPerspectiveDisplayMode() ? listDisplay : perspectiveDisplay;
    return isPerspectiveDisplayMode();
  }


  /**
   * Cycle between an animated and a list display mode, returning true if
   * more than one equation is displayed.
   */
  public void cygleDisplayMode() {
    if (display==listDisplay) {
      display = networkDisplay;
    } else if (display==networkDisplay) {
      display = graphDisplay;
    } else if (display==graphDisplay) {
      display = animatedDisplay;
    } else if (display==animatedDisplay) {
      display = gridDisplay;
      gridDisplay.viewEquation(0);
    } else if (display==gridDisplay) {
      display = listDisplay;
    }
    document.getFrameManager().getMenuEventHandler().updateMultipleEquations();
  }

  public Vector<Integer> findVisibleEquationIndices() {
    return display.findVisibleEquationIndices()
  }

  public boolean isNetworkDisplayMode() {
    return display instanceof NetworkDisplay;
  }
  public boolean isGraphDisplayMode() {
    return display instanceof GraphDisplay;
  }
  public boolean isHistoryDisplayMode() {
    return display instanceof HistoryDisplay;
  }
  public boolean isAnimatedDisplayMode() {
    return display instanceof AnimatedDisplay;
  }
  public boolean isListDisplayMode() {
    return display instanceof ListDisplay;
  }
  public boolean isPerspectiveDisplayMode() {
    return display instanceof PerspectiveDisplay;
  }
  public boolean isScatterDisplayMode() {
    return display instanceof ScatterDisplay;
  }
  public boolean isGridDisplayMode() {
    return display==gridDisplay;
  }

  public Display getDisplay() {
    return display;
  }

  // Used by mouse event handler
  public Vector<Box> getVisibleBoxes() {
    // TODO: Implement based on mathCollection.getSelection().
    //    getEquationList().getEquations().subList(visible-box-range);
    Ket.out.println(" !!! ketPanel.getVisibleBoxes():: Not implemented !!! ");
    return null;
  }

  public boolean isArgumentVisible(Argument argument) {
    return display.isArgumentVisible(argument);
  }

  public void viewEquation(int index) {
    listDisplay.viewEquation(index);
  }
 
  /**
   * This highlights only a particular argument when it is exactly
   * clicked on.  If no argument is clicked on, regardless if you click
   * on either an equation's background or equation number, the selection
   * does not change.
   */
  public void selectArgument(Position p) {
    Argument argument = findDeepestArgument(p);
    if (argument!=null) {
      document.getSelection().setOnly(argument);
    }
  }

  public AnimatedDisplay getAnimatedDisplay() {
    return animatedDisplay;
  }

  public Document getDocument() {
    return document;
  }

  private int getFontSize() {
    return document.getBoxFontSize();
  }

  public Offset getPanelRectangle() {
    return new Offset(this.getWidth(), this.getHeight());
  }

  private ColourScheme getColourScheme() {
    return document.getColourScheme();
  }

  public void toggleScatter() {
    scatterDisplay.toggleScatter();
  }

  /**
   * Drag an equation (current) to a given position (within scatter display).
   */
  public void dragTo(Equation e, Position p) {
    assert display==scatterDisplay;
    scatterDisplay.dragTo(e, p);
  }

  public void processPairs() {
    if (isAnimatedDisplayMode()) {
      animatedDisplay.processPairs();
    }
  }

  public void setDragLocation(Position dragPosition) {
    this.dragPosition = dragPosition;
    // Find the deepest box and highlight it.
    deepest = findDeepestBox(dragPosition);
  }

  public void clearDragIcon() {
    dragPosition = null;
    icon = null;
  }

  public void updateDragIcon(Position dragPosition) {
    //- this.dragPosition = dragPosition;
    Box box = getCurrent().toBox(0L, getColourScheme());
    box.setupInnerRectangle(getFontSize());
    Offset innerRectangle = box.getInnerRectangle();
    int w = (int) Math.max(10, innerRectangle.width);
    int h = (int) Math.max(10, innerRectangle.height);
    icon = ImageTools.boxToBufferedImage(box, w, h, getColourScheme(), false);
    deepest = null;
  }
  Box deepest = null;

  Box mark = null;
  public void markBorder(Box mark) {
    if (this.mark==mark) return;
    this.mark = mark;
    repaint();
  }

  private Argument getCurrent() {
    return document.getCursor().getCurrent();
  }

}
TOP

Related Classes of ketUI.panel.KetPanel

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.