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

gapt.prooftool.DrawSequentProof.scala Maven / Gradle / Ivy

The newest version!
package gapt.prooftool

import java.awt.Font._
import java.awt.event.{MouseEvent, MouseMotionListener}
import java.awt.{BasicStroke, Color, RenderingHints, Stroke}

import gapt.expr.Expr
import gapt.formats.latex.LatexExporter
import gapt.proofs.lk._
import gapt.proofs.lk.rules.ContractionLeftRule
import gapt.proofs.lk.rules.ContractionRightRule
import gapt.proofs.lk.rules.CutRule
import gapt.proofs.lk.rules.ProofLink
import gapt.proofs.lk.rules.WeakeningLeftRule
import gapt.proofs.lk.rules.WeakeningRightRule
import gapt.proofs.{SequentIndex, SequentProof}

import scala.swing._
import scala.swing.event._

/**
 * A panel containing a sequent proof.
 * @param main The instance of [[gapt.prooftool.SequentProofViewer]] that this belongs to.
 * @param proof The proof being displayed.
 * @param auxIndices The indices of the auxiliary formulas of the bottommost inference.
 * @param cutAncestorIndices The indices of ancestors of cut formulas in the end sequent.
 * @param pos The position of this proof relative to the main proof being displayed.
 */
class DrawSequentProof[F, T <: SequentProof[F, T]](
    val main: SequentProofViewer[F, T],
    val proof: SequentProof[F, T],
    val auxIndices: Set[SequentIndex],
    val cutAncestorIndices: Set[SequentIndex],
    sequentElementRenderer: F => String,
    val pos: List[Int]
) extends BoxPanel(Orientation.Vertical) with MouseMotionListener {
  val defaultBorder = Swing.LineBorder(Color.WHITE)

  private var lineHideLevel = 0

  private def showLine() = {
    lineHideLevel = if (lineHideLevel == 0) 0 else lineHideLevel - 1
    assert(lineHideLevel >= 0)

    linePanel.visible = lineHideLevel == 0
  }

  private def hideLine() = {
    lineHideLevel += 1
    linePanel.visible = false
  }

  val endSequentPanel = {
    val mainAuxIndices = proof.mainIndices.toSet ++ auxIndices
    DrawSequent(
      this,
      proof.conclusion,
      mainAuxIndices,
      cutAncestorIndices,
      sequentElementRenderer
    )
  }

  val cutAncestorIndicesNew = proof match {
    case p: CutRule =>
      List(cutAncestorIndices.flatMap(proof.occConnectors.head.parents) + p.aux1, cutAncestorIndices.flatMap(proof.occConnectors.tail.head.parents) + p.aux2)
    case _ =>
      for ((p, i) <- proof.immediateSubProofs.zipWithIndex) yield cutAncestorIndices flatMap proof.occConnectors(i).parents
  }

  val subProofs = for ((p, i) <- proof.immediateSubProofs.zipWithIndex) yield {
    new DrawSequentProof(
      main,
      p,
      proof.auxIndices(i).toSet,
      cutAncestorIndicesNew(i),
      sequentElementRenderer,
      i :: pos
    )
  }

  val subProofsPanel = new SubproofsPanel(this, subProofs)
  var aboveLinePanel: AboveLinePanel[F, T] = proof match {
    case p: ProofLink =>
      new ProoflinkLabelPanel(this, p.referencedProof)
    case _ => subProofsPanel
  }

  val linePanel = new ProofLinePanel(this, proof.name)

  val aboveLinePanelIndex = if (pos.isEmpty) 1 else 0

  if (pos.isEmpty) contents += Swing.VGlue
  contents += aboveLinePanel
  contents += linePanel
  contents += endSequentPanel
  if (pos.isEmpty) contents += Swing.VGlue

  def endSequentWidth() = endSequentPanel.width()
  def subProofsWidth() = subProofsPanel.width()
  def width() = size.width
  def endSequentLeftMarginWidth() = (width() * subProofsPanel.xLayoutAlignment - endSequentWidth() * endSequentPanel.xLayoutAlignment).toInt
  def endSequentRightMarginWidth() = (width() * (1 - subProofsPanel.xLayoutAlignment) - endSequentWidth() * (1 - endSequentPanel.xLayoutAlignment)).toInt

  linePanel.xLayoutAlignment = 0.5
  endSequentPanel.xLayoutAlignment = 0.5
  subProofsPanel.xLayoutAlignment = 0.5

  updateSubProofAlignment()
  revalidate()
  repaint()

  /**
   * Recomputes the alignments of the subproofs, line, and end sequent panels.
   */
  private def updateSubProofAlignment() = {

    // This value is equal to the middle of the end sequents of the subproofs as a fraction of the width of the subproofs.
    val subProofsAligmentNew = if (subProofs.isEmpty)
      0.5
    else
      (subProofsPanel.endSequentWidth().toDouble / 2 + subProofsPanel.endSequentLeftMarginWidth()) / subProofsPanel.width()

    if (subProofsPanel.xLayoutAlignment != subProofsAligmentNew) {
      subProofsPanel.xLayoutAlignment = subProofsAligmentNew
      publish(AlignmentChanged)

    }
    revalidate()
    repaint()
  }

  /*
   * Window management stuff & reactions
   */
  opaque = false
  border = defaultBorder

  this.peer.setAutoscrolls(true)
  this.peer.addMouseMotionListener(this)

  def mouseMoved(e: MouseEvent): Unit = {}
  def mouseDragged(e: MouseEvent): Unit = {
    // The user is dragging us, so scroll!
    val r = new Rectangle(e.getX, e.getY, 1, 1)
    this.peer.scrollRectToVisible(r)
  }

  listenTo(mouse.moves, mouse.clicks, mouse.wheel, main.publisher, endSequentPanel, linePanel, subProofsPanel)
  deafTo(this)
  reactions += {
    case e: MouseDragged =>
      main.scrollPane.cursor = new java.awt.Cursor(java.awt.Cursor.MOVE_CURSOR)

    case e: MouseReleased =>
      main.scrollPane.cursor = java.awt.Cursor.getDefaultCursor

    case e: MouseWheelMoved =>
      main.scrollPane.peer.dispatchEvent(e.peer)

    case HideStructuralRules => // Fix: contraction is still drawn when a weakening is followed by a contraction.
      proof match {
        case _: WeakeningLeftRule | _: WeakeningRightRule | _: ContractionLeftRule | _: ContractionRightRule =>
          hideLine()
          main.publisher.publish(HideEndSequent(0 :: pos))
        case _ =>
      }

    case HideEndSequent(p) if p == pos =>
      endSequentPanel.visible = false

    case ShowAllRules(p) if pos endsWith p =>
      showLine()
      endSequentPanel.visible = true

    case ShowSequentProof(p) if p == pos =>
      showLine()
      contents(aboveLinePanelIndex) = subProofsPanel

      revalidate()
      repaint()

    case HideSequentProof(p) if p == pos =>
      hideLine()
      contents(aboveLinePanelIndex) = new CollapsedSubproofsPanel(this)

      revalidate()
      repaint()

    case ShowDebugBorders =>
      border = Swing.LineBorder(Color.BLUE) // DEBUG
      endSequentPanel.border = Swing.LineBorder(Color.MAGENTA) // DEBUG

    case HideDebugBorders =>
      border = defaultBorder
      endSequentPanel.border = Swing.EmptyBorder

    case FontChanged(_) =>
      revalidate()
      repaint()

    case UIElementResized(_) | UIElementHidden(_) | UIElementShown(_) =>
      updateSubProofAlignment()

    case AlignmentChanged =>
      updateSubProofAlignment()

    case e: MouseClicked if e.peer.getButton == MouseEvent.BUTTON3 =>
      gapt.prooftool.PopupMenu(this, e.point.x, e.point.y)

  }
}

/**
 * Abstract class for things that can be placed above a proof line (subproofs, labels).
 */
abstract class AboveLinePanel[F, T <: SequentProof[F, T]](val parent: DrawSequentProof[F, T]) extends BoxPanel(Orientation.Horizontal) {
  final def width() = size.width
  def endSequentLeftMarginWidth(): Int
  def endSequentRightMarginWidth(): Int
  final def endSequentWidth() = width() - endSequentLeftMarginWidth() - endSequentRightMarginWidth()

  border = Swing.EmptyBorder
  opaque = false

  listenTo(parent.main.publisher)
  deafTo(this)

  reactions += {
    case ShowDebugBorders =>
      border = Swing.LineBorder(Color.GREEN) // DEBUG

    case HideDebugBorders =>
      border = Swing.EmptyBorder

    case AlignmentChanged =>
      publish(AlignmentChanged)

  }
}

/**
 * Class that puts the name of a proof link above the proof line.
 * @param referencedProof The name of the link.
 */
class ProoflinkLabelPanel[F, T <: SequentProof[F, T]](parent: DrawSequentProof[F, T], referencedProof: Expr) extends AboveLinePanel[F, T](parent) {
  private val proofLinkLabel = new LatexLabel(parent.main, LatexExporter(referencedProof))

  override def endSequentLeftMarginWidth() = 0
  override def endSequentRightMarginWidth() = 0

  contents += proofLinkLabel
}

/**
 * Class that puts "(...)" above the proof line for a collapsed proof.
 */
class CollapsedSubproofsPanel[F, T <: SequentProof[F, T]](parent: DrawSequentProof[F, T]) extends AboveLinePanel[F, T](parent) {
  private val subProofsHiddenLabel = new LatexLabel(parent.main, "(...)")
  subProofsHiddenLabel.listenTo(mouse.clicks)
  subProofsHiddenLabel.reactions += {
    case e: MouseClicked =>
      parent.main.publisher.publish(ShowSequentProof(parent.pos))
  }

  override def endSequentLeftMarginWidth() = 0
  override def endSequentRightMarginWidth() = 0

  contents += subProofsHiddenLabel
}

/**
 * A panel containing the subproofs of a proof arranged side by side.
 * @param parent The [[gapt.prooftool.DrawSequentProof]] instance that this belongs to.
 * @param subProofs The The [[gapt.prooftool.DrawSequentProof]] instances containing the subproofs.
 */
class SubproofsPanel[F, T <: SequentProof[F, T]](
    parent: DrawSequentProof[F, T],
    val subProofs: Seq[DrawSequentProof[F, T]]
) extends AboveLinePanel[F, T](parent) {

  subProofs.foreach(contents +=)
  subProofs.foreach(listenTo(_))
  subProofs.foreach(_.yLayoutAlignment = 1) // Forces the subproof panels to align along their bottom edges

  override def endSequentLeftMarginWidth() = if (subProofs.isEmpty) 0 else subProofs.head.endSequentLeftMarginWidth()
  override def endSequentRightMarginWidth() = if (subProofs.isEmpty) 0 else subProofs.last.endSequentRightMarginWidth()
}

/**
 * Panel that contains an inference line and the name of the inference.
 * @param parent The [[gapt.prooftool.DrawSequentProof]] instance that this belongs to.
 * @param proofName The name of the inference.
 */
class ProofLinePanel[F, T <: SequentProof[F, T]](
    val parent: DrawSequentProof[F, T],
    val proofName: String
) extends BoxPanel(Orientation.Horizontal) {
  private var fSize_ = parent.main.currentFontSize
  def fSize = fSize_
  def fSize_=(sz: Int) = {
    fSize_ = sz
    labelFont = new Font(SANS_SERIF, ITALIC, fSize - 2)
  }
  private var labelFont_ = new Font(SANS_SERIF, ITALIC, fSize - 2)
  def labelFont = labelFont_
  def labelFont_=(ft: Font) = {
    labelFont_ = ft
    updateWidth(fontSizeHasChanged = true)
  }
  def labelFontMetrics = peer.getFontMetrics(labelFont)

  private def updateWidth(fontSizeHasChanged: Boolean = false): Unit = {
    val newLineWidth = math.max(parent.subProofsPanel.endSequentWidth(), parent.endSequentPanel.width())
    if (fontSizeHasChanged || math.abs(lineWidth - newLineWidth) > 2) {
      lineWidth = newLineWidth
      val labelWidth = labelFontMetrics.stringWidth(proofName) * 2 + fSize
      _width = lineWidth + labelWidth
      val height = labelFontMetrics.getHeight + fSize / 4
      preferredSize = new Dimension(_width, height)
      minimumSize = preferredSize
      maximumSize = preferredSize
      repaint()
    }
  }
  def width() = _width
  private var _width = 0; private var lineWidth = 0; updateWidth()

  override def paintComponent(g: Graphics2D): Unit = {
    val leftPoint = (size.width - lineWidth) / 2
    val rightPoint = (size.width + lineWidth) / 2

    g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB)
    g.drawLine(leftPoint, size.height / 2, rightPoint, size.height / 2)
    g.setFont(labelFont)
    g.drawString(proofName, rightPoint + fSize / 8, size.height / 2 + labelFontMetrics.getMaxDescent)
  }

  border = Swing.EmptyBorder
  listenTo(parent.main.publisher, parent.endSequentPanel, parent.subProofsPanel)
  deafTo(this)
  reactions += {
    case UIElementShown(_) | UIElementResized(_) | AlignmentChanged => updateWidth()

    case ShowDebugBorders =>
      border = Swing.LineBorder(Color.RED) // DEBUG

    case HideDebugBorders =>
      border = Swing.EmptyBorder

    case FontChanged(_) =>
      fSize = parent.main.currentFontSize
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy