This is probably an elementary question. However, I have completed reading the 12th Chapter of Java Programming for the Absolute Beginner and have approached the Challenges section. I cannot quite get the progam to display a labeled button with the extended class.
The specification states:
Extend the JPRButton3D class to create a button that displays a label just like
the AWT Button class you're so familiar with by now. As an extra test, override
the isFocusable() method so that your button class can be traversed and make sure you
paint some special graphic to make it obvious when your button has focus.
How can I edit my code of LabelButton3D and LabelButton3DTest to accomplish this task?
An answer to this specification can potentially aid many new Java programmers in extending their own classes.
Thank you very much for your time and cooperation reagrding this matter.
HERE IS THE CODE FOR JPRButton3D:
package jpr.lightweight;
import java.awt.*;
import java.awt.event.*;
/**
* A lightweight 3D Button class that fires actions when clicked.
* When it is enabled it appears {#link #RAISED RAISED}, when
* it is pressed it appears {#link #SUNK SUNK}, and when it is
* not enabled, it appears {#link #FLAT FLAT}.
*/
public class JPRButton3D extends JPRRectComponent3D {
private boolean pressed;
/**
* This <code>JPRButton3D</code>'s <code>ActionListener</code>.
*/
protected ActionListener actionListener;
private String actionCommand;
/**
* Constructs a new <code>JPRButton3D</code> with minimum size
*/
public JPRButton3D() {
this(ABSOLUTE_MIN_WIDTH, ABSOLUTE_MIN_HEIGHT, 1);
}
/**
* Constructs a new <code>JPRButton3D</code> with the given dimensions.
* #param wide the width
* #param high the height
*/
public JPRButton3D(int wide, int high) {
this(wide, high, 1);
}
/**
* Constructs a new <code>JPRButton3D</code> with the given dimensions
* and border magnitude.
* #param wide the width
* #param high the height
* #param border_magnitude the border's magnitude
*/
public JPRButton3D(int wide, int high, int border_magnitude) {
super(wide, high, RAISED, border_magnitude);
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
public void processMouseEvent(MouseEvent e) {
if (isEnabled() & e.getModifiers() == MouseEvent.BUTTON1_MASK) {
switch(e.getID()) {
case MouseEvent.MOUSE_PRESSED:
pressed = true;
current_appearance = SUNK;
repaint();
break;
case MouseEvent.MOUSE_EXITED:
if (pressed) {
pressed = false;
current_appearance = RAISED;
repaint();
}
break;
case MouseEvent.MOUSE_RELEASED:
if (pressed) {
current_appearance = RAISED;
repaint();
if (actionListener != null) {
actionListener.actionPerformed(new ActionEvent(this,
ActionEvent.ACTION_PERFORMED, actionCommand,
e.getModifiers()));
}
}
break;
}
}
super.processMouseEvent(e);
}
/**
* Adds the specified <code>ActionListener</code>
* #param listener <code>ActionListener</code> to add
*/
public void addActionListener(ActionListener listener) {
actionListener = AWTEventMulticaster.add(actionListener, listener);
}
/**
* Removes the specified <code>ActionListener</code>
* #param listener <code>ActionListener</code> to remove
*/
public void removeActionListener(ActionListener listener) {
actionListener = AWTEventMulticaster.remove(actionListener,
listener);
}
/**
* Sets the action command associated with action events.
* #param command The action command.
*/
public void setActionCommand(String command) {
actionCommand = command;
}
/**
* Gets the action command associated with action events.
* #return the action command
*/
public String getActionCommand() {
return actionCommand;
}
/**
* Enables or disables this <code>JPRButton3D</code>.
* #param b <code>true</code> to enable, <code>false</code> to disable
*/
public void setEnabled(boolean b) {
if (b) current_appearance = RAISED;
else current_appearance = FLAT;
repaint();
super.setEnabled(b);
}
}
HERE IS MY CODE FOR LabelButton3DTest (to extend JPRButton3D):
import java.awt.*;
import java.awt.event.*;
import jpr.lightweight.JPRButton3D;
public class LabelButton3DTest extends GUIFrame {
LabelButton3D[] b;
String s;
public LabelButton3DTest() {
super("LabelButton3D Test");
setLayout(new FlowLayout());
b = new LabelButton3D[1];
b[0] = new LabelButton3D("Favorite Button");
b[0] = new LabelButton3D(75, 35, 1);
add(b[0]);
pack();
setVisible(true);
}
public static void main(String args[]) {
new LabelButton3DTest();
}
}
HERE IS MY CODE FOR LabelButton3D:
public class LabelButton3D extends JPRButton3D {
public LabelButton3D(String label) {
}
public LabelButton3D(int wide, int high, int border_magnitude) {
super(wide, high, border_magnitude);
}
}
Related
I am creating a window with JavaFX, using an object oriented approach which will allow different kinds of windows to be created with the same basic framework. These windows will display html content.
I have run into a concurrency problem when trying to add an observer.
Window:
public Window(String linkName, String fName, String wName, int width, int height, GuiObserver o) throws IOException {
wApp = new WindowApp();
wApp.setObserver(o);
new Thread(new Runnable() {
public void run() {
wApp.launch(WindowApp.class,linkName,fName,wName,""+width,""+height);
/*try {
wApp.launch(WindowApp.class,linkName,fName,wName,""+width,""+height);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
}
}).start();
}
This creates the window and sets the observer, but puts the launch into a new thread. I can confirm that the observer is added, but the observer cannot be notified. Here is the WindowApp class:
/**
* Instantiate the class with an html file
* THIS MUST NOT TAKE ANY PARAMETERS
*/
public WindowApp() throws IOException {}
#Override
public void start(Stage s) throws Exception {
this.stage = s;
//get the parameters
List<String> params = getParameters().getRaw();
String linkName = params.get(0);
updateHtmlFromFile(new File(params.get(1)));
windowName = params.get(2);
windowWidth = Integer.parseInt(params.get(3));
windowHeight = Integer.parseInt(params.get(4));
//create the window
stage.setWidth(windowWidth);
stage.setHeight(windowHeight);
Scene scene = new Scene(new Group());
browser = new WebView();
webEngine = browser.getEngine();
webEngine.setJavaScriptEnabled(true);
//add the link which will bind Java and Javascript functionality
Class jC = Class.forName(linkName);
Constructor jCon = jC.getConstructor(browser.getClass(),webEngine.getClass(),stage.getClass());
javascriptLink = (JavascriptLink) jCon.newInstance(browser,webEngine,stage);
javascriptLink.setObserver(gObserver);
//javascriptLink = new JavascriptLink(browser,webEngine,stage);
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(browser);
webEngine.getLoadWorker().stateProperty()
.addListener(new ChangeListener<State>() {
#Override
public void changed(ObservableValue ov, State oldState, State newState) {
if (newState == Worker.State.SUCCEEDED) {
stage.setTitle(windowName);
}
}
});
webEngine.load(htmlUrl);
scene.setRoot(scrollPane);
stage.setScene(scene);
stage.show();
}
/**
* Add an observer to the javascript Link
* #param o
*/
public void setObserver(GuiObserver o) {
gObserver = o;
}
This object also has a "Javascript link" which interacts with the HTML document after its launch:
package gui;
import java.io.FileNotFoundException;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class MainWindowJSLink extends JavascriptLink {
private GuiObserver gObserver;
/**
* This will be called by WindowApp when appropriate, and does not need to be called by a person
* #param b
* #param we
* #param st
* #throws FileNotFoundException
*/
public MainWindowJSLink(WebView b, WebEngine we, Stage st) throws FileNotFoundException {
super(b, we, st);
}
#Override
public void eventTriggered(String eventName) {
System.out.println("Event triggered -> "+eventName);
switch (eventName) {
case "codeEditorContentsChanged":
handleCodeEditorContentsChanged();
break;
}
//notify observers if necessary
this.triggerObserver(eventName, null);
}
/**
* Handle the event that the contents of the code editor have changed
* This will probably setup a timer to wait a few seconds until the user has stopped typing,
* then send the contents to the syntax highlighter and get the result back
*/
private void handleCodeEditorContentsChanged() {
//System.out.println(getJSCodeEditorContents());
}
/**
* Get the raw contents of the code editor (unedited and not stripped of html entities)
* #return
*/
public String getJSCodeEditorContents() {
String contents = (String) super.webEngine.executeScript("getCodeEditorContents()");
return contents;
}
/**
* Set the contents of the code editor
* #param contents String
* NOTE:
*/
public void setJSCodeEditorContents(String contents) {
super.webEngine.executeScript("setCodeEditorContents(`"+contents+"`)");
}
//observer observable stuff
#Override
public void setObserver(GuiObserver o) {
gObserver = o;
gObserver.setJS(this);
System.out.println("MainWindow -> observer set");
}
#Override
public void triggerObserver(String s, Object o) {
gObserver.trigger(s,o);
}
#Override
/**
* Handle information sent from the
*/
public void send(String s, Object o) {
//handle information sent to this object
}
}
Unfortunately, the gObserver.trigger does not execute. Interestingly enough, if I put a System.exit(0) before that trigger is called, the program exits. However, if I put it after it is called (or in the corresponding method), it is not.
Can anyone offer any advice on how to fix this concurrency issue? If you need more information, please let me know, and I will provide it.
I have a jtable with a custom editor and renderer applied to a column to turn the column's contents into a Button, however once i press on any of the buttons, the table loses focus, and the only interactive object in the panel is the button that was just clicked on, which can be clicked on repeatedly.
Here is the button renderer code:
public class ButtonRenderer implements TableCellRenderer {
private Border dsd_originalBorder;
private int in_mnemonic;
private Border dsd_focusBorder;
private JButton dsd_renderButton;
/**
* empty constructor
*/
public ButtonRenderer() {
dsd_renderButton = new JButton();
}
public Component getTableCellRendererComponent(JTable dsd_table, Object dsd_value,
boolean bo_isSelected, boolean bo_hasFocus, int in_row, int in_column) {
if (!WorkplaceConstants.STR_INACTIVE.equals(dsd_table.getValueAt(in_row, dsd_table
.getColumn(Main.hm_language.get(Language.STATUS))
.getModelIndex())))
return new JLabel("");
if (bo_isSelected) {
dsd_renderButton.setForeground(dsd_table.getSelectionForeground());
dsd_renderButton.setBackground(dsd_table.getSelectionBackground());
} else {
dsd_renderButton.setForeground(dsd_table.getForeground());
dsd_renderButton.setBackground(UIManager.getColor("Button.background"));
}
if (bo_hasFocus) {
dsd_renderButton.setBorder(dsd_focusBorder);
} else {
dsd_renderButton.setBorder(dsd_originalBorder);
}
// renderButton.setText( (value == null) ? "" : value.toString() );
if (dsd_value == null) {
dsd_renderButton.setText("");
dsd_renderButton.setIcon(null);
} else if (dsd_value instanceof Icon) {
dsd_renderButton.setText("");
dsd_renderButton.setIcon((Icon) dsd_value);
} else {
dsd_renderButton.setText(dsd_value.toString());
dsd_renderButton.setIcon(null);
}
return dsd_renderButton;
}
/**
* returns the mnemonic to activate the button when the cell has focus
*
* #return the mnemonic
*/
public int m_getMnemonic() {
return in_mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param p_in_mnemonic
* the mnemonic
*/
public void m_setMnemonic(int p_in_mnemonic) {
this.in_mnemonic = p_in_mnemonic;
dsd_renderButton.setMnemonic(p_in_mnemonic);
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border m_getFocusBorder() {
return dsd_focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param dsd_focusBorder
* the foreground color
*/
public void m_setFocusBorder(Border dsd_focusBorder) {
this.dsd_focusBorder = dsd_focusBorder;
}
Here is the button Editor code:
public class ButtonEditor extends AbstractCellEditor implements TableCellEditor, ActionListener, MouseListener{
private JTable dsd_table;
private int in_mnemonic;
private Border dsd_originalBorder;
private Border dsd_focusBorder;
private JButton dsd_editButton;
private Object dsd_editorValue;
private boolean bo_isButtonColumnEditor;
/**
* Constructor
* #param dsdp_table the table to which the editor is going to be applied.
* #param action the action which is to be executed when the button is clicked
*/
public ButtonEditor(JTable dsdp_table) {
this.dsd_table = dsdp_table;
dsd_editButton = new JButton();
dsd_editButton.setFocusPainted( false );
dsd_editButton.addActionListener( this );
dsd_originalBorder = dsd_editButton.getBorder();
m_setFocusBorder( new LineBorder(Color.BLUE) );
dsdp_table.addMouseListener( this );
}
public Object getCellEditorValue() {
return dsd_editorValue;
}
/**
* returns the mnemonic to activate the button when the cell has focus
*
* #return the mnemonic
*/
public int m_getMnemonic()
{
return in_mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param in_mnemonic the mnemonic
*/
public void m_setMnemonic(int in_mnemonic)
{
this.in_mnemonic = in_mnemonic;
dsd_editButton.setMnemonic(in_mnemonic);
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border m_getFocusBorder() {
return dsd_focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param dsdp_focusBorder
* the foreground color
*/
public void m_setFocusBorder(Border dsdp_focusBorder) {
this.dsd_focusBorder = dsdp_focusBorder;
dsd_editButton.setBorder(dsdp_focusBorder);
}
public void actionPerformed(ActionEvent arg0) {
int in_modelRow = dsd_table.convertRowIndexToModel( dsd_table.getEditingRow() );
fireEditingStopped();
// Here i start a custom thread to run in the background.
}
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
if (dsd_table.isEditing() && dsd_table.getCellEditor() == this)
bo_isButtonColumnEditor = true;
}
public void mouseReleased(MouseEvent arg0) {
if (bo_isButtonColumnEditor && dsd_table.isEditing())
dsd_table.getCellEditor().stopCellEditing();
bo_isButtonColumnEditor = false;
}
public void addCellEditorListener(CellEditorListener arg0) {
}
public void cancelCellEditing() {
}
public void removeCellEditorListener(CellEditorListener arg0) {
}
public boolean shouldSelectCell(EventObject arg0) {
return false;
}
public boolean stopCellEditing() {
return false;
}
public Component getTableCellEditorComponent(JTable dsd_table, Object dsd_value,
boolean bo_isSelected, int in_row, int in_column) {
if (!WorkplaceConstants.STR_INACTIVE.equals(dsd_table.getValueAt(in_row, dsd_table
.getColumn(Main.hm_language.get(Language.STATUS))
.getModelIndex())))
return new JLabel("");
if (dsd_value == null)
{
dsd_editButton.setText( "" );
dsd_editButton.setIcon( null );
}
else if (dsd_value instanceof Icon)
{
dsd_editButton.setText( "" );
dsd_editButton.setIcon( (Icon)dsd_value );
}
else
{
dsd_editButton.setText( dsd_value.toString() );
dsd_editButton.setIcon( null );
}
bo_isButtonColumnEditor = true;
this.dsd_editorValue = dsd_value;
dsd_editButton.setBorder(dsd_originalBorder);
return dsd_editButton;
}
public boolean isCellEditable(EventObject e){
return true;
}
public Border m_getoriginalBorder() {
return dsd_originalBorder;
}
public void m_set_originalBorder(Border dsd_originalBorder) {
this.dsd_originalBorder = dsd_originalBorder;
}
}
Here is how i am assigning the editor and renderer to the table
public static void m_setButtonColumnConfiguration(JTable table) {
ButtonEditor dsd_btn_edit = new ButtonEditor(table);
dsd_btn_edit.m_setMnemonic(KeyEvent.VK_D);
table.getColumn(/*i get the identifier for the column here*/).setCellEditor(dsd_btn_edit);
ButtonRenderer dsd_btn_rend = new ButtonRenderer();
dsd_btn_rend.m_setMnemonic(KeyEvent.VK_D);
table.getColumn(/*i get the identifier for the column here*/)
.setCellRenderer(dsd_btn_rend);
}
Button, however once i press on any of the buttons, the table loses focus
Yes, anytime you click on a component it gets focus, so this makes sense.
and the only interactive object in the panel is the button that was just clicked on,
Not sure I understand this statement. You can click on the table again and it will regain focus. You can then navigate around the table.
Maybe you are just suggesting the button renderer/editor isn't working the way you expect. We can't test this because you didn't post a SSCCE.
Anyway, check out Table Button Column for another example of a button renderer/editor that responds to mouse clicks or key board events.
If I create a new shell using the following code:
shell = new Shell( Display.getDefault(), SWT.RESIZE);
Then this gives me a shell without a title bar or minimize / maximize buttons, which is what I want. I'm able to resize this window to any size, which works great. But the problem is, the window is fixed in its place, and I cannot move it by dragging it around.
If I add either SWT.CASCADE or SWT.CLOSE, this gives me the title bar and close button, which I don't want, but moreover, it puts a limit on how small the window can be resized, i.e I can't resize it horizontally past a certain limit.
How can I make the window moveable without the close button / title bar? If there's no native way in SWT to do it, can I do it by listening for a mouse drag event and manually setting the location of the shell? If so, how would I get the mouse coordinates from the movement of the mouse?
Help would be appreciated. Thanks!
You need use own listeners. Below code should help:-
public class Demo {
static Boolean blnMouseDown=false;
static int xPos=0;
static int yPos=0;
public static void main(final String[] args) {
Display display=new Display();
final Shell shell = new Shell( Display.getDefault(), SWT.RESIZE);
shell.open();
shell.addMouseListener(new MouseListener() {
#Override
public void mouseUp(MouseEvent arg0) {
// TODO Auto-generated method stub
blnMouseDown=false;
}
#Override
public void mouseDown(MouseEvent e) {
// TODO Auto-generated method stub
blnMouseDown=true;
xPos=e.x;
yPos=e.y;
}
#Override
public void mouseDoubleClick(MouseEvent arg0) {
// TODO Auto-generated method stub
}
});
shell.addMouseMoveListener(new MouseMoveListener() {
#Override
public void mouseMove(MouseEvent e) {
// TODO Auto-generated method stub
if(blnMouseDown){
shell.setLocation(shell.getLocation().x+(e.x-xPos),shell.getLocation().y+(e.y-yPos));
}
}
});
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.close();
}
}
This is my implementation:
/**
* Class to allow user to move a shell without a title.
*
* #author Laurent Muller
* #version 1.0
*/
public class MoveShellListener implements Listener {
/*
* the parent shell
*/
private final Shell parent;
/*
* the mouse down location
*/
private Point ptMouseDown;
/**
* Creates a new instance of this class.
*
* #param parent
* the shell to handle.
*/
public MoveShellListener(final Shell parent) {
if (parent == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
if (parent.isDisposed()) {
SWT.error(SWT.ERROR_WIDGET_DISPOSED);
}
// copy and add listener
this.parent = parent;
addControl(parent);
}
/**
* Adds the given control to the list of listened controls. If the given
* control is an instance of {#link Composite}, the children controls are
* also added.
*
* #param control
* the control to add.
*/
public void addControl(final Control control) {
// check control
if (isDisposed(control) || control.getShell() != parent) {
return;
}
// add listeners
control.addListener(SWT.MouseDown, this);
control.addListener(SWT.MouseUp, this);
control.addListener(SWT.MouseMove, this);
// children
if (control instanceof Composite) {
final Control[] children = ((Composite) control).getChildren();
for (final Control child : children) {
addControl(child);
}
}
}
/**
* Adds the given controls to the list of listened controls. If one of the
* given controls is an instance of {#link Composite}, the children controls
* are also added.
*
* #param controls
* the controls to add.
*/
public void addControls(final Control... controls) {
if (controls != null) {
for (final Control control : controls) {
addControl(control);
}
}
}
/**
* {#inheritDoc}
*/
#Override
public void handleEvent(final Event e) {
switch (e.type) {
case SWT.MouseDown:
onMouseDown(e);
break;
case SWT.MouseUp:
onMouseUp(e);
break;
case SWT.MouseMove:
onMouseMove(e);
break;
}
}
/**
* Removes the given control to the list of listened controls. If the given
* control is an instance of {#link Composite}, the children controls are
* also removed.
*
* #param control
* the control to remove.
*/
public void removeControl(final Control control) {
// check control
if (control == parent || isDisposed(control)
|| control.getShell() != parent) {
return;
}
// remove listeners
control.removeListener(SWT.MouseDown, this);
control.removeListener(SWT.MouseUp, this);
control.removeListener(SWT.MouseMove, this);
// children
if (control instanceof Composite) {
final Control[] children = ((Composite) control).getChildren();
for (final Control child : children) {
removeControl(child);
}
}
}
/**
* Removes the given controls to the list of listened controls. If one of
* the given controls is an instance of {#link Composite}, the children
* controls are also removed.
*
* #param controls
* the controls to remove.
*/
public void removeControls(final Control... controls) {
if (controls != null) {
for (final Control control : controls) {
removeControl(control);
}
}
}
/**
* Checks if the given control is null or disposed.
*
* #param control
* the control to verify.
* #return true if the control is null or
* disposed.
*/
private boolean isDisposed(final Control control) {
return control == null || control.isDisposed();
}
/**
* Handles the mouse down event.
*
* #param e
* the event data.
*/
private void onMouseDown(final Event e) {
if (e.button == 1) {
ptMouseDown = new Point(e.x, e.y);
}
}
/**
* Handles the mouse move event.
*
* #param e
* the event data.
*/
private void onMouseMove(final Event e) {
if (ptMouseDown != null) {
final Point location = parent.getLocation();
location.x += e.x - ptMouseDown.x;
location.y += e.y - ptMouseDown.y;
parent.setLocation(location);
}
}
/**
* Handles the mouse up event.
*
* #param e
* the event data.
*/
private void onMouseUp(final Event e) {
ptMouseDown = null;
}
}
Hi, I am trying to show more detailed information after a user clicks on the message balloon tooltip.
However, I can't find how to capture that event.
Is this possible to do?
1) Is possible to listening MouseClickEvents by add ActionListener to the TrayIcon, then Message body listening for MouseClicked
2) (not asked directly) but I can't to give you an answer listening if message was closed by close button, and Message gone away from screen same way, but without catch any event(s)
3) looks like as this Java TrayIcon message close button should be only one solutions, because API doesn't implements another methods,
import java.awt.*;
import java.awt.event.*;
public class FullTray {
private static class ShowMessageListener implements ActionListener {
private TrayIcon trayIcon;
private String title;
private String message;
private TrayIcon.MessageType messageType;
ShowMessageListener(TrayIcon trayIcon, String title, String message, TrayIcon.MessageType messageType) {
this.trayIcon = trayIcon;
this.title = title;
this.message = message;
this.messageType = messageType;
}
public void actionPerformed(ActionEvent e) {
trayIcon.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Message Clicked");
}
});
trayIcon.displayMessage(title, message, messageType);
}
}
public static void main(String args[]) {
Runnable runner = new Runnable() {
public void run() {
if (SystemTray.isSupported()) {
final SystemTray tray = SystemTray.getSystemTray();
Image image = Toolkit.getDefaultToolkit().getImage("gifIcon.gif");
PopupMenu popup = new PopupMenu();
final TrayIcon trayIcon = new TrayIcon(image, "The Tip Text", popup);
MenuItem item = new MenuItem("Error");
item.addActionListener(new ShowMessageListener(trayIcon, "Error Title", "Error", TrayIcon.MessageType.ERROR));
popup.add(item);
item = new MenuItem("Warning");
item.addActionListener(new ShowMessageListener(trayIcon, "Warning Title", "Warning", TrayIcon.MessageType.WARNING));
popup.add(item);
item = new MenuItem("Info");
item.addActionListener(new ShowMessageListener(trayIcon, "Info Title", "Info", TrayIcon.MessageType.INFO));
popup.add(item);
item = new MenuItem("None");
item.addActionListener(new ShowMessageListener(trayIcon, "None Title", "None", TrayIcon.MessageType.NONE));
popup.add(item);
item = new MenuItem("Close");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
tray.remove(trayIcon);
}
});
popup.add(item);
try {
tray.add(trayIcon);
} catch (AWTException e) {
System.err.println("Can't add to tray");
}
} else {
System.err.println("Tray unavailable");
}
}
};
EventQueue.invokeLater(runner);
}
private FullTray() {
}
}
Even though this topic isn't exactly recent, I figured I'd share my solution since this kind of thing doesn't really change.
How To Use
To use this code, just create the 1 public class and the 2 public interfaces.
Now create an instance of the CustomTrayIcon class and call the addToSystemTray() method, once you're done initializing it.
Anytime you want to show a notification bubble, call the showBubbleNotification(...) method.
To remove this tray icon, just call the removeFromSystemTray() method.
CustomTrayIcon
This class is the base which does all of the heavy lifting.
package com.samples;
import java.awt.AWTException;
import java.awt.Image;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
/**
* This class can be used to create an icon in the {#link SystemTray}, providing
* the current operating system supports the {#link SystemTray}. <br />
* <br />
* <b><u>Userful Methods</u>:</b>
* <ul>
* <li>
* Use the {#link CustomTrayIcon#addToSystemTray() addToSystemTray()} method to
* add this {#link CustomTrayIcon} to the {#link SystemTray}. <br />
* </li>
* <li>
* Use the {#link CustomTrayIcon#removeFromSystemTray() removeFromSystemTray()}
* method to remove this {#link CustomTrayIcon} from the {#link SystemTray}. <br />
* </li>
* <li>
* Use the
* {#link CustomTrayIcon#showBubbleNotification(String, String, MessageType, CustomTrayIconNotificationBubbleClickEvent)
* showBubbleNotification(String, String, MessageType,
* CustomTrayIconNotificationBubbleClickEvent)} method to show a bubble notification to
* the user. <br />
* </li>
* <li>
* Use the {#link CustomTrayIcon#setClickDetectionDelay_override(int)
* setClickDetectionDelay_override(int)} method to override the default delay to
* be used when detecting if a user performed a single/double/triple click.</li>
* </ul>
*
* #author Matthew Weiler
* */
public abstract class CustomTrayIcon extends TrayIcon implements CustomTrayIconMouseInterface
{
/* PRIVATE CONSTANTS */
/**
* This will be used as the millisecond delay between clicks to detect
* single or double clicks. <br />
* <br />
* <b><u>Default Value</u>:</b> 300
* */
private static final int DEFAULT_CLICK_DETECTION_DELAY = 300;
/**
* This will be used to ensure that we can synchronize the click counter
* changes.
* */
private static final Object clickLock = new Object();
/* PRIVATE VARIABLES */
/**
* This will store the override value to be used in-place of the
* {#link CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY CLICK_DETECTION_DELAY}
* default value.
*
* #see CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY
* */
private int clickDetectionDelay_override = -1;
/**
* This will store the {#link CustomTrayIconNotificationBubbleClickEvent} which
* should be executed the next time that an {#link ActionEvent} is fired by
* the user clicking a notification bubble.
* */
private CustomTrayIconNotificationBubbleClickEvent newBubbleAction = null;
/**
* This will be used to keep track of which click was responsible for the
* current click timer.
* */
private long clickOwnerIndex = 0L;
/**
* This will be used to keep track of how many clicks occurred in the
* current click detection cycle.
* */
private int clickCounter = 0;
/* CONSTRUCTORS */
/**
* This will create a new instance of a {#link CustomTrayIcon}.
*
* #param image
* The {#link Image} to display as the icon in the notification
* tray.
* */
public CustomTrayIcon(final Image image)
{
super(image);
this.postCreateTasks();
}
/**
* This will create a new instance of a {#link CustomTrayIcon}.
*
* #param image
* The {#link Image} to display as the icon in the notification
* tray.
* #param tooltip
* The string to be used as tooltip text; if the value is null no
* tooltip is shown.
* */
public CustomTrayIcon(final Image image, final String tooltip)
{
super(image, tooltip);
this.postCreateTasks();
}
/**
* This will create a new {#link CustomTrayIcon}. <br />
* The {#link CustomTrayIcon}
* */
public CustomTrayIcon(final Image image, final String tooltip, final PopupMenu popup)
{
super(image, tooltip, popup);
this.postCreateTasks();
}
/* PUBLIC METHODS */
/**
* This will determine if the current operating system supports
* {#link SystemTray}.
*
* #see SystemTray#isSupported()
*
* #return <code>true</code> if the current operating system supports
* {#link SystemTray}; <code>false</code> otherwise.
* */
public static boolean isSystemTraySupported()
{
return SystemTray.isSupported();
}
/**
* This method will add this {#link CustomTrayIcon} to {#link SystemTray} . <br />
* <br />
* <i>this method may return <code>false</code> if the current operating
* system does not have or support a {#link SystemTray}</i>
*
* #return <code>true</code> if this {#link CustomTrayIcon} was added;
* <code>false</code> otherwise.
* */
public boolean addToSystemTray()
{
// Ensure that the current operating system supports
// the SystemTray.
if (CustomTrayIcon.isSystemTraySupported())
{
try
{
// Add this CustomTrayIcon to the SystemTray.
SystemTray.getSystemTray().add(this);
return true;
}
catch (AWTException e)
{
// ignore
}
}
return false;
}
/**
* This method will remove this {#link CustomTrayIcon} from the
* {#link SystemTray}.
* */
public void removeFromSystemTray()
{
SystemTray.getSystemTray().remove(this);
}
#Override
public void displayMessage(final String caption, final String text, final MessageType messageType)
{
this.newBubbleAction = null;
super.displayMessage(caption, text, messageType);
}
/**
* This method will show a bubble notification near the
* {#link CustomTrayIcon}.
*
* #see TrayIcon#displayMessage(String, String, MessageType)
*
* #param title
* The title to be displayed in the bubble notification.
* #param message
* The message to be displayed in the bubble notification.
* #param messageType
* The type of {#link MessageType} which will denote the style of
* the bubble notification.
* #param actionOnClick
* The {#link CustomTrayIconNotificationBubbleClickEvent} which should be
* fired when the user clicks the bubble notification.
*
* #throws NullPointerException
* If both caption and text are <code>null</code>.
* */
public void displayMessage(final String title, final String message, final MessageType messageType, final CustomTrayIconNotificationBubbleClickEvent actionOnClick)
{
this.showBubbleNotification(title, message, messageType, actionOnClick);
}
/**
* This method will show a bubble notification near the
* {#link CustomTrayIcon}.
*
* #see TrayIcon#displayMessage(String, String, MessageType)
*
* #param title
* The title to be displayed in the bubble notification.
* #param message
* The message to be displayed in the bubble notification.
* #param messageType
* The type of {#link MessageType} which will denote the style of
* the bubble notification.
*
* #throws NullPointerException
* If both caption and text are <code>null</code>.
* */
public void showBubbleNotification(final String title, final String message, final MessageType messageType)
{
this.showBubbleNotification(title, message, messageType, null);
}
/**
* This method will show a bubble notification near the
* {#link CustomTrayIcon}.
*
* #see TrayIcon#displayMessage(String, String, MessageType)
*
* #param title
* The title to be displayed in the bubble notification.
* #param message
* The message to be displayed in the bubble notification.
* #param messageType
* The type of {#link MessageType} which will denote the style of
* the bubble notification.
* #param actionOnClick
* The {#link CustomTrayIconNotificationBubbleClickEvent} which should be
* fired when the user clicks the bubble notification.
*
* #throws NullPointerException
* If both caption and text are <code>null</code>.
* */
public void showBubbleNotification(final String title, final String message, final MessageType messageType, final CustomTrayIconNotificationBubbleClickEvent actionOnClick)
{
this.newBubbleAction = actionOnClick;
super.displayMessage(title, message, messageType);
}
/* GETTERS & SETTERS */
/**
* This will get the override value to be used in-place of the
* {#link CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY CLICK_DETECTION_DELAY}
* default value.
*
* #see CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY
*
* #return The override value to be used in-place of the
* {#link CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY
* CLICK_DETECTION_DELAY} default value.
* */
public int getClickDetectionDelay_override()
{
return this.clickDetectionDelay_override;
}
/**
* This will set the override value to be used in-place of the
* {#link CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY CLICK_DETECTION_DELAY}
* default value.
*
* #see CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY
*
* #param clickDetectionDelay_override
* The override value to be used in-place of the
* {#link CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY
* CLICK_DETECTION_DELAY} default value.
* */
public void setClickDetectionDelay_override(final int clickDetectionDelay_override)
{
this.clickDetectionDelay_override = clickDetectionDelay_override;
}
/* PRIVATE METHODS */
/**
* This method will return the delay to have between each click when
* determining if the user performed a single/double/triple click.
*
* #return The delay to have between each click when determining if the user
* performed a single/double/triple click.
* */
private int getClickDetectionDelay()
{
if (this.clickDetectionDelay_override > 0)
{
return this.clickDetectionDelay_override;
}
return CustomTrayIcon.DEFAULT_CLICK_DETECTION_DELAY;
}
/**
* This method will be executed as the constructors last task. <br />
* <br />
* <i>this method will set some attributes on this {#link CustomTrayIcon} and
* apply the appropriate listeners</i>
* */
private void postCreateTasks()
{
this.setImageAutoSize(true);
this.applyCustomListeners();
}
/**
* This will apply the various listeners to this {#link CustomTrayIcon}
* object.
* */
private void applyCustomListeners()
{
// Add an ActionListener which will fire when the user
// clicks a bubble notification.
this.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if (CustomTrayIcon.this.newBubbleAction != null)
{
CustomTrayIcon.this.newBubbleAction.button1_click();
CustomTrayIcon.this.newBubbleAction = null;
}
}
});
// Add a MouseListener on this CustomTrayIcon which will
// fire when the user interacts with this CustomTrayIcon
// using their mouse.
this.addMouseListener(new MouseListener()
{
#Override
public void mouseReleased(MouseEvent arg0)
{
// NOT SUPPORTED BY TrayIcon
}
#Override
public void mousePressed(MouseEvent arg0)
{
// NOT SUPPORTED BY TrayIcon
}
#Override
public void mouseExited(MouseEvent arg0)
{
}
#Override
public void mouseEntered(MouseEvent arg0)
{
}
#Override
public void mouseClicked(MouseEvent arg0)
{
// Clear the bubble notification action as any open
// bubbles should be popped/closed when this interaction
// occurs.
CustomTrayIcon.this.newBubbleAction = null;
// If this MouseEvent was triggered by mouse button 1
// being clicked... then continue.
if (arg0.getButton() == MouseEvent.BUTTON1)
{
long tmp_clickOwnerIndex = 0L;
// Ensure that only one thread can modify the click owner index
// at any given time.
synchronized (CustomTrayIcon.clickLock)
{
// Increase the click owner index so that only the very
// last click can fire an event.
CustomTrayIcon.this.clickOwnerIndex++;
// Store the current value of the click owner index
tmp_clickOwnerIndex = CustomTrayIcon.this.clickOwnerIndex;
}
// Extract the click owner index, which was obtained in a
// thread-safe way, into a final variable so we can access it
// below.
final long tmp_clickOwnerIndex_final = tmp_clickOwnerIndex;
// Launch a new Thread which will sleep for a pre-determined
// period of time to catch the delay of a double/triple click.
(new Thread()
{
int tmp_clickCounter = 0;
public void run()
{
// Ensure that only one thread can modify the click
// counter at any given time.
synchronized (CustomTrayIcon.clickLock)
{
// Increase the number of active clicks that occurred
// during the current detection cycle.
CustomTrayIcon.this.clickCounter++;
// Extract the total number of active clicks that occurred
// during the current detection cycle up to this point.
tmp_clickCounter = CustomTrayIcon.this.clickCounter;
}
try
{
// Sleep for the pre-determined multiple click detection
// time.
// This ensures that we give the use enough time to
// actually perform multiple clicks.
Thread.sleep(CustomTrayIcon.this.getClickDetectionDelay());
}
catch (InterruptedException e)
{
// ignore
}
// Ensure that only one thread can modify the click owner index
// at any given time.
synchronized (CustomTrayIcon.clickLock)
{
// If this click was the last click since the appropriate
// delay, then it's clear that this click should be
// processed.
if (CustomTrayIcon.this.clickOwnerIndex == tmp_clickOwnerIndex_final)
{
// If only one click occurred during the allocated delayed
// time, then fire a button 1 click.
if (tmp_clickCounter == 1)
{
CustomTrayIcon.this.button1_click();
}
// If two clicks occurred during the allocated delayed
// time, then fire a button 2 click.
else if (tmp_clickCounter == 2)
{
CustomTrayIcon.this.button1_doubleClick();
}
// If three clicks occurred during the allocated delayed
// time, then fire a button 3 click.
else if (tmp_clickCounter == 3)
{
CustomTrayIcon.this.button1_tripleClick();
}
}
// Decrease the click owner index which will reset the
// click stack.
CustomTrayIcon.this.clickCounter--;
}
}
}).start();
}
// If this MouseEvent was triggered by mouse button 2
// being clicked... then continue.
else if (arg0.getButton() == MouseEvent.BUTTON2)
{
// Ensure that only one thread can modify the click owner index
// at any given time.
synchronized (CustomTrayIcon.clickLock)
{
// Increase the click owner index so that only the very
// last click can fire an event.
CustomTrayIcon.this.clickOwnerIndex++;
}
CustomTrayIcon.this.button2_click();
}
// If this MouseEvent was triggered by mouse button 3
// being clicked... then continue.
else if (arg0.getButton() == MouseEvent.BUTTON3)
{
// Ensure that only one thread can modify the click owner index
// at any given time.
synchronized (CustomTrayIcon.clickLock)
{
// Increase the click owner index so that only the very
// last click can fire an event.
CustomTrayIcon.this.clickOwnerIndex++;
}
CustomTrayIcon.this.button3_click();
}
}
});
}
}
CustomTrayIconMouseInterface
This interface is used to externalize various user interactions via the mouse.
package com.samples;
import javax.swing.Popup;
/**
* This interface contains several methods which can be fired when a user
* interacts with the {#link CustomTrayIcon} using their mouse.
*
* #author Matthew Weiler
* */
public interface CustomTrayIconMouseInterface
{
/**
* This method will be fired when the user clicks, with mouse button 1, this
* {#link CustomTrayIcon}.
* */
public void button1_click();
/**
* This method will be fired when the user double-clicks, with mouse button
* 1, this {#link CustomTrayIcon}.
* */
public void button1_doubleClick();
/**
* This method will be fired when the user triple-clicks, with mouse button
* 1, this {#link CustomTrayIcon}.
* */
public void button1_tripleClick();
/**
* This method will be fired when the user clicks, with mouse button 2, this
* {#link CustomTrayIcon}.
* */
public void button2_click();
/**
* This method will be fired when the user clicks, with mouse button 3, this
* {#link CustomTrayIcon}. <br />
* <br />
* <i>if a {#link Popup} menu is assigned to this {#link CustomTrayIcon}, it
* will still popup regardless of any actions taken by this method</i>
* */
public void button3_click();
}
CustomTrayIconNotificationBubbleClickEvent
This interface is used to externalize the interaction with the notification bubble.
package com.samples;
/**
* This interface contains several methods which can be fired when a user
* interacts with a bubble notification using their mouse.
*
* #author Matthew Weiler
* */
public interface CustomTrayIconNotificationBubbleClickEvent
{
/**
* This method will be fired when the user clicks, with mouse button 1, this
* bubble notification.
* */
public void button1_click();
}
In windows it is possible to show a grayed out JCheckbox, to show that the collection of data which it represents not all items have the same value.
Is this even possible with a JCheckBox?
How do i go about this?
(Hoping there's a way to not override it)
Thanks
JIDE Common Layer has a TristateCheckBox.
It's possible with some of work.
I have this code from some years ago. Is based in some examples I found in internet, but I cannot find any reference to the original creator, so I apologize
import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import java.awt.event.*;
/**
* Maintenance tip - There were some tricks to getting this code
* working:
*
* 1. You have to overwite addMouseListener() to do nothing
* 2. You have to add a mouse event on mousePressed by calling
* super.addMouseListener()
* 3. You have to replace the UIActionMap for the keyboard event
* "pressed" with your own one.
* 4. You have to remove the UIActionMap for the keyboard event
* "released".
* 5. You have to grab focus when the next state is entered,
* otherwise clicking on the component won't get the focus.
* 6. You have to make a TristateDecorator as a button model that
* wraps the original button model and does state management.
*/
public class TristateCheckBox extends JCheckBox {
/** This is a type-safe enumerated type */
public static class State { private State() { } }
public static final State NOT_SELECTED = new State();
public static final State SELECTED = new State();
public static final State DONT_CARE = new State();
private final TristateDecorator model;
public TristateCheckBox(String text, Icon icon, State initial){
super(text, icon);
// Add a listener for when the mouse is pressed
super.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
grabFocus();
model.nextState();
}
});
// Reset the keyboard action map
ActionMap map = new ActionMapUIResource();
map.put("pressed", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
grabFocus();
model.nextState();
}
});
map.put("released", null);
SwingUtilities.replaceUIActionMap(this, map);
// set the model to the adapted model
model = new TristateDecorator(getModel());
setModel(model);
setState(initial);
}
public TristateCheckBox(String text, State initial) {
this(text, null, initial);
}
public TristateCheckBox(String text) {
this(text, DONT_CARE);
}
public TristateCheckBox() {
this(null);
}
/** No one may add mouse listeners, not even Swing! */
public void addMouseListener(MouseListener l) { }
/**
* Set the new state to either SELECTED, NOT_SELECTED or
* DONT_CARE. If state == null, it is treated as DONT_CARE.
*/
public void setState(State state) { model.setState(state); }
/** Return the current state, which is determined by the
* selection status of the model. */
public State getState() { return model.getState(); }
public void setSelected(boolean b) {
if (b) {
setState(SELECTED);
} else {
setState(NOT_SELECTED);
}
}
/**
* Exactly which Design Pattern is this? Is it an Adapter,
* a Proxy or a Decorator? In this case, my vote lies with the
* Decorator, because we are extending functionality and
* "decorating" the original model with a more powerful model.
*/
private class TristateDecorator implements ButtonModel {
private final ButtonModel other;
private TristateDecorator(ButtonModel other) {
this.other = other;
}
private void setState(State state) {
if (state == NOT_SELECTED) {
other.setArmed(false);
setPressed(false);
setSelected(false);
} else if (state == SELECTED) {
other.setArmed(false);
setPressed(false);
setSelected(true);
} else { // either "null" or DONT_CARE
other.setArmed(true);
setPressed(true);
setSelected(true);
}
}
/**
* The current state is embedded in the selection / armed
* state of the model.
*
* We return the SELECTED state when the checkbox is selected
* but not armed, DONT_CARE state when the checkbox is
* selected and armed (grey) and NOT_SELECTED when the
* checkbox is deselected.
*/
private State getState() {
if (isSelected() && !isArmed()) {
// normal black tick
return SELECTED;
} else if (isSelected() && isArmed()) {
// don't care grey tick
return DONT_CARE;
} else {
// normal deselected
return NOT_SELECTED;
}
}
/** We rotate between NOT_SELECTED, SELECTED and DONT_CARE.*/
private void nextState() {
State current = getState();
if (current == NOT_SELECTED) {
setState(SELECTED);
} else if (current == SELECTED) {
setState(DONT_CARE);
} else if (current == DONT_CARE) {
setState(NOT_SELECTED);
}
}
/** Filter: No one may change the armed status except us. */
public void setArmed(boolean b) {
}
/** We disable focusing on the component when it is not
* enabled. */
public void setEnabled(boolean b) {
setFocusable(b);
other.setEnabled(b);
}
/** All these methods simply delegate to the "other" model
* that is being decorated. */
public boolean isArmed() { return other.isArmed(); }
public boolean isSelected() { return other.isSelected(); }
public boolean isEnabled() { return other.isEnabled(); }
public boolean isPressed() { return other.isPressed(); }
public boolean isRollover() { return other.isRollover(); }
public void setSelected(boolean b) { other.setSelected(b); }
public void setPressed(boolean b) { other.setPressed(b); }
public void setRollover(boolean b) { other.setRollover(b); }
public void setMnemonic(int key) { other.setMnemonic(key); }
public int getMnemonic() { return other.getMnemonic(); }
public void setActionCommand(String s) {
other.setActionCommand(s);
}
public String getActionCommand() {
return other.getActionCommand();
}
public void setGroup(ButtonGroup group) {
other.setGroup(group);
}
public void addActionListener(ActionListener l) {
other.addActionListener(l);
}
public void removeActionListener(ActionListener l) {
other.removeActionListener(l);
}
public void addItemListener(ItemListener l) {
other.addItemListener(l);
}
public void removeItemListener(ItemListener l) {
other.removeItemListener(l);
}
public void addChangeListener(ChangeListener l) {
other.addChangeListener(l);
}
public void removeChangeListener(ChangeListener l) {
other.removeChangeListener(l);
}
public Object[] getSelectedObjects() {
return other.getSelectedObjects();
}
}
}
My colleague who this question came from thought of this;
Create a dummy JCheckBox which is disabled and selected. set the same size as the real one.
Create an Icon which' paint method actually paints the dummy JCheckbox.
Set the original JCheckBox' Icon to the one painting the dummy.
Remove the icon as soon as the JCheckBox is clicked.
++ No overridden JCheckBox
-- not a real tri-state Combo
I think he's satisfied.
Thanks for the help