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

de.sciss.mellite.gui.impl.ExprHistoryView.scala Maven / Gradle / Ivy

/*
 *  ExprHistoryView.scala
 *  (Mellite)
 *
 *  Copyright (c) 2012-2016 Hanns Holger Rutz. All rights reserved.
 *
 *  This software is published under the GNU General Public License v3+
 *
 *
 *  For further information, please contact Hanns Holger Rutz at
 *  [email protected]
 */

package de.sciss.mellite
package gui
package impl

import java.text.SimpleDateFormat
import java.util.{Date, Locale}

import de.sciss.lucre.confluent.Cursor
import de.sciss.lucre.expr.Expr
import de.sciss.lucre.stm
import de.sciss.lucre.stm.{Identifiable, Sys}
import de.sciss.lucre.swing.impl.ComponentHolder
import de.sciss.lucre.swing.{defer, deferTx}
import de.sciss.processor.Processor
import de.sciss.processor.impl.ProcessorImpl
import de.sciss.serial.Serializer
import de.sciss.swingplus.{ListView, SpinningProgressBar}
import de.sciss.synth.proc.Confluent

import scala.language.higherKinds
import scala.swing.{BoxPanel, Component, Orientation, ScrollPane}

object ExprHistoryView {
  type S = Confluent
  type D = S#D

  var DEBUG = false

  def apply[A, Ex[~ <: Sys[~]] <: Expr[~, A]](workspace: Workspace.Confluent, expr: Ex[S])
              (implicit tx: S#Tx, serializer: Serializer[S#Tx, S#Acc, Ex[S]]): ViewHasWorkspace[S] = {
    val sys       = workspace.system
    val cursor    = Cursor[S, D](tx.inputAccess)(tx.durable, sys)
    val exprH     = tx.newHandle(expr)
    val pos0      = tx.inputAccess
    val time0     = pos0.info.timeStamp
    val val0      = expr.value

    val stop = expr match {
      case hid: Identifiable[_] =>
        val id    = hid.id // .asInstanceOf[confluent.Identifier[S]]
        val head  = id.path.head.toInt
        if (DEBUG) println(s"ID = $id, head = $head")
        head
      case _ => 0
    }

    val res       = new Impl[A](workspace, cursor, exprH, pos0, time0, val0, stop = stop)
    deferTx(res.guiInit())
    res
  }

  private final class Impl[A](val workspace: Workspace[S],
                              val cursor: Cursor[S, D], exprH: stm.Source[S#Tx, Expr[S, A]],
                              pos0: S#Acc, time0: Long, value0: A, stop: Int)
    extends ViewHasWorkspace[S] with ComponentHolder[Component] {

    private val mod     = ListView.Model.empty[String]
    private val format  = new SimpleDateFormat("yyyy MM dd MM | HH:mm:ss", Locale.US) // don't bother user with alpha characters

    private var busy: SpinningProgressBar = _

    private def mkString(time: Long, value: A): String = {
      val date  = format.format(new Date(time))
      s"$date    $value"
    }

    private object proc extends Processor[Unit] with ProcessorImpl[Unit, Processor[Unit]] {
      protected def body(): Unit = {
        var path    = pos0
        var ok      = true
        var time    = time0
        var value   = value0

        def addEntry(): Unit = {
          val s = mkString(time, value)
          if (DEBUG) println(s"----add $path, $s")
          defer {
            mod.prepend(s)
          }
        }

        while (ok) {
          val (newTime, newValue, newPath) = cursor.stepFrom(path) { implicit tx =>
            if (DEBUG) println(s"----path = $path")
            val v     = try {
              val expr  = exprH()
              if (DEBUG) println(s"----try $expr")
              val res   = expr.value
              if (DEBUG) println(s"----ok $res")
              res
            } catch {
              case _: NoSuchElementException => value // XXX TODO - ugly ugly ugly
            }
            val time  = path.info.timeStamp
            val path1 = path.takeUntil(time - 1L)
            (time, v, path1)
          }
          checkAborted()

          if (value != newValue) {
            addEntry()
            value     = newValue
            time      = newTime
          }

          ok    = path != newPath && (newPath.last.toInt >= stop)
          path  = newPath
        }
        addEntry()
      }
    }

    def guiInit(): Unit = {
      val lv      = new ListView(mod)
      lv.prototypeCellValue = s"${mkString(System.currentTimeMillis(), value0)}XXXXX"
      val scroll  = new ScrollPane(lv)
      scroll.peer.putClientProperty("styleId", "undecorated")
      scroll.border = null
      busy        = new SpinningProgressBar
      proc.onComplete(_ => defer(busy.spinning = false))
      proc.start()
      busy.spinning = true
      component   = new BoxPanel(Orientation.Vertical) {
        contents += scroll
        contents += busy
      }
    }

    def dispose()(implicit tx: S#Tx): Unit = {
      deferTx {
        proc.abort()
        busy.spinning = false
      }
      cursor.dispose()(tx.durable)    // XXX TODO - should we check the processor first?
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy