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

de.sciss.mellite.impl.document.FolderEditorViewImpl.scala Maven / Gradle / Ivy

/*
 *  FolderEditorViewImpl.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.document

import de.sciss.desktop
import de.sciss.desktop.{KeyStrokes, Window}
import de.sciss.lucre.edit.{EditFolder, UndoManager}
import de.sciss.lucre.synth.Txn
import de.sciss.lucre.{Copy, Expr, Folder, Obj, StringObj, expr}
import de.sciss.mellite.impl.component.CollectionViewImpl
import de.sciss.mellite.{FolderEditorView, FolderView, ObjView, UniverseHandler, ViewState}
import de.sciss.proc.{ObjKeys, Universe}
import de.sciss.swingplus.{ComboBox, GroupPanel, Spinner}

import javax.swing.SpinnerNumberModel
import scala.swing.Swing.EmptyIcon
import scala.swing.event.Key
import scala.swing.{Action, Alignment, CheckBox, Dialog, Label, Swing, TextField}

object FolderEditorViewImpl extends FolderEditorView.Companion {
  def install(): Unit =
    FolderEditorView.peer = this

  def apply[T <: Txn[T]](folder: Folder[T])(implicit tx: T, handler: UniverseHandler[T],
                                            undoManager: UndoManager[T]): FolderEditorView[T] = {
    import handler.universe
    val peer  = FolderView(folder)
    val view  = new Impl[T](peer)
    view.init()
  }

  private final class Impl[T <: Txn[T]](val peer: FolderView[T])
                                       (implicit protected val universeHandler: UniverseHandler[T],
                                        val undoManager: UndoManager[T])
    extends CollectionViewImpl[T] with FolderEditorView[T]
  {

    impl =>

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

    override def obj(implicit tx: T): Folder[T] = peer.obj
    override def viewState: Set[ViewState] = peer.viewState

    protected type InsertConfig = peer.SelectionImpl // FolderView.Selection[T]

    private def selection: InsertConfig = impl.peer.selectionImpl

    protected def prepareInsertDialog(f: ObjView.Factory): Option[InsertConfig] =
      Some(selection)

    protected def prepareInsertCmdLine(args: List[String]): Option[(InsertConfig, List[String])] =
      Some((selection, args))

    protected override def editInsert(f: ObjView.Factory, xs: List[Obj[T]], config: InsertConfig)
                            (implicit tx: T): Boolean = {
      val (parent, idx) = impl.peer.insertionPointOf(config)
      val numObj        = xs.size
      undoManager.capture("Create Objects") {
        xs.zipWithIndex.foreach { case (child, ci) =>
          EditFolder.insertUndo(/*f.prefix,*/ parent, index = idx + ci, child = child)
          if (ci == numObj - 1) peer.select(child)
        }
      }
      xs.nonEmpty
    }

    def dispose()(implicit tx: T): Unit =
      peer.dispose()

    protected lazy val actionDelete: Action = Action(null) {
      val sel = peer.selection
      cursor.step { implicit tx =>
        undoManager.capture("Delete Elements") {
          sel.foreach { nodeView =>
            val parent = nodeView.parent
            val childH = nodeView.modelData
            val child = childH()
            val idx = parent.indexOf(child)
            if (idx < 0) {
              println("WARNING: Parent folder of object not found")
            } else {
              EditFolder.removeAtUndo[T](/*nodeView.renderData.humanName,*/ parent, idx /*, child*/)
            }
          }
        }
      }
    }

    protected def initGUI2(): Unit = {
      peer.addListener {
        case FolderView.SelectionChanged(_, sel) =>
          selectionChanged(sel.map(_.renderData))
          actionDuplicate.enabled = sel.nonEmpty
      }
    }

    lazy val actionDuplicate: Action = new Action("Duplicate...") {
      accelerator = Some(KeyStrokes.menu1 + Key.D)
      enabled     = impl.peer.selection.nonEmpty

      private var appendText  = "-1"
      private var count       = 1
      private var append      = true
      private var attrMode    = 0     // 0 - deep copy, 1 - shallow copy, 2 - share, 3 - empty

      private def incLast(x: String, c: Int): String = {
        val p = "\\d+".r
        val m = p.pattern.matcher(x)
        var start = -1
        var end   = -1
        while (m.find()) {
          start = m.start()
          end   = m.end()
        }
        if (start < 0) x else {
          val (pre, tmp) = x.splitAt(start)
          val (old, suf) = tmp.splitAt(end - start)
          s"$pre${old.toInt + c}$suf"
        }
      }

      def apply(): Unit = {
        val sel     = impl.peer.selection
        val numSel  = sel.size
        if (numSel == 0) return

        sel.map(view => view.renderData.name)

        val txtInfo = s"Duplicate ${if (numSel == 1) s"'${sel.head.renderData.name}'" else s"$numSel Objects"}"
        val lbInfo  = new Label(txtInfo)
        lbInfo.border = Swing.EmptyBorder(0, 0, 8, 0)
        val ggName  = new TextField(6)
        ggName.text = appendText
        val mCount  = new SpinnerNumberModel(count, 1, 0x4000, 1)
        val ggCount = new Spinner(mCount)
        val lbName  = new CheckBox("Append to Name:")
        lbName.selected = append
        val lbCount = new Label("Number of Copies:" , EmptyIcon, Alignment.Right)
        val lbAttr  = new Label("Attribute Map")
        val ggAttr  = new ComboBox(Seq("Deep Copy", "Shallow Copy", "Share", "Empty"))
        ggAttr.selection.index = attrMode

        val box = new GroupPanel {
          horizontal = Par(
            lbInfo,
            Seq(
              Par(lbName, lbCount, lbAttr),
              Par(ggName, ggCount, ggAttr),
            )
          )
          vertical = Seq(
            lbInfo,
            Par(Baseline)(lbName  , ggName  ),
            Par(Baseline)(lbCount , ggCount ),
            Par(Baseline)(lbAttr  , ggAttr  ),
          )
        }

        val pane = desktop.OptionPane.confirmation(box, optionType = Dialog.Options.OkCancel,
          messageType = Dialog.Message.Question, focus = Some(ggCount))
        pane.title  = "Duplicate"
        val window  = Window.find(component)
        val res     = pane.show(window)

        import de.sciss.equal.Implicits._
        if (res === Dialog.Result.Ok) {
          // save state
          count       = mCount.getNumber.intValue()
          appendText  = ggName.text
          append      = lbName.selected
          attrMode    = ggAttr.selection.index

          // lazy val regex = "\\d+".r // do _not_ catch leading minus character

          cursor.step { implicit tx =>
            undoManager.capture(txtInfo) {
              sel.flatMap { nodeView =>
                val p       = nodeView.parent
                val orig    = nodeView.modelData()
                val idx     = p.indexOf(orig)
                val copies  = List.tabulate(count) { n =>
                  // val cpy = Obj.copy[T, T, Obj](orig)
                  val cpy     = {
                    val context   = Copy[T, T]()
                    val aDeep     = attrMode == 0
                    val aShallow  = attrMode == 1
                    val aShare    = attrMode == 2
                    val res       = if (aDeep) context.apply(orig) else context.copyPlain(orig)
                    context.finish()
                    if (aShallow || aShare) {
                      val attrIn  = orig.attr
                      val attrOut = res .attr
                      attrIn.keysIterator.foreach {
                        case ObjKeys.attrName => () // handled later
                        case key =>
                          attrIn.get(key).foreach {
                            case ex: Expr[T, _] if aShallow =>
                              ex.tpe match {
                                case tpe: Expr.Type[_, _] =>
                                  val exT = ex.asInstanceOf[tpe.E[T]] // XXX TODO what else can we do
                                  val exVl = tpe.Var.unapply(exT) match {
                                    case Some(vr) => vr()
                                    case None     => exT
                                  }
                                  val exVr = tpe.newVar[T](exVl).asInstanceOf[Obj[T]] // XXX TODO what else can we do
                                  attrOut.put(key, exVr)

                                case _ =>
                                  attrOut.put(key, ex)
                              }

                            case other =>
                              attrOut.put(key, other)
                          }
                      }
                    }
                    res
                  }

                  if (append || attrMode != 0) {
                    orig.attr.$[StringObj](ObjKeys.attrName).foreach { oldName =>
                      // val imp = ExprImplicits[T]
                      import expr.Ops._
                      val suffix  = if (append) incLast(appendText, n) else ""
                      val newName = oldName ++ suffix
                      cpy.attr.put(ObjKeys.attrName, StringObj.newVar(newName))
                    }
                    // cpy.attr.name = s"${cpy.attr.name}$suffix"
                  }
                  EditFolder.insert(/*"Copy",*/ p, idx + n + 1, cpy)
                }
                copies
              }
            }
          }
        }
      }
    }

    def selectedObjects: List[ObjView[T]] = peer.selection.map(_.renderData)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy