    001package org.vishia.gral.widget;
    002
    003import java.io.File;
    004import java.io.IOException;
    005import java.text.SimpleDateFormat;
    006import java.util.Date;
    007import java.util.Iterator;
    008import java.util.LinkedList;
    009import java.util.List;
    010import java.util.Map;
    011import java.util.TreeMap;
    012
    013import org.vishia.commander.Fcmd;
    014import org.vishia.fileRemote.FileAccessZip;
    015import org.vishia.fileRemote.FileCluster;
    016import org.vishia.fileRemote.FileMark;
    017import org.vishia.fileRemote.FileRemote;
    018import org.vishia.fileRemote.FileRemoteCallback;
    019import org.vishia.gral.base.GralButton;
    020import org.vishia.gral.base.GralMenu;
    021import org.vishia.gral.base.GralMng;
    022import org.vishia.gral.base.GralPanelContent;
    023import org.vishia.gral.base.GralPos;
    024import org.vishia.gral.base.GralTable;
    025import org.vishia.gral.base.GralTextField;
    026import org.vishia.gral.base.GralValueBar;
    027import org.vishia.gral.base.GralWidget;
    028import org.vishia.gral.base.GralWindow;
    029import org.vishia.gral.ifc.GralColor;
    030import org.vishia.gral.ifc.GralMngBuild_ifc;
    031import org.vishia.gral.ifc.GralMng_ifc;
    032import org.vishia.gral.ifc.GralTable_ifc;
    033import org.vishia.gral.ifc.GralTextField_ifc;
    034import org.vishia.gral.ifc.GralUserAction;
    035import org.vishia.gral.ifc.GralTableLine_ifc;
    036import org.vishia.gral.ifc.GralWidget_ifc;
    037import org.vishia.gral.ifc.GralWindow_ifc;
    038import org.vishia.util.Assert;
    039import org.vishia.util.Debugutil;
    040import org.vishia.util.FileSystem;
    041import org.vishia.util.IndexMultiTable;
    042import org.vishia.util.KeyCode;
    043import org.vishia.util.Removeable;
    044import org.vishia.util.MarkMask_ifc;
    045import org.vishia.util.SortedTreeWalkerCallback;
    046import org.vishia.util.Timeshort;
    047
    048/**This class is a large widget which contains a list to select files in a directory,
    049 * supports navigation in the directory tree and shows the current path in an extra text field.
    050 * Additional 'search in files' is supported.
    051 * <br><br>
    052 * The type of a file is a {@link FileRemote}. This type inherits from {@link java.io.File}
    053 * and is supported for a local file system on PC. It is possible to work with any remote file system.
    054 * <br><br>
    055 * The graphical user interface allows some action to do with the right mouse menu:
    056 * <ul>
    057 * <li>Refresh content
    058 * <li>sort files by date latest, newest, by size largest, smallest, by name and by extension.
    059 * <li>Search content in files with a dialog window.
    060 * </ul>
    061 * <b>Callback to users program</b>:<br>
    062 * The method {@link #specifyActionOnFileSelected(GralUserAction)} allows any action while the user
    063 * select any file. For example a user's program can show the content of the file.
    064 * <br><br>
    065 * @author Hartmut Schorrig
    066 *
    067 */
    068public class GralFileSelector extends GralWidget implements Removeable //extends GralWidget
    069{
    070
    071  /**Version, history and copyright/copyleft.
    072   * <ul>
    073   * <li>2018-10-28 Hartmut: The Creation and Position is old style yet. Should invoked in the graphic thread.
    074   * TODO: create and position in other (main) thread, createImplWidget_Gthread for Swt incarnation.
    075   * <li>2018-10-28 Hartmut: The {@link #favorList} is unused yet, create only if position is given.
    076   * <li>2018-10-28 Hartmut: questionWindow is removed yet, TODO: should have better solution. Status line!
    077   * <li>2018-10-28 Hartmut: {@link #GralFileSelector(String, int, int[], int[])}: The rows argument is used yet for table zLinesMax.
    078   *   The old last argument size was never used. replaced by the column designation for the {@link #favorList}.
    079   *   If it is null, do not create a {@link #favorList}.
    080   * <li>2016-05-02 Hartmut: actionFileSearch writes a result list in the panel of the file window.
    081   *   Yet you can copy the path and insert in the directory text widget to select the file.
    082   *   TODO: Pressing enter to select, store the list etc.
    083   * <li>2016-05-02 Hartmut now: derived from GralWidget, it is a large widget
    084   * <li>2016-05-03 Hartmut new: {@link #setVisible(boolean)} for this large widget.
    085   * <li>2015-11-19 Hartmut chg: {@link #fillInCurrentDir()} does not refresh if it was refreshed in the last seconds.
    086   * <li>2014-12-26 Hartmut chg: {@link #fillIn(FileRemote, boolean)} now with new meaning of boolean: show newly without refresh with the file system.
    087   *   This is used in callback routines which does a refresh but does not invoke {@link #showFile(FileRemote)}
    088   *   especially called from {@link org.vishia.commander.Fcmd#refreshFilePanel(FileRemote)}.
    089   * <li>2014-01-02 Hartmut chg: does not call 'file.refreshProperties(null);' in {@link #actionSetPath}
    090   *   because the property whether it is a directory or not should be known. Prevents a timeout waiting in graphic thread!!!
    091   * <li>2013-09-06 Hartmut chg: Now a new request of fillIn can be executed with aborting the old one.
    092   * <li>2013-09-15 Hartmut chg: New implementation of {@link #fillIn(FileRemote, boolean)}
    093   *   using the new {@link FileRemote#getChildren(org.vishia.fileRemote.FileRemote.ChildrenEvent).}
    094   * <li>2013-09-14 Hartmut chg: Sets the background to magenta while refreshing.
    095   * <li>2013-09-05 Hartmut chg: {@link #getSelectedFiles(boolean, int)} now has that 2 arguments for directory and mark mask.
    096   *   Now it is possible and necessary for the application to choice whether directories are gotten too.
    097   *   The usage is improved. If some files are marked but not visible, it was able that the user does not know about
    098   *   this situation and an unexpected behavior for the user is done. Now only marked files are returned
    099   *   if the current file is marked too. The method is correct named 'selected' because the selection is returned,
    100   *   either the current file or all marked.
    101   * <li>2013-06-15 Hartmut chg: {@link #fillIn(FileRemote, boolean)} modi of refresh.
    102   * <li>2013-05-30 Hartmut new: switch refresh mode on and off
    103   * <li>2013-05-20 Hartmut chg: {@link #fillInRefreshed(FileRemote, boolean)} now does not clear
    104   *   the table but writes only lines if the data are changed. This method can be called
    105   *   in a higher frequency without disturbing the appearance of the table. Used for cyclically refresh.
    106   * <li>2013-05-20 Hartmut new: Context menu for sort etc. It was part of Fcmd, now here.
    107   * <li>2013-04-30 Hartmut new: context menu now available. Sort, refresh, deselect
    108   * <li>2013-04-30 Hartmut new: {@link #checkRefresh(long)}, chg: Don't change the content in the table
    109   *   if the content is identical with the current presentation in table, for refreshing.
    110   * <li>2013-04-30 Hartmut chg: {@link #fillIn(FileRemote, boolean)} now uses the {@link FileRemote#timeRefresh}
    111   * <li>2013-04-28 Hartmut new: {@link #actionOnMarkLine} changes the select status of {@link FileRemote#setMarked(int)}
    112   * <li>2013-04-12 Hartmut adapt Event, FileRemote: The attributes Event.data1, data2, oData, refData are removed. Any special data should be defined in any derived instance of the event. A common universal data concept may be error-prone  because unspecified types and meanings.
    113   *   FileRemote: Dedicated attributes for {@link CallbackCmd#successCode} etc.
    114   * <li>2013-03-28 Hartmut chg: {@link #setToPanel(GralMngBuild_ifc, String, int, int[], char)} preserves the panel
    115   *   before calling.
    116   * <li>2012-11-11 Hartmut bugfix: {@link #fillInRefreshed(File, boolean)}: If all files are complete with file info,
    117   *   nevertheless the {@link RefreshTimed#delayedFillin(int) was called because the whole routine was called with false
    118   *   as bCompleteWithFileInfo-argument. Now that is prevented if all files were tested already.
    119   * <li>2012-10-12 Hartmut chg {@link WindowFileSelection#openDialog(String, String)} with title.
    120   * <li>2012-10-01 Hartmut new now {@link #fillIn(File, boolean)} doesn't get the file properties if it is called with false.
    121   *   This makes it faster to show large content of folders on remote devices (in PC-network). If a file is selected
    122   *   it replaces its properties.
    123   * <li>2012-09-24 Hartmut new.jar files opened as zip file.
    124   * <li>2012-07-30 Hartmut improved using of extra thread on refreshing file properties. Write first 'waiting',
    125   *   the other thread writes the content maybe delayed. The user can abort the access if a response is not kept.
    126   * <li>2012-07-28 Hartmut improved zipfile access.
    127   * <li>2012-07-29 Hartmut improved access to the file system using the new capabilities of FileRemote.
    128   *  TODO consequently writing 'wait for response' in table and access to the file in another thread.
    129   * <li>2012-07-01 Hartmut new {@link #setActionOnEnterPathNewFile(GralUserAction)}.
    130   *   Now this widget is used to select a file to read and save in an application other than the.File.commander.
    131   * <li>2012-07-01 Hartmut Refactoring usage of FileRemote: Normally the java.io.File is used.
    132   *   The FileRemote is a specialization, which is not used in all cases.
    133   * <li>2012-06-17 new Hartmut: Now sorts list with name, size, date, format of size and date in list adjusted:
    134   *   Separate timestamp of file for today, last year.
    135   * <li>2012-06-09 new Hartmut: {@link GralFileSelector.WindowFileSelection}, not ready yet.
    136   * <li>2012-04-16 chg: Capability to sort enhanced.
    137   * <li>2012-04-10 chg: Now ctrl-PgUp to select parent dir, like in Norton Commander,
    138   *   ctrl-PgDn to entry in dir (parallel to Enter).
    139   * <li>2012-03-09 new {@link #kColFilename} etc.
    140   * <li>2012-02-14 corr: Anytime a file was selected as current, it was added in the {@link #indexSelection}.
    141   *   Not only if the directory was changed. It is necessary for refresh to select the current file again.
    142   * <li>2011-12-11 chg: If the directory is empty, show --empty-- because the table should not be empty.
    143   * <li>2011-11-27 new: {@link FileAndName#isWriteable}-property.
    144   * <li>2011-11-20 new: Phenomenal basic idea: The files may be located in remote hardware.
    145   *   It means, that a File can't be access here. Therefore the path, name, date, length in the class {@link FileAndName}
    146   *   are the data represents on this process on PC. The access to the file is given with remote access.
    147   *   Usage of inner class FileAndName containing path, name, date, length instead a File instance.
    148   * <li>2011-10-02 new: {@link #setActionOnEnterFile(GralUserAction)}. It executes this action if Enter is pressed (or mouse-left- or doubleclick-TODO).
    149   * <li>2011-10-01 new: {@link #setOriginDir(File)} and {@link #fillInOriginDir()}.
    150   *   The origin dir is the directory of first selection or can be set by user. If the user navigates misty,
    151   *   the origin dir helps to find again the start point.
    152   * <li>2011-09-28 new: {@link #getCurrentDir()}
    153   * <li>2011-08-14 created. Firstly used for the Java commander. But it is a universal file select widget.
    154   * </ul>
    155   *
    156   * <b>Copyright/Copyleft</b>:<br>
    157   * For this source the LGPL Lesser General Public License,
    158   * published by the Free Software Foundation is valid.
    159   * It means:
    160   * <ol>
    161   * <li> You can use this source without any restriction for any desired purpose.
    162   * <li> You can redistribute copies of this source to everybody.
    163   * <li> Every user of this source, also the user of redistribute copies
    164   *    with or without payment, must accept this license for further using.
    165   * <li> But the LPGL is not appropriate for a whole software product,
    166   *    if this source is only a part of them. It means, the user
    167   *    must publish this part of source,
    168   *    but doesn't need to publish the whole source of the own product.
    169   * <li> You can study and modify (improve) this source
    170   *    for own using or for redistribution, but you have to license the
    171   *    modified sources likewise under this LGPL Lesser General Public License.
    172   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
    173   * </ol>
    174   * If you intent to use this source without publishing its usage, you can get
    175   * a second license subscribing a special contract with the author.
    176   *
    177   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
    178   */
    179  public static final String sVersion = "2018-10-28";
    180
    181  //FileRemoteAccessor localFileAccessor = FileRemoteAccessorLocalFile.getInstance();
    182
    183
    184  /**A window for search-in-file dialogue.
    185   * It is instantiated calling {@link GralFileSelector#createWindowConfirmSearchGthread(GralMngBuild_ifc)}.
    186   * The user can invoke {@link #confirmSearchInFiles(GralFileSelector, Appendable)} to open that window.
    187   * Note: An application may have only one of this window thow more as one GralFileSelector may be exist.
    188   * Therefore the window is not instantiated automatically in this class. The user can instantiate it.
    189   *
    190   */
    191  public static class WindowConfirmSearch {
    192
    193    GralWindow_ifc windConfirmSearch;
    194
    195    GralTextField_ifc widgPath, widgMask, widgText;
    196
    197    GralValueBar widgProgression;
    198
    199    /**Buttons. */
    200    GralButton widgEsc, widgSubdirs, widgSearch;
    201
    202    GralFileSelector fileSelector;
    203
    204    Appendable searchOutput;
    205
    206    /**Use {@link GralFileSelector#createWindowConfirmSearchGthread(GralMngBuild_ifc)} to create.
    207    */
    208    protected WindowConfirmSearch(){}
    209
    210
    211    /**Shows the window.
    212     * @param fileSelector
    213     */
    214    public void confirmSearchInFiles(GralFileSelector fileSelector, Appendable searchOutput){
    215      this.fileSelector = fileSelector;
    216      this.searchOutput = searchOutput;
    217      this.widgPath.setText(fileSelector.sCurrentDir);
    218      windConfirmSearch.setFocus(); //setWindowVisible(true);
    219    }
    220
    221
    222    /**Action is called if the button search is pressed. */
    223    GralUserAction actionFileSearch = new GralUserAction("actionFileSearch"){
    224      @Override
    225      public boolean exec(int key, GralWidget_ifc widgi, Object... params){
    226        if(widgi.getCmd().equals("search") && key == KeyCode.mouse1Up){
    227          boolean subDirs = widgSubdirs.isOn();
    228          String mask = (subDirs ? "**/" : "") + widgMask.getText();
    229          String text = widgText.getText();
    230          List<File> files = new LinkedList<File>();
    231          try{
    232            FileSystem.addFileToList(fileSelector.currentDir, mask, files);
    233            fileSelector.fillIn(files);
    234            if(text.length() > 0){
    235              FileSystem.searchInFiles(files, text, searchOutput);
    236            } else {
    237              try{
    238                for(File file: files){
    239                  searchOutput.append("<File=").append(file.getPath()).append(">\n");
    240                }
    241                searchOutput.append("<done: search in files>\n");
    242              }catch(IOException exc){}
    243            }
    244          }
    245          catch(Exception exc){}
    246        }
    247        return true;
    248      }
    249
    250    };
    251
    252  }
    253
    254  protected WindowConfirmSearch windSearch;
    255
    256
    257
    258  /**Action to show the file properties in the info line. This action is called anytime if a line
    259   * was changed in the file view table. */
    260  private final GralUserAction actionOnFileSelection = new GralUserAction(){
    261    @Override public boolean userActionGui(int actionCode, GralWidget widgd, Object... params) {
    262      if(actionCode == KeyCode.userSelect){
    263        @SuppressWarnings("unchecked")
    264        GralTableLine_ifc<FileRemote> line = (GralTableLine_ifc<FileRemote>) params[0];
    265        if(line != null){
    266          FileRemote file = line.getUserData();
    267          String sName = line.getCellText(kColFilename);
    268          if(file.isTested() && file.exists()) {                       //TODO: if the file is not refreshed, new network access, the GUI hangs a long time.
    269            if(sName.equals("..")){
    270              currentFile = file; //.getParentFile();
    271              currentDir = file;  //the same, the directory of this panel.
    272
    273            } else {
    274              currentDir = file.getParentFile();
    275              currentFile = file;
    276              String sDir = file.getParent();
    277              indexSelection.put(sDir, file);    //store the last selection.
    278            }
    279            //System.out.println("GralFileSelector: " + sDir + ":" + sName);
    280            if(actionOnFileSelected !=null){
    281              actionOnFileSelected.exec(0, selectList.wdgdTable, line, file);
    282            }
    283            if(line.getCellText(kColDesignation).startsWith("?")){
    284              completeLine(line, file, System.currentTimeMillis());
    285            }
    286          } else {
    287            //try refresh the file and show in callback. TODO
    288            Debugutil.stop();
    289          }
    290        }
    291      }
    292      return true;
    293    }
    294  };
    295
    296
    297
    298  private final MarkMask_ifc actionOnMarkLine = new MarkMask_ifc(){
    299
    300    @Override public int getMark()
    301    {return 0;
    302    }
    303
    304    @Override public int setNonMarked(int mask, Object oData)
    305    { assert(oData instanceof FileRemote);
    306      FileRemote file = (FileRemote)oData;
    307      file.resetMarked(mask);
    308      return mask;
    309    }
    310
    311    @Override public int setMarked(int mask, Object oData)
    312    { assert(oData instanceof FileRemote);
    313      FileRemote file = (FileRemote)oData;
    314      file.setMarked(mask);
    315      return mask;
    316    }
    317
    318  };
    319
    320
    321  /**Implementation of the base widget.
    322   */
    323  protected class FileSelectList extends GralSelectList<FileRemote>
    324  {
    325    final GralFileSelector outer;
    326
    327    FileSelectList(GralFileSelector outer, String posName, int rows, int[] columns, char size){
    328      //super(name, mng);
    329      super(posName, rows, columns, size);
    330      this.outer = outer;
    331      if(columns.length !=4) { throw new IllegalArgumentException("FileSelectList should have 4 columns");}
    332      super.setLeftRightKeys(KeyCode.ctrl + KeyCode.pgup, KeyCode.ctrl + KeyCode.pgdn);
    333    }
    334
    335
    336
    337    @Override public boolean actionOk(Object userData, GralTableLine_ifc line)
    338    { boolean done = true;
    339      File file = (File)userData;
    340      String fileName;
    341      //File dir = data.file.getParentFile();
    342      //String sDir = dir ==null ? "/" : FileSystem.getCanonicalPath(dir) + "/";
    343      String sName = line.getCellText(1);
    344      if(sName.equals("..")){
    345        actionLeft(userData, line);
    346        //String sParent = getParentDir(file);
    347        //if(sParent !=null){
    348        //  fillIn(sParent);
    349        //}
    350      } else if(file !=null && file.isDirectory()){
    351        actionRight(userData, line);
    352      } else if(file !=null && ((fileName = file.getName()).endsWith(".zip") || fileName.endsWith(".jar"))){
    353        actionRightZip(userData, line);
    354      } else {
    355        if(actionOnEnterFile !=null){
    356          actionOnEnterFile.userActionGui(KeyCode.enter, widgdPathDir, file);
    357        } else {
    358          done = false;
    359        }
    360      }
    361      return done;
    362    }
    363
    364
    365    private String getParentDir(File data){
    366      int zPath = data.getParent().length();
    367      int posSep = data.getParent().lastIndexOf('/',zPath-2);
    368      if(posSep >=0){
    369        String sDirP = data.getParent().substring(0, posSep+1);
    370        return sDirP;
    371      }
    372      else return null;
    373    }
    374
    375
    376    /**The 'action left' for the FileSelector shows the parent directory.
    377     * The {@link GralFileSelector#currentDir} is used to get its parent to show.
    378     * @param line the current line. It is unused because userData contains the file.
    379     * @param userData The {@link GralTableLine_ifc#getUserData()} from line.
    380     *   It is a java.io.File or a {@link FileRemote}
    381     *   which is currently selected. This file is stored as current for the current directory.
    382     *   The parent of the file is the directory which is shown yet.
    383     * @see org.vishia.gral.widget.GralSelectList#actionLeft(java.lang.Object, org.vishia.gral.ifc.GralTableLine_ifc)
    384     */
    385    @Override public void actionLeft(Object userData, GralTableLine_ifc line)
    386    {
    387      //FileRemote currentFile = (FileRemote)userData;
    388      if(currentDir !=null){
    389        String sDir = currentDir.getParent();
    390        String sName = currentDir.getName();
    391        FileRemote parentDir = currentDir.getParentFile();
    392        if(parentDir !=null){
    393          indexSelection.put(sDir, currentDir); //currentDir.getName());
    394          //System.out.println("GralFileSelector: " + sDir + ":" + sName);
    395          fillIn(parentDir, false);
    396        }
    397      }
    398    }
    399
    400
    401    @Override public void actionRight(Object userData, GralTableLine_ifc line)
    402    {
    403      FileRemote currentFile = (FileRemote)userData;
    404      //File dir = data.file.getParentFile();
    405      //String sDir = dir ==null ? "/" : FileSystem.getCanonicalPath(dir);
    406      //String sName = line.getCellText(1);
    407      if(currentFile.isDirectory()){
    408        //save the last selection of that level
    409        //indexSelection.put(currentFile.getParent(), currentFile.getName());
    410        fillIn(currentFile, false);
    411        //fillIn(data.getParent() + "/" + data.getName());
    412      }
    413    }
    414
    415
    416    public void actionRightZip(Object userData, GralTableLine_ifc line)
    417    {
    418      FileRemote currentFile = (FileRemote)userData;
    419      FileRemote fileZipAsDir = FileAccessZip.examineZipFile(currentFile);
    420      //FileZip fileZip = new FileZip(currentFile);
    421      fillIn(fileZipAsDir, false);
    422    }
    423
    424
    425
    426    /* (non-Javadoc)
    427     * @see org.vishia.gral.widget.SelectList#actionUserKey(int, java.lang.Object, org.vishia.gral.ifc.GralTableLine_ifc)
    428     */
    429    @Override public boolean actionUserKey(int keyCode, Object oData, GralTableLine_ifc line)
    430    { boolean ret = true;
    431      ret = outer.actionUserKey(keyCode, oData, line);
    432      return ret;
    433    }
    434
    435
    436  } //selectList implementation
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449  /**Number of columns of the table. */
    450  public static final int zColumns = 4;
    451
    452  /**Column which contains the designation of entry.
    453   * <ul>
    454   * <li>"/" a directory
    455   * <li>">" a linked directory (unix systems, symbolic link)
    456   * <li>" " space, normal file
    457   * <li>"*" a linked file
    458   * <li>"#" comparison result: there are differences
    459   * <li>"+" comparison result: contains more
    460   * <li>"-" comparison result: contains less
    461   *
    462   * </ul>
    463   */
    464  public static final int kColDesignation = 0;
    465
    466  /**Column which contains the filename. The column contains either the name of the file
    467   * or ".." or "--unknown--".
    468   */
    469  public static final int kColFilename = 1;
    470
    471  /**Column which contains the length of file
    472   */
    473  public static final int kColLength = 2;
    474
    475  /**Column which contains the time stamp
    476   */
    477  public static final int kColDate = 3;
    478
    479  public static final char kSortName = 'n';
    480
    481  public static final char kSortNameNonCase = 'N';
    482
    483  public static final char kSortExtension = 'x';
    484
    485  public static final char kSortExtensionNonCase = 'X';
    486
    487  public static final char kSortDateNewest = 'd';
    488
    489  public static final char kSortDateOldest = 'o';
    490
    491  public static final char kSortSizeLargest = 'l';
    492
    493  public static final char kSortSizeSmallest = 's';
    494
    495
    496  public static MenuTexts contextMenuTexts = new MenuTexts();
    497
    498
    499  private char sortOrder = kSortName, sortOrderLast = '0';
    500
    501  /**Determines which time are present in the date/time column.
    502   * m a c for last modified, last access, creation
    503   */
    504  protected char showTime = 'm';
    505
    506
    507  /**The implementation of SelectList. */
    508  protected FileSelectList selectList;
    509
    510
    511  GralColor colorBack, colorBackPending;
    512
    513  private final IndexMultiTable<String, GralTableLine_ifc<FileRemote>> idxLines =
    514    new IndexMultiTable<String, GralTableLine_ifc<FileRemote>>(IndexMultiTable.providerString);
    515
    516  protected final GralTable<String> favorList;
    517
    518  //String name;
    519  //final int rows;
    520  //final int[] columns;
    521  //final char size;
    522
    523
    524  /**This index stores the last selected file for any directory path which was used.
    525   * If the directory path is reused later, the same file will be selected initially.
    526   * It helps by navigation through the file tree.
    527   * <ul>
    528   * <li>The key is the path in canonical form with '/' as separator (in windows too!)
    529   *   but without terminating '/'.
    530   * <li>The value is the name of the file in this directory.
    531   * </ul>
    532   */
    533  private final Map<String, FileRemote> indexSelection = new TreeMap<String, FileRemote>();
    534
    535  //int lineSelected;
    536
    537  /**The time after 1970 when the fillin was invoked and finished at last.
    538   * timeFillinFinished=0, then pending.
    539   */
    540  protected long timeFillinInvoked, timeFilesRefreshed, timeFillinFinished;
    541
    542  /**Duration of last fillin. */
    543  protected int durationRefresh, durationFillin;
    544
    545  protected int refreshCount;
    546
    547  boolean donotCheckRefresh = true;
    548
    549  /**The widget for showing the path. */
    550  protected GralTextField widgdPathDir;
    551
    552  protected GralButton widgBtnFavor;
    553
    554  String sDatePrefixNewer = "";
    555  SimpleDateFormat dateFormatNewer = new SimpleDateFormat("?yy-MM-dd HH:mm:ss");
    556
    557  String sDatePrefixToday = "";
    558  SimpleDateFormat dateFormatToday = new SimpleDateFormat("@ HH:mm:ss");
    559
    560  String sDatePrefixYear = "";
    561  SimpleDateFormat dateFormatYear = new SimpleDateFormat("MMM-dd HH:mm:ss");
    562
    563  String sDatePrefixOlder = "";
    564  SimpleDateFormat dateFormatOlder = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
    565
    566  //final MainCmd_ifc mainCmd;
    567
    568  /**The current shown directory. */
    569  protected FileRemote currentDir;
    570
    571  /**Currently selected file in the table.*/
    572  protected FileRemote currentFile;
    573
    574  String sCurrentDir;
    575
    576  /**Name of the current file.
    577   *
    578   */
    579  //String sCurrentFile;
    580
    581
    582  /**The directory which was used on start. */
    583  FileRemote originDir;
    584
    585
    586
    587  /**This action will be called any time when the selection of a current file is changed. */
    588  protected GralUserAction actionOnFileSelected;
    589
    590
    591  /**This action will be called on pressing enter or mouse-click on a simple file.
    592   */
    593  private GralUserAction actionOnEnterFile;
    594
    595  /**This action will be called on pressing enter or mouse-click on a directory.
    596   * Usual the directory can be entered and showed. But the user can do any other action.
    597   * If this action returns false, the default behavior: enter the directory will be done.
    598   */
    599  private GralUserAction actionOnEnterDirectory;
    600
    601
    602  /**This action will be called on pressing enter or mouse-click on the path text field
    603   * if it contains any text which can't assigned to an existing file.
    604   *
    605   */
    606  private GralUserAction actionOnEnterPathNewFile;
    607
    608
    609
    610
    611  GralUserAction actionSetFileAttribs;
    612
    613
    614  //private GralInfoBox questionWindow;
    615
    616
    617  private enum ERefresh{ doNothing, refreshAll, refreshChildren}
    618
    619  //private final EventSource evSrc = new EventSource("GralFileSelector"){};
    620
    621
    622
    623
    624  /**Set to true if a fillin is pending. */
    625  boolean fillinPending;
    626
    627
    628  /**Creates
    629   * @param posName <code>"@position=name"</code> or <code>"name"</code>, see {@link GralWidget#GralWidget(String, char)} arg1,
    630   * @param rows
    631   * @param columns
    632   * @param size
    633   */
    634  public GralFileSelector(String name, int rows, int[] columns, int[] columnsFavorlist)
    635  { //this.name = name; this.rows = rows; this.columns = columns; this.size = size;
    636    super(null, name, 'f');
    637    favorList = columnsFavorlist !=null ? new GralTable<String>(name, columnsFavorlist) : null;
    638    selectList = new FileSelectList(this, name, rows, columns, 'A');
    639    colorBack = GralColor.getColor("wh");
    640    colorBackPending = GralColor.getColor("pma");
    641    //this.mainCmd = mainCmd;
    642  }
    643
    644
    645  /**Maybe called after construction, should be called before {@link #setToPanel(GralMngBuild_ifc)}
    646   * @param name
    647   */
    648  public void setNameWidget(String name){
    649    //this.name = name;
    650    selectList.wdgdTable.name = name;
    651  }
    652
    653
    654  public void setDateFormat(String sFormat){
    655    dateFormatOlder = new SimpleDateFormat(sFormat);
    656  }
    657
    658
    659  /**Sets the widgets of this instance to a panel.
    660   * The panel and the position in the panel
    661   * should be set before using {@link GralMngBuild_ifc#selectPanel(String)} and
    662   * {@link GralMngBuild_ifc#setPositionInPanel(float, float, float, float, char)}.
    663   * The instance has more as one widget, all widgets are set in the area of the given position.
    664   * The position area should be a range of at least 3 lines.
    665   * @param panelMng The panelManager.
    666   * @param identArgJbat The name of the table widget. The Text-widget for the path gets the name * "-Path".
    667   * @param rows Number of rows to show
    668   * @param columns Array with column width.
    669   * @param size Presentation size. It is a character 'A'..'E', where 'A' is a small size. The size determines
    670   *        the font size especially.
    671   */
    672  public void createImplWidget_Gthread() //GralMngBuild_ifc panelMng)
    673  { GralMng panelMng = GralMng.get();
    674    //The macro widget consists of more as one widget. Position the inner widgets:
    675    GralPos posAll = panelMng.getPositionInPanel();
    676    GralPanelContent panel = posAll.panel;
    677    String sPanel = panel.getName();
    678    //Text field for path above list
    679    panelMng.setPosition(posAll, GralPos.same, GralPos.size + 2.0F, GralPos.same, GralPos.same-6, 1, 'r');
    680    widgdPathDir = panelMng.addTextField(null, true, null, null);
    681    widgdPathDir.setActionChange(actionSetPath);
    682    widgdPathDir.setBackColor(panelMng.getColor("pye"), 0xeeffff);  //color pastel yellow
    683    GralMenu menuFolder = widgdPathDir.getContextMenu();
    684    menuFolder.addMenuItem("x", "refresh [cR]", actionRefreshFileTable);
    685    panelMng.setPosition(GralPos.same, GralPos.same, GralPos.next+0.5f, GralPos.size+5.5f, 1, 'd');
    686    widgBtnFavor = new GralButton(null, "favor", actionFavorButton);
    687    widgBtnFavor.createImplWidget_Gthread();
    688    widgBtnFavor.setVisible(false);
    689    //the list
    690    //on same position as favor table: the file list.
    691    panelMng.setPosition(posAll, GralPos.refer+2, GralPos.same, GralPos.same, GralPos.same, 1, 'd');
    692    selectList.createImplWidget_Gthread();
    693    selectList.wdgdTable.setVisible(true);
    694    selectList.wdgdTable.addContextMenuEntryGthread(1, null, contextMenuTexts.refresh, actionRefreshFileTable);
    695    selectList.wdgdTable.addContextMenuEntryGthread(1, null, contextMenuTexts.refreshCyclicOff, actionSwitchoffCheckRefresh);
    696    selectList.wdgdTable.addContextMenuEntryGthread(1, null, contextMenuTexts.refreshCyclicOn, actionSwitchonCheckRefresh);
    697    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.sortNameCase, actionSortFilePerNameCase);
    698    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.sortNameNonCase, actionSortFilePerNameNonCase);
    699    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.sortExtCase, actionSortFilePerExtensionCase);
    700    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.sortExtNonCase, actionSortFilePerExtensionNonCase);
    701    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.sortDateNewest, actionSortFilePerTimestamp);
    702    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.sortOldest, actionSortFilePerTimestampOldestFirst);
    703    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.showLastModifiedTime, actionShowLastModifiedTime);
    704    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.showLastAccessTime, actionShowLastAccessTime);
    705    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.showCreationTime, actionShowCreationTime);
    706    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.sizeLarge, actionSortFilePerLenghLargestFirst);
    707    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.sortSizeSmall, actionSortFilesPerLenghSmallestFirst);
    708    selectList.wdgdTable.addContextMenuEntryGthread(1, "sort", contextMenuTexts.deselectRecursFiles, actionDeselectDirtree);
    709
    710    //store this in the GralWidgets to get back from widgets later.
    711    widgdPathDir.setContentInfo(this);
    712    selectList.wdgdTable.setContentInfo(this);
    713    selectList.wdgdTable.specifyActionOnLineSelected(actionOnFileSelection);
    714    selectList.wdgdTable.specifyActionOnLineMarked(actionOnMarkLine);
    715
    716    panelMng.setPosition(posAll, GralPos.refer+2, GralPos.same, GralPos.same, 0, 1, 'd');
    717    //on same position as favor table: the file list.
    718    if(favorList !=null) {
    719      favorList.setToPanel(panelMng);
    720      favorList.insertLine(null, 0, new String[]{"test", "path"}, null);
    721      favorList.setVisible(false);
    722    }
    723    //
    724    panelMng.setPosition(5, 0, 10, GralPos.size + 40, 1, 'd');
    725    //questionWindow = GralInfoBox.createTextInfoBox(panelMng, "questionInfoBox", "question");
    726    panelMng.selectPanel(sPanel);  //if finished this panel is selected for like entry.
    727  }
    728
    729
    730  /**Creates the window to confirm search in files. This window can be created only one time
    731   * for all file panels, if the application has more as one. On activating the directory
    732   * and the file panel to show results should be given. But only one search process can be run
    733   * simultaneously.
    734   * @return The created window.
    735   */
    736  public static WindowConfirmSearch createWindowConfirmSearchGthread(GralMngBuild_ifc mng){
    737    WindowConfirmSearch wind = new WindowConfirmSearch();
    738    mng.selectPanel("primaryWindow");
    739    mng.setPosition(-24, 0, -67, 0, 1, 'r'); //right buttom, about half less display width and hight.
    740    wind.windConfirmSearch = mng.createWindow("windConfirmSearch", "search in file tree", GralWindow.windConcurrently);
    741    mng.setPosition(4, GralPos.size -3.5f, 1, -1, 0, 'd', 0.5f);
    742    wind.widgPath = mng.addTextField("path", true, "path", "t");
    743    wind.widgMask = mng.addTextField("mask", true, "search name/mask:", "t");
    744    wind.widgText = mng.addTextField("containsText", true, "contains text:", "t");
    745
    746    mng.setPosition(-5, GralPos.size - 1, 1, -1, 0, 'r',2);
    747    wind.widgProgression = mng.addValueBar(null, null);
    748    mng.setPosition(-1, GralPos.size - 3, 1, GralPos.size + 8, 0, 'r',2);
    749    mng.addButton(null, wind.actionFileSearch, "esc", null, "esc");
    750    wind.widgSubdirs = mng.addSwitchButton(null, null, "subdirs", null, "subdirs", "wh", "gn");
    751    wind.widgSearch = mng.addButton(null, wind.actionFileSearch, "search", null, "search");
    752    wind.widgSearch.setPrimaryWidgetOfPanel();
    753    return wind;
    754  }
    755
    756  /**Sets an action which is called any time when another line is selected.
    757   * @param actionOnLineSelected The action, null to switch off this functionality.
    758   */
    759  public void specifyActionOnFileSelected(GralUserAction actionOnLineSelected){
    760    this.actionOnFileSelected = actionOnLineSelected;
    761  }
    762
    763
    764
    765  public String getCurrentDirPath(){ return sCurrentDir; }
    766
    767  public void setOriginDir(FileRemote dir){ originDir = dir; }
    768
    769
    770  /**Sets the sort order of entries.
    771   * Valid chars for 'sortOrder' are:
    772   * <ul>
    773   * <li>n: sort by name, case sensitive, A..Z a..z in ASCII order
    774   * <li>N: sort by name, non case-sensitive, Aa..Zz, other characters in ASCII order but '_' first.
    775   * <li>x: sort by extension, case sensitive. sort by names with equal extension, case sensitive
    776   * <li>X: sort by extension, non case sensitive. sort by names with equal extension, non case sensitive
    777   * <li>d: sort by timestamp, newest first
    778   * <li>o: sort by timestamp, oldest first.
    779   * <li>l: sort by size, largest first.
    780   * <li>s: sort by size, smallest first.
    781   * </ul>
    782   * @param sortOrder
    783   */
    784  public void setSortOrder(char sortOrder){
    785    this.sortOrder = sortOrder;
    786  }
    787
    788
    789  /**Sets the action which is called if any file is entered. It means the Enter-Key is pressed or
    790   * a mouse double-click is done on a file.
    791   * @param newAction The action to use. The action is invoked with TODO
    792   * @return The current assigned action or null.
    793   */
    794  public GralUserAction setActionOnEnterFile(GralUserAction newAction)
    795  { GralUserAction oldAction = actionOnEnterFile;
    796    actionOnEnterFile = newAction;
    797    return oldAction;
    798  }
    799
    800
    801
    802  /**This action will be called on pressing enter or mouse-click on a directory.
    803   * Usual the directory can be entered and showed. But the user can do any other action.
    804   * If this action returns false, the default behavior: enter the directory will be done.
    805   */
    806  public GralUserAction setActionOnEnterDirectory(GralUserAction newAction)
    807  { GralUserAction oldAction = actionOnEnterDirectory;
    808    actionOnEnterDirectory = newAction;
    809    return oldAction;
    810  }
    811
    812
    813  /**This action will be called on pressing enter or mouse-click on the path text field
    814   * if it contains any text which can't assigned to an existing file.
    815   *
    816   */
    817  public GralUserAction setActionOnEnterPathNewFile(GralUserAction newAction)
    818  { GralUserAction oldAction = actionOnEnterPathNewFile;
    819    actionOnEnterPathNewFile = newAction;
    820    return oldAction;
    821  }
    822
    823
    824
    825
    826  /**Sets the action which is called if any file is set to the table.
    827   * @param newAction The action to use. The action is invoked with TODO
    828   * @return The current assigned action or null.
    829   */
    830  public GralUserAction setActionSetFileLineAttrib(GralUserAction newAction)
    831  { GralUserAction oldAction = actionSetFileAttribs;
    832    actionSetFileAttribs = newAction;
    833    return oldAction;
    834  }
    835
    836
    837  /**Fills the content with the first directory or the directory which was set with
    838   * {@link #setOriginDir(File)}.
    839   */
    840  public void fillInOriginDir()
    841  {
    842    fillIn(originDir, false);
    843  }
    844
    845
    846
    847  /**It is the refresh operation.
    848   * If {@link #fillinPending} is set yet this operation does nothing. It means it is possible to call in a short cycle
    849   * more as one time after another, for example for all files of a directory without calculation effort.
    850   */
    851  public void fillInCurrentDir(){
    852    if(currentDir !=null && !fillinPending) {
    853      //assume that a yet tested directory should not refreshed twice because another thread had refreshed already.
    854      //yet is 2 seconds.
    855      boolean bDonotRefresh = currentDir.isTested(System.currentTimeMillis() - 2000);
    856      fillIn(currentDir, bDonotRefresh);
    857    }
    858  }
    859
    860
    861
    862  private void fillIn(List<File> files) {
    863
    864    selectList.wdgdTable.clearTable();
    865    idxLines.clear();
    866    for(File file1: files ){
    867      FileCluster fc = currentDir.itsCluster;
    868      FileRemote file = fc.getFile(FileSystem.normalizePath(file1.getAbsolutePath()), null);
    869      String name = file.getName();
    870      String path = file.getCanonicalPath();
    871      GralTableLine_ifc<FileRemote> tline = selectList.wdgdTable.insertLine(path, 0, null, file);
    872      tline.setCellText("?", kColDesignation);
    873      tline.setCellText(path, kColFilename);
    874      tline.setCellText(""+file.length(), kColLength);
    875      long timeNow = System.currentTimeMillis();
    876      long fileTime;
    877      switch(showTime){
    878        case 'm': fileTime = file.lastModified(); break;
    879        case 'c': fileTime = file.creationTime(); break;
    880        case 'a': fileTime = file.lastAccessTime(); break;
    881        default: fileTime = -1; //error
    882      }
    883
    884      long diffTime = timeNow - fileTime;
    885      Date timestamp = new Date(fileTime);
    886      String sDate;
    887      if(diffTime < -10 * 3600000L){
    888        sDate = sDatePrefixNewer + dateFormatNewer.format(timestamp);
    889      } else if(diffTime < 18*3600000){
    890        //files today
    891        sDate = sDatePrefixToday + dateFormatToday.format(timestamp);
    892      } else if(diffTime < 320 * 24* 3600000L){
    893        sDate = sDatePrefixYear + dateFormatYear.format(timestamp);
    894      } else {
    895        sDate = sDatePrefixOlder + dateFormatOlder.format(timestamp);
    896      }
    897      tline.setCellText(sDate, kColDate);
    898      tline.setBackColor(colorBack, -1);
    899      idxLines.put(path, tline);
    900    }
    901  }
    902
    903
    904
    905
    906  public void forcefillIn(FileRemote fileIn, boolean bCompleteWithFileInfo) //String path)
    907  {
    908    fillinPending = false;
    909    fillIn(fileIn, bCompleteWithFileInfo);
    910  }
    911
    912  /**Fills the content with given directory.
    913   * If the same directory was refreshed in a short time before, it is not refreshed here.
    914   * That is while fast navigation in a tree.
    915   * @param fileIn The directory which's files are shown.
    916   * @param bDonotRefrehs false then invoke an extra thread to walk through the file system,
    917   *   see @{@link FileRemote#refreshPropertiesAndChildren(FileRemoteCallback)} and {@link #callbackChildren1}.
    918   *   If true then it is presumed that the FileRemote children are refreshed in the last time already.
    919   *   The fill the table newly with given content in this thread.
    920   */
    921  public void fillIn(FileRemote fileIn, boolean bDonotRefrehs) //String path)
    922  { long timenow = System.currentTimeMillis();
    923    timeFillinInvoked = timenow;
    924    final FileRemote dir, file;
    925    if(!fileIn.isDirectory()){
    926      dir = fileIn.getParentFile(); file = fileIn;
    927      String sDir = FileSystem.getCanonicalPath(dir); //with / as separator!
    928      String sFile = fileIn.getName();
    929      indexSelection.put(sDir, fileIn); //sFile);
    930    } else {
    931      dir = fileIn; file = null;
    932    }
    933    if(originDir == null){
    934      originDir = dir;    //should exist in any case.
    935    }
    936
    937    fileIn.internalAccess().setRefreshed();
    938    boolean bSameDirectory = dir == currentDir;
    939    if(!bSameDirectory || !fillinPending){  //new request anytime if other directory, or if it is not pending.
    940      fillinPending = true;
    941      //selectList.wdgdTable.setBackColor(colorBackPending, -1);  //for all cells.
    942      if(!bSameDirectory){
    943        currentDir = dir;
    944        this.sCurrentDir = dir.getAbsolutePath();  //though it may not exist, store it for refresh (may be exist later).
    945      }
    946      System.out.println("FcmdFileCard - start fillin; " + sCurrentDir + (bSameDirectory ? "; same" : "; new"));
    947      final ERefresh eRefresh;
    948      if(bSameDirectory){
    949        //it is a refresh.
    950        if(true || (timenow - dir.timeChildren) > 4000){
    951          eRefresh = ERefresh.refreshAll; //needs to refresh
    952        } else {
    953          eRefresh = ERefresh.doNothing;  //do nothing, it is actual
    954        }
    955      } else {
    956        //other directory
    957        currentFile = null;
    958              //the directory is unknown yet.
    959          //GralTableLine_ifc<FileRemote> tline = selectList.wdgdTable.insertLine(null, 0, null, null);
    960          //tline.setCellText("--waiting--", kColFilename);
    961          eRefresh = ERefresh.refreshChildren;  //do nothing, it is shown and refreshed in execFillIn
    962      }
    963      if(!bSameDirectory || sortOrder != sortOrderLast){
    964        selectList.wdgdTable.clearTable();
    965        idxLines.clear();
    966        if(dir.getParentFile() !=null){
    967          GralTableLine_ifc<FileRemote> tline = selectList.wdgdTable.insertLine("..", 0, null, dir);
    968          tline.setCellText("<", kColDesignation);
    969          tline.setCellText("..", kColFilename);
    970          tline.setCellText("", kColLength);
    971          tline.setCellText("", kColDate);
    972          tline.setBackColor(colorBack, -1);
    973          idxLines.put("..", tline);
    974        }
    975        //Build the table lines newly.
    976      } else {
    977        for(GralTable<?>.TableLineData line: selectList.wdgdTable.iterLines()){
    978          //if(!line.getCellText(kColFilename).equals("..")){
    979            line.setBackColor(colorBackPending, -1);
    980          //}
    981        }
    982      }
    983      widgdPathDir.setText(sCurrentDir, -1);
    984      sortOrderLast = sortOrder;
    985      ////
    986      if(bDonotRefrehs) {
    987        //do not refresh, show given files.
    988        Map<String, FileRemote> files = dir.children();
    989        if(files !=null) {
    990          for(Map.Entry<String,FileRemote> entry: files.entrySet()) {
    991            FileRemote file1 = entry.getValue();
    992            showFile(file1);
    993          }
    994        }
    995        finishShowFileTable();   //removed lines with not existing files,
    996        ////
    997      } else {
    998        //refresh it in an extra thread therefore show all lines with colorBackPending.
    999        //Remove lines which remains the colorBackPending after refreshing.
    1000        dir.refreshPropertiesAndChildren(callbackChildren1, false);
    1001      }
    1002    }
    1003  }
    1004
    1005
    1006
    1007  /**This routine is invoked in callback of {@link #callbackChildren1} for {@link #fillIn(FileRemote, boolean)} in the refresh thread.
    1008   * @param file1
    1009   */
    1010  void showFile(FileRemote file1)
    1011  {
    1012    String key = buildKey(file1, true, null);
    1013    boolean[] found = new boolean[1];
    1014    GralTableLine_ifc<FileRemote> tline = idxLines.search(key, false, found);
    1015
    1016    if(!found[0]){ //no such line with this file
    1017      String name = file1.getName();  //use the file name as key in the table for the table line.
    1018      if(tline ==null){
    1019        //on empty table, first line.
    1020        tline = selectList.wdgdTable.insertLine(name, 0, null, file1);
    1021      }else {
    1022        //insert after found line.
    1023        tline = tline.addNextLine(name, null, file1);
    1024      }
    1025      tline.setCellText(file1.getName(), kColFilename);
    1026      idxLines.add(key, tline);
    1027    }
    1028    completeLine(tline, file1, System.currentTimeMillis());
    1029    tline.setBackColor(colorBack, -1); //set for the whole line.
    1030  }
    1031
    1032
    1033
    1034  /**Finishes a newly showed file table.
    1035   * Removes all lines which have the {@link #colorBackPending} yet, they are not refreshed because that files don't exist furthermore.
    1036   * Gets the {@link #currentFile()} of this table from the {@link #indexSelection} if the {@link #currentFile} is null,
    1037   * sets the current line and repaint the table.
    1038   */
    1039  void finishShowFileTable()
    1040  {
    1041    System.out.println("FcmdFileCard - finish fillin; " + sCurrentDir);
    1042    Iterator<Map.Entry<String, GralTableLine_ifc<FileRemote>>> iter = idxLines.entrySet().iterator();
    1043    while(iter.hasNext()){
    1044      Map.Entry<String, GralTableLine_ifc<FileRemote>> entry = iter.next();
    1045      GralTableLine_ifc<FileRemote> tline = entry.getValue();
    1046      if(tline.getKey().equals("..")){
    1047        tline.setBackColor(colorBack, -1); //set for the whole line.
    1048      } else if(tline.getBackColor(-1) == colorBackPending){
    1049        selectList.wdgdTable.deleteLine(tline);  //it is a non existing one yet.
    1050        iter.remove();
    1051      }
    1052    }
    1053    selectList.wdgdTable.setBackColor(colorBack, GralTable_ifc.kEmptyArea);
    1054    if(currentFile == null){
    1055      currentFile = indexSelection.get(sCurrentDir);
    1056    }
    1057    GralTableLine_ifc<FileRemote> tline;
    1058    if(currentFile !=null){
    1059      String key = buildKey(currentFile, true, null);
    1060      tline = idxLines.search(key); //maybe line before
    1061    } else {
    1062      tline = null;  //first line is selected
    1063    }
    1064    if(tline !=null){
    1065      selectList.wdgdTable.setCurrentLine(tline, -3, 1);
    1066      currentFile = tline.getUserData();  //adjust the file if the currentFile was not found exactly.
    1067    }
    1068    selectList.wdgdTable.repaint(100, 200);
    1069    fillinPending = false;
    1070
    1071  }
    1072
    1073
    1074
    1075
    1076  @SuppressWarnings("boxing")
    1077  private void completeLine(GralTableLine_ifc<FileRemote> tline, FileRemote file, long timeNow){
    1078    final String sDesign, sDir;
    1079    int mark = file.mark ==null ? 0 : file.mark.getMark();
    1080    if(file.isSymbolicLink()){
    1081      sDir =  file.isDirectory() ? ">" : "s";
    1082    }
    1083    else if(file.isDirectory()){
    1084      sDir = "/";
    1085    } else {
    1086      sDir = "";
    1087    }
    1088    if(mark != 0){
    1089      if((mark & FileMark.cmpFileDifferences ) !=0){ sDesign = "#"; }
    1090      else if((mark & FileMark.cmpMissingFiles ) !=0){ sDesign = "*"; }  //directory contains more files
    1091      else if((mark & FileMark.cmpAlone ) !=0){ sDesign = "+"; }
    1092      else if((mark & FileMark.mCmpFile) !=0){
    1093        switch(mark & FileMark.mCmpFile){
    1094          case FileMark.cmpContentEqual: sDesign = " ";break;
    1095          case FileMark.cmpContentNotEqual: sDesign = "#";break;
    1096          default: sDesign = " ";
    1097        }
    1098      } else {
    1099        sDesign = " ";
    1100      }
    1101    }
    1102    else { sDesign = " ";}
    1103    tline.setCellText(sDir + sDesign, kColDesignation);
    1104    long fileTime;
    1105    switch(showTime){
    1106      case 'm': fileTime = file.lastModified(); break;
    1107      case 'c': fileTime = file.creationTime(); break;
    1108      case 'a': fileTime = file.lastAccessTime(); break;
    1109      default: fileTime = -1; //error
    1110    }
    1111
    1112    long diffTime = timeNow - fileTime;
    1113    Date timestamp = new Date(fileTime);
    1114    String sDate;
    1115    if(diffTime < -10 * 3600000L){
    1116      sDate = sDatePrefixNewer + dateFormatNewer.format(timestamp);
    1117    } else if(diffTime < 18*3600000){
    1118      //files today
    1119      sDate = sDatePrefixToday + dateFormatToday.format(timestamp);
    1120    } else if(diffTime < 320 * 24* 3600000L){
    1121      sDate = sDatePrefixYear + dateFormatYear.format(timestamp);
    1122    } else {
    1123      sDate = sDatePrefixOlder + dateFormatOlder.format(timestamp);
    1124    }
    1125    tline.setCellText(sDate, kColDate);
    1126    //line[kColDate] = sDate;
    1127    //
    1128    String sLength;
    1129    long fileLength = file.length();
    1130    if(fileLength < 1024){
    1131      sLength = "" + fileLength;
    1132    } else if(fileLength < 10000){
    1133      sLength = String.format("%1.1f k", fileLength / 1024.0f);
    1134    } else if(fileLength < 1000000){
    1135      sLength = String.format("%3.0f k", fileLength / 1024.0f);
    1136    } else if(fileLength < 10000000){
    1137      sLength = String.format("%1.1f M", fileLength / (1024 * 1024.0f));
    1138    } else if(fileLength < 1000000000){
    1139      sLength = String.format("%3.0f M", fileLength / (1024 * 1024.0f));
    1140    } else if(fileLength < 10000000000L){
    1141      sLength = String.format("%1.1f G", fileLength / (1024 * 1024.0f));
    1142    } else {
    1143      sLength = String.format("%2.0f G", fileLength / (1024 * 1024.0f));
    1144    }
    1145    tline.setCellText(sLength, kColLength);
    1146    tline.setContentIdent(fileTime);
    1147    //line[kColLength] = sLength;
    1148
    1149  }
    1150
    1151
    1152  ////
    1153  protected String buildKey(FileRemote file, boolean bAllCompleteWithFileInfo, String[] retSortName){
    1154    String sort, sortName;
    1155    if(!file.isTested()){
    1156      bAllCompleteWithFileInfo = false;
    1157    }
    1158    switch(sortOrder){
    1159    case kSortName: {
    1160      String sName = file.getName();
    1161      if(file.isDirectory()){ sName += "/"; }
    1162      sortName = sort = (file.isDirectory()? "D" : "F") + sName;
    1163    } break;
    1164    case kSortNameNonCase: {
    1165      String sName = file.getName().toLowerCase();
    1166      if(file.isDirectory()){ sName += "/"; }
    1167      sortName = sort = (file.isDirectory()? "D" : "F") + sName;
    1168    } break;
    1169    case kSortExtension: {
    1170      String sName = file.getName();
    1171      int posDot = sName.lastIndexOf('.');
    1172      String sExt = sName.substring(posDot+1);
    1173      if(file.isDirectory()){ sName += "/"; }
    1174      sortName = sort = (file.isDirectory()? "D" : "F") + sExt + sName;
    1175    } break;
    1176    case kSortExtensionNonCase: {
    1177      String sName = file.getName().toLowerCase();
    1178      int posDot = sName.lastIndexOf('.');
    1179      String sExt = sName.substring(posDot+1);
    1180      if(file.isDirectory()){ sName += "/"; }
    1181      sortName = sort = (file.isDirectory()? "D" : "F") + sExt + sName;
    1182    } break;
    1183    case kSortDateNewest: {
    1184      String sName = file.getName().toLowerCase();
    1185      if(bAllCompleteWithFileInfo){
    1186        long nDate = -file.lastModified();
    1187        String sDate = String.format("%016X", new Long(nDate));
    1188        sort = (file.isDirectory()? "D" : "F") + sDate + sName;
    1189      } else { sort = ""; }
    1190      sortName = (file.isDirectory()? "D" : "F") + sName;
    1191    } break;
    1192    case kSortDateOldest: {
    1193      String sName = file.getName().toLowerCase();
    1194      if(bAllCompleteWithFileInfo){
    1195        long nDate = file.lastModified();
    1196        String sDate = String.format("%016X", new Long(nDate));
    1197        sort = (file.isDirectory()? "D" : "F") + sDate + sName;
    1198      } else { sort = ""; }
    1199      sortName = (file.isDirectory()? "D" : "F") + sName;
    1200    } break;
    1201    case kSortSizeLargest: {
    1202      String sName = file.getName().toLowerCase();
    1203      if(bAllCompleteWithFileInfo){
    1204        long nSize = 0x7fffffffffffffffL - file.length();
    1205        String sSize = String.format("%016d", new Long(nSize));
    1206        sort = (file.isDirectory()? "D" : "F") + sSize + sName;
    1207      } else { sort = ""; }
    1208      sortName = (file.isDirectory()? "D" : "F") + sName;
    1209    } break;
    1210    case kSortSizeSmallest: {
    1211      String sName = file.getName().toLowerCase();
    1212      if(bAllCompleteWithFileInfo){
    1213        long nSize = file.length();
    1214        String sSize = String.format("%016d", new Long(nSize));
    1215        sort = (file.isDirectory()? "D" : "F") + sSize + sName;
    1216      } else { sort = ""; }
    1217      sortName = (file.isDirectory()? "D" : "F") + sName;
    1218    } break;
    1219    default: { sortName = sort = file.getName(); }
    1220    }
    1221    if(retSortName !=null){ retSortName[0] = sortName; }
    1222    return sort;
    1223  }
    1224
    1225
    1226
    1227
    1228  public void checkRefresh(long since){
    1229    if(currentDir !=null
    1230      && (  !donotCheckRefresh && !currentDir.isTested(since - 5000)
    1231         || currentDir.shouldRefresh()
    1232       )   ){
    1233      fillIn(currentDir, false);
    1234    }
    1235  }
    1236
    1237
    1238  //public FileRemote getCurrentDir(){ return currentDir; }
    1239
    1240
    1241  /**Gets the selected file from this panel.
    1242   * @return null if no line is selected, for example if the panel isn't used yet.
    1243   */
    1244  public File XXXgetSelectedFile()
    1245  {
    1246    if(selectList.wdgdTable == null){
    1247      stop();
    1248      return null;
    1249    }
    1250    GralTableLine_ifc<FileRemote> line = selectList.wdgdTable.getCurrentLine();
    1251    if(line !=null){
    1252      File data = line.getUserData();
    1253      return data;
    1254    } else {
    1255      return null;
    1256    }
    1257  }
    1258
    1259
    1260
    1261
    1262  /**Gets the selected file from this panel.
    1263   * If the current file of this panel is marked, then all other marked files and directories
    1264   * of this panel are returned too. If the current file is not marked, then only this current file
    1265   * is returned as selected file.
    1266   * <br><br>
    1267   * Strategy changed since 2013-09-05: Before, the marked files are returned. But it is possible
    1268   * that some files are marked outside of the visible area, and the user does not know that there are
    1269   * marked files.
    1270   * In this case an unexpected behavior from the user's view occurs.
    1271   * If the user selects a marked file, then one should be sure that there are other marked files
    1272   * in non visible areas too.
    1273   *
    1274   * @param bAlsoDirs false then returns never a directory. If the current selected file is a directory
    1275   *   then return null.
    1276   * @return null if no line is selected, for example if the panel isn't used yet.
    1277   *   If the current file is non-marked, then returns a list of only 1 element, that current file.
    1278   *   If the current file is marked, return all marked files.
    1279   */
    1280  public List<FileRemote> getSelectedFiles(boolean bAlsoDirs, int mask)
    1281  { if(selectList.wdgdTable == null){
    1282      stop();
    1283      return null;
    1284    } else if(currentFile == null){
    1285      return null;
    1286    } else if(currentFile.isMarked(0x1)){
    1287      List<FileRemote> list = new LinkedList<FileRemote>();
    1288      for(GralTableLine_ifc<FileRemote> line: selectList.wdgdTable.getMarkedLines(mask)){
    1289        FileRemote file = line.getUserData();
    1290        if(bAlsoDirs || !file.isDirectory()){
    1291          list.add(file);
    1292        }
    1293      }
    1294      return list;
    1295    } else if(bAlsoDirs || !currentFile.isDirectory()) {
    1296      List<FileRemote> list = new LinkedList<FileRemote>();
    1297      list.add(currentFile);
    1298      return list;
    1299    } else { //no mark file selected, current file is a directory.
    1300      return null;
    1301    }
    1302  }
    1303
    1304
    1305  /**Selects the file with the given name in the table
    1306   * @param name name of file like it is shown in the table (given as key).
    1307   * @return true if found and selected.
    1308   */
    1309  public boolean selectFile(String name){
    1310    return selectList.wdgdTable.setCurrentLine(name);
    1311
    1312  }
    1313
    1314
    1315  /**Gets the current selected file.
    1316   * Note: If the .. is selected, the current file is the parent directory.
    1317   * If any directory is selected, this is the currentFile(). Check with {@link java.io.File#isDirectory()}.
    1318   */
    1319  public FileRemote currentFile(){ return currentFile; }
    1320
    1321  /**Gets the directory which is currently shown.
    1322   * Note: If the .. is selected, the current directory is the directory where the .. is located,
    1323   * whereby the {@link #currentFile()} is the parent. Elsewhere this method returns the parent of
    1324   * {@link #currentFile()}.
    1325   */
    1326  public FileRemote currentDir(){ return currentDir; }
    1327
    1328  /**same as {@link #currentDir()}. */
    1329  public FileRemote getCurrentDir(){ return currentDir; }
    1330
    1331
    1332
    1333  /**Sets the focus of the associated table widget.
    1334   * @return true if focused.
    1335   */
    1336  public void setFocus(){ selectList.wdgdTable.setFocus(); }
    1337
    1338
    1339  @Override public boolean setVisible(boolean visible)
    1340  {
    1341    selectList.wdgdTable.setVisible(visible);
    1342    widgdPathDir.setVisible(visible);
    1343    widgBtnFavor.setVisible(visible);
    1344    return bVisibleState;
    1345  }
    1346
    1347  void stop(){}
    1348
    1349  @Override public boolean remove(){
    1350    selectList.remove();
    1351    widgdPathDir.remove();
    1352    indexSelection.clear();
    1353    currentDir = null;
    1354    return true;
    1355  }
    1356
    1357
    1358
    1359  /**This method is called on any user key or mouse event while operating in the file table.
    1360   * It should be overwritten by a derived class. This routine is empty.
    1361   * @param key code or mouse code, one of constants from {@link KeyCode}.
    1362   * @param userDataOfLine The user data stored in the line of table.
    1363   * @param line The table line.
    1364   * @return true if is was relevant for the key.
    1365   */
    1366  public boolean actionUserKey(int keyCode, Object userDataOfLine, GralTableLine_ifc<FileRemote> line)
    1367  {
    1368    return false;
    1369  }
    1370
    1371
    1372
    1373
    1374  public FileRemoteCallback callbackChildren1 = new FileRemoteCallback()
    1375  {
    1376
    1377    @Override public void start(FileRemote startDir){}
    1378
    1379    @Override public Result offerParentNode(FileRemote file) {
    1380      return Result.cont;
    1381    }
    1382
    1383    @Override public Result finishedParentNode(FileRemote file, FileRemoteCallback.Counters cnt) {
    1384      //don't call showFile(file) because it is the parent which should be shown.
    1385      return Result.cont;
    1386    }
    1387
    1388    @Override public Result offerLeafNode(FileRemote file, Object info)
    1389    { showFile(file);  //invoked also for dirs because depth=1
    1390      return Result.cont;
    1391    }
    1392
    1393    @Override public void finished(FileRemote startDir, SortedTreeWalkerCallback.Counters cnt)
    1394    {
    1395      finishShowFileTable();
    1396    }
    1397
    1398    @Override public boolean shouldAborted()
    1399    { return false;
    1400    }
    1401  };
    1402
    1403
    1404
    1405
    1406
    1407
    1408
    1409  /**Action on [Enter]-key on the path text field.
    1410   * <ul>
    1411   * <li>If the text represents an existing directory, it is used as current.
    1412   * <li>If the text represents an existing file, the parent directory is used as current
    1413   *   and the file is stored as current in this directory, see {@link #indexSelection}.
    1414   * <li>If the path is absolute (starting with '/' or '\\' maybe with leading drive letter for windows
    1415   *   then it is used absolute. If the path is not absolute, it is used starting from the current one.
    1416   * <li>If the path contains a name only, it is a file. You can
    1417   * <li>If the text represents a file which is not existing, but its directory path is existing,
    1418   *   a quest is posted whether the file should be created. On [OK] either the file will be
    1419   *   created as new file or its path is returned.
    1420   * </ul>
    1421   */
    1422  GralUserAction actionSetPath = new GralUserAction(){
    1423    @Override
    1424    public boolean userActionGui(int key, GralWidget widg, Object... params)
    1425    {
    1426      if(key == KeyCode.enter){
    1427        String sPath = widg.getValue();
    1428        int posWildcard = sPath.indexOf('*');
    1429        if(posWildcard >=0){
    1430
    1431        } else {
    1432          FileRemote file = originDir.itsCluster.getDir(sPath);
    1433          //file.refreshProperties(null);
    1434          if(file.isDirectory()){
    1435            fillIn(file, false);
    1436          } else if(file.isFile()){
    1437            FileRemote dir = file.getParentFile();
    1438            String sDir = FileSystem.getCanonicalPath(dir);
    1439            String sFile = file.getName();
    1440            indexSelection.put(sDir, file);
    1441            fillIn(dir, false);
    1442          } else {
    1443            File parent = file.getParentFile();
    1444            if(parent !=null && parent.exists()){
    1445              if(actionOnEnterPathNewFile !=null){
    1446                actionOnEnterPathNewFile.userActionGui(KeyCode.enter, widgdPathDir, file);
    1447              } else {
    1448                String question = "Do you want to create file\n"
    1449                  +file.getName()
    1450                  + "\n  in directory\n"
    1451                  + parent.getPath();
    1452//                questionWindow.setText(question);
    1453//                questionWindow.setActionOk(confirmCreate);
    1454//                questionWindow.setFocus(); //setWindowVisible(true);
    1455              }
    1456            } else {
    1457//              questionWindow.setText("unknown path");
    1458//              questionWindow.setActionOk(null);
    1459//              questionWindow.setFocus(); //setWindowVisible(true);
    1460            }
    1461          }
    1462        }
    1463        //widg.getMng().widgetHelper.showContextMenu(widg);
    1464      }
    1465
    1466      stop();
    1467      return false;
    1468    }
    1469  };
    1470
    1471
    1472  public void setSortOrderFiles(char order){
    1473    setSortOrder(order);
    1474    fillInCurrentDir();
    1475  }
    1476
    1477
    1478
    1479  GralUserAction actionSortFilePerNameCase = new GralUserAction("actionSortFilePerNameCase")
    1480  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1481      setSortOrderFiles(GralFileSelector.kSortName);
    1482      return true;
    1483  } };
    1484
    1485
    1486  GralUserAction actionSortFilePerNameNonCase = new GralUserAction("actionSortFilePerNameNonCase")
    1487  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1488      setSortOrderFiles(GralFileSelector.kSortNameNonCase);
    1489      return true;
    1490  } };
    1491
    1492
    1493  GralUserAction actionSortFilePerExtensionCase = new GralUserAction("actionSortFilePerExtensionCase")
    1494  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1495      setSortOrderFiles(GralFileSelector.kSortExtension);
    1496      return true;
    1497  } };
    1498
    1499  GralUserAction actionSortFilePerExtensionNonCase = new GralUserAction("actionSortFilePerExtensionNonCase")
    1500  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1501      setSortOrderFiles(GralFileSelector.kSortExtensionNonCase);
    1502      return true;
    1503  } };
    1504
    1505  GralUserAction actionSortFilePerTimestamp = new GralUserAction("actionSortFilePerTimestamp")
    1506  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1507      setSortOrderFiles(GralFileSelector.kSortDateNewest);
    1508      return true;
    1509  } };
    1510
    1511  GralUserAction actionSortFilePerTimestampOldestFirst = new GralUserAction("actionSortFilePerTimestampOldestFirst")
    1512  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1513      setSortOrderFiles(GralFileSelector.kSortDateOldest);
    1514      return true;
    1515  } };
    1516
    1517  GralUserAction actionShowLastModifiedTime = new GralUserAction("actionSortFilePerTimestamp")
    1518  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1519      showTime = 'm';
    1520      return true;
    1521  } };
    1522
    1523  GralUserAction actionShowLastAccessTime = new GralUserAction("actionSortFilePerTimestamp")
    1524  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1525      showTime = 'a';
    1526      return true;
    1527  } };
    1528
    1529  GralUserAction actionShowCreationTime = new GralUserAction("actionSortFilePerTimestamp")
    1530  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1531      showTime = 'c';
    1532      return true;
    1533  } };
    1534
    1535  GralUserAction actionSortFilePerLenghLargestFirst = new GralUserAction("actionSortFilePerLenghLargestFirst")
    1536  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1537      setSortOrderFiles(GralFileSelector.kSortSizeLargest);
    1538      return true;
    1539  } };
    1540
    1541  GralUserAction actionSortFilesPerLenghSmallestFirst = new GralUserAction("actionSortFilesPerLenghSmallestFirst")
    1542  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1543      setSortOrderFiles(GralFileSelector.kSortSizeSmallest);
    1544      return true;
    1545  } };
    1546
    1547
    1548  GralUserAction actionDeselectDirtree = new GralUserAction("actionDeselectDirtree")
    1549  { @Override public boolean exec(int key, GralWidget_ifc widgi, Object... params) {
    1550    //if(fileCard !=null){
    1551    if(currentFile !=null){
    1552      Thread run1 = new Thread("actionDeselectDirtree") {
    1553        @Override public void run() {
    1554          currentFile.resetMarkedRecurs(0xffffffff, null);
    1555          currentFile.setDirShouldRefresh();
    1556      } };
    1557      run1.start();
    1558    }
    1559    return true;
    1560  } };
    1561
    1562
    1563
    1564
    1565
    1566  /**Sets the origin dir of the last focused file table.
    1567   * <br>
    1568   * Implementation note: The last focused file tab is searched using {@link Fcmd#getLastSelectedFileCards()}.
    1569   */
    1570  GralUserAction actionRefreshFileTable = new GralUserAction(){
    1571    @Override public boolean userActionGui(int key, GralWidget widgd, Object... params){
    1572      if(KeyCode.isControlFunctionMouseUpOrMenu(key)){  //supress both mouse up and down reaction
    1573        fillInCurrentDir();
    1574        return true;
    1575      } else return false;
    1576    }
    1577  };
    1578
    1579  /**Sets the check refresh mode to off.
    1580   * <br>
    1581   */
    1582  GralUserAction actionSwitchoffCheckRefresh = new GralUserAction("actionSwitchoffCheckRefresh"){
    1583    @Override public boolean userActionGui(int key, GralWidget widgd, Object... params){
    1584      if(KeyCode.isControlFunctionMouseUpOrMenu(key)){  //supress both mouse up and down reaction
    1585        donotCheckRefresh = true;
    1586        return true;
    1587      } else return false;
    1588    }
    1589  };
    1590
    1591  /**Sets the check refresh mode to on.
    1592   * <br>
    1593   */
    1594  GralUserAction actionSwitchonCheckRefresh = new GralUserAction("actionSwitchoffCheckRefresh"){
    1595    @Override public boolean userActionGui(int key, GralWidget widgd, Object... params){
    1596      if(KeyCode.isControlFunctionMouseUpOrMenu(key)){  //supress both mouse up and down reaction
    1597        donotCheckRefresh = false;
    1598        return true;
    1599      } else return false;
    1600    }
    1601  };
    1602
    1603  GralUserAction confirmCreate = new GralUserAction()
    1604  {
    1605    @Override public boolean userActionGui(int key, GralWidget widgd, Object... params){
    1606      return true;
    1607    }
    1608  };
    1609
    1610
    1611
    1612
    1613  GralUserAction actionFavorButton = new GralUserAction("actionFavorButton"){
    1614    @Override public boolean exec(int key, GralWidget_ifc widgd, Object... params){
    1615      if(KeyCode.isControlFunctionMouseUpOrMenu(key)){  //supress both mouse up and down reaction
    1616        if(selectList.wdgdTable.isVisible()){
    1617          selectList.wdgdTable.setVisible(false);
    1618          favorList.setVisible(true);
    1619        } else {
    1620          selectList.wdgdTable.setVisible(true);
    1621          favorList.setVisible(false);
    1622        }
    1623        return true;
    1624      } else return false;
    1625    }
    1626  };
    1627
    1628
    1629
    1630  /**This class is instantiated static and contains English menu texts. The user can change it
    1631   * touching the public static instance {@link GralFileSelector#contextMenuTexts}
    1632   * before calling {@link GralFileSelector#setToPanel(GralMngBuild_ifc, String, int, int[], char)}.
    1633   */
    1634  public static class MenuTexts{
    1635    public String refresh = "&Refresh [F5]";
    1636    public String sortNameCase = "&Sort/&Name case sensit";
    1637    public String sortNameNonCase = "&Sort/&Name non-case";
    1638    public String sortExtCase = "&Sort/e&Xt case sensit";
    1639    public String sortExtNonCase = "&Sort/e&Xt non-case";
    1640    public String sortOldest = "&Sort/date &Oldest";
    1641    public String sortDateNewest = "&Sort/&Date newest";
    1642    public String sizeLarge = "&Sort/size &Largest";
    1643    public String sortSizeSmall = "&Sort/size &Smallest";
    1644    public String deselectRecursFiles = "actionDeselectDirtree";
    1645    public String refreshCyclicOff = "Cyclic refresh/o&ff";
    1646    public String refreshCyclicOn = "Cyclic refresh/&on";
    1647    public String showLastAccessTime = "show date last &Access";
    1648    public String showLastModifiedTime = "show date last &Modified";
    1649    public String showCreationTime = "show date &Creation";
    1650  }
    1651
    1652
    1653}