解决JTable排序问题的方法详解
程序员文章站
2023-12-13 11:07:40
jtable的排序是一个让人头疼的问题,sun没有为排序这个最常用的功能提供类。但是近日翻看sun官方java的tutorial,却发现其在文档中提供了这个类的实现,使用非...
jtable的排序是一个让人头疼的问题,sun没有为排序这个最常用的功能提供类。
但是近日翻看sun官方java的tutorial,却发现其在文档中提供了这个类的实现,使用非常简单!
使用方法示例:
tablesorter sorter = new tablesorter(new mytablemodel()); //added this
//jtable table = new jtable(new mytablemodel()); //old
jtable table = new jtable(sorter); //new
sorter.settableheader(table.gettableheader()); //added this
我把tablesorter类贴出来,自己编译就可以使用了
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.list;
import javax.swing.*;
import javax.swing.event.tablemodelevent;
import javax.swing.event.tablemodellistener;
import javax.swing.table.*;
/**
* tablesorter is a decorator for tablemodels; adding sorting
* functionality to a supplied tablemodel. tablesorter does
* not store or copy the data in its tablemodel; instead it maintains
* a map from the row indexes of the view to the row indexes of the
* model. as requests are made of the sorter (like getvalueat(row, col))
* they are passed to the underlying model after the row numbers
* have been translated via the internal mapping array. this way,
* the tablesorter appears to hold another copy of the table
* with the rows in a different order.
* <p/>
* tablesorter registers itself as a listener to the underlying model,
* just as the jtable itself would. events recieved from the model
* are examined, sometimes manipulated (typically widened), and then
* passed on to the tablesorter's listeners (typically the jtable).
* if a change to the model has invalidated the order of tablesorter's
* rows, a note of this is made and the sorter will resort the
* rows the next time a value is requested.
* <p/>
* when the tableheader property is set, either by using the
* settableheader() method or the two argument constructor, the
* table header may be used as a complete ui for tablesorter.
* the default renderer of the tableheader is decorated with a renderer
* that indicates the sorting status of each column. in addition,
* a mouse listener is installed with the following behavior:
* <ul>
* <li>
* mouse-click: clears the sorting status of all other columns
* and advances the sorting status of that column through three
* values: {not_sorted, ascending, descending} (then back to
* not_sorted again).
* <li>
* shift-mouse-click: clears the sorting status of all other columns
* and cycles the sorting status of the column through the same
* three values, in the opposite order: {not_sorted, descending, ascending}.
* <li>
* control-mouse-click and control-shift-mouse-click: as above except
* that the changes to the column do not cancel the statuses of columns
* that are already sorting - giving a way to initiate a compound
* sort.
* </ul>
* <p/>
* this is a long overdue rewrite of a class of the same name that
* first appeared in the swing table demos in 1997.
*
* @author philip milne
* @author brendon mclean
* @author dan van enckevort
* @author parwinder sekhon
* @version 2.0 02/27/04
*/
public class tablesorter extends abstracttablemodel {
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();
public tablesorter() {
this.mouselistener = new mousehandler();
this.tablemodellistener = new tablemodelhandler();
}
public tablesorter(tablemodel tablemodel) {
this();
settablemodel(tablemodel);
}
public tablesorter(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;
}
return new arrow(directive.direction == descending, size, sortingcolumns.indexof(directive));
}
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) {
return getviewtomodel()[viewindex].modelindex;
}
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;
}
// tablemodel interface methods
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);
}
// helper classes
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(tablesorter.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 static class arrow implements icon {
private boolean descending;
private int size;
private int priority;
public arrow(boolean descending, int size, int priority) {
this.descending = descending;
this.size = size;
this.priority = priority;
}
public void painticon(component c, graphics g, int x, int y) {
color color = c == null ? color.gray : c.getbackground();
// in a compound sort, make each succesive triangle 20%
// smaller than the previous one.
int dx = (int)(size/2*math.pow(0.8, priority));
int dy = descending ? dx : -dx;
// align icon (roughly) with font baseline.
y = y + 5*size/6 + (descending ? -dy : 0);
int shift = descending ? 1 : -1;
g.translate(x, y);
// right diagonal.
g.setcolor(color.darker());
g.drawline(dx / 2, dy, 0, 0);
g.drawline(dx / 2, dy + shift, 0, shift);
// left diagonal.
g.setcolor(color.brighter());
g.drawline(dx / 2, dy, dx, 0);
g.drawline(dx / 2, dy + shift, dx, shift);
// horizontal line.
if (descending) {
g.setcolor(color.darker().darker());
} else {
g.setcolor(color.brighter().brighter());
}
g.drawline(dx, 0, 0, 0);
g.setcolor(color);
g.translate(-x, -y);
}
public int geticonwidth() {
return size;
}
public int geticonheight() {
return size;
}
}
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;
}
}
}
但是近日翻看sun官方java的tutorial,却发现其在文档中提供了这个类的实现,使用非常简单!
使用方法示例:
复制代码 代码如下:
tablesorter sorter = new tablesorter(new mytablemodel()); //added this
//jtable table = new jtable(new mytablemodel()); //old
jtable table = new jtable(sorter); //new
sorter.settableheader(table.gettableheader()); //added this
我把tablesorter类贴出来,自己编译就可以使用了
复制代码 代码如下:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.list;
import javax.swing.*;
import javax.swing.event.tablemodelevent;
import javax.swing.event.tablemodellistener;
import javax.swing.table.*;
/**
* tablesorter is a decorator for tablemodels; adding sorting
* functionality to a supplied tablemodel. tablesorter does
* not store or copy the data in its tablemodel; instead it maintains
* a map from the row indexes of the view to the row indexes of the
* model. as requests are made of the sorter (like getvalueat(row, col))
* they are passed to the underlying model after the row numbers
* have been translated via the internal mapping array. this way,
* the tablesorter appears to hold another copy of the table
* with the rows in a different order.
* <p/>
* tablesorter registers itself as a listener to the underlying model,
* just as the jtable itself would. events recieved from the model
* are examined, sometimes manipulated (typically widened), and then
* passed on to the tablesorter's listeners (typically the jtable).
* if a change to the model has invalidated the order of tablesorter's
* rows, a note of this is made and the sorter will resort the
* rows the next time a value is requested.
* <p/>
* when the tableheader property is set, either by using the
* settableheader() method or the two argument constructor, the
* table header may be used as a complete ui for tablesorter.
* the default renderer of the tableheader is decorated with a renderer
* that indicates the sorting status of each column. in addition,
* a mouse listener is installed with the following behavior:
* <ul>
* <li>
* mouse-click: clears the sorting status of all other columns
* and advances the sorting status of that column through three
* values: {not_sorted, ascending, descending} (then back to
* not_sorted again).
* <li>
* shift-mouse-click: clears the sorting status of all other columns
* and cycles the sorting status of the column through the same
* three values, in the opposite order: {not_sorted, descending, ascending}.
* <li>
* control-mouse-click and control-shift-mouse-click: as above except
* that the changes to the column do not cancel the statuses of columns
* that are already sorting - giving a way to initiate a compound
* sort.
* </ul>
* <p/>
* this is a long overdue rewrite of a class of the same name that
* first appeared in the swing table demos in 1997.
*
* @author philip milne
* @author brendon mclean
* @author dan van enckevort
* @author parwinder sekhon
* @version 2.0 02/27/04
*/
public class tablesorter extends abstracttablemodel {
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();
public tablesorter() {
this.mouselistener = new mousehandler();
this.tablemodellistener = new tablemodelhandler();
}
public tablesorter(tablemodel tablemodel) {
this();
settablemodel(tablemodel);
}
public tablesorter(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;
}
return new arrow(directive.direction == descending, size, sortingcolumns.indexof(directive));
}
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) {
return getviewtomodel()[viewindex].modelindex;
}
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;
}
// tablemodel interface methods
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);
}
// helper classes
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(tablesorter.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 static class arrow implements icon {
private boolean descending;
private int size;
private int priority;
public arrow(boolean descending, int size, int priority) {
this.descending = descending;
this.size = size;
this.priority = priority;
}
public void painticon(component c, graphics g, int x, int y) {
color color = c == null ? color.gray : c.getbackground();
// in a compound sort, make each succesive triangle 20%
// smaller than the previous one.
int dx = (int)(size/2*math.pow(0.8, priority));
int dy = descending ? dx : -dx;
// align icon (roughly) with font baseline.
y = y + 5*size/6 + (descending ? -dy : 0);
int shift = descending ? 1 : -1;
g.translate(x, y);
// right diagonal.
g.setcolor(color.darker());
g.drawline(dx / 2, dy, 0, 0);
g.drawline(dx / 2, dy + shift, 0, shift);
// left diagonal.
g.setcolor(color.brighter());
g.drawline(dx / 2, dy, dx, 0);
g.drawline(dx / 2, dy + shift, dx, shift);
// horizontal line.
if (descending) {
g.setcolor(color.darker().darker());
} else {
g.setcolor(color.brighter().brighter());
}
g.drawline(dx, 0, 0, 0);
g.setcolor(color);
g.translate(-x, -y);
}
public int geticonwidth() {
return size;
}
public int geticonheight() {
return size;
}
}
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;
}
}
}