I'm developing a dynamic menu using Primefaces and JSF 2.2.
The problem is that it's not inserting the menu id. Looking primefaces' code it hits a code that will always be false:
BaseMenuRenderer:
protected boolean shouldRenderId(MenuElement element) {
if(element instanceof UIComponent)
return shouldWriteId((UIComponent) element);
else
return false;
}
TieredMenuRenderer:
writer.startElement("li", null);
if(shouldRenderId(submenu)) {
writer.writeAttribute("id", submenu.getClientId(), null);
}
So, I decided to override primefaces' TieredMenuRenderer, but my override constructor classe is called but the override method is never called.
Here's how I set my facesconfig.xml
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.TieredMenuRenderer</renderer-type>
<renderer-class>ui.jsf.TieredMenuRenderer</renderer-class>
</renderer>
</render-kit>
My override class:
public class TieredMenuRenderer extends org.primefaces.component.tieredmenu.TieredMenuRenderer {
#Override
protected void encodeElements(FacesContext context, AbstractMenu menu, List<MenuElement> elements) throws IOException {
System.out.println("----------- TEST --------------");
super.encodeElements(context, menu, elements);
}
Sysout is never print.
Does anyone know what i'm doing wrong?
Thanks!
Edit:
Add ID to DefaultMenuItem:
DefaultMenuItem item = new DefaultMenuItem();
item.setId(menuItem.getMenuId());// just return a string value.
Adding menu xhtml, the "menucontroller.model" is a primefaces MenuModel which I use a DefaultMenuModel :
Iterating over renderer kit, When I execute the following command, returns the correct renderer-type org.primefaces.component.TieredMenuRenderer
Iterator<String> renderKit = kit.getRendererTypes("org.primefaces.component");
When I execute the following code returns my qualified classname ui.jsf.TieredMenuRenderer#64baec0e:
Renderer renderer = kit.getRenderer("org.primefaces.component", "org.primefaces.component.TieredMenuRenderer");
First of all, My renderer was not overriding the correct renderer. I was overriding org.primefaces.component.tieredmenu.TieredMenuRenderer when I should override org.primefaces.component.MenubarRenderer.
Then, to correct the primefaces id problem I did the following in my rendered class:
#Override
protected boolean shouldRenderId(MenuElement element) {
return true;
}
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
AbstractMenu menu = (AbstractMenu) component;
encodeMarkup(context, menu);
encodeScript(context, menu);
}
EncodedEnd was calling "generateIds()" from primefaces which override my ids.
Full rendered class:
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import org.primefaces.component.menu.AbstractMenu;
import org.primefaces.model.menu.MenuElement;
public class MenuRenderer extends org.primefaces.component.menubar.MenubarRenderer {
#Override
protected boolean shouldRenderId(MenuElement element) {
return true;
}
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
AbstractMenu menu = (AbstractMenu) component;
encodeMarkup(context, menu);
encodeScript(context, menu);
}
}
Related
So, I'm developing a program using the Swing library and I obviously have buttons and menu items. Some of these are supposed to do the same stuff, and I thought using the Command Pattern should be the way to do it, e.g. I have a "save" button and a "save" menu item and they have to implement the same saving algorithm.
Command Pattern seems to be ok but I can't get who's the receiver in all that. Isn't a comand supposed to work on an object which implements some sort of "receiver interface", so that you can use different commands on different receivers coupling them aribtrarily? It looks like there's no "receiver" in my implementation of the pattern.
Another doubt i have is should a command be implemented as a singleton, since you could potentially call its functions from differents parts of the same project, and it would be handly to instantiate it only once and make it statically invokable?
Thank you.
" I obviously have buttons and menu items. Some of these are supposed to do the same stuff,"
As #nIcEcOw noted, that's what Actions are for. This Answer Shows exactly this.
As stated in the How to use Actions :
An Action can be used to separate functionality and state from a component. For example, if you have two or more components that perform the same function, consider using an Action object to implement the function. An Action object is an action listener that provides not only action-event handling, but also centralized handling of the state of action-event-firing components such as tool bar buttons, menu items, common buttons, and text fields. The state that an action can handle includes text, icon, mnemonic, enabled, and selected status.
An There only three Actions. Ont to open, save, and new. Each Action has an ActionCommand, and icon, and and action to perform. Both the JMenuItem and JToolBar button share the same Action and do the same thing. Here is the code you can run.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public class ActionTest {
public ActionTest() {
ImageIcon openIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/open.gif"));
ImageIcon saveIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/save.gif"));
ImageIcon newIcon = new ImageIcon(
ActionTest.class.getResource("/resources/image/new.gif"));
Action openAction = new AbstractAction("Open", openIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Open File");
}
};
Action saveAction = new AbstractAction("Save", saveIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Save File");
}
};
Action newAction = new AbstractAction("New", newIcon) {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("New File");
}
};
JMenuItem openMenuItem = new JMenuItem(openAction);
JMenuItem saveMenuItem = new JMenuItem(saveAction);
JMenuItem newMenuItem = new JMenuItem(newAction);
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
fileMenu.add(openMenuItem);
fileMenu.add(saveMenuItem);
fileMenu.add(newMenuItem);
menuBar.add(fileMenu);
JToolBar toolBar = new JToolBar();
toolBar.add(Box.createHorizontalGlue());
toolBar.setBorder(new LineBorder(Color.LIGHT_GRAY, 1));
toolBar.add(newAction);
toolBar.add(openAction);
toolBar.add(saveAction);
JFrame frame = new JFrame("Toolbar and Menu Test");
frame.setJMenuBar(menuBar);
frame.add(toolBar, BorderLayout.PAGE_START);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ActionTest();
}
});
}
}
As stated in the quote from the above mentioned tutorial, you can do more than just add an image and an action command to the Action. You can use it to set mnemonics and accelorators. Here is a custom Action class that takes
An action command String
an icon
a description for tooltips
a mnemonic
and a key accelorator.
private class MyAction extends AbstractAction {
String name;
public MyAction(String name, Icon icon) {
super(name, icon);
this.name = name;
}
public MyAction(String name, Icon icon, String desc,
Integer mnemonic, KeyStroke accelorator) {
super(name, icon);
putValue(Action.SHORT_DESCRIPTION, desc);
putValue(Action.MNEMONIC_KEY, mnemonic);
putValue(Action.ACCELERATOR_KEY, accelorator);
this.name = name;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (name) {
case "Open":
System.out.println("Open");
break;
case "New":
System.out.println("New");
break;
case "Save":
System.out.println("Save");
break;
}
}
}
Here's an instantiation of this Action
Action newAction = new MyAction("New", newIcon,
"Creates a new file",
new Integer(KeyEvent.VK_N),
KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
And here's the new result. You will see the actionCommand in the menu, along with the key mnemonics and accelerators, tooltips, and you will see the jtoolbar buttons share the same traits. You will also see in the final code, that never once once a component created. All you do is add the Action to the JToolBar and the JMenu and let them work their magic.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ActionInterfaceDemo extends JFrame {
public ActionInterfaceDemo() {
ImageIcon openIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/open.gif"));
ImageIcon saveIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/save.gif"));
ImageIcon newIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/new.gif"));
Action openAction = new MyAction("Open", openIcon,
"Opens a file",
new Integer(KeyEvent.VK_O),
KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));
Action saveAction = new MyAction("Save", saveIcon,
"Saves a file",
new Integer(KeyEvent.VK_S),
KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
Action newAction = new MyAction("New", newIcon,
"Creates a new file",
new Integer(KeyEvent.VK_N),
KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
setJMenuBar(menuBar);
menuBar.add(fileMenu);
fileMenu.add(newAction);
fileMenu.add(openAction);
fileMenu.add(saveAction);
JToolBar toolBar = new JToolBar("Alignment");
toolBar.setBorder(BorderFactory.createLineBorder(Color.BLUE));
toolBar.add(Box.createHorizontalGlue());
toolBar.add(newAction);
toolBar.add(openAction);
toolBar.add(saveAction);
add(toolBar, BorderLayout.PAGE_START);
add(new JScrollPane(new TextArea(10, 40)), BorderLayout.CENTER);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("Action Interface Demo");
pack();
setLocationByPlatform(true);
setVisible(true);
}
private class MyAction extends AbstractAction {
String name;
public MyAction(String name, Icon icon) {
super(name, icon);
this.name = name;
}
public MyAction(String name, Icon icon, String desc,
Integer mnemonic, KeyStroke accelorator) {
super(name, icon);
putValue(Action.SHORT_DESCRIPTION, desc);
putValue(Action.MNEMONIC_KEY, mnemonic);
putValue(Action.ACCELERATOR_KEY, accelorator);
this.name = name;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (name) {
case "Open":
System.out.println("Open");
break;
case "New":
System.out.println("New");
break;
case "Save":
System.out.println("Save");
break;
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new ActionInterfaceDemo();
}
});
}
}
UPDATE
The better explain the relationship of Action and Command Patterns
As noted in Command Pattern
The command pattern is a commonly used pattern which encapsulates a method call or action-like code into a single class. The advantages of being able to package a method (or methods) into a class become evident when you have multiple invokers for a single action (for example a button and a menu item may perform the same action).
In Swing and Borland Delphi programming, an Action is a command object. In addition to the ability to perform the desired command, an Action may have an associated icon, keyboard shortcut, tooltip text, and so on. A toolbar button or menu item component may be completely initialized using only the Action object.
So basically Swing uses the concept of the command pattern through the use of Actions
As for OP's question
"Command Pattern seems to be ok but I can't get who's the receiver in all that."
As for the receiver, the wiki uses a text editor as an example and defines the receiver as such
Receiver, Target Object: the object that is about to be copied, pasted, moved, etc. The receiver object owns the method that is called by the command's execute method. The receiver is typically also the target object. For example, if the receiver object is a cursor and the method is called moveUp, then one would expect that the cursor is the target of the moveUp action. On the other hand, if the code is defined by the command object itself, the target object will be a different object entirely.
The main more components of a Command Pattern are stated as follows
Four terms always associated with the command pattern are command, receiver, invoker and client.
Client, Source, Invoker: the button, toolbar button, or menu item clicked, the shortcut key pressed by the user.
So to put it all together:
The MenuItem (client) invokes it
Action (command object) which calls it actionPerformed which in turn
Invokes an method on the receiver.
The wiki article is good read with a Java example
When two or more components are mean to do exactly the same thingy, one should look at Action, which reduces the duplicate code.
Small example for further help :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ActionExample {
private JFrame frame;
private JButton button;
private JMenuItem exitItem;
private Action commonActions;
private class CommonActions extends AbstractAction {
public CommonActions(String title, String desc) {
super(title);
putValue(SHORT_DESCRIPTION, desc);
}
#Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(frame,
"Closing Frame", "Information", JOptionPane.INFORMATION_MESSAGE);
frame.dispose();
}
};
private void displayGUI() {
frame = new JFrame("Action Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
commonActions = new CommonActions("Exit", "To Exit the Application");
JPanel contentPane = new JPanel();
button = new JButton();
button.setAction(commonActions);
contentPane.add(button);
frame.setJMenuBar(getMenuBar());
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JMenuBar getMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
exitItem = new JMenuItem(commonActions);
fileMenu.add(exitItem);
menuBar.add(fileMenu);
return menuBar;
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
#Override
public void run() {
new ActionExample().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
ADDED an example with SINGLETON PATTERN (though I am not sure of this approach(about how good this approach is))
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ActionExample {
private JFrame frame;
private JButton button;
private JMenuItem exitItem;
private void displayGUI() {
frame = new JFrame("Action Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
CommonActions.setValues(frame);
JPanel contentPane = new JPanel();
button = new JButton();
button.setAction(CommonActions.getInstance());
contentPane.add(button);
frame.setJMenuBar(getMenuBar());
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JMenuBar getMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
exitItem = new JMenuItem(CommonActions.getInstance());
fileMenu.add(exitItem);
menuBar.add(fileMenu);
return menuBar;
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
#Override
public void run() {
new ActionExample().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
class CommonActions extends AbstractAction {
private static CommonActions commonActions = null;
private static JFrame frame = null;
static {
try {
commonActions = new CommonActions("Exit", "To Exit the Application");
} catch (Exception e) {
throw new RuntimeException("BINGO, an error");
}
}
private CommonActions(String title, String desc) {
super(title);
putValue(SHORT_DESCRIPTION, desc);
}
public static CommonActions getInstance() {
return commonActions;
}
public static void setValues(JFrame f) {
frame = f;
}
#Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(frame,
"Closing Frame", "Information", JOptionPane.INFORMATION_MESSAGE);
frame.dispose();
}
}
I'm trying to paint component inside paint(Graphics) method of JPanel.
The following code snippet works just fine, a JButton is painted nicely in my JPanel:
#Override
public void paint(Graphics g) {
super.paint(g);
JButton btn = new JButton("hello");
Dimension dim = btn.getPreferredSize();
btn.setSize(dim.width, dim.height);
btn.paint(g); // paint the button
}
The code snippet works perfectly also for other components (JLabel, JTree, ...) except JPanel.
The following code will cause very strange NullPointerException at java.awt.Window.access$700(Window.java:132).
#Override
public void paint(Graphics g) {
super.paint(g);
JPanel panel = new JPanel();
panel.setSize(10, 10);
panel.paint(g); // paint the panel
}
Here the full stacktrace:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at java.awt.Window.access$700(Window.java:132)
at java.awt.Window$1.isOpaque(Window.java:3458)
at javax.swing.RepaintManager.getVolatileOffscreenBuffer(RepaintManager.java:983)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1395)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:294)
at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
at javax.swing.JComponent.paint(JComponent.java:1015)
at test.paintcontainer.TestPaintContainerMain$TestContentPane.paint(TestPaintContainerMain.java:48)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paint(JComponent.java:1038)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:567)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5131)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:278)
at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
at javax.swing.JComponent.paint(JComponent.java:1015)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:21)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:60)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
at java.awt.Container.paint(Container.java:1780)
at java.awt.Window.paint(Window.java:3375)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:713)
at javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:693)
at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:125)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:641)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:602)
at java.awt.EventQueue$1.run(EventQueue.java:600)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:611)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Any idea how to solve this problem? I need to paint JPanel inside paint(Graphics) method.
I wrote a simple test application which you can copy-paste to reproduce the aforementioned exception:
package test.paintcontainer;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class TestPaintContainerMain extends JFrame {
public static void main(String[] args) {
TestPaintContainerMain test = new TestPaintContainerMain();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setBounds(0, 0, 300, 200);
test.setContentPane(new TestContentPane());
test.setVisible(true);
}
static class TestContentPane extends JPanel {
JRadioButton paintButtonCheck;
JRadioButton paintPanelCheck;
public TestContentPane() {
paintButtonCheck = createRadioButton("paint button", true);
paintPanelCheck = createRadioButton("paint panel", false);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(paintButtonCheck);
buttonGroup.add(paintPanelCheck);
add(paintButtonCheck);
add(paintPanelCheck);
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.translate(100, 100);
if (paintButtonCheck.isSelected()) {
createButton().paint(g);
} else {
createPanel().paint(g);
}
}
private JButton createButton() {
JButton button = new JButton("button");
button.setSize(button.getPreferredSize().width, button.getPreferredSize().height);
return button;
}
private JPanel createPanel() {
JPanel panel = new JPanel();
panel.setBackground(Color.GREEN);
panel.add(createButton());
panel.setSize(panel.getPreferredSize().width, panel.getPreferredSize().height);
return panel;
}
private JRadioButton createRadioButton(String title, boolean selected) {
JRadioButton radio = new JRadioButton(title, selected);
radio.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
TestContentPane.this.repaint();
}
});
return radio;
}
}
}
This is most likely not a bug in Swing, but more of a problem because you are trying to paint a component which has not yet been realized, meaning it has no active graphic context. You can realize a component by adding it to already realized component like your JFrame - which itself gets realized by setVisible(true).
Also one should probably never call JComponent.paint(Graphics) manually, because this is the job of Swing (more precisely the Event Dispatcher Thread) - it even says so in the documentation of the paint method:
Applications should not invoke paint directly, but should instead use the repaint method to schedule the component for redrawing.
What you can call is the method printAll(Graphics g), which paints the component and all its subcomponents. Also in Swing one should also not override paint but paintComponent.
So here is a test code:
JButton button = createButton();
JPanel panel = createPanel();
public TestContentPane() {
paintButtonCheck = createRadioButton("paint button", true);
paintPanelCheck = createRadioButton("paint panel", false);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(paintButtonCheck);
buttonGroup.add(paintPanelCheck);
add(paintButtonCheck);
add(paintPanelCheck);
//Hack, just prove something (realize both components)
add(panel);
add(button);
}
...
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.translate(100, 100);
if (paintButtonCheck.isSelected()) {
button.paintAll(g);
} else {
panel.paintAll(g);
}
g.translate(-100, -100);
}
This should work (although you will obviously have two components on the screen you don't want). Also note "reset" the graphics object, because it will still be used afterwards by Swing.
So this is the theory, but it's not yet an actual solution.
My solution to your problem is: "Don't do it like this"!
Components are not like images, in the sense that they don't look the same everywhere. The output of the paintAll call will be different, depending on how (or where) the components were realized.
So one suggestion is to show actual components. Create your tooltip box, add your panel and your button and let them draw themselves. You can even subclass these components and override their paintComponent() methods, add transparency and all. It will require some work, but Swing was never known to be easy.
I just found a solution.
The only modification of a sample code from my question is that called panel.setDoubleBuffered(false) on JPanel I was trying to paint.
However, I would still consider the exeption to be a Swing bug. If double buffering should be turned off by design you shouldn't get NullPointerException but some other, more meaningful exception which explains the condition.
Here is a fixed sample application:
package test.paintcontainer;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class TestPaintContainerMain extends JFrame {
public static void main(String[] args) {
TestPaintContainerMain test = new TestPaintContainerMain();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setBounds(0, 0, 300, 200);
test.setContentPane(new TestContentPane());
test.setVisible(true);
}
static class TestContentPane extends JPanel {
JRadioButton paintButtonCheck;
JRadioButton paintPanelCheck;
public TestContentPane() {
paintButtonCheck = createRadioButton("paint button", false);
paintPanelCheck = createRadioButton("paint panel", true);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(paintButtonCheck);
buttonGroup.add(paintPanelCheck);
add(paintButtonCheck);
add(paintPanelCheck);
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.translate(100, 100);
if (paintButtonCheck.isSelected()) {
createButton().paint(g);
} else {
createPanel().paint(g);
}
}
private JButton createButton() {
JButton button = new JButton("button");
button.setSize(button.getPreferredSize().width, button.getPreferredSize().height);
return button;
}
private JPanel createPanel() {
JPanel panel = new JPanel();
panel.setBackground(Color.GREEN);
panel.add(createButton());
// --------------------------------
panel.setDoubleBuffered(false); // <-- TURN OFF DOUBLE BUFFERING
// --------------------------------
panel.setSize(panel.getPreferredSize().width, panel.getPreferredSize().height);
return panel;
}
private JRadioButton createRadioButton(String title, boolean selected) {
JRadioButton radio = new JRadioButton(title, selected);
radio.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
TestContentPane.this.repaint();
}
});
return radio;
}
}
}
I am trying to synchronize popups of two combo boxes - call them comboBox and mirrorComboBox.
I want to set popup of mirrorComboBox visible when popup of comboBox becomes visible.
I tried to implement this behavior by adding PopupMenuListener to comboBox and calling mirrorComboBox.setPopupVisible(true) when popupMenuWillBecomeVisible event occurs. It works fine, but unfortunatelly it causes another problem - the popup of comboBox will never be hidden! Event the popupMenuWillBecomeInvisible method is never called after the popup is once set visible.
How to sync popups visibility of two combo boxes?
Here is my implementation:
import java.awt.FlowLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class MirrorPopupsMainFrame extends JFrame implements PopupMenuListener {
public static void main(String[] args) {
new MirrorPopupsMainFrame().setVisible(true);
}
JComboBox comboBox;
JComboBox mirrorComboBox;
public MirrorPopupsMainFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
buildUi();
}
protected void buildUi() {
String[] items = new String[]{"item1", "item2", "item3"};
comboBox = new JComboBox(items);
comboBox.addPopupMenuListener(this);
mirrorComboBox = new JComboBox(items);
setLayout(new FlowLayout());
add(comboBox);
add(mirrorComboBox);
setBounds(0, 0, 300, 200);
}
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
// Not calling the following line will cause
// comboBox's popup to hide correctly.
mirrorComboBox.setPopupVisible(true);
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
mirrorComboBox.setPopupVisible(false);
}
#Override
public void popupMenuCanceled(PopupMenuEvent e) {}
}
I am trying to write a renderer which would process the placeholder attribute on an <h:inputText> component.
I headed to this path after reading JSF 2.0 strips out needed HTML5 attributes and it seems correct. Here's my custom renderer
public class InputRenderer extends com.sun.faces.renderkit.html_basic.TextRenderer{
#Override
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
System.out.println("Rendering :"+component.getClientId());
String placeholder = (String)component.getAttributes().get("placeholder");
if(placeholder != null) {
ResponseWriter writer = context.getResponseWriter();
writer.writeAttribute("placeholder", placeholder, "placeholder");
}
super.encodeBegin(context, component);
}
#Override
public void decode(FacesContext context, UIComponent component) {
super.decode(context, component);
}
#Override
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
super.encodeEnd(context, component);
}
}
And this renderer is registered in faces config as
<render-kit>
<renderer>
<component-family>javax.faces.Input</component-family>
<renderer-type>javax.faces.Text</renderer-type>
<renderer-class>com.example.renderer.InputRenderer</renderer-class>
</renderer>
</render-kit>
This gets registered fine, no issues there.
My intention is to process the placeholder attribute, insert it, and then delegate the processing to super. My above code doesn't work because I'm inserting the attribute at a wrong place. It must be inserted after writer.startElement('input') has executed. However, the startElement must be happening somewhere in the super's encodeBegin() method. So how do I insert a custom attribute ('placeholder' in this case) and then continue the execution flow?
NB: The above code does add a placeholder attribute but not to the input component that I intend to, It writes it to the parent of the Input (since I'm trying to write an attribute before the component itself is actually written in the stream, it applies the attribute to the current component)
This is my way. I added placeholder and data-theme attributes. If you want to add more attributes, you should just add its name to attributes array.
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.sun.faces.renderkit.html_basic.TextRenderer;
public class InputRender extends TextRenderer {
#Override
protected void getEndTextToRender(FacesContext context,
UIComponent component,
String currentValue)
throws java.io.IOException{
String [] attributes = {"placeholder","data-theme"};
ResponseWriter writer = context.getResponseWriter();
for(String attribute : attributes)
{
String value = (String)component.getAttributes().get(attribute);
if(value != null) {
writer.writeAttribute(attribute, value, attribute);
}
}
super.getEndTextToRender(context, component, currentValue);
}
}
You should add this to faces-config.xml file.
<render-kit>
<renderer>
<component-family>javax.faces.Input</component-family>
<renderer-type>javax.faces.Text</renderer-type>
<renderer-class>your.package.InputRenderer</renderer-class>
</renderer>
</render-kit>
You can just override ResponseWriters startElement method, that method is only called once and then you can restore to the original responsewriter object.
import javax.faces.context.*;
import java.io.IOException;
public class InputRenderer extends com.sun.faces.renderkit.html_basic.TextRenderer{
// Put all of the attributes you want to render here...
private static final String[] ATTRIBUTES = {"required","placeholder"};
#Override
protected void getEndTextToRender(FacesContext context,
UIComponent component, String currentValue) throws IOException {
final ResponseWriter originalResponseWriter = context.getResponseWriter();
context.setResponseWriter(new ResponseWriterWrapper() {
#Override
// As of JSF 1.2 this method is now public.
public ResponseWriter getWrapped() {
return originalResponseWriter;
}
#Override
public void startElement(String name, UIComponent component)
throws IOException {
super.startElement(name, component);
if ("input".equals(name)) {
for (String attribute : ATTRIBUTES)
{
Object value = component.getAttributes().get(attribute);
if (value != null)
{
super.writeAttribute(attribute,value,attribute);
}
}
}
});
super.getEndTextToRender(context, component, currentValue);
context.setResponseWriter(originalResponseWriter); // Restore original writer.
}
}
And to override for MyFaces 2.0.8+
package com.hsop.abc.eld;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.apache.myfaces.renderkit.html.HtmlTextRenderer;
public class InputRenderer extends HtmlTextRenderer
{
#Override
protected void renderInputBegin(FacesContext context, UIComponent component)
throws IOException
{
// TODO Auto-generated method stub
super.renderInputBegin(context, component);
Object placeholder = component.getAttributes().get("placeholder");
if(placeholder != null) {
ResponseWriter writer = context.getResponseWriter();
writer.writeAttribute("placeholder", placeholder, "placeholder");
}
}
}
I have Swing application which manipulate shapes. In my toolbar I have a zoom function that the user activate by clicking on a button, then the cursor of the mouse changes to a magnifier which is an image.
My problem is actually the cursor, for some raisons, when I set the cursor on the panel displaying the shapes, I can't save my model and I get the java.io.NotSerializableException: sun.awt.image.ToolkitImage exception.
My model
public class Document implements IDocObservable,Serializable{
...
public void updateCursor() {
Iterator<IDocObserver> iter = docObservers.iterator();
while (iter.hasNext()) {
iter.next().docCursorChanged();
}
}
...
}
The action
public class ZoomInAction extends AbstractAction {
public void actionPerformed(ActionEvent arg0) {
...
Application.getInstance().getActiveDocument().updateCursor();
}
}
The display Panel (note : if I comment the setCursor(..) line, I'am able to save )
public class Window extends JPanel implements IDocObserver{
...
public void paint(Graphics g){
//drawing the differents shapes
}
#Override
public void docCursorChanged() {
setCursor(Utile.getZoomInCursor();
}
}
}
The class that provide the cursor
public class Utile {
private static Image zoomIn = toolkit.getImage(Utile.class.getResource("/images/zoomin_mouse.png"));
...
public static Cursor getZoomInCursor() {
return toolkit.createCustomCursor(zoomIn, hotSpot, "");
}
}
The writing of the object is a standard Java methode with outStream.writeObject(doc);
thanks
You aren't just serializing a model, you are serializing a list of IDocObservers, which includes Window extends JPanel implements IDocObserver. IOW you are serializing a JPanel. Don't do that: see the warning at the top of the Javadoc. You don't need to save the observers along with the observable, surely: can't you make that list transient?