diff options
author | Martin Odersky <odersky@gmail.com> | 2008-03-13 15:43:11 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2008-03-13 15:43:11 +0000 |
commit | 954309658261dde811c30f2511d45f622f4b8880 (patch) | |
tree | ead2ce36d75d681c2b6ebd46360d9c2215ed684a | |
parent | f8a14831d854fd00aaa5cb229fb1eb6c7351ba25 (diff) | |
download | scala-954309658261dde811c30f2511d45f622f4b8880.tar.gz scala-954309658261dde811c30f2511d45f622f4b8880.tar.bz2 scala-954309658261dde811c30f2511d45f622f4b8880.zip |
added scala.swing
48 files changed, 845 insertions, 0 deletions
diff --git a/src/swing/scala/swing/AWTComponentWrapper.scala b/src/swing/scala/swing/AWTComponentWrapper.scala new file mode 100644 index 0000000000..9a140e5506 --- /dev/null +++ b/src/swing/scala/swing/AWTComponentWrapper.scala @@ -0,0 +1,3 @@ +package swing; + +class AWTComponentWrapper(val acomponent: java.awt.Component) extends Component diff --git a/src/swing/scala/swing/Button.scala b/src/swing/scala/swing/Button.scala new file mode 100644 index 0000000000..0f6d8dc056 --- /dev/null +++ b/src/swing/scala/swing/Button.scala @@ -0,0 +1,20 @@ +package swing; + +import javax.swing._ +import event._ + +/** A class for buttons; standard constructor wraps around a swing button */ +class Button(val jbutton: JButton) extends Container(jbutton) with SwingComponent with Publisher { + def this(txt: String) = this(new JButton(txt)) + def this() = this(new JButton()) + def text: String = jbutton.getText() + def text_=(s: String) = jbutton.setText(s) + def icon: Icon = jbutton.getIcon() + def icon_=(i: Icon) = jbutton.setIcon(i) + jbutton.addActionListener { + new java.awt.event.ActionListener { + def actionPerformed(e: java.awt.event.ActionEvent): unit = + publish(ButtonPressed(Button.this)) + } + } +} diff --git a/src/swing/scala/swing/Caret.scala b/src/swing/scala/swing/Caret.scala new file mode 100644 index 0000000000..9c3cc903de --- /dev/null +++ b/src/swing/scala/swing/Caret.scala @@ -0,0 +1,8 @@ +package swing; + +import javax.swing + +class Caret(val jcaret: swing.text.Caret) extends Publisher { + def dot: int = jcaret.getDot() + def mark: int = jcaret.getMark() +} diff --git a/src/swing/scala/swing/Color.scala b/src/swing/scala/swing/Color.scala new file mode 100644 index 0000000000..fff628d8bd --- /dev/null +++ b/src/swing/scala/swing/Color.scala @@ -0,0 +1,5 @@ +package swing; + +case class Color(r: Int, g: Int, b: Int) extends java.awt.Color(r, g, b) { + def this(col: java.awt.Color) = this(col.getRed, col.getGreen, col.getBlue) +} diff --git a/src/swing/scala/swing/Component.scala b/src/swing/scala/swing/Component.scala new file mode 100644 index 0000000000..3a5c98d90b --- /dev/null +++ b/src/swing/scala/swing/Component.scala @@ -0,0 +1,26 @@ +package swing; + +import javax.swing._; +import java.awt._; + +abstract class Component +extends Object +with Reactor +{ + val acomponent: java.awt.Component + + def preferredSize = acomponent.getPreferredSize + def preferredSize_=(x: Dimension) = acomponent.setPreferredSize(x) + def preferredSize_=(xy: (Int, Int)) = acomponent.setPreferredSize(Dimension(xy._1, xy._2)) + + def foreground: Color = new Color(acomponent.getForeground) + def foreground_=(x: Color) = acomponent.setForeground(x) + + def background: Color = new Color(acomponent.getBackground) + def background_=(x: Color) = acomponent.setBackground(x) + + def font: Font = acomponent.getFont + def font_=(x: Font) = acomponent.setFont(x) + + def show: this.type = { acomponent.setVisible(true); this } +} diff --git a/src/swing/scala/swing/ComponentList.scala b/src/swing/scala/swing/ComponentList.scala new file mode 100644 index 0000000000..3876c84492 --- /dev/null +++ b/src/swing/scala/swing/ComponentList.scala @@ -0,0 +1,15 @@ +package swing + +import javax.swing._ +import event._ + +class ComponentList(val jlist: JList) extends Container(jlist) with SwingComponent with Publisher { + def this() = this(new JList()) + def this(elems: Seq[Object]) = this(new JList(elems.toArray)) + + def fixedCellWidth = jlist.getFixedCellWidth + def fixedCellWidth_=(x: Int) = jlist.setFixedCellWidth(x) + + def fixedCellHeight = jlist.getFixedCellHeight + def fixedCellHeight_=(x: Int) = jlist.setFixedCellHeight(x) +} diff --git a/src/swing/scala/swing/Container.scala b/src/swing/scala/swing/Container.scala new file mode 100644 index 0000000000..12cbf11072 --- /dev/null +++ b/src/swing/scala/swing/Container.scala @@ -0,0 +1,19 @@ +package swing; + +import javax.swing._ +import scala.collection.mutable.ListBuffer + +class Container(val jcontainer: java.awt.Container) extends Component { + val acomponent = jcontainer + def this() = this(new java.awt.Container()) + val elems = new ListBuffer[Component] + def += (c: Component) = { + elems += c + jcontainer.add(c.acomponent) + } + def -= (c: Component) = { + elems -= c + jcontainer.remove(c.acomponent) + } +} + diff --git a/src/swing/scala/swing/Dimension.scala b/src/swing/scala/swing/Dimension.scala new file mode 100644 index 0000000000..2c1ebde994 --- /dev/null +++ b/src/swing/scala/swing/Dimension.scala @@ -0,0 +1,5 @@ +package swing; + +case class Dimension(w: Int, h: Int) extends java.awt.Dimension(w, h) { + def this(dim: java.awt.Dimension) = this(dim.width, dim.height) +} diff --git a/src/swing/scala/swing/EmptyBorder.scala b/src/swing/scala/swing/EmptyBorder.scala new file mode 100644 index 0000000000..4524f00fe3 --- /dev/null +++ b/src/swing/scala/swing/EmptyBorder.scala @@ -0,0 +1,11 @@ +package swing; + +import javax.swing._ + +class EmptyBorder(_top: int, _left: int, _bottom: int, _right: int) +extends border.EmptyBorder(_top, _left, _bottom, _right) { + def this() = this(0, 0, 0, 0) +} + + + diff --git a/src/swing/scala/swing/FormattedTextField.scala b/src/swing/scala/swing/FormattedTextField.scala new file mode 100644 index 0000000000..7b88d1b5e8 --- /dev/null +++ b/src/swing/scala/swing/FormattedTextField.scala @@ -0,0 +1,9 @@ +package swing; + +import javax.swing._ +import java.awt.event._ +import event._ + +class FormattedTextField(val jftextfield: JFormattedTextField) extends TextComponent(jftextfield) { + def this(format: java.text.Format) = this(new JFormattedTextField(format)); +} diff --git a/src/swing/scala/swing/Frame.scala b/src/swing/scala/swing/Frame.scala new file mode 100644 index 0000000000..fadd69d00c --- /dev/null +++ b/src/swing/scala/swing/Frame.scala @@ -0,0 +1,26 @@ +package swing; + +import javax.swing._; +import event._; + +class Frame(val jframe: JFrame) extends Container(jframe) with Publisher { + def this() = this(new JFrame("Untitled Frame")) + def title: String = jframe.getTitle() + def title_=(s: String) = jframe.setTitle(s) + val contents = new Container(jframe.getContentPane()) + private var default_button: Button = null + def defaultButton = default_button + def defaultButton_=(b: Button) = { default_button = b; jframe.getRootPane().setDefaultButton(b.jbutton) } + def pack: this.type = { jframe.pack(); this } + jframe.addWindowListener { + new java.awt.event.WindowListener { + def windowActivated(e: java.awt.event.WindowEvent) = publish(WindowActivated(Frame.this)) + def windowClosed(e: java.awt.event.WindowEvent) = publish(WindowClosed(Frame.this)) + def windowClosing(e: java.awt.event.WindowEvent) = publish(WindowClosing(Frame.this)) + def windowDeactivated(e: java.awt.event.WindowEvent) = publish(WindowDeactivated(Frame.this)) + def windowDeiconified(e: java.awt.event.WindowEvent) = publish(WindowDeiconified(Frame.this)) + def windowIconified(e: java.awt.event.WindowEvent) = publish(WindowIconified(Frame.this)) + def windowOpened(e: java.awt.event.WindowEvent) = publish(WindowOpened(Frame.this)) + } + } +} diff --git a/src/swing/scala/swing/GUIApplication.scala b/src/swing/scala/swing/GUIApplication.scala new file mode 100644 index 0000000000..39c2a96561 --- /dev/null +++ b/src/swing/scala/swing/GUIApplication.scala @@ -0,0 +1,23 @@ +package swing + +import javax.swing._ +import event.Event + +class GUIApplication { + + type Reaction = PartialFunction[Event, unit] + + def defaultLookAndFeelDecorated: boolean = true + + def init() = { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) + JFrame.setDefaultLookAndFeelDecorated(defaultLookAndFeelDecorated) + } + + def run(prog: => unit): unit = + SwingUtilities.invokeLater { + new Runnable() { + def run() = { init(); prog } + } + } +} diff --git a/src/swing/scala/swing/Label.scala b/src/swing/scala/swing/Label.scala new file mode 100644 index 0000000000..622d74d9de --- /dev/null +++ b/src/swing/scala/swing/Label.scala @@ -0,0 +1,14 @@ +package swing; + +import javax.swing._; + +class Label(val jlabel: JLabel) extends Container(jlabel) with SwingComponent { + def this(txt: String) = this(new JLabel(txt)) + def this() = this("Untitled Label") + def text: String = jlabel.getText() + def text_=(s: String) = jlabel.setText(s) + def halign: Orientation.Value = Orientation(jlabel.getHorizontalAlignment()) + def halign_=(x: Orientation.Value) = jlabel.setHorizontalAlignment(x.id) + def valign: Orientation.Value = Orientation(jlabel.getVerticalAlignment()) + def valign_=(x: Orientation.Value) = jlabel.setVerticalAlignment(x.id) +} diff --git a/src/swing/scala/swing/MainFrame.scala b/src/swing/scala/swing/MainFrame.scala new file mode 100644 index 0000000000..59701ae5e9 --- /dev/null +++ b/src/swing/scala/swing/MainFrame.scala @@ -0,0 +1,11 @@ +package swing; + +import javax.swing._; +import event._; + +class MainFrame(jframe: JFrame) extends Frame(jframe) { + def this() = this(new JFrame("Untitled Frame")) + reactions += { + case WindowClosing(_) => System.exit(1) + } +} diff --git a/src/swing/scala/swing/Orientation.scala b/src/swing/scala/swing/Orientation.scala new file mode 100644 index 0000000000..4c6bed34ab --- /dev/null +++ b/src/swing/scala/swing/Orientation.scala @@ -0,0 +1,11 @@ +package swing + +import javax.swing.SwingConstants._ + +object Orientation extends Enumeration { + val left = Value(LEFT, "left") + val right = Value(RIGHT, "right") + val bottom = Value(BOTTOM, "bottom") + val top = Value(TOP, "top") + val center = Value(CENTER, "center") +} diff --git a/src/swing/scala/swing/Panel.scala b/src/swing/scala/swing/Panel.scala new file mode 100644 index 0000000000..e848e1bb7f --- /dev/null +++ b/src/swing/scala/swing/Panel.scala @@ -0,0 +1,15 @@ +package swing; + +import javax.swing._ +import java.awt.event._ + +class Panel(val jpanel: JPanel) extends Container(jpanel) with SwingComponent { + def this(layout: java.awt.LayoutManager, elements: Component*) = { + this(new JPanel(layout)); + for (val elem <- elements) this += elem + } + def this(elements: Component*) = this(new java.awt.FlowLayout, elements: _*) + + def layout: java.awt.LayoutManager = jpanel.getLayout() + def layout_=(x: java.awt.LayoutManager) = jpanel.setLayout(x) +} diff --git a/src/swing/scala/swing/Publisher.scala b/src/swing/scala/swing/Publisher.scala new file mode 100644 index 0000000000..09ff2522fe --- /dev/null +++ b/src/swing/scala/swing/Publisher.scala @@ -0,0 +1,18 @@ +package swing; + +import scala.collection.mutable.HashSet +import event.Event + +trait Publisher extends Object with Reactor { + + protected var listeners = new HashSet[Reactions]; + + def subscribe(listener: Reactions) = { listeners += listener } + + def unsubscribe(listener: Reactions) = { listeners -= listener } + + def publish(e: Event) = + for (val l <- listeners) l.send(e) + + listenTo(this) +} diff --git a/src/swing/scala/swing/Reactions.scala b/src/swing/scala/swing/Reactions.scala new file mode 100644 index 0000000000..9322c181a0 --- /dev/null +++ b/src/swing/scala/swing/Reactions.scala @@ -0,0 +1,28 @@ +package swing + +import event.Event + +class Reactions { + type Reaction = PartialFunction[Event, unit] + + private var parts: List[Reaction] = List() + + def += (r: Reaction) = { parts = r :: parts } + + def -= (r: Reaction) = { + def withoutR(xs: List[Reaction]): List[Reaction] = + if (xs.isEmpty) xs + else if (xs.head == r) xs.tail + else xs.head :: withoutR(xs.tail) + parts = withoutR(parts) + } + + def send(e: Event) = { + def sendTo(ps: List[Reaction]): Unit = ps match { + case Nil => + case p :: ps => + if (p isDefinedAt e) p(e) else sendTo(ps) + } + sendTo(parts) + } +} diff --git a/src/swing/scala/swing/Reactor.scala b/src/swing/scala/swing/Reactor.scala new file mode 100644 index 0000000000..0867454f44 --- /dev/null +++ b/src/swing/scala/swing/Reactor.scala @@ -0,0 +1,7 @@ +package swing; + +trait Reactor { + val reactions = new Reactions + def listenTo(ps: Publisher*) = for (val p <- ps) p.subscribe(reactions) + def deafTo(ps: Publisher*) = for (val p <- ps) p.unsubscribe(reactions) +} diff --git a/src/swing/scala/swing/ScrollPane.scala b/src/swing/scala/swing/ScrollPane.scala new file mode 100644 index 0000000000..d66d590d2e --- /dev/null +++ b/src/swing/scala/swing/ScrollPane.scala @@ -0,0 +1,18 @@ +package swing; + +import javax.swing._ +import javax.swing.table._ +import javax.swing.event._ +import event._ + +class ScrollPane(val jscrollpane: JScrollPane) extends Container(jscrollpane) with SwingComponent with Publisher { + def this() = this(new JScrollPane()) + def this(contents: Component) = this(new JScrollPane(contents.acomponent)) + + def rowHeaderView: Component = null + def rowHeaderView_=(c: Component) = jscrollpane.setRowHeaderView(c.acomponent) + + def viewportView: Component = null + def viewportView_=(c: Component) = jscrollpane.setViewportView(c.acomponent) + +} diff --git a/src/swing/scala/swing/SimpleGUIApplication.scala b/src/swing/scala/swing/SimpleGUIApplication.scala new file mode 100644 index 0000000000..43708d955a --- /dev/null +++ b/src/swing/scala/swing/SimpleGUIApplication.scala @@ -0,0 +1,18 @@ +package swing + +import javax.swing._ + +abstract class SimpleGUIApplication extends GUIApplication { + + def top: Frame; + + def main(args: Array[String]) = { + SwingUtilities.invokeLater { + new Runnable() { + def run(): unit = { init(); top.pack.show } + } + } + } + + implicit def string2label(s: String): Label = new Label(s) +} diff --git a/src/swing/scala/swing/Slider.scala b/src/swing/scala/swing/Slider.scala new file mode 100644 index 0000000000..18fae3225b --- /dev/null +++ b/src/swing/scala/swing/Slider.scala @@ -0,0 +1,17 @@ +package swing; + +import javax.swing._ +import event._ + +class Slider(val jbutton: JButton) extends Container(jbutton) with Publisher { + def this(txt: String) = this(new JButton(txt)) + def this() = this("Untitled Button") + def text: String = jbutton.getText() + def text_=(s: String) = jbutton.setText(s) + jbutton.addActionListener { + new java.awt.event.ActionListener { + def actionPerformed(e: java.awt.event.ActionEvent): unit = + {} + } + } +} diff --git a/src/swing/scala/swing/Spreadsheet.scala b/src/swing/scala/swing/Spreadsheet.scala new file mode 100644 index 0000000000..6bd0d4a21e --- /dev/null +++ b/src/swing/scala/swing/Spreadsheet.scala @@ -0,0 +1,100 @@ +package swing + +import javax.swing.table.{AbstractTableModel, TableCellRenderer} +import javax.swing.event.{TableModelListener, TableModelEvent} +import javax.swing._ +import event._ + +class Spreadsheet(width: Int, height: Int) extends Component with SwingComponent with Publisher { + + + def showGrid: Boolean = acomponent.table.getShowHorizontalLines && acomponent.table.getShowVerticalLines + def showGrid_=(x: Boolean) = acomponent.table.setShowGrid(x) + + case class SpreadsheetCell(var userData: String, var displayData: String) + + val data = model.Matrix[SpreadsheetCell](SpreadsheetCell("", ""), width, height) + + val acomponent = new JScrollPane { + setPreferredSize(new java.awt.Dimension(800, 600)) + val table = new JTable { + setSelectionModel(new DefaultListSelectionModel) + setAutoResizeMode(JTable.AUTO_RESIZE_OFF) + setRowHeight(25) + val model = new AbstractTableModel { model => + class UserDataEvent(col: Int, row: Int) extends TableModelEvent(model, row, row, col) + val getRowCount = height + val getColumnCount = width + def getValueAt(row: Int, col: Int) = + data(col, row).userData + override def setValueAt(value: AnyRef, row: Int, col: Int) = { + data(col, row).userData = value.toString + fireTableChanged(new UserDataEvent(col, row)) + } + def setDisplayValueAt(value: AnyRef, row: Int, col: Int) = { + data(col, row).displayData = value.toString + fireTableCellUpdated(row, col) + } + override def isCellEditable(row: Int, col: Int) = true + addTableModelListener(new TableModelListener { + def tableChanged(event: TableModelEvent) = event match { + case de:UserDataEvent => + publish(CellModified(event.getColumn, event.getFirstRow, data(event.getColumn, event.getFirstRow).userData)) + case _ => + } + }) + } + setModel(model) + setDefaultRenderer(classOf[AnyRef], new TableCellRenderer { + def getTableCellRendererComponent(table: JTable, value: AnyRef, selected: Boolean, focus: Boolean, row: Int, col: Int) = + if (focus) new JTextField { + setText(data(col, row).userData) + } + else new JLabel { + setText(data(col, row).displayData) + } + }) + getTableHeader.setReorderingAllowed(false) + getTableHeader.setResizingAllowed(false) + } + setViewportView(table) + setColumnHeaderView(table.getTableHeader) + setRowHeaderView(new JList { + setListData(((1 to height) map (_.toString)).toArray.asInstanceOf[Array[Object]]) + setFixedCellWidth(50) + setCellRenderer(new JLabel with ListCellRenderer { + setOpaque(true) + setBorder(UIManager.getBorder("TableHeader.cellBorder")) + setHorizontalAlignment(SwingConstants.CENTER) + setForeground(table.getTableHeader.getForeground) + setBackground(table.getTableHeader.getBackground) + setFont(table.getTableHeader.getFont) + def getListCellRendererComponent(list: JList, value: AnyRef, index: Int, isSelected: Boolean, cellHasFocus: Boolean) = { + setPreferredSize(new Dimension(50, table.getRowHeight(index))) + setText(value.toString) + this + } + }) + }) + def corner = new JLabel{ + setOpaque(true) + setBorder(UIManager.getBorder("TableHeader.cellBorder")) + setForeground(table.getTableHeader.getForeground) + setBackground(table.getTableHeader.getBackground) + } + setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER, corner) + setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER, corner) + setCorner(ScrollPaneConstants.LOWER_LEFT_CORNER, corner) + setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER, corner) + } + + def update(col: Int, row: Int, value: Any) = { + acomponent.table.model.setDisplayValueAt(value.toString, row, col) + } +// SwingUtilities.invokeLater(new Runnable { +// def run = acomponent.table.model.setDisplayValueAt(value.toString, row, col) +// }) + + def apply(pos: (Int, Int)) = data(pos._1, pos._2).userData + +} diff --git a/src/swing/scala/swing/SwingComponent.scala b/src/swing/scala/swing/SwingComponent.scala new file mode 100644 index 0000000000..73c7c43aaf --- /dev/null +++ b/src/swing/scala/swing/SwingComponent.scala @@ -0,0 +1,11 @@ +package swing + +import javax.swing._ +import java.awt._ + +trait SwingComponent extends Component { + val jcomponent = acomponent.asInstanceOf[JComponent]; + def border: javax.swing.border.Border = jcomponent.getBorder() + def border_=(x: javax.swing.border.Border): unit = jcomponent.setBorder(x) +} + diff --git a/src/swing/scala/swing/Table.scala b/src/swing/scala/swing/Table.scala new file mode 100644 index 0000000000..17a44797c6 --- /dev/null +++ b/src/swing/scala/swing/Table.scala @@ -0,0 +1,80 @@ +package swing + +import model.Matrix +import javax.swing._ +import javax.swing.table._ +import javax.swing.event._ +import event._ + +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") + } +} + +class Table(val jtable: JTable) extends Container(jtable) with SwingComponent 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)) + + def rowHeight = jtable.getRowHeight + def rowHeight_=(x: Int) = jtable.setRowHeight(x) + + def rowCount = jtable.getRowCount + + def model = jtable.getModel() + def model_=(x: TableModel) = jtable.setModel(x) + + def autoResizeMode: AutoResizeMode.Value = AutoResizeMode(jtable.getAutoResizeMode) + def autoResizeMode_=(x: Table.AutoResizeMode.Value) = jtable.setAutoResizeMode(x.id) + + def showGrid = jtable.getShowHorizontalLines && jtable.getShowVerticalLines + def showGrid_=(grid: Boolean) = jtable.setShowGrid(grid) + + def gridColor = new swing.Color(jtable.getGridColor) + def gridColor_=(color: swing.Color) = jtable.setGridColor(color) + + private val initialRenderer = jtable.getDefaultRenderer(classOf[AnyRef]) + + protected def render(isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int): Component = + new AWTComponentWrapper( + initialRenderer.getTableCellRendererComponent(jtable, jtable.getValueAt(row, column), isSelected, hasFocus, row, column)) + + jtable.setDefaultRenderer(classOf[AnyRef], new TableCellRenderer { + def getTableCellRendererComponent(jtable: JTable, value: AnyRef, isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int) = + render(isSelected, hasFocus, row, column).acomponent + }) + + def apply(row: Int, column: Int) = jtable.getValueAt(row, column) + def update(row: Int, column: Int, value: AnyRef) = jtable.setValueAt(value, row, column) + + def markUpdated(row: Int, column: Int) = update(row, column, apply(row, column)) + + /* + jtable.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) + ) + }) +} diff --git a/src/swing/scala/swing/TextComponent.scala b/src/swing/scala/swing/TextComponent.scala new file mode 100644 index 0000000000..353349ffcf --- /dev/null +++ b/src/swing/scala/swing/TextComponent.scala @@ -0,0 +1,23 @@ +package swing + +import javax.swing._ +import javax.swing.text.JTextComponent +import javax.swing.event.{CaretEvent,CaretListener} +import event.CaretUpdate + +class TextComponent(val jtextcomponent: JTextComponent) +extends Container(jtextcomponent) with SwingComponent with Publisher { + + def text: String = jtextcomponent.getText() + def text_=(x: String) = jtextcomponent.setText(x) + + val caret = new Caret(jtextcomponent.getCaret()) + + jtextcomponent.addCaretListener { + new CaretListener { + def caretUpdate(e: CaretEvent) = + publish(CaretUpdate(TextComponent.this)) + } + } +} + diff --git a/src/swing/scala/swing/TextField.scala b/src/swing/scala/swing/TextField.scala new file mode 100644 index 0000000000..5dd781011d --- /dev/null +++ b/src/swing/scala/swing/TextField.scala @@ -0,0 +1,22 @@ +package swing; + +import javax.swing._ +import java.awt.event._ +import event._ + +class TextField(val jtextfield: JTextField) extends TextComponent(jtextfield) { + def this(text: String, columns: int) = this(new JTextField(text, columns)); + def this(text: String) = this(new JTextField(text)); + def this(columns: int) = this(new JTextField(columns)); + def this() = this(new JTextField()); + + def columns: int = jtextfield.getColumns() + def columns_=(x: int) = jtextfield.setColumns(x) + + jtextfield.addActionListener { + new ActionListener { + def actionPerformed(e: ActionEvent) = + publish(TextModified(TextField.this)) + } + } +} diff --git a/src/swing/scala/swing/event/ButtonPressed.scala b/src/swing/scala/swing/event/ButtonPressed.scala new file mode 100644 index 0000000000..fb7850e93d --- /dev/null +++ b/src/swing/scala/swing/event/ButtonPressed.scala @@ -0,0 +1,3 @@ +package swing.event + +case class ButtonPressed(b: Button) extends Event diff --git a/src/swing/scala/swing/event/CaretUpdate.scala b/src/swing/scala/swing/event/CaretUpdate.scala new file mode 100644 index 0000000000..98d8b7f27c --- /dev/null +++ b/src/swing/scala/swing/event/CaretUpdate.scala @@ -0,0 +1,3 @@ +package swing.event + +case class CaretUpdate(text: TextComponent) extends Event diff --git a/src/swing/scala/swing/event/CellModified.scala b/src/swing/scala/swing/event/CellModified.scala new file mode 100644 index 0000000000..bfaf61013b --- /dev/null +++ b/src/swing/scala/swing/event/CellModified.scala @@ -0,0 +1,3 @@ +package swing.event + +case class CellModified(col: Int, row: int, value: String) extends Event diff --git a/src/swing/scala/swing/event/Event.scala b/src/swing/scala/swing/event/Event.scala new file mode 100644 index 0000000000..21306d5b94 --- /dev/null +++ b/src/swing/scala/swing/event/Event.scala @@ -0,0 +1,3 @@ +package swing.event + +abstract class Event diff --git a/src/swing/scala/swing/event/TableChanged.scala b/src/swing/scala/swing/event/TableChanged.scala new file mode 100644 index 0000000000..3e1c6f8f6d --- /dev/null +++ b/src/swing/scala/swing/event/TableChanged.scala @@ -0,0 +1,5 @@ +package swing.event + +case class TableChanged(table: Table, firstRow: Int, lastRow: Int, column: Int) extends Event { + println("table changed: "+table+"/"+firstRow+"-"+lastRow+":"+column) +} diff --git a/src/swing/scala/swing/event/TableResized.scala b/src/swing/scala/swing/event/TableResized.scala new file mode 100644 index 0000000000..716ae89bf0 --- /dev/null +++ b/src/swing/scala/swing/event/TableResized.scala @@ -0,0 +1,3 @@ +package swing.event; + +case class TableResized(table: Table) extends Event diff --git a/src/swing/scala/swing/event/TextModified.scala b/src/swing/scala/swing/event/TextModified.scala new file mode 100644 index 0000000000..703d7711a0 --- /dev/null +++ b/src/swing/scala/swing/event/TextModified.scala @@ -0,0 +1,3 @@ +package swing.event + +case class TextModified(text: TextComponent) extends Event diff --git a/src/swing/scala/swing/event/WindowActivated.scala b/src/swing/scala/swing/event/WindowActivated.scala new file mode 100644 index 0000000000..5f4e0b3bbf --- /dev/null +++ b/src/swing/scala/swing/event/WindowActivated.scala @@ -0,0 +1,3 @@ +package swing.event + +case class WindowActivated(window: Frame) extends WindowEvent; diff --git a/src/swing/scala/swing/event/WindowClosed.scala b/src/swing/scala/swing/event/WindowClosed.scala new file mode 100644 index 0000000000..5834d69c46 --- /dev/null +++ b/src/swing/scala/swing/event/WindowClosed.scala @@ -0,0 +1,3 @@ +package swing.event + +case class WindowClosed(window: Frame) extends WindowEvent; diff --git a/src/swing/scala/swing/event/WindowClosing.scala b/src/swing/scala/swing/event/WindowClosing.scala new file mode 100644 index 0000000000..6938590347 --- /dev/null +++ b/src/swing/scala/swing/event/WindowClosing.scala @@ -0,0 +1,3 @@ +package swing.event + +case class WindowClosing(window: Frame) extends WindowEvent; diff --git a/src/swing/scala/swing/event/WindowDeactivated.scala b/src/swing/scala/swing/event/WindowDeactivated.scala new file mode 100644 index 0000000000..d44214c73d --- /dev/null +++ b/src/swing/scala/swing/event/WindowDeactivated.scala @@ -0,0 +1,3 @@ +package swing.event + +case class WindowDeactivated(window: Frame) extends WindowEvent; diff --git a/src/swing/scala/swing/event/WindowDeiconified.scala b/src/swing/scala/swing/event/WindowDeiconified.scala new file mode 100644 index 0000000000..6a956a47d2 --- /dev/null +++ b/src/swing/scala/swing/event/WindowDeiconified.scala @@ -0,0 +1,3 @@ +package swing.event + +case class WindowDeiconified(window: Frame) extends WindowEvent; diff --git a/src/swing/scala/swing/event/WindowEvent.scala b/src/swing/scala/swing/event/WindowEvent.scala new file mode 100644 index 0000000000..c6c8b6ffca --- /dev/null +++ b/src/swing/scala/swing/event/WindowEvent.scala @@ -0,0 +1,5 @@ +package swing.event + +abstract class WindowEvent extends Event { + val window: Frame +} diff --git a/src/swing/scala/swing/event/WindowIconified.scala b/src/swing/scala/swing/event/WindowIconified.scala new file mode 100644 index 0000000000..2b6f6378c3 --- /dev/null +++ b/src/swing/scala/swing/event/WindowIconified.scala @@ -0,0 +1,3 @@ +package swing.event + +case class WindowIconified(window: Frame) extends WindowEvent; diff --git a/src/swing/scala/swing/event/WindowOpened.scala b/src/swing/scala/swing/event/WindowOpened.scala new file mode 100644 index 0000000000..952176670a --- /dev/null +++ b/src/swing/scala/swing/event/WindowOpened.scala @@ -0,0 +1,3 @@ +package swing.event + +case class WindowOpened(window: Frame) extends WindowEvent; diff --git a/src/swing/scala/swing/layout.scala b/src/swing/scala/swing/layout.scala new file mode 100644 index 0000000000..3628b2de3a --- /dev/null +++ b/src/swing/scala/swing/layout.scala @@ -0,0 +1,12 @@ +package swing + +import java.awt._ + +object layout { + + val flex = 0 + + def grid(rows: int, columns: int) = new GridLayout(rows, columns) + def row = new FlowLayout() + def column = grid(flex, 1) +} diff --git a/src/swing/scala/swing/model/Matrix.scala b/src/swing/scala/swing/model/Matrix.scala new file mode 100644 index 0000000000..9bd4c9c4d0 --- /dev/null +++ b/src/swing/scala/swing/model/Matrix.scala @@ -0,0 +1,108 @@ +package swing.model + +trait Matrix[A]extends Function2[Int, Int, A] { + + val width: Int + val height: Int + + assert(width > 0 && height > 0) + + private val delegate = new Array[A](width * height) + + override def apply(col: Int, row: Int): A = + delegate(col * height + row) + + def apply(coord: (Int, Int)): A = + apply(coord._1, coord._2) + + def col(index: Int): Matrix.FlatSeq[A] = + new Matrix.SubArray[A](delegate, index * height, height) + + def row(index: Int): Matrix.FlatSeq[A] = + new Matrix.SparseArray[A](delegate, index, height) + + def update(xpos: Int, ypos: Int, elem: A) { + delegate(xpos % width * height + ypos % height) = elem + } + + def update(coord: (Int, Int), elem: A) { + update(coord._1, coord._2, elem) + } + + def initializeWith(f: (Int, Int) => A): this.type = { + for (index <- 0 until (width * height)) + delegate(index) = f(index / height, index % height) + this + } + + def initializeTo(v: => A): this.type = { + for (index <- 0 until (width * height)) + delegate(index) = v + this + } + + def size: (Int, Int) = (width, height) + + /** A flattened view of the matrix. The flattening is done on columns i.e. + * the first values of the flattened sequence are the cells of the first + * column. As this is a view of the matrix, any change to the matrix will + * also be visible in the flattened array, and vice-versa. */ + def flat: Array[A] = delegate + +} + +object Matrix { + + def apply[A](columns: Int, rows: Int) = new Matrix[A] { + val width = columns + val height = rows + } + + def apply[A](default: (Int, Int) => A, columns: Int, rows: Int) = new Matrix[A] { + val width = columns + val height = rows + initializeWith(default) + } + + def apply[A](default: => A, columns: Int, rows: Int) = new Matrix[A] { + val width = columns + val height = rows + initializeTo(default) + } + + trait FlatSeq[A] extends RandomAccessSeq[A] { + def update (index: Int, elem: A): Unit + } + + private class SubArray[A](delegate: Array[A], start: Int, val length: Int) extends FlatSeq[A] { + def apply(index: Int): A = + if (index < length) + delegate(index + start) + else throw new IndexOutOfBoundsException + def update(index: Int, elem: A): Unit = + if (index < length) + delegate(index + start) = elem + else throw new IndexOutOfBoundsException + } + + private class SparseArray[A](delegate: Array[A], start: Int, span: Int) extends FlatSeq[A] { + def apply(index: Int): A = { + if (index < length) + delegate((index * span) + start) + else throw new IndexOutOfBoundsException + } + def length: Int = delegate.length / span + def update(index: Int, elem: A): Unit = + if (index < length) + delegate((index * span) + start) = elem + else throw new IndexOutOfBoundsException + } + + implicit def MatrixToSeqs[A](matrix: Matrix[A]): Seq[Seq[A]] = { + val result = new Array[SubArray[A]](matrix.width) + for (val col <- 0 until matrix.width) + result(col) = new SubArray[A](matrix.delegate, col * matrix.height, matrix.height) + result + } + +} diff --git a/src/swing/scala/swing/test/CelsiusConverter.scala b/src/swing/scala/swing/test/CelsiusConverter.scala new file mode 100644 index 0000000000..8d78667aa5 --- /dev/null +++ b/src/swing/scala/swing/test/CelsiusConverter.scala @@ -0,0 +1,40 @@ +package swing.test; + +import swing._ +import layout._ +import event._ +import Orientation.{left, right} + +/** A GUI app to convert celsius to centigrade + */ +object CelsiusConverter extends SimpleGUIApplication { + def top: Frame = new MainFrame { + title = "Convert Celsius to Fahrenheit" + defaultButton = convertButton + object tempCelsius extends TextField(); + object celsiusLabel extends Label { + text = "Celsius" + border = new EmptyBorder(5, 5, 5, 5) + } + object convertButton extends Button { + icon = new javax.swing.ImageIcon("c:\\workspace\\gui\\images\\convert.gif") + border = new EmptyBorder(5, 5, 5, 5) + } + object fahrenheitLabel extends Label { + text = "Fahrenheit " + border = new EmptyBorder(5, 5, 5, 5) + listenTo(convertButton, tempCelsius) + reactions += { + case ButtonPressed(_) | TextModified(_) => + val c = Integer.parseInt(tempCelsius.text) + val f = c * 9 / 5 + 32 + text = "<html><font color = red>"+f+"</font> Fahrenheit</html>" + } + } + contents += new Panel(tempCelsius, celsiusLabel, convertButton, fahrenheitLabel) { + layout = grid(2, 2) + border = new EmptyBorder(10, 10, 10, 10) + } + } +} + diff --git a/src/swing/scala/swing/test/CelsiusConverter2.scala b/src/swing/scala/swing/test/CelsiusConverter2.scala new file mode 100644 index 0000000000..5813b3042d --- /dev/null +++ b/src/swing/scala/swing/test/CelsiusConverter2.scala @@ -0,0 +1,30 @@ +package swing.test; + +import swing._ +import layout._ +import event._ +import Orientation.{left, right, center} + +object CelsiusConverter2 extends SimpleGUIApplication { + def top = new MainFrame { + title = "Convert Celsius / Fahrenheit" + object Celsius extends TextField { columns = 5 }; + object Fahrenheit extends TextField { columns = 5 } + contents += new Panel(Celsius, new Label(" Celsius = "), + Fahrenheit, new Label(" Fahrenheit")) { + border = new EmptyBorder(15, 10, 10, 10) + } + listenTo(Fahrenheit, Celsius) + reactions += { + case TextModified(Fahrenheit) => + val f = Integer.parseInt(Fahrenheit.text) + val c = (f - 32) * 5 / 9 + Celsius.text = c.toString + case TextModified(Celsius) => + val c = Integer.parseInt(Celsius.text) + val f = c * 9 / 5 + 32 + Fahrenheit.text = f.toString + } + } +} + diff --git a/src/swing/scala/swing/test/HelloWorld.scala b/src/swing/scala/swing/test/HelloWorld.scala new file mode 100644 index 0000000000..e19a6e92e4 --- /dev/null +++ b/src/swing/scala/swing/test/HelloWorld.scala @@ -0,0 +1,13 @@ +package swing.test; + +import swing._; +import javax.swing._; + +object HelloWorld extends GUIApplication { + def main(args: Array[String]) = run { + new Frame { + title = "HelloWorldSwing"; + contents += new Label("Hello World"); + }.pack.show + } +} diff --git a/src/swing/scala/swing/test/SwingApp.scala b/src/swing/scala/swing/test/SwingApp.scala new file mode 100644 index 0000000000..1712b74ee4 --- /dev/null +++ b/src/swing/scala/swing/test/SwingApp.scala @@ -0,0 +1,30 @@ +package swing.test; + +import swing._ +import swing.event._ +import swing.layout._ + +object SwingApp extends SimpleGUIApplication { + def top = new MainFrame { + title = "SwingApp" + var numclicks = 0 + object label extends Label { + val prefix = "Number of button clicks: " + text = prefix + "0 " + listenTo(button) + reactions += { + case ButtonPressed(button) => + numclicks = numclicks + 1 + text = prefix + numclicks + } + } + object button extends Button { + text = "I am a button" + } + contents += new Panel(label, button) { + layout = column + border = new EmptyBorder(30, 30, 10, 30) + } + } +} + |