All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
de.sciss.mellite.impl.markdown.MarkdownEditorViewImpl.scala Maven / Gradle / Ivy
/*
* MarkdownViewImpl.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.markdown
import de.sciss.desktop.{KeyStrokes, Util}
import de.sciss.icons.raphael
import de.sciss.lucre.edit.UndoManager
import de.sciss.lucre.swing.LucreSwing.{deferTx, requireEDT}
import de.sciss.lucre.swing.View
import de.sciss.lucre.swing.edit.EditVar
import de.sciss.lucre.swing.impl.ComponentHolder
import de.sciss.lucre.{Source, TxnLike, synth}
import de.sciss.mellite.{GUI, MarkdownEditorView, MarkdownRenderView, UniverseHandler, ViewState}
import de.sciss.model.impl.ModelImpl
import de.sciss.proc.{Markdown, Universe}
import de.sciss.scalainterpreter.Fonts
import de.sciss.scalainterpreter.impl.CodePaneImpl
import de.sciss.swingplus.Implicits._
import de.sciss.syntaxpane.SyntaxDocument
import de.sciss.syntaxpane.syntaxkits.MarkdownSyntaxKit
import java.beans.PropertyChangeEvent
import scala.collection.immutable.{Seq => ISeq}
import scala.concurrent.stm.Ref
import scala.swing.Swing._
import scala.swing.event.Key
import scala.swing.{Action, BorderPanel, Button, Component, EditorPane, FlowPanel, TabbedPane}
object MarkdownEditorViewImpl extends MarkdownEditorView.Companion {
def install(): Unit =
MarkdownEditorView.peer = this
override def apply[T <: synth.Txn[T]](obj: Markdown[T], showEditor: Boolean, bottom: ISeq[View[T]])
(implicit tx: T, handler: UniverseHandler[T],
undoManager: UndoManager[T]): MarkdownEditorView[T] = {
val editable = obj match {
case Markdown.Var(_) => true
case _ => false
}
val textValue = obj.value
val renderer = MarkdownRenderView[T](obj, embedded = true)
val res = new Impl[T](renderer, tx.newHandle(obj), editable = editable, bottom = bottom)
res.init(textValue, showEditor = showEditor)
}
private def createPane(initialText: String): PaneImpl = {
CodePaneImpl.initKit[MarkdownSyntaxKit]()
new PaneImpl(initialText).init()
}
private final class PaneImpl(protected val initialText: String)
extends CodePaneImpl.Basic {
val editor : EditorPane = CodePaneImpl.createEditorPane()
protected def mimeType : String = "text/markdown"
protected def fonts : Fonts.List = Fonts.defaultFonts
protected def tabSize : Int = 4
}
private final class Impl[T <: synth.Txn[T]](val renderer: MarkdownRenderView[T],
markdownH: Source[T, Markdown[T]],
editable: Boolean,
bottom: ISeq[View[T]])
(implicit undoManager: UndoManager[T])
extends ComponentHolder[Component] with MarkdownEditorView[T] with ModelImpl[MarkdownEditorView.Update] { impl =>
type C = Component
override def obj(implicit tx: T): Markdown[T] = markdownH()
override def viewState: Set[ViewState] = Set.empty
implicit val universe: Universe[T] = renderer.universe
private[this] val _dirty = Ref(false)
def dirty(implicit tx: TxnLike): Boolean = _dirty.get(tx.peer)
protected def dirty_=(value: Boolean): Unit = {
requireEDT()
val wasDirty = _dirty.single.swap(value)
if (wasDirty != value) {
// deferTx {
actionApply.enabled = value
dispatch(MarkdownEditorView.DirtyChange(value))
}
// }
}
private[this] var paneImpl : PaneImpl = _
private[this] var actionApply : Action = _
private[this] var actionRender: Action = _
private[this] var tabs : TabbedPane = _
protected def currentText: String = paneImpl.editor.text
def dispose()(implicit tx: T): Unit = ()
def undoAction: Action = Action.wrap(paneImpl.editor.peer.getActionMap.get("undo"))
def redoAction: Action = Action.wrap(paneImpl.editor.peer.getActionMap.get("redo"))
private def saveText(newTextValue: String)(implicit tx: T): Boolean =
if (!editable) false else Markdown.Var.unapply(markdownH()).exists { vr =>
val newMarkdown = Markdown.newConst[T](newTextValue)
EditVar.exprUndo[T, Markdown.Value, Markdown]("Change Source Markdown", vr, newMarkdown)
true
}
private def clearAfterEdit(): Unit = {
requireEDT()
// this doesn't work properly
// component.setDirty(value = false) // do not erase undo history
// so let's clear the undo history now...
paneImpl.editor.peer.getDocument.asInstanceOf[SyntaxDocument].clearUndos()
}
def save(): Unit = {
requireEDT()
val newMarkdown = currentText
val edited = cursor.step { implicit tx =>
saveText(newMarkdown)
}
if (edited) clearAfterEdit()
}
def markdown(implicit tx: T): Markdown[T] = markdownH()
private def renderAndShow(): Unit = {
val t = currentText
cursor.step { implicit tx =>
val md = markdown
renderer.setInProgress(md, t /* md.value */)
}
tabs.selection.index = 1
}
def init(initialText: String, showEditor: Boolean)(implicit tx: T): this.type = {
deferTx(initGUI(initialText, showEditor = showEditor))
this
}
private def initGUI(initialText: String, showEditor: Boolean): Unit = {
paneImpl = createPane(initialText)
actionApply = Action("Apply")(save())
actionRender = Action(null )(renderAndShow())
actionApply.enabled = false
lazy val doc = paneImpl.editor.peer.getDocument.asInstanceOf[SyntaxDocument]
doc.addPropertyChangeListener(SyntaxDocument.CAN_UNDO, (_: PropertyChangeEvent) => dirty = doc.canUndo)
val ksRender = KeyStrokes.menu1 + Key.Enter
val ttRender = s"Render (${Util.keyStrokeText(ksRender)})"
lazy val ggApply : Button = GUI.toolButton(actionApply , raphael.Shapes.Check , tooltip = "Save text changes")
lazy val ggRender: Button = GUI.toolButton(actionRender, raphael.Shapes.RefreshArrow, tooltip = ttRender)
Util.addGlobalKeyWhenVisible(ggRender, ksRender)
val bot1: List[Component] = if (bottom.isEmpty) Nil else bottom.iterator.map(_.component).toList
val bot2 = HGlue :: ggApply :: ggRender :: bot1
val panelBottom = new FlowPanel(FlowPanel.Alignment.Trailing)(bot2: _*)
val paneEdit = new BorderPanel {
add(paneImpl.component, BorderPanel.Position.Center)
add(panelBottom , BorderPanel.Position.South )
}
val _tabs = new TabbedPane
_tabs.peer.putClientProperty("styleId", "attached") // XXX TODO: obsolete
_tabs.focusable = false
val pageEdit = new TabbedPane.Page("Editor" , paneEdit , null)
val pageRender = new TabbedPane.Page("Rendered", renderer.component, null)
_tabs.pages += pageEdit
_tabs.pages += pageRender
// _tabs.pages += pageAttr
Util.addTabNavigation(_tabs)
// render(initialText)
tabs = _tabs
component = _tabs
if (showEditor) {
paneImpl.component.requestFocus()
} else {
_tabs.selection.index = 1
// paneEdit.preferredSize = renderer.component.preferredSize
}
}
}
}