gt:       Node p = tmodel.getRoot();
context: /*********************************************************************************
   *  TotalCross Software Development Kit                                          *
   *  Copyright (C) 2000-2012 SuperWaba Ltda.                                      *
   *  All Rights Reserved                                                          *
   *                                                                               *
   *  This library and virtual machine is distributed in the hope that it will     *
   *  be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of    *
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                         *
   *                                                                               *
   *  This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0    *
   *  A copy of this license is located in file license.txt at the root of this    *
   *  SDK or can be downloaded here:                                               *
   *  http://www.gnu.org/licenses/lgpl-3.0.txt                                     *
   *                                                                               *
   *********************************************************************************/
  
  package totalcross.ui.dialog;
  
  import totalcross.io.File;
  import totalcross.io.FileNotFoundException;
  import totalcross.io.IOException;
  import totalcross.sys.Convert;
  import totalcross.sys.Settings;
  import totalcross.sys.Vm;
  import totalcross.ui.Button;
  import totalcross.ui.ComboBox;
  import totalcross.ui.Container;
  import totalcross.ui.ImageControl;
  import totalcross.ui.Label;
  import totalcross.ui.PushButtonGroup;
  import totalcross.ui.ScrollBar;
  import totalcross.ui.UIColors;
  import totalcross.ui.Window;
  import totalcross.ui.event.ControlEvent;
  import totalcross.ui.event.Event;
  import totalcross.ui.event.PenEvent;
  import totalcross.ui.gfx.Color;
  import totalcross.ui.image.Image;
  import totalcross.ui.tree.Node;
  import totalcross.ui.tree.PathEntry;
  import totalcross.ui.tree.Tree;
  import totalcross.ui.tree.TreeModel;
  import totalcross.util.Vector;
  
  /** A class that shows all folders from a startup one to allow the user select a file or a folder.
   * Here's a sample of how to use it:
   *
   * <pre>
     try
     {
        FileChooserBox w = new FileChooserBox("Select the folder",new String[]{"  This one  "," Cancel "},
           new FileChooserBox.Filter()
           {
              public boolean accept(File f) throws IOException
              {
                 return f.isDir(); // will only list folders. you may filter by other file types too
              }
           });
        w.mountTree(Settings.appPath,1);
        w.popup();
        return w.getPressedButtonIndex() == 0 ? w.getAnswer() : null;
     }
     catch (IOException e)
     {
        return null;
     }
   * </pre>
   * The tree is mounted <i>on demand</i> to speedup the process.
   * <p>
   * Here's a list of customizations you can do:
   * <ul>
   * <li> You can set a path to be selected initially by setting the <code>initialPath</code> property.
   * <li> Set the defaultButton property to allow the selection of an item doing a double-click on it.
   * </ul>
   */
  
  public class FileChooserBox extends Window {
    protected PushButtonGroup pbg;
    protected Tree tree;
    protected Node lastSelected;
    protected Filter ff;
    /** The default button captions: " Select " and " Cancel ". You can localize them if you want. */
    public static String[] defaultButtonCaptions = { " Select ", " Cancel " };
    /** The "Volume: " label that's placed before the volume combo. You can localize it if you want. */
    public static String msgVolume = "Volume: ";
    /** The " Refresh " button that's placed after the volume combo. You can localize it if you want. */
    public static String msgRefresh = " Refresh ";
    /** The title of a message box that appears if the user tries to access a volume and an error is issued by the operating system. Defaults to "Error". You can localize it if you want. */
    public static String msgInvalidVolumeTitle = "Error";
    /** The " Preview " button title. You can localize it if you want. */
    public static String previewTitle = " Preview ";
    /** The body of a message box that appears if the user tries to access a volume and an error is issued by the operating system. Defaults to "Unable to read the contents of the selected volume. Make sure the volume is mounted and you have enough privileges to query its contents.". You can localize it if you want. */
    public static String msgInvalidVolumeMessage = "Unable to read the contents of the selected volume. Make sure the volume is mounted and you have enough privileges to query its contents.";
    protected String[] buttonCaptions;
    protected TreeModel tmodel;
    protected int selectedIndex;
    protected ComboBox cbRoot;
    protected Button btRefresh;
    protected ImageControl preview;
    private int previouslySelectedRootIndex = -1;
    private Button btnPreview;
    private Container tap;
    private static final boolean isAndroid = Settings.platform.equals(Settings.ANDROID);
  
    /** On Win32, we show by default the root drive and expand to the default path.
     * Set this to false to show the initial path only.
     * @since TotalCross 3.2
     */
    public boolean showInitialPathOnly = !Settings.onJavaSE && !Settings.platform.equals(Settings.WIN32);
  
    /* return the number of files found in the current directory
     * @since TotalCross 1.53 
     */
    public int fileCount;
  
    /** Set to true to allow multiple selections using a Check drawn before the nodes.
     * @since TotalCross 1.15 
     */
    public boolean multipleSelection; // guich@tc115_4
  
    /** Selects a file that is in this tree.
     * @since TotalCross 1.25
     */
    public String initialPath; // guich@tc125_21
  
    /** The button index that will be choosen if the user makes a double-click in an item.
     * Usually you set this to the index of the "Ok" button. 
     */
    public int defaultButton = -1; // guich@tc125_23
  
    /** The preview height in percentage of the total height. Defaults to 30. */
    public static final int PREVIEW_HEIGHT = 30;
  
    /** Set to true to use a preview window to show photo thumbnails */
    public boolean showPreview;
  
    /** Set to true to sort the list showing the newest files first, instead of alphabetical order */
    public boolean newestFirst;
  
    class LoadOnDemandTree extends Tree {
      public LoadOnDemandTree() {
        super(tmodel);
      }
  
      /** Checks if a node was already loaded and load it otherwise */
      @Override
      public boolean expand(Node node) {
        if (node.size() == 1 && node.getFirstChild().userObject instanceof File) {
          try {
            // replace the string by the files
            File f = (File) node.getFirstChild().userObject;
            node.removeAllChildren();
            mountTree(node, f);
          } catch (Exception e) {
          }
        }
        return super.expand(node);
      }
    }
  
    /** Interface used if you want to filter the files that will be added to the tree. */
    public static interface Filter {
      /** Must return true if the file is to be added to the tree. */
      public boolean accept(File f) throws IOException;
    }
  
    /** Constructs a file chooser with the given parameters.
     * @param caption The caption to be displayed in the title
     * @param buttonCaptions The button captions that will be used in the PushButtonGroup
     * @param ff The Filter. Pass null to accept all files.
     */
    public FileChooserBox(String caption, String[] buttonCaptions, Filter ff) {
      super(caption, RECT_BORDER);
      transitionEffect = Settings.enableWindowTransitionEffects ? TRANSITION_OPEN : TRANSITION_NONE;
      fadeOtherWindows = Settings.fadeOtherWindows;
      uiAdjustmentsBasedOnFontHeightIsSupported = false;
      this.ff = ff;
      this.buttonCaptions = buttonCaptions;
      tmodel = new TreeModel();
      if (Settings.isWindowsCE() || isAndroid || Settings.platform.equals(Settings.WIN32)
          || Settings.platform.equals(Settings.JAVA)) {
        cbRoot = new ComboBox(listRoots());
      }
    }
  
    private static String[] listRoots() {
      if (!isAndroid) {
        return File.listRoots();
      }
      Vector v = new Vector(10);
      v.addElement("device/");
      try {
        for (int i = 0; i < 9; i++) {
          if (File.isCardInserted(i)) {
            v.addElement("/sdcard" + i);
          }
        }
      } catch (Exception e) {
      }
      return (String[]) v.toObjectArray();
    }
  
    /** Constructs a file chooser with "Select a file" as the window title, and "Select" and "Cancel" buttons.
     * @param ff The Filter. Pass null to accept all files.
     */
    public FileChooserBox(Filter ff) {
      this("Select a file", defaultButtonCaptions, ff);
    }
  
    @Override
    protected void onPopup() // guich@tc100b5_28
    {
      if (children != null) {
        if (cbRoot != null) // guich@tc126_10
        {
          cbRoot.removeAll();
          cbRoot.add(listRoots());
          selectedIndex = previouslySelectedRootIndex = -1;
        }
        return;
      }
      setRect(LEFT + 5, TOP + 5, FILL - 5, FILL - 5);
      setBackForeColors(UIColors.fileChooserBack, UIColors.fileChooserFore);
  
      if (cbRoot != null) // guich@tc126_10
      {
        Label l;
        add(l = new Label(msgVolume), LEFT + 2, TOP + 2);
        add(btRefresh = new Button(msgRefresh), RIGHT - 2, TOP + 2);
        add(cbRoot, AFTER + 2, SAME, FIT - 2, btRefresh.getHeight(), l);
      }
  
      pbg = new PushButtonGroup(buttonCaptions, 8, 1);
      pbg.setFont(font);
      add(pbg, RIGHT - 2, BOTTOM - 2, PREFERRED + 4, PREFERRED + 4);
      tree = new LoadOnDemandTree();
      tree.multipleSelection = multipleSelection;
      tree.setFont(font);
      add(tap = new Container(), LEFT + 2, btRefresh == null ? TOP + 2 : AFTER + 2, FILL - 2, FIT - 5, btRefresh);
      if (showPreview) {
        add(btnPreview = new Button(previewTitle), LEFT + 2, BOTTOM - 2);
        tap.add(preview = new ImageControl(), LEFT, BOTTOM, FILL, PARENTSIZE + PREVIEW_HEIGHT);
        preview.setBackColor(Color.getCursorColor(backColor));
        preview.setEventsEnabled(true);
        preview.centerImage = preview.scaleToFit = true;
      }
      tap.add(tree, LEFT, TOP, FILL, showPreview ? PARENTSIZE + 69 : FILL, btRefresh);
      //tree.dontShowFileAndFolderIcons();
      int c = getBackColor();
      if (cbRoot != null) {
        cbRoot.setBackColor(Color.brighter(c));
      }
      pbg.setBackColor(c);
      tree.setBackColor(c);
      tree.setCursorColor(c);
      if (initialPath != null) {
        reload(initialPath);
      }
      tree.requestFocus();
    }
  
    private void reload(String initialPath) {
      try {
        if (showInitialPathOnly) {
          mountTree(initialPath);
        } else {
          int dp = initialPath.indexOf(':');
          boolean cut = initialPath.length() > 3 && dp != -1; // dont cut if is "c:\"
          String ini = cut ? initialPath.substring(0, dp + 2) : initialPath;
          mountTree(ini);
          if (cut) {
            tree.expandTo(initialPath.substring(dp + 2));
          }
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  
    /** @deprecated */
    @Deprecated
    public void mountTree(String filePath, int volume) throws IOException {
      mountTree(filePath);
    }
  
    /** Call this method to mount the tree, starting from the given path and volume.
     * @param filePath The root from where the tree will be mounted.
     */
    public void mountTree(String filePath) throws IOException {
      Node root;
      filePath = filePath.replace('\\', '/');
      File f = new File(filePath, File.DONT_OPEN);
      if (!f.isDir()) {
        throw new IOException(filePath + " is not a valid path");
      }
      root = new Node(new PathEntry(filePath, true));
      mountTree(root, f);
      tmodel.setRoot(root);
      if (cbRoot != null) // guich@tc126_10: select current drive on combobox
      {
        previouslySelectedRootIndex = cbRoot.getSelectedIndex();
  