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}