diff --git a/core/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java b/core/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java index 984147ffa..1c08f09be 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java +++ b/core/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java @@ -10,14 +10,12 @@ import java.util.List; import javax.swing.JCheckBoxMenuItem; import javax.swing.JMenu; -import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.RowFilter; import javax.swing.RowSorter.SortKey; import javax.swing.SortOrder; -import javax.swing.event.ListSelectionEvent; import javax.swing.table.AbstractTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumn; @@ -30,6 +28,7 @@ import net.sf.openrocket.preset.TypedKey; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.AlphanumComparator; public class ComponentPresetTable extends JTable { @@ -46,31 +45,6 @@ public class ComponentPresetTable extends JTable { this.presets = presets; this.columns = new ComponentPresetTableColumn[ComponentPreset.orderedKeyList.size()+1]; - tableColumnModel = new XTableColumnModel(); - - /* - * Set up the Column Table model - */ - columns[0] = new ComponentPresetTableColumn.Favorite(0); - tableColumnModel.addColumn(columns[0]); - - List hiddenColumns = new ArrayList(); - { - int index = 1; - for (final TypedKey key: ComponentPreset.orderedKeyList ) { - if ( key.getType() == Double.class && key.getUnitGroup() != null ) { - columns[index] = new ComponentPresetTableColumn.DoubleWithUnit((TypedKey)key,index); - } else { - columns[index] = new ComponentPresetTableColumn.Parameter(key,index); - } - tableColumnModel.addColumn(columns[index]); - if ( visibleColumnKeys.indexOf(key) < 0 ) { - hiddenColumns.add(columns[index]); - } - index ++; - } - } - tableModel = new AbstractTableModel() { final ComponentPresetTableColumn[] myColumns = columns; @@ -111,18 +85,47 @@ public class ComponentPresetTable extends JTable { }; + + sorter = new TableRowSorter(tableModel); + + tableColumnModel = new XTableColumnModel(); + + /* + * Set up the Column Table model, and customize the sorting. + */ + columns[0] = new ComponentPresetTableColumn.Favorite(0); + tableColumnModel.addColumn(columns[0]); + + List hiddenColumns = new ArrayList(); + { + int index = 1; + for (final TypedKey key: ComponentPreset.orderedKeyList ) { + if ( key.getType() == Double.class && key.getUnitGroup() != null ) { + columns[index] = new ComponentPresetTableColumn.DoubleWithUnit((TypedKey)key,index); + } else { + columns[index] = new ComponentPresetTableColumn.Parameter(key,index); + } + tableColumnModel.addColumn(columns[index]); + if ( key == ComponentPreset.MANUFACTURER || key == ComponentPreset.PARTNO ) { + sorter.setComparator(index, new AlphanumComparator()); + } + if ( visibleColumnKeys.indexOf(key) < 0 ) { + hiddenColumns.add(columns[index]); + } + index ++; + } + } + this.setAutoCreateColumnsFromModel(false); this.setColumnModel( tableColumnModel ); this.setModel(tableModel); this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + this.setRowSorter(sorter); for ( TableColumn hiddenColumn : hiddenColumns ) { tableColumnModel.setColumnVisible(hiddenColumn, false); } - sorter = new TableRowSorter(tableModel); - this.setRowSorter(sorter); - JTableHeader header = this.getTableHeader(); header.setReorderingAllowed(true); diff --git a/core/src/net/sf/openrocket/util/AlphanumComparator.java b/core/src/net/sf/openrocket/util/AlphanumComparator.java new file mode 100644 index 000000000..c7a7b4887 --- /dev/null +++ b/core/src/net/sf/openrocket/util/AlphanumComparator.java @@ -0,0 +1,139 @@ +/* + * The Alphanum Algorithm is an improved sorting algorithm for strings + * containing numbers. Instead of sorting numbers in ASCII order like + * a standard sort, this algorithm sorts numbers in numeric order. + * + * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* + * Subsequently this code had been hacked up to make it genericized and support + * folding upper/lower case. + */ +package net.sf.openrocket.util; + +import java.text.Collator; +import java.util.Comparator; + +/** + * This is an updated version with enhancements made by Daniel Migowski, + * Andre Bogus, and David Koelle + * + * To convert to use Templates (Java 1.5+): + * - Change "implements Comparator" to "implements Comparator" + * - Change "compare(Object o1, Object o2)" to "compare(String s1, String s2)" + * - Remove the type checking and casting in compare(). + * + * To use this class: + * Use the static "sort" method from the java.util.Collections class: + * Collections.sort(your list, new AlphanumComparator()); + */ +public class AlphanumComparator implements Comparator +{ + + private static final Collator sorter = Collator.getInstance(); + static { + sorter.setStrength(Collator.TERTIARY); + sorter.setDecomposition(Collator.CANONICAL_DECOMPOSITION); + } + + private final boolean isDigit(char ch) + { + return ch >= 48 && ch <= 57; + } + + /** Length of string is passed in for improved efficiency (only need to calculate it once) **/ + private final String getChunk(String s, int slength, int marker) + { + StringBuilder chunk = new StringBuilder(); + char c = s.charAt(marker); + chunk.append(c); + marker++; + if (isDigit(c)) + { + while (marker < slength) + { + c = s.charAt(marker); + if (!isDigit(c)) + break; + chunk.append(c); + marker++; + } + } else + { + while (marker < slength) + { + c = s.charAt(marker); + if (isDigit(c)) + break; + chunk.append(c); + marker++; + } + } + return chunk.toString(); + } + + @Override + public int compare(String s1, String s2) + { + + int thisMarker = 0; + int thatMarker = 0; + int s1Length = s1.length(); + int s2Length = s2.length(); + + while (thisMarker < s1Length && thatMarker < s2Length) + { + String thisChunk = getChunk(s1, s1Length, thisMarker); + thisMarker += thisChunk.length(); + + String thatChunk = getChunk(s2, s2Length, thatMarker); + thatMarker += thatChunk.length(); + + // If both chunks contain numeric characters, sort them numerically + int result = 0; + if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) + { + // Simple chunk comparison by length. + int thisChunkLength = thisChunk.length(); + result = thisChunkLength - thatChunk.length(); + // If equal, the first different number counts + if (result == 0) + { + for (int i = 0; i < thisChunkLength; i++) + { + result = thisChunk.charAt(i) - thatChunk.charAt(i); + if (result != 0) + { + return result; + } + } + } + } else + { + result = sorter.compare(thisChunk, thatChunk); + } + + if (result != 0) + return result; + } + + return s1Length - s2Length; + } +}