Графический интерфейс браузера файлов
FileBro - это основанный на графическом интерфейсе браузер файлов.
Функциональность FileBro
- Дерево каталогов - показывает корни файловой системы при запуске, но в противном случае создается лениво, когда пользователь просматривает файловую систему. FileBro отображает индикатор выполнения при загрузке новых записей.
- Список файлов (таблица) отображает список каталогов и файлов в выбранном дереве каталогов. Сортировка, нажав на заголовок столбца.
- Кнопки - все используют класс
Desktop
для своей функциональности. Если действие не может быть завершено, значимая причина должна отображаться в сообщении об ошибкеJEditorPane
.-
Locate
- открывает родительский каталог текущего выбранного файла. -
Open
- запускает любое приложение, являющееся потребителем по умолчанию для этого типа файла. -
Edit
- открывает файл для редактирования у потребителя по умолчанию. -
Print
- печатает файл с использованием потребителя по умолчанию.
-
- Информация о выбранном файле отображается под кнопками.
Работает ли текущая функциональность согласно описанию выше?
FileBrowser.java
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Container;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.swing.table.*;
import javax.swing.filechooser.FileSystemView;
import javax.imageio.ImageIO;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.io.*;
import java.nio.channels.FileChannel;
import java.net.URL;
/**
A basic File Browser. Requires 1.6+ for the Desktop & SwingWorker
classes, amongst other minor things.
Includes support classes FileTableModel & FileTreeCellRenderer.
@TODO Bugs
<li>Fix keyboard focus issues - especially when functions like
rename/delete etc. are called that update nodes & file lists.
<li>Needs more testing in general.
@TODO Functionality
<li>Double clicking a directory in the table, should update the tree
<li>Move progress bar?
<li>Add other file display modes (besides table) in CardLayout?
<li>Menus + other cruft?
<li>Implement history/back
<li>Allow multiple selection
<li>Add file search
@author Andrew Thompson
@version 2011-06-08
@see http://codereview.stackexchange.com/q/4446/7784
@license LGPL
*/
class FileBrowser {
/** Title of the application */
public static final String APP_TITLE = "FileBro";
/** Used to open/edit/print files. */
private Desktop desktop;
/** Provides nice icons and names for files. */
private FileSystemView fileSystemView;
/** currently selected File. */
private File currentFile;
/** Main GUI container */
private JPanel gui;
/** File-system tree. Built Lazily */
private JTree tree;
private DefaultTreeModel treeModel;
/** Directory listing */
private JTable table;
private JProgressBar progressBar;
/** Table model for File[]. */
private FileTableModel fileTableModel;
private ListSelectionListener listSelectionListener;
private boolean cellSizesSet = false;
private int rowIconPadding = 6;
/* File controls. */
private JButton openFile;
private JButton printFile;
private JButton editFile;
/* File details. */
private JLabel fileName;
private JTextField path;
private JLabel date;
private JLabel size;
private JCheckBox readable;
private JCheckBox writable;
private JCheckBox executable;
private JRadioButton isDirectory;
private JRadioButton isFile;
/* GUI options/containers for new File/Directory creation. Created lazily. */
private JPanel newFilePanel;
private JRadioButton newTypeFile;
private JTextField name;
public Container getGui() {
if (gui==null) {
gui = new JPanel(new BorderLayout(3,3));
gui.setBorder(new EmptyBorder(5,5,5,5));
fileSystemView = FileSystemView.getFileSystemView();
desktop = Desktop.getDesktop();
JPanel detailView = new JPanel(new BorderLayout(3,3));
table = new JTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setAutoCreateRowSorter(true);
table.setShowVerticalLines(false);
listSelectionListener = new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent lse) {
int row = table.getSelectionModel().getLeadSelectionIndex();
setFileDetails( ((FileTableModel)table.getModel()).getFile(row) );
}
};
table.getSelectionModel().addListSelectionListener(listSelectionListener);
JScrollPane tableScroll = new JScrollPane(table);
Dimension d = tableScroll.getPreferredSize();
tableScroll.setPreferredSize(new Dimension((int)d.getWidth(), (int)d.getHeight()/2));
detailView.add(tableScroll, BorderLayout.CENTER);
// the File tree
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
treeModel = new DefaultTreeModel(root);
TreeSelectionListener treeSelectionListener = new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent tse){
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)tse.getPath().getLastPathComponent();
showChildren(node);
setFileDetails((File)node.getUserObject());
}
};
// show the file system roots.
File[] roots = fileSystemView.getRoots();
for (File fileSystemRoot : roots) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(fileSystemRoot);
root.add( node );
File[] files = fileSystemView.getFiles(fileSystemRoot, true);
for (File file : files) {
if (file.isDirectory()) {
node.add(new DefaultMutableTreeNode(file));
}
}
//
}
tree = new JTree(treeModel);
tree.setRootVisible(false);
tree.addTreeSelectionListener(treeSelectionListener);
tree.setCellRenderer(new FileTreeCellRenderer());
tree.expandRow(0);
JScrollPane treeScroll = new JScrollPane(tree);
// as per trashgod tip
tree.setVisibleRowCount(15);
Dimension preferredSize = treeScroll.getPreferredSize();
Dimension widePreferred = new Dimension(
200,
(int)preferredSize.getHeight());
treeScroll.setPreferredSize( widePreferred );
// details for a File
JPanel fileMainDetails = new JPanel(new BorderLayout(4,2));
fileMainDetails.setBorder(new EmptyBorder(0,6,0,6));
JPanel fileDetailsLabels = new JPanel(new GridLayout(0,1,2,2));
fileMainDetails.add(fileDetailsLabels, BorderLayout.WEST);
JPanel fileDetailsValues = new JPanel(new GridLayout(0,1,2,2));
fileMainDetails.add(fileDetailsValues, BorderLayout.CENTER);
fileDetailsLabels.add(new JLabel("File", JLabel.TRAILING));
fileName = new JLabel();
fileDetailsValues.add(fileName);
fileDetailsLabels.add(new JLabel("Path/name", JLabel.TRAILING));
path = new JTextField(5);
path.setEditable(false);
fileDetailsValues.add(path);
fileDetailsLabels.add(new JLabel("Last Modified", JLabel.TRAILING));
date = new JLabel();
fileDetailsValues.add(date);
fileDetailsLabels.add(new JLabel("File size", JLabel.TRAILING));
size = new JLabel();
fileDetailsValues.add(size);
fileDetailsLabels.add(new JLabel("Type", JLabel.TRAILING));
JPanel flags = new JPanel(new FlowLayout(FlowLayout.LEADING,4,0));
isDirectory = new JRadioButton("Directory");
flags.add(isDirectory);
isFile = new JRadioButton("File");
flags.add(isFile);
fileDetailsValues.add(flags);
JToolBar toolBar = new JToolBar();
// mnemonics stop working in a floated toolbar
toolBar.setFloatable(false);
JButton locateFile = new JButton("Locate");
locateFile.setMnemonic('l');
locateFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
try {
System.out.println("Locate: " + currentFile.getParentFile());
desktop.open(currentFile.getParentFile());
} catch(Throwable t) {
showThrowable(t);
}
gui.repaint();
}
});
toolBar.add(locateFile);
openFile = new JButton("Open");
openFile.setMnemonic('o');
openFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
try {
System.out.println("Open: " + currentFile);
desktop.open(currentFile);
} catch(Throwable t) {
showThrowable(t);
}
gui.repaint();
}
});
toolBar.add(openFile);
editFile = new JButton("Edit");
editFile.setMnemonic('e');
editFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
try {
desktop.edit(currentFile);
} catch(Throwable t) {
showThrowable(t);
}
}
});
toolBar.add(editFile);
printFile = new JButton("Print");
printFile.setMnemonic('p');
printFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
try {
desktop.print(currentFile);
} catch(Throwable t) {
showThrowable(t);
}
}
});
toolBar.add(printFile);
// Check the actions are supported on this platform!
openFile.setEnabled(desktop.isSupported(Desktop.Action.OPEN));
editFile.setEnabled(desktop.isSupported(Desktop.Action.EDIT));
printFile.setEnabled(desktop.isSupported(Desktop.Action.PRINT));
flags.add(new JLabel(":: Flags"));
readable = new JCheckBox("Read ");
readable.setMnemonic('a');
flags.add(readable);
writable = new JCheckBox("Write ");
writable.setMnemonic('w');
flags.add(writable);
executable = new JCheckBox("Execute");
executable.setMnemonic('x');
flags.add(executable);
int count = fileDetailsLabels.getComponentCount();
for (int ii=0; ii<count; ii++) {
fileDetailsLabels.getComponent(ii).setEnabled(false);
}
count = flags.getComponentCount();
for (int ii=0; ii<count; ii++) {
flags.getComponent(ii).setEnabled(false);
}
JPanel fileView = new JPanel(new BorderLayout(3,3));
fileView.add(toolBar,BorderLayout.NORTH);
fileView.add(fileMainDetails,BorderLayout.CENTER);
detailView.add(fileView, BorderLayout.SOUTH);
JSplitPane splitPane = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
treeScroll,
detailView);
gui.add(splitPane, BorderLayout.CENTER);
JPanel simpleOutput = new JPanel(new BorderLayout(3,3));
progressBar = new JProgressBar();
simpleOutput.add(progressBar, BorderLayout.EAST);
progressBar.setVisible(false);
gui.add(simpleOutput, BorderLayout.SOUTH);
}
return gui;
}
public void showRootFile() {
// ensure the main files are displayed
tree.setSelectionInterval(0,0);
}
private TreePath findTreePath(File find) {
for (int ii=0; ii<tree.getRowCount(); ii++) {
TreePath treePath = tree.getPathForRow(ii);
Object object = treePath.getLastPathComponent();
DefaultMutableTreeNode node = (DefaultMutableTreeNode)object;
File nodeFile = (File)node.getUserObject();
if (nodeFile==find) {
return treePath;
}
}
// not found!
return null;
}
private void showErrorMessage(String errorMessage, String errorTitle) {
JOptionPane.showMessageDialog(
gui,
errorMessage,
errorTitle,
JOptionPane.ERROR_MESSAGE
);
}
private void showThrowable(Throwable t) {
t.printStackTrace();
JOptionPane.showMessageDialog(
gui,
t.toString(),
t.getMessage(),
JOptionPane.ERROR_MESSAGE
);
gui.repaint();
}
/** Update the table on the EDT */
private void setTableData(final File[] files) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (fileTableModel==null) {
fileTableModel = new FileTableModel();
table.setModel(fileTableModel);
}
table.getSelectionModel().removeListSelectionListener(listSelectionListener);
fileTableModel.setFiles(files);
table.getSelectionModel().addListSelectionListener(listSelectionListener);
if (!cellSizesSet) {
Icon icon = fileSystemView.getSystemIcon(files[0]);
// size adjustment to better account for icons
table.setRowHeight( icon.getIconHeight()+rowIconPadding );
setColumnWidth(0,-1);
setColumnWidth(3,60);
table.getColumnModel().getColumn(3).setMaxWidth(120);
setColumnWidth(4,-1);
setColumnWidth(5,-1);
setColumnWidth(6,-1);
setColumnWidth(7,-1);
setColumnWidth(8,-1);
setColumnWidth(9,-1);
cellSizesSet = true;
}
}
});
}
private void setColumnWidth(int column, int width) {
TableColumn tableColumn = table.getColumnModel().getColumn(column);
if (width<0) {
// use the preferred width of the header..
JLabel label = new JLabel( (String)tableColumn.getHeaderValue() );
Dimension preferred = label.getPreferredSize();
// altered 10->14 as per camickr comment.
width = (int)preferred.getWidth()+14;
}
tableColumn.setPreferredWidth(width);
tableColumn.setMaxWidth(width);
tableColumn.setMinWidth(width);
}
/** Add the files that are contained within the directory of this node.
Thanks to Hovercraft Full Of Eels for the SwingWorker fix. */
private void showChildren(final DefaultMutableTreeNode node) {
tree.setEnabled(false);
progressBar.setVisible(true);
progressBar.setIndeterminate(true);
SwingWorker<Void, File> worker = new SwingWorker<Void, File>() {
@Override
public Void doInBackground() {
File file = (File) node.getUserObject();
if (file.isDirectory()) {
File[] files = fileSystemView.getFiles(file, true); //!!
if (node.isLeaf()) {
for (File child : files) {
if (child.isDirectory()) {
publish(child);
}
}
}
setTableData(files);
}
return null;
}
@Override
protected void process(List<File> chunks) {
for (File child : chunks) {
node.add(new DefaultMutableTreeNode(child));
}
}
@Override
protected void done() {
progressBar.setIndeterminate(false);
progressBar.setVisible(false);
tree.setEnabled(true);
}
};
worker.execute();
}
/** Update the File details view with the details of this File. */
private void setFileDetails(File file) {
currentFile = file;
Icon icon = fileSystemView.getSystemIcon(file);
fileName.setIcon(icon);
fileName.setText(fileSystemView.getSystemDisplayName(file));
path.setText(file.getPath());
date.setText(new Date(file.lastModified()).toString());
size.setText(file.length() + " bytes");
readable.setSelected(file.canRead());
writable.setSelected(file.canWrite());
executable.setSelected(file.canExecute());
isDirectory.setSelected(file.isDirectory());
isFile.setSelected(file.isFile());
JFrame f = (JFrame)gui.getTopLevelAncestor();
if (f!=null) {
f.setTitle(
APP_TITLE +
" :: " +
fileSystemView.getSystemDisplayName(file) );
}
gui.repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// Significantly improves the look of the output in
// terms of the file names returned by FileSystemView!
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception weTried) {
}
JFrame f = new JFrame(APP_TITLE);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FileBrowser FileBrowser = new FileBrowser();
f.setContentPane(FileBrowser.getGui());
try {
URL urlBig = FileBrowser.getClass().getResource("fb-icon-32x32.png");
URL urlSmall = FileBrowser.getClass().getResource("fb-icon-16x16.png");
ArrayList<Image> images = new ArrayList<Image>();
images.add( ImageIO.read(urlBig) );
images.add( ImageIO.read(urlSmall) );
f.setIconImages(images);
} catch(Exception weTried) {}
f.pack();
f.setLocationByPlatform(true);
f.setMinimumSize(f.getSize());
f.setVisible(true);
FileBrowser.showRootFile();
}
});
}
}
/** A TableModel to hold File[]. */
class FileTableModel extends AbstractTableModel {
private File[] files;
private FileSystemView fileSystemView = FileSystemView.getFileSystemView();
private String[] columns = {
"Icon",
"File",
"Path/name",
"Size",
"Last Modified",
"R",
"W",
"E",
"D",
"F",
};
FileTableModel() {
this(new File[0]);
}
FileTableModel(File[] files) {
this.files = files;
}
public Object getValueAt(int row, int column) {
File file = files[row];
switch (column) {
case 0:
return fileSystemView.getSystemIcon(file);
case 1:
return fileSystemView.getSystemDisplayName(file);
case 2:
return file.getPath();
case 3:
return file.length();
case 4:
return file.lastModified();
case 5:
return file.canRead();
case 6:
return file.canWrite();
case 7:
return file.canExecute();
case 8:
return file.isDirectory();
case 9:
return file.isFile();
default:
System.err.println("Logic Error");
}
return "";
}
public int getColumnCount() {
return columns.length;
}
public Class<?> getColumnClass(int column) {
switch (column) {
case 0:
return ImageIcon.class;
case 3:
return Long.class;
case 4:
return Date.class;
case 5:
case 6:
case 7:
case 8:
case 9:
return Boolean.class;
}
return String.class;
}
public String getColumnName(int column) {
return columns[column];
}
public int getRowCount() {
return files.length;
}
public File getFile(int row) {
return files[row];
}
public void setFiles(File[] files) {
this.files = files;
fireTableDataChanged();
}
}
/** A TreeCellRenderer for a File. */
class FileTreeCellRenderer extends DefaultTreeCellRenderer {
private FileSystemView fileSystemView;
private JLabel label;
FileTreeCellRenderer() {
label = new JLabel();
label.setOpaque(true);
fileSystemView = FileSystemView.getFileSystemView();
}
@Override
public Component getTreeCellRendererComponent(
JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
File file = (File)node.getUserObject();
label.setIcon(fileSystemView.getSystemIcon(file));
label.setText(fileSystemView.getSystemDisplayName(file));
label.setToolTipText(file.getPath());
if (selected) {
label.setBackground(backgroundSelectionColor);
label.setForeground(textSelectionColor);
} else {
label.setBackground(backgroundNonSelectionColor);
label.setForeground(textNonSelectionColor);
}
return label;
}
}
Исходный код FileBrowser.java
составляет около 650 строк.
Также
- Вот два значка для приложения. (но без них он будет работать нормально). Они будут использоваться, если они будут найдены в том же каталоге, что и классы. Значки используют
Color.WHITE
для представления невыбранных строк таблицы и amp; узлы дерева, поэтому они невидимы на веб-странице с белым фоном. ;)-
fb-icon-16x16.png
-
fb-icon-32x32.png
-
- Любые другие комментарии /советы /предложения приветствуются.
6 ответов
Этот код касается меня:
SwingWorker worker = new SwingWorker() {
@Override
public String doInBackground() {
tree.setEnabled(false);
progressBar.setVisible(true);
progressBar.setIndeterminate(true);
File file = (File) node.getUserObject();
if (file.isDirectory()) {
File[] files = fileSystemView.getFiles(file, true);
if (node.isLeaf()) {
for (File child : files) {
if (child.isDirectory()) {
node.add(new DefaultMutableTreeNode(child));
}
}
}
setTableData(files);
}
progressBar.setIndeterminate(false);
progressBar.setVisible(false);
tree.setEnabled(true);
return "done";
}
};
worker.execute();
, когда вы вызываете вызовы Swing на JProgressBar с EDT. Лучше всего запустить индикатор выполнения перед SwingWorker и завершить его в готовом методе. Либо это, либо добавить PropertyChangeListener в SwingWorker и завершить индикатор выполнения, когда свойство состояния рабочего объекта StateValue.DONE.
Еще одна проблема заключается в том, что вы используете DefaultMutableTreeNode и для API, при использовании этого следует учитывать необходимость параллелизма, поскольку вы, кажется, используете это более чем в одном потоке:
Это не потокобезопасный класс. Если вы намерены использовать DefaultMutableTreeNode (или дерево TreeNodes) в нескольких потоках, вам необходимо выполнить свою собственную синхронизацию. Хорошее соглашение для принятия - синхронизация на корневом узле дерева.
ИЗМЕНИТЬ
Один из способов возможно получить DefaultMutableTreeNode по крайней мере из уравнения состоит в том, чтобы добавлять узлы к нему только в одном потоке, EDT, используя публикацию /процесс SwingWorker. Например:
private void showChildren(final DefaultMutableTreeNode node) {
tree.setEnabled(false);
progressBar.setVisible(true);
progressBar.setIndeterminate(true);
SwingWorker<Void, File> worker = new SwingWorker<Void, File>() {
@Override
public Void doInBackground() {
File file = (File) node.getUserObject();
if (file.isDirectory()) {
File[] files = fileSystemView.getFiles(file, true); //!!
if (node.isLeaf()) {
for (File child : files) {
if (child.isDirectory()) {
publish(child);
}
}
}
setTableData(files);
}
return null;
}
@Override
protected void process(List<File> chunks) {
for (File child : chunks) {
node.add(new DefaultMutableTreeNode(child));
}
}
@Override
protected void done() {
progressBar.setIndeterminate(false);
progressBar.setVisible(false);
tree.setEnabled(true);
}
};
worker.execute();
}
Mac OS X 10.5 и Ubuntu 10.04: Повторный нефатальный NPE при удалении.
Приложение: В строке 441 findTreePath()
всегда возвращает null
, потому что currentFile.getParentFile()
возвращает путь к файлу, а не сам файл. По сути, путь не может соответствовать самому себе. Я не вижу очевидной работы.
Приложение: Я не могу найти приемлемое решение. Я подозреваю, что проблема связана с дихотомией между представлением (JTree
) и моделью (DefaultTreeModel
). В частности, findTreePath()
выполняет поиск среди видимых строк, а не узлов модели дерева. Может оказаться полезным реализовать TreeModel
явно, как показано в этом FileTreeModel
. Это также может помочь абстрактным методам преобразования модели представления, аналогичным тем, которые предоставляются JTable
.
В качестве альтернативного подхода рассмотрим org.netbeans.swing.outline.Outline
, как показано на рисунке ниже. Он принимает FileTreeModel
, упомянутых ранее. Расширяя JTable
, он использует знакомый схему рендеринга и редактора через удобный RowModel
, аналогичный TableModel
. Самое главное, что он предоставляет convertRowIndexToModel()
, а также convertRowIndexToView()
. JTable
метод getValueAt()
переопределяется для вызова convertRowIndexToModel()
, создавая прослушиватель выбора в этом пример . JAR независим, мал (~ 230 KiB) и стабилен; он может быть найден в дистрибутиве NetBeans:
NetBeans/platform/modules/org-netbeans-swing-outline.jar
Приложение: FileBrowser
версия 2011-06-08 работает правильно в обеих системах, указанных выше.
currentFile: /temp.txt parentPath: null java.lang.NullPointerException в FileManager.deleteFile (FileManager.java:443) в FileManager.access $ 1000 (FileManager.java:56) в FileManager $ 9.actionPerformed (FileManager.java306) в javax.swing.AbstractButton.fireActionPerformed (AbstractButton.java:2028) в javax.swing.AbstractButton $ Handler.actionPerformed (AbstractButton.java:2351) в javax.swing.DefaultButtonModel.fireActionPerformed (DefaultButtonModel.java:387) в javax.swing.DefaultButtonModel.setPressed (DefaultButtonModel.java:242) в javax.swing.plaf.basic.BasicButtonListener.mouseReleased (BasicButtonListener.java:236) в java.awt.Component.processMouseEvent (Component.java:6374) в javax.swing.JComponent.processMouseEvent (JComponent.java:3267) в java.awt.Component.processEvent (Component.java:6139) в java.awt.Container.processEvent (Container.java:2085) в java.awt.Component.dispatchEventImpl (Component.java:4736) в java.awt.Container.dispatchEventImpl (Container.java:2143) в java.awt.Component.dispatchEvent (Component.java:4566) at java.awt.LightweightDispatcher.retargetMouseEvent (Container.java:4621) в java.awt.LightweightDispatcher.processMouseEvent (Container.java:4282) at java.awt.LightweightDispatcher.dispatchEvent (Container.java:4212) в java.awt.Container.dispatchEventImpl (Container.java:2129) в java.awt.Window.dispatchEventImpl (Window.java:2478) в java.awt.Component.dispatchEvent (Component.java:4566) вjava.awt.EventQueue.dispatchEventImpl (EventQueue.java:680) в java.awt.EventQueue.access $ 000 (EventQueue.java:86) at java.awt.EventQueue $ 1.run (EventQueue.java:639) at java.awt.EventQueue $ 1.run (EventQueue.java:637) в java.security.AccessController.doPrivileged (собственный метод) в java.security.AccessControlContext $ 1.doIntersectionPrivilege (AccessControlContext.java:87) в java.security.AccessControlContext $ 1.doIntersectionPrivilege (AccessControlContext.java:98) в java.awt.EventQueue $ 2.run (EventQueue.java:653) в java.awt.EventQueue $ 2.run (EventQueue.java:651) в java.security.AccessController.doPrivileged (собственный метод) в java.security.AccessControlContext $ 1.doIntersectionPrivilege (AccessControlContext.java:87) в java.awt.EventQueue.dispatchEvent (EventQueue.java:650) в java.awt.EventDispatchThread.pumpOneEventForFilters (EventDispatchThread.java:296) в java.awt.EventDispatchThread.pumpEventsForFilter (EventDispatchThread.java:211) в java.awt.EventDispatchThread.pumpEventsForHierarchy (EventDispatchThread.java:201) в java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:196) в java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:188) в java.awt.EventDispatchThread.run (EventDispatchThread.java:122)
Это последнее редактирование кода, прежде чем я решил превратить FileMan (ager) в FileBro (wser) ( FileBro подходит к редактированию исходного вопроса, «сейчас скоро») , Эта версия FileMan размещена здесь в интересах людей, которые хотят расширить этот (очень неполный, багги) код в этом направлении.
FileManager.java
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Container;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.swing.table.*;
import javax.swing.filechooser.FileSystemView;
import javax.imageio.ImageIO;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.io.*;
import java.nio.channels.FileChannel;
import java.net.URL;
/**
A basic File Manager. Requires 1.6+ for the Desktop & SwingWorker
classes, amongst other minor things.
Includes support classes FileTableModel & FileTreeCellRenderer.
@TODO Bugs
<li>Still throws occasional AIOOBEs and NPEs, so some update on
the EDT must have been missed.
<li>Fix keyboard focus issues - especially when functions like
rename/delete etc. are called that update nodes & file lists.
<li>Needs more testing in general.
@TODO Functionality
<li>Implement Read/Write/Execute checkboxes
<li>Implement Copy
<li>Extra prompt for directory delete (camickr suggestion)
<li>Add File/Directory fields to FileTableModel
<li>Double clicking a directory in the table, should update the tree
<li>Move progress bar?
<li>Add other file display modes (besides table) in CardLayout?
<li>Menus + other cruft?
<li>Implement history/back
<li>Allow multiple selection
<li>Add file search
@author Andrew Thompson
@version 2011-06-01
@see http://stackoverflow.com/questions/6182110
@license LGPL
*/
class FileManager {
/** Title of the application */
public static final String APP_TITLE = "FileMan";
/** Used to open/edit/print files. */
private Desktop desktop;
/** Provides nice icons and names for files. */
private FileSystemView fileSystemView;
/** currently selected File. */
private File currentFile;
/** Main GUI container */
private JPanel gui;
/** File-system tree. Built Lazily */
private JTree tree;
private DefaultTreeModel treeModel;
/** Directory listing */
private JTable table;
private JProgressBar progressBar;
/** Table model for File[]. */
private FileTableModel fileTableModel;
private ListSelectionListener listSelectionListener;
private boolean cellSizesSet = false;
private int rowIconPadding = 6;
/* File controls. */
private JButton openFile;
private JButton printFile;
private JButton editFile;
private JButton deleteFile;
private JButton newFile;
private JButton copyFile;
/* File details. */
private JLabel fileName;
private JTextField path;
private JLabel date;
private JLabel size;
private JCheckBox readable;
private JCheckBox writable;
private JCheckBox executable;
private JRadioButton isDirectory;
private JRadioButton isFile;
/* GUI options/containers for new File/Directory creation. Created lazily. */
private JPanel newFilePanel;
private JRadioButton newTypeFile;
private JTextField name;
public Container getGui() {
if (gui==null) {
gui = new JPanel(new BorderLayout(3,3));
gui.setBorder(new EmptyBorder(5,5,5,5));
fileSystemView = FileSystemView.getFileSystemView();
desktop = Desktop.getDesktop();
JPanel detailView = new JPanel(new BorderLayout(3,3));
//fileTableModel = new FileTableModel();
table = new JTable();
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setAutoCreateRowSorter(true);
table.setShowVerticalLines(false);
listSelectionListener = new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent lse) {
int row = table.getSelectionModel().getLeadSelectionIndex();
setFileDetails( ((FileTableModel)table.getModel()).getFile(row) );
}
};
table.getSelectionModel().addListSelectionListener(listSelectionListener);
JScrollPane tableScroll = new JScrollPane(table);
Dimension d = tableScroll.getPreferredSize();
tableScroll.setPreferredSize(new Dimension((int)d.getWidth(), (int)d.getHeight()/2));
detailView.add(tableScroll, BorderLayout.CENTER);
// the File tree
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
treeModel = new DefaultTreeModel(root);
TreeSelectionListener treeSelectionListener = new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent tse){
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)tse.getPath().getLastPathComponent();
showChildren(node);
setFileDetails((File)node.getUserObject());
}
};
// show the file system roots.
File[] roots = fileSystemView.getRoots();
for (File fileSystemRoot : roots) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(fileSystemRoot);
root.add( node );
//showChildren(node);
//
File[] files = fileSystemView.getFiles(fileSystemRoot, true);
for (File file : files) {
if (file.isDirectory()) {
node.add(new DefaultMutableTreeNode(file));
}
}
//
}
tree = new JTree(treeModel);
tree.setRootVisible(false);
tree.addTreeSelectionListener(treeSelectionListener);
tree.setCellRenderer(new FileTreeCellRenderer());
tree.expandRow(0);
JScrollPane treeScroll = new JScrollPane(tree);
// as per trashgod tip
tree.setVisibleRowCount(15);
Dimension preferredSize = treeScroll.getPreferredSize();
Dimension widePreferred = new Dimension(
200,
(int)preferredSize.getHeight());
treeScroll.setPreferredSize( widePreferred );
// details for a File
JPanel fileMainDetails = new JPanel(new BorderLayout(4,2));
fileMainDetails.setBorder(new EmptyBorder(0,6,0,6));
JPanel fileDetailsLabels = new JPanel(new GridLayout(0,1,2,2));
fileMainDetails.add(fileDetailsLabels, BorderLayout.WEST);
JPanel fileDetailsValues = new JPanel(new GridLayout(0,1,2,2));
fileMainDetails.add(fileDetailsValues, BorderLayout.CENTER);
fileDetailsLabels.add(new JLabel("File", JLabel.TRAILING));
fileName = new JLabel();
fileDetailsValues.add(fileName);
fileDetailsLabels.add(new JLabel("Path/name", JLabel.TRAILING));
path = new JTextField(5);
path.setEditable(false);
fileDetailsValues.add(path);
fileDetailsLabels.add(new JLabel("Last Modified", JLabel.TRAILING));
date = new JLabel();
fileDetailsValues.add(date);
fileDetailsLabels.add(new JLabel("File size", JLabel.TRAILING));
size = new JLabel();
fileDetailsValues.add(size);
fileDetailsLabels.add(new JLabel("Type", JLabel.TRAILING));
JPanel flags = new JPanel(new FlowLayout(FlowLayout.LEADING,4,0));
isDirectory = new JRadioButton("Directory");
isDirectory.setEnabled(false);
flags.add(isDirectory);
isFile = new JRadioButton("File");
isFile.setEnabled(false);
flags.add(isFile);
fileDetailsValues.add(flags);
int count = fileDetailsLabels.getComponentCount();
for (int ii=0; ii<count; ii++) {
fileDetailsLabels.getComponent(ii).setEnabled(false);
}
JToolBar toolBar = new JToolBar();
// mnemonics stop working in a floated toolbar
toolBar.setFloatable(false);
openFile = new JButton("Open");
openFile.setMnemonic('o');
openFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
try {
desktop.open(currentFile);
} catch(Throwable t) {
showThrowable(t);
}
gui.repaint();
}
});
toolBar.add(openFile);
editFile = new JButton("Edit");
editFile.setMnemonic('e');
editFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
try {
desktop.edit(currentFile);
} catch(Throwable t) {
showThrowable(t);
}
}
});
toolBar.add(editFile);
printFile = new JButton("Print");
printFile.setMnemonic('p');
printFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
try {
desktop.print(currentFile);
} catch(Throwable t) {
showThrowable(t);
}
}
});
toolBar.add(printFile);
// Check the actions are supported on this platform!
openFile.setEnabled(desktop.isSupported(Desktop.Action.OPEN));
editFile.setEnabled(desktop.isSupported(Desktop.Action.EDIT));
printFile.setEnabled(desktop.isSupported(Desktop.Action.PRINT));
toolBar.addSeparator();
newFile = new JButton("New");
newFile.setMnemonic('n');
newFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
newFile();
}
});
toolBar.add(newFile);
copyFile = new JButton("Copy");
copyFile.setMnemonic('c');
copyFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
showErrorMessage("'Copy' not implemented.", "Not implemented.");
}
});
toolBar.add(copyFile);
JButton renameFile = new JButton("Rename");
renameFile.setMnemonic('r');
renameFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
renameFile();
}
});
toolBar.add(renameFile);
deleteFile = new JButton("Delete");
deleteFile.setMnemonic('d');
deleteFile.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
deleteFile();
}
});
toolBar.add(deleteFile);
toolBar.addSeparator();
readable = new JCheckBox("Read ");
readable.setMnemonic('a');
//readable.setEnabled(false);
toolBar.add(readable);
writable = new JCheckBox("Write ");
writable.setMnemonic('w');
//writable.setEnabled(false);
toolBar.add(writable);
executable = new JCheckBox("Execute");
executable.setMnemonic('x');
//executable.setEnabled(false);
toolBar.add(executable);
JPanel fileView = new JPanel(new BorderLayout(3,3));
fileView.add(toolBar,BorderLayout.NORTH);
fileView.add(fileMainDetails,BorderLayout.CENTER);
detailView.add(fileView, BorderLayout.SOUTH);
JSplitPane splitPane = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
treeScroll,
detailView);
gui.add(splitPane, BorderLayout.CENTER);
JPanel simpleOutput = new JPanel(new BorderLayout(3,3));
progressBar = new JProgressBar();
simpleOutput.add(progressBar, BorderLayout.EAST);
progressBar.setVisible(false);
gui.add(simpleOutput, BorderLayout.SOUTH);
}
return gui;
}
public void showRootFile() {
// ensure the main files are displayed
tree.setSelectionInterval(0,0);
}
private TreePath findTreePath(File find) {
for (int ii=0; ii<tree.getRowCount(); ii++) {
TreePath treePath = tree.getPathForRow(ii);
Object object = treePath.getLastPathComponent();
DefaultMutableTreeNode node = (DefaultMutableTreeNode)object;
File nodeFile = (File)node.getUserObject();
if (nodeFile==find) {
return treePath;
}
}
// not found!
return null;
}
private void renameFile() {
if (currentFile==null) {
showErrorMessage("No file selected to rename.","Select File");
return;
}
String renameTo = JOptionPane.showInputDialog(gui, "New Name");
if (renameTo!=null) {
try {
boolean directory = currentFile.isDirectory();
TreePath parentPath = findTreePath(currentFile.getParentFile());
DefaultMutableTreeNode parentNode =
(DefaultMutableTreeNode)parentPath.getLastPathComponent();
boolean renamed = currentFile.renameTo(new File(
currentFile.getParentFile(), renameTo));
if (renamed) {
if (directory) {
// rename the node..
// delete the current node..
TreePath currentPath = findTreePath(currentFile);
System.out.println(currentPath);
DefaultMutableTreeNode currentNode =
(DefaultMutableTreeNode)currentPath.getLastPathComponent();
treeModel.removeNodeFromParent(currentNode);
// add a new node..
}
showChildren(parentNode);
} else {
String msg = "The file '" +
currentFile +
"' could not be renamed.";
showErrorMessage(msg,"Rename Failed");
}
} catch(Throwable t) {
showThrowable(t);
}
}
gui.repaint();
}
private void deleteFile() {
if (currentFile==null) {
showErrorMessage("No file selected for deletion.","Select File");
return;
}
int result = JOptionPane.showConfirmDialog(
gui,
"Are you sure you want to delete this file?",
"Delete File",
JOptionPane.ERROR_MESSAGE
);
if (result==JOptionPane.OK_OPTION) {
try {
System.out.println("currentFile: " + currentFile);
TreePath parentPath = findTreePath(currentFile.getParentFile());
System.out.println("parentPath: " + parentPath);
DefaultMutableTreeNode parentNode =
(DefaultMutableTreeNode)parentPath.getLastPathComponent();
System.out.println("parentNode: " + parentNode);
boolean directory = currentFile.isDirectory();
boolean deleted = currentFile.delete();
if (deleted) {
if (directory) {
// delete the node..
TreePath currentPath = findTreePath(currentFile);
System.out.println(currentPath);
DefaultMutableTreeNode currentNode =
(DefaultMutableTreeNode)currentPath.getLastPathComponent();
treeModel.removeNodeFromParent(currentNode);
}
showChildren(parentNode);
} else {
String msg = "The file '" +
currentFile +
"' could not be deleted.";
showErrorMessage(msg,"Delete Failed");
}
} catch(Throwable t) {
showThrowable(t);
}
}
gui.repaint();
}
private void newFile() {
if (currentFile==null) {
showErrorMessage("No location selected for new file.","Select Location");
return;
}
if (newFilePanel==null) {
newFilePanel = new JPanel(new BorderLayout(3,3));
JPanel southRadio = new JPanel(new GridLayout(1,0,2,2));
newTypeFile = new JRadioButton("File", true);
JRadioButton newTypeDirectory = new JRadioButton("Directory");
ButtonGroup bg = new ButtonGroup();
bg.add(newTypeFile);
bg.add(newTypeDirectory);
southRadio.add( newTypeFile );
southRadio.add( newTypeDirectory );
name = new JTextField(15);
newFilePanel.add( new JLabel("Name"), BorderLayout.WEST );
newFilePanel.add( name );
newFilePanel.add( southRadio, BorderLayout.SOUTH );
}
int result = JOptionPane.showConfirmDialog(
gui,
newFilePanel,
"Create File",
JOptionPane.OK_CANCEL_OPTION);
if (result==JOptionPane.OK_OPTION) {
try {
boolean created;
File parentFile = currentFile;
if (!parentFile.isDirectory()) {
parentFile = parentFile.getParentFile();
}
File file = new File( parentFile, name.getText() );
if (newTypeFile.isSelected()) {
created = file.createNewFile();
} else {
created = file.mkdir();
}
if (created) {
TreePath parentPath = findTreePath(parentFile);
DefaultMutableTreeNode parentNode =
(DefaultMutableTreeNode)parentPath.getLastPathComponent();
if (file.isDirectory()) {
// add the new node..
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(file);
TreePath currentPath = findTreePath(currentFile);
DefaultMutableTreeNode currentNode =
(DefaultMutableTreeNode)currentPath.getLastPathComponent();
treeModel.insertNodeInto(newNode, parentNode, parentNode.getChildCount());
}
showChildren(parentNode);
} else {
String msg = "The file '" +
file +
"' could not be created.";
showErrorMessage(msg, "Create Failed");
}
} catch(Throwable t) {
showThrowable(t);
}
}
gui.repaint();
}
private void showErrorMessage(String errorMessage, String errorTitle) {
JOptionPane.showMessageDialog(
gui,
errorMessage,
errorTitle,
JOptionPane.ERROR_MESSAGE
);
}
private void showThrowable(Throwable t) {
t.printStackTrace();
JOptionPane.showMessageDialog(
gui,
t.toString(),
t.getMessage(),
JOptionPane.ERROR_MESSAGE
);
gui.repaint();
}
/** Update the table on the EDT */
private void setTableData(final File[] files) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (fileTableModel==null) {
fileTableModel = new FileTableModel();
table.setModel(fileTableModel);
}
table.getSelectionModel().removeListSelectionListener(listSelectionListener);
fileTableModel.setFiles(files);
table.getSelectionModel().addListSelectionListener(listSelectionListener);
if (!cellSizesSet) {
Icon icon = fileSystemView.getSystemIcon(files[0]);
// size adjustment to better account for icons
table.setRowHeight( icon.getIconHeight()+rowIconPadding );
setColumnWidth(0,-1);
setColumnWidth(3,60);
table.getColumnModel().getColumn(3).setMaxWidth(120);
setColumnWidth(4,-1);
setColumnWidth(5,-1);
setColumnWidth(6,-1);
setColumnWidth(7,-1);
setColumnWidth(8,-1);
setColumnWidth(9,-1);
cellSizesSet = true;
}
}
});
}
private void setColumnWidth(int column, int width) {
TableColumn tableColumn = table.getColumnModel().getColumn(column);
if (width<0) {
// use the preferred width of the header..
JLabel label = new JLabel( (String)tableColumn.getHeaderValue() );
Dimension preferred = label.getPreferredSize();
// altered 10->14 as per camickr comment.
width = (int)preferred.getWidth()+14;
}
tableColumn.setPreferredWidth(width);
tableColumn.setMaxWidth(width);
tableColumn.setMinWidth(width);
}
/** Add the files that are contained within the directory of this node.
Thanks to Hovercraft Full Of Eels. */
private void showChildren(final DefaultMutableTreeNode node) {
tree.setEnabled(false);
progressBar.setVisible(true);
progressBar.setIndeterminate(true);
SwingWorker<Void, File> worker = new SwingWorker<Void, File>() {
@Override
public Void doInBackground() {
File file = (File) node.getUserObject();
if (file.isDirectory()) {
File[] files = fileSystemView.getFiles(file, true); //!!
if (node.isLeaf()) {
for (File child : files) {
if (child.isDirectory()) {
publish(child);
}
}
}
setTableData(files);
}
return null;
}
@Override
protected void process(List<File> chunks) {
for (File child : chunks) {
node.add(new DefaultMutableTreeNode(child));
}
}
@Override
protected void done() {
progressBar.setIndeterminate(false);
progressBar.setVisible(false);
tree.setEnabled(true);
}
};
worker.execute();
}
/** Update the File details view with the details of this File. */
private void setFileDetails(File file) {
currentFile = file;
Icon icon = fileSystemView.getSystemIcon(file);
fileName.setIcon(icon);
fileName.setText(fileSystemView.getSystemDisplayName(file));
path.setText(file.getPath());
date.setText(new Date(file.lastModified()).toString());
size.setText(file.length() + " bytes");
readable.setSelected(file.canRead());
writable.setSelected(file.canWrite());
executable.setSelected(file.canExecute());
isDirectory.setSelected(file.isDirectory());
isFile.setSelected(file.isFile());
JFrame f = (JFrame)gui.getTopLevelAncestor();
if (f!=null) {
f.setTitle(
APP_TITLE +
" :: " +
fileSystemView.getSystemDisplayName(file) );
}
gui.repaint();
}
public static boolean copyFile(File from, File to) throws IOException {
boolean created = to.createNewFile();
if (created) {
FileChannel fromChannel = null;
FileChannel toChannel = null;
try {
fromChannel = new FileInputStream(from).getChannel();
toChannel = new FileOutputStream(to).getChannel();
toChannel.transferFrom(fromChannel, 0, fromChannel.size());
// set the flags of the to the same as the from
to.setReadable(from.canRead());
to.setWritable(from.canWrite());
to.setExecutable(from.canExecute());
} finally {
if (fromChannel != null) {
fromChannel.close();
}
if (toChannel != null) {
toChannel.close();
}
return false;
}
}
return created;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// Significantly improves the look of the output in
// terms of the file names returned by FileSystemView!
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception weTried) {
}
JFrame f = new JFrame(APP_TITLE);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FileManager fileManager = new FileManager();
f.setContentPane(fileManager.getGui());
try {
URL urlBig = fileManager.getClass().getResource("fm-icon-32x32.png");
URL urlSmall = fileManager.getClass().getResource("fm-icon-16x16.png");
ArrayList<Image> images = new ArrayList<Image>();
images.add( ImageIO.read(urlBig) );
images.add( ImageIO.read(urlSmall) );
f.setIconImages(images);
} catch(Exception weTried) {}
f.pack();
f.setLocationByPlatform(true);
f.setMinimumSize(f.getSize());
f.setVisible(true);
fileManager.showRootFile();
}
});
}
}
/** A TableModel to hold File[]. */
class FileTableModel extends AbstractTableModel {
private File[] files;
private FileSystemView fileSystemView = FileSystemView.getFileSystemView();
private String[] columns = {
"Icon",
"File",
"Path/name",
"Size",
"Last Modified",
"R",
"W",
"E",
"D",
"F",
};
FileTableModel() {
this(new File[0]);
}
FileTableModel(File[] files) {
this.files = files;
}
public Object getValueAt(int row, int column) {
File file = files[row];
switch (column) {
case 0:
return fileSystemView.getSystemIcon(file);
case 1:
return fileSystemView.getSystemDisplayName(file);
case 2:
return file.getPath();
case 3:
return file.length();
case 4:
return file.lastModified();
case 5:
return file.canRead();
case 6:
return file.canWrite();
case 7:
return file.canExecute();
case 8:
return file.isDirectory();
case 9:
return file.isFile();
default:
System.err.println("Logic Error");
}
return "";
}
public int getColumnCount() {
return columns.length;
}
public Class<?> getColumnClass(int column) {
switch (column) {
case 0:
return ImageIcon.class;
case 3:
return Long.class;
case 4:
return Date.class;
case 5:
case 6:
case 7:
case 8:
case 9:
return Boolean.class;
}
return String.class;
}
public String getColumnName(int column) {
return columns[column];
}
public int getRowCount() {
return files.length;
}
public File getFile(int row) {
return files[row];
}
public void setFiles(File[] files) {
this.files = files;
fireTableDataChanged();
}
}
/** A TreeCellRenderer for a File. */
class FileTreeCellRenderer extends DefaultTreeCellRenderer {
private FileSystemView fileSystemView;
private JLabel label;
FileTreeCellRenderer() {
label = new JLabel();
label.setOpaque(true);
fileSystemView = FileSystemView.getFileSystemView();
}
@Override
public Component getTreeCellRendererComponent(
JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
File file = (File)node.getUserObject();
label.setIcon(fileSystemView.getSystemIcon(file));
label.setText(fileSystemView.getSystemDisplayName(file));
label.setToolTipText(file.getPath());
if (selected) {
label.setBackground(backgroundSelectionColor);
label.setForeground(textSelectionColor);
} else {
label.setBackground(backgroundNonSelectionColor);
label.setForeground(textNonSelectionColor);
}
return label;
}
}
Добавление
В конце концов я решил, что мне не нужно ничего больше, чем файловый браузер. Когда дело доходило до управления файлами, просто открыв родительский каталог & использование встроенной функциональности ОС для создания новых файлов, их переименования, копирования или удаления, а также изменения флагов.
Это не только избавило от многих из оставшихся ошибок, но и устранило любую возможность, что адвокаты пользователя могут связаться со мной о том, как мое программное обеспечение удалило все в локальной сети своего клиента, когда они попросили его удалить символическую ссылку. ;)
Эндрю, это выглядит хорошо. Я использую XP и JDK6.07 и не имел никаких проблем при просмотре или использовании функций «Новый /Удалить /Переименовать».
Несколько комментариев:
-
Заголовок значка и все заголовки галочки усечены и отображаются «...». Для меня это было исправлено:
// width = (int)preferred.getWidth()+10; width = (int)preferred.getWidth()+14;
-
Я обнаружил, что чтение текста выбранного узла затруднено. Это черный текст на синем фоне. JTable было намного легче читать с белым текстом на синем фоне. Я вижу, что рендеринг дерева по умолчанию также поддерживает цвета выбора текста /не выбора.
-
Если вы действительно хотите дать пользователям взломать свой доллар, то, возможно, при удалении каталога вы можете попросить пользователя увидеть, хотите ли они удалить все файлы перед удалением каталога.
Существует ошибка: при сортировке таблицы сопоставление между индексом строки модели выбора и индексом строки модели таблицы прерывается, а кнопки в конечном итоге работают с другими файлами.
ListSelectionListener
должен исправить следующее:
int row = table.getSelectionModel().getLeadSelectionIndex();
RowSorter sorter = table.getRowSorter();
if ( sorter != null ) {
row = sorter.convertRowIndexToModel( row );
}
Он работает на WinXP и Win2008_64b (JRE64b), и все выглядит правильно. Для самых глубоких тестов просто нажмите на каждый доступный Component
s без ошибок.
Но, как я уже упоминал, выбор в сети повесил (на WinXP, никаких ошибок, убитых из IDE), JProgresBar тоже, в отличие от win2008, работает ProgresBar и просто кратковременно и diplay правильное содержимое сетевого дерева.
Мое предположение о демона Active Directory (следующий только AD) заключается в том, что у меня есть локальный доступ администратора для Intranet, и он выглядит как от ПК конечного пользователя локальной учетной записью администратора AD. *** ограниченное представление для дерева сети, AD *** управлял этим доступом, и я не могу получить доступ к настройкам AD, потому что это аутсорсинговая служба.
-
WinXP SP3 SK LanguagePack (это не полные локализации, такие как Испания, Германия, ...), зарегистрированный здесь локальной учетной записью администратора. Нажмите значок «Сеть» в JTree, умер без ошибок, как я описал дважды.
-
Win7 64b SK (жаль нет времени для самых глубоких анализов, только регистрируется здесь локальной учетной записью администратора и запускает вашего ребенка из консоли). icon в JTree возвращает null в JTable и щелкает Open JButton и возвращает только ошибку Win7.
-
Win2008 - это доступ, управляемый контекстом доступа AD, поскольку создать локальную учетную запись (внутренние правила) невозможно.