HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMajor

File Browser GUI

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
guifilebrowser

Problem

FileBro is a basic GUI based File Browser.

FileBro Functionality

  • Directory tree - shows the file system roots at start-up, but is otherwise built lazily as the user browses around the file system. FileBro displays a progress bar as it is loading new entries.



  • The file list (a table) displays a list of the directories and files in the current directory tree selection. Sort by clicking on the column header.



  • Buttons - all use the Desktop class for their functionality. If the action cannot be completed, a meaningful reason should by displayed in a JEditorPane error message.



  • Locate - opens the parent directory of the currently selected file.



  • Open - launches whatever application is the default consumer for that file type.



  • Edit - opens the file for edit in the default consumer.



  • Print - prints the file using the default consumer.



  • Details on the selected file are displayed below the buttons.



Does the current functionality work as per above description?

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
Fix keyboard focus issues - especially when functions like
rename/delete etc. are called that update nodes & file lists.
Needs more t

Solution

This code concerns me:

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();


as you're making Swing calls to a JProgressBar off of the EDT. Best to start the progress bar before the SwingWorker and end it in the done method. Either that or add a PropertyChangeListener to the SwingWorker and end the progress bar when the worker's state property is StateValue.DONE.

Another issue is that you're using a DefaultMutableTreeNode, and per the API, concurrency care must be taken when using this since you do appear to be using this in more than one thread:


This is not a thread safe class.If you intend to use a DefaultMutableTreeNode (or a tree of TreeNodes) in more than one thread, you need to do your own synchronizing. A good convention to adopt is synchronizing on the root node of a tree.

EDIT

One way to possibly get DefaultMutableTreeNode at least out of the equation is to add nodes to it in one thread only, the EDT, by using SwingWorker's publish/process. For example:

private void showChildren(final DefaultMutableTreeNode node) {
      tree.setEnabled(false);
      progressBar.setVisible(true);
      progressBar.setIndeterminate(true);

      SwingWorker worker = new SwingWorker() {
         @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 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();
   }

Code Snippets

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();
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();
   }

Context

StackExchange Code Review Q#4446, answer score: 42

Revisions (0)

No revisions yet.