    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}