How to get a Swing ListSelectionEvent's "source" item? - java

I am trying to write a ListSelectionListener for a JList that knows which list item the user is selecting away from, and which list item the user is selecting to. So if a list has three items in it {Apple, Orange, Pear}, and the current selection is on Orange and the user selects Pear, then:
srcFruit is Orange; and
destFruit is Pear
Here's the code I have:
myList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent fruitSelectionEvent) {
printSourceAndDestFruit(myList, fruitSelectionEvent);
}
});
private void printSourceAndDestFruit(JList list, ListSelectionEvent event) {
FruitVO srcFruit = (FruitVO)list.getModel().getElementAt(event.getFirstIndex());
FruitVO destFruit = (FruitVO)list.getModel().getElementAt(event.getLastIndex());
System.out.println("srcFruit = " + srcFruit.getName() + " and destFruit = " = destFruit.getName());
}
When the application loads and initializes the JList, there is no default selection. When I take the following actions:
Click Orange
Click Pear
Click Orange again
Here's the print out I get:
srcFruit = Orange and destFruit = Pear
srcFruit = Orange and destFruit = Pear
Where am I going wrong here? Are getFirstIndex()/getLastIndex() buggy or just not the correct Swing methods to be using?
Here's the output I should be seeing:
srcFruit = Orange and destFruit = Pear
srcFruit = Pear and destFruit = Orange
So even though I made 3 selects (mouse clicks), since the first time I click Orange wasn't a change from one value to the next, I believe it is correct to not fire and call printSourceAndDestFruit. The I select Pear and it is correct in stating that srcFruit is Orange and that destFruit is Pear. But when I click back to Orange the 2nd println should have srcFruit as Pear and destFruit as Orange. Why doesn't it?!?!
Thanks in advance!

The first and last index are not what you think they are. Here's what the javadoc says:
getFirstIndex()
Returns the index of the first row whose selection may have changed.
getLastIndex()
Returns the index of the last row whose selection may have changed.
So, since the selection of pear and orange changes at each click, and since pear is after orange in the list, getFirstIndex() always returns Orange and getLastIndex() always returns Pear.
If you want to compare the new selection with the last one, then keep the last selection in some member variable, and compare it with the new selection each time the selection changes, and getValueIsAdjusting() returns false.

The reason that selection is in the order Orange, Pear is that the DefaultListSelectionModel calls fireValueChanged solely based on the items index location in the ListModel rather than the new selection index.
This related bug report asked a similar question but shows that this was normal behavior and showed that you could add a workaround by using:
FruitVO srcFruit = (FruitVO)list.getModel().getElementAt(list.getMinSelectionIndex());
FruitVO destFruit = (FruitVO)list.getModel().getElementAt(list.getMaxSelectionIndex());

Related

MouseEvents inside JList objects

I am a bit confused regarding a situation I have. I created a ListModel extending DefaultListModel and ListRenderer implementing ListCellRenderer for displaying a custom cell in a JList. The cells are some objects created from a class extending JPanel, that contain a JLabel and a JButton.
My issue is related to the mouse events: I want to trigger a certain event when clicking on the JButton inside a cell of the JList, yet I can not figure out how to match the mouse source point to that of the JButton from the respective index. More exactly, I added a mouse listener to the list, but I want it to trigger something if the mouse point is located inside the bounds of the JButton, and another action if it's on the data item. I added some prints to find out the cause of this, but before that some code to highlight the structure:
public WifiGuiHandler(JButton reference) {
btnReference = reference;
wifiListener = new WifiListener();
wifiPopupContainer = new JScrollPopupMenu("Connections.");
wifiPopupContainer.setMaximumVisibleRows(7);
connectionsHolder = new ArrayList<>();
listOfConnections = new JList();
listOfConnectionsModel = new ListModel(connectionsHolder);
listOfConnectionsRenderer = new ListRenderer();
listOfConnections.setModel(listOfConnectionsModel);
listOfConnections.setCellRenderer(listOfConnectionsRenderer);
wifiPopupContainer.add(listOfConnections);
wifiPopupContainer.pack();
initializeTestVariables();
initializeListeners();
}
Here, the constructor for the class that takes a JButton and adds a mouse listener to it, that triggers the appearance of a JPopupMenu, which has only one component, the JList that hold the entire data. Also, links the ArrayList with the data items to the ListModel.
public void initializeTestVariables() {
for (int i = 0; i <= 10; i++) {
WifiItem item = new WifiItem("Connection number " + i + ".", i);
connectionsHolder.add(item);
}
}
Setting up the data items.
public void initializeListeners() {
listOfConnections.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
int index = listOfConnections.locationToIndex(e.getPoint());
if (index >= 0) {
WifiItem item = (WifiItem) ((ListModel) listOfConnections.getModel()).getElementAt(index);
System.out.println("Button of " + item.getConnectionName() + " is at location :" + item.getButton().getLocation());
System.out.println("Button has the bounds : " + item.getButton().getBounds());
System.out.println("MouseEvent detected on : " + e.getPoint().getLocation());
if (item.getButton().getBounds().contains(e.getPoint())) {
item.connectHere();
}
if (item.getButton().isVisible()) {
System.out.println("Set expanded on : " + item.getConnectionName());
item.setExpandedState(false);
listOfConnectionsModel.fireContentsChanged(item, index, index);
updateGui(false);
} else {
System.out.println("Set expanded on : " + item.getConnectionName());
listOfConnectionsModel.fireContentsChanged(item, index, index);
item.setExpandedState(true);
updateGui(false);
}
}
}
});
btnReference.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
updateGui(true);
}
});
}
And this is where the confusion arises. I correctly get the data item( WifiItem) from the mouse event location/point, but when I click on the JButton of the WifiItem, it doesn't trigger that method, like it doesn't seem to detect that the JButton actually is there. I also set up the prints, and what is strange, the Point for the JButton is always the same, even though it actually is different, and this seems to be the problem. More exactly, from the output of the program:
Button of Connection number 2. is at location :java.awt.Point[x=137,y=33]
Button has the bounds : java.awt.Rectangle[x=137,y=33,width=90,height=26]
MouseEvent detected on : java.awt.Point[x=172,y=125]
Button of Connection number 3. is at location :java.awt.Point[x=137,y=33]
Button has the bounds : java.awt.Rectangle[x=137,y=33,width=90,height=26]
MouseEvent detected on : java.awt.Point[x=172,y=125]
The above mouse events points were actually located on the JButton itself, only it didn't get that. As another strange fact, only if I click the JButton of the FIRST element of the list does it trigger the required mouse action.
Another print revealed that all the JButtons have the same Point and Rectangle, and I don't get it. There are 10 items in the JList, each displayed properly, how can all their JButtons have the same location? I must be missing some key element here. I looked at other posts and tried other recommendations: converting the point with SwingUtilities, removing all the mouse listeners from the JList and adding them to the data items.
To sum it up,the issue is that the list triggers the events for the correct data item in it(meaning, I do get the correct index for the item located there), but if the mouse event happens on the JButton of any data item inside the list, it doesn't trigger the required effect (the point is not withing the bounds of the button, even though it should be).
More exactly, I added a mouse listener for the list, but I want it to trigger something if the mouse point is located inside the bounds of the JButton, and another action if it's on the data item.
An easier solution would be to use a JTable. The data is separated into columns and the JTable has an API to let you know which row/column was selected.
You can use the Table Button Column as your renderer/editor for the button.
Edit:
only if I click the JButton of the FIRST element of the list does it trigger the required mouse action
Sounds like your conversion of the mouse point is off.
, how can all their JButtons have the same location?
Again, the button location is releative to the renderer panel. The panel itself is relative to the row in the JList. So I would guess you need to need the row index and add the height of each of the previous rows to your calculation.

Extracting and manipulating data from a multiple interval JList to use in textArea/JOptionPane

As the title says, I have a multiple interval selection JList and i'm having trouble properly manipulating the data. This is my first time using a JList and it's proving difficult for me.
My GUI acts as a ticket ordering interface for a sports team, and the JList i'm referring to holds a list of souvenirs customers can order. Since it's a multiple selection JList, they can select multiple souvenirs if they so choose.
My issue is extracting the items from the JList and properly printing them to a JOptionPane textArea window, which acts as a summary for the user's order. Here is a breakdown of my goals/issues:
Extracting the souvenirs selected, where the names of the souvenirs are stored in a String[] array
Matching a parallel array of prices, stored in a Double[] array
Using the proper methods from the JList event handler to then print the data to the textArea summary
Eliminating duplicates of souvenirs that seem to appear when I try and print the data to the textArea
Here is the creation of my JList:
souvenirList = new JList(itemNames); //itemNames is an array of Strings[] for souvenirs
souvenirList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
souvenirList.setLayoutOrientation(JList.VERTICAL);
scrollPane = new JScrollPane(souvenirList);
scrollPane.setPreferredSize(new Dimension(200,100));
gbc3.gridx = 1;
gbc3.gridy = 1;
centerPanel.add(scrollPane, gbc3);
c.add(centerPanel, BorderLayout.CENTER);
Here is my event handler for the JList:
private class ListHandler implements ListSelectionListener
{
public void valueChanged(ListSelectionEvent le)
{
boolean adjust = souvenirList.getValueIsAdjusting();
if (!adjust)
{
//not sure if/how I should use this
souvIndex = souvenirList.getSelectedIndices();
//I know this is depreciated, I dont know another way
souvItems = souvenirList.getSelectedValues();
for (int i = 0; i < souvItems.length; i++)
{
System.out.println(souvItems[i] + "\n");
//my attempt to save the souvenirs to an accumlator, doesnt work right
souvString += souvItems[i];
}
}
}
}//end List handler
Now, i'm attempting to take what is stored in souvString and print it to the textArea. I have an add to cart JButton which compiles all the data from the GUI to give the user their overall price. I'll focus on the JList since that is what is giving me trouble. Here is the actionEvent() for the add to cart JButton:
else if (ae.getSource() == cartBtn)
{
textArea = new JTextArea(10, 20);
textArea.setFont(f2);
textArea.setLineWrap(true);
textArea.setOpaque(false);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
//appending all the data from the GUI. souvString holds my JList selections
textArea.setText("Team: Tigers" + "\nMeal: " + restaurant
+ "\n\nSeats Ordered: " + seatingType
+ "\nItems Ordered:" + "\n " + souvString);
JOptionPane.showMessageDialog(null, textArea, "OrderReview",
JOptionPane.INFORMATION_MESSAGE,
new ImageIcon(Project7.class.getResource("/tigers.jpg")));
While it prints the souvenirs, weird things happen. I get a combination of null and duplicate souvenirs. I also can't figure out how to use the parallel array of prices that go along with the souvenirs that do print. The parallel array looks like:
private double[] prices = {2.0, 10.0, 15.0, 25.0, 3.0, 5.0, 9.0, 8.0, 12.0, 6.0};
Each price matches w/ it's corresponding souvenir from this array:
private String[] itemNames = {"mug","cap","tee shirt","sweat shirt","pennant","mini stick",
"bobblehead","paper bag","foam paw","thunderstix"};
I've been messing around with this for about 6 hours and can't get it to work, partially bc I don't understand JLists all that well, and partially bc my logic is flawed I feel. I've tried a few different approaches and those didn't work, either. If someone has some suggestions on how I could approach things differently, or even some advice/clarification on how to properly manipulate a JList, i'd be soo appreciative. I really want to understand this! Thanks again stack users :)
Instead of using two arrays i would create a new Class Souvenir in which you save your souvenirs with a subject and a price.
class Souvenir{
String subject;
double price;
}
About the JList i also dont know really much but i thought there is a method you can get a normal List from the JList Object and with this list you could work threw with the indices and put into an ArrayList of Souvenir or something like that.
This is just an idea i can't really give you an example for it so if someone else could verify this i would also be interested :)

bin packing boolean if bin is full

I'm trying to implement a bin packing algorithm using Next Fit online. So I don't know the size of the boxes before hand. Anyway I had a Box class with height and width. A column class which represents and stack of boxes and a Truck class which holds the stacks of boxes and a boolean if the truck is full or not.
However when I'm in my test class I generate a list of boxes and a list of trucks to hold the boxes. I add the boxes to each truck, when the truck is almost out of space, the final box tries to get added, and it doesn't fit, so the boolean isFull is set to true, but the box that went into the method is then lost. How can I add the box to the truck and if its full, use the same box on a call on the next truck?
Many Thanks
code
public void addBoxesNextFit(Box b)
{
if(truck.isEmpty()) // easy first case
{
*make a new column*
*add box to column and add column to truck*
}
else
{
*Get the last column in list*
if(remainingHeight in column > BoxesHeight && widthOftheColumn > boxesWidth)
{
*add box to column*
*add column to truck*
boxesInTruck++;
}
else if(there is enough space to make another column with the box)
{
*Make new column*
*add box to column and column to truck*
boxesInTruck++;
widthofAllColumns += b.getWidth(); //update width of all columns
}
else
{
truckFull = true; // if u can't add the box to 1 of the columns and you can't create a new column
} // then the truck is full
}
}

ListCellRenderer setting all rows to the same color

My intention is to use the ListCellRenderer in order to highlight red cells that contain links that have been visited(or clicked) and green those which have not been visited this works partially but not quite. It seems that the renderer works as far as it concerns marking the cells red. If I however add more rows, they come all red colored thereafter. In addition if I mark two cells that are not adjacent then it marks them all red as well.
I have a class Feed, where I initially had a boolean variable, but I have modified the code so that the m_isRead variable is in the listModel here is the constructor:
public Feed (URL url, String urlName) {
this.m_urlName = urlName;
this.m_observers = new ArrayList<Observer>();
this.m_isRead = isRead;
}
Now this instance variable is set to false in the listModel Class which is the one that contains the renderer.
m_isRead = false.
When using the ListCellRenderer which I now have adjusted so that it does not require this method:
m_feeds.get(index).getM_urlName();
I proceed as follows:
class MyCellRenderer extends JLabel implements ListCellRenderer {
public MyCellRenderer() {
setOpaque(true);
}
public Component getListCellRendererComponent(JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
setText(value.toString());
Color background = Color.GREEN;
Color foreground = Color.BLACK;
//find out if the specific has been read or not
if (m_feeds.get(index).isM_isRead() == true) {
background = Color.RED;
foreground = Color.WHITE;
} else {
background = Color.GREEN;
foreground = Color.BLACK;
};
setBackground(background);
setForeground(foreground);
return this;
}
}
Then I have another inner class with a method which I use to get the selected item, at that point I set m_isRead to true (to read) this is now independent from the Feed class and the code which related to it has been commented out:
public class ChangeSelectedIndex implements ListSelectionListener {
#Override
public void valueChanged(ListSelectionEvent e) {
for (int i = 0; i < m_listModel.size(); i++) {
if (m_listModel.getElementAt(i) != null) {
m_urlName = m_list.getSelectedValue();
initiateParsing();
m_updateFeedButton.setEnabled(true);
// TODO fix behavior for cell renderer
//this sets the value of the feed being clicked to true
// m_feeds.get(i).setM_isRead(true);
m_isRead = true;
}
}// end for
}
}
Now the result is the same, if I add the rows they are green and that is correct, if I click on each row each turns read provided that I have clicked the adjacent rows to the first one I click but if I, for example, have four rows and I click the first row and the last row, all the rows, including those in between (which I have not clicked) turn red. Likewise, if I add new rows they come in red. That is if I click even one of those rows then the ones I add thereafter will be red.
Can anybody help?
Thank you in advance,
Cheers
After a while thinking about it I have concluded that there was nothing wrong my original cell renderer, it has had to do with the list model itself. The JList simply did not support multiple NON contiguous selection without clicking the Ctrl button right out of the box. This is what triggered further searching on my side on how to emulate the Ctrl click down; which I found here on answer number 8 (working code):
Individual and not continuous JTable's cell selection
The interesting here is adding the mouse event to the list. This mouse event emulates a Ctrl down event, which the ListSelectionModel which is used by JList as well as JTable is set to MULTPLE_SELECTION_INTERVAL it behaves as desired. That is, the user now able to click on whatever feed, even if it is not contiguous, and it will color the desired feed or feeds without coloring whatever unclicked feed may lay in between.
As for the renderer, it would suffice to use the isSelected parameter which comes in through with its method getListCellRenderer(). However, in my case, what I had done has the same effect with the addition that I was using an array to add all the statuses of the feeds, meaning, read or unread. Proceeding this way, I had in mind that if I closed the program and save the feed list, including its isRead parameter set to either true or false, then later on upon retrieving the feed list, the same feed status would be restored from, for example, a file, or at least that is what I had in mind.

One to Many search using AND condition

I have the following product which contain many colors.
I wish to find the product which contain at least RED and GREEN.
Product class
String id;
List<Color> colors{};
Color class
id
color
kindly ignore the syntax error.
I'm able to use the following to search OR condition.
Criteria criteria = createCriteria();
criteria.createAlias("colors","colors");
List<String> colorsList = new LinkedList();
colorsList.add("GREEN");
colorsList.add("RED");
criteria.add(Restriction.in("colors.color",colorsList);
The above will give me products which has red or green in their colors BUT not products which contain at least RED AND GREEN.
Example
Product: RED GREEN - PASS
Product: RED GREEN YELLOW - PASS
Product: RED YELLOW - FAIL
Thanks in advance.
the idea is we select all products with the colors and count each product, then products with both colors should have a count of 2 as the number of colors
DetachedCriteria colorCrit = DetachedCriteria.For(Product.class)
.createAlias("colors","color")
.add(Restriction.eq("color.color", "RED")
.add(Restriction.eq("color.color", "GREEN")
.SetProjection(Projections.Group("id"))
.add(Restriction.eq(Projections.rowCount(), 2));
Criteria criteria = createCriteria()
.add(Subqueries.in("id", colorCrit)
.list();
Update:
there is an issue for hibernate for exactly this. the last comment describes how to use.

Categories