Problem with downloading and displaying image - java

I have made a program in Java, that downloads an image from the internet, given a link, and saves it into a specific folder in my computer, upon the pressing of a button in the gui. What I want to do next is to display that image on the screen. Please note, the URL input is not necessarily the URL of the image itself, but of an HTML webpage containing the image. The problem is, I can't simply create an ImageIcon object preemptively, because the image file doesn't exist in the system yet in compile time.
Panel Class
public class AdditionPanel extends JPanel
{
// ...
static JTextPane textpane;
JLabel paneInstructions;
JButton linkOk;
public AdditionPanel() throws IOException
{
textpane = new JTextPane();
paneInstructions = new JLabel("Paste the link here:");
linkOk = new JButton(" OK ");
// ...
linkOk.addActionListener(new LinkOkPressed());
// ...
this.add(textpane);
this.add(paneInstructions);
this.add(linkOk);
}
}
An idea I've had is to create an ActionListener for that button, and try to access the file only after the button has been pressed, and thus the file has been downloaded. In this case, I don't know how to make an image display on a JPanel, from a different class.
Action Listener
public class LinkOkPressed implements ActionListener
{
JLabel test;
#Override
public void actionPerformed(ActionEvent e)
{
// ImageDownloader is a class I have created, that simply saves the image
// from the given URL in a predetermined directory
ImageDownloader.saveImage(ImageDownloader.getImageUrl(AdditionPanel.textpane.getText()));
ImageIcon poster = new ImageIcon(getClass().getResource("/resources/myimage.png"));
test= new JLabel(poster);
AdditionPanel.add(test); // Does not work
}
}
So, can I add an image to the panel from a different class, or is there a better way to access a file that is downloaded during the runtime of the app? Thanks for the help.
ImageDownloader
public class ImageDownloader
{
public static String getImageUrl(String imdbLink)
{
String imageLink = "";
try
{
Document doc = Jsoup.connect(imdbLink).get();
Elements divs = doc.getElementsByClass("poster");
Element poster = divs.first();
Elements image = poster.getElementsByTag("a");
Element downloadImage = image.first();
Elements img = downloadImage.getElementsByTag("img");
imageLink = img.attr("src");
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return imageLink;
}
public static void saveImage(String imageLink)
{
BufferedImage image = null;
try
{
URL url =new URL(imageLink);
image = ImageIO.read(url);
ImageIO.write(image, "png", new File("C:\\...\\resources\\myimage.png"));
}
catch(IOException e)
{
e.printStackTrace();
}
}
}

Elaborating on PM 77-1's comment.
public class LinkOkPressed implements ActionListener
{
JLabel test;
#Override
public void actionPerformed(ActionEvent e)
{
// ImageDownloader is a class I have created, that simply saves the image
// from the given URL in a predetermined directory
ImageDownloader.saveImage(ImageDownloader.getImageUrl(AdditionPanel.textpane.getText()));
ImageIcon poster = new ImageIcon(getClass().getResource("/resources/myimage.png"));
test= new JLabel(poster);
Object obj = e.getSource(); // "obj" is really "linkOk" from class "AdditionPanel"
java.awt.Container parent = ((JButton) obj).getParent(); // "parent" is instance of "AdditionPanel"
((JPanel) parent).add(test);
}
}

SwingWorker example.
Create a separate class like so...
public class SaveImage extends javax.swing.SwingWorker<Void, Void> {
private AdditionPanel additionPanel;
public SaveImage(AdditionPanel addPanel) {
additionPanel = addPanel;
}
#Override
protected Void doInBackground() throws Exception {
ImageDownloader.saveImage(ImageDownloader.getImageUrl(AdditionPanel.textpane.getText()));
// Wait until the image is saved.
// Since I don't have code for class "ImageDownloader", can't tell you how to do this.
return null;
}
#Override
protected void done() {
javax.swing.ImageIcon poster = new javax.swing.ImageIcon(getClass().getResource("/resources/myimage.png"));
javax.swing.JLabel test= new javax.swing.JLabel(poster);
additionPanel.add(test);
}
}
And change your LinkOkPressed class like so...
public class LinkOkPressed implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
Object obj = e.getSource(); // "obj" is really "linkOk" from class "AdditionPanel"
java.awt.Container parent = ((JButton) obj).getParent(); // "parent" is instance of "AdditionPanel"
SaveImage saveImage = new SaveImage((AdditionPanel) parent);
saveImage.execute();
}
}

Related

Show custom loading dialog with gif

I am trying to get my application to display a simple loading dialog so users know when a time intensive process is working and when its done. I just want it to show a simple "loading" using a gif I downloaded. I already tried using only text and it still doesn't work.
I can get the dialog to display (and disappear) when I want it to, the problem is nothing will display on the dialog (or frame) after displaying it. I have tried many different techniques and all give the same result, a blank dialog.
I finally made a separate class to display the dialog (with loading gif) and I got it to display properly (by itself), but when I run it from my main application, it shows a black dialog again. I tested putting the gif into a JOptionPane and it works, the problem with that is I can't close it at will.
Here is my custom code.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
import java.util.logging.*;
import org.w3c.dom.*;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import javax.swing.filechooser.FileNameExtensionFilter;
public class Loader implements Runnable {
final JFileChooser jfc = new JFileChooser();
static JFrame frame = new JFrame();
Frame parentUI = new Frame();
JDialog dialog = new JDialog();
JLabel lbl_filename = new JLabel();
JLabel lbl_path = new JLabel();
static Loader load = new Loader(null);
public static void main(String[] args) throws InterruptedException, InvocationTargetException {
load.run();
frame.setVisible(true);
}
public Loader(Frame parent) {
init();
parentUI = parent;
}
#Override
public void run() {
createDialog(parentUI);
}
public final void init() {
JButton btn = new JButton("Open");
frame.setTitle("Loader Test");
frame.setSize(500, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setLayout(new FlowLayout());
btn.addActionListener(new Action1());
frame.add(btn);
frame.add(lbl_filename);
frame.add(lbl_path);
}
class Action1 implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
openFile();
load.Close();
}
}
private void createDialog(final Frame parent) {
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setTitle("Loader");
URL url = this.getClass().getResource("/resource/loader.gif");
Icon icon = new ImageIcon(url);
JLabel label = new JLabel(icon);
dialog.add(label);
dialog.pack();
dialog.setLocationRelativeTo(parent);
}
public void Show(Boolean visible) {
this.run();
dialog.setVisible(visible);
}
public void Close() {
dialog.setVisible(false);
}
private void setJFCFilter(String file, String ext) {
FileNameExtensionFilter filter = new FileNameExtensionFilter(file, ext);
jfc.setFileFilter(filter);
}
private void openFile() {
File default_dir = new File(".");
jfc.setCurrentDirectory(default_dir);
setJFCFilter("Scalable Vector Graphics", "svg");
int returnVal = jfc.showOpenDialog(parentUI);
if (returnVal == JFileChooser.APPROVE_OPTION) {
String path = jfc.getSelectedFile().getAbsolutePath();
String fileName = jfc.getSelectedFile().getName();
lbl_filename.setText(fileName);
lbl_path.setText(path);
load.Show(true);
createDoc(path);
load.Close();
}
}
private void createDoc(String file) {
try {
NodeList svgIDPaths;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(file);
String xpathIDExp = "//g/#id";
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
XPathExpression expression = xpath.compile(xpathIDExp);
svgIDPaths = (NodeList)expression.evaluate(doc, XPathConstants.NODESET);
} catch (Exception ex) {
Logger.getLogger(Loader.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Edit: Use this file for testing -> svg_test.svg
I have tried calling it like this:
loader.show(true);
And also in its own thread like this:
private void load(final Boolean visible) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
loader.show(visible);
}
});
t.start();
}
Neither method works and gives me the same result, a blank dialog. I have had this issue in the past, but just gave up and removed it (loading dialog). I have tried it with a progress bar and simple text, nothing seems to work.
Also I tried it in a JOptionPane and it worked, but that's not desirable (I want to close/open when I want not via a button click).
private void load() {
ImageIcon icon = new ImageIcon(MainForm.class.getResource("/resource/loader.gif").getFile());
JOptionPane.showMessageDialog(null, "Loading...", "Loader", JOptionPane.INFORMATION_MESSAGE, icon);
}
I am aware you can't run multiple dialogs on the EDT and have to use a separate thread, but I'm using a separate thread and its not working (it works by itself).
(Also note I have one main application (frame) that is running/opening this second dialog).
Any assistance is appreciated.
You look to have a Swing threading issue where you have long-running code on the event thread messing up drawing of images, and my guess is that the long running code is in your createDoc method. Consider calling that from a background thread, such as from a SwingWorker, and calling close on your load object only after the worker has completed its work. For example something like so:
class Action1 implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
openFile();
// load.Close(); // get rid of this
}
}
// .......
private void openFile() {
// ....
load.Show(true); // load dialog on event thread
new SwingWorker<Void, Void>() {
protected Void doInBackground() throws Exception {
createDoc(path); // call this from background thread
return null;
};
protected void done() {
load.Close(); // only call this once createDoc has completed
// probably should call get() in here to catch all exceptions
};
}.execute();
}

How to repaint JPanel from outside its parent JFrame?

I can add/remove elements to/from a panel and repaint it when the method used to fill the panel is called by one of its parent JFrame events, but I can not repaint it by events from other classes even if their sources have been added to it, or that is how I understand the problem for now.
I want to understand what is going on here, Thank you.
Main Class
public class Principal extends JFrame implements ActionListener{
private static Principal instPrincipal = null;
private SubClass subClassInst =new SubClass();
public JPanel panelPrincipal;
public static Principal getInstance() {
if (instPrincipal != null)
return instPrincipal ;
else {
instPrincipal = new Principal ();
return instPrincipal ;
}
}
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
try {
if(source == btnSub)
{
subClassInst.fillPanelPrincipal();
}
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
Sub Classes Example
public class SubClass implements ActionListener {
private JPanel tempPanel;
private JButton btnSave;
private Principal instPrincipal;
public void fillPanelPrincipal() {
instPrincipal = Principal.getInstance();
instPrincipal.panelPrincipal.removeAll();
//Start adding elements..
tempPanel = new JPanel();
instPrincipal.panelPrincipal.add(tempPanel);
btnSave = new JButton("Save");
btnSave.addActionListener(this);
tempPanel.add(btnSave);
//End.
instPrincipal.panelPrincipal.repaint();
}
public void actionPerformed(ActionEvent event) {
instPrincipal = Principal.getInstance();
Object source = event.getSource();
if (source == btnSave) {
// modify local data, Database .. ; //work but need to be repainted on panelPrincipal
instPrincipal.panelPrincipal.repaint();//does not work
}
}
}
Update
To clarify the problem more, I have one single JPanel on a JFrame and there are different classes to fill it for multiple functionalities, I call their methods using JMenuItems on the main frame, these Classes implement ActionListener, passing the panel didn't work, and also the method I am trying here.
I thought about changing the design to use CardLayout, but it was very difficult.
You are calling Principal as a static reference, so how is it supposed to know what frame to repaint? You should pass the instance of the JFrame through the constructor of the subclass. Like so:
private SubClass subClassInst = new SubClass(this);
And create the constructor like this
private JFrame parent;
public SubClass(JFrame parent) { this.parent = parent; }
You can then use it like so
this.parent.repaint();

Accessing data from another class which reads values

Ok, so here is a problem: I have main class which generates a window and two buttons:
...
public MainWindow()
{
...
b_read.addActionListener(new ReadStudents());
b_open_all.addActionListener(new OpenStudents());
...
In ReadStudents.java class I load data from a file with JFileChooser and print it on the screen. Sample of code thats load file:
#Override
public void actionPerformed(ActionEvent e) {
...
JFileChooser fc = new JFileChooser(".");
...
int rez = fc.showOpenDialog(fc);
if (rez == JFileChooser.APPROVE_OPTION) {
file = fc.getSelectedFile();
...
So now in my ReadStudents.java class variable "file" has my loaded information.
How do i pass this variable with containing information in it to a class that prints Students on the screen (second button OpenStudents.java) ?
Edit:1) I can not initialize an object of ReadStudents.java in OpenStudents.java class because in the new object variable "file" will be empty. Something is loaded to a "file" only when a button b_read is pressed.
One option would be to implement the action listener on your MainWindow class and the call a method in the ReadStudents/OpenStudents that return the list of files.
For example:
/**
* Main window class.
*/
public static void main(String args[]) {
JFrame mainFrame = new JFrame();
mainFrame.setLayout(new BorderLayout());
mainFrame.setSize(100, 100);
final StudentReader student = new StudentReader();
JButton button = new JButton("Click Me");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Make the call here. Take note!
// Look at the return type!
List<String> strings = student.fileNames();
for (String s : strings) {
System.out.println(s);
}
}
});
mainFrame.add(button);
mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mainFrame.setVisible(true);
}
public class StudentReader {
public List<String> fileNames() {
// Do your magic here :)
// Open a dialog. Get the files.
// Return it as a list
return Arrays.asList(new String[]{"Filename"});
}
}
I Hope this helps!

Saving changes made in a JFrame

I have a simple JFrame where the user is changing the background color of it using a JPopupMenu. When the user exit the application, I want to save the changes made in the background.
I have tried to handle this with method windowClosing() from WindowAdapter class but when when I launch the application again, I don't see the changes I made earlier. I don't know what the problem is. I will appreciate any help. Here's my code.
/*i have removed unnnecessary codes*/
public class Popupframe extends JFrame{
private JRadioButtonMenuItem[] items;
private final Color[] colorvalues={Color.BLUE,Color.YELLOW,Color.RED};
static Color bgcolor=Color.CYAN;
JRadioButtonMenuItem[] cheek;
public Popupframe() {
super("using popups");
String[] colors = {"Blue","Yellow","Red"};
setBackground(bgcolor);
addMouseListener(new Handler());
}
private class Handler extends MouseAdapter implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
for(int i=0; i<items.length; i++) {
if(event.getSource()==items[i]) {
getContentPane().setBackground(colorvalues[i]);
bgcolor=colorvalues[i];
}
}
}
}
public static void main(String[] args) {
Popupframe frame=new Popupframe();
frame.setSize(width,height);
frame.setDefaultCloseOperation(Popupframe.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
int ok=JOptionPane.showConfirmDialog(frame,"are sure?","close",JOptionPane.WARNING_MESSAGE);
if(ok==JOptionPane.OK_OPTION) {
bgcolor=frame.getContentPane().getBackground();
System.out.println(bgcolor);
System.exit(0);
}
}
});
frame.setVisible(true);
}
You need to save the color code into a file(like settings file or shared preference) before System.exit(0) and read it in the main and set that color code. Then it works fine.
You are not persisting the color. Color is serializable, so you could just save the object in the program root directory. Put this code in your WindowClosing method :
//serialize the Color
try (
OutputStream file = new FileOutputStream("myBgColor.ser");
OutputStream buffer = new BufferedOutputStream(file);
ObjectOutput output = new ObjectOutputStream(buffer);
){
output.writeObject(bgColor);
}
catch(IOException ex){
log.log(Level.SEVERE, "Cannot perform output.", ex);
}
When you reload the application you need to get the color back. In the PopupFrame() constructor, before you call setBackground(color), put in this code here :
//deserialize the Color file
try(
InputStream file = new FileInputStream("myBgColor.ser");
InputStream buffer = new BufferedInputStream(file);
ObjectInput input = new ObjectInputStream (buffer);
){
//deserialize the List
bgColor = (Color)input.readObject();
}
catch(ClassNotFoundException ex){
fLogger.log(Level.SEVERE, "Cannot perform input. Class not found.", ex);
}
That should do the trick.

Calling super.approveSelection() within a SwingWorker

I have a customized JFileChooser
Its approveSelection() method is slightly modified:
public void approveSelection()
{
File file = getSelectedFile();
changeGui();
final Object a = makeALongCalcualtion(file);
if (a != null)
{
super.approveSelection();
SwingWorker<Document, Void> open = new SwingWorker<Document, Void>()
{
#Override
protected Document doInBackground() throws Exception
{
return createADocument(a);
}
#Override
protected void done()
{
try
{
if(get() != null)
{
changeGui();
}
else
{
//TODO error message
changeGui();
}
}
catch (InterruptedException | ExecutionException e)
{
//TODO error message
changeGui();
}
}
};
open.execute();
}
else
{
//TODO error message
changeGui();
}
}
The changeGui() method sets a JProgressBar to indeterminate and updates a JLabel with a new string.
If file provided to makeALongCalcualtion(file) is of invalid type, it will return null, otherwise it returns info that is passed to SwingWorker which can use it to acutally create the representation of a file in the program (the Document object).
However, this doesn't work as it should because makeALongCalcualtion(file) isn't called within SwingWorker method, and that blocks the EDT.
In order to fix the problem, I would have to call makeALongCalcualtion(file) within a SwingWorker. I could move that part of the code into the SwingWorker without any problems, but then I would have to (due to my code logic) move super.approveSelection() along with it.
So the bottom line is, how do I call super.approveSelection() from within doInBackground() for this specific case?
//More info
What is supposed to happen:
User selects and opens a file
JLabel and JProgressBar are updated, indeterminate progress starts to play.
If makeALongCalcualtion(file) return null user is warned with an error window, but the JFileChooser stys open, making it possible to choose again when the error window is closed.
Otherwise, super.approveSelection() is called, allowing the chooser to close.
A document is created (but the method that creates the document return null if something goes wrong).
If everything is fine, JLabel updates and progressBar animation is stopped (indeterminate is set to false).
If something goes wrong same thing happens as in step 6, only with different message in JLabel.
What happens:
same
same
same, but when makeALongCalculation(file); begins, progressBar freezes.
same
same
same, but the animation isn't stopped (since the progressbar is frozen), only the frozen "picture" is removed and progressbar returns to it's previous state.
same
EDIT
I have made some alterations to my program and I now have this:
approveSelection():
public void approveSelection()
{
File file = getSelectedFile();
Main.getStatusBar().startOpen();
final WorkOpen open = new WorkOpen(file);
open.execute();
open.addPropertyChangeListener(new PropertyChangeListener()
{
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
if (evt.getNewValue().equals("DONE"))
{
if (open.failed())
{
//TODO error message
Main.getStatusBar().endOpen(false);
}
else
{
Main.getStatusBar().endOpen(true);
}
}
}
}
});
}
SwingWorker:
class WorkOpen extends SwingWorker<Document, Void>
{
boolean failed = false;
File file;
public boolean failed()
{
return failed;
}
#Override
protected Document doInBackground() throws Exception
{
ArrayList<String> data = Opener.extractData(file);
if (data != null)
{
//My little path/name/similar managing system
FileComplex fullPath = new FileComplex(file.toString());
return Opener.createDocument(fullPath.getFullName(), fullPath.getFullPath(), data);
}
else
{
failed = true;
return null;
}
}
#Override
protected void done()
{
try
{
if(get() != null)
{
Main.addDocument(get());
}
}
catch (InterruptedException | ExecutionException e)
{
failed = true;
}
}
WorkOpen(File file)
{
this.file = file;
}
}
The problem now is where to call super.approveSelection(). It has to wait for the worker to finish executing, yet I can't call it from the property change listener.
What to do here?
EDIT 2
As HovercraftFullOfEels suggested, I fixed my code and it compiled and ran. But the problem of JProgressBar freezeing remained. Also, I had to introduce something I don't know if I should have:
private void superApproveSelection()
{
super.approveSelection();
}
public void approveSelection()
{
final File file = getSelectedFile();
class OpenWorker extends SwingWorker<Boolean, Void>
{
Document document;
Document getDocument()
{
return document;
}
#Override
protected Boolean doInBackground() throws Exception
{
ArrayList<String> data = Opener.extractData(file);
if (data != null)
{
//I had to start the progressBar here, because if invalid
//file was selected (extractData(file) returns null if it was),
//nothing should happen (maybe an error
//window later, handled with a new Runnable() same as this:
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
Main.getStatusBar().startOpen();
}
});
FileComplex fullPath = new FileComplex(file.toString());
document = Opener.createDocument(fullPath.getFullName(), fullPath.getFullPath(), data);
return true;
}
else
{
return false;
}
}
};
final OpenWorker opener = new OpenWorker();
opener.addPropertyChangeListener(new PropertyChangeListener()
{
#Override
public void propertyChange(PropertyChangeEvent evt)
{
if ("state".equals(evt.getPropertyName()))
{
if (evt.getNewValue() == SwingWorker.StateValue.DONE)
{
if(opener.getDocument() != null)
{
superApproveSelection();
Main.addDocument(opener.getDocument());
Main.getStatusBar().endOpen(true);
}
else
{
try
{
//I'm retrieveing doInBackground()'s value to see if
//progressBar needs stoping (it also displays some
//text, so it must not be called unless the
//progressBar was started).
if (opener.get())
{
Main.getStatusBar().endOpen(false);
}
}
catch (InterruptedException | ExecutionException e)
{
//TODO error something went wrong
}
}
}
}
}
});
opener.execute();
}
"In order to fix the problem, I would have to call makeALongCalcualtion(file) within a SwingWorker. I could move that part of the code into the SwingWorker without any problems, but then I would have to (due to my code logic) move super.approveSelection() along with it."
No, not true at all. super.approveSelection() would not have to be called inside of the SwingWorker.
Why not simply create a SwingWorker, add a PropertyChangeListener to it, and when the SwingWorker's state is done, call super.approveSelection() if indicated?
OK, here is my example below. Explanation to follow:
import java.awt.*;
import java.awt.Dialog.ModalityType;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
import javax.swing.text.*;
#SuppressWarnings("serial")
public class ApproveSelectionTest extends JPanel {
private JTextArea textArea = new JTextArea(30, 60);
public ApproveSelectionTest() {
textArea.setEditable(false);
textArea.setFocusable(false);
JPanel btnPanel = new JPanel();
btnPanel.add(new JButton(new MyGetFileAction("Get Text File Text")));
setLayout(new BorderLayout());
add(new JScrollPane(textArea), BorderLayout.CENTER);
add(btnPanel, BorderLayout.PAGE_END);
}
private class MyGetFileAction extends AbstractAction {
public MyGetFileAction(String text) {
super(text);
}
public void actionPerformed(java.awt.event.ActionEvent arg0) {
MyFileChooser myFileChooser = new MyFileChooser();
int result = myFileChooser.showOpenDialog(ApproveSelectionTest.this);
if (result == JFileChooser.APPROVE_OPTION) {
Document doc = myFileChooser.getDocument();
textArea.setDocument(doc);
}
};
}
private static void createAndShowGui() {
ApproveSelectionTest mainPanel = new ApproveSelectionTest();
JFrame frame = new JFrame("ApproveSelectionTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class MyFileChooser extends JFileChooser {
private WorkOpen workOpen = null;
private JDialog progressDialog = null;
public MyFileChooser() {
}
#Override
public void approveSelection() {
JProgressBar progBar = new JProgressBar();
progBar.setIndeterminate(true);
Window win = SwingUtilities.getWindowAncestor(this);
progressDialog = new JDialog(win, "Checking File", ModalityType.APPLICATION_MODAL);
progressDialog.getContentPane().add(progBar);
progressDialog.pack();
progressDialog.setLocationRelativeTo(null);
File file = getSelectedFile();
workOpen = new WorkOpen(file);
workOpen.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (SwingWorker.StateValue.DONE == pcEvt.getNewValue()) {
if (progressDialog != null) {
progressDialog.dispose();
}
try {
boolean bool = workOpen.get().booleanValue();
if (bool) {
superApproveSelection();
} else {
JOptionPane.showMessageDialog(MyFileChooser.this, "Invalid File Chosen");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
});
workOpen.execute();
progressDialog.setVisible(true);
}
// ****** this is key *****
private void superApproveSelection() {
super.approveSelection();
}
public Document getDocument() {
if (workOpen == null) {
return null;
} else {
return workOpen.getDocument();
}
}
}
class WorkOpen extends SwingWorker<Boolean, Void> {
private static final long SLEEP_TIME = 4 * 1000;
private Document document = null;
private File file = null;
public WorkOpen(File file) {
this.file = file;
}
#Override
protected Boolean doInBackground() throws Exception {
if (file == null || !file.exists()) {
return Boolean.FALSE;
}
Thread.sleep(SLEEP_TIME);
String fileName = file.getName();
if (fileName.contains(".txt")) {
Scanner scan = new Scanner(file);
StringBuilder stringBuilder = new StringBuilder();
while (scan.hasNextLine()) {
stringBuilder.append(scan.nextLine() + "\n");
}
document = new PlainDocument();
document.insertString(0, stringBuilder.toString(), null);
return Boolean.TRUE;
}
return Boolean.FALSE;
}
public Document getDocument() {
return document;
}
}
Explanation and key points from my example:
This example behaves very simply. You choose a file, and if the file exists and contains ".txt" in its name, then it reads in the document and displays the text in a JTextField.
Else it displays a warning message but leaves the JFileChooser displayed.
Probably the key point: I've given my MyFileChooser class a private superApproveSelection() method that can be called by my PropertyChangeListener. This exposes the super's approveSelection() method to the inner class, one of the problems you were having.
The order of calling code is important in my approveSelection() override.
In this method I first create my JProgressBar and its dialog but don't yet display it immediately. It really doesn't have to be created first, but it sure needs to be displayed last.
I create my SwingWorker, workOpen, but don't yet execute it.
I add my PropertyChangeListener to the SwingWorker before executing the SwingWorker.
I then execute my SwingWorker
I then display my modal JDialog with the indeterminate JProgressBar.
My SwingWorker is structured so that its doInBackground returns a Boolean, not a Document.
I have it create a (very simple) Document if all works out OK that holds the content of the text file, and set a private "doc" field obtainable by a getter method, and then have doInBackground return Boolean.TRUE if all works well.
I've given my doInBackground a Thread.sleep(4000) just to pretend that its action takes a lot of time. Yours of course won't have this.
In the PropertyChangeListener if the SwingWorker is DONE, I'll dispose of the progress bar dialog and then call get() on the SW to get the Boolean result.
If it's Boolean.TRUE, then call the superApproveSelection() method described above.
Else show an error message. Note that since the super's approveSelection() isn't called, the file chooser dialog remains displayed.
If the approveSelection is called then the code that displays my file chooser dialog will get the appropriate return value, will extract the Document from the file chooser and displays the Document in a JTextArea.

Categories