I would like my JFileChooser to allow multiple file selection, but with a limit on the number of files that can be selected simultaneously.
Ideally I would like to constrain the selection in real-time, with priority given to the most-recently selected file, i.e. when a 3rd file is selected, the 1st file (i.e the one that was selected earliest) should be deselected automatically.
I thought that a PropertyChangeListener like this one would work:
public static void main(String[] args) throws IOException {
final JFileChooser fc = new JFileChooser(didir);
fc.setMultiSelectionEnabled(true);
fc.addPropertyChangeListener(new PropertyChangeListener() {
private final Set<File> selected = Sets.newLinkedHashSet();
public void propertyChange(PropertyChangeEvent evt) {
if (JFileChooser.SELECTED_FILES_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
File[] selectedFiles = fc.getSelectedFiles();
if (selectedFiles.length > 2) {
selected.addAll(Arrays.asList(selectedFiles));
int numToRemove = Math.max(0, selected.size() - 2);
Iterables.removeIf(Iterables.limit(selected, numToRemove),
Predicates.alwaysTrue());
fc.setSelectedFiles(selected.toArray(new File[0]));
}
}
}
});
fc.showOpenDialog(null);
}
However the call to fc.setSelectedFiles() has no effect (although it fires an event, it does not update the selection in the list.)
Is there any way to programatically force a change to the selection while the JFileChooser is open? Or is there another way to limit the size of the selection?
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import javax.swing.JFileChooser;
public class MyClass {
final static JFileChooser fc = new JFileChooser("/");
public static void main(String[] args) throws IOException {
fc.setMultiSelectionEnabled(true);
fc.addPropertyChangeListener(new ChangeListener());
fc.showOpenDialog(null);
}
private static class ChangeListener implements PropertyChangeListener{
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (JFileChooser.SELECTED_FILES_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
File[] selectedFiles = fc.getSelectedFiles();
File[] allowedFiles = new File[2];
if (selectedFiles.length > 2) {
allowedFiles[0] = selectedFiles[1];
allowedFiles[1] = selectedFiles[0];
fc.setSelectedFiles(allowedFiles);
}
}
}
}
}
I have discovered that this bug is specific to the Macintosh look-and feel. setSelectedFile and setSelectedFiles do not work at all on the Mac (even before the dialog is opened.) My sample code works fine with the Metal look-and-feel.
Possible workarounds include:
Use a different look-and-feel
Use a FileDialog instead of a JFileChooser (does not support multiple file selection)
Related
I'm using a jFileChooser and I'm trying to achieve the following :
https://i.stack.imgur.com/O6MNj.png
I'm trying to force the the LIST view to have a VERTICAL scroll bar or my second option is to disable the size and modified columns from the details view.
EDIT:
Is there any way that I can insert a JScrollBar inside the jFileChooser?
You can access the JList and change the orientation of the list as follows:
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import java.util.List;
class FileChooserList
{
private static void createAndShowUI()
{
JFileChooser fileChooser = new JFileChooser(".");
// Change list orientation
JList list = SwingUtils.getDescendantsOfType(JList.class, fileChooser).get(0);
list.setLayoutOrientation( JList.VERTICAL );
fileChooser.showOpenDialog(null);
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
The above code requires the Swing Utils class.
disable the size and modified columns from the details view
Depends on what you mean by "disable".
You can remove those columns from the view of the table:
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import java.util.List;
class FileChooserDetails
{
private static void createAndShowUI()
{
JFileChooser fileChooser = new JFileChooser(".");
// Show the Details view of the file chooser
Action details = fileChooser.getActionMap().get("viewTypeDetails");
details.actionPerformed(null);
// Remove columns from view
JTable table = SwingUtils.getDescendantsOfType(JTable.class, fileChooser).get(0);
TableColumnModel tcm = table.getColumnModel();
tcm.removeColumn( tcm.getColumn(3) );
tcm.removeColumn( tcm.getColumn(1) );
fileChooser.showOpenDialog(null);
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
I'm making a program for a game HUD GUI but java cant get global keystrokes when the window isn't in focus so, and i dont want to do any black magic with jnativeinterface as i need it to work on both Linux and windows. My idea for getting around this was to pipe output from python to java but I'm not entirely sure how to do so.
here's my code so far for reference:
I've tried assigning a java key-listener to a none existent window just because i was curious and it didn't work.
package main;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MainThread extends JFrame implements KeyListener{
private static final long serialVersionUID = 1L;
public static void main(String[] args) throws Exception {
System.out.println("Jeffrey was here");
JFrame win = new JFrame();
JPanel wind = new JPanel();
win.add(wind);
win.setDefaultCloseOperation(EXIT_ON_CLOSE);
win.setVisible(true);
win.setLocation(500, 500);
win.setResizable(false);
win.setSize(300,50);
win.setAlwaysOnTop(true);
win.setTitle("Mouse Cordinates");
JLabel xCord = new JLabel("");
JLabel yCord = new JLabel("");
wind.add(xCord);
wind.add(yCord);
while(true) {
Thread.sleep(30);
PointerInfo mouse = MouseInfo.getPointerInfo();
Point poin = mouse.getLocation();
xCord.setText("X cordinates: " + (int) poin.getX());
yCord.setText("Y cordinates: " + (int) poin.getY());
}
}
#Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
int key = arg0.getKeyCode();
if(key == KeyEvent.VK_W) {
System.out.print("w pressed");
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}
i need it to detect when the key is pressed without the window is in focus.
There are already existing libraries that give you this option (i know this is not exactly what you are looking for (python and stuff), but it might be a solution to your problem). Take a look at kristian's system hook code snippet:
public static void main(String[] args) {
// might throw a UnsatisfiedLinkError if the native library fails to load or a
// RuntimeException if hooking fails
GlobalKeyboardHook keyboardHook = new GlobalKeyboardHook(true);
keyboardHook.addKeyListener(new GlobalKeyAdapter() {
#Override
public void keyPressed(GlobalKeyEvent event) {
System.out.println(event);
if (event.getVirtualKeyCode() == GlobalKeyEvent.VK_ESCAPE)
run = false;
}
#Override
public void keyReleased(GlobalKeyEvent event) {
System.out.println(event);
}
});
And of course there is also the JNativeHook option.
Some other already existing questions to take a look:
How to capture global key presses in java
How can I write a key listener to track all keystrokes in Java?
EDIT: Everything displays correctly when either field or hunterField hold no objects in any location. field and hunterField both exclusively hold objects which extend the same class, so I guess it may have something to do with inheritance...?
I have created a simple Agent-Based Model using MASON. The back-end works find, but when I try displaying my agents only "wall" agents are displayed. (Wall Portrayal) My code is below... Any idea?
package sim.app.celai;
import java.awt.Color;
import javax.swing.JFrame;
import sim.app.tutorial3.Tutorial3WithUI;
import sim.display.Controller;
import sim.display.Display2D;
import sim.display.GUIState;
import sim.portrayal.grid.SparseGridPortrayal2D;
import sim.util.Bag;
public class FieldWithGUI extends GUIState {
public Display2D display;
public JFrame frame;
SparseGridPortrayal2D hunterPortrayal = new SparseGridPortrayal2D();
SparseGridPortrayal2D wallPortrayal = new SparseGridPortrayal2D();
SparseGridPortrayal2D childPortrayal = new SparseGridPortrayal2D();
public FieldWithGUI() {
super(new Field(System.currentTimeMillis()));
}
public void setupPortrayals() {
childPortrayal.setField(((Field) state).field);
hunterPortrayal.setField(((Field) state).hunterField);
wallPortrayal.setField(((Field) state).wallField);
childPortrayal.setPortrayalForAll(new sim.portrayal.simple.OvalPortrayal2D(Color.blue));
hunterPortrayal.setPortrayalForAll(new sim.portrayal.simple.OvalPortrayal2D(Color.red));
wallPortrayal.setPortrayalForAll(new sim.portrayal.simple.OvalPortrayal2D(Color.green));
display.reset();
display.repaint();
}
public void quit()
{
super.quit();
if (frame!=null) frame.dispose();
frame = null; // let gc
display = null; // let gc
}
public static void main(String[] args)
{
new FieldWithGUI().createController();
}
public void start()
{
super.start();
// set up our portrayals
setupPortrayals();
}
public void init(Controller c)
{
super.init(c);
// Make the Display2D. We'll have it display stuff later.
display = new Display2D(400,400,this); // at 400x400, we've got 4x4 per array position
frame = display.createFrame();
c.registerFrame(frame); // register the frame so it appears in the "Display" list
frame.setVisible(true);
// specify the backdrop color -- what gets painted behind the displays
display.setBackdrop(Color.black);
// attach the portrayals
display.attach(childPortrayal, "children");
display.attach(hunterPortrayal, "hunter");
display.attach(wallPortrayal, "wall");
}
}
I want to learn how to write an ActionListener for JLabel. I want to make a label that open a new frame for user when user clicks the label. Maybe MouseListener can work but I do not know how to make it.
I recommend using a JTextField rather than a JLabel for this use.
Being based on a component designed to be focusable, it allows an ActionListener and looks and feels more like an HTML link.
E.G.
That is how it appears when the mouse is hovering over the first link.
LinkLabel
/* License - LGPL
LinkLabel Using the Desktop Class
There are a lot of link labels. This one uses the Java 1.6+
Desktop class for its functionality, and is thereby suitable
for applets, applications launched using JWS, and 'standard'
(non-JWS) desktop applications.
Note that the Desktop class can be used with pre 1.6 JREs. It
is available through JDIC.
https://jdic.dev.java.net/nonav/documentation/javadoc/jdic/org/jdesktop/jdic/desktop/Desktop.html
*/
import java.awt.Desktop;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.*;
import javax.swing.JTextField;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import javax.swing.border.MatteBorder;
import javax.swing.border.Border;
import java.net.URI;
import java.io.File;
/**
A Java 1.6+ LinkLabel that uses the Desktop class for opening
the document of interest.
The Desktop.browse(URI) method can be invoked from applications,
applets and apps. launched using Java Webstart. In the latter
two cases, the usual fall-back methods are used for sandboxed apps
(see the JavaDocs for further details).
While called a 'label', this class actually extends JTextField,
to easily allow the component to become focusable using keyboard
navigation.
To successfully browse to a URI for a local File, the file name
must be constructed using a canonical path.
#author Andrew Thompson
#version 2008/08/23
*/
public class LinkLabel
// we extend a JTextField, to get a focusable component
extends JTextField
implements MouseListener, FocusListener, ActionListener {
/** The target or href of this link. */
private URI target;
public Color standardColor = new Color(0,0,255);
public Color hoverColor = new Color(255,0,0);
public Color activeColor = new Color(128,0,128);
public Color transparent = new Color(0,0,0,0);
public boolean underlineVisible = true;
private Border activeBorder;
private Border hoverBorder;
private Border standardBorder;
/** Construct a LinkLabel that points to the given target.
The URI will be used as the link text.*/
public LinkLabel(URI target) {
this( target, target.toString() );
}
/** Construct a LinkLabel that points to the given target,
and displays the text to the user. */
public LinkLabel(URI target, String text) {
super(text);
this.target = target;
}
/* Set the active color for this link (default is purple). */
public void setActiveColor(Color active) {
activeColor = active;
}
/* Set the hover/focused color for this link (default is red). */
public void setHoverColor(Color hover) {
hoverColor = hover;
}
/* Set the standard (non-focused, non-active) color for this
link (default is blue). */
public void setStandardColor(Color standard) {
standardColor = standard;
}
/** Determines whether the */
public void setUnderlineVisible(boolean underlineVisible) {
this.underlineVisible = underlineVisible;
}
/* Add the listeners, configure the field to look and act
like a link. */
public void init() {
this.addMouseListener(this);
this.addFocusListener(this);
this.addActionListener(this);
setToolTipText(target.toString());
if (underlineVisible) {
activeBorder = new MatteBorder(0,0,1,0,activeColor);
hoverBorder = new MatteBorder(0,0,1,0,hoverColor);
standardBorder = new MatteBorder(0,0,1,0,transparent);
} else {
activeBorder = new MatteBorder(0,0,0,0,activeColor);
hoverBorder = new MatteBorder(0,0,0,0,hoverColor);
standardBorder = new MatteBorder(0,0,0,0,transparent);
}
// make it appear like a label/link
setEditable(false);
setForeground(standardColor);
setBorder(standardBorder);
setCursor( new Cursor(Cursor.HAND_CURSOR) );
}
/** Browse to the target URI using the Desktop.browse(URI)
method. For visual indication, change to the active color
at method start, and return to the standard color once complete.
This is usually so fast that the active color does not appear,
but it will take longer if there is a problem finding/loading
the browser or URI (e.g. for a File). */
public void browse() {
setForeground(activeColor);
setBorder(activeBorder);
try {
Desktop.getDesktop().browse(target);
} catch(Exception e) {
e.printStackTrace();
}
setForeground(standardColor);
setBorder(standardBorder);
}
/** Browse to the target. */
public void actionPerformed(ActionEvent ae) {
browse();
}
/** Browse to the target. */
public void mouseClicked(MouseEvent me) {
browse();
}
/** Set the color to the hover color. */
public void mouseEntered(MouseEvent me) {
setForeground(hoverColor);
setBorder(hoverBorder);
}
/** Set the color to the standard color. */
public void mouseExited(MouseEvent me) {
setForeground(standardColor);
setBorder(standardBorder);
}
public void mouseReleased(MouseEvent me) {}
public void mousePressed(MouseEvent me) {}
/** Set the color to the standard color. */
public void focusLost(FocusEvent fe) {
setForeground(standardColor);
setBorder(standardBorder);
}
/** Set the color to the hover color. */
public void focusGained(FocusEvent fe) {
setForeground(hoverColor);
setBorder(hoverBorder);
}
public static void main(String[] args) throws Exception {
JPanel p = new JPanel(new GridLayout(0,1));
File f = new File(".","LinkLabel.java");
/* Filename must be constructed with a canonical path in
order to successfully use Desktop.browse(URI)! */
f = new File(f.getCanonicalPath());
URI uriFile = f.toURI();
LinkLabel linkLabelFile = new LinkLabel(uriFile);
linkLabelFile.init();
p.add(linkLabelFile);
LinkLabel linkLabelWeb = new LinkLabel(
new URI("http://sscce.org/"),
"SSCCE");
linkLabelWeb.setStandardColor(new Color(0,128,0));
linkLabelWeb.setHoverColor(new Color(222,128,0));
linkLabelWeb.init();
/* This shows a quirk of the LinkLabel class, the
size of the text field needs to be constrained to
get the underline to appear properly. */
p.add(linkLabelWeb);
LinkLabel linkLabelConstrain = new LinkLabel(
new URI("http://sdnshare.sun.com/"),
"SDN Share");
linkLabelConstrain.init();
/* ..and this shows one way to constrain the size
(appropriate for this layout).
Similar tricks can be used to ensure the underline does
not drop too far *below* the link (think BorderLayout
NORTH/SOUTH).
The same technique can also be nested further to produce
a NORTH+EAST packing (for example). */
JPanel labelConstrain = new JPanel(new BorderLayout());
labelConstrain.add( linkLabelConstrain, BorderLayout.EAST );
p.add(labelConstrain);
LinkLabel linkLabelNoUnderline = new LinkLabel(
new URI("http://java.net/"),
"java.net");
// another way to deal with the underline is to remove it
linkLabelNoUnderline.setUnderlineVisible(false);
// we can use the methods inherited from JTextField
linkLabelNoUnderline.
setHorizontalAlignment(JTextField.CENTER);
linkLabelNoUnderline.init();
p.add(linkLabelNoUnderline);
JOptionPane.showMessageDialog( null, p );
}
}
I have a JDesktopPane containing some JInternalFrames. I want some menus on the menubar to be activated only when one of the JInternalFrames is selected. I've tried using VetoableChangeListener, with the following code in it:
JInternalFrame selectedFrame = desk.getSelectedFrame();
if ((selectedFrame != null)) {
imageMenu.setEnabled(Boolean.TRUE);
} else {
imageMenu.setEnabled(Boolean.FALSE);
}
But the results are not what I expected - for example, the menu is enabled only the second time I add a frame. when I close all frames, it remains enabled.
How can I make this work?
you have to read basic tutorial about JInternalFrames with link to the InternalFrameListener,
but another and look like as better way is programatically to know those event in all cases and evety times is by adding PropertyChangeListener as shows examples Getting All Frames in a JDesktopPane Container, by adding PropertyChangeListener you can listeng for these events
Add an InternalFrameListener to each internal frame added to the desktop pane, and each time an event is triggered, execute the code you have shown in your question.
This code could be better written though:
setEnabled takes a primitive boolean as argument, not a java.lang.Boolean. Use true and false rather than Boolean.TRUE and Boolean.FALSE.
The expression (selectedFrame != null) evaluates as a boolean. Just write
imageMenu.setEnabled(selectedFrame != null);
instead of
if ((selectedFrame != null)) {
imageMenu.setEnabled(Boolean.TRUE);
} else {
imageMenu.setEnabled(Boolean.FALSE);
}
I would just create a custom event and fire it when a JInternalFrame gets focus (isActivated).
The menu items would listen for this event, intercept it and set their status enabled or disabled accordingly.
The advantage here is that you don't have to handle what menu items should be available for which types of internal frames, just fire the appropriate event. It'll make your life easier if you add more internal frames in the future.
This answer is based on the answer by #mKorbel. This example shows one of the ways to detect focus between internal frames as is demonstrated here:
package com.apexroot.sandbox;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
/**
* author grants unlimited license to modify, reuse and redistribute. based on
* the suggestion by #mKorbel on stackoverflow at
* http://stackoverflow.com/questions/7219860/jinternalframe-selection
* please keep a URL to the original version in the source code.
* http://javajon.blogspot.com/2015/08/windowfocuslistener-for-jinternalframe.html
*
* #author Apexroot
*/
public class InternalFrameFocusListenerExample {
public static final String INTERNAL_FRAME_FOCUS_EVENT_PROPERTY = "selected";
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
final JFrame jFrame = new JFrame();
final JDesktopPane jDesktopPane = new JDesktopPane();
final JInternalFrame[] jInternalFrames = new FocusInternalFrame[3];
for (int i = 0; i < jInternalFrames.length; i++) {
jInternalFrames[i] = new FocusInternalFrame();
}
jFrame.dispose();
jFrame.setContentPane(jDesktopPane);
jDesktopPane.setPreferredSize(new Dimension(400, 200));
jFrame.pack();
jFrame.setVisible(true);
for (int i = 0; i < jInternalFrames.length; i++) {
jDesktopPane.add(jInternalFrames[i]);
jInternalFrames[i].setLocation(10 + 60 * i, 10 + 40 * i);
jInternalFrames[i].setVisible(true);
}
}
});
}
public static class FocusInternalFrame extends JInternalFrame {
public FocusInternalFrame() {
final JLabel jLabel = new JLabel("placeholder for pack();");
setContentPane(jLabel);
pack();
this.addPropertyChangeListener(
INTERNAL_FRAME_FOCUS_EVENT_PROPERTY,
new LabelFocusListener(jLabel));
}
}
private static class LabelFocusListener implements PropertyChangeListener {
private final JLabel jLabel;
public LabelFocusListener(JLabel jLabel) {
this.jLabel = jLabel;
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
// please keep a URL to the original version in the source code.
// http://javajon.blogspot.com/2015/08/windowfocuslistener-for-jinternalframe.html
if (INTERNAL_FRAME_FOCUS_EVENT_PROPERTY.equals(
evt.getPropertyName())) {
final Object oldValue = evt.getOldValue();
final Object newValue = evt.getNewValue();
if (oldValue instanceof Boolean
&& newValue instanceof Boolean) {
boolean wasInFocus = (Boolean) oldValue;
boolean isInFocus = (Boolean) newValue;
if (isInFocus && !wasInFocus) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
// focus gained
jLabel.setText("focus gained");
}
});
} else if (wasInFocus && !isInFocus) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
// focus lost
jLabel.setText("focus lost");
}
});
}
}
}
}
}
}