    001package org.vishia.commander;
    002
    003import java.io.File;
    004import java.text.DateFormat;
    005import java.text.ParseException;
    006import java.text.SimpleDateFormat;
    007import java.util.Date;
    008import java.util.EventObject;
    009
    010import org.vishia.event.EventCmdtype;
    011import org.vishia.event.EventConsumer;
    012import org.vishia.event.EventSource;
    013import org.vishia.fileRemote.FileRemote;
    014import org.vishia.fileRemote.FileRemoteAccessor;
    015import org.vishia.gral.base.GralButton;
    016import org.vishia.gral.base.GralLed;
    017import org.vishia.gral.base.GralPos;
    018import org.vishia.gral.base.GralWidget;
    019import org.vishia.gral.base.GralWindow;
    020import org.vishia.gral.ifc.GralColor;
    021import org.vishia.gral.ifc.GralTextField_ifc;
    022import org.vishia.gral.ifc.GralUserAction;
    023import org.vishia.gral.ifc.GralWindow_ifc;
    024import org.vishia.gral.widget.GralFileSelector;
    025import org.vishia.util.FileSystem;
    026import org.vishia.util.KeyCode;
    027
    028public class FcmdFileProps
    029{
    030  /**Version, history and license
    031   * <ul>
    032   * <li>2015-05-16 Hartmut bufgix: dateFormat
    033   * <li>2012-03-10 Hartmut improved: Now works tested in windows
    034   * <li>2012-03-09 Hartmut created, but not used yet
    035   * </ul>
    036   *
    037   *
    038   * <b>Copyright/Copyleft</b>:
    039   * For this source the LGPL Lesser General Public License,
    040   * published by the Free Software Foundation is valid.
    041   * It means:
    042   * <ol>
    043   * <li> You can use this source without any restriction for any desired purpose.
    044   * <li> You can redistribute copies of this source to everybody.
    045   * <li> Every user of this source, also the user of redistribute copies
    046   *    with or without payment, must accept this license for further using.
    047   * <li> But the LPGL ist not appropriate for a whole software product,
    048   *    if this source is only a part of them. It means, the user
    049   *    must publish this part of source,
    050   *    but don't need to publish the whole source of the own product.
    051   * <li> You can study and modify (improve) this source
    052   *    for own using or for redistribution, but you have to license the
    053   *    modified sources likewise under this LGPL Lesser General Public License.
    054   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
    055   * </ol>
    056   * If you are intent to use this sources without publishing its usage, you can get
    057   * a second license subscribing a special contract with the author.
    058   *
    059   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
    060   *
    061   *
    062   */
    063  public static final int version = 20120309;
    064
    065
    066  protected final Fcmd main;
    067
    068  GralWindow_ifc windFileProps;
    069  GralTextField_ifc widgName, widgDir, widgLink, widgDate, widgLength;
    070
    071  GralButton[] widgRd, widgWr, widgEx;
    072  GralButton widgUID, widgGID, widgSticky;
    073  GralButton widgHidden, widgDirectory;
    074  GralButton widgBtnDirBytes;
    075
    076  /**Action button. */
    077  GralButton widGetAllProps, widgChrRecurs, widgChgFile, widgCopyFile;
    078
    079
    080  DateFormat formatDate;
    081
    082  final String sWrAble = "wr / ?rd", sRdOnly = "rd / ?wr";
    083
    084  final String sHidden = "hidden / ?", sNonHidden = "non hidden / ?";
    085
    086  final String sSubdir = "recursive / ?", sNonSubdir = "non recurs/ ?";
    087
    088  static final String sCmdChg = "change", sCmdCopy = "copy", sCmdChgRecurs = "chgRecurs"
    089    , sCmdQuit = "quit", sCmdAbort = "abort";
    090
    091  /**True if it is a unix system. It checks the "OS" environment variable. */
    092  boolean bUnixSystem;
    093
    094  /**True then the window is opened. Write acutal file into. */
    095  boolean isVisible;
    096
    097  /**True while a change commission is send and no answer is received yet. */
    098  //boolean busyChanging;
    099
    100  FileRemote actFile;
    101
    102  /**
    103   *
    104   */
    105  final FileRemote.CallbackEvent evChg;
    106
    107  final FileRemote.CallbackEvent evCntLen;
    108
    109
    110
    111  public FcmdFileProps(Fcmd main)
    112  { this.main = main;
    113    this.formatDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    114    evChg = new FileRemote.CallbackEvent(evSrc, null, null, callbackChgProps, null, evSrc);
    115    evCntLen = new FileRemote.CallbackEvent(evSrc, null, null, callbackCntLen, null, evSrc);
    116  }
    117
    118
    119  /**Builds the content of the file property window. The window is created static. It is shown
    120   * whenever it is used.  */
    121  void buildWindow()
    122  { String sUnix = System.getenv("OS");
    123    if(sUnix !=null){
    124      sUnix = sUnix.toUpperCase();
    125      if(sUnix.contains("WINDOWS")){
    126        bUnixSystem = false;
    127      } else {
    128        bUnixSystem = true;
    129      }
    130    } else {
    131      bUnixSystem = System.getenv("WINDIR") == null;
    132    }
    133    main._gralMng.selectPanel("primaryWindow");
    134    main._gralMng.setPosition(-30, 0, -47, 0, 1, 'r'); //right buttom, about half less display width and hight.
    135    int windProps = GralWindow.windConcurrently;
    136    GralWindow window =  main._gralMng.createWindow("windProp", "file properties - The.file.Commander", windProps);
    137    windFileProps = window;
    138    main._gralMng.setPosition(3.5f, GralPos.size -3, 1, -1, 0, 'd');
    139    widgLink = main._gralMng.addTextField("link", false, "symbolic link", "t");
    140    widgDir = main._gralMng.addTextField("dir", false, "directory path", "t");
    141    main._gralMng.setPosition(10, GralPos.size -4, 1, -1, 0, 'd');
    142    widgName = main._gralMng.addTextField("name", true, "filename", "t");
    143    main._gralMng.setPosition(14, GralPos.size -3.5f, 1, 24, 0, 'r');
    144    widgLength = main._gralMng.addTextField("length", false, "file-length", "t");
    145    main._gralMng.setPosition(14, GralPos.size -3.5f, 25, -1, 0, 'r');
    146    widgDate = main._gralMng.addTextField("data", true, "last modified", "t");
    147    GralColor colorBack = main._gralMng.propertiesGui.colorBackground_;
    148    GralColor colorText = GralColor.getColor("bk");
    149    int ii;
    150    GralColor colorOn = GralColor.getColor("lgn");
    151    GralColor colorOff = GralColor.getColor("wh");
    152    GralColor colorDis = GralColor.getColor("gr");
    153    String textOn = "X", textOff = " ", textDis = "?";
    154
    155    main._gralMng.setPosition(17, GralPos.size -2, 1, 20, 0, 'r', 0.2f);
    156    widgBtnDirBytes = main._gralMng.addButton("dirBytes", actionBtnCntLen, "dirBytes", null, main.idents.buttonFilePropsCntLen);
    157    widgBtnDirBytes.setDisableColorText(colorDis, " (file) ");
    158    //widgDirectory = main.gralMng.addCheckButton("FileProp:btndir", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    159    //main.gralMng.addText("directory");
    160    main._gralMng.setPosition(17, GralPos.size -2, 22, GralPos.size +2, 0, 'r', 0.2f);
    161    widgHidden = main._gralMng.addCheckButton("FileProp:btnhidden", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    162    main._gralMng.addText("hidden");
    163
    164    //bUnixSystem = true;
    165    if(bUnixSystem){
    166      main._gralMng.setPosition(20, GralPos.size -2, 10, 28, 0, 'd');
    167      widGetAllProps = main._gralMng.addButton("buttonFilePropsGetAll", actionButton, "getAll", null, main.idents.buttonFilePropsGetAll);
    168
    169      main._gralMng.setPosition(20, GralPos.size -2, 1, GralPos.size +2, 0, 'd');
    170      main._gralMng.addText("rd");
    171      widgRd = new GralButton[3];
    172      for(ii=0; ii < 3; ++ii){
    173        widgRd[ii] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    174        //widgReadonly[ii] = main.gralMng.addSwitchButton("FileProp:btnro", "X", " ", GralColor.getColor("wh"), GralColor.getColor("lgn"));
    175      }
    176      main._gralMng.setPosition(20, GralPos.size -2, 4, GralPos.size +2, 0, 'd');
    177      main._gralMng.addText("wr");
    178      widgWr = new GralButton[3];
    179      for(ii=0; ii < 3; ++ii){
    180        widgWr[ii] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    181      }
    182      main._gralMng.setPosition(20, GralPos.size -2, 7, GralPos.size +2, 0, 'd');
    183      main._gralMng.addText("ex");
    184      widgEx = new GralButton[3];
    185      for(ii=0; ii < 3; ++ii){
    186        widgEx[ii] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    187      }
    188      main._gralMng.setPosition(22, GralPos.size -2, 10, GralPos.size +6, 0, 'd');
    189      main._gralMng.addText("owner");
    190      main._gralMng.addText("group");
    191      main._gralMng.addText("all");
    192      main._gralMng.setPosition(22, GralPos.size -2, 18, GralPos.size +2, 0, 'r', 0.2f);
    193      widgUID = main._gralMng.addCheckButton("FileProp:btnUID", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    194      main._gralMng.addText("UID");
    195      main._gralMng.setPosition(24, GralPos.size -2, 18, GralPos.size +2, 0, 'r', 0.2f);
    196      widgGID = main._gralMng.addCheckButton("FileProp:btnGID", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    197      main._gralMng.addText("GID");
    198      main._gralMng.setPosition(26, GralPos.size -2, 18, GralPos.size +2, 0, 'r', 0.2f);
    199      widgSticky = main._gralMng.addCheckButton("FileProp:btnSticky", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    200      main._gralMng.addText("sticky");
    201    } else {
    202      main._gralMng.setPosition(20, GralPos.size -2, 1, GralPos.size +2, 0, 'd');
    203      main._gralMng.addText("rd");
    204      widgRd = new GralButton[1];
    205      widgRd[0] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    206      main._gralMng.setPosition(20, GralPos.size -2, 4, GralPos.size +2, 0, 'd');
    207      main._gralMng.addText("wr");
    208      widgWr = new GralButton[1];
    209      widgWr[0] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    210      main._gralMng.setPosition(20, GralPos.size -2, 7, GralPos.size +2, 0, 'd');
    211      main._gralMng.addText("ex");
    212      widgEx = new GralButton[1];
    213      widgEx[0] = main._gralMng.addCheckButton("FileProp:btnro", textOn, textOff, textDis, colorOn, colorOff, colorDis);
    214
    215    }
    216    main._gralMng.setPosition(-12, GralPos.size +3, -16, -1, 0, 'd',1);
    217    widgCopyFile =  main._gralMng.addButton("buttonFilePropsCopy", actionButton, sCmdCopy, null,  main.idents.buttonFilePropsCopy);
    218    widgChrRecurs =   main._gralMng.addButton("buttonFilePropsChgRecursive", actionButton, sCmdChgRecurs, null,  main.idents.buttonFilePropsChgRecurisve);
    219    widgChgFile = main._gralMng.addButton("buttonFilePropsChg", actionButton, sCmdChg, null,  main.idents.buttonFilePropsChg);
    220  }
    221
    222
    223  /**Opens the view window and fills its content.
    224   * @param src The path which is selected as source. It may be a directory or a file.
    225   */
    226  void openDialog(FileRemote src)
    227  { //String sSrc, sTrash;
    228    isVisible = true;
    229    showFileInfos(src);
    230    windFileProps.setFocus(); //WindowVisible(true);
    231
    232  }
    233
    234
    235  void showFileInfos(FileRemote src){
    236    if(isVisible && !evChg.isOccupied()){
    237      actFile = src;
    238      //TODO don't access the file system without user activity!!! need: a refresh button!!
    239      widgChgFile.setText(main.idents.buttonFilePropsChg);
    240      widgChrRecurs.setText(main.idents.buttonFilePropsChgRecurisve);
    241      widgCopyFile.setText(main.idents.buttonFilePropsCopy);
    242      widgName.setText(src.getName());
    243      widgDir.setText(src.getParent());
    244      String sDate = formatDate.format(new Date(src.lastModified()));
    245      widgDate.setText(sDate);
    246      String sLength;
    247      long length = src.length();
    248      sLength = "" + length;
    249      if(length >= 10000 && length < 10000000){
    250        sLength += " = " + length/1000 + "k";
    251      } else if( length >= 10000000){
    252        sLength += " = " + length/1000000 + "M";
    253      }
    254      widgLength.setText(sLength);
    255      if(src instanceof FileRemote && (src).isSymbolicLink()){
    256        widgLink.setText(FileSystem.getCanonicalPath(src));
    257      } else {
    258        widgLink.setText("");
    259      }
    260      widgRd[0].setState(src.canRead() ? GralButton.State.On : GralButton.State.Off);
    261      widgEx[0].setState(src.canExecute() ? GralButton.State.On : GralButton.State.Off);
    262      widgWr[0].setState(src.canWrite() ? GralButton.State.On : GralButton.State.Off);
    263      if(bUnixSystem){
    264        widgRd[1].setState(GralButton.State.Disabled);
    265        widgRd[2].setState(GralButton.State.Disabled);
    266        widgWr[1].setState(GralButton.State.Disabled);
    267        widgWr[2].setState(GralButton.State.Disabled);
    268        widgEx[1].setState(GralButton.State.Disabled);
    269        widgEx[2].setState(GralButton.State.Disabled);
    270        widgSticky.setState(GralButton.State.Disabled);
    271        widgUID.setState(GralButton.State.Disabled);
    272        widgGID.setState(GralButton.State.Disabled);
    273      }
    274      widgHidden.setState(src.isHidden() ? GralButton.State.On : GralButton.State.Off);
    275      widgBtnDirBytes.setState(src.isDirectory() ? GralButton.State.On : GralButton.State.Disabled);
    276      //widgDirectory.setState(src.isDirectory() ? GralButton.State.On : GralButton.State.Off);
    277    }
    278  }
    279
    280
    281  /**Action for Key F2 for view command.
    282   */
    283  GralUserAction actionOpenDialog = new GralUserAction("actionOpenDialog")
    284  {
    285    @Override public boolean userActionGui(int keyCode, GralWidget infos, Object... params){
    286      if(KeyCode.isControlFunctionMouseUpOrMenu(keyCode)){
    287        openDialog(main.currentFile());
    288      }
    289      return true;
    290    }
    291  };
    292
    293
    294  EventSource evSrc = new EventSource("FcmdFileProps"){
    295    @Override public void notifyDequeued(){
    296
    297    }
    298    @Override public void notifyConsumed(int ctConsumed){}
    299    @Override public void notifyRelinquished(int ctConsumed){}
    300  };
    301
    302
    303
    304  /**Action for all buttons of 'file properties' window:
    305   */
    306  GralUserAction actionButton = new GralUserAction("actionBtnCntLen")
    307  {
    308    @Override public boolean userActionGui(int keyCode, GralWidget infos, Object... params)
    309    { if(KeyCode.isControlFunctionMouseUpOrMenu(keyCode)){  //not on mouse down but on mouse up.
    310        String name = widgName.getText();
    311        if(name.equals(actFile.getName())){ name = null; } //don't change it.
    312        int noMask = 0;
    313        int val = 0; //actFileRemote.getFlags();
    314        int mask;
    315        String sDate = widgDate.getText();
    316        long date;
    317        try{
    318          Date date1 = formatDate.parse(sDate);
    319          date = date1.getTime();
    320        } catch(ParseException exc){
    321          date = 0;
    322        }
    323        if(bUnixSystem){
    324          mask = FileRemote.mCanRead | FileRemote.mCanWrite | FileRemote.mExecute;
    325        } else {
    326          mask = FileRemote.mCanWrite | FileRemote.mHidden;
    327        }
    328        switch(widgRd[0].getState()){
    329          case Off:       val &= ~FileRemote.mCanRead; break;
    330          case On:       val |= FileRemote.mCanRead; break;
    331          case Disabled: mask &= ~FileRemote.mCanRead; break;
    332        }
    333        switch(widgWr[0].getState()){
    334          case Off:       val &= ~FileRemote.mCanWrite; break;
    335          case On:       val |= FileRemote.mCanWrite; break;
    336          case Disabled: mask &= ~FileRemote.mCanWrite; break;
    337        }
    338        switch(widgEx[0].getState()){
    339          case Off:       val &= ~FileRemote.mExecute; break;
    340          case On:       val |= FileRemote.mExecute; break;
    341          case Disabled: mask &= ~FileRemote.mExecute; break;
    342        }
    343        if(bUnixSystem){
    344          switch(widgRd[1].getState()){
    345            case Off:       val &= ~FileRemote.mCanReadGrp; break;
    346            case On:       val |= FileRemote.mCanReadGrp; break;
    347            case Disabled: mask &= ~FileRemote.mCanReadGrp; break;
    348          }
    349          switch(widgWr[1].getState()){
    350            case Off:       val &= ~FileRemote.mCanWriteGrp; break;
    351            case On:       val |= FileRemote.mCanWriteGrp; break;
    352            case Disabled: mask &= ~FileRemote.mCanWriteGrp; break;
    353          }
    354          switch(widgEx[1].getState()){
    355            case Off:       val &= ~FileRemote.mExecuteGrp; break;
    356            case On:       val |= FileRemote.mExecuteGrp; break;
    357            case Disabled: mask &= ~FileRemote.mExecuteGrp; break;
    358          }
    359          switch(widgRd[2].getState()){
    360            case Off:       val &= ~FileRemote.mCanReadAny; break;
    361            case On:       val |= FileRemote.mCanReadAny; break;
    362            case Disabled: mask &= ~FileRemote.mCanReadAny; break;
    363          }
    364          switch(widgWr[2].getState()){
    365            case Off:       val &= ~FileRemote.mCanWriteAny; break;
    366            case On:       val |= FileRemote.mCanWriteAny; break;
    367            case Disabled: mask &= ~FileRemote.mCanWriteAny; break;
    368          }
    369          switch(widgEx[2].getState()){
    370            case Off:       val &= ~FileRemote.mExecuteAny; break;
    371            case On:       val |= FileRemote.mExecuteAny; break;
    372            case Disabled: mask &= ~FileRemote.mExecuteAny; break;
    373          }
    374        }
    375        switch(widgHidden.getState()){
    376          case Off:       val &= ~FileRemote.mHidden; break;
    377          case On:        val |=  FileRemote.mHidden; break;
    378          case Disabled: mask &= ~FileRemote.mHidden; break;
    379        }
    380        val &= mask;   //only used bits.
    381        boolean bAbort = false;
    382        if(infos.sCmd.equals(sCmdAbort)){
    383          if(evChg.occupy(evSrc, callbackChgProps, null, true)){
    384            widgChgFile.setText(main.idents.buttonFilePropsChg);
    385            infos.sCmd = sCmdChg;
    386          } else {
    387            System.err.println("chg properties hangs");
    388          }
    389        } else if(infos.sCmd.equals(sCmdChg)){
    390          if(evChg.occupy(evSrc, callbackChgProps, null, true)){
    391            //cmds with callback
    392            widgChgFile.setText(main.idents.buttonFilePropsChanging);
    393            actFile.chgProps(name, mask, val, date, evChg);
    394            main.refreshFilePanel(actFile.getParentFile());  //refresh the panel if the directory is shown there
    395          } else { bAbort = true; }
    396          //
    397        } else if(infos.sCmd.equals(sCmdChgRecurs)){
    398          if(evChg.occupy(evSrc, callbackChgProps, null, true)){
    399            //cmds with callback
    400            widgChrRecurs.setText(main.idents.buttonFilePropsChanging);
    401            actFile.chgPropsRecursive(mask, val, date, evChg);
    402          } else { bAbort = true; }
    403          //
    404        } else if(infos.sCmd.equals(sCmdCopy)){
    405          if(evChg.occupy(evSrc, callbackChgProps, null, true)){
    406            if(name !=null && !name.equals(actFile.getName())){
    407              widgCopyFile.setText(main.idents.buttonFilePropsCopying);
    408              FileRemote fileNew = actFile.getParentFile().child(name);
    409              actFile.copyTo(fileNew, evChg, FileRemote.modeCopyReadOnlyOverwrite | FileRemote.modeCopyCreateYes | FileRemote.modeCopyExistAll);
    410            } else {
    411              widgCopyFile.setText("copy - name?");
    412            }
    413          } else { bAbort = true; }
    414        }
    415        if(bAbort){
    416          widgChgFile.setText(main.idents.buttonFilePropsAbort);
    417          widgChgFile.sCmd = sCmdAbort;
    418        }
    419      }
    420      return true;
    421      // /
    422    }
    423  };
    424
    425
    426
    427  GralUserAction actionInvisible = new GralUserAction("actionInvisible")
    428  { @Override public boolean userActionGui(int keyCode, GralWidget infos, Object... params)
    429    { isVisible = false;
    430      return true;
    431    }
    432  };
    433
    434
    435  EventConsumer callbackChgProps = new EventConsumer()
    436  { @Override public int processEvent(EventObject evP)
    437    { FileRemote.CallbackEvent ev = (FileRemote.CallbackEvent)evP;
    438      if(ev.getCmd() == FileRemote.CallbackCmd.done){
    439        showFileInfos(actFile);
    440        widgChgFile.setText(main.idents.buttonFilePropsOk);
    441      } else {
    442        widgChgFile.setText(main.idents.buttonFilePropsRetry);
    443      }
    444      ev.relinquish();
    445      return 1;
    446    }
    447
    448     @Override public String toString(){ return "FcmdFileProps-callbackChgProps"; }
    449
    450  };
    451
    452
    453
    454
    455  /**Action for Key F2 for view command.
    456   */
    457  GralUserAction actionBtnCntLen = new GralUserAction("actionBtnCntLen")
    458  {
    459    @Override public boolean userActionGui(int keyCode, GralWidget infos, Object... params)
    460    { if(KeyCode.isControlFunctionMouseUpOrMenu(keyCode)){
    461        widgBtnDirBytes.setText("counting ...");
    462        if(0 != evCntLen.occupyRecall(100, evSrc, callbackCntLen, null, true)){
    463          actFile.countAllFileLength(evCntLen);
    464        }
    465      }
    466      return true;
    467  } };
    468
    469  EventConsumer callbackCntLen = new EventConsumer()
    470  { @Override public int processEvent(EventObject evP)
    471    { FileRemote.CallbackEvent ev = (FileRemote.CallbackEvent)evP;
    472      if(ev.getCmd() == FileRemote.CallbackCmd.done){
    473        String sLen = "" + ev.nrofBytesAll;
    474        widgLength.setText(sLen);
    475      } else {
    476        widgLength.setText("error count bytes");
    477      }
    478      widgBtnDirBytes.setText(main.idents.buttonFilePropsCntLen);
    479      ev.relinquish();
    480      return 1;
    481    }
    482
    483    @Override public String toString(){ return "FcmdFileProps - callback cnt length"; }
    484
    485  };
    486
    487
    488
    489}