java - JTable loses focus when a dialog is shown from a cell editor component -


in main application, jtable losing focus when dialog shown cell editor component.

below simple sscce made see problem.

do these simples experiments:

  • press f2 in first table column start editing. change column contents number 2 , press enter key. table lose focus , first field in form focus.
  • press f2 in first table column start editing. change column contents number 2 , press tab key. table lose focus , first field in form focus.

the first field in form searchfield component. because not in jtable, behaves when change contente number 2 , commit edit (with enter or tab).

import java.awt.borderlayout; import java.awt.dimension; import java.beans.propertychangeevent; import java.beans.propertychangelistener; import java.text.numberformat; import java.text.parseexception; import java.util.objects;  import javax.swing.borderfactory; import javax.swing.box; import javax.swing.boxlayout; import javax.swing.defaultcelleditor; import javax.swing.jformattedtextfield; import javax.swing.jframe; import javax.swing.joptionpane; import javax.swing.jpanel; import javax.swing.jscrollpane; import javax.swing.jtable; import javax.swing.jtextfield; import javax.swing.listselectionmodel; import javax.swing.swingutilities; import javax.swing.table.abstracttablemodel; import javax.swing.text.defaultformatterfactory; import javax.swing.text.numberformatter;  public class sscce extends jpanel {     private sscce()     {         setlayout(new borderlayout());         setborder(borderfactory.createemptyborder(5, 5, 5, 5));          jpanel pnlfields = new jpanel();         pnlfields.setlayout(new boxlayout(pnlfields, boxlayout.page_axis));         pnlfields.setborder(borderfactory.createemptyborder(0, 0, 10, 0));          searchfield field1 = new searchfield();         configurefield(field1);         pnlfields.add(field1);          pnlfields.add(box.createrigidarea(new dimension(0, 3)));          jtextfield field2 = new jtextfield();         configurefield(field2);         pnlfields.add(field2);          add(pnlfields, borderlayout.page_start);         add(new jscrollpane(createtable()), borderlayout.center);     }      private void configurefield(jtextfield field)     {         field.setpreferredsize(new dimension(150, field.getpreferredsize().height));         field.setmaximumsize(field.getpreferredsize());         field.setalignmentx(left_alignment);     }      private jtable createtable()     {         jtable table = new jtable(new customtablemodel());          table.setselectionmode(listselectionmodel.single_selection);         table.setcellselectionenabled(true);         table.gettableheader().setreorderingallowed(false);         table.setpreferredscrollableviewportsize(new dimension(500, 170));          table.setdefaulteditor(integer.class, new searchfieldcelleditor(new searchfield()));          return table;     }      private static void createandshowgui()     {         jframe frame = new jframe("sscce (jtable loses focus)");         frame.setdefaultcloseoperation(jframe.exit_on_close);         frame.add(new sscce());         frame.pack();         frame.setlocationrelativeto(null);         frame.setvisible(true);     }      public static void main(string[] args)     {         swingutilities.invokelater(             new runnable()             {                 @override                 public void run()                 {                     createandshowgui();                 }             }         );     } }  class customtablemodel extends abstracttablemodel {     private string[] columnnames = {"column1 (search field)", "column 2"};     private class<?>[] columntypes = {integer.class, string.class};     private object[][] data = {{1, ""}, {3, ""}, {4, ""}, {5, ""}, {6, ""}};      @override     public int getcolumncount()     {         return columnnames.length;     }      @override     public int getrowcount()     {         return data.length;     }      @override     public string getcolumnname(int col)     {         return columnnames[col];     }      @override     public object getvalueat(int row, int col)     {         return data[row][col];     }      @override     public class<?> getcolumnclass(int c)     {         return columntypes[c];     }      @override     public boolean iscelleditable(int rowindex, int columnindex)     {         return true;     }      @override     public void setvalueat(object value, int row, int col)     {         data[row][col] = value;         firetablecellupdated(row, col);     } }  class searchfieldcelleditor extends defaultcelleditor {     searchfieldcelleditor(final searchfield searchfield)     {         super(searchfield);         searchfield.removeactionlistener(delegate);         delegate = new editordelegate()         {             @override             public void setvalue(object value)             {                 searchfield.setvalue(value);             }              @override             public object getcelleditorvalue()             {                 return searchfield.getvalue();             }         };         searchfield.addactionlistener(delegate);     }      @override     public boolean stopcellediting()     {         try         {             ((searchfield) getcomponent()).commitedit();         }         catch (parseexception ex)         {             ex.printstacktrace();         }         return super.stopcellediting();     } }  class searchfield extends jformattedtextfield implements propertychangelistener {     private object _oldvalue;      searchfield()     {         setupformatter();         addpropertychangelistener("value", this);     }      private void setupformatter()     {         numberformat integerformat = numberformat.getintegerinstance();         integerformat.setgroupingused(false);          numberformatter integerformatter =             new numberformatter(integerformat)             {                 @override                 public object stringtovalue(string text) throws parseexception                 {                     return text.isempty() ? null : super.stringtovalue(text);                 }             };         integerformatter.setvalueclass(integer.class);         integerformatter.setminimum(integer.min_value);         integerformatter.setmaximum(integer.max_value);          setformatterfactory(new defaultformatterfactory(integerformatter));     }      @override     public void propertychange(propertychangeevent evt)     {         object newvalue = evt.getnewvalue();         if (!objects.equals(newvalue, _oldvalue))         {             _oldvalue = newvalue;             // suppose value of 2 means data wasn't found.             // display message user.             if (new integer(2).equals(newvalue))             {                 joptionpane.showmessagedialog(                     null, "not found: " + newvalue + ".", "warning",                     joptionpane.warning_message);             }         }     } } 

so, there way solve problem? solution of issue important me.

thank you.

marcos

* update *

i think i've found solution, have opinion if trustworthy solution.

change stopcellediting method , test sscce again:

@override public boolean stopcellediting() {     searchfield searchfield = (searchfield) getcomponent();      try     {         searchfield.commitedit();     }     catch (parseexception ex)     {         ex.printstacktrace();     }      component table = searchfield.getparent();     table.requestfocusinwindow();      return super.stopcellediting(); } 

so, think solves problem or there flaw?

marcos

update 2

i've found little flaw. corrected these changes:

class searchfieldcelleditor extends defaultcelleditor {     searchfieldcelleditor(final searchfield searchfield)     {         super(searchfield);         searchfield.removeactionlistener(delegate);         delegate = new editordelegate()         {             @override             public void setvalue(object value)             {                 searchfield.setvalue(value);             }              @override             public object getcelleditorvalue()             {                 return searchfield.getvalue();             }         };         searchfield.addactionlistener(delegate);     }      @override     public component gettablecelleditorcomponent(         jtable table, object value, boolean isselected, int row, int column)     {         searchfield searchfield = (searchfield) getcomponent();         searchfield.setpreparingforedit(true);         try         {             return super.gettablecelleditorcomponent(                 table, value, isselected, row, column);         }                 {             searchfield.setpreparingforedit(false);         }     }      @override     public boolean stopcellediting()     {         searchfield searchfield = (searchfield) getcomponent();          try         {             searchfield.commitedit();         }         catch (parseexception ex)         {             ex.printstacktrace();         }          component table = searchfield.getparent();         table.requestfocusinwindow();          return super.stopcellediting();     } }  class searchfield extends jformattedtextfield implements propertychangelistener {     private boolean _ispreparingforedit;     private object _oldvalue;      searchfield()     {         setupformatter();         addpropertychangelistener("value", this);     }      void setpreparingforedit(boolean ispreparingforedit)     {         _ispreparingforedit = ispreparingforedit;     }      private void setupformatter()     {         numberformat integerformat = numberformat.getintegerinstance();         integerformat.setgroupingused(false);          numberformatter integerformatter =             new numberformatter(integerformat)             {                 @override                 public object stringtovalue(string text) throws parseexception                 {                     return text.isempty() ? null : super.stringtovalue(text);                 }             };         integerformatter.setvalueclass(integer.class);         integerformatter.setminimum(integer.min_value);         integerformatter.setmaximum(integer.max_value);          setformatterfactory(new defaultformatterfactory(integerformatter));     }      @override     public void propertychange(propertychangeevent evt)     {         final object newvalue = evt.getnewvalue();         if (!objects.equals(newvalue, _oldvalue))         {             _oldvalue = newvalue;             // suppose value of 2 means data wasn't found.             // display message user.             if (new integer(2).equals(newvalue) && !_ispreparingforedit)             {                 joptionpane.showmessagedialog(null, "not found: " + newvalue + ".", "warning",                     joptionpane.warning_message);             }         }     } } 

have found more flaws too? have review.

marcos

update 3

another solution after suggestion kleopatra :

class searchfieldcelleditor extends defaultcelleditor {     searchfieldcelleditor(final searchfield searchfield)     {         super(searchfield);         searchfield.setshowmessageasynchronously(true);         searchfield.removeactionlistener(delegate);         delegate = new editordelegate()         {             @override             public void setvalue(object value)             {                 searchfield.setvalue(value);             }              @override             public object getcelleditorvalue()             {                 return searchfield.getvalue();             }         };         searchfield.addactionlistener(delegate);     }      @override     public component gettablecelleditorcomponent(         jtable table, object value, boolean isselected, int row, int column)     {         searchfield searchfield = (searchfield) getcomponent();         searchfield.setpreparingforedit(true);         try         {             return super.gettablecelleditorcomponent(                 table, value, isselected, row, column);         }                 {             searchfield.setpreparingforedit(false);         }     }      @override     public boolean stopcellediting()     {         searchfield searchfield = (searchfield) getcomponent();          try         {             searchfield.commitedit();         }         catch (parseexception ex)         {             ex.printstacktrace();         }          return super.stopcellediting();     } }  class searchfield extends jformattedtextfield implements propertychangelistener {     private boolean _showmessageasynchronously;     private boolean _ispreparingforedit;     private object _oldvalue;      searchfield()     {         setupformatter();         addpropertychangelistener("value", this);     }      public boolean isshowmessageasynchronously()     {         return _showmessageasynchronously;     }      public void setshowmessageasynchronously(boolean showmessageasynchronously)     {         _showmessageasynchronously = showmessageasynchronously;     }      void setpreparingforedit(boolean ispreparingforedit)     {         _ispreparingforedit = ispreparingforedit;     }      private void setupformatter()     {         numberformat integerformat = numberformat.getintegerinstance();         integerformat.setgroupingused(false);          numberformatter integerformatter =             new numberformatter(integerformat)             {                 @override                 public object stringtovalue(string text) throws parseexception                 {                     return text.isempty() ? null : super.stringtovalue(text);                 }             };         integerformatter.setvalueclass(integer.class);         integerformatter.setminimum(integer.min_value);         integerformatter.setmaximum(integer.max_value);          setformatterfactory(new defaultformatterfactory(integerformatter));     }      @override     public void propertychange(propertychangeevent evt)     {         final object newvalue = evt.getnewvalue();         if (!objects.equals(newvalue, _oldvalue))         {             _oldvalue = newvalue;             // suppose value of 2 means data wasn't found.             // display message user.             if (new integer(2).equals(newvalue) && !_ispreparingforedit)             {                 if (_showmessageasynchronously)                 {                     swingutilities.invokelater(                         new runnable()                         {                             @override                             public void run()                             {                                 showmessage(newvalue);                             }                         }                     );                 }                 else                 {                     showmessage(newvalue);                 }             }         }     }      private void showmessage(object value)     {         joptionpane.showmessagedialog(null, "not found: " + value + ".",             "warning", joptionpane.warning_message);     } } 

comments , suggestions last solution still appreciated. ultimate , optimal solution?

marcos

as commented: it's bit fishy change state of table in editor, if it's related focus brittle @ best. go great lengths avoid it.

the mis-behaviour feels similar incorrectly implemented inputverifier has side-effects (like grabbing focus) in verify vs. in shouldyieldfocus correct: in such context focusmanager gets confused, "forgets" natural last-focusowner-before.

the remedy might let manager job first, , show message when it's done. in example code can achieved wrapping invokelater:

if (needsmessage()) {     swingutilities.invokelater(new runnable() {         public void run() {             joptionpane.showmessagedialog(null, "not found: " +                     newvalue + ".", "warning",                     joptionpane.warning_message);          }     }); } 

Comments

Popular posts from this blog

monitor web browser programmatically in Android? -

Shrink a YouTube video to responsive width -

wpf - PdfWriter.GetInstance throws System.NullReferenceException -