commit c703e45d907f794cbee7b0f7c605cca87c05d5c5 Author: oleg20111511 Date: Wed Jun 1 21:19:28 2022 +0300 Project received diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a3417b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/out/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..09e91bf --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..f6545e3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Derevia.in b/Derevia.in new file mode 100644 index 0000000..d97dc32 --- /dev/null +++ b/Derevia.in @@ -0,0 +1,11 @@ +10 4 +Яблоко Яблоко Яблоко Яблоко +Яблоко Груша Вишня Персик +Яблоко Абрикос Вишня Слива +Яблоко Айва Слива Слива +Яблоко Слива Слива Слива +Яблоко Слива Слива Слива +Яблоко Слива Слива Слива +Яблоко Слива Слива Слива +Яблоко Слива Слива Слива +Яблоко Слива Слива Слива \ No newline at end of file diff --git a/Urojai.in b/Urojai.in new file mode 100644 index 0000000..0775bfe --- /dev/null +++ b/Urojai.in @@ -0,0 +1,10 @@ +1.1 1.2 1.3 1.4 +2.1 2.2 2.3 2.4 +3.1 3.2 3.3 3.4 +4.1 4.2 4.3 4.4 +5.1 5.2 5.3 5.4 +5.1 5.2 5.3 5.4 +5.1 5.2 5.3 5.4 +5.1 5.2 5.3 5.4 +5.1 5.2 5.3 5.4 +5.1 5.2 5.3 5.4 \ No newline at end of file diff --git a/livada.iml b/livada.iml new file mode 100644 index 0000000..c90834f --- /dev/null +++ b/livada.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Data.java b/src/Data.java new file mode 100644 index 0000000..316173f --- /dev/null +++ b/src/Data.java @@ -0,0 +1,39 @@ +public class Data { + private String tree; + private double harvest; + + public Data(String tree, double harvest) { + this.tree = tree; + this.harvest = harvest; + } + + /** + * Restore data from table cell value + * + * @param data Value in JTable + */ + public Data(String data) { + String[] split = data.split("-"); + this.tree = split[0].trim(); + this.harvest = Double.parseDouble(split[1].trim()); + } + + public String getTree() { + return tree; + } + + public double getHarvest() { + return harvest; + } + + @Override + public String toString() { + return String.format("%s - %.3f", tree, harvest); + } + + public static void main(String[] args) { + System.out.println(new Data("Яблоко", 1.123)); + System.out.println(new Data("Яблоко - 2.2345")); + System.out.println(new Data("Яблоко - 123456789.123456789")); + } +} diff --git a/src/DataReader.java b/src/DataReader.java new file mode 100644 index 0000000..72fec4a --- /dev/null +++ b/src/DataReader.java @@ -0,0 +1,63 @@ +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; + +public class DataReader { + public String[][] readTrees(String fileName) { + String[][] data; + try (BufferedReader br = new BufferedReader(new FileReader(fileName))) { + String header = br.readLine(); + String[] size = header.split(" "); + int n = Integer.parseInt(size[0]); + int m = Integer.parseInt(size[1]); + + System.out.printf("Array size is %d x %d", n, m); + + data = new String[n][m]; + + String line; + int row = 0; + while ((line = br.readLine()) != null) { + String[] trees = line.split(" "); + data[row++] = trees; + } + Arrays.toString(data); + } catch (IOException e) { + throw new RuntimeException(e); + } + return data; + } + + public double[][] readHarvest(String fileName, int rows) { + double[][] data = new double[rows][]; + int row = 0; + try (BufferedReader br = new BufferedReader(new FileReader(fileName))) { + String line; + while ((line = br.readLine()) != null) { + String[] s = line.split(" "); + double[] centners = new double[s.length]; + for (int i = 0; i < s.length; i++) { + centners[i] = Double.parseDouble(s[i]); + } + data[row++] = centners; + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + return data; + } + + public Data[][] mergeData(String[][] trees, double[][] harvest) { + Data[][] result = new Data[trees.length][trees[0].length]; + + for (int row = 0; row < trees.length; row++) { + for (int col = 0; col < trees[0].length; col++) { + result[row][col] = new Data(trees[row][col], harvest[row][col]); + } + } + + return result; + } +} diff --git a/src/DataTableModel.java b/src/DataTableModel.java new file mode 100644 index 0000000..b3e36eb --- /dev/null +++ b/src/DataTableModel.java @@ -0,0 +1,129 @@ +import javax.swing.*; +import javax.swing.table.AbstractTableModel; + +public class DataTableModel extends AbstractTableModel { + + private Data[][] rowData; + private String[] columnNames; + + public DataTableModel(Data[][] rowData, String[] columnNames) { + this.rowData = rowData; + this.columnNames = columnNames; + } + + public Data[][] getRowData() { + return rowData; + } + + public void addColumn(int i) { + if (i > rowData[0].length) { + String message = String.format("Невозможно добавить столбец %d так как в таблце всего %d колонок", i, rowData[0].length); + JOptionPane.showMessageDialog(null, message, "Неправильный индекс столбца", JOptionPane.ERROR_MESSAGE); + return; + } + + Data[][] newRowData = new Data[rowData.length][rowData[0].length + 1]; + for (int row = 0; row < rowData.length; row++) { + for (int col = 0; col < rowData[0].length + 1; col++) { + if (col == i) { + newRowData[row][col] = new Data("", 0.); + } else if (col > i) { + newRowData[row][col] = rowData[row][col - 1]; + } else { + newRowData[row][col] = rowData[row][col]; + } + } + } + rowData = newRowData; + + String[] newColumnNames = new String[columnNames.length + 1]; + for (int col = 0; col < columnNames.length + 1; col++) { + newColumnNames[col] = col + 1 + ""; + } + columnNames = newColumnNames; + + this.fireTableDataChanged(); + this.fireTableStructureChanged(); + } + +// public void removeColumn(int i) { +// +// Data colToDelete = columnNames.getColumnModel().getColumn(number); +// table.removeColumn(colToDelete); +// ColumnField.setText(""); +// } + + + + public void addRow(int i) { + if (i > rowData[0].length) { + String message = String.format("Невозможно добавить строчку %d так как в таблце всего %d колонок", i, rowData[0].length); + JOptionPane.showMessageDialog(null, message, "Неправильный индекс строчки", JOptionPane.ERROR_MESSAGE); + return; + } + Data[][] newRowData = new Data[rowData.length + 1][rowData[0].length]; + for (int row = 0; row < rowData.length + 1; row++) { + for (int col = 0; col < rowData[0].length; col++) { + if (row == i) { + newRowData[row][col] = new Data("", 0.); + } else if (row > i) { + newRowData[row][col] = rowData[row - 1][col]; + } else { + newRowData[row][col] = rowData[row][col]; + } + } + } + + rowData = newRowData; + + + this.fireTableDataChanged(); + this.fireTableStructureChanged(); + + + } + + public void removeRow(int i) + { + + } + + public String[] getColumnNames() { + return columnNames; + } + + public void setColumnNames(String[] columnNames) { + this.columnNames = columnNames; + } + + @Override + public String getColumnName(int column) { + return columnNames[column]; + } + + @Override + public int getRowCount() { + return rowData.length; + } + + @Override + public int getColumnCount() { + return columnNames.length; + } + + @Override + public Object getValueAt(int row, int col) { + return rowData[row][col]; + } + + @Override + public boolean isCellEditable(int row, int column) { + return true; + } + + @Override + public void setValueAt(Object value, int row, int col) { + rowData[row][col] = new Data(value.toString()); + fireTableCellUpdated(row, col); + } +} diff --git a/src/LivadaPlan.java b/src/LivadaPlan.java new file mode 100644 index 0000000..443e7d4 --- /dev/null +++ b/src/LivadaPlan.java @@ -0,0 +1,35 @@ +import javax.swing.*; +import java.awt.*; + + +public class LivadaPlan extends JPanel { + + private DataTableModel tableModel; + + public LivadaPlan(Data[][] data) { + super(new BorderLayout()); + + String[] header = createHeader(data[0].length); + + tableModel = new DataTableModel(data, header); + JTable table = new JTable(tableModel); + JScrollPane scrollPane = new JScrollPane(table); + + JTable rowTable = new RowNumberTable(table); + scrollPane.setRowHeaderView(rowTable); + scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, rowTable.getTableHeader()); + add(scrollPane, BorderLayout.CENTER); + } + + public DataTableModel getTableModel() { + return tableModel; + } + + private String[] createHeader(int length) { + String[] header = new String[length]; + for (int i = 1; i <= length; i++) { + header[i - 1] = i + ""; + } + return header; + } +} diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000..190b06f --- /dev/null +++ b/src/Main.java @@ -0,0 +1,45 @@ +import javax.swing.*; + +public class Main { + public static void main(String[] args) { + //Schedule a job for the event-dispatching thread: + //creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void run() { + createAndShowGUI(); + } + }); + } + + private static void createAndShowGUI() { + try { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | + IllegalAccessException e) { + e.printStackTrace(); + } + + //Create and set up the window. + JFrame frame = new JFrame("Livada"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + DataReader dataReader = new DataReader(); + + String[][] trees = dataReader.readTrees("Derevia.in"); + double[][] harvest = dataReader.readHarvest("Urojai.in", trees.length); + + Data[][] data = dataReader.mergeData(trees, harvest); + + //Create and set up the content pane. + LivadaPlan livadaPlan = new LivadaPlan(data); + livadaPlan.setOpaque(true); + frame.setContentPane(livadaPlan); + + // Add menu bar with all 7 actions + frame.setJMenuBar(new MenuBar(livadaPlan.getTableModel())); + + //Display the window. + frame.pack(); + frame.setVisible(true); + } +} \ No newline at end of file diff --git a/src/MenuBar.java b/src/MenuBar.java new file mode 100644 index 0000000..3a0195b --- /dev/null +++ b/src/MenuBar.java @@ -0,0 +1,114 @@ +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class MenuBar extends JMenuBar { + private DataTableModel tableModel; + + public MenuBar(DataTableModel tableModel) { + this.tableModel = tableModel; + JMenu menu = new JMenu("Меню"); + this.add(menu); + + menu.add(createAddRowColItem()); + menu.add(createRemoveRowColItem()); + menu.add(createMockMenuItem("Макс Урожай")); + menu.add(createMockMenuItem("Средн Урожай")); + menu.add(createMockMenuItem("Общий урожай")); + menu.add(createMockMenuItem("Lin Spec")); + menu.add(createMockMenuItem("Сектор К деревьев")); + } + private JMenuItem createRemoveRowColItem() { + JMenuItem item = new JMenuItem(); + item.setText("Удалить"); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + System.out.println("Remove Row/Col"); + Object[] options = {"Удалить строку", "Удалить колонку", "Отмена"}; + + + JPanel panel = new JPanel(); + panel.add(new JLabel("Введите номер строки или стоблца который должен быть удален")); + JTextField textField = new JTextField(10); + panel.add(textField); + + int result = JOptionPane.showOptionDialog(null, + panel, "Введите номер строки / столбца", + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.PLAIN_MESSAGE, + null, + options, + null); + if (textField.getText().trim().isEmpty()) { + return; + + } + if (result == JOptionPane.YES_OPTION) { + int row = Integer.parseInt(textField.getText()); + System.out.println("Remove row # " + row); + tableModel.removeRow(row - 1); + } else if (result == JOptionPane.NO_OPTION) { + int column = Integer.parseInt(textField.getText()); + System.out.println("Remove column # " + column); +// tableModel.removeColumn(column - 1); + } else { + System.out.println("Cancel add row/col"); + } + } + }); return item; + + } + + private JMenuItem createMockMenuItem(String text) { + JMenuItem item = new JMenuItem(text); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JOptionPane.showMessageDialog(null, text); + } + }); + return item; + } + + private JMenuItem createAddRowColItem() { + JMenuItem item = new JMenuItem(); + item.setText("Добавить"); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + System.out.println("Create Row/Col"); + + Object[] options = {"Добавить строку", "Добавить колонку", "Отмена"}; + + JPanel panel = new JPanel(); + panel.add(new JLabel("Введите номер строки или стоблца который должен быть добавлен")); + JTextField textField = new JTextField(10); + panel.add(textField); + + int result = JOptionPane.showOptionDialog(null, + panel, "Введите номер строки / столбца", + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.PLAIN_MESSAGE, + null, + options, + null); + if (textField.getText().trim().isEmpty()) { + return; + } + if (result == JOptionPane.YES_OPTION) { + int row = Integer.parseInt(textField.getText()); + System.out.println("Add row # " + row); + tableModel.addRow(row); + } else if (result == JOptionPane.NO_OPTION) { + int column = Integer.parseInt(textField.getText()); + System.out.println("Add column # " + column); + tableModel.addColumn(column - 1); + } else { + System.out.println("Cancel add row/col"); + } + } + }); + return item; + } +} diff --git a/src/MenuDemo.java b/src/MenuDemo.java new file mode 100644 index 0000000..7766d05 --- /dev/null +++ b/src/MenuDemo.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle or the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; + +/* MenuDemo.java requires images/middle.gif. */ + +/* + * This class is just like MenuLookDemo, except the menu items + * actually do something, thanks to event listeners. + */ +public class MenuDemo implements ActionListener, ItemListener { + JTextArea output; + JScrollPane scrollPane; + String newline = "\n"; + + public JMenuBar createMenuBar() { + JMenuBar menuBar; + JMenu menu, submenu; + JMenuItem menuItem; + JRadioButtonMenuItem rbMenuItem; + JCheckBoxMenuItem cbMenuItem; + + //Create the menu bar. + menuBar = new JMenuBar(); + + //Build the first menu. + menu = new JMenu("A Menu"); + menu.setMnemonic(KeyEvent.VK_A); + menu.getAccessibleContext().setAccessibleDescription( + "The only menu in this program that has menu items"); + menuBar.add(menu); + + //a group of JMenuItems + menuItem = new JMenuItem("A text-only menu item", + KeyEvent.VK_T); + //menuItem.setMnemonic(KeyEvent.VK_T); //used constructor instead + menuItem.setAccelerator(KeyStroke.getKeyStroke( + KeyEvent.VK_1, ActionEvent.ALT_MASK)); + menuItem.getAccessibleContext().setAccessibleDescription( + "This doesn't really do anything"); + menuItem.addActionListener(this); + menu.add(menuItem); + + ImageIcon icon = createImageIcon("images/middle.gif"); + menuItem = new JMenuItem("Both text and icon", icon); + menuItem.setMnemonic(KeyEvent.VK_B); + menuItem.addActionListener(this); + menu.add(menuItem); + + menuItem = new JMenuItem(icon); + menuItem.setMnemonic(KeyEvent.VK_D); + menuItem.addActionListener(this); + menu.add(menuItem); + + //a group of radio button menu items + menu.addSeparator(); + ButtonGroup group = new ButtonGroup(); + + rbMenuItem = new JRadioButtonMenuItem("A radio button menu item"); + rbMenuItem.setSelected(true); + rbMenuItem.setMnemonic(KeyEvent.VK_R); + group.add(rbMenuItem); + rbMenuItem.addActionListener(this); + menu.add(rbMenuItem); + + rbMenuItem = new JRadioButtonMenuItem("Another one"); + rbMenuItem.setMnemonic(KeyEvent.VK_O); + group.add(rbMenuItem); + rbMenuItem.addActionListener(this); + menu.add(rbMenuItem); + + //a group of check box menu items + menu.addSeparator(); + cbMenuItem = new JCheckBoxMenuItem("A check box menu item"); + cbMenuItem.setMnemonic(KeyEvent.VK_C); + cbMenuItem.addItemListener(this); + menu.add(cbMenuItem); + + cbMenuItem = new JCheckBoxMenuItem("Another one"); + cbMenuItem.setMnemonic(KeyEvent.VK_H); + cbMenuItem.addItemListener(this); + menu.add(cbMenuItem); + + //a submenu + menu.addSeparator(); + submenu = new JMenu("A submenu"); + submenu.setMnemonic(KeyEvent.VK_S); + + menuItem = new JMenuItem("An item in the submenu"); + menuItem.setAccelerator(KeyStroke.getKeyStroke( + KeyEvent.VK_2, ActionEvent.ALT_MASK)); + menuItem.addActionListener(this); + submenu.add(menuItem); + + menuItem = new JMenuItem("Another item"); + menuItem.addActionListener(this); + submenu.add(menuItem); + menu.add(submenu); + + //Build second menu in the menu bar. + JMenuItem anotherMenu = new JMenuItem("Another Menu"); + anotherMenu.setMnemonic(KeyEvent.VK_N); + anotherMenu.getAccessibleContext().setAccessibleDescription("This menu does nothing"); + anotherMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + System.out.println("Click!"); +// JOptionPane.showMessageDialog(null, "Hello There!"); + } + }); + menuBar.add(anotherMenu); + + return menuBar; + } + + public Container createContentPane() { + //Create the content-pane-to-be. + JPanel contentPane = new JPanel(new BorderLayout()); + contentPane.setOpaque(true); + + //Create a scrolled text area. + output = new JTextArea(5, 30); + output.setEditable(false); + scrollPane = new JScrollPane(output); + + //Add the text area to the content pane. + contentPane.add(scrollPane, BorderLayout.CENTER); + + return contentPane; + } + + public void actionPerformed(ActionEvent e) { + JMenuItem source = (JMenuItem)(e.getSource()); + String s = "Action event detected." + + newline + + " Event source: " + source.getText() + + " (an instance of " + getClassName(source) + ")"; + output.append(s + newline); + output.setCaretPosition(output.getDocument().getLength()); + } + + public void itemStateChanged(ItemEvent e) { + JMenuItem source = (JMenuItem)(e.getSource()); + String s = "Item event detected." + + newline + + " Event source: " + source.getText() + + " (an instance of " + getClassName(source) + ")" + + newline + + " New state: " + + ((e.getStateChange() == ItemEvent.SELECTED) ? + "selected":"unselected"); + output.append(s + newline); + output.setCaretPosition(output.getDocument().getLength()); + } + + // Returns just the class name -- no package info. + protected String getClassName(Object o) { + String classString = o.getClass().getName(); + int dotIndex = classString.lastIndexOf("."); + return classString.substring(dotIndex+1); + } + + /** Returns an ImageIcon, or null if the path was invalid. */ + protected static ImageIcon createImageIcon(String path) { + java.net.URL imgURL = MenuDemo.class.getResource(path); + if (imgURL != null) { + return new ImageIcon(imgURL); + } else { + System.err.println("Couldn't find file: " + path); + return null; + } + } + + /** + * Create the GUI and show it. For thread safety, + * this method should be invoked from the + * event-dispatching thread. + */ + private static void createAndShowGUI() { + //Create and set up the window. + JFrame frame = new JFrame("MenuDemo"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + //Create and set up the content pane. + MenuDemo demo = new MenuDemo(); + frame.setJMenuBar(demo.createMenuBar()); + frame.setContentPane(demo.createContentPane()); + + //Display the window. + frame.setSize(450, 260); + frame.setVisible(true); + } + + public static void main(String[] args) { + //Schedule a job for the event-dispatching thread: + //creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void run() { + createAndShowGUI(); + } + }); + } +} \ No newline at end of file diff --git a/src/RowNumberTable.java b/src/RowNumberTable.java new file mode 100644 index 0000000..0bca52d --- /dev/null +++ b/src/RowNumberTable.java @@ -0,0 +1,167 @@ +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableColumn; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + + +/* + * This code is taken from - https://tips4java.wordpress.com/2008/11/18/row-number-table/ + * Use a JTable as a renderer for row numbers of a given main table. + * This table must be added to the row header of the scrollpane that contains the main table. + */ +public class RowNumberTable extends JTable + implements ChangeListener, PropertyChangeListener, TableModelListener { + private JTable main; + + public RowNumberTable(JTable table) { + main = table; + main.addPropertyChangeListener(this); + main.getModel().addTableModelListener(this); + + setFocusable(false); + setAutoCreateColumnsFromModel(false); + setSelectionModel(main.getSelectionModel()); + + TableColumn column = new TableColumn(); + column.setHeaderValue(" "); + addColumn(column); + column.setCellRenderer(new RowNumberRenderer()); + + getColumnModel().getColumn(0).setPreferredWidth(50); + setPreferredScrollableViewportSize(getPreferredSize()); + } + + @Override + public void addNotify() { + super.addNotify(); + + Component c = getParent(); + + // Keep scrolling of the row table in sync with the main table. + + if (c instanceof JViewport) { + JViewport viewport = (JViewport) c; + viewport.addChangeListener(this); + } + } + + /* + * Delegate method to main table + */ + @Override + public int getRowCount() { + return main.getRowCount(); + } + + @Override + public int getRowHeight(int row) { + int rowHeight = main.getRowHeight(row); + + if (rowHeight != super.getRowHeight(row)) { + super.setRowHeight(row, rowHeight); + } + + return rowHeight; + } + + /* + * No model is being used for this table so just use the row number + * as the value of the cell. + */ + @Override + public Object getValueAt(int row, int column) { + return Integer.toString(row + 1); + } + + /* + * Don't edit data in the main TableModel by mistake + */ + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + /* + * Do nothing since the table ignores the model + */ + @Override + public void setValueAt(Object value, int row, int column) { + } + + // +// Implement the ChangeListener +// + public void stateChanged(ChangeEvent e) { + // Keep the scrolling of the row table in sync with main table + + JViewport viewport = (JViewport) e.getSource(); + JScrollPane scrollPane = (JScrollPane) viewport.getParent(); + scrollPane.getVerticalScrollBar().setValue(viewport.getViewPosition().y); + } + + // +// Implement the PropertyChangeListener +// + public void propertyChange(PropertyChangeEvent e) { + // Keep the row table in sync with the main table + + if ("selectionModel".equals(e.getPropertyName())) { + setSelectionModel(main.getSelectionModel()); + } + + if ("rowHeight".equals(e.getPropertyName())) { + repaint(); + } + + if ("model".equals(e.getPropertyName())) { + main.getModel().addTableModelListener(this); + revalidate(); + } + } + + // +// Implement the TableModelListener +// + @Override + public void tableChanged(TableModelEvent e) { + revalidate(); + } + + /* + * Attempt to mimic the table header renderer + */ + private static class RowNumberRenderer extends DefaultTableCellRenderer { + public RowNumberRenderer() { + setHorizontalAlignment(JLabel.CENTER); + } + + public Component getTableCellRendererComponent( + JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + if (table != null) { + JTableHeader header = table.getTableHeader(); + + if (header != null) { + setForeground(header.getForeground()); + setBackground(header.getBackground()); + setFont(header.getFont()); + } + } + + if (isSelected) { + setFont(getFont().deriveFont(Font.BOLD)); + } + + setText((value == null) ? "" : value.toString()); + setBorder(UIManager.getBorder("TableHeader.cellBorder")); + + return this; + } + } +} \ No newline at end of file