scala.swing.ListView.scala Maven / Gradle / Ivy
The newest version!
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2007-2013, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
package scala.swing
import event._
import javax.swing._
import javax.swing.event._
object ListView {
/**
* The supported modes of user selections.
*/
object IntervalMode extends Enumeration {
val Single = Value(ListSelectionModel.SINGLE_SELECTION)
val SingleInterval = Value(ListSelectionModel.SINGLE_INTERVAL_SELECTION)
val MultiInterval = Value(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
}
def wrap[A](c: JList) = new ListView[A] {
override lazy val peer = c
}
object Renderer {
def wrap[A](r: ListCellRenderer): Renderer[A] = new Wrapped[A](r)
/**
* Wrapper for javax.swing.ListCellRenderers
*/
class Wrapped[A](override val peer: ListCellRenderer) extends Renderer[A] {
def componentFor(list: ListView[_], isSelected: Boolean, focused: Boolean, a: A, index: Int) = {
Component.wrap(peer.getListCellRendererComponent(list.peer, a, index, isSelected, focused).asInstanceOf[JComponent])
}
}
/**
* Returns a renderer for items of type A
. The given function
* converts items of type A
to items of type B
* for which a renderer is implicitly given. This allows chaining of
* renderers, e.g.:
*
*
* case class Person(name: String, email: String)
* val persons = List(Person("John", "[email protected]"), Person("Mary", "[email protected]"))
* new ListView(persons) {
* renderer = ListView.Renderer(_.name)
* }
*
*/
def apply[A,B](f: A => B)(implicit renderer: Renderer[B]): Renderer[A] = new Renderer[A] {
def componentFor(list: ListView[_], isSelected: Boolean, focused: Boolean, a: A, index: Int): Component =
renderer.componentFor(list, isSelected, focused, f(a), index)
}
}
/**
* Item renderer for a list view. This is contravariant on the type of the
* items, so a more general renderer can be used in place of a more specific
* one. For instance, an Any
renderer can be used for a list view
* of strings.
*
* @see javax.swing.ListCellRenderer
*/
abstract class Renderer[-A] {
def peer: ListCellRenderer = new ListCellRenderer {
def getListCellRendererComponent(list: JList, a: Any, index: Int, isSelected: Boolean, focused: Boolean) =
componentFor(ListView.wrap[A](list), isSelected, focused, a.asInstanceOf[A], index).peer
}
def componentFor(list: ListView[_], isSelected: Boolean, focused: Boolean, a: A, index: Int): Component
}
/**
* A default renderer that maintains a single component for item rendering
* and preconfigures it to sensible defaults. It is polymorphic on the
* component's type so clients can easily use component specific attributes
* during configuration.
*/
abstract class AbstractRenderer[-A, C<:Component](protected val component: C) extends Renderer[A] {
// The renderer component is responsible for painting selection
// backgrounds. Hence, make sure it is opaque to let it draw
// the background.
component.opaque = true
/**
* Standard preconfiguration that is commonly done for any component.
* This includes foreground and background colors, as well as colors
* of item selections.
*/
def preConfigure(list: ListView[_], isSelected: Boolean, focused: Boolean, a: A, index: Int) {
if (isSelected) {
component.background = list.selectionBackground
component.foreground = list.selectionForeground
} else {
component.background = list.background
component.foreground = list.foreground
}
}
/**
* Configuration that is specific to the component and this renderer.
*/
def configure(list: ListView[_], isSelected: Boolean, focused: Boolean, a: A, index: Int)
/**
* Configures the component before returning it.
*/
def componentFor(list: ListView[_], isSelected: Boolean, focused: Boolean, a: A, index: Int): Component = {
preConfigure(list, isSelected, focused, a, index)
configure(list, isSelected, focused, a, index)
component
}
}
/**
* A generic renderer that uses Swing's built-in renderers. If there is no
* specific renderer for a type, this renderer falls back to a renderer
* that renders the string returned from an item's toString
.
*/
implicit object GenericRenderer extends Renderer[Any] {
override lazy val peer: ListCellRenderer = new DefaultListCellRenderer
def componentFor(list: ListView[_], isSelected: Boolean, focused: Boolean, a: Any, index: Int): Component = {
val c = peer.getListCellRendererComponent(list.peer, a, index, isSelected, focused).asInstanceOf[JComponent]
Component.wrap(c)
}
}
}
/**
* A component that displays a number of elements in a list. A list view does
* not support inline editing of items. If you need it, use a table view instead.
*
* Named ListView
to avoid a clash with the frequently used
* scala.List
*
* @see javax.swing.JList
*/
class ListView[A] extends Component {
import ListView._
override lazy val peer: JList = new JList with SuperMixin
def this(items: Seq[A]) = {
this()
listData = items
}
protected class ModelWrapper(val items: Seq[A]) extends AbstractListModel {
def getElementAt(n: Int) = items(n).asInstanceOf[AnyRef]
def getSize = items.size
}
def listData: Seq[A] = peer.getModel match {
case model: ModelWrapper => model.items
case model @ _ => new Seq[A] { selfSeq =>
def length = model.getSize
def iterator = new Iterator[A] {
var idx = 0
def next = { idx += 1; apply(idx-1) }
def hasNext = idx < selfSeq.length
}
def apply(n: Int) = model.getElementAt(n).asInstanceOf[A]
}
}
def listData_=(items: Seq[A]) {
peer.setModel(new AbstractListModel {
def getElementAt(n: Int) = items(n).asInstanceOf[AnyRef]
def getSize = items.size
})
}
/**
* The current item selection.
*/
object selection extends Publisher {
protected abstract class Indices[A](a: =>Seq[A]) extends scala.collection.mutable.Set[A] {
def -=(n: A): this.type
def +=(n: A): this.type
def contains(n: A) = a.contains(n)
override def size = a.length
def iterator = a.iterator
}
def leadIndex: Int = peer.getSelectionModel.getLeadSelectionIndex
def anchorIndex: Int = peer.getSelectionModel.getAnchorSelectionIndex
/**
* The indices of the currently selected items.
*/
object indices extends Indices(peer.getSelectedIndices) {
def -=(n: Int): this.type = { peer.removeSelectionInterval(n,n); this }
def +=(n: Int): this.type = { peer.addSelectionInterval(n,n); this }
}
/**
* The currently selected items.
*/
lazy val items = peer.getSelectedValues.map(_.asInstanceOf[A])
def intervalMode: IntervalMode.Value = IntervalMode(peer.getSelectionModel.getSelectionMode)
def intervalMode_=(m: IntervalMode.Value) { peer.getSelectionModel.setSelectionMode(m.id) }
peer.getSelectionModel.addListSelectionListener(new ListSelectionListener {
def valueChanged(e: javax.swing.event.ListSelectionEvent) {
publish(new ListSelectionChanged(ListView.this, e.getFirstIndex to e.getLastIndex, e.getValueIsAdjusting))
}
})
def adjusting = peer.getSelectionModel.getValueIsAdjusting
}
def renderer: ListView.Renderer[A] = ListView.Renderer.wrap[A](peer.getCellRenderer)
def renderer_=(r: ListView.Renderer[A]) { peer.setCellRenderer(r.peer) }
def fixedCellWidth = peer.getFixedCellWidth
def fixedCellWidth_=(x: Int) = peer.setFixedCellWidth(x)
def fixedCellHeight = peer.getFixedCellHeight
def fixedCellHeight_=(x: Int) = peer.setFixedCellHeight(x)
def prototypeCellValue: A = peer.getPrototypeCellValue.asInstanceOf[A]
def prototypeCellValue_=(a: A) { peer.setPrototypeCellValue(a) }
def visibleRowCount = peer.getVisibleRowCount
def visibleRowCount_=(n: Int) = peer.setVisibleRowCount(n)
def ensureIndexIsVisible(idx: Int) = peer.ensureIndexIsVisible(idx)
def selectionForeground: Color = peer.getSelectionForeground
def selectionForeground_=(c: Color) = peer.setSelectionForeground(c)
def selectionBackground: Color = peer.getSelectionBackground
def selectionBackground_=(c: Color) = peer.setSelectionBackground(c)
def selectIndices(ind: Int*) = peer.setSelectedIndices(ind.toArray)
peer.getModel.addListDataListener(new ListDataListener {
def contentsChanged(e: ListDataEvent) { publish(ListChanged(ListView.this)) }
def intervalRemoved(e: ListDataEvent) { publish(ListElementsRemoved(ListView.this, e.getIndex0 to e.getIndex1)) }
def intervalAdded(e: ListDataEvent) { publish(ListElementsAdded(ListView.this, e.getIndex0 to e.getIndex1)) }
})
}