Sunday 15 May 2011

java - Row filter doesn't work as expected on cell update events -



java - Row filter doesn't work as expected on cell update events -

working in shared table model illustration realized if attach row filter table's row sorter filter doesn't have effect on cell update events. according rowsorter api:

concrete implementations of rowsorter need reference model such tablemodel or listmodel. view classes, such jtable , jlist, have reference model. avoid ordering dependencies, rowsorter implementations should not install listener on model. instead view class phone call rowsorter when model changes. example, if row updated in tablemodel jtable invokes rowsupdated. when model changes, view may phone call of next methods: modelstructurechanged, allrowschanged, rowsinserted, rowsdeleted , rowsupdated.

so understand paragraph, cell update particular case of row update , such rowsupdated should called , row filtered accordingly.

to illustrate i'm saying, please consider simple filter:

private void applyfilter() { defaultrowsorter sorter = (defaultrowsorter)table.getrowsorter(); sorter.setrowfilter(new rowfilter() { @override public boolean include(rowfilter.entry entry) { boolean value = (boolean)entry.getvalue(2); homecoming value == null || value; } }); }

here 3rd column expected boolean , entry (row) has included if cell value either null or true. if edit cell placed @ 3rd column , set value false i'd expect row "disappear" view. however, accomplish have set new filter 1 time again because doesn't seem work "automatically".

attaching tablemodellistener model follows, can see update event on cell edits:

model.addtablemodellistener(new tablemodellistener() { @override public void tablechanged(tablemodelevent e) { if (e.gettype() == tablemodelevent.update) { int row = e.getlastrow(); int column = e.getcolumn(); object value = ((tablemodel)e.getsource()).getvalueat(row, column); string text = string.format("update event. row: %1s column: %2s value: %3s", row, column, value); system.out.println(text); } } });

as i've said, if reset filter using tablemodellistener works expected:

model.addtablemodellistener(new tablemodellistener() { @override public void tablechanged(tablemodelevent e) { if (e.gettype() == tablemodelevent.update) { applyfilter(); } } });

question: bug/implementation issue? or i'm misunderstanding api?

here finish mcve illustrating problem.

import java.awt.borderlayout; import javax.swing.borderfactory; import javax.swing.defaultrowsorter; import javax.swing.jframe; import javax.swing.jpanel; import javax.swing.jscrollpane; import javax.swing.jtable; import javax.swing.rowfilter; import javax.swing.swingutilities; import javax.swing.event.tablemodelevent; import javax.swing.event.tablemodellistener; import javax.swing.table.defaulttablemodel; import javax.swing.table.tablemodel; public class demo { private jtable table; private void createandshowgui() { defaulttablemodel model = new defaulttablemodel(5, 3) { @override public boolean iscelleditable(int row, int column) { homecoming column == 2; } @override public class<?> getcolumnclass(int columnindex) { homecoming columnindex == 2 ? boolean.class : super.getcolumnclass(columnindex); } }; model.addtablemodellistener(new tablemodellistener() { @override public void tablechanged(tablemodelevent e) { if (e.gettype() == tablemodelevent.update) { int row = e.getlastrow(); int column = e.getcolumn(); object value = ((tablemodel)e.getsource()).getvalueat(row, column); string text = string.format("update event. row: %1s column: %2s value: %3s", row, column, value); system.out.println(text); // applyfilter(); un-comment line create work } } }); table = new jtable(model); table.setautocreaterowsorter(true); applyfilter(); jpanel content = new jpanel(new borderlayout()); content.setborder(borderfactory.createemptyborder(8,8,8,8)); content.add(new jscrollpane(table)); jframe frame = new jframe("demo"); frame.setdefaultcloseoperation(jframe.dispose_on_close); frame.add(content); frame.pack(); frame.setlocationbyplatform(true); frame.setvisible(true); } private void applyfilter() { defaultrowsorter sorter = (defaultrowsorter)table.getrowsorter(); sorter.setrowfilter(new rowfilter() { @override public boolean include(rowfilter.entry entry) { boolean value = (boolean)entry.getvalue(2); homecoming value == null || value; } }); } public static void main(string[] args) { swingutilities.invokelater(new runnable() { @override public void run() { new demo().createandshowgui(); } }); } }

well, after doing research , reading api, bugs reports , oracle forum i've found interesting things.

1. set defaultrowsorter's sortsonupdate property true

the first thing found have set sortsonupdate property true in order enable notification chain when rowsupdated(...) called. otherwise no rowsorterevent fired , view (our jtable) won't aware happened , won't repaint accordingly. making little change:

defaultrowsorter sorter = (defaultrowsorter)table.getrowsorter(); sorter.setrowfilter(new rowfilter() { @override public boolean include(rowfilter.entry entry) { boolean value = (boolean)entry.getvalue(2); homecoming value == null || value; } }); sorter.setsortsonupdates(true);

we won't have re-apply filter on table model update. but...

2. there's bug in jtable component processing rowsorterevent notification

while jtable implements rowsorterlistener interface, subscribes row sorter listener , process rowsorterevents. there's bug repainting table. odd behavior described in these posts:

how filter jtable rows info changes defaultrowsorter.setsortsonupdates(true) disaster

in nutshell:

when rowsorterevent.type.sorted event processed jtable, repaints area related involded rows not rest of table, remains was. let's edit first row , should filtered now. rest of rows should shifted 1 row top turns out not: first row correctly repainted show sec row rest of table still same. in fact bug because in particular case whole table needs repainted. see core bug # 6791934

as workaround either attach new rowsorterlistener rowsorter or override jtable's sorterchanged(...) follows in order forcefulness whole repaint on our table (imho sec method preferred).

defaultrowsorter sorter = (defaultrowsorter)table.getrowsorter(); ... sorter.addrowsorterlistener(new rowsorterlistener() { @override public void sorterchanged(rowsorterevent e) { if (e.gettype() == rowsorterevent.type.sorted) { // need phone call both revalidate() , repaint() table.revalidate(); table.repaint(); } } });

or

jtable table = new jtable(tablemodel) { @override public void sorterchanged(rowsorterevent e) { super.sorterchanged(e); if (e.gettype() == rowsorterevent.type.sorted) { resizeandrepaint(); // protected method calls both revalidate() , repaint() } } }; 3. jxtable component has workaround bug

the jxtable component part of swingx library , extends jtable doesn't have problem because swinglabs team has overwrote sorterchanged(...) mthod follows hack around bug:

//----> start hack around core issue 6791934: // table not updated correctly after updating model // while having sorter filter. /** * overridden hack around core bug * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791934 * */ @override public void sorterchanged(rowsorterevent e) { super.sorterchanged(e); postprocesssorterchanged(e); } /** flag indicate if forced revalidate needed. */ protected boolean forcerevalidate; /** flag indicate if sortorderchanged has happened between pre- , postprocessmodelchange. */ protected boolean filteredrowcountchanged; /** * hack around core issue 6791934: sets flags forcefulness revalidate if appropriate. * called before processing event. * @param e tablemodelevent received model */ protected void preprocessmodelchange(tablemodelevent e) { forcerevalidate = getsortsonupdates() && getrowfilter() != null && isupdate(e) ; } /** * hack around core issue 6791934: forces revalidate if appropriate , resets * internal flags. * called after processing event. * @param e tablemodelevent received model */ protected void postprocessmodelchange(tablemodelevent e) { if (forcerevalidate && filteredrowcountchanged) { resizeandrepaint(); } filteredrowcountchanged = false; forcerevalidate = false; } /** * hack around core issue 6791934: sets sorter changed flag if appropriate. * called after processing event. * @param e sorter event received sorter */ protected void postprocesssorterchanged(rowsorterevent e) { filteredrowcountchanged = false; if (forcerevalidate && e.gettype() == rowsorterevent.type.sorted) { filteredrowcountchanged = e.getpreviousrowcount() != getrowcount(); } } //----> end hack around core issue 6791934:

so, 1 more reason (if missing) utilize swingx.

java swing jtable rowfilter rowsorter

No comments:

Post a Comment