001package org.vishia.guiInspc;
002
003import java.util.LinkedList;
004import java.util.List;
005import java.util.ListIterator;
006import java.util.Map;
007import java.util.TreeMap;
008import java.util.concurrent.atomic.AtomicBoolean;
009
010import org.vishia.byteData.VariableAccess_ifc;
011import org.vishia.gral.base.GralButton;
012import org.vishia.gral.base.GralMng;
013import org.vishia.gral.base.GralTable;
014import org.vishia.gral.base.GralTextField;
015import org.vishia.gral.base.GralWidget;
016import org.vishia.gral.base.GralWindow;
017import org.vishia.gral.ifc.GralColor;
018import org.vishia.gral.ifc.GralTableLine_ifc;
019import org.vishia.gral.ifc.GralUserAction;
020import org.vishia.gral.ifc.GralWidget_ifc;
021import org.vishia.gral.ifc.GralWindow_ifc;
022import org.vishia.inspcPC.InspcAccessExecRxOrder_ifc;
023import org.vishia.inspcPC.InspcAccess_ifc;
024import org.vishia.inspcPC.accTarget.InspcTargetAccessor;
025import org.vishia.inspcPC.mng.InspcFieldOfStruct;
026import org.vishia.inspcPC.mng.InspcMng;
027import org.vishia.inspcPC.mng.InspcStruct;
028import org.vishia.inspcPC.mng.InspcVariable;
029import org.vishia.util.KeyCode;
030
031
032/**This class presents a window with one table and some buttons to view and edit all fields in one instance
033 * in a target system.
034 * 
035 * 
036 * <br>
037 * The InspcFieldTable shows one struct from target data or one instance, all fields of a class of Java reflection view. 
038 * The fields are stored in instances of {@link InspcFieldOfStruct} which are referenced as data in the {@link GralTable}
039 * of the private composite reference {@link #widgTable}. 
040 * <br><br>
041 *  
042 * <br><br>
043 * <b>Filling the table with the fields of a reflection target</b>:<br>
044 * <img src="../../../img/guiInspc_getFieldsTableOmd.png">
045 * <br>guiInspc_getFieldsTableOmd  
046 * <br>
047 * One of the methods {@link #fillTableFromFocusedVariable()},  {@link #actionBack()}, {@link #getSubStruct(GralTableLine_ifc)}
048 * are invoked from operator GUI actions. They call 
049 * <br> {@link #fillTableStruct()}<br>
050 * This method checks the {@link InspcStruct} of the variable respectively the shown struct in table whether it is known already, 
051 * using {@link InspcStruct#isUpdated()}. It it is not updated, on selection a new variable, the 
052 * <br><br>
053 * {@link InspcTargetAccessor#requestFields(org.vishia.inspcPC.InspcTargetAccessData, InspcAccessExecRxOrder_ifc, Runnable)} 
054 * <br><br>
055 * is called. This routine sets only the reference to the {@link InspcTargetAccessData} and the 2 callbacks. 
056 * This action is done especially in the graphical thread.
057 * <br><br>
058 * If {@link InspcStruct#isUpdated()} returns true, then the struct was updated already. Then it can be shown in the {@link GralTable}
059 * with the names of its elements and the last gotten values.
060 * <br><br>
061 * The organization thread of {@link InspcMng} invokes {@link InspcMng#procComm()} cyclically to show and request values. 
062 * In this thread via calling {@link InspcTargetAccessor#requestStart(long)} the request for getFields is recognized.
063 * For that the {@link InspcTargetAccessor#cmdGetFields(String, org.vishia.inspcPC.InspcAccessExecRxOrder_ifc)}
064 * is invoked for the path of the struct. That is sent to the target.
065 * <br><br>
066 * The target answers with some items for all fields in one or more datagram but for that request with the same sequence number. 
067 * For any item respectively element of the struct the given instance to {@link InspcStruct#rxActionGetFields}
068 * is invoked which fills the struct.
069 * <br><br>
070 * After the last datagram of this answer the stored reference to the {@link Runnable}: {@link #actionUpdated} of this class
071 * is invoked. This action calls {@link #fillTableStruct()} now with received and stored fields.
072 * <br>
073 * <img src="../../../img/guiInspc_getFieldsTableSeq.png">
074 * <br>guiInspc_getFieldsTableSeq<br>
075 * <b>Show and request values for the fields</b><br>
076 * This is simple done by getting maybe with creation of the variable for the requested field: 
077 * {@link InspcFieldOfStruct#variable(InspcVariable, org.vishia.byteData.VariableContainer_ifc)}
078 * and request a new value with {@link InspcVariable#requestValue(long, Runnable)}. The callback invoked on end of the last datagram
079 * is a temporary instance {@link RunOnReceive#RunOnReceive(GralTableLine_ifc)} with the given line of the table which invokes
080 * {@link #showValue(GralTableLine_ifc, boolean)}.
081 * <br>
082 * <img src="../../../img/guiInspc_getValueTableOmd.png">
083 * <br>guiInspc_getValueTableOmd<br>
084 * 
085 * @author Hartmut Schorrig
086 *
087 */
088public class InspcFieldTable
089{
090  /**Version, history and license.
091   * <ul>
092   * <li>2018-09-08 Hartmut chg: because {@link InspcFieldOfStruct} hasSubstruct.
093   * <li>2016-10 Hartmut chg: Regards a field which is a node of tree, not only leaf. 
094   * <li>2015-06-21 requests a new value if the older is older than 1 second, fast refresh
095   * <li>2015-05-29 requests a new value only if the current is older than 5 seconds.
096   * <li>2014-01-05 indexSelection
097   * <li>2013-12-18 Created.
098   * </ul>
099   * 
100   * <b>Copyright/Copyleft</b>:
101   * For this source the LGPL Lesser General Public License,
102   * published by the Free Software Foundation is valid.
103   * It means:
104   * <ol>
105   * <li> You can use this source without any restriction for any desired purpose.
106   * <li> You can redistribute copies of this source to everybody.
107   * <li> Every user of this source, also the user of redistribute copies
108   *    with or without payment, must accept this license for further using.
109   * <li> But the LPGL is not appropriate for a whole software product,
110   *    if this source is only a part of them. It means, the user
111   *    must publish this part of source,
112   *    but don't need to publish the whole source of the own product.
113   * <li> You can study and modify (improve) this source
114   *    for own using or for redistribution, but you have to license the
115   *    modified sources likewise under this LGPL Lesser General Public License.
116   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
117   * </ol>
118   * If you are intent to use this sources without publishing its usage, you can get
119   * a second license subscribing a special contract with the author. 
120   * 
121   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
122   * 
123   */
124  //@SuppressWarnings("hiding")
125  protected final static String sVersion = "2015-05-30";
126
127  @SuppressWarnings("synthetic-access") 
128  class RunOnReceive implements Runnable {
129    //GralTable<FieldOfStruct>.TableLineData line;
130    GralTableLine_ifc<InspcFieldOfStruct> line;
131    
132    RunOnReceive(GralTableLine_ifc<InspcFieldOfStruct> line){
133      this.line = line;
134    }
135    
136    @Override public void run(){
137      InspcFieldTable.this.showValue(line, false);
138    }
139  }
140  
141  
142  /**An instance of this class is used to prepare setValueByPath on a changed value in a line.
143   */
144  public static class TxOrderSetValue implements Runnable
145  {
146    final String sValue;
147    final InspcVariable var;
148
149    public TxOrderSetValue(String sValue, InspcVariable var)
150    { this.sValue = sValue;
151      this.var = var;
152    }
153
154    /**This method will be invoked before finish of a send session. */
155    @Override public void run()
156    {
157      var.ds.targetAccessor.cmdSetStringByPath(var, sValue);
158    }
159    
160    
161  }
162  
163  
164  /**Sizes in table in Graphic grid units.*/
165  private static final int sizeStruct = 3, sizeName = 20, sizeType = 10;
166  
167  /**The window to present. */
168  private final GralWindow wind;
169  
170  /**Shows the path in target to this struct. */
171  private final GralTextField widgPath;
172  
173  /**Table of fields, type and value. */
174  private final GralTable<InspcFieldOfStruct> widgTable;
175
176  
177  private final GralButton btnBack, btnHelp, btnRefresh, btnShowAll, btnSetValue, btnRepeat;
178  
179  /**Aggregation. To get variable, send requests etc. */
180  private final InspcMng inspcMng;
181  
182  /**This index stores the last selected file for any component which was used.
183   * If the component path is reused later, the same field will be selected initially.
184   * It helps by navigation through the file tree.
185   * <ul>
186   * <li>The key is the path in canonical form with '/' as separator (in windows too!) 
187   *   but without terminating '/'.
188   * <li>The value is the name of the file in this directory.   
189   * </ul>
190   */
191  private final Map<String, String> indexSelection = new TreeMap<String, String>(); 
192  
193
194  
195  //private InspcStruct struct;
196  private InspcVariable structVar;
197  
198  /**Path in target to the struct which is shown in table.  */
199  private String sPathStruct;
200  
201  
202  /**Name and path to the current selected field. */
203  private String sFieldCurrent, sPathCurrent;
204  
205  private long timeLineSelected;
206  
207  /**Set in graphic thread with {@link #btnSetValue}, ask and reset in the InspcMng-thread. */
208  protected AtomicBoolean XXXbSetValue = new AtomicBoolean();
209  
210  public InspcFieldTable(InspcMng inspcMng)
211  { //inspcMng.addUserOrder(this);  //invoke run in any communication step.
212    this.wind = new GralWindow(null, "InspcFieldTableWind", "Fields of ...", GralWindow_ifc.windOnTop | GralWindow_ifc.windResizeable);
213    this.widgPath = new GralTextField("InspcFieldTableWind");
214    this.widgTable = new GralTable<InspcFieldOfStruct>("InspcFieldTable", new int[]{sizeStruct, sizeName, 0, -sizeType});
215    this.widgTable.setColumnEditable(2, true);
216    this.widgTable.setActionChange(this.actionChgTable);
217    this.widgTable.specifyActionOnLineSelected(actionLineSelected);
218    this.widgTable.setHtmlHelp("HelpInspc.html#Topic.HelpInspc.fieldsof.");
219    this.btnBack = new GralButton("@InspcFieldBack", "<<", actionBack);
220    this.btnHelp = new GralButton("@InspcFieldHelp", "help [F1]", GralMng.get().actionHelp);
221    this.btnRefresh = new GralButton("@InspcFieldRefresh", "refresh [F5]", actionRefresh);
222    this.btnShowAll = new GralButton("@InspcFieldShowAll", "show all [c+]", actionShowAll);
223    this.btnSetValue = new GralButton("@InspcFieldSetValue", "set values", actionSetValues);
224    this.btnRepeat = new GralButton("@InspcFieldSetValue", "repeat", null);
225    this.btnRepeat.setSwitchMode(GralColor.getColor("gn"), GralColor.getColor("wh"), null);
226    this.inspcMng = inspcMng;
227  }
228  
229  
230  public void setToPanel(GralMng mng){
231    wind.createImplWidget_Gthread();
232    mng.setPosition(0, 2, 0, 3, 0, 'd');
233    btnBack.createImplWidget_Gthread();
234    mng.setPosition(0, 2, 3, 0, 0, 'd');
235    widgPath.createImplWidget_Gthread();
236    mng.setPosition(2, -4, 0, 0, 0, 'd');
237    widgTable.createImplWidget_Gthread();
238    mng.setPosition(-2, 0, 0, 9, 0, 'r');
239    btnHelp.createImplWidget_Gthread();
240    btnRefresh.createImplWidget_Gthread();
241    mng.setPosition(-2, 0, sizeName, sizeName + 12, 1, 'r');
242    btnSetValue.createImplWidget_Gthread();
243    //mng.setPosition(-2, 0, sizeName+13, sizeName + 23, 0, 'r');
244    btnRepeat.createImplWidget_Gthread();
245    //mng.setPosition(-2, 0, sizeName + 13, sizeName + 23, 0, 'r');
246    btnShowAll.createImplWidget_Gthread();
247  }
248  
249  
250  
251  void fillTableFromFocusedVariable(){
252    GralWidget widgd = wind.gralMng().getWidgetInFocus();
253    if(widgd !=null){
254      String sDatapathWithPrefix = widgd.getDataPath();
255      String sDatapath = widgd.gralMng().getReplacerAlias().replaceDataPathPrefix(sDatapathWithPrefix);
256      int posLastDot = sDatapath.lastIndexOf('.');
257      if(posLastDot >=0) {
258        sPathStruct = sDatapath.substring(0, posLastDot);  //With device:path
259      } else {
260        sPathStruct = "";
261      }
262      VariableAccess_ifc vari = inspcMng.getVariable(sDatapath);
263      if(vari instanceof InspcVariable){
264        InspcVariable vars = (InspcVariable) vari;
265        if(vars.getStruct() != null) {
266          this.structVar = vars; //It is a non-leafe-variable, use it to show the fields. 
267        } else {
268          this.structVar = vars.parent;   //for a leaf variable, normal case, if it is a non-leafe-variable, use its parent though.
269        }
270        //if(structVar == null)
271        //struct = var.struct();
272        //- sPathStruct = struct.path();  //NOTE it is without device.
273        //
274        fillTableStruct(true);  //checks whether the struct is updated, requests update.
275        //
276      } else { //NOTE: fillTableStruct sets the widgPath too.
277        widgPath.setText(sPathStruct);
278      }
279    }
280    widgTable.setFocus(); 
281    wind.setVisible(true);
282    
283  }
284  
285  
286  
287  /**Fills the graphic table to show the structural content of a data node (a C struct or class). 
288   * @since 2018-09: For array elements uses the {@link InspcFieldOfStruct#hasSubstruct} info from the parent (the field of the array).
289   *   Note: It comes from the target: "type..." or "type[size]..." in the telegram. 
290   * @param bCanRequest
291   */
292  void fillTableStruct(boolean bCanRequest){
293    widgTable.clearTable();
294    InspcStruct struct = structVar.getOrCreateStructForNonLeafVariables();
295    if(struct.isUpdated()){
296      //search the root:
297      List<InspcVariable> parents = new LinkedList<InspcVariable>();
298      InspcVariable parent = structVar;
299      while(parent !=null) {
300        parents.add(parent);
301        parent = parent.parent;
302      }
303      //fill parents
304      ListIterator<InspcVariable> iter = parents.listIterator(parents.size());
305      
306      while( iter.hasPrevious()) {  //traverse through list from start.
307        parent = iter.previous();
308        //InspcStruct struct1 = parent.struct();
309        InspcFieldOfStruct fieldParent = new InspcFieldOfStruct(parent, null, 0, true);
310        GralTableLine_ifc<InspcFieldOfStruct> line = widgTable.addLine(parent.ds.sName, null, fieldParent);
311        line.setCellText("/", 0);
312        line.setCellText(parent.ds.sName, 1);
313        //line.setCellText()
314        line.setDataPath(parent.ds.sDataPath);
315      }
316      //
317      //fill with all fields
318      //
319      for(InspcFieldOfStruct field: struct.fieldIter()){
320        GralTableLine_ifc<InspcFieldOfStruct> line = widgTable.addLine(field.nameShow, null, field);
321        if(field.hasSubstruct) { 
322          line.setCellText("+", 0);
323        } else {
324          line.setCellText("", 0);
325        }
326        line.setCellText(field.nameShow, 1);
327        line.setCellText(field.type, 3);
328        line.setDataPath(sPathStruct + "." + field.nameShow);
329      }
330      //
331      //Select last line:
332      //
333      String key = indexSelection.get(sPathStruct);
334      if(key !=null){
335        widgTable.setCurrentLine(key);
336      }
337    } else if(bCanRequest) {
338      GralTableLine_ifc<InspcFieldOfStruct> line = widgTable.addLine("$", null, null);
339      line.setCellText("pending request", 1);
340      InspcAccess_ifc target = structVar.ds.targetAccessor;
341      //=========>
342      struct.requestFields();  //clear the struct, set to request.
343      target.requestFields(struct.varOfStruct(inspcMng).ds, struct.rxActionGetFields, actionUpdated);
344    } else { //!bCanRequest - calling parameter:
345      GralTableLine_ifc<InspcFieldOfStruct> line = widgTable.addLine("?", null, null);
346      line.setCellText("...no answer", 1);
347    }
348    
349  }
350  
351  
352  
353  
354  /**Shows the current value inside the target for the specified line.
355   * This routine is called both if the operator requests a refresh of content of the line: {@link #actionChgTable}
356   * or if a new value was received: {@link RunOnReceive#run()} invoked from {@link InspcVariable#requestValue(long, Runnable)}.
357   * The last one method requestValue() is invoked in this routine if the argument request is set to true. 
358   * @param line The line of this table, contains the {@link InspcFieldOfStruct} as user data , {@link GralTableLine_ifc#getUserData()}.
359   * Therein the variable for this line is stored or will be created with {@link InspcMng#getOrCreateVariable(InspcStruct, InspcFieldOfStruct)}. 
360   * @param request true then a new value for the variable of this line will be requested here. 
361   */
362  private void showValue(GralTableLine_ifc<InspcFieldOfStruct> line, boolean request){
363    InspcFieldOfStruct field = line.getUserData();
364    if(field !=null){
365      InspcVariable var = field.variable(structVar, inspcMng);  //get or create the variable for the field
366      if(var !=null){
367        long time = System.currentTimeMillis();
368        long timelast = var.getLastRefreshTime();
369        char cType = var.getType();
370        String sVal;
371        switch(cType){
372          case 'F': { float val = var.getFloat(); sVal = Float.toString(val); } break;
373          case 'I': { int val = var.getInt(); sVal = "0x" + Integer.toHexString(val); } break;
374          case 'c': case 's': { sVal = var.getString(); } break;
375          default: { float val = var.getFloat(); sVal = Float.toString(val); }
376        }
377        int dtime = (int)(time - timelast);
378        if(timelast == 0 || dtime > 5000){ //5 sec
379          sVal = "? " + sVal;  //a really old value or never gotten.
380        }
381        if(request && timelast == 0 || dtime > 500){ //request a new value if it is older than 0.5 sec
382          var.requestValue(time, this.new RunOnReceive(line)); 
383        }
384        line.setCellText(sVal, 2);
385      }
386    }
387    
388  }
389  
390  
391  void refresh(){
392    InspcStruct struct = structVar.getOrCreateStructForNonLeafVariables();
393    struct.requestFields();
394    fillTableStruct(true);
395  }
396  
397  
398  void showAll(){
399    fillTableStruct(true);
400    for(GralTableLine_ifc<InspcFieldOfStruct> line: widgTable.iterLines()){
401      showValue(line, true);
402    }
403  }
404  
405  
406  
407  public String getCurrentPathField(){ return sPathCurrent; }
408  
409  
410  
411  public static String getDataPath(GralTable<InspcFieldOfStruct>.TableLineData line)
412  { String sPath = null;
413    String sField1 = line.getKey();
414    //sPathCurrent = line.getDataPath();
415    InspcFieldOfStruct field = line.data;
416    InspcVariable var = field.variableExisting();
417    if(var !=null) {
418      sPath = var.ds.sPathWithAlias;
419    }
420    else 
421    { //The varOfStruct is exisiting in any case. get it to get the short data path.
422      InspcVariable varStruct = field.struct.varOfStruct(null);
423      if(sField1.charAt(0) =='[') { //array element
424        sPath = varStruct.ds.sPathWithAlias + sField1;
425      }
426      else if(":.".indexOf(varStruct.ds.sPathWithAlias.charAt(varStruct.ds.sPathWithAlias.length()-1))<0) {
427        sPath = varStruct.ds.sPathWithAlias + "." + sField1;
428      } else {
429        sPath = varStruct.ds.sPathWithAlias + sField1;
430      }
431    }
432    return sPath;
433  }
434  
435  
436  
437  
438  void setCurrentFieldInfo(GralTable<InspcFieldOfStruct>.TableLineData line){
439    sFieldCurrent = line.getKey();
440    sPathCurrent = getDataPath(line);
441    timeLineSelected = System.currentTimeMillis();
442    widgPath.setText(sPathCurrent + " : " + line.getCellText(3));
443
444  }
445  
446  
447  void actionBack(){
448    GralTable<InspcFieldOfStruct>.TableLineData line = widgTable.getCurrentLine();
449    String key;
450    if(line !=null) {
451      key = line.getKey();
452      indexSelection.put(sPathStruct, key);  //select the current field if the view goes back to this table
453    }  
454    InspcVariable parent = structVar.parent;
455    if(parent !=null){
456      int posNameInPath = sPathStruct.lastIndexOf('.')+1; 
457      key = sPathStruct.substring(posNameInPath);   //it is the field name of this table in the parent.
458      this.structVar = parent;
459      this.sPathStruct = parent.ds.sDataPath;
460      indexSelection.put(sPathStruct, key);  //select the field of the leaved table in its parent!
461      fillTableStruct(true);
462    }
463  }
464  
465  
466  void getSubStruct(GralTableLine_ifc<InspcFieldOfStruct> line){
467    String key = line.getKey();
468    indexSelection.put(sPathStruct, key);
469    
470    InspcFieldOfStruct field = line.getUserData();
471    if(field.nrofArrayElements >1){
472      if(!line.hasChildren()){
473        //no array elements yet initialized:
474        //InspcVariable varArray = field.variable(structVar, inspcMng);
475        InspcStruct structArray = field.struct;  //varArray.struct();
476        String[] lineTexts = new String[4];
477        for(int ix = 0; ix < field.nrofArrayElements; ++ix) {
478          String ident = field.identifier + "[" + ix + "]";  //creates a field with index
479          //The field for the array element:
480          boolean bHasSubstruct = field.hasSubstruct; //The info about sub structure is stored in the array field already.
481          InspcFieldOfStruct fieldElement = new InspcFieldOfStruct(structArray, ident, ident, field.type, -1, bHasSubstruct);
482          lineTexts[0] = "-";
483          lineTexts[1] = ident;
484          lineTexts[2] = "";
485          lineTexts[3] = field.type;
486          line.addChildLine(ident, lineTexts, fieldElement);
487        }
488      }
489      line.showChildren(true, false, true);
490    }
491    else if(field.hasSubstruct){
492      structVar = field.variable(structVar, inspcMng);
493      sPathStruct = structVar.ds.sDataPath;
494      fillTableStruct(true);
495    }
496  }
497  
498  
499  
500  void actionSetValues(){
501    for(GralTableLine_ifc<InspcFieldOfStruct> line: widgTable.iterLines()){
502      if(line.isChanged(true)){
503        sendValueChange(line);
504      }
505    }
506  }
507  
508  /**This method is invoked if the value in the table is changed and this change should be applied to the target, key ctrl-enter. */
509  void sendValueChange(GralTableLine_ifc<InspcFieldOfStruct> line) {
510    InspcFieldOfStruct field = line.getUserData(); ////
511    String sValue = line.getCellText(2);
512    InspcVariable var = field.variable(structVar, inspcMng);  //creates the variable if not given yet.
513    TxOrderSetValue order = new TxOrderSetValue(sValue, var);
514    var.ds.targetAccessor.addUserTxOrder(order);
515  }
516  
517  
518  
519  
520  
521  
522  GralUserAction actionOpenWindow = new GralUserAction("InspcFieldTable - open window"){
523    @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params){
524      if(KeyCode.isControlFunctionMouseUpOrMenu(key)){
525        fillTableFromFocusedVariable();
526        return true;
527      } else { 
528        return false;
529      }
530    }
531  };
532
533  
534  
535  /**This action is invoked on any keyboard hits which are not handled in the GralTable.
536   * 
537   */
538  GralUserAction actionChgTable = new GralUserAction("InspcFieldTable - change Table"){
539    @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params){
540      if(KeyCode.isControlFunctionMouseUpOrMenu(key)){
541        assert(params[0] instanceof GralTableLine_ifc<?>);
542        @SuppressWarnings("unchecked")
543        GralTableLine_ifc<InspcFieldOfStruct> line = (GralTableLine_ifc<InspcFieldOfStruct>)params[0];
544        if(key == KeyCode.enter){
545          showValue(line, true);
546        } else if(key == KeyCode.ctrl + KeyCode.enter) {
547          sendValueChange(line); ////
548        } else if(key == KeyCode.ctrl + 'R' || key == KeyCode.F5){
549          refresh();
550        } else if(key == KeyCode.ctrl + KeyCode.pgup) {
551          actionBack();
552        } else if(key == KeyCode.ctrl + KeyCode.pgdn) {
553          getSubStruct(line);
554        } else if(key == KeyCode.ctrl + '+') {
555          showAll();
556        }
557        return true;
558      } else if(key == KeyCode.mouse1Double){
559        assert(params[0] instanceof GralTableLine_ifc<?>);
560        @SuppressWarnings("unchecked")
561        GralTableLine_ifc<InspcFieldOfStruct> line = (GralTableLine_ifc<InspcFieldOfStruct>)params[0];
562        InspcFieldOfStruct field = line.getUserData();
563        if(field.hasSubstruct){
564          getSubStruct(line);
565        } else {
566          showValue(line, true);
567        }
568        return true;
569      } else { 
570        return false;
571      }
572    }
573  };
574
575  
576  
577  GralUserAction actionLineSelected = new GralUserAction("InspcFieldTable - line selected"){
578    @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params){
579      if(key == KeyCode.defaultSelect || key == KeyCode.userSelect){
580        @SuppressWarnings("unchecked")
581        GralTable<InspcFieldOfStruct>.TableLineData line = (GralTable.TableLineData)params[0];
582        if(line !=null && line.data !=null) { setCurrentFieldInfo(line); }
583        return true;
584      } else { 
585        return false;
586      }
587    }
588  };
589
590  
591  
592  
593  GralUserAction actionBack = new GralUserAction("InspcFieldTable - back"){
594    @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params){
595      if(KeyCode.isControlFunctionMouseUpOrMenu(key)){
596        actionBack();
597        return true;
598      } else { 
599        return false;
600      }
601    }
602  };
603
604  
605  
606  
607  GralUserAction actionRefresh = new GralUserAction("InspcFieldTable - refresh"){
608    @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params){
609      if(KeyCode.isControlFunctionMouseUpOrMenu(key)){
610        refresh();
611        return true;
612      } else { 
613        return false;
614      }
615    }
616  };
617
618  
619  
620  GralUserAction actionShowAll = new GralUserAction("InspcFieldTable - show all"){
621    @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params){
622      if(KeyCode.isControlFunctionMouseUpOrMenu(key)){
623        showAll();
624        return true;
625      } else { 
626        return false;
627      }
628    }
629  };
630
631  
632  
633  GralUserAction actionSetValues = new GralUserAction("InspcFieldTable - set values"){
634    @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params){
635      if(KeyCode.isControlFunctionMouseUpOrMenu(key)){
636        //bSetValue.set(true);
637        actionSetValues();
638        //InspcFieldTable.this.inspcMng.addUserOrder(InspcFieldTable.this);
639        return true;
640      } else { 
641        return false;
642      }
643    }
644  };
645
646  
647  
648  Runnable actionUpdated = new Runnable(){
649    @Override public void run(){ fillTableStruct(false); }  //after receive the datagram, can not request newly.
650  };
651
652  
653  
654}