package nxm.ucl.libg;

import java.awt.event.ActionListener;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;

import javax.swing.ListModel;

import nxm.sys.lib.Midas;
import nxm.sys.lib.Parser;
import nxm.sys.lib.Table;
import nxm.sys.libg.MJPopup.HasValue;

/**
  A class to implement a graphical file selection widget for the AUX list using swing.

  NOTE: MJFileChooser does NOT support Java Serialization and will be removed soon

  @author Neon Ngo
  @since NeXtMidas 2.5.0
  @deprecated Since NeXtMidas 3.3.1, this was meant for Internal use only. Use POPUP FUNC=FILE
*/
@SuppressWarnings({"rawtypes","unchecked"}) // <-- NOTE: JDK7 has some unchecked & rawtypes Xlint warnings that CANNOT be fixed since we still support JDK6
@Deprecated // TODO: remove in 4.1.2
public class MJFileChooser extends javax.swing.JDialog implements ActionListener, HasValue {
  /** NOTE: this should be changed if the structure/fields of this class changes.
   *  This is being left to avoid warnings, though this class is not truly serializable.
   *  We are not overwriting readObject and writeObject because serialization will error out on javax.swing.GroupLayout */
  private static final long serialVersionUID = 2012122800331L; // for Serializable interface

  /** File chooser options */
  public static final String optionList = "Modal,NoExt";
  private static final int MODAL = 1; // modal (blocking parent Frame/Window) dialog
  private static final int NOEXT = 2; // strip extension from filename (e.g. apenny)

  /** File chooser filename path options.
  <pre>
    FullPath  - Display full path to file (e.g. /home/user/nxm/sys/dat/apenny.prm)
    NoPath    - Display filename without path (e.g. apenny.prm)
    AuxPath   - Display {@code <aux>/<filename>} (e.g. DAT/apenny.prm)
    AuxQual   - Display {@code <filename>}{AUX=&lt;aux&gt;} (e.g. apenny.prm{AUX=DAT})
  </pre>
   */
  public static final String pathOptionList = "FullPath,NoPath,AuxPath,AuxQual";
  private static final int FULLPATH = 1; // full path to file (e.g. /home/user/nxm/sys/dat/apenny.prm)
  private static final int NOPATH   = 2; // filename without path (e.g. apenny.prm)
  private static final int AUXPATH  = 3; // AUX name + / + filename (e.g. DAT/apenny.prm)
  private static final int AUXQUAL  = 4; // filename + AUX qualifier (e.g. apenny.prm{AUX=DAT})

  /** info field value for no button/event selected */
  public static final int NOTSET = 0;
  /** Submit button return value for info field */
  public static final int SUBMIT = 1;
  /** Cancel/Close button return value for info field */
  public static final int CANCEL = -1;

  private static final String AUX_FILE_SEPARATOR = "/";

  /** The Midas Context */
  private transient Midas M;
  /** initial filename */
  private String seedFilename;
  /** The filename filter option */
  private String filter;
  /** AUXes to select in AUX list */
  private String selectedAuxList;
  /** Info value for Messages that are sent */
  private int info;

  /** list model for the AUX list */
  private transient ListModel auxListModel;
  /** list model for the files list */
  private DataListModel  fileListModel;
  /** normal (sort by AUX) and strip AUX (sort by filename) comparator */
  private transient FileComparator comparator;

  /** file chooser mask options (see {@link #optionList}) */
  private int options;
  /** filename path option (defaults to FullPath) */
  private int pathOption;
  /** true if externally canceled programmatically or by user */
  private boolean cancel;

  /** Creates new form MJFileChooser dialog. */
  private MJFileChooser(java.awt.Frame parent, String options, String pathOption) {
    this(parent, "FileChooser", "seedFilename", "*.tmp|prm", "1|CWD", options, pathOption, null);
  }

  /** Creates new form MJFileChooser dialog, and set Midas Context
      @param parent          the parent frame of this file chooser dialog
      @param title           the String to display in the dialog's title bar
      @param seedFilename    the seed (default) filename for the File Name JTextField
      @param filter          the filename filter options (Format: *.prm|tmp|ext1|ext2|...)
      @param selectedAuxList the selected AUX list (Format: CWD|HOME|AUX1|AUX2|...)
      @param options         the options mask. see {@link #optionList}. default=NONE
      @param pathOption      the filename path option. see {@link #pathOptionList}. default=FULLPATH
      @param m               the Midas context reference
   */
  public MJFileChooser(java.awt.Frame parent, String title,
                       String seedFilename, String filter, String selectedAuxList,
                       String options, String pathOption, Midas m) {
    super(parent, title);
    this.M               = m;
    this.seedFilename    = seedFilename;
    this.filter          = filter;
    this.selectedAuxList = selectedAuxList;
    this.options         = Parser.mask(optionList, options, Parser.MASK_NONE);
    this.pathOption      = Parser.find(pathOptionList, pathOption, FULLPATH);
    setModal(is(MODAL));

    this.fileListModel    = new DataListModel();  // DataModel for displaying files
    this.comparator       = new FileComparator();
    int[] selectedIndices = formAuxList(this.selectedAuxList);

    // additional default settings
    this.info      = NOTSET;

    // initialize graphics components
    initComponents();

    // select specified AUXes
    jAuxList.setSelectedIndices(selectedIndices);

    // form file name list
    formNameList();
  }

  /** Refresh the contents of the file name list from the currently selected AUXes */
  public void refresh() { formNameList(); }

  /** Get the seed (initial) filename
   *  @return the seed (initial) filename to use for this file chooser */
  public String getSeedFilename() { return seedFilename; }

  /** Get the filename filter to use
   *  @return the filename filter to use for this file chooser */
  public String getFilter() { return filter; }

  /** Return a SortedListModel instead of the unordered ListModel */
  private ListModel getFileListModel() { return fileListModel; }

  /** Get the ListModel for the AUX list
   *  @return the ListModel for the AUX list */
  public ListModel getAuxListModel() { return auxListModel; }

  /** Gets the info of the object.
     @return 0 for normal submit, -1 for cancel/window closing, 1 for apply
   */
  public int getInfo() { return info; }

  /** Return user selected/inputed filename. Returns null, if dialog has been canceled. */
  public Object getValue() {
    Object result = (cancel? null : jSelectedFilename.getText());
    return result;
  }

  /** check if the specified option is set (see {@link #optionList}) */
  protected boolean is (int mask) { return (options&mask)!=0; }

  /** Form the ListModel of all the AUXes to put in JList
      @param selectedAuxList list of AUXes to return the indices of in the model for selection
      @return indices to select in the JList
   */
  private int[] formAuxList(String selectedAuxList) {
    String auxes[];
    if (M == null) {
      // if we don't have Midas content, default to some reasonable AUX values
      auxes = new String[] {"CWD", "HOME", "DAT", "RAM", "1", "9", "11", "12", "99"};
    } else {
      String nrauxm = "|READ|WRITE|HOMEPATH|"; // <-- these will be removed from AUX list
      Table auxTable = M.results.getTable("AUX");
      ArrayList<String> list = new ArrayList<>();
      for (Table.Iterator ti=auxTable.iterator(); ti.getNext(); ) {
        String aux = ti.key;
        String auxm = "|"+aux+"|";
        if (nrauxm.indexOf(auxm)>=0) continue; // this will remove current item from AUX list
        list.add(aux);
      }
      auxes = list.toArray(new String[list.size()]);
    }
    this.auxListModel = new DataListModel(auxes);

    // get selected indices
    ArrayList<Integer> indices = new ArrayList<>();
    String rauxm = ("|"+selectedAuxList+"|").toUpperCase();
    for (int i=0, len=auxes.length; i<len; i++) {
      String aux = "|"+auxes[i]+"|";
      if (rauxm.indexOf(aux) >= 0) { indices.add(Integer.valueOf(i)); }
    }

    // convert from ArrayList<Integer> to int[]
    int result[] = new int[indices.size()];
    for (int j=0; j<result.length; j++) {
      result[j] = indices.get(j).intValue();
    }

    return result;
  }

  /** Form the Filename list from the selected AUXes and add them to the Filenames ListModel */
  private void formNameList() {
    String filter = jFilterComboBox.getSelectedItem() + "";
    int pos       = filter.indexOf('.');
    String root   = filter;
    String ext    = "*";
    if (pos>=0) {
      root = filter.substring(0,pos);
      ext  = filter.substring(pos+1);
    }

    fileListModel.removeAllElements();
    Object[] selectedAuxes = jAuxList.getSelectedValues();
    if ( selectedAuxes != null ) {
      for (int j=0; j<selectedAuxes.length; j++) {
        String   aux   = (String) selectedAuxes[j];
// below line is for DEBUGGING outside NeXtMidas
// if (M==null) { fileListModel.addElement(aux + "/testFile" + j + ".tmp"); continue; }
        String   path  = M.io.getAuxPath(aux);
        String[] files = M.io.listFiles(path, root, ext);
        if (files==null) continue;
        fileListModel.addElements(aux + AUX_FILE_SEPARATOR, files);
      } // end for loop
    }
    fileListModel.sortBy(comparator); // sort files list with current comparator
  }

  /** This method is called from within the constructor to initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    jFilesPopupMenu = new javax.swing.JPopupMenu();
    javax.swing.JMenuItem jRefreshMenuItem = new javax.swing.JMenuItem();
    javax.swing.JSeparator jSeparator1 = new javax.swing.JSeparator();
    javax.swing.JRadioButtonMenuItem jSortByAuxMenuItem = new javax.swing.JRadioButtonMenuItem();
    javax.swing.JRadioButtonMenuItem jSortByFilenameMenuItem = new javax.swing.JRadioButtonMenuItem();
    javax.swing.JSeparator jSeparator2 = new javax.swing.JSeparator();
    javax.swing.JRadioButtonMenuItem jSortAscending = new javax.swing.JRadioButtonMenuItem();
    javax.swing.JRadioButtonMenuItem jSortDescending = new javax.swing.JRadioButtonMenuItem();
    javax.swing.ButtonGroup sortOptionGroup = new javax.swing.ButtonGroup();
    javax.swing.ButtonGroup sortOrderGroup = new javax.swing.ButtonGroup();
    javax.swing.JScrollPane auxListScrollPane = new javax.swing.JScrollPane();
    jAuxList = new javax.swing.JList();
    javax.swing.JScrollPane fileListScrollPane = new javax.swing.JScrollPane();
    jFileList = new javax.swing.JList();
    jOpenButton = new javax.swing.JButton();
    jCancelButton = new javax.swing.JButton();
    jSelectedFilename = new javax.swing.JTextField();
    jFilterComboBox = new javax.swing.JComboBox();
    javax.swing.JLabel jAuxLabel = new javax.swing.JLabel();
    javax.swing.JLabel jFilenameLabel = new javax.swing.JLabel();
    javax.swing.JLabel jFilterLabel = new javax.swing.JLabel();
    javax.swing.JLabel jFilesLabel = new javax.swing.JLabel();

    jFilesPopupMenu.setToolTipText("");
    jFilesPopupMenu.setLabel("hi");
    jFilesPopupMenu.setName(""); // NOI18N

    jRefreshMenuItem.setText("Refresh List");
    jRefreshMenuItem.setToolTipText("Refresh Files List");
    jRefreshMenuItem.addActionListener(this);
    jFilesPopupMenu.add(jRefreshMenuItem);
    jFilesPopupMenu.add(jSeparator1);

    sortOptionGroup.add(jSortByAuxMenuItem);
    jSortByAuxMenuItem.setSelected(true);
    jSortByAuxMenuItem.setText("Sort By AUX");
    jSortByAuxMenuItem.setToolTipText("Sort By AUX, then Filename");
    jSortByAuxMenuItem.addActionListener(this);
    jFilesPopupMenu.add(jSortByAuxMenuItem);

    sortOptionGroup.add(jSortByFilenameMenuItem);
    jSortByFilenameMenuItem.setText("Sort By Filename");
    jSortByFilenameMenuItem.setToolTipText("Sort By Filename");
    jSortByFilenameMenuItem.addActionListener(this);
    jFilesPopupMenu.add(jSortByFilenameMenuItem);
    jFilesPopupMenu.add(jSeparator2);

    sortOrderGroup.add(jSortAscending);
    jSortAscending.setSelected(true);
    jSortAscending.setText("Ascending");
    jSortAscending.setToolTipText("Ascending Sort Order");
    jSortAscending.addActionListener(this);
    jFilesPopupMenu.add(jSortAscending);

    sortOrderGroup.add(jSortDescending);
    jSortDescending.setText("Descending");
    jSortDescending.setToolTipText("Descending Sort Order");
    jSortDescending.addActionListener(this);
    jFilesPopupMenu.add(jSortDescending);

    setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
    setTitle(getTitle());
    addWindowListener(new java.awt.event.WindowAdapter() {
      public void windowClosing(java.awt.event.WindowEvent evt) {
        formWindowClosing(evt);
      }
    });

    auxListScrollPane.setMaximumSize(jAuxList.getMaximumSize());
    auxListScrollPane.setMinimumSize(jAuxList.getMinimumSize());
    auxListScrollPane.setPreferredSize(jAuxList.getPreferredSize());

    jAuxList.setModel(getAuxListModel());
    jAuxList.setMaximumSize(new java.awt.Dimension(50, 0));
    jAuxList.setMinimumSize(new java.awt.Dimension(30, 40));
    jAuxList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
      public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
        jAuxListValueChanged(evt);
      }
    });
    auxListScrollPane.setViewportView(jAuxList);

    fileListScrollPane.setMaximumSize(jFileList.getMaximumSize());
    fileListScrollPane.setMinimumSize(jFileList.getMinimumSize());
    fileListScrollPane.setPreferredSize(jFileList.getPreferredSize());

    jFileList.setModel(getFileListModel());
    jFileList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
    jFileList.setMinimumSize(new java.awt.Dimension(100, 40));
    jFileList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
      public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
        jFileListValueChanged(evt);
      }
    });
    jFileList.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mousePressed(java.awt.event.MouseEvent evt) {
        displayPopupMenu(evt);
      }
      public void mouseReleased(java.awt.event.MouseEvent evt) {
        displayPopupMenu(evt);
      }
    });
    fileListScrollPane.setViewportView(jFileList);

    jOpenButton.setText("Open");
    jOpenButton.addActionListener(this);

    jCancelButton.setText("Cancel");
    jCancelButton.addActionListener(this);

    jSelectedFilename.setText(getSeedFilename());

    jFilterComboBox.setEditable(true);
    jFilterComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { this.filter }));
    jFilterComboBox.setToolTipText("Filter Format =  *.extension1|extension2|extension3|...");

    jAuxLabel.setLabelFor(jAuxList);
    jAuxLabel.setText("AUXes");
    jAuxLabel.setToolTipText("AUX Selection List");
    jAuxLabel.setFocusable(false);
    jAuxLabel.setOpaque(true);

    jFilenameLabel.setLabelFor(jSelectedFilename);
    jFilenameLabel.setText("File Name");

    jFilterLabel.setLabelFor(jFilterComboBox);
    jFilterLabel.setText("Filter");

    jFilesLabel.setLabelFor(jFileList);
    jFilesLabel.setText("Files");
    jFilesLabel.setToolTipText("Files Selection List");

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
            .addComponent(jOpenButton)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jCancelButton))
          .addGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
              .addComponent(jFilenameLabel, javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(jFilterLabel, javax.swing.GroupLayout.Alignment.LEADING)
              .addComponent(jAuxLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 90, Short.MAX_VALUE)
              .addComponent(auxListScrollPane, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 90, Short.MAX_VALUE))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
              .addComponent(jFilterComboBox, 0, 235, Short.MAX_VALUE)
              .addComponent(jSelectedFilename, javax.swing.GroupLayout.DEFAULT_SIZE, 235, Short.MAX_VALUE)
              .addComponent(jFilesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 235, Short.MAX_VALUE)
              .addComponent(fileListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 235, Short.MAX_VALUE))))
        .addContainerGap())
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jAuxLabel)
          .addComponent(jFilesLabel))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addComponent(auxListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 156, Short.MAX_VALUE)
          .addComponent(fileListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 156, Short.MAX_VALUE))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jFilenameLabel)
          .addComponent(jSelectedFilename, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
          .addComponent(jFilterComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(jFilterLabel))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jCancelButton)
          .addComponent(jOpenButton))
        .addContainerGap())
    );

    pack();
  }// </editor-fold>//GEN-END:initComponents

  private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
    cancel = true;
    info   = CANCEL;
    setVisible(false);
  }//GEN-LAST:event_formWindowClosing

  private void jFileListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_jFileListValueChanged
    if (!evt.getValueIsAdjusting()) {
      String selected = (String) jFileList.getSelectedValue();
      if (selected == null) return; // nothing was selected, so return

      int    index    = selected.indexOf("/");
      String aux      = selected.substring(0, index);
      String name     = selected.substring(index + 1);
      int    dotIndex = name.lastIndexOf(".");

      String filename;
      if (is(NOEXT) && dotIndex>0) name = name.substring(0, dotIndex);
      if      (pathOption == NOPATH)  filename = name;
      else if (pathOption == AUXPATH) filename = aux + "/" + name;
      else if (pathOption == AUXQUAL) filename = name + "{AUX=" + aux + "}";
      else { // default to FULLPATH
        String path = (M==null)? "": M.io.getAuxPath(aux);
        filename    = path + name;
      }

      jSelectedFilename.setText(filename);
      jSelectedFilename.setColumns(filename.length());
    }
  }//GEN-LAST:event_jFileListValueChanged

  private void displayPopupMenu(java.awt.event.MouseEvent evt) {
    if (evt.isPopupTrigger()) jFilesPopupMenu.show(evt.getComponent(), evt.getX(), evt.getY());
  }

  /** AUX list selection has changed */
  private void jAuxListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_jAuxListValueChanged
    if (!evt.getValueIsAdjusting()) formNameList();
  }//GEN-LAST:event_jAuxListValueChanged


  public void actionPerformed(java.awt.event.ActionEvent evt) {
    String actionCommand = evt.getActionCommand();
    if (actionCommand.startsWith("Refresh")) {
      refresh();
    } else if (actionCommand.equals("Open")) {
      info = SUBMIT;
      setVisible(false); // hide dialog
    } else if (actionCommand.equals("Cancel")) {
      cancel = true;
      info = CANCEL;
      setVisible(false);
    } else if (actionCommand.equals("Sort By Filename")) {
      if (! AUX_FILE_SEPARATOR.equals(comparator.skip)) {
        comparator.skip = AUX_FILE_SEPARATOR;
        fileListModel.sortBy(comparator);
      }
    } else if (actionCommand.equals("Sort By AUX")) {
      if (comparator.skip != null) {
        comparator.skip = null;
        fileListModel.sortBy(comparator);
      }
    } else if (actionCommand.equals("Ascending")) {
      if (!comparator.ascending) {
        comparator.ascending = true;
        fileListModel.sortBy(comparator);
      }
    } else if (actionCommand.equals("Descending")) {
      if (comparator.ascending) {
        comparator.ascending = false;
        fileListModel.sortBy(comparator);
      }
    } else {
      String message = "MJFileChooser: Unsupported ActionEvent=" + evt;
      if (M != null) M.info(message);
      else System.out.println(message);
    }
  }

  /** Use to display the Swing File Chooser Dialog outside of NeXtMidas,
      but will not do much, as it will not be able to display files
      Command line arguments:
      1. {@code <options>}    - see {@link #optionList}
      2. {@code <pathOption>} - see {@link #pathOptionList}
     @param args the command line arguments
   */
  public static void main(String args[]) {
    final String options;
    final String pathOption;
    if (args.length > 0) options    = args[0]; else options    = null;
    if (args.length > 1) pathOption = args[1]; else pathOption = null;

    java.awt.EventQueue.invokeLater(new Runnable() {
      public void run() {
        javax.swing.JFrame frame = new javax.swing.JFrame();
        new MJFileChooser(frame, options, pathOption).setVisible(true);
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
      }
    });
  }

  // Variables declaration - do not modify//GEN-BEGIN:variables
  /** The Auxiliary Selection List */
  private javax.swing.JList jAuxList;
  /** The Cancel Button */
  private javax.swing.JButton jCancelButton;
  /** The Files Selection List */
  private javax.swing.JList jFileList;
  /** The Files Popup Menu */
  private javax.swing.JPopupMenu jFilesPopupMenu;
  /** The Filter Combo Box */
  private javax.swing.JComboBox jFilterComboBox;
  /** The Open Button */
  private javax.swing.JButton jOpenButton;
  /** The Selected Filename */
  private javax.swing.JTextField jSelectedFilename;
  // End of variables declaration//GEN-END:variables

  /** Comparator to do a string comparison while ignoring (skipping), the specified
      "skip" string at the beginning. If skip string is null, then use Comparator
      from {@link Collator#getInstance()}.
      @since NeXtMidas 2.5.0
   */
  private static class FileComparator implements Comparator<Object> {
    protected String   skip       = null;
    protected boolean  ascending  = true; // true for ascending (default), false for descending
    private Comparator<Object> comparator = Collator.getInstance();

    protected FileComparator() {}

    public int compare(Object o1, Object o2) {
      if ( (skip != null) && (o1 instanceof String) && (o2 instanceof String) )  {
        String s1  = (String) o1;
        String s2  = (String) o2;
        int index1 = s1.indexOf(skip);
        int index2 = s2.indexOf(skip);
        if (index1>0) o1 = s1.substring(index1);
        if (index2>0) o2 = s2.substring(index2);
      }
      int result = comparator.compare(o1, o2);
      if (!ascending) result = -result;
      return result;
    }
  } // end class FileComparator
}