summaryrefslogtreecommitdiff
path: root/src/swing/scala/swing/Table.scala
blob: 0e7263be214d92a07ee86e952df2e565d43be156 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package scala.swing

import javax.swing._
import javax.swing.table._
import javax.swing.event._
import java.awt.{Dimension, Color}
import event._
import scala.collection.mutable.Set


object Table {
  object AutoResizeMode extends Enumeration {
    import JTable._
    val Off = Value(AUTO_RESIZE_OFF, "Off")
    val NextColumn = Value(AUTO_RESIZE_NEXT_COLUMN, "NextColumn")
    val SubsequentColumns = Value(AUTO_RESIZE_SUBSEQUENT_COLUMNS, "SubsequentColumns")
    val LastColumn = Value(AUTO_RESIZE_LAST_COLUMN, "LastColumn")
    val AllColumns = Value(AUTO_RESIZE_ALL_COLUMNS, "AllColumns")
  }

  object IntervalMode extends Enumeration {
    val Single = Value(ListSelectionModel.SINGLE_SELECTION)
    val SingleInterval = Value(ListSelectionModel.SINGLE_INTERVAL_SELECTION)
    val MultiInterval = Value(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
  }
  object ElementMode extends Enumeration {
    val Row, Column, Cell, None = Value
  }
}

/**
 * @see javax.swing.JTable
 */
class Table(override val peer: JTable) extends Component(peer) with Scrollable with Publisher {
  import Table._
  def this() = this(new JTable())
  def this(numRows: Int, numColumns: Int) = this(new JTable(numRows, numColumns))
  def this(rowData: Array[Array[AnyRef]], columnNames: Array[AnyRef]) = this(new JTable(rowData, columnNames))
  //todo: create constructor with List[List[Any]]

  def this(dm: TableModel) = this(new JTable(dm))
  def this(dm: TableModel, cm: TableColumnModel) = this(new JTable(dm, cm))
  def this(dm: TableModel, cm: TableColumnModel, sm: ListSelectionModel) = this(new JTable(dm, cm, sm))

  protected def scrollablePeer = peer

  def rowHeight = peer.getRowHeight
  def rowHeight_=(x: Int) = peer.setRowHeight(x)

  def rowCount = peer.getRowCount

  def model = peer.getModel()
  def model_=(x: TableModel) = peer.setModel(x)

  def autoResizeMode: AutoResizeMode.Value = AutoResizeMode(peer.getAutoResizeMode)
  def autoResizeMode_=(x: Table.AutoResizeMode.Value) = peer.setAutoResizeMode(x.id)

  def showGrid = peer.getShowHorizontalLines && peer.getShowVerticalLines
  def showGrid_=(grid: Boolean) = peer.setShowGrid(grid)

  def gridColor = peer.getGridColor
  def gridColor_=(color: Color) = peer.setGridColor(color)

  def preferredViewportSize_=(dim: Dimension) = peer.setPreferredScrollableViewportSize(dim)
  //1.6: def fillsViewportHeight: Boolean = peer.getFillsViewportHeight
  //def fillsViewportHeight_=(b: Boolean) = peer.setFillsViewportHeight(b)

  // TODO: could be a sorted set
  protected abstract class SelectionSet[A](a: =>Seq[A]) extends Set[A] {
    def -=(n: A)
    def +=(n: A)
    def contains(n: A) = a.contains(n)
    def size = a.length
    def elements = a.elements
  }

  class Selection extends Publisher {
    object rows extends SelectionSet(peer.getSelectedRows) {
      def -=(n: Int) { peer.removeRowSelectionInterval(n,n) }
      def +=(n: Int) { peer.addRowSelectionInterval(n,n) }

      def leadIndex: Int = peer.getSelectionModel.getLeadSelectionIndex
      def anchorIndex: Int = peer.getSelectionModel.getAnchorSelectionIndex
    }

    object columns extends SelectionSet(peer.getSelectedColumns) {
      def -=(n: Int) { peer.removeColumnSelectionInterval(n,n) }
      def +=(n: Int) { peer.addColumnSelectionInterval(n,n) }

      def leadIndex: Int = peer.getColumnModel.getSelectionModel.getLeadSelectionIndex
      def anchorIndex: Int = peer.getColumnModel.getSelectionModel.getAnchorSelectionIndex
    }

    def cells: Set[(Int, Int)] =
      new SelectionSet[(Int, Int)]((for(r <- selection.rows; c <- selection.columns) yield (r,c)).toSeq) { outer =>
        def -=(n: (Int, Int)) {
          peer.removeRowSelectionInterval(n._1,n._1)
          peer.removeColumnSelectionInterval(n._2,n._2)
        }
        def +=(n: (Int, Int)) {
          peer.addRowSelectionInterval(n._1,n._1)
          peer.addColumnSelectionInterval(n._2,n._2)
        }
        override def size = peer.getSelectedRowCount * peer.getSelectedColumnCount
      }

    /**
     * From the JTable Swing tutorial:
     * You can specify selection by cell in multiple interval selection mode,
     * but the result is a table that does not produce useful selections.
     */
    def intervalMode: IntervalMode.Value = IntervalMode(peer.getSelectionModel.getSelectionMode)
    def intervalMode_=(m: IntervalMode.Value) { peer.getSelectionModel.setSelectionMode(m.id) }
    def elementMode: ElementMode.Value =
      if(peer.getColumnSelectionAllowed && peer.getRowSelectionAllowed) ElementMode.Cell
      else if(peer.getColumnSelectionAllowed) ElementMode.Column
      else if(peer.getRowSelectionAllowed) ElementMode.Row
      else ElementMode.None
    def elementMode_=(m: ElementMode.Value) {
      m match {
        case ElementMode.Cell => peer.setCellSelectionEnabled(true)
        case ElementMode.Column => peer.setRowSelectionAllowed(false); peer.setColumnSelectionAllowed(true)
        case ElementMode.Row => peer.setRowSelectionAllowed(true); peer.setColumnSelectionAllowed(false)
        case ElementMode.None => peer.setRowSelectionAllowed(false); peer.setColumnSelectionAllowed(false)
      }
    }

    peer.getColumnModel.getSelectionModel.addListSelectionListener(new ListSelectionListener {
      def valueChanged(e: javax.swing.event.ListSelectionEvent) {
        publish(ColumnsSelected(Table.this, e.getFirstIndex to e.getLastIndex, e.getValueIsAdjusting))
      }
    })
    peer.getSelectionModel.addListSelectionListener(new ListSelectionListener {
      def valueChanged(e: javax.swing.event.ListSelectionEvent) {
        publish(RowsSelected(Table.this, e.getFirstIndex to e.getLastIndex, e.getValueIsAdjusting))
      }
    })
  }

  val selection: Selection = new Selection

  private val initialRenderer = peer.getDefaultRenderer(classOf[AnyRef])

  /**
   * Supplies a renderer component for a given cell.
   */
  protected def render(isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int): Component =
    new Component {
      override lazy val peer = initialRenderer.getTableCellRendererComponent(Table.this.peer,
                 Table.this.peer.getValueAt(row, column), isSelected, hasFocus, row, column).asInstanceOf[JComponent]
    }

  peer.setDefaultRenderer(classOf[AnyRef], new TableCellRenderer {
    def getTableCellRendererComponent(tabBlockIle: JTable, value: AnyRef, isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int) =
      render(isSelected, hasFocus, row, column).peer
  })

  def apply(row: Int, column: Int) = peer.getValueAt(row, column)
  def update(row: Int, column: Int, value: AnyRef) = peer.setValueAt(value, row, column)

  def markUpdated(row: Int, column: Int) = update(row, column, apply(row, column))

  /*
  peer.addActionListener {
    new java.awt.event.ActionListener {
      def actionPerformed(e: java.awt.event.ActionEvent): unit =
        publish(ButtonPressed(Button.this))
    }
  }
*/
  model.addTableModelListener(new TableModelListener {
    def tableChanged(event: TableModelEvent) = publish(
      if (event.getType == TableModelEvent.UPDATE)
        TableChanged(Table.this, event.getFirstRow, event.getLastRow, event.getColumn)
      else
    	TableResized(Table.this)
    )
  })
}