MouseEvents inside JList objects - java

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.

Related

How to get the index of an Arraylist Through mouselistener?

I have putted the Arraylist into a JList and i want to get the value/index of the Arraylist when the mouse is clicked on the Jlist.
i have tried with these lines but it always shows -1 as index on the console for every elements clicked.
here is the part of my code..
list2.addMouseListener(new MouseListener(){
public void mouseClicked(MouseEvent e){
JPanel MousePanel=new JPanel();
JList listp=new JList(PatientDetailArlist.toArray());
int index = listp.getSelectedIndex();
System.out.println("Index Selected: " + index);
String sp = (String) listp.getSelectedValue();
System.out.println("Value Selected: " + sp.toString());
MousePanel.add(listp);
tab.add("tab4",MousePanel);
visibleGui();
}
You add a MouseListener to "list2" which is your JList.
list2.addMouseListener(new MouseListener(){
But then in your code for some reason you create a new JList? Well that JList is not visible on the GUI so there is no way it could have a selected index.
JList listp=new JList(PatientDetailArlist.toArray());
int index = listp.getSelectedIndex();
All you need in the listener code is:
int index = list2.getSelectedIndex();
Or even better is to get the JList component that was clicked from the MouseEvent:
JList list = (JList)e.getSource();
int index = list.getSelectedIndex();
However, that is still not a good solution. What if the user uses the keyboard to select an item? A proper design of a GUI should allow the user to use the mouse or the keyboard.
So you should not be using a MouseListener. Instead you should be using a ListSelectionListener to listen for changes in selection of an item in the list.
Read the section from the Swing tutorial on How to Write a ListSelectionListener for more information and examples to get you started.

JavaFX GridPane Drag and Drop with a dynamic amount of Labels

At the moment I am struggling with a JavaFX problem. I will try to explain it as detailed as possible. If you need any further information, just let me know!
What do I want to achieve:
In the picture below you can see what I am trying to achive
I am creating a variable number of Labels (the amount will differ from time to time)
The references to this labels are stored in an ArrayList. The Labels then are placed on the GridPane
Now I want to be able to drag and drop that Labels in the GridPane
I would like to achieve a solution like in the picture below
When you drag a Label it should be visually shown, where I am dragging it to
When passing another Label the visual effect should show, what happens when I drop it here (nr. 3 in the picture). After I drop the Label there, the Labels on the right are switching their position one field to the right. The opened space where I took the source have to be filled with the Labels on the right.
Here is the picture of what I am dreaming right now
At the moment I am able to create the Labels and place them on the GridPane. But when I try to use an inner class for the .onDragListener I am getting the message, that the Label has to be final. But I can't make it final, because it was created during the runtime and the references are stored in an ArrayList.
Okay here is some Code. I hope it helps.
public class Center {
public static GridPane createCenterContent(List<Tour> tourList){
GridPane gridPaneCenter = new GridPane();
final List<List<Label>> customerLabelList = new ArrayList<List<Label>>();
Label customerLabel = new Label();
for(int i = 0 ; i < tourList.size() ; i++){
customerLabelList.add(i, new ArrayList<Label>());
for(int j = 0 ; j < tourList.get(i).getTour().size() ; j++){
customerLabelList.get(i).add(new Label(tourList.get(i).getTour().get(j).getVorname() + " " + tourList.get(i).getTour().get(j).getNachname() + " \n" +
tourList.get(i).getTour().get(j).getStrasse() + " \n" + tourList.get(i).getTour().get(j).getAdresse()));
customerLabelList.get(i).get(j).getStyleClass().add("kdLabelGridLayout");
customerLabelList.get(i).get(j).setMinWidth(200);
customerLabelList.get(i).get(j).setMinHeight(100);
customerLabel = customerLabelList.get(i).get(j);
customerLabel.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
/* drag was detected, start a drag-and-drop gesture*/
/* allow any transfer mode */
Dragboard db = customerLabel.startDragAndDrop(TransferMode.ANY);
System.out.println("Label wird bewegt!");
}
});
customerLabelList.get(i).get(j).setOnDragOver(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
/* data is dragged over the target */
/* accept it only if it is not dragged from the same node
* and if it has a string data */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
System.out.println("Label berührt anderes Label");
}
});
gridPaneCenter.add(customerLabelList.get(i).get(j), i, j, 1, 1);
}
}
gridPaneCenter.setHgap(10);
gridPaneCenter.setVgap(10);
return gridPaneCenter;
}
}
I am getting an error on the Dragboard db = customerLabel.startDragAndDrop(TransferMode.ANY); line. Eclipse tells me that the variable has to be final.
I have a List which contains Lists of Labels. I am placing that Labels in the GridPane and trying to give them all the drag and drop listeners. The amount of labels in the List> differs from time to time.
What can I do to solve the problem? I am very thankful for any hint or link you guys can provide me with.

How to get specific ID for a JButton?

I'm trying to build a program that utilizes a 3x3 grid of buttons (using Java Swing), so I initialize it with a GridLayout and a loop to create the buttons:
panel.setBorder(BorderFactory.createEmptyBorder(3,3,5,5))
panel.setLayout(new GridLayout(3,3,10,10));
String[] buttons = {"Top Left", "Top Middle", "Top Right", "Middle Left", "Middle", "Middle Right", "Bottom Left", "Bottom Middle", "Bottom Right"};
for(int i = 0; i < buttons.length; i++) {
buttray[i] = new JButton(buttons[i]);
panel.add(buttray[i]);
buttray[i].addActionListener(this);
}
The buttons load just fine, but I do not understand how to use ActionListeners to differentiate between the buttons. When I check the paramString() method from the printout, each button gives the same modifier:
Top Left
ACTION_PERFORMED,cmd=Top Left,when=1431640106712,modifiers=Button1
Top Middle
ACTION_PERFORMED,cmd=Top Middle,when=1431640107566,modifiers=Button1
Top Right
ACTION_PERFORMED,cmd=Top Right,when=1431640107978,modifiers=Button1
Does this modifier value act as the button's identifier, and if so, how do I change it?
There are multiple ways to distinguish which button fired the ActionEvent:
Set/get the action command of each button (eg if (e.getActionCommand().equals("Top Left"))
Use == to compare instances (eg if (e.getSource() == buttray[0] ))
Get the text of the JButton (eg if (e.getSource().getText().equals("Top Left"))
Set/get the name of the JButton (eg if (e.getSource().getName().equals("Top Left"))
Add a different ActionListener to each button (in other words 1:1 Listener to button)
...and perhaps more ways will be added in the comments section below.
you already keep a track of the buttons by the array index i.e. buttray[i]. Use getSource()
public void actionPerformed(ActionEvent e)
{
// TODO Auto-generated method stub
for(int i=0;i<buttray.length;i++)
if(e.getSource()==buttray[i])
{
//code here
}
}

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.

adding items to JList

I want to add items to my List. My list is first initialized by initComponent() called automatically by instructor (I'm using NetBeans, and all GUI componenets are initialized by the prog automatically).
My questions is:
let's say that we have a Frame1, in this frame we have a Button "show images", when click on it
open Frame2 which has JList...
images list are added through Frame3 successfully...
Below is my code where i want to list all images in my list:
private void setImagesToList()
{
***//bLayer is my Business Layer and _getNomOfSelectedImg() returns number of
//images.***
int imagesCount = bLayer._getNomOfSelectedImg();
***// through my searches i fount that i've to create ListModel to hold my items***
DefaultListModel listModel = new DefaultListModel();
if (imagesCount > 0) // there is/are image(s)
{
for(int i=0; i < imagesCount ; i++)
{
// ***i want to add image name and tooltip (image path) ***
String imgName = bLayer._getImageName(i);
String imgPath = bLayer._getImagePath(i);
listModel.add(i, imgName);
break;
}
images_List.setModel(listModel);
}
}
when I run this code it throws NullPointerException in the last line images_List.setModel(listModel);
What to do to display these items, allow multi-selection, adding mouse click event?
Yes, you can add tooltips. You just have to set the tooltip text on the component returned by the renderer. The JList will use those component tooltip's to determine the correct tooltip text. This can be seen in the JList#getTooltipText implementation of which I copied the relevant part
Component rComponent = r.getListCellRendererComponent(
this, getModel().getElementAt(index), index,
lsm.isSelectedIndex(index),
(hasFocus() && (lsm.getLeadSelectionIndex() ==
index)));
if(rComponent instanceof JComponent) {
MouseEvent newEvent;
p.translate(-cellBounds.x, -cellBounds.y);
newEvent = new MouseEvent(rComponent, event.getID(),
event.getWhen(),
event.getModifiers(),
p.x, p.y,
event.getXOnScreen(),
event.getYOnScreen(),
event.getClickCount(),
event.isPopupTrigger(),
MouseEvent.NOBUTTON);
String tip = ((JComponent)rComponent).getToolTipText(
newEvent);
if (tip != null) {
return tip;
}
Could you also update your question with those new questions, as your 'answer with the new question' will float to the bottom
i found my great mistake :( :( i called the functions which set the images to the list before calling initComponent(), that's why the exception was thrown..
thnx all for your answer, but i have to more questions:
1) could i add ToolTipText to the list item, i want to add the image path
2) what did you mean about "my accept ratio"...

Categories