when i click adjacent two radio button then a line drawn between these button i put conditions in my code that line only will draw when two horizontal or vertical radio button selected else it show a error message ........... it works for me but if i click randomly two radio button it will throw an exception which i cant understand please give some discription about answer because i am beginner too............ and what is this means it shows when i compile prog..........Note: C:\Users\MUHAMMAD Umair\Desktop\ComponentLinkerTest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
/**
* #see http://stackoverflow.com/a/12389479/909085
*/
public class ComponentLinkerTest extends JComponent
{
// private Map<JComponent, JComponent> linked;
Map<JComponent, java.util.List<JComponent> > linked;// = new HashMap<>();
int n=1;
public ComponentLinkerTest ()
{
super ();
linked = new HashMap ();
}
static JRadioButton[] button = new JRadioButton[25];
public void gui()
{
setupLookAndFeel ();
JFrame frame = new JFrame ();
linker = new ComponentLinkerTest ();
frame.setGlassPane ( linker );
linker.setVisible ( true );
JPanel content = new JPanel ();
content.setLayout ( new GridLayout ( 5, 5, 5, 5 ) );
content.setBorder ( BorderFactory.createEmptyBorder ( 5, 5, 5, 5 ) );
frame.add ( content );
int i;
for (i = 0; i < 25; i++ )
{
// final JButton button = new JButton ( "Button" + i );
button[i] = new JRadioButton();
// panel.add(fontButtons[i]);
button[i].addActionListener ( new ActionListener ()
{
public void actionPerformed ( ActionEvent e )
{
link ((JRadioButton) e.getSource() );
}
} );
content.add ( button [i]);
}
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
}
/*public void link ( JComponent c1, JComponent c2 )
{
linked.put ( c1, c2 );
repaint ();
}*/
public void link ( JComponent c1, JComponent c2 )
{
if(linked.containsKey(c1)){
linked.get(c1).add(c2);
}else{
java.util.List<JComponent> list = new LinkedList<>();
list.add(c2);
linked.put ( c1, list );
}
repaint ();
}
/* protected void paintComponent ( Graphics g )
{
Graphics2D g2d = ( Graphics2D ) g;
g2d.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g2d.setPaint ( Color.BLACK );
for ( JComponent c1 : linked.keySet () )
{
Point p1 = getRectCenter ( getBoundsInWindow ( c1 ) );
Point p2 = getRectCenter ( getBoundsInWindow ( linked.get ( c1 ) ) );
/* Stroke stroke = new BasicStroke(8//,
/*BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0,
new float[] { 12, 12 }, 0);
g2d.setStroke(stroke);
g2d.setColor(Color.RED);
g2d.drawLine ( p1.x, p1.y, p2.x, p2.y );
}
}*/
#Override
protected void paintComponent ( Graphics g )
{
super.paintComponent(g);
Graphics2D g2d = ( Graphics2D ) g;
g2d.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g2d.setPaint ( Color.BLACK );
for ( JComponent c1 : linked.keySet () )
{
for(JComponent c2 : linked.get(c1)){
Point p1 = getRectCenter ( getBoundsInWindow ( c1 ) );
Point p2 = getRectCenter ( getBoundsInWindow ( c2 ) );
/* Stroke stroke = new BasicStroke(8//,
/*BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0,
new float[] { 12, 12 }, 0);
g2d.setStroke(stroke);*/
if(n==1)
{
g2d.setColor(Color.RED);
n=2;
}
else
{
g2d.setColor(Color.BLUE);
n=1;
}
g2d.drawLine ( p1.x, p1.y, p2.x, p2.y );
}
}
}
private Point getRectCenter ( Rectangle rect )
{
return new Point ( rect.x + rect.width / 2, rect.y + rect.height / 2 );
}
private Rectangle getBoundsInWindow ( Component component )
{
return getRelativeBounds ( component, getRootPaneAncestor ( component ) );
}
private Rectangle getRelativeBounds ( Component component, Component relativeTo )
{
return new Rectangle ( getRelativeLocation ( component, relativeTo ),
component.getSize () );
}
private Point getRelativeLocation ( Component component, Component relativeTo )
{
Point los = component.getLocationOnScreen ();
Point rt = relativeTo.getLocationOnScreen ();
return new Point ( los.x - rt.x, los.y - rt.y );
}
private JRootPane getRootPaneAncestor ( Component c )
{
for ( Container p = c.getParent (); p != null; p = p.getParent () )
{
if ( p instanceof JRootPane )
{
return ( JRootPane ) p;
}
}
return null;
}
public boolean contains ( int x, int y )
{
return false;
}
private static ComponentLinkerTest linker;
public static void main ( String[] args )
{
ComponentLinkerTest ct = new ComponentLinkerTest();
ct.gui();
}
private static JRadioButton last = null;
private static void link ( JRadioButton buton )
{
int a=0;
int i;
if ( last == null )
{
last = buton;
System.out.println(last.getX());
}
else
{
for( i=0 ;i<25 ;i++)
{
if(buton == button[i])
{
if(button[i-1] == last || button[i+1]==last || button[i-5] == last || button[i+5]==last)
{
System.out.println("in cond");
linker.link ( last, buton );
buton.setSelected(false);
last.setSelected(false);
last = null;
}
else
{
System.out.println("out cond");
buton.setSelected(false);
last.setSelected(false);
last = null;
JOptionPane.showMessageDialog(null,"Wrong position clicked ");
}
break;
}
else
{
System.out.println("button not found");
}
}
}
}
private static void setupLookAndFeel ()
{
try
{
UIManager.setLookAndFeel ( UIManager.getSystemLookAndFeelClassName () );
}
catch ( ClassNotFoundException e )
{
e.printStackTrace ();
}
catch ( InstantiationException e )
{
e.printStackTrace ();
}
catch ( IllegalAccessException e )
{
e.printStackTrace ();
}
catch ( UnsupportedLookAndFeelException e )
{
e.printStackTrace ();
}
}
}
You do no range checking...
This is going to cause an exception if you the last button is the on the last line, or the second button is one the first row
if (button[i - 1] == last || button[i + 1] == last || button[i - 5] == last || button[i + 5] == last)
You need to modify your checks to ensure that they don't extend below (0) or beyond (button.length - 1)...
if ((i > 0 && button[i - 1] == last) ||
(i < (button.length - 1) && button[i + 1] == last) ||
(i > 5 && button[i - 5] == last) ||
(i < (button.length - 1) && button[i - 5] == last)) {
Updated with alternative
Okay, that if statement was doing my head...so I took a different tact...
Basically, instead, I found the index of each button and calculated the distance that each button was from each other, they could only be 1 or 5 indices apart...
int lastIndex = indexOf(last);
int currentIndex = indexOf(buton);
int diff = Math.max(lastIndex, currentIndex) - Math.min(lastIndex, currentIndex);
if (diff == 1 || diff == 5) {
System.out.println("in cond");
linker.link(last, buton);
buton.setSelected(false);
last.setSelected(false);
last = null;
} else {
System.out.println("out cond");
buton.setSelected(false);
last.setSelected(false);
last = null;
JOptionPane.showMessageDialog(null, "Wrong position clicked ");
}
And the indexOf method...
protected static int indexOf(JRadioButton btn) {
int index = -1;
for (int value = 0; value < button.length; value++) {
if (button[value].equals(btn)) {
index = value;
break;
}
}
return index;
}
As a side note, I'd also beware of over using static. This suggest a bad design
Related
I'm writing a calculator program for my java class. I want to account for errors like
User decides to divide by 0. I want the calculator to be able to acknowledge this error and display ERROR.
The user enters 6+7+ and then hits the equal button.
I want, once again, for the calculator to acknowledge that there is an error. I was hoping I could get some hints as to how to go about this. Thanks!
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Calculator
{
JFrame window;
// stuff for top panel
JPanel topPanel;
JTextField expr,result;
JButton equals;
// stuff for bottom panel
JPanel bottomPanel,digitsPanel,opsPanel;
JButton[] digits,ops;
JButton clear, clearEntry;
Container content;
Listener listener;
String[] oplabels = { "+", "-", "/", "*" };
public Calculator()
{
listener = new Listener(); // our Listener class implements ActionListener
window= new JFrame("GUI Calc");
content=window.getContentPane();
content.setLayout( new GridLayout(2,1) );
topPanel=new JPanel();
topPanel.setLayout( new GridLayout(1,3) );
// TOP PANEL WORK
expr = new JTextField( );
equals = new JButton("=");
equals.addActionListener( listener );
result = new JTextField( );
topPanel.add( expr );
topPanel.add( equals );
topPanel.add( result );
// BOTTOM PANEL WORK
bottomPanel = new JPanel();
bottomPanel.setLayout( new GridLayout(1,2) );
digitsPanel = new JPanel();
digitsPanel.setLayout( new GridLayout(4,3) );
opsPanel = new JPanel();
opsPanel.setLayout( new GridLayout(4,1) );
digits = new JButton[12];
ops = new JButton[4];
for (int i=0 ; i<10 ; i++)
{
digits[i] = new JButton( i+"" );
digits[i].addActionListener(listener);
digitsPanel.add( digits[i] );
}
clear = new JButton( "C" );
clearEntry = new JButton( "CE" );
clear.addActionListener(listener);
clearEntry.addActionListener(listener);
digitsPanel.add( clear );
digitsPanel.add( clearEntry);
for (int i=0 ; i<4 ; i++)
{
ops[i] = new JButton( oplabels[i] ) ;
ops[i].addActionListener(listener);
opsPanel.add( ops[i] );
}
bottomPanel.add( digitsPanel );
bottomPanel.add( opsPanel );
content.add( topPanel);
content.add( bottomPanel);
window.setVisible(true);
}
class Listener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Component clicked = (Component) e.getSource();
if ( clicked == equals )
{
result.setText( evaluate( expr.getText() ) );
return;
}
for ( int i=0 ; i<10 ; i++)
{
if ( clicked == digits[i] )
{
expr.setText( expr.getText() + i );
return;
}
}
if ( clicked == clear )
{
expr.setText("0");
return;
// do something
}
/* if (clicked == clearEntry)
{
expr.setText(expr.getDigits[i]);
}
for (int i=0; i < 10; i++)
{
if (clicked == digits[i])
{
int lastValue = clicked;
if(clicked == clearEntry)
{
expr.setText(expr.getText()-clicked);
return;
}
}
} */
for ( int i=0 ; i<4 ; i++ )
{
if(clicked == digits[i])
{
expr.setText(expr.getText() + oplabels[i]);
return;
}
// tack on that operator to the expr string
}
}
String evaluate( String exp )
{
return "NOT WRITTEN YET";
}
}
public static void main(String [] args)
{
new Calculator();
}
}
I'm still working on it. Especially where I'm trying to figure out the clearEntry button so I have put that section as a comment because its still a work in progress.
You can try this:
add a new class(Calculator) member:
Component last_clicked;
Inside Constructor:
last_clicked=null;
Before every return statement of actionPerformed, you can add this:
last_clicked=clicked;
Now, in actionPerformed method, when checking value of clicked, you can do this:
When clicked is '0' & last_clicked is '/', you can display error 1.
When clicked is 'equals' & last_clicked is an operator, you can display error 2.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have to write a calculator program using the Gui components. I have written the program but I'm having issues with my CE button. It is giving me errors. I'm also getting and error in my for loop where I add the operand labels to the expression. I was just hoping to get some help in fixing these errors. Thank you! Please ignore the comments. They are there for my to keep track of what I have to do.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Calculator
{
JFrame window;
// stuff for top panel
JPanel topPanel;
JTextField expr,result;
JButton equals;
// stuff for bottom panel
JPanel bottomPanel,digitsPanel,opsPanel;
JButton[] digits,ops;
JButton clear, clearEntry;
Container content;
Listener listener;
String[] oplabels = { "+", "-", "/", "*" };
public Calculator()
{
listener = new Listener(); // our Listener class implements ActionListener
window= new JFrame("GUI Calc");
content=window.getContentPane();
content.setLayout( new GridLayout(2,1) );
topPanel=new JPanel();
topPanel.setLayout( new GridLayout(1,3) );
// TOP PANEL WORK
expr = new JTextField( );
equals = new JButton("=");
equals.addActionListener( listener );
result = new JTextField( );
topPanel.add( expr );
topPanel.add( equals );
topPanel.add( result );
// BOTTOM PANEL WORK
bottomPanel = new JPanel();
bottomPanel.setLayout( new GridLayout(1,2) );
digitsPanel = new JPanel();
digitsPanel.setLayout( new GridLayout(4,3) );
opsPanel = new JPanel();
opsPanel.setLayout( new GridLayout(4,1) );
digits = new JButton[12];
ops = new JButton[4];
for (int i=0 ; i<10 ; i++)
{
digits[i] = new JButton( i+"" );
digits[i].addActionListener(listener);
digitsPanel.add( digits[i] );
}
clear = new JButton( "C" );
clearEntry = new JButton( "CE" );
clear.addActionListener(listener);
clearEntry.addActionListener(listener);
digitsPanel.add( clear );
digitsPanel.add( clearEntry);
for (int i=0 ; i<4 ; i++)
{
ops[i] = new JButton( oplabels[i] ) ;
ops[i].addActionListener(listener);
opsPanel.add( ops[i] );
}
bottomPanel.add( digitsPanel );
bottomPanel.add( opsPanel );
content.add( topPanel);
content.add( bottomPanel);
window.setVisible(true);
}
class Listener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Component clicked = (Component) e.getSource();
if ( clicked == equals )
{
result.setText( evaluate( expr.getText() ) );
return;
}
for ( int i=0 ; i<10 ; i++)
{
if ( clicked == digits[i] )
{
expr.setText( expr.getText() + i );
return;
}
}
if ( clicked == clear )
{
expr.setText("0");
return;
// do something
}
for (int i=0; i < 10; i++)
{
if (clicked == digits[i])
{
int lastValue = i;
return;
}
}
if ( clicked == clearEntry )
{
expr.setText(expr.getText() - lastValue);
return;
// do something
}
for ( int i=0 ; i<4 ; i++ )
{
if(clicked == oplabels[i])
{
expr.setText(expr.getText + oplabels[i]);
return;
}
// tack on that operator to the expr string
}
}
String evaluate( String exp )
{
return "NOT WRITTEN YET";
}
}
public static void main(String [] args)
{
new Calculator();
}
}
Problem #1
The compiler is complaining about expr.setText(expr.getText() - lastValue); because lastValue is undefined.
Having a close look at the code, the only place that lastValue is defined is within the for-loop before the if statement...but the next issues thereturn` statement following it's decleration...
for (int i = 0; i < 10; i++) {
if (clicked == digits[i]) {
int lastValue = i;
return;
}
}
if (clicked == clearEntry) {
expr.setText(expr.getText() - lastValue);
return;
// do something
}
This not only makes the lastValue useless, but it's unlikely that the if statement would ever be reached...
Problem #2
for (int i = 0; i < 4; i++) {
if (clicked == oplabels[i]) {
expr.setText(expr.getText + oplabels[i]);
return;
}
// tack on that operator to the expr string
}
clicked is defined as Component clicked = (Component) e.getSource(); and oplables are defined as String[] oplabels = {"+", "-", "/", "*"};. String and Component are not comparable (also, you want expr.getText() not expr.getText).
I think you want to do something more like...
if (clicked instanceof JButton) {
JButton btn = (JButton)clicked)
for (int i = 0; i < oplabels.length; i++) {
if (oplabels[i].equals(btn.getText()) {
//...
}
}
}
Line 128: clicked is a Component and oplabels[i] is a string - you can't compare them directly.
Line 130: Missing () on getText() call.
I'm wondering if there's anything that allows N-Level nested generics?
Suppose I have
class Example< A > {
...
}
Is there any trick to get
Example<Example<Example<Example<Example<Example<...A...>>>>>>>
when the program is running (i.e. not hardcoding it)?
The reason I'm wondering about this is because I parameterized the coefficients of a univariate Polynomial class - so I had Polynomial< C >, where C is the type of coefficient, and C could be Integer, Rational, Polynomial (this would solve multivariate Polynomial issues), or a lot of other things. Having
Polynomial< Polynomial< Integer > >
for two variable polynomials might be okay, but if it gets to an arbitrary number of variables, I'd be in trouble. I realize this is probably bad design. Is there any way to resolve this issue (aside from restart)?
Thanks for the help!
There actually is a way to approach this problem using parametric recursion. It doesn't give you exactly N nested parametrization, but it addresses the problem of having to type
Example< Example < Example< ... ... > > > >
I stumbled upon an article on this topic today: http://www.cs.ubbcluj.ro/~studia-i/2011-4/02-Niculescu.pdf
Skip down to section 4 for the part about Java Generics. Below is what I got from the article's discussion on parametric recursion, and a coding experiment I tried.
I never thought about trying something like this:
public interface RingElem< T extends RingElem< T > > {
public abstract RingElem< T > add( RingElem< T > e );
public abstract RingElem< T > multiply( RingElem< T > e );
}
I didn't realize you could parametrize an interface with something that extended "itself." Now, if you have a RingElem object, then it would be parametrized by T, which is of type RingElem. By recursion, you pretty much have RingElem< RingElem < RingElem < ... > > >, but it's "infinitely" nested.
And now, you can implement the RingElem interface to create your own Polynomial class:
public class Polynomial implements RingElem< Polynomial > {
///...
}
And if you have a Polynomial object, it is parametrized as Polynomial< Polynomial< ... ... > >
When I thought about this in my head, I was skeptical that you could possibly initialize such an object. But once I started coding, it seemed simple enough. Below is are 3 classes I "hacked" together that allow one to manipulate multivariate monomials. I'll rewrite with better design (I should really be using public interface Monomial< T extends Polynomial< T > > where T is the type of coefficient for the Monomial) and add comments if I get the time.
As the example demonstrates, you can get a multivariate monomial that uses every letter of the alphabet without having to type Monomial< Monomial< Monomial< Monomial< ... ... > > > 26 times.
Monomial class: (e.g. 5x^2, 4a^-5)
public class Monomial implements RingElem< Monomial > {
private Monomial m_coefficient;
private Monomial m_base;
private Monomial m_exponent;
public Monomial() {
}
public Monomial( Monomial coefficient , Monomial base , Monomial exponent ) {
if ( base.m_coefficient != null ) {
this.m_coefficient = coefficient.multiply( base.m_coefficient );
if ( base.m_base != null ) {
this.m_coefficient = coefficient.multiply( base.m_base );
}
}
else {
this.m_coefficient = coefficient;
}
this.m_exponent = exponent;
this.m_base = base;
}
#Override
public Monomial add( RingElem< Monomial > e ) {
if ( e instanceof Monomial ) {
Monomial addend = (Monomial) e;
if ( this.m_base.equals( addend.m_base ) && this.m_exponent.equals( addend.m_exponent ) ) {
Monomial rtn = new Monomial( this.m_coefficient.add( addend.m_coefficient ) , this.m_base , this.m_exponent );
return rtn;
}
else {
throw new RuntimeException( "Addition would produce a polynomial, which is not implemented yet!" );
}
}
else {
throw new RuntimeException( "Operation not defined" );
}
}
#Override
public Monomial multiply( RingElem< Monomial > e ) {
if ( e instanceof Constant ) {
Constant c = ( Constant ) e;
return new Monomial( this.m_coefficient.multiply( c ) , this.m_base , this.m_exponent );
}
else if ( e instanceof Monomial ) {
Monomial m = ( Monomial ) e;
return new Monomial( this.m_coefficient.multiply( m.m_coefficient ).multiply( m.m_base ) , this.m_base , this.m_exponent.add( m.m_exponent ));
}
else {
throw new RuntimeException( "Operation not defined" );
}
}
#Override
public String toString() {
if ( this.m_coefficient == null || this.m_base == null || this.m_exponent == null ) {
return "<error>";
}
return this.m_coefficient.toString() + this.m_base.toString() + "^" + this.m_exponent.toString();
}
final public static void main( String[] args ) {
String[] letters = { "a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" , "i" , "j" , "k" , "l" , "m" , "n" , "o" , "p" , "q" , "r" , "s" , "t" , "u" , "v" , "w" , "x" , "y" , "z" };
Variable[] v = new Variable[ letters.length ];
for ( int i=0 ; i<letters.length ; i++ ) {
v[ i ] = new Variable( letters[ i ] );
}
Monomial coefficient = new Constant( 1.2 );
Monomial exponent = new Constant( 4.5 );
Monomial test = new Monomial( coefficient , v[ 0 ] , exponent );
for ( int i=1 ; i<letters.length ; i++ ) {
test = test.multiply( new Constant( 1.2 ) );
test = new Monomial( test , v[ i ] , new Constant( (int)(Math.random() * 25 )) );
}
System.out.println( test.toString() );
}
#Override
public boolean equals( Object o ) {
if ( o instanceof Monomial ) {
Monomial m = ( Monomial ) o;
return this.m_coefficient.equals( m.m_coefficient ) && this.m_base.equals( m.m_base ) && this.m_exponent.equals( m.m_exponent );
}
else {
return false;
}
}
}
Constant class: (e.g. 1, 2, 3.14159, -10000)
public class Constant extends Monomial {
private double m_val;
public Constant( double val ) {
super();
this.m_val = val;
}
#Override
public Constant add ( RingElem e ) {
if ( e instanceof Constant ) {
Constant c = ( Constant ) e;
return new Constant( this.m_val + c.m_val );
}
else if ( e instanceof Variable ) {
throw new RuntimeException( "Operation would create a polynomial, which is not defined yet!" );
}
else {
throw new RuntimeException( "Operation not defined" );
}
}
#Override
public Monomial multiply( RingElem e ) {
if ( e instanceof Constant ) {
Constant c = ( Constant ) e;
return new Constant( this.m_val * c.m_val );
}
else if ( e instanceof Variable ) {
return new Monomial( this.clone() , (Variable) e , new Constant( 1 ) );
}
else {
throw new RuntimeException( "Operation not defined" );
}
}
#Override
public String toString() {
return String.valueOf( this.m_val );
}
#Override
public boolean equals( Object o ) {
if ( o instanceof Constant ) {
Constant c = ( Constant ) o;
return this.m_val == c.m_val;
}
else {
return false;
}
}
#Override
public Constant clone() {
return new Constant( this.m_val );
}
}
Variable class: (e.g. x, y, z)
public class Variable extends Monomial {
final private String m_varName;
public Variable( String varName ) {
this.m_varName = varName;
}
#Override
public Monomial add( RingElem e ) {
if ( e instanceof Variable ) {
if ( e.equals( this ) ) {
return new Monomial( new Constant( 2 ) , this , new Constant( 1 ) );
}
else {
throw new RuntimeException( "Operation would create a polynomial, which is not defined yet!" );
}
}
else if ( e instanceof Monomial ) {
throw new RuntimeException( "Operation would create a polynomial, which is not defined yet!" );
}
else {
throw new RuntimeException( "operation not defined!" );
}
}
#Override
public Monomial multiply( RingElem e ) {
if ( e instanceof Constant ) {
Constant c = ( Constant ) e;
return c.multiply( this );
}
if ( e instanceof Variable ) {
return new Monomial( (Variable) e , this , new Constant( 1 ) );
}
else {
return this.multiply( e );
}
}
#Override
public String toString() {
return this.m_varName;
}
#Override
public boolean equals( Object o ) {
if ( o instanceof Variable ) {
Variable v = ( Variable ) o;
return this.m_varName.equals( v.m_varName );
}
else {
return false;
}
}
}
I'm using a JTable with say 3 columns, displaying information about files and folders:
Col1: Either "file" or "folder" (String)
Col2: Name of the file or folder (String)
Col3: Creation date (Timstamp)
I can easily sort the table by any column. But I'm not able to achieve the following desired behavior:
No matter what column header the user clicks, folders should always display before files. So basically sorting by Col1 in any order other than descending should not be possible. Also, if sorted by Col3, any folders should still display before files, regardless of their timestamp.
Any hint to how to do this is greatly appreciated.
Yours
Andreas
Try to use next code it helps you:
public class Example extends JFrame {
public Example() {
DefaultTableModel defaultTableModel = new DefaultTableModel(new Object[][]{{"1"},{"3"},{"2"}},new Object[]{"text"});
JTable t = new JTable(defaultTableModel);
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(defaultTableModel);
sorter.setComparator(0, new Comparator<Object>() {
#Override
public int compare(Object arg0, Object arg1) {
return arg0.toString().compareTo(arg1.toString());
}
});
t.setRowSorter(sorter);
add(new JScrollPane(t));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String...strings ){
Example e = new Example();
}
}
Here I set custom Comparator to TableRowSorter for column 0. You can compare your rows in different ways with your own Comparator implementation. Try it!
Comparator
RowSorter tutorial
Maybe this is a way to do it. There's probably a more elegant way to do it around.
The DefaultRowSorter Comparator only compares values for one column at a time, without additional information it alone can not make sure that one column in the set of columns (say column 1) must always be the first column to sort by.
If you mess with DefaultRowSorter.toggleSortOrder and add a new SortKey to keys at position 0 before setSortKeys gets called (this would be the primary column you always want to sort by first [column 1]), the ascending and descending arrows displayed in the column headers constantly mark column 1 (rightfully so), but this is not what the user expects if she clicks on column 2.
Another approach is to have Comparator know about column sort relationships. An easy way is to add information about sort precedences to the Objects for Comparator.compare. Simple precedences can be represented by a prefix String (for example: prefix "a" for column 1 and prefix "b" for all other columns).
PrefixedData can contain String, Boolean, Timestamp or null values and a prefix:
public class PrefixedData {
private String sData=null, prefix="";
private Timestamp tData=null;
private Boolean bData=null;
public void setData(String data) {
sData=data;
}
public void setData(Timestamp data) {
tData=data;
}
public void setData(boolean data) {
bData=data;
}
public void setPrefix(String prefix) {
this.prefix=prefix;
}
public String getPrefix() {
return prefix;
}
public Object getData() {
if(sData!=null) return sData;
if(tData!=null) return tData;
if(bData!=null) return bData;
return null;
}
}
JTable cells can be rendered like this for String, Boolean and Timestamp, if your table model returns PrefixedData for DefaultTableModel.getValueAt:
private static class PrefixedDataRenderer3 extends JLabel implements TableCellRenderer {
private static final SimpleDateFormat formatter = new SimpleDateFormat("MM.yyyy HH:mm:ss");
private static final JCheckBox box = new JCheckBox();
public PrefixedDataRenderer() {
setOpaque(true);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
remove(box);
PrefixedData p=(PrefixedData) value;
if(p.getData()==null) return this;
if(p.getData().getClass().equals(String.class))
{
setText((String)p.getData());
return this;
}
if(p.getData().getClass().equals(Timestamp.class))
{
setText(formatter.format((Timestamp)p.getData()));
return this;
}
if(p.getData().getClass().equals(Boolean.class))
{
box.setSelected(((Boolean)p.getData()).booleanValue());
add(box);
}
return this;
}
}
And finally the Comparator can decide the order of things:
Comparator<Object> PrefixedDataComparator = new Comparator<Object>() {
private List<? extends SortKey> sortKeys;
#Override
public int compare(Object o1, Object o2) {
PrefixedData p1=(PrefixedData) o1;
PrefixedData p2=(PrefixedData) o2;
//First: compare prefixes (precedence data)
int prefixResult=p1.getPrefix().compareTo(p2.getPrefix());
sortKeys = tableOU.getRowSorter().getSortKeys();
//The prefixes are not the same, so return the result of the prefix comparision
//The result has to be inverted if we're sorting in descending order
//for the column the user has clicked
if(prefixResult!=0) return (sortKeys.get(0).getSortOrder().equals(SortOrder.ASCENDING) ? prefixResult : -prefixResult );
//Only if the prefixes are the same do we have to compare the payload data
//Try to impose an order for null
if(p1.getData()==null && p2.getData()!=null) return -1;
if(p1.getData()==null && p2.getData()==null) return 0;
if(p1.getData()!=null && p2.getData()==null) return 1;
//Objects compared are identical
if(p1.getData().equals(p2.getData())) return 0;
//Compare String
if(p1.getData().getClass().equals(String.class)) {
String s1=(String) p1.getData();
String s2=(String) p2.getData();
return s1.toLowerCase().compareTo(s2.toLowerCase());
}
//Compare Timestamp
if(p1.getData().getClass().equals(Timestamp.class)) {
Timestamp t1=(Timestamp) p1.getData();
Timestamp t2=(Timestamp) p2.getData();
if(t1.before(t2)) return -1;
if(t1.equals(t2)) return 0;
return 1;
}
//Compare Bool
if(p1.getData().getClass().equals(Boolean.class)) {
boolean b1=((Boolean)p1.getData()).booleanValue();
boolean b2=((Boolean)p2.getData()).booleanValue();
if(b1==false && b2==true) return -1;
if(b1==b2) return 0;
return 1;
}
return 0;
}
};
Any suggestions to this or different approaches a very welcome.
Well, the question is old but Google returned this page to me when I had a similar task. So, I'll post here my solution. Hope that it would help someone.
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.DefaultRowSorter;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import org.apache.log4j.Logger;
public class PredefinedRowSorter extends TableRowSorter< TableModel > {
private static final Logger LOGGER = Logger.getLogger( PredefinedRowSorter.class );
private static final SortKey EMPTY_ARRAY[] = new SortKey[ 0 ];
private final JTable table;
private SortKey preColumns[] = EMPTY_ARRAY;
private SortKey postColumns[] = EMPTY_ARRAY;
public PredefinedRowSorter( JTable table ) {
super( table.getModel() );
this.table = table;
}
private void check( SortKey modelColumns[], SortKey crossCheckColumns[], boolean post ) {
TableModel tm = table.getModel();
int max = tm.getColumnCount();
Set< Integer > used = new HashSet< Integer >();
for ( SortKey key : modelColumns ) {
if ( key == null )
throw new IllegalArgumentException( "SortKey must be non-null" );
if ( key.getColumn() < 0 )
throw new IllegalArgumentException( "SortKey column must be non-negative" );
if ( key.getColumn() >= max )
throw new IllegalArgumentException( "SortKey column is too high (out of model scope)" );
if ( key.getSortOrder() == SortOrder.UNSORTED )
throw new IllegalArgumentException( "SortKey must be ordered (ascending or descending)" );
if ( !used.add( key.getColumn() ) )
throw new IllegalArgumentException( "SortKey column must be unique (column " + key.getColumn() + " is repeating)" );
}
for ( SortKey key : crossCheckColumns )
if ( used.contains( key.getColumn() ) )
throw new IllegalArgumentException( "SortKey column must be unique (column " + key.getColumn() + " is already contained in " + ( post ? "post" : "pre" ) + " columns list)" );
}
public PredefinedRowSorter withPreColumns( SortKey... modelColumns ) {
if ( modelColumns == null )
modelColumns = EMPTY_ARRAY;
if ( !Arrays.equals( preColumns, modelColumns ) ) {
check( modelColumns, postColumns, true );
preColumns = modelColumns;
setSortKeys( getSortKeys() );
}
return this;
}
public PredefinedRowSorter withPostColumns( SortKey... modelColumns ) {
if ( modelColumns == null )
modelColumns = EMPTY_ARRAY;
if ( !Arrays.equals( postColumns, modelColumns ) ) {
check( modelColumns, preColumns, false );
postColumns = modelColumns;
setSortKeys( getSortKeys() );
}
return this;
}
public JTable getTable() {
return table;
}
public SortKey[] getPreColumns() {
return preColumns.length == 0 ? preColumns : preColumns.clone();
}
public SortKey[] getPostColumns() {
return postColumns.length == 0 ? postColumns : postColumns.clone();
}
private void setSortKeysInternal( List< ? extends SortKey > sortKeys ) {
try {
Field field = DefaultRowSorter.class.getDeclaredField( "sortKeys" );
boolean accessible = field.isAccessible();
if ( !accessible )
field.setAccessible( true );
field.set( this, sortKeys );
if ( !accessible )
field.setAccessible( false );
} catch ( IllegalAccessException e ) {
LOGGER.error( null, e );
} catch ( IllegalArgumentException e ) {
LOGGER.error( null, e );
} catch ( NoSuchFieldException e ) {
LOGGER.error( null, e );
} catch ( SecurityException e ) {
LOGGER.error( null, e );
}
}
private Object getViewToModelInternal() {
try {
Field field = DefaultRowSorter.class.getDeclaredField( "viewToModel" );
boolean accessible = field.isAccessible();
if ( !accessible )
field.setAccessible( true );
Object ret = field.get( this );
if ( !accessible )
field.setAccessible( false );
return ret;
} catch ( IllegalAccessException e ) {
LOGGER.error( null, e );
} catch ( IllegalArgumentException e ) {
LOGGER.error( null, e );
} catch ( NoSuchFieldException e ) {
LOGGER.error( null, e );
} catch ( SecurityException e ) {
LOGGER.error( null, e );
}
return null;
}
private void sortExistingDataInternal() {
try {
Method method = DefaultRowSorter.class.getDeclaredMethod( "sortExistingData" );
boolean accessible = method.isAccessible();
if ( !accessible )
method.setAccessible( true );
method.invoke( this );
if ( !accessible )
method.setAccessible( false );
} catch ( IllegalAccessException e ) {
LOGGER.error( null, e );
} catch ( IllegalArgumentException e ) {
LOGGER.error( null, e );
} catch ( NoSuchMethodException e ) {
LOGGER.error( null, e );
} catch ( InvocationTargetException e ) {
LOGGER.error( null, e );
LOGGER.error( null, ( ( InvocationTargetException )e ).getCause() );
}
}
#Override
public void setSortKeys( List< ? extends SortKey > sortKeys ) {
List< ? extends SortKey > oldSortKeys = getSortKeys();
List< ? extends SortKey > newSortKeys;
if ( sortKeys != null && !sortKeys.isEmpty() ) {
int max = getModelWrapper().getColumnCount();
for ( SortKey key : sortKeys )
if ( key == null || key.getColumn() < 0 || key.getColumn() >= max )
throw new IllegalArgumentException( "Invalid SortKey" );
newSortKeys = Collections.unmodifiableList( new ArrayList< SortKey >( sortKeys ) );
} else
newSortKeys = Collections.emptyList();
setSortKeysInternal( newSortKeys );
if ( !newSortKeys.equals( oldSortKeys ) ) {
fireSortOrderChanged();
boolean wasChanged = false;
if ( preColumns.length > 0 || postColumns.length > 0 ) {
List< SortKey > editableSortKeys = new ArrayList< SortKey >( newSortKeys );
for ( int i = preColumns.length - 1; i >= 0; i-- ) {
int modelColumn = preColumns[ i ].getColumn();
int idx = indexOfColumn( editableSortKeys, preColumns.length - i - 1, editableSortKeys.size(), modelColumn );
SortOrder sortOrder = idx < 0 ? preColumns[ i ].getSortOrder() : editableSortKeys.remove( idx ).getSortOrder();
editableSortKeys.add( 0, new SortKey( modelColumn, sortOrder ) );
}
int to = editableSortKeys.size();
for ( SortKey postColumn : postColumns ) {
int modelColumn = postColumn.getColumn();
int idx = indexOfColumn( editableSortKeys, preColumns.length, to, modelColumn );
SortOrder sortOrder;
if ( idx < 0 )
sortOrder = postColumn.getSortOrder();
else {
sortOrder = editableSortKeys.remove( idx ).getSortOrder();
to--;
}
editableSortKeys.add( new SortKey( modelColumn, sortOrder ) );
}
if ( wasChanged = !editableSortKeys.equals( newSortKeys ) )
setSortKeysInternal( editableSortKeys );
}
if ( getViewToModelInternal() == null )
sort();
else
sortExistingDataInternal();
if ( wasChanged )
setSortKeysInternal( newSortKeys );
}
}
private int indexOfColumn( List< SortKey > sortKeys, int fromIncl, int toExcl, int column ) {
for ( int i = toExcl - 1; i >= fromIncl; i-- )
if ( sortKeys.get( i ).getColumn() == column )
return i;
return -1;
}
};
Usage:
table.setRowSorter( new PredefinedRowSorter( table )
.withPreColumns( new SortKey( 0, SortOrder.ASCENDING ),
new SortKey( 1, SortOrder.ASCENDING ) ) );
This would set a preceding sorting on first two columns.
Post-sorting is available.
An end-user may sort these two columns too, thus switching the order of sorting (ascending/descending).
Also, this class is available as a part of JBroTable library (source code).
The principle of action is so: predefined columns are added to a sorting columns list before sorting and removed from this list after sorting. Adding and removal is performed via reflection because DefaultRowSorter implementation doesn't provide an access to the list.
I'd like to create a Google Chrome favorites-style popover with arrow, rounded corners and if I have time a shadow effect. In Java Swing. What is the best approach? SplashScreen? Or just a plain AWT Window? Other ideas? Thanks!
There are a few options and each of them has its own pros and cons...
Create a custom-shaped window - with this approach some systems will be able to create additional shade behind the shaped window, also this works on most of systems (should work even on linux JDK's). The bad thing about this approach (that actually makes it unusable) is the unaliased shape border line - if you create some ellipse-shaped window its sides will appear rough.
Create a non-opaque undecorated window with drawn shape - this approach will fix the main problem of the (1) approach. You can alias shape you re drawing on fully transparent window. The bad thing about this one is that it works only on Win and Mac systems. On (mostly) any linux system you will get a rectangle resulting window and tons of errors about unsupported operations.
Create a custom-shaped popup inside the java-window and place it on the window layered or glass panes. This will allow you to fully avoid any compatibility problems and get the benefits of the (2) approach. There is a bad thing about this approach though - you can only display such popup in window root pane bounds. This is still much better than two other ways in most of cases, since it uses less resources, does not create additional windows and you can control every part of the popup.
About the 3rd approach - you can check TooltipManager i have created in my own project WebLookAndFeel - it uses window glass pane to display custom-shaped semi-transparent tooltips with a shadow-effect. Also soon enough i will add window PopupManager that will allow quick creation of "inner" window popups.
Here are some examples of the approaches:
A bit of code that used ahead in all of the examples
Method to create shape:
private static Area createShape ()
{
Area shape = new Area ( new RoundRectangle2D.Double ( 0, 20, 500, 200, 20, 20 ) );
GeneralPath gp = new GeneralPath ( GeneralPath.WIND_EVEN_ODD );
gp.moveTo ( 230, 20 );
gp.lineTo ( 250, 0 );
gp.lineTo ( 270, 20 );
gp.closePath ();
shape.add ( new Area ( gp ) );
return shape;
}
Mouse adapter that allows to move window by dragging the component:
public static class WindowMoveAdapter extends MouseAdapter
{
private boolean dragging = false;
private int prevX = -1;
private int prevY = -1;
public WindowMoveAdapter ()
{
super ();
}
public void mousePressed ( MouseEvent e )
{
if ( SwingUtilities.isLeftMouseButton ( e ) )
{
dragging = true;
}
prevX = e.getXOnScreen ();
prevY = e.getYOnScreen ();
}
public void mouseDragged ( MouseEvent e )
{
if ( prevX != -1 && prevY != -1 && dragging )
{
Window w = SwingUtilities.getWindowAncestor ( e.getComponent () );
if ( w != null && w.isShowing () )
{
Rectangle rect = w.getBounds ();
w.setBounds ( rect.x + ( e.getXOnScreen () - prevX ),
rect.y + ( e.getYOnScreen () - prevY ), rect.width, rect.height );
}
}
prevX = e.getXOnScreen ();
prevY = e.getYOnScreen ();
}
public void mouseReleased ( MouseEvent e )
{
dragging = false;
}
}
1st approach example:
public static void main ( String[] args )
{
JFrame frame = new JFrame ();
frame.setUndecorated ( true );
JPanel panel = new JPanel ();
panel.setBackground ( Color.BLACK );
WindowMoveAdapter wma = new WindowMoveAdapter ();
panel.addMouseListener ( wma );
panel.addMouseMotionListener ( wma );
frame.getContentPane ().add ( panel );
Area shape = createShape ();
AWTUtilities.setWindowShape ( frame, shape );
frame.setSize ( shape.getBounds ().getSize () );
frame.setLocationRelativeTo ( null );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setVisible ( true );
}
As you can see - corners of the rounded shape are pretty rough and not good-looking
2nd approach:
public static void main ( String[] args )
{
JFrame frame = new JFrame ();
frame.setUndecorated ( true );
final Area shape = createShape ();
JPanel panel = new JPanel ()
{
protected void paintComponent ( Graphics g )
{
super.paintComponent ( g );
Graphics2D g2d = ( Graphics2D ) g;
g2d.setRenderingHint ( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON );
g2d.setPaint ( Color.BLACK );
g2d.fill ( shape );
}
};
panel.setOpaque ( false );
WindowMoveAdapter wma = new WindowMoveAdapter ();
panel.addMouseListener ( wma );
panel.addMouseMotionListener ( wma );
frame.getContentPane ().add ( panel );
AWTUtilities.setWindowOpaque ( frame, false );
frame.setSize ( shape.getBounds ().getSize () );
frame.setLocationRelativeTo ( null );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setVisible ( true );
}
Now it should look perfect - the only problem this will properly work only on Windows and Mac (atleast in 1.6.x JDK). Atleast it was so about a month ago when i last time checked it on various OS.
3rd approach
public static void main ( String[] args )
{
JFrame frame = new JFrame ();
JPanel panel = new JPanel ( new BorderLayout () );
panel.setOpaque ( false );
WindowMoveAdapter wma = new WindowMoveAdapter ();
panel.addMouseListener ( wma );
panel.addMouseMotionListener ( wma );
frame.getContentPane ().add ( panel );
panel.add ( new JButton ( "Test" ) );
final Area shape = createShape ();
JPanel glassPane = new JPanel ( null )
{
public boolean contains ( int x, int y )
{
// This is to avoid cursor and mouse-events troubles
return shape.contains ( x, y );
}
};
glassPane.setOpaque ( false );
frame.setGlassPane ( glassPane );
glassPane.setVisible ( true );
JComponent popup = new JComponent ()
{
protected void paintComponent ( Graphics g )
{
super.paintComponent ( g );
Graphics2D g2d = ( Graphics2D ) g;
g2d.setRenderingHint ( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON );
g2d.setPaint ( Color.BLACK );
g2d.fill ( shape );
}
};
popup.addMouseListener ( new MouseAdapter ()
{
// To block events on the popup
});
glassPane.add ( popup );
popup.setBounds ( shape.getBounds () );
popup.setVisible ( true );
frame.setSize ( 800, 500 );
frame.setLocationRelativeTo ( null );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setVisible ( true );
}
This is a simple example of the popup placed on glass-pane. As you can see it exists only inside of the JFrame, but has the aliased side and works properly on any type of OS.