All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.l2fprod.common.swing.plaf.windows.WindowsDirectoryChooserUI Maven / Gradle / Ivy

Go to download

Orbit, a versatile image analysis software for biological image-based quantification

There is a newer version: 3.15
Show newest version
/**
 * $ $ License.
 *
 * Copyright $ L2FProd.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.l2fprod.common.swing.plaf.windows;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Stack;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileSystemView;
import javax.swing.filechooser.FileView;
import javax.swing.plaf.basic.BasicFileChooserUI;
import javax.swing.text.JTextComponent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import com.l2fprod.common.swing.JDirectoryChooser;
import com.l2fprod.common.swing.LookAndFeelTweaks;
import com.l2fprod.common.swing.plaf.DirectoryChooserUI;
import com.l2fprod.common.swing.tree.LazyMutableTreeNode;
import com.l2fprod.common.util.OS;
import com.l2fprod.common.util.ResourceManager;

/**
 * WindowsDirectoryChooserUI. 
* */ public class WindowsDirectoryChooserUI extends BasicFileChooserUI implements DirectoryChooserUI { private static Queue nodeQueue; private JDirectoryChooser chooser; private JTextComponent message; private JTree tree; private JScrollPane treeScroll; private JButton approveButton; private JButton cancelButton; private JPanel buttonPanel; private BasicFileView fileView = new WindowsFileView(); private Action approveSelectionAction = new ApproveSelectionAction(); private boolean useNodeQueue; public WindowsDirectoryChooserUI(JDirectoryChooser chooser) { super(chooser); } public void rescanCurrentDirectory(JFileChooser fc) { super.rescanCurrentDirectory(fc); findFile( chooser.getSelectedFile() == null ? chooser.getCurrentDirectory() : chooser.getSelectedFile(), true, true); } public void ensureFileIsVisible(JFileChooser fc, File f) { super.ensureFileIsVisible(fc, f); findFile(f, false, false); } protected String getToolTipText(MouseEvent event) { TreePath path = tree.getPathForLocation(event.getX(), event.getY()); if (path != null && path.getLastPathComponent() instanceof FileTreeNode) { FileTreeNode node = (FileTreeNode)path.getLastPathComponent(); String typeDescription = getFileView(chooser).getTypeDescription(node.getFile()); if (typeDescription == null || typeDescription.length() == 0) { return node.toString(); } else { return node.toString() + " - " + typeDescription; } } else { return null; } } public void installComponents(JFileChooser chooser) { this.chooser = (JDirectoryChooser)chooser; chooser.setLayout(LookAndFeelTweaks.createBorderLayout()); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); Component accessory = chooser.getAccessory(); if (accessory != null) { chooser.add("North", chooser.getAccessory()); } tree = new JTree() { public String getToolTipText(MouseEvent event) { String tip = WindowsDirectoryChooserUI.this.getToolTipText(event); if (tip == null) { return super.getToolTipText(event); } else { return tip; } } }; tree.addTreeExpansionListener(new TreeExpansion()); tree.setModel(new FileSystemTreeModel(chooser.getFileSystemView())); tree.setRootVisible(false); tree.setShowsRootHandles(false); tree.setCellRenderer(new FileSystemTreeRenderer()); tree.setToolTipText(""); chooser.add("Center", treeScroll = new JScrollPane(tree)); treeScroll.setPreferredSize(new Dimension(300, 300)); approveButton = new JButton(); approveButton.setAction(getApproveSelectionAction()); cancelButton = new JButton(); cancelButton.addActionListener(getCancelSelectionAction()); buttonPanel = new JPanel(LookAndFeelTweaks.createButtonAreaLayout()); buttonPanel.add(approveButton); buttonPanel.add(cancelButton); chooser.add("South", buttonPanel); updateView(chooser); } protected void installStrings(JFileChooser fc) { super.installStrings(fc); saveButtonToolTipText = ResourceManager.get(DirectoryChooserUI.class).getString( "DirectoryChooser.saveButtonToolTipText"); openButtonToolTipText = ResourceManager.get(DirectoryChooserUI.class).getString( "DirectoryChooser.openButtonToolTipText"); cancelButtonToolTipText = ResourceManager.get(DirectoryChooserUI.class).getString( "DirectoryChooser.cancelButtonToolTipText"); } public void uninstallComponents(JFileChooser chooser) { chooser.remove(message); chooser.remove(treeScroll); chooser.remove(buttonPanel); } public FileView getFileView(JFileChooser fc) { return fileView; } protected void installListeners(JFileChooser fc) { super.installListeners(fc); tree.addTreeSelectionListener(new SelectionListener()); } protected void uninstallListeners(JFileChooser fc) { super.uninstallListeners(fc); } public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { return new ChangeListener(); } private void updateView(JFileChooser chooser) { if (chooser.getApproveButtonText() != null) { approveButton.setText(chooser.getApproveButtonText()); approveButton.setMnemonic(chooser.getApproveButtonMnemonic()); } else { if (JFileChooser.OPEN_DIALOG == chooser.getDialogType()) { approveButton.setText(openButtonText); approveButton.setToolTipText(openButtonToolTipText); approveButton.setMnemonic(openButtonMnemonic); } else { approveButton.setText(saveButtonText); approveButton.setToolTipText(saveButtonToolTipText); approveButton.setMnemonic(saveButtonMnemonic); } } cancelButton.setText(cancelButtonText); cancelButton.setMnemonic(cancelButtonMnemonic); buttonPanel.setVisible(chooser.getControlButtonsAreShown()); } /** * Ensures the file is visible, tree expanded and optionally selected * * @param fileToLocate * @param selectFile */ private void findFile( File fileToLocate, boolean selectFile, boolean reload) { if (fileToLocate == null || !fileToLocate.isDirectory()) { return; } // build the canonical path so we can navigate the tree model to find the // node File file = null; try { file = fileToLocate.getCanonicalFile(); } catch (Exception e) { return; } // temporarly disable loading nodes in the background useNodeQueue = false; try { // split the full path into individual files to locate them in the tree // model List files = new ArrayList(); files.add(file); while ((file = chooser.getFileSystemView().getParentDirectory(file)) != null) { files.add(0, file); } List path = new ArrayList(); // start from the root DefaultMutableTreeNode node = (DefaultMutableTreeNode)tree.getModel().getRoot(); path.add(node); DefaultMutableTreeNode current; boolean found = true; // ...and go through the tree model to find the files. Stop as soon as // path is completely found or if one of the files in the path is not // found. while (files.size() > 0 && found) { found = false; for (int i = 0, c = node.getChildCount(); i < c; i++) { current = (DefaultMutableTreeNode)node.getChildAt(i); File f = ((FileTreeNode)current).getFile(); if (files.get(0).equals(f)) { path.add(current); files.remove(0); node = current; found = true; break; } } } // select the path we found, it may be the file we were looking for or a // subpath only TreePath pathToSelect = new TreePath(path.toArray()); if (pathToSelect.getLastPathComponent() instanceof FileTreeNode && reload) { ((FileTreeNode) (pathToSelect.getLastPathComponent())).clear(); } if (selectFile) { tree.expandPath(pathToSelect); tree.setSelectionPath(pathToSelect); } tree.scrollPathToVisible(pathToSelect); } finally { // re-enable background loading useNodeQueue = true; } } private class ChangeListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { if (JFileChooser .APPROVE_BUTTON_TEXT_CHANGED_PROPERTY .equals(evt.getPropertyName())) { updateView(chooser); } if (JFileChooser .MULTI_SELECTION_ENABLED_CHANGED_PROPERTY .equals(evt.getPropertyName())) { if (chooser.isMultiSelectionEnabled()) { tree.getSelectionModel().setSelectionMode( TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); } else { tree.getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION); } } if (JFileChooser .DIRECTORY_CHANGED_PROPERTY .equals(evt.getPropertyName())) { findFile(chooser.getCurrentDirectory(), false, false); } if (JFileChooser .ACCESSORY_CHANGED_PROPERTY .equals(evt.getPropertyName())) { Component oldValue = (Component)evt.getOldValue(); Component newValue = (Component)evt.getNewValue(); if (oldValue != null) { chooser.remove(oldValue); } if (newValue != null) { chooser.add("North", newValue); } chooser.revalidate(); chooser.repaint(); } if (JFileChooser .CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY .equals(evt.getPropertyName())) { updateView(chooser); } } } private class SelectionListener implements TreeSelectionListener { public void valueChanged(TreeSelectionEvent e) { getApproveSelectionAction().setEnabled(tree.getSelectionCount() > 0); setSelectedFiles(); } } public Action getApproveSelectionAction() { return approveSelectionAction; } private void setSelectedFiles() { TreePath[] selectedPaths = tree.getSelectionPaths(); if (selectedPaths == null || selectedPaths.length == 0) { chooser.setSelectedFile(null); return; } List files = new ArrayList(); for (int i = 0, c = selectedPaths.length; i < c; i++) { File f = ((FileTreeNode)selectedPaths[i].getLastPathComponent()).getFile(); files.add(f); } chooser.setSelectedFiles((File[])files.toArray(new File[0])); } private class ApproveSelectionAction extends AbstractAction { public ApproveSelectionAction() { setEnabled(false); } public void actionPerformed(ActionEvent e) { setSelectedFiles(); chooser.approveSelection(); } } /** * Listens on nodes being opened and preload their children to get better UI * experience (GUI should be more responsive and empty nodes discovered * automatically). */ private class TreeExpansion implements TreeExpansionListener { public void treeCollapsed(TreeExpansionEvent event) { } public void treeExpanded(TreeExpansionEvent event) { // ensure children gets expanded later if (event.getPath() != null) { Object lastElement = event.getPath().getLastPathComponent(); if (lastElement instanceof FileTreeNode && useNodeQueue) { if (((FileTreeNode)lastElement).isLoaded()) { for (Enumeration e = ((FileTreeNode)lastElement).children(); e.hasMoreElements(); ) { //Object node = enum.nextElement(); addToQueue((FileTreeNode)e.nextElement(), tree); } } } } } } private class FileSystemTreeRenderer extends DefaultTreeCellRenderer { public Component getTreeCellRendererComponent( JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, false, // even "leaf" folders should look like other folders row, hasFocus); if (value instanceof FileTreeNode) { FileTreeNode node = (FileTreeNode)value; setText(getFileView(chooser).getName(node.getFile())); if (OS.isMacOSX() && UIManager.getLookAndFeel().isNativeLookAndFeel()) { // do not set icon for MacOSX when native look is used, it seems the // Tree.icons set by the // look and feel are not that good or Apple is doing something I // can't figure. // setIcon only if not running in MacOSX } else { setIcon(getFileView(chooser).getIcon(node.getFile())); } } return this; } } private class FileSystemTreeModel extends DefaultTreeModel { public FileSystemTreeModel(FileSystemView fsv) { super(new MyComputerTreeNode(fsv), false); } } private class MyComputerTreeNode extends LazyMutableTreeNode { public MyComputerTreeNode(FileSystemView fsv) { super(fsv); } protected void loadChildren() { FileSystemView fsv = (FileSystemView)getUserObject(); File[] roots = fsv.getRoots(); if (roots != null) { Arrays.sort(roots); for (int i = 0, c = roots.length; i < c; i++) { add(new FileTreeNode(roots[i])); } } } public String toString() { return "/"; } } private class FileTreeNode extends LazyMutableTreeNode implements Comparable { public FileTreeNode(File file) { super(file); } public boolean canEnqueue() { return !isLoaded() && !chooser.getFileSystemView().isFloppyDrive(getFile()) && !chooser.getFileSystemView().isFileSystemRoot(getFile()); } public boolean isLeaf() { if (!isLoaded()) { return false; } else { return super.isLeaf(); } } protected void loadChildren() { FileTreeNode[] nodes = getChildren(); for (int i = 0, c = nodes.length; i < c; i++) { add(nodes[i]); } } private FileTreeNode[] getChildren() { File[] files = chooser.getFileSystemView().getFiles( getFile(), chooser.isFileHidingEnabled()); ArrayList nodes = new ArrayList(); // keep only directories, no "file" in the tree. if (files != null) { for (int i = 0, c = files.length; i < c; i++) { if (files[i].isDirectory()) { nodes.add(new FileTreeNode(files[i])); } } } // sort directories, FileTreeNode implements Comparable FileTreeNode[] result = (FileTreeNode[])nodes.toArray(new FileTreeNode[0]); Arrays.sort(result); return result; } public File getFile() { return (File)getUserObject(); } public String toString() { return chooser.getFileSystemView().getSystemDisplayName( (File)getUserObject()); } public int compareTo(Object o) { if (!(o instanceof FileTreeNode)) { return 1; } return getFile().compareTo(((FileTreeNode)o).getFile()); } public void clear() { super.clear(); ((DefaultTreeModel)tree.getModel()).nodeStructureChanged(this); } } /** * From WindowsFileChooserUI */ protected class WindowsFileView extends BasicFileView { public Icon getIcon(File f) { Icon icon = getCachedIcon(f); if (icon != null) { return icon; } if (f != null) { icon = getFileChooser().getFileSystemView().getSystemIcon(f); } if (icon == null) { icon = super.getIcon(f); } cacheIcon(f, icon); return icon; } } private static synchronized void addToQueue(FileTreeNode node, JTree tree) { if (nodeQueue == null || !nodeQueue.isAlive()) { nodeQueue = new Queue(); nodeQueue.start(); } if (node.canEnqueue()) { nodeQueue.add(node, tree); } } /** * This queue takes care of loading nodes in the background. */ private static final class Queue extends Thread { private volatile Stack nodes = new Stack(); private Object lock = new Object(); private volatile boolean running = true; public Queue() { super("DirectoryChooser-BackgroundLoader"); setDaemon(true); } public void add(WindowsDirectoryChooserUI.FileTreeNode node, JTree tree) { if (!isAlive()) { throw new IllegalArgumentException("Queue is no longer alive"); } synchronized (lock) { if (running) { nodes.addElement(new QueueItem(node, tree)); lock.notifyAll(); } } } public void run() { while (running) { while (nodes.size() > 0) { final QueueItem item = (QueueItem)nodes.pop(); final WindowsDirectoryChooserUI.FileTreeNode node = item.node; final JTree tree = item.tree; // ask how many items we got node.getChildCount(); Runnable runnable = new Runnable() { public void run() { ((DefaultTreeModel)tree.getModel()).nodeChanged(node); tree.repaint(); } }; try { SwingUtilities.invokeAndWait(runnable); } catch (InterruptedException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } // wait for 5 seconds for someone to use the queue, else just ends this // queue try { synchronized (lock) { lock.wait(5000); } if (nodes.size() == 0) { running = false; } } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * An entry in the queue. */ private static final class QueueItem { WindowsDirectoryChooserUI.FileTreeNode node; JTree tree; public QueueItem(WindowsDirectoryChooserUI.FileTreeNode node, JTree tree) { this.node = node; this.tree = tree; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy