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

de.sciss.mellite.impl.artifact.ArtifactViewImpl.scala Maven / Gradle / Ivy

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

package de.sciss.mellite.impl.artifact

import de.sciss.desktop.{Desktop, FileDialog, PathField}
import de.sciss.file.File
import de.sciss.icons.raphael
import de.sciss.lucre.edit.UndoManager
import de.sciss.lucre.edit.impl.BasicUndoableEdit
import de.sciss.lucre.swing.LucreSwing.{deferTx, requireEDT}
import de.sciss.lucre.swing.View
import de.sciss.lucre.swing.impl.ComponentHolder
import de.sciss.lucre.synth.Txn
import de.sciss.lucre.{Artifact, Disposable, Source}
import de.sciss.mellite.GUI.iconNormal
import de.sciss.mellite.impl.objview.ArtifactObjView.humanName
import de.sciss.mellite.{ArtifactLocationFrame, ArtifactLocationObjView, ArtifactView, UniverseHandler, ViewState}
import de.sciss.proc.Universe
import de.sciss.swingplus.ComboBox

import java.net.URI
import javax.swing.undo.{CannotRedoException, CannotUndoException}
import scala.swing.event.{SelectionChanged, ValueChanged}
import scala.swing.{Action, Button, Component, FlowPanel}
import scala.util.Try

object ArtifactViewImpl {
  def mkPathField(reveal: Boolean, mode: Boolean,
                  initMode: FileDialog.Mode = FileDialog.Save): (PathField, FlowPanel) = {
    val ggFile  = new PathField
    ggFile.mode = initMode

    val c0: List[Component] = if (!mode) ggFile :: Nil else {
      val ggMode  = new ComboBox(Seq("New File", "Existing File", "Existing Folder")) {
        listenTo(selection)
        reactions += {
          case SelectionChanged(_) =>
            ggFile.mode = selection.index match {
              case 1 => FileDialog.Open
              case 2 => FileDialog.Folder
              case _ => FileDialog.Save
            }
        }
      }
      ggFile :: ggMode :: Nil
    }

    val c = if (!reveal) c0 else {
      val ggReveal = new Button(Action(null)(Desktop.revealFile(ggFile.value)))
      ggReveal.icon      = iconNormal(raphael.Shapes.Inbox)
      ggReveal.tooltip   = s"Reveal in ${if (Desktop.isMac) "Finder" else "File Manager"}"
      ggReveal :: c0
    }

    val ggValue = new FlowPanel(c: _*)
    (ggFile, ggValue)
  }

  def apply[T <: Txn[T]](obj: Artifact[T], mode: Boolean, initMode: FileDialog.Mode)
                        (implicit tx: T, handler: UniverseHandler[T], undo: UndoManager[T]): ArtifactView[T] = {
    val objH      = tx.newHandle(obj)
    val editable  = obj.modifiableOption.isDefined
    val res       = new Impl(objH, mode = mode, initMode = initMode, editable = editable)
    res.init(obj)
    res
  }

  private final class UpdateChild[T <: Txn[T]](val name: String, aH: Source[T, Artifact.Modifiable[T]],
                                               oldChild: Artifact.Child,
                                               newChild: Artifact.Child)
    extends BasicUndoableEdit[T] {

    override protected def undoImpl()(implicit tx: T): Unit = {
      val a = aH()
      if (a.child != newChild) throw new CannotUndoException()
      a.child = oldChild
    }

    override protected def redoImpl()(implicit tx: T): Unit =
      perform()

    def perform()(implicit tx: T): Unit = {
      val a = aH()
      if (a.child != oldChild) throw new CannotRedoException()
      a.child = newChild
    }
  }

  private final class Impl[T <: Txn[T]](objH: Source[T, Artifact[T]], mode: Boolean, initMode: FileDialog.Mode,
                                        val editable: Boolean)
                                       (implicit handler: UniverseHandler[T], val undoManager: UndoManager[T])
    extends ArtifactView[T] with View.Editable[T] with ComponentHolder[Component] {

    type C = Component

    private[this] var ggPath      : PathField   = _
    private[this] var observer    : Disposable[T]  = _

    override implicit val universe: Universe[T] = handler.universe

    override def obj(implicit tx: T): Artifact[T] = objH()

    override def viewState: Set[ViewState] = Set.empty

    def init(obj0: Artifact[T])(implicit tx: T): this.type = {
      val value0 = obj0.value
      deferTx(initGUI(value0))
      observer = obj0.changed.react { implicit tx => upd =>
        deferTx {
          val fileNowOpt = Try(new File(upd.now)).toOption
          ggPath.valueOption = fileNowOpt
        }
      }
      this
    }

    private def initGUI(value0: URI): Unit = {
      val (_ggPath, p) = mkPathField(reveal = true, mode = mode, initMode = initMode)
      val file0Opt = Try(new File(value0)).toOption
      _ggPath.valueOption = file0Opt

      val ggLoc: Button = new Button(Action(null) {
        cursor.step { implicit tx =>
          ArtifactLocationFrame(objH().location)
        }
      })
      ggLoc.icon      = iconNormal(raphael.Shapes.Location)
      ggLoc.tooltip   = s"${ArtifactLocationObjView.humanName} View"
      p.contents.insert(1, ggLoc)

      ggPath = _ggPath

      if (editable) _ggPath.reactions += {
        case ValueChanged(_) => save()
      }

      component = p
    }

    def save(): Unit = {
      requireEDT()
      val newPath = ggPath.valueOption.map(_.toURI)
      cursor.step { implicit tx =>
        val title = s"Edit $humanName"
        objH().modifiableOption match {
          case Some(pVr) if newPath.isDefined =>
            val oldVal = pVr.child
            val newVal = Artifact.Value.relativize(pVr.location.value, newPath.get)
            import de.sciss.equal.Implicits._
            if (newVal === oldVal) None else {
              val edit = new UpdateChild[T](title, tx.newHandle(pVr), oldChild = oldVal, newChild = newVal)
              edit.perform()
              undoManager.addEdit(edit)
            }

          case _ => None
        }
      }
    }

    def dispose()(implicit tx: T): Unit = observer.dispose()
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy