# HG changeset patch # User František Kučera # Date 1235836314 -3600 # Node ID 0359f2c3b3bae1b21c06fc02b972cd33461fbb35 Prvotní přidání zdrojáků JFTable – Swingová tabulka s možností řazení diff -r 000000000000 -r 0359f2c3b3ba .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sat Feb 28 16:51:54 2009 +0100 @@ -0,0 +1,3 @@ +java/JFTable/dist/* +java/JFTable/build/* +java/JFTable/nbproject/private diff -r 000000000000 -r 0359f2c3b3ba java/JFTable/manifest.mf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/JFTable/manifest.mf Sat Feb 28 16:51:54 2009 +0100 @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff -r 000000000000 -r 0359f2c3b3ba java/JFTable/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/JFTable/nbproject/build-impl.xml Sat Feb 28 16:51:54 2009 +0100 @@ -0,0 +1,627 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + diff -r 000000000000 -r 0359f2c3b3ba java/JFTable/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/JFTable/nbproject/genfiles.properties Sat Feb 28 16:51:54 2009 +0100 @@ -0,0 +1,8 @@ +build.xml.data.CRC32=18ffd276 +build.xml.script.CRC32=1966b3ef +build.xml.stylesheet.CRC32=be360661 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=18ffd276 +nbproject/build-impl.xml.script.CRC32=44ad41ff +nbproject/build-impl.xml.stylesheet.CRC32=f1d9da08 diff -r 000000000000 -r 0359f2c3b3ba java/JFTable/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/JFTable/nbproject/project.properties Sat Feb 28 16:51:54 2009 +0100 @@ -0,0 +1,59 @@ +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/JFTable.jar +dist.javadoc.dir=${dist.dir}/javadoc +excludes= +includes=** +jar.compress=false +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.source=1.5 +javac.target=1.5 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit.classpath}:\ + ${libs.junit_4.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class= +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff -r 000000000000 -r 0359f2c3b3ba java/JFTable/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/JFTable/nbproject/project.xml Sat Feb 28 16:51:54 2009 +0100 @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + JFTable + 1.6.5 + + + + + + + + + diff -r 000000000000 -r 0359f2c3b3ba java/JFTable/src/cz/frantovo/gui/tabulky/JTable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/JFTable/src/cz/frantovo/gui/tabulky/JTable.java Sat Feb 28 16:51:54 2009 +0100 @@ -0,0 +1,115 @@ +package cz.frantovo.gui.tabulky; + + +import java.awt.Point; +import java.awt.event.MouseEvent; + +import javax.swing.ImageIcon; +import javax.swing.table.TableModel; + +/** + * Tahle t��da umo��uje jednoduch�m zp�sobem p�idat va�im tabulk�m funkci + * �azen�. Sta�� ve va�� t��d� nahradit:
+ * "import javax.swing.JTable;"
+ * t�mto importem: " import cz.frantovo.gui.tabulky.JTable;
+ *
+ * Pomoc� kl�vesy ctrl jde �adit podle v�ce sloupc�"
+ *
+ * Tak� p�id�v� funkci zobrazov�n� tooltipu (zobrazuje obsah bu�ky) + * + * Proto�e t��d�c� model a skute�n� model obsahuj� ��dky v jin�m po�ad�,
+ * je pot�eba p�epo��tat index z�skan� metodou getSelectedRow() na index ve skute�n�m modelu
+ *
+ * k tomu slou�� tento postup:
+ * int r = jTable1.getSelectedRow();
+ * int rr = ((TableSorterModel)tabulka.getModel()).modelIndex(r);
+ *
+ * rr je potom index ��dku ve skute�n�m modelu. * + * + * @author František Kučera + */ +public class JTable extends javax.swing.JTable { + + private static final long serialVersionUID = -5133441062459764995L; + + private TableSorterModel tableSorterModel; + + private TableModel realTableModel; + + private boolean showTooltips = false; + + public boolean isShowTooltips () + { + return showTooltips; + } + + /** Nastavuje, zda se maj� zobrazovat tooltipy zobrazuj�c� obsah bu�ky */ + public void setShowTooltips (boolean showTooltips) + { + this.showTooltips = showTooltips; + } + + /** + * Nastav� TableModel a zabal� ho do TableSorterModelu, tabulka t�m + * z�sk� schopnost �azen� ��dk� + */ + @Override + public void setModel (TableModel dataModel) + { + realTableModel = dataModel; + tableSorterModel = new TableSorterModel(dataModel); + + tableSorterModel.setTableHeader(getTableHeader()); + + super.setModel(tableSorterModel); + } + + /** + * @return TableModel, kter� obsahuje data. Nen� tedy zabalen do + * TableSorterModelu + */ + public TableModel getRealTableModel () + { + return realTableModel; + } + + /** + * @return TableSorterModel, kter� obaluje skute�n� TableModel, kter� + * obsahuje data. TableSorterModel slou�� pouze k �azen� polo�ek + */ + @Override + public TableModel getModel () + { + return super.getModel(); + } + + /** + * Pokud je nastaveno showTooltips, zobrazuje v tooltipu obsah bu�ky pod + * my�� (hod� se, pokud je obsah bu�ky p��li� dlouh�). Pokud bu�ka + * obsahuje ImageIcon, pak tooltip zobrazuje ImageIcon.getDescription() + */ + @Override + public String getToolTipText (MouseEvent event) + { + if (showTooltips) { + Point p = event.getPoint(); + int col = columnAtPoint(p); + int rw = rowAtPoint(p); + if (col >= 0 && rw >= 0) { + Object o = getValueAt(rw, + col); + if (o != null) { + if (o instanceof ImageIcon) { + return ((ImageIcon) o).getDescription(); + } else { + return o.toString(); + } + } + } + return super.getToolTipText(); + } else { + return super.getToolTipText(); + } + } + +} diff -r 000000000000 -r 0359f2c3b3ba java/JFTable/src/cz/frantovo/gui/tabulky/TableSorterModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/JFTable/src/cz/frantovo/gui/tabulky/TableSorterModel.java Sat Feb 28 16:51:54 2009 +0100 @@ -0,0 +1,533 @@ +package cz.frantovo.gui.tabulky; + + +import java.awt.Component; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumnModel; +import javax.swing.table.TableModel; + +/** + * Tato t��da p�id�v� tabulce funk�nost �azen� podle v�ce sloupc�. Skute�n� + * model obsahuj�c� data sta�� obalit touto t��dou + je pot�eba nastavit: + * tableSorterModel.setTableHeader(table.getTableHeader());
+ *
+ * Jednodu��� ale je pou��vat t��du cz.ebanka.util.table.JTable, kter� v�echno + * �e�� za v�s.
+ *
+ * Tento TableSorterModel je zalo�en na p�vodn�m TableSorter od autor�: Philip + * Milne, Brendon McLean, Dan van Enckevort a Parwinder Sekhon.
+ *
+ * J� jsem p�idal hez�� grafick� �ipky v z�hlav� tabulky + * + * + * @author František Kučera + */ + +public class TableSorterModel extends AbstractTableModel { + + private static final long serialVersionUID = 7902301859145867387L; + + protected TableModel tableModel; + + public static final int DESCENDING = -1; + + public static final int NOT_SORTED = 0; + + public static final int ASCENDING = 1; + + private static Directive EMPTY_DIRECTIVE = new Directive( -1, + NOT_SORTED); + + public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() { + + public int compare (Object o1, + Object o2) + { + return ((Comparable) o1).compareTo(o2); + } + }; + + public static final Comparator LEXICAL_COMPARATOR = new Comparator() { + + public int compare (Object o1, + Object o2) + { + return o1.toString().compareTo(o2.toString()); + } + }; + + private Row[] viewToModel; + + private int[] modelToView; + + private JTableHeader tableHeader; + + private MouseListener mouseListener; + + private TableModelListener tableModelListener; + + private Map columnComparators = new HashMap(); + + private List sortingColumns = new ArrayList(); + + private ImageIcon headerIconDown = new ImageIcon(getClass().getResource("/cz/frantovo/gui/tabulky/dolu.png")); + + private ImageIcon headerIconUp = new ImageIcon(getClass().getResource("/cz/frantovo/gui/tabulky/nahoru.png")); + + public TableSorterModel () + { + this.mouseListener = new MouseHandler(); + this.tableModelListener = new TableModelHandler(); + } + + public TableSorterModel (TableModel tableModel) + { + this(); + setTableModel(tableModel); + } + + public TableSorterModel (TableModel tableModel, + JTableHeader tableHeader) + { + this(); + setTableHeader(tableHeader); + setTableModel(tableModel); + } + + private void clearSortingState () + { + viewToModel = null; + modelToView = null; + } + + public TableModel getTableModel () + { + return tableModel; + } + + public void setTableModel (TableModel tableModel) + { + if (this.tableModel != null) { + this.tableModel.removeTableModelListener(tableModelListener); + } + + this.tableModel = tableModel; + if (this.tableModel != null) { + this.tableModel.addTableModelListener(tableModelListener); + } + + clearSortingState(); + fireTableStructureChanged(); + } + + public JTableHeader getTableHeader () + { + return tableHeader; + } + + public void setTableHeader (JTableHeader tableHeader) + { + if (this.tableHeader != null) { + this.tableHeader.removeMouseListener(mouseListener); + TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer(); + if (defaultRenderer instanceof SortableHeaderRenderer) { + this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer); + } + } + this.tableHeader = tableHeader; + if (this.tableHeader != null) { + this.tableHeader.addMouseListener(mouseListener); + this.tableHeader.setDefaultRenderer(new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer())); + } + } + + public boolean isSorting () + { + return sortingColumns.size() != 0; + } + + private Directive getDirective (int column) + { + for (int i = 0; i < sortingColumns.size(); i++) { + Directive directive = (Directive) sortingColumns.get(i); + if (directive.column == column) { + return directive; + } + } + return EMPTY_DIRECTIVE; + } + + public int getSortingStatus (int column) + { + return getDirective(column).direction; + } + + private void sortingStatusChanged () + { + clearSortingState(); + fireTableDataChanged(); + if (tableHeader != null) { + tableHeader.repaint(); + } + } + + public void setSortingStatus (int column, + int status) + { + Directive directive = getDirective(column); + if (directive != EMPTY_DIRECTIVE) { + sortingColumns.remove(directive); + } + if (status != NOT_SORTED) { + sortingColumns.add(new Directive(column, + status)); + } + sortingStatusChanged(); + } + + protected Icon getHeaderRendererIcon (int column, + int size) + { + Directive directive = getDirective(column); + if (directive == EMPTY_DIRECTIVE) { + return null; + } + + if (directive.direction == DESCENDING) { + return headerIconDown; + } else { + return headerIconUp; + } + } + + private void cancelSorting () + { + sortingColumns.clear(); + sortingStatusChanged(); + } + + public void setColumnComparator (Class type, + Comparator comparator) + { + if (comparator == null) { + columnComparators.remove(type); + } else { + columnComparators.put(type, + comparator); + } + } + + protected Comparator getComparator (int column) + { + Class columnType = tableModel.getColumnClass(column); + Comparator comparator = (Comparator) columnComparators.get(columnType); + if (comparator != null) { + return comparator; + } + if (Comparable.class.isAssignableFrom(columnType)) { + return COMPARABLE_COMAPRATOR; + } + return LEXICAL_COMPARATOR; + } + + private Row[] getViewToModel () + { + if (viewToModel == null) { + int tableModelRowCount = tableModel.getRowCount(); + viewToModel = new Row[tableModelRowCount]; + for (int row = 0; row < tableModelRowCount; row++) { + viewToModel[row] = new Row(row); + } + + if (isSorting()) { + Arrays.sort(viewToModel); + } + } + return viewToModel; + } + + public int modelIndex (int viewIndex) + { + if (viewIndex > -1 && viewIndex < getViewToModel().length) { + return getViewToModel()[viewIndex].modelIndex; + } else { + return -1; + } + } + + private int[] getModelToView () + { + if (modelToView == null) { + int n = getViewToModel().length; + modelToView = new int[n]; + for (int i = 0; i < n; i++) { + modelToView[modelIndex(i)] = i; + } + } + return modelToView; + } + + // Metody rozhran� TableModel + + public int getRowCount () + { + return (tableModel == null) ? 0 : tableModel.getRowCount(); + } + + public int getColumnCount () + { + return (tableModel == null) ? 0 : tableModel.getColumnCount(); + } + + public String getColumnName (int column) + { + return tableModel.getColumnName(column); + } + + public Class getColumnClass (int column) + { + return tableModel.getColumnClass(column); + } + + public boolean isCellEditable (int row, + int column) + { + return tableModel.isCellEditable(modelIndex(row), + column); + } + + public Object getValueAt (int row, + int column) + { + return tableModel.getValueAt(modelIndex(row), + column); + } + + public void setValueAt (Object aValue, + int row, + int column) + { + tableModel.setValueAt(aValue, + modelIndex(row), + column); + } + + // Pomocn� t��dy + + private class Row implements Comparable { + + private int modelIndex; + + public Row (int index) + { + this.modelIndex = index; + } + + public int compareTo (Object o) + { + int row1 = modelIndex; + int row2 = ((Row) o).modelIndex; + + for (Iterator it = sortingColumns.iterator(); it.hasNext();) { + Directive directive = (Directive) it.next(); + int column = directive.column; + Object o1 = tableModel.getValueAt(row1, + column); + Object o2 = tableModel.getValueAt(row2, + column); + + int comparison = 0; + // Define null less than everything, except + // null. + if (o1 == null && o2 == null) { + comparison = 0; + } else if (o1 == null) { + comparison = -1; + } else if (o2 == null) { + comparison = 1; + } else { + comparison = getComparator(column).compare(o1, + o2); + } + if (comparison != 0) { + return directive.direction == DESCENDING ? -comparison : comparison; + } + } + return 0; + } + } + + private class TableModelHandler implements TableModelListener { + + public void tableChanged (TableModelEvent e) + { + // If we're not sorting by anything, just pass the event + // along. + if ( !isSorting()) { + clearSortingState(); + fireTableChanged(e); + return; + } + + // If the table structure has changed, cancel the + // sorting; the + // sorting columns may have been either moved or deleted + // from + // the model. + if (e.getFirstRow() == TableModelEvent.HEADER_ROW) { + cancelSorting(); + fireTableChanged(e); + return; + } + + // We can map a cell event through to the view without + // widening + // when the following conditions apply: + // + // a) all the changes are on one row (e.getFirstRow() == + // e.getLastRow()) and, + // b) all the changes are in one column (column != + // TableModelEvent.ALL_COLUMNS) and, + // c) we are not sorting on that column + // (getSortingStatus(column) == NOT_SORTED) and, + // d) a reverse lookup will not trigger a sort + // (modelToView != null) + // + // Note: INSERT and DELETE events fail this test as they + // have column == ALL_COLUMNS. + // + // The last check, for (modelToView != null) is to see + // if modelToView + // is already allocated. If we don't do this check; + // sorting can become + // a performance bottleneck for applications where cells + // change rapidly in different parts of the table. If + // cells + // change alternately in the sorting column and then + // outside of + // it this class can end up re-sorting on alternate cell + // updates - + // which can be a performance problem for large tables. + // The last + // clause avoids this problem. + int column = e.getColumn(); + if (e.getFirstRow() == e.getLastRow() && column != TableModelEvent.ALL_COLUMNS && getSortingStatus(column) == NOT_SORTED + && modelToView != null) { + int viewIndex = getModelToView()[e.getFirstRow()]; + fireTableChanged(new TableModelEvent(TableSorterModel.this, + viewIndex, + viewIndex, + column, + e.getType())); + return; + } + + // Something has happened to the data that may have + // invalidated the row order. + clearSortingState(); + fireTableDataChanged(); + return; + } + } + + private class MouseHandler extends MouseAdapter { + + public void mouseClicked (MouseEvent e) + { + JTableHeader h = (JTableHeader) e.getSource(); + TableColumnModel columnModel = h.getColumnModel(); + int viewColumn = columnModel.getColumnIndexAtX(e.getX()); + int column = columnModel.getColumn(viewColumn).getModelIndex(); + if (column != -1) { + int status = getSortingStatus(column); + if ( !e.isControlDown()) { + cancelSorting(); + } + // Cycle the sorting states through {NOT_SORTED, + // ASCENDING, DESCENDING} or + // {NOT_SORTED, DESCENDING, ASCENDING} depending + // on whether shift is pressed. + status = status + (e.isShiftDown() ? -1 : 1); + status = (status + 4) % 3 - 1; // signed mod, + // returning + // {-1, 0, 1} + setSortingStatus(column, + status); + } + } + } + + private class SortableHeaderRenderer implements TableCellRenderer { + + private TableCellRenderer tableCellRenderer; + + public SortableHeaderRenderer (TableCellRenderer tableCellRenderer) + { + this.tableCellRenderer = tableCellRenderer; + } + + public Component getTableCellRendererComponent (JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, + int column) + { + Component c = tableCellRenderer.getTableCellRendererComponent(table, + value, + isSelected, + hasFocus, + row, + column); + if (c instanceof JLabel) { + JLabel l = (JLabel) c; + l.setHorizontalTextPosition(JLabel.LEFT); + int modelColumn = table.convertColumnIndexToModel(column); + l.setIcon(getHeaderRendererIcon(modelColumn, + l.getFont().getSize())); + } + return c; + } + } + + private static class Directive { + + private int column; + + private int direction; + + public Directive (int column, + int direction) + { + this.column = column; + this.direction = direction; + } + } + + public void fireTableColumnUpdated (int index) + { + for (int i = 0; i < getRowCount(); i++) { + fireTableCellUpdated(i, + index); + } + } +} diff -r 000000000000 -r 0359f2c3b3ba java/JFTable/src/cz/frantovo/gui/tabulky/dolu.png Binary file java/JFTable/src/cz/frantovo/gui/tabulky/dolu.png has changed diff -r 000000000000 -r 0359f2c3b3ba java/JFTable/src/cz/frantovo/gui/tabulky/nahoru.png Binary file java/JFTable/src/cz/frantovo/gui/tabulky/nahoru.png has changed