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

scalaswingcontrib.tree.TreeModel.scala Maven / Gradle / Ivy

The newest version!
package scalaswingcontrib
package tree

import Tree.Path
import javax.swing.{tree => jst}

import scala.reflect.ClassTag

object TreeModel {
  
  /**
   * This value is the root node of every TreeModel's underlying javax.swing.tree.TreeModel.  As we wish to support multiple root 
   * nodes in a typesafe manner, we need to maintain a permanently hidden dummy root to hang the user's "root" nodes off.
   */
  private[tree] case object hiddenRoot
  
  def empty[A: ClassTag]: TreeModel[A] = new ExternalTreeModel[A](Seq.empty, _ => Seq.empty)
  def apply[A: ClassTag](roots: A*)(children: A => Seq[A]): TreeModel[A] = new ExternalTreeModel(roots, children)
}


trait TreeModel[A] {
  
  def roots: collection.Seq[A]
  val peer: jst.TreeModel 
  def getChildrenOf(parentPath: Path[A]): collection.Seq[A]
  def getChildPathsOf(parentPath: Path[A]): collection.Seq[Path[A]] = getChildrenOf(parentPath).map(parentPath :+ _)
  def filter(p: A => Boolean): TreeModel[A]
  def map[B: ClassTag](f: A => B): TreeModel[B]
  def foreach[U](f: A => U): Unit = { depthFirstIterator foreach f }
  def isExternalModel: Boolean
  def toInternalModel: InternalTreeModel[A]
  
  
  def pathToTreePath(path: Path[A]): jst.TreePath
  def treePathToPath(tp: jst.TreePath): Path[A]
 
  /**
   * Replace the item at the given path in the tree with a new value. 
   * Events are fired as appropriate.
   */
  def update(path: Path[A], newValue: A): Unit
  def remove(pathToRemove: Path[A]): Boolean
  def insertUnder(parentPath: Path[A], newValue: A, index: Int): Boolean
  
  def insertBefore(path: Path[A], newValue: A): Boolean = {
    if (path.isEmpty) throw new IllegalArgumentException("Cannot insert before empty path")
    
    val parentPath = path.init
    val index = siblingsUnder(parentPath) indexOf path.last
    insertUnder(parentPath, newValue, index)
  }
  
  def insertAfter(path: Path[A], newValue: A): Boolean = {
    if (path.isEmpty) throw new IllegalArgumentException("Cannot insert after empty path")
    
    val parentPath = path.init
    val index = siblingsUnder(parentPath) indexOf path.last
    insertUnder(parentPath, newValue, index + 1)
  }
  
  protected def siblingsUnder(parentPath: Path[A]) = if (parentPath.isEmpty) roots 
                                                     else getChildrenOf(parentPath)
  

  /**
   * Iterates sequentially through each item in the tree, either in breadth-first or depth-first ordering, 
   * as decided by the abstract pushChildren() method.
   */
  private trait TreeIterator extends Iterator[A] {
    protected var openNodes: Iterator[Path[A]] = roots.map(Path(_)).iterator

    def pushChildren(path: Path[A]): Unit
    def hasNext = openNodes.nonEmpty
    def next() = if (openNodes.hasNext) {
      val path = openNodes.next()
      pushChildren(path)
      path.last
    }
    else throw new NoSuchElementException("No more items")
  }
  
  def breadthFirstIterator: Iterator[A] = new TreeIterator {
    override def pushChildren(path: Path[A]): Unit = { openNodes ++= getChildPathsOf(path).toIterator }
  }
  
  def depthFirstIterator: Iterator[A] = new TreeIterator {
    override def pushChildren(path: Path[A]): Unit = {
      val open = openNodes
      openNodes = getChildPathsOf(path).toIterator ++ open // ++'s argument is by-name, and should not directly pass in a var
    }
  }
  
  def size: Int = depthFirstIterator.size
  
  def unpackNode(node: Any): A = node.asInstanceOf[A]

  private [tree] def isHiddenRoot(node: Any): Boolean = node == TreeModel.hiddenRoot
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy