I need to get contents of JPanel component (one of tabs), which a part of JTabbedPane. From class where JTabbedPane is defined there is an event listener which gets current selected tab (on state change).
Here is sample code:
...
Component tab = jTabbedPane1.getSelectedComponent();
...
I need to get all components in that tab. For example:
Component[] comps = tab.getComponents(); // obviously it didn't work
I need this, because I have to disable/enable some buttons depending user rights.
Better to use your own class with the buttons as fields and then be able to directly obtain references to the buttons held by the components, or better still, be able to interact with public mutator methods that can change the button state for you (you want to expose the least amount of information to the outside world as possible -- to encapsulate your information), something like:
// assuming the JButtons are in an array
public void setButtonEnabled(int buttonIndex, boolean enabled) {
buttonArray[buttonIndex].setEnabled(enabled);
}
Or same example for if the buttons are in a HashMap that uses the button text String as the key:
// assuming the JButtons are in an hashmap
public void setButtonEnabled(String buttonMapKey, boolean enabled) {
JButton button = buttonMap.get(buttonMapKey);
if (button != null) {
button.setEnabled(enabled);
}
}
Also, your code suggests that you're using NetBeans to create your Swing code. I suggest that you avoid doing this until you fully understand Swing, that instead you use the tutorials to help you to learn to create Swing by hand as this will give you a much better understanding of the underpinnings of Swing. Then later when you understand it well, sure, use code-generation software to speed up your development time, only now you'll know what it's doing under the surface and you will be able to control it better.
Luck!
I would encapsulate this logic in the panel.
How about extending JPanel to create a RoleAwareButtonPanel that contains your buttons. You could then pass in some kind of Role object and have your panel enable/disable buttons as appropriate.
class Role {
private boolean canCreate;
private boolean canEdit;
//etc...
//getters and setters
}
class RoleAwareButtonPanel extends JPanel {
private JButton createButton;
private JButton editButton;
//other stuff you need for your panel
public void enableButtonsForRole(Role role) {
createButton.setEnabled(role.canCreate());
editButton.setEnabled(role.canEdit());
}
}
Related
I'm having some difficulty implementing the MVC pattern using reflection.
I have the Model. I know nothing about this model. And I'm inferring with reflection;
I also have a View; This view will instantiate a list of objects ( JButton , JTextField , and others, ...) and also has a method to add listeners to buttons, that in turn, will invoke methods on my controller.
Then I have the controller that implements an ActionListener.
All good so far, it works... I can set the buttons programmatically , add listeners, execute methods on the controller and ask the controller to update the model, I believe I'm on the right track with MVC. However, I'm trying to do this with reflection.
E.g: I go to the Model from the Controller. I pick a given class (again, I know nothing about this class) and get ALL the setters of the class.
Then I ask the View to create a series of JTextFields (if that's the case), for all my setters, with a Save button to later on perform the setter from an action listener.
If I get 2 setters, the view creates 2 JTextFields and 2 save buttons. But how do I know, what button was pressed? In other words, how would I know what setter that button belongs to? Normally, this wouldn't be a problem, but like I said, I know nothing about the Model, so the View will might create 1, 2, 3, or a gazillion of buttons with JTextFields, JButtons and so on, ...
I'm developing a Naked Objects framework for a class project. So when I say I know nothing about the Model, is because I'm creating a GUI, based on any Model that might exist. Thus creating me some problems relating my buttons, text fields, lists in my View, to my the methods that need to be executed.
All help is greatly appreciated. Thank you!
The only way you have to distinguish two buttons is by adding state that you know that will be there when the listener you registred fires. Earlier, in my comment, I suggested that you could inherit from the control and add your state there.
public class MyButton extends JButton{
private Runnable onActionPerfomed;
public Runnable getMyAction(){
return onActionPerfomed;
}
public void setMyAction(Runnable r){
onActionPerfomed = r;
}
}
final MyButton button = new MyButton();
button.setName("button xpto");
button.setMyAction(new Runnable() {
#Override
public void run() {
System.out.println("Hey I'm running this on button " + button.getName());
}
});
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() instanceof MyButton){
MyButton btn = (MyButton)e.getSource();
btn.getMyAction().run();
}
}
});
You can achieve a effect like this one by making the Controls implement a interface instead which is more advisable.
Finally Why register the same listener for all Controls? I think that you really should register different listeners because that is what the code I provided does in a different way.
I have 2 forms in Netbeans, 1 form will launch and the user will input information. Then if the user wants to, will open up the other jframe. The jframe that the user was working on will close. Now from the newly opened jframe, the user decides to go back to the previous frame. The problem is when the user does decide to go back to the previous frame, the text in the textboxes are gone, the buttons that were enabled are disabled, and the combobox selection resets. Does anyone know how I can save the state of all these elements? Thank you.
Would something like this help?
public class MyFrame extends JFrame{
private JCheckBox box1;
public MyFrame(){
box1 = new JCheckBox();
}
public void work(){
if(getbox().isSelected){
//work
}else{
//do else
}
}
public JCheckBox getbox1(){
return this.box1;
}
public JCheckBox setSelectedbox1(boolean set){
getbox1().setSelected(set);
}
If you want to keep that object alive you can use:
myFrame.setVisible(false)
myFrame.setVisible(true)
An application should generally only have a single JFrame.
You can probably use a Card Layout to swap different panels. Read the Swing tutorial on How to Use CardLayout for more information and example.
Other options or to use a JTappedPane or JInternalFrames. The tutorial also has section on these components.
I dont know how to make this with simple and easy code.
I can just go to each piece of my code and use setEnabled to false on each component, but I want a easy way.
I want to disable the entire frame, but still want to close/maximize/minize it. do you understand?
And if I use setEnabled(false) on my frame, it disables that options too, the options of windows menu bar you know?
Thanks alot in advance..
Simple,Put all your components in a JPanel and disable the JPanel :-)
You can use a code snippet like below. Insert this method/routine in a utility class. Since the method is static you can call it without that class's instance. And make invocations to this method to enable/disable components (menu items, buttons, text fields etc.)
// Let's say you have a JFrame object called myFrame
// and a reference to its content pane.
// Container container = myFrame.getContentPane();
public static void toggleAbilities(Container container, boolean enabled)
{
Component[] components = container.getComponents();
for(Component component: components)
component.setEnabled( enabled );
}
If you get stuck, I can post overall working code as well.
(Toggling the ability of a set of buttons in a JFrame)
Is it possible to use a single JPanel for multiple tab items in JTabbedPane?
EG:
JTabbedPanel tabs=new JTabbePanel();
JPanel panel=new JPanel();
JButton but=new JButton("TEXT");
but.addActionlistener(this);
panel.add(but)
tabs.add("First",panel);
tabs.add("Second",panel);
An ActionListener is added to the JTabbedPane to notify the program of tab changes (change cur_tab to tab number)
public void actionPerformed(..)
{ System.out.println("Now in "+cur_tab); }
The same component cannot be used for several tabs
taken from here
Sounds like you need a subclass of JPanel. Just create an abstract class that handles the complex layout and have the subclasses create the necessary GUI elements that are required.
If you are just inserting a single text box, button, etc, you may not even need subclasses per tab. Just create multiple instances of the base class and add the component you need.
New panels and other GUI items are relatively cheap. Performance issues in a Swing GUI are more likely to come from event handling or firing too many events rather than how complex or how many components it has. Make things easier to maintain and understand, then worry about performance.
if you'll create class that returns JPanel then yes that's possible
but Notice:
there are long time Bug that two Tabs can't contains same component schemas, with schemas I means for example one Tab contains JPanel + JTextField + JButton, but then second JPanel must contains another Numbers or Type of JComponents
unfortunatelly (nothing special) BugsDatabase isn't accesible in this moment
I had a panel with memory-intensive components on it which I only wanted to create one instance of, but change the behaviour of it using the attractive JTabbedPane.
I did it by creating empty panels for each tab, and a third panel that contains my (single) complicated components. On the StateChange event for the JTabbedPane, I remove the third panel from whichever of the first two it was in, and add it to whichever one is newly selected.
Bit hacky, but it works fine.
You are wrong.
Just set a panel on the first tab in function initComponents() like that:
p.add("1", MainPanel);
Then use:
p.add("2", p.getTabComponentAt(0));
Using this metode you will have the same component on 2 tabs.
You can use StateChanged Event to change actions in this tabs.
For example:
JTabbedPane p = (JTabbedPane)Tabbar;
int idx = p.getSelectedIndex();
if(idx==0){
Do something...
}
if(idx==1){
Do something different...
}
The following will allow you to add the same component with different titles to a JTabbedPane:
JTabbedPane tabbedPane = new JTabbedPane()
{
boolean adding = false;
#Override
public void removeTabAt(int index)
{
if(!adding)
{
super.removeTabAt(index);
}
}
#Override
public void insertTab(String title, Icon icon, Component component, String tip, int index)
{
adding = true;
super.insertTab(title, icon, component, tip, index);
adding = false;
}
};
For most GUI's I've used, when a control that contains text gets the focus, the entire contents of the control are selected. This means if you just start typing, you completely replace the former contents.
Example: You have spin control that is initialized with the value zero. You tab to it and type "1" The value in the control is now 1.
With Swing, this doesn't happen. The text in the control is not selected and the carat appears at one end or another of the existing text. Continuing the above example:
With a Swing JSpinner, when you tab to the spin control, the carat is at the left. You type "1" and the value in the control is now 10.
This drives me, (and my users) up a wall, and I'd like to change it. Even more important, I'd like to change it globally so the new behavior applies to JTextField, JPasswordField, JFormattedTextField, JTextArea, JComboBox, JSpinner, and so on. The only way I have found to do this to add a FocusAdapter to each control and override the focusGained() method to Do The Right Thing[tm].
There's gotta be an easier, and less fragile way. Please?
EDIT: One additional piece of information for this particular case. The form I am working with was generated using Idea's form designer. That means I normally don't actually write the code to create the components. It is possible to tell Idea that you want to create them yourself, but that's a hassle I'd like to avoid.
Motto: All good programmers are basically lazy.
When I've needed this in the past, I've created subclasses of the components I wanted to add "auto-clearing" functionality too. eg:
public class AutoClearingTextField extends JTextField {
final FocusListener AUTO_CLEARING_LISTENER = new FocusListener(){
#Override
public void focusLost(FocusEvent e) {
//onFocusLost(e);
}
#Override
public void focusGained(FocusEvent e) {
selectAll();
}
};
public AutoClearingTextField(String string) {
super(string);
addListener();
}
private void addListener() {
addFocusListener(AUTO_CLEARING_LISTENER);
}
}
The biggest problem is that I haven't found a "good" way to get all the standard constructors without writing overrides. Adding them, and forcing a call to addListener is the most general approach I've found.
Another option is to watch for ContainerEvents on a top-level container with a ContainerListeer to detect the presence of new widgets, and add a corresponding focus listener based on the widgets that have been added. (eg: if the container event is caused by adding a TextField, then add a focus listener that knows how to select all the text in a TextField, and so on.) If a Container is added, then you need to recursively add the ContainerListener to that new sub-container as well.
Either way, you won't need to muck about with focus listeners in your actual UI code -- it will all be taken care of at a higher level.
I haven't tried this myself (only dabbled in it a while ago), but you can probably get the current focused component by using:
KeyboardFocusManager (there is a static method getCurrentKeyboardFocusManager())
an adding a PropertyChangeListener to it.
From there, you can find out if the component is a JTextComponent and select all text.
A separate class that attaches a FocusListener to the desired text field can be written. All the focus listener would do is call selectAll() on the text widget when it gains the focus.
public class SelectAllListener implements FocusListener {
private static INSTANCE = new SelectAllListener();
public void focusLost(FocusEvent e) { }
public void focusGained(FocusEvent e) {
if (e.getSource() instanceof JTextComponent) {
((JTextComponent)e.getSource()).selectAll();
}
};
public static void addSelectAllListener(JTextComponent tc) {
tc.addFocusListener(INSTANCE);
}
public static void removeSelectAllListener(JTextComponent tc) {
tc.removeFocusListener(INSTANCE);
}
}
By accepting a JTextComponent as an argument this behavior can be added to JTextArea, JPasswordField, and all of the other text editing components directly. This also allows the class to add select all to editable combo boxes and JSpinners, where your control over the text editor component may be more limited. Convenience methods can be added:
public static void addSelectAllListener(JSpinner spin) {
if (spin.getEditor() instanceof JTextComponent) {
addSelectAllListener((JTextComponent)spin.getEditor());
}
}
public static void addSelectAllListener(JComboBox combo) {
JComponent editor = combo.getEditor().getEditorComponent();
if (editor instanceof JTextComponent) {
addSelectAllListener((JTextComponent)editor);
}
}
Also, the remove listener methods are likely unneeded, since the listener contains no exterior references to any other instances, but they can be added to make code reviews go smoother.
After reading the replies so far (Thanks!) I passed the outermost JPanel to the following method:
void addTextFocusSelect(JComponent component){
if(component instanceof JTextComponent){
component.addFocusListener(new FocusAdapter() {
#Override
public void focusGained(FocusEvent event) {
super.focusGained(event);
JTextComponent component = (JTextComponent)event.getComponent();
// a trick I found on JavaRanch.com
// Without this, some components don't honor selectAll
component.setText(component.getText());
component.selectAll();
}
});
}
else
{
for(Component child: component.getComponents()){
if(child instanceof JComponent){
addTextFocusSelect((JComponent) child);
}
}
}
}
It works!
The only way I know is to create a FocusListener and attach it to your component. If you want it this FocusListener to be global to all components in your application you might consider using Aspect Oriented Programming (AOP). With AOP is possible to code it once and apply your focus listener to all components instantiated in your app without having to copy-and-paste the component.addFocusListener(listener) code throughout your application..
Your aspect would have to intercept the creation of a JComponent (or the sub-classes you want to add this behaviour to) and add the focus listener to the newly created instance. The AOP approach is better than copy-and-pasting the FocusListener to your entire code because you keep it all in a single piece of code, and don't create a maintenance nightmare once you decide to change your global behavior like removing the listener for JSpinners.
There are many AOP frameworks out there to choose from. I like JBossAOP since it's 100% pure Java, but you might like to take a look at AspectJ.