Index of the tab that was closed [duplicate] - java

In a JTabbedPane, I associated a custom-made Data object to each added tab. I also have a corresponding Metadata object that shows up in another panel when the tab is selected. The problem I have now is when a tab is closed, the metadata panel shows the metadata of the Data object in the tab that just gets closed. Ideally, I want the panel to show the metadata for the in-focus tab that the user sees. However, the act of closing a tab means the “selected tab” is the tab being closed, so tabpane.getSelectedIndex() would not work. How can I get the tab that is in focus after closing a tab? Thank you in advance!

Devil is in the detail, which you provided none.
I did a quick test and discovered that, ChangeListener is called before ContainerListener, which is a real pain, but, it was always reporting the correct index.
So, what you need to do is marry the two together, so that, both will update the meta data pane when they are called.
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("One", new TabPane(tabbedPane));
tabbedPane.addTab("Two", new TabPane(tabbedPane));
tabbedPane.addTab("Three", new TabPane(tabbedPane));
tabbedPane.addTab("Four", new TabPane(tabbedPane));
tabbedPane.addContainerListener(new ContainerListener() {
#Override
public void componentAdded(ContainerEvent e) {
}
#Override
public void componentRemoved(ContainerEvent e) {
System.out.println("Removed " + e.getChild());
}
});
tabbedPane.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
System.out.println(tabbedPane.getSelectedIndex());
}
});
JFrame frame = new JFrame();
frame.add(tabbedPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TabPane extends JPanel {
private JTabbedPane parent;
public TabPane(JTabbedPane parent) {
this.parent = parent;
setLayout(new GridBagLayout());
JButton btn = new JButton("Close");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
parent.remove(TabPane.this);
}
});
}
}
}

Related

How to set scroll to the top - JPanel that has multiple JTextareas inside Jscrollpane

I have a JPanel in a JScrollPane.
The JPanel contains multiple JTextAreas vertically.
I like to keep the scroll of the scrollpane to the top whenever the page is refreshed.
Currently, the scroll always starts from the bottom.
this is my current code and it doesn't work..
panel.invalidate();
panel.revalidate();
panel.repaint();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
((JPanel) panel).setLocation(new Point(0, 0));
}
});
I've also tried adding this code below to scrollpane, but it doesn't work..
scrollPanel.getViewport().setViewPosition( new Point(0, 0) );
I've looked into other stackoverflow questions and they use Jtextarea inside Jscrollpane (they solved it using setCaretPosition(0), however I can't use the same function to the panel). In my case, there is an extra layer.
How can I solve this..?
EDIT**
Based on advice from Pavlo Viazovskyy, I've also tried this below and it still doesn't work for me.. :(
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
scrollPane.getVerticalScrollBar().setValue(0);
}
});
Thank you very much for all the comments.
sorry I didn't give a full proper example in the question as there were too many different classes involved..
In my case, textareas inside Panel inside ScrollPane, I made the scroll to the top by default by using setViewPosition method to scrollPane in the invokelater method.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
scrollPane.getViewport().setViewPosition( new Point(0, 0) );
}
});
For when you don't have direct access to the JScrollPane, you can simply use JComponent#scrollRectToVisible
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class ScrollTest {
public static void main(String[] args) {
new ScrollTest();
}
public ScrollTest() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.add(new JScrollPane(new BigPane()));
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BigPane extends JPanel {
public BigPane() {
setLayout(new BorderLayout());
JButton scroll = new JButton("Scroll to top");
add(scroll, BorderLayout.SOUTH);
scroll.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
scrollRectToVisible(new Rectangle(0, 0, 1, 1));
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
}
Yes, you could walk the component hierarchy till you found a JViewport, but this method does it for you.
Just remember though, the Rectangle is relative to the component which called the method, so if I used the JButton instead, it would try and make the JButton visible, not the panel

How to run panels one after another in a same frame

In my application, I have a main frame that holds a base panel. The base panel will hold 4 sub-panels. So lets say, the first sub panel appears, do a process on a data, then it must let the second sub panel to appear and also pass the result data to it. The the second sub panel must do another calculation and pass it to third one and so on.
I used card Layout for this, but I do not know how to make panels visible one after end of the previous panel's work.
Here is a simplified version:
public class LittleCardLayout{
public static void main(String[] args) {
new LittleCardLayout();
}
public LittleCardLayout(){
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BasePane());
frame.setSize(800, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Base Panel:
public class BasePane extends JPanel{
public BasePane() {
setLayout(new BorderLayout());
CardLayout cl = new CardLayout();
JPanel mainView = new JPanel(cl);
mainView.add(new JPanel(), "empty");
mainView.add(new TopPanel(), "toppanel");
cl.show(mainView, "toppanel");
add(mainView, BorderLayout.NORTH);
}
}
Sub Panel 1:
public class TopPanel extends JPanel {
int myValue = 23;
int newVal;
public TopPanel(){
JButton btn = new JButton("Load Value");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
newVal= 23-3;
add(new BottomPanel(newVal), "toppanel");
}
});
add(btn, BorderLayout.NORTH);
}
}
Sub Panel 2:
public class BottomPanel extends JPanel {
int num;
JLabel myLabel = new JLabel();
BottomPanel(int num){
this.num = num;
num = num*5;
myLabel.setText(Integer.toString(num));
add(myLabel);
}
}
it must work like a step by step process.
So any idea to pass data from one panel to another and make them visible after one another?
Also is it the right way to make such a step by step process, or there is a better idea?
As each panel completes it's processing, it should send a notification back to the parent panel, telling it that it has finished. The parent panel would then decide what to do.
The child panel should NEVER make navigation decisions...
This is pretty basic example of the concept.
It uses two interfaces (I'm a stickler for coding to interface and limiting exposure of implementation details), a Processor which processes stuff and a ProcessListener which is used to notify the registered listener that the Processor has finished.
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BasePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BasePane extends JPanel {
private List<Processor> order;
private CardLayout cardLayout;
private ProcessListener processListener;
public BasePane() {
cardLayout = new CardLayout();
setLayout(cardLayout);
order = new ArrayList<>(4);
processListener = new ProcessListener() {
#Override
public void processingComplete(Processor source) {
int nextIndex = order.indexOf(source) + 1;
System.out.println(nextIndex);
if (nextIndex < order.size()) {
Processor next = order.get(nextIndex);
System.out.println(next.getName());
cardLayout.show(BasePane.this, next.getName());
next.startProcessing();
}
}
};
createProcessor("Meaning of life");
createProcessor("Apple Pi");
createProcessor("Thermal dynamics");
createProcessor("Microsoft Word");
Processor first = order.get(0);
cardLayout.show(BasePane.this, first.getName());
first.startProcessing();
}
protected Processor createProcessor(String name) {
ProcessorPane pane = new ProcessorPane(name);
pane.setProcessListener(processListener);
add(pane, name);
order.add(pane);
return pane;
}
}
public interface Processor {
public void setProcessListener(ProcessListener listener);
public ProcessListener getProcessListener();
public void startProcessing();
public String getName();
}
public interface ProcessListener {
public void processingComplete(Processor source);
}
public class ProcessorPane extends JPanel implements Processor {
private ProcessListener listener;
public ProcessorPane(String name) {
setName(name);
setLayout(new GridBagLayout());
add(new JLabel(name));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void setProcessListener(ProcessListener listener) {
this.listener = listener;
}
#Override
public void startProcessing() {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
getProcessListener().processingComplete(ProcessorPane.this);
}
});
timer.setRepeats(false);
timer.start();
}
#Override
public ProcessListener getProcessListener() {
return listener;
}
}
}
The basic concept could be expanded so that Processor had a getValue and setValue (for example) methods which would allow the controller (in this case the BasePane) to pass information from one Processor to another
This is just the implementation based on your current approach. I might be possible to use a different approach which separated the UI from the processing, so that you had a "processing controller" which was controlling the processing work flow and passing notifications back to the UI (or registered listeners) about it's current state of operations.
Equally, you could use the above idea, but instead use polling, periodically requesting information from the "processing controller"...just as some ideas of the top of the head

accessing swing component of other class

I have two classes mainpanel.java and subpanel.java. The subpanel.class contains a checkbox and some labels. I want to change the setSelected() and setText() of these components when i click some buttons in the mainpanel.java .
I have created a method in subpanel.java which i call from mainpanel.java and pass the boolean values.
public void schedulerchange(boolean check){
System.out.println("checked"+check);
scheduleenabler.setEnabled(check);
scheduleenabler.setSelected(check);
scheduleinfo.setText("Scheduler in On");
//subpanel21.updateUI();
}
When i call this function from mainpanel.java the function is called but the values don't change unless i make jcheckbox and jlabel static. But from what i learned we should not use static components unless very necessary.
Is there some other way to change the components?
If I have understood your question then I think you want to write a separate ActionListener class and perform action there which will enable or disable the JCheckBox in the UI-class. The below code shows that. Pass your checkbox reference to that PerformAction class and make it enabled or disabled by clicking on the button.
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MainClass {
MainClass() {
JFrame jfrm = new JFrame("JTable Demo");
jfrm.setLayout(new FlowLayout());
jfrm.setSize(460, 180);
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JCheckBox check = null;
// Get the Panel from the subclass;
JPanel panel = new CheckBox().getCheckBoxPanel();
// From the compoenents present in the panel get the CheckBox compoenent.
for(int i = 0; i < panel.getComponentCount(); i++) {
if(panel.getComponent(i) instanceof JCheckBox) {
check = (JCheckBox) panel.getComponent(i);
}
}
JButton button = new JButton("Click");
// Pass the CheckBox Compoenent to the ActionListener.
button.addActionListener(new PerformAction(check));
jfrm.add(button);
jfrm.add(panel);
jfrm.setVisible(true);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new MainClass();
}
});
}
}
class PerformAction implements ActionListener {
JCheckBox check = null;
public PerformAction(JCheckBox checkBox) {
check = checkBox;
}
#Override
public void actionPerformed(ActionEvent e) {
boolean checkStatus = check.isSelected();
if(checkStatus == true) {
check.setEnabled(false);
check.setSelected(false);
} else {
check.setEnabled(true);
check.setSelected(true);
}
}
}
class CheckBox {
public JPanel getCheckBoxPanel() {
JPanel checkPanel = new JPanel();
JCheckBox check = new JCheckBox();
checkPanel.add(new JLabel("CheckBox"));
checkPanel.add(check);
return checkPanel;
}
}
This is not an appropriate use of updateUI(), which "Resets the UI property to a value from the current look and feel." Using revalidate(), as suggested in a comment, would be helpful only if components are added to, or removed from, the enclosing Container. Instead, invoke repaint() directly on the sub-panel instance. For greater flexibility, use the observer pettern suggested here.
Addendum: This example use Action to encapsulate the button's behavior. Because the checkbox's selected state is a bound property, the component is repainted automatically, but you can invoke repaint() explicitly if needed.
Addendum: Update to pass a reference as a parameter.
Addendum: In this variation, the parameter is a reference to the exported Action.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see https://stackoverflow.com/a/14412516/230513 */
public class Example {
private void display() {
JFrame f = new JFrame("Example");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(0, 1));
JPanel panel = new JPanel();
final JCheckBox check = new JCheckBox("Check");
Action checkAction = new AbstractAction("Update") {
#Override
public void actionPerformed(ActionEvent e) {
check.setSelected(!check.isSelected());
}
};
panel.add(check);
f.add(panel);
f.add(new SubPanel(checkAction));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static class SubPanel extends JPanel {
public SubPanel(final Action action) {
this.add(new JButton(action));
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Example().display();
}
});
}
}

Java - JTextArea - modifying after giving away focus?

Here is a simple version of my code:
JTextArea textareaA = new JTextArea(...);
JTextArea textareaB = new JTextArea(...);
textareaA.addModificationListener(new Modification Listener()
{
public void modified(Modifiable arg0){
if (textareaA.getValue().contains("\t"))
{
textareaA.setValue(textareaA.getValue().trim());
textareaB.getTextComponent().requestFocusInWindow();
}
}
});
The problem is, when I try different ways to change the value of A, B never gets the focus. If I turn off the focusable of A, I can't turn it back on. I need to remove the tab from A, then give focus to B, allowing the user to click back to A if needed, and the tab be gone. The above code causes the focus to stay in A, even though I had B request it. Is this a timing issue maybe?
Thank you!
As far as I understood your question, you want a user to be able to type "Tab" and switch to the next textarea. You also want to trim the last "Tab" entered.
Besides the fact that if you type a "Tab" in the middle of the text, it will not get removed, the next code (based and elaborated from your own) seems to do what you want:
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class Test {
protected void initUI() {
final JFrame frame = new JFrame();
frame.setTitle("Test dialog synch");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout());
final JTextArea textareaA = new JTextArea(24, 80);
final JTextArea textareaB = new JTextArea(24, 80);
textareaA.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
textareaB.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
textareaA.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void changedUpdate(DocumentEvent e) {
modified();
}
#Override
public void insertUpdate(DocumentEvent e) {
modified();
}
#Override
public void removeUpdate(DocumentEvent e) {
modified();
}
public void modified() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (textareaA.getText().contains("\t")) {
textareaA.setText(textareaA.getText().trim());
textareaB.requestFocusInWindow();
}
}
});
}
});
panel.add(textareaA, BorderLayout.NORTH);
panel.add(textareaB, BorderLayout.SOUTH);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test().initUI();
}
});
}
}

how to stop JPopupMenu show() from visually un-selecting the list item clicked on

Right now when a user right clicks on a selected JList item in my program the resulting JPopupMenu clears the selection (at least visually) until the popup menu is closed. This isn't consistent with the native look and feel of any platform I know of. The item should stay visually selected or have a selected-color border around it. But I can't find anythin in the API about popup menus changing selection appearance. Is there any way I can control this behavior?
How are you implementing your Mouse Listener that shows the popup? I have created a test application to demonstrate the behaviour of List selections and popup menus that I would typically expect. On Windows with Java 1.5/6 this behaves correctly.
Maybe this will help you with your particular problem.
package jlist;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class Test extends JPanel implements ListSelectionListener {
private static final String ACTION_FEED = "Feed";
private JList list;
private JPopupMenu menu;
// Initialise a JList and add to JPanel.
public Test() {
super(new BorderLayout());
list = new JList(new Object[]{"Badger", "Ferret", "Stoat", "Weasel"});
initActions();
list.addListSelectionListener(this);
// Add mouse listener
list.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) showPopup(e);
}
#Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) showPopup(e);
}
private void showPopup(MouseEvent e) {
menu.show(e.getComponent(), e.getX(), e.getY());
}
});
add(new JScrollPane(list), BorderLayout.CENTER);
valueChanged(null);
}
// Triggered when List Selection changes. Used to control Actions enabled state.
public void valueChanged(ListSelectionEvent e) {
boolean selected = list.getSelectedValue() != null;
getActionMap().get(ACTION_FEED).setEnabled(selected);
}
// Initialise Actions and Popup Menu
private void initActions() {
menu = new JPopupMenu();
Action feed = new AbstractAction(ACTION_FEED) {
public void actionPerformed(ActionEvent e) {
String value = (String) list.getSelectedValue();
JOptionPane.showMessageDialog(Test.this, "Fed " + value);
}
};
getActionMap().put(ACTION_FEED, feed);
menu.add(feed);
}
public static void main(String [] args) {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(new Test());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}

Categories