Class linked into start button won't run - java

In a GUI in Java, when I click a Start button, I want this main class WumpusWorld to run a certain method. I then want the GUI to refresh and see a change. At the moment, I click Start and nothing happens. EDIT - I am going to give you guys the full classes I am using. You should be able to run them in Eclipse. I just want this program to do SOMETHING when I click start, because at the moment it is doing NOTHING.
WumpusWorld.java (main class)
import java.util.LinkedList;
public class WumpusWorld {
private static int size;
private static Node startpoint;
private static Node endpoint;
private static int mode;
//private static String[][] nodeTypes;
private static LinkedList<Node> thegrid;
private static WumpusGui wgui;
public static void main(String[] args) {
// Show the GUI
thegrid = new LinkedList<Node>();
wgui = new WumpusGui();
// Set an initial size*/
setSize(5);
}
// Set up the grid
public static void setUpGrid() {
thegrid.clear();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
thegrid.add(new Node(i, j, "empty"));
}
}
wgui.setGridSize(size);
wgui.setGrid(thegrid);
}
/**
*
*/
public static void run() {
mode = 0; // Manhattan Heuristic
// Ensure that the grid is valid
int start = 0;
int end = 0;
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if(thegrid.get(i*size+j).isGoal()) {
end++;
endpoint = thegrid.get(i*size+j);
} else if(thegrid.get(i*size+j).isRobot()) {
start++;
startpoint = thegrid.get(i*size+j);
}
}
}
if(!(start == 1 && end == 1)) {
return;
}
Pathfinder pathf = new Pathfinder(thegrid);
String temp = pathf.pathfind(startpoint, endpoint, mode);
temp = temp.substring(1);
temp = temp.substring(0, temp.length()-1);
String[] result = temp.split("\\)\\(");
int finalcounter = result.length;
LinkedList<Node> victoryPath = new LinkedList<Node>();
for (String a : result) {
String[] b = a.split(",");
victoryPath.add(new Node(Integer.parseInt(b[0]), Integer.parseInt(b[1]), "empty"));
System.out.println("lol");
}
for(Node n : victoryPath) {
thegrid.get(n.getX()*size + n.getY()).setType("path");
}
}
// Setter for size
public static void setSize(int newSize) {
size = newSize;
setUpGrid();
}
public static void setStartPos(int x, int y) {
// Remove the old start pos
for(int i=0; i<size*size; i++) {
if(thegrid.get(i).isRobot()) {
thegrid.get(i).setType("empty");
break;
}
}
// Add the new pos
thegrid.get((x*size) + y).setType("robot");
}
public static void setEndPos(int x, int y) {
// Remove the old start pos
for(int i=0; i<size*size; i++) {
if(thegrid.get(i).isGoal()) {
thegrid.get(i).setType("empty");
break;
}
}
// Add the new pos
thegrid.get((x*size) + y).setType("goal");
}
public static void setObstacle(int x, int y) {
// Add the obstacle
thegrid.get((x*size) + y).setType("wall");
}
public static void removeObstacle(int x, int y) {
// Add the obstacle
thegrid.get((x*size) + y).setType("empty");
}
}
WumpusGui.java (JPanel)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.LinkedList;
import javax.swing.*;
public class WumpusGui extends JFrame {
private static final long serialVersionUID = 1L;
// This GUI
private static WumpusGui wgui;
// Controls
// NxN Grid Input
private JPanel pnlLayoutGrid;
private JLabel lblGrid;
private JTextField txtGrid;
private JButton btnGrid;
// Obstacle input
private JPanel pnlLayoutObstacles;
private JLabel lblObstacleX;
private JTextField txtObstacleX;
private JLabel lblObstacleY;
private JTextField txtObstacleY;
private JButton btnObstacleAdd;
private JLabel lblObstacleLog;
private JPanel pnlLayoutObstacleButtons;
// Start and end box positions
private JPanel pnlStartEndPos;
private JPanel pnlStartPos;
private JLabel lblStartX;
private JTextField txtStartX;
private JLabel lblStartY;
private JTextField txtStartY;
private JButton btnStartPos;
private JPanel pnlEndPos;
private JLabel lblEndPos;
private JLabel lblEndX;
private JTextField txtEndX;
private JLabel lblEndY;
private JTextField txtEndY;
private JButton btnEndPos;
// Start and stop buttons
private JPanel pnlStartStop;
private JButton btnStart;
private JButton btnStop;
// Display
private JPanel pnlDisplay;
private JPanelGrid pnlGraphics;
private JLabel pnlText;
// Window layouts
private JPanel pnlTop;
private JPanel pnlTopLeft;
private JPanel pnlTopRight;
public WumpusGui() {
// Set up window
setTitle("Wumpus World GUI");
setBackground(Color.GRAY);
// Set up controls
Dimension d = new Dimension(50, 25);
// Set up basic layout
pnlTop = new JPanel();
add(pnlTop, BorderLayout.NORTH);
pnlTopLeft = new JPanel();
pnlTopLeft.setLayout(new BoxLayout(pnlTopLeft, BoxLayout.Y_AXIS));
pnlTopRight = new JPanel();
pnlTop.add(pnlTopLeft, BorderLayout.WEST);
pnlTop.add(pnlTopRight, BorderLayout.EAST);
// Set up NxN grid controls
pnlLayoutGrid = new JPanel();
pnlLayoutGrid.setBorder(BorderFactory.createTitledBorder("Grid"));
pnlTopLeft.add(pnlLayoutGrid);
lblGrid = new JLabel("Grid size");
txtGrid = new JTextField();
txtGrid.setPreferredSize(d);
btnGrid = new JButton("Set size");
pnlLayoutGrid.add(lblGrid);
pnlLayoutGrid.add(txtGrid);
pnlLayoutGrid.add(btnGrid);
// Set up start and end position controls
pnlStartEndPos = new JPanel();
pnlStartEndPos.setLayout(new BoxLayout(pnlStartEndPos, BoxLayout.Y_AXIS));
pnlTopLeft.add(pnlStartEndPos);
pnlStartPos = new JPanel();
pnlStartPos.setBorder(BorderFactory.createTitledBorder("Start position"));
pnlStartEndPos.add(pnlStartPos);
lblStartX = new JLabel("X: ");
txtStartX = new JTextField();
txtStartX.setPreferredSize(d);
lblStartY = new JLabel("Y: ");
txtStartY = new JTextField();
txtStartY.setPreferredSize(d);
btnStartPos = new JButton("Set");
pnlStartPos.add(lblStartX);
pnlStartPos.add(txtStartX);
pnlStartPos.add(lblStartY);
pnlStartPos.add(txtStartY);
pnlStartPos.add(btnStartPos);
pnlEndPos = new JPanel();
pnlEndPos.setBorder(BorderFactory.createTitledBorder("End position"));
pnlStartEndPos.add(pnlEndPos);
lblEndX = new JLabel("X: ");
txtEndX = new JTextField();
txtEndX.setPreferredSize(d);
lblEndY = new JLabel("Y: ");
txtEndY = new JTextField();
txtEndY.setPreferredSize(d);
btnEndPos = new JButton("Set");
pnlEndPos.add(lblEndX);
pnlEndPos.add(txtEndX);
pnlEndPos.add(lblEndY);
pnlEndPos.add(txtEndY);
pnlEndPos.add(btnEndPos);
// Set up obstacles controls
pnlLayoutObstacles = new JPanel();
pnlLayoutObstacles.setBorder(BorderFactory.createTitledBorder("Obstacles"));
pnlLayoutObstacles.setLayout(new BoxLayout(pnlLayoutObstacles, BoxLayout.Y_AXIS));
pnlTopRight.add(pnlLayoutObstacles);
lblObstacleX = new JLabel("X: ");
txtObstacleX = new JTextField();
txtObstacleX.setPreferredSize(d);
lblObstacleY = new JLabel("Y: ");
txtObstacleY = new JTextField();
txtObstacleY.setPreferredSize(d);
btnObstacleAdd = new JButton("Add");
pnlLayoutObstacleButtons = new JPanel();
pnlLayoutObstacles.add(pnlLayoutObstacleButtons, BorderLayout.NORTH);
pnlLayoutObstacleButtons.add(lblObstacleX);
pnlLayoutObstacleButtons.add(txtObstacleX);
pnlLayoutObstacleButtons.add(lblObstacleY);
pnlLayoutObstacleButtons.add(txtObstacleY);
pnlLayoutObstacleButtons.add(btnObstacleAdd);
// Graphical display
pnlGraphics = new JPanelGrid();
pnlGraphics.setPreferredSize(new Dimension(200, 200));
pnlGraphics.setN(10);
pnlGraphics.setBackground(Color.WHITE);
add(pnlGraphics, BorderLayout.CENTER);
// Add the start button
btnStart = new JButton("Start");
pnlTopRight.add(btnStart, BorderLayout.SOUTH);
// Button setup
btnGrid.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
WumpusWorld.setSize(Integer.parseInt(txtGrid.getText()));
pnlGraphics.repaint();
} catch(Exception ex) {
// Nothing
}
}
});
btnStartPos.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
WumpusWorld.setStartPos(Integer.parseInt(txtStartX.getText()), Integer.parseInt(txtStartY.getText()));
pnlGraphics.repaint();
} catch(Exception ex) {
// Nothing
}
}
});
btnEndPos.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
WumpusWorld.setEndPos(Integer.parseInt(txtEndX.getText()), Integer.parseInt(txtEndY.getText()));
pnlGraphics.repaint();
} catch(Exception ex) {
// Nothing
}
}
});
btnObstacleAdd.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
WumpusWorld.setObstacle(Integer.parseInt(txtObstacleX.getText()), Integer.parseInt(txtObstacleY.getText()));
pnlGraphics.repaint();
} catch(Exception ex) {
// Nothing
}
}
});
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
WumpusWorld.run();
pnlGraphics.repaint();
} catch(Exception ex) {
// Nothing
}
}
});
pnlGraphics.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
// Get X and Y
int x = e.getX();
int y = e.getY();
// Left click = add obstacle
if(e.getButton() == MouseEvent.BUTTON1) {
pnlGraphics.addObstacle(x, y);
// Right click = remove obstacle
} else {
pnlGraphics.removeObstacle(x, y);
}
pnlGraphics.repaint();
}
// These methods are not used
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
});
// finish off the window
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void drawGrid(int n) {
// Draw the base grid
Graphics g;
}
public static WumpusGui get() {
return wgui;
}
public void setGridSize(int size) {
pnlGraphics.setN(size);
txtGrid.setText(String.valueOf(size));
}
// Set the grid in the graphics display
public void setGrid(LinkedList<Node> thegrid) {
pnlGraphics.setObstacles(thegrid);
}
// Private class used for displaying the grid visually
private class JPanelGrid extends JPanel {
private int n; // width/height of grid
private LinkedList<Node> obstacles;
private LinkedList<Clickable> clickables = new LinkedList<>();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Set up dimensions
int width = getWidth();
int height = getHeight();
int gridX = 10;
int gridY = 10;
int gridW = width-20;
int gridH = height-20;
// Draw the grid's background
g.setColor(Color.BLACK);
g.fillRect(gridX, gridY, gridW, gridH);
// Empty the clickable list
clickables.clear();
// Draw cells
int nodePointer = 0;
if(obstacles != null) {
for(int i=0, ii=0; ii<n; i+=(gridW/n), ii++) {
for(int j=0, jj=0; jj<n; j+=(gridH/n), jj++) {
// Set the color depending on the node's type
if(obstacles.get(nodePointer).isEmpty()) {
g.setColor(Color.WHITE);
} else if(obstacles.get(nodePointer).isWall()) {
g.setColor(Color.DARK_GRAY);
} else if(obstacles.get(nodePointer).isRobot()) {
g.setColor(Color.RED);
} else if(obstacles.get(nodePointer).isEmpty()) {
g.setColor(Color.CYAN);
} else {
g.setColor(Color.YELLOW);
}
// Draw the rectangle
int x = gridX + (int)i + 1;
int y = gridY + (int)j + 1;
int w = gridW/n - 2;
int h = gridH/n - 2;
clickables.add(new Clickable(x, y, w, h, ii, jj));
g.fillRect(x, y, w, h);
// Draw the X and Y coords
g.setColor(Color.GRAY);
g.drawString("x" + ii + ", y" + jj, x+4, y+11);
nodePointer++;
}
}
}
}
// Setter for N (size)
public void setN(int n) {
this.n = n;
}
// Setter for obstacles
public void setObstacles(LinkedList<Node> obstacles) {
this.obstacles = obstacles;
}
public void addObstacle(int x, int y) {
for(Clickable c : clickables) {
if(c.inside(x, y)) {
WumpusWorld.setObstacle(c.getX(), c.getY());
}
}
}
public void removeObstacle(int x, int y) {
for(Clickable c : clickables) {
if(c.inside(x, y)) {
WumpusWorld.removeObstacle(c.getX(), c.getY());
}
}
}
// This class is used to allow the user to click on the grid to add an obstacle.
private class Clickable {
private Rectangle rect;
private int x;
private int y;
public Clickable(int rectX, int rectY, int width, int height, int x, int y) {
rect = new Rectangle(rectX, rectY, width, height);
this.x = x;
this.y = y;
}
public boolean inside(int x, int y) {
return rect.contains(x, y);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
}
}
Pathfinder.java (A* search algorithm)
import java.util.HashSet;
import java.util.HashMap;
import java.util.Iterator;
import java.lang.Math;
public class Pathfinder {
/**
* Variable for the cost of each movement on the grid. Only for up/down/left/right movement.
* Use {#link #setMovementCost(double)} to change from an external class.
*/
private double movementCost = 1.0;
/**
* Storage variable for the input
*/
private Iterable<Node> iterableInput; //Input
/**
* Storage variable for the G-cost of the node, in A*
* <p>
* F = G + H
*/
private HashMap<Node,Double> gmap; //map of travel cost
/**
* Storage variable for the F-cost of the node, in A*
* <p>
* F = G + H
*/
private HashMap<Node,Double> fmap; //map of predicted total cost
/**
* Storage variable for a map of the paths on the grid. Every evaluated node points to its predecessor.
*/
private HashMap<Node,Node> navmap; //map of path to each node
/**
* Variable for which Pathfinding Heuristic to use.
* <p>
* Used in {#link #heuristicCost(Node, Node, int)}
* <p>
* default = Manhattan Distance
* <p>
* 1 = Diagonal Distance
*/
private static int heuristicMode;
/**
* General Pathfinder constructor
* <p>
* Takes iterable input as a parameter - use anything that extends Iterable
*
* #param input iterable input of nodes/boxes, such as a list
*/
public Pathfinder(Iterable<Node> input) {
iterableInput=input;
}
/**
* Pathfind the correct path
* <p>
* There is only one public method and it returns a path in the form of a String.
* <p>
* Parsing the answer string is not included in this class.
*
* #param start the starting node
* #param goal the finishing node
* #param mode integer for the heuristic being used
* #return String path of the form (0,0)(0,1)...
*/
public String pathfind(Node start, Node goal, int mode) {
heuristicMode=mode;
return astar(start, goal);
}
/**
* A* method.
* <p>
* Algorithm adapted from Wikipedia.
*
* #param start the starting node
* #param goal the finishing node
* #return String path of the form (0,0)(0,1)...
*/
private String astar(Node start, Node goal) {
HashSet<Node> closedset = new HashSet<Node>(); //set of evaluated nodes
HashSet<Node> openset = new HashSet<Node>(); //set of nodes to be evaluated
openset.add(start); //first node to be evaluated
gmap=populateHashMap(iterableInput); //initialize HashMap of visited Nodes
navmap=new HashMap<Node,Node>();
fmap=populateHashMap(iterableInput);
//Heuristic: F=G+H
//F = total cost
//G = path cost
//H = heuristic cost
//put appropriate G for start
gmap.put(start, 0.0);
fmap.put(start, gmap.get(start) + ManhattanDistance(start, goal)); //heuristic cost of 0
Node current;
while(!openset.isEmpty()) {
current = getCheapest(openset);
if(sameNode(current, goal))
return constructPath(navmap,goal);
openset.remove(current);
closedset.add(current);
HashSet<Node> neighbors = getNeighbors(current, heuristicMode);
for(Node neighbor : neighbors) {
if(closedset.contains(neighbor))
continue;
double temp_g = gmap.get(current) + movementCost;
if(!openset.contains(neighbor) || temp_g<gmap.get(neighbor)) {
navmap.put(neighbor, current);
gmap.put(neighbor,temp_g);
fmap.put(neighbor, gmap.get(neighbor) + heuristicCost(neighbor, goal, heuristicMode));
if(!openset.contains(neighbor))
openset.add(neighbor);
}
}
}
return "failed";
}
/**
* Set the cost for each movement.
* <p>
* This is for the up/down/left/right directions. The cost for moving diagonally is calculated based on that.
* #param cost the cost of moving up/down/left/right
*/
public void setMovementCost(double cost) {
movementCost=cost;
}
//Cost Heuristic
/**
* Heuristic switch function.
*
* #param current the current node
* #param goal the goal node
* #param mode the switch integer. Default for Manhattan. 1 for Diagonal.
* #return the cost that the switched-to heuristic has calculated
*/
private double heuristicCost(Node current, Node goal, int mode) {
switch(mode){
case 1: return DiagonalDistance(current, goal);
default: return ManhattanDistance(current,goal);
}
}
//Manhattan Distance
/**
* Manhattan Distance heuristic.
* <p>
* Adapted from http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
*
* #param current the current node
* #param goal the goal node
* #return the H-cost of the current node
*/
private double ManhattanDistance(Node current, Node goal) {
return movementCost*(Math.abs(current.getX()-goal.getX())+Math.abs(current.getY()-goal.getY()));
}
//Diagonal Distance
/**
* Diagonal Distance heuristic.
* <p>
* Adapted from http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
*
* #param current the current node
* #param goal the goal node
* #return the H-cost of the current node
*/
private double DiagonalDistance(Node current, Node goal) {
return movementCost*(Math.abs(current.getX()-goal.getX())+Math.abs(current.getY()-goal.getY())) + (Math.sqrt(2)*movementCost-2*movementCost)*Math.min(Math.abs(current.getX()-goal.getX()), Math.abs(current.getY()-goal.getY()));
}
//Populating the Navigation Map
//All Values set to -1.0
/**
* Populates HashMap with parameter iterable input
* #param input the input which gets fed into the hashmap
*/
private HashMap<Node,Double> populateHashMap(Iterable<Node> input) {
HashMap<Node,Double> hashmap = new HashMap<Node,Double>();
Iterator<Node> iterator=input.iterator();
while(iterator.hasNext()) {
hashmap.put(iterator.next(), -1.0);
}
return hashmap;
}
//Getting the Cheapest Node from a HashSet
/**
* Gets the cheapest node of the openset
* #param openset the set of open nodes
* #return the node with the cheapest F-cost
*/
private Node getCheapest(HashSet<Node> openset) {
Iterator<Node> iterator=openset.iterator();
Node thisNode = iterator.next();
Node nextNode;
while(iterator.hasNext()) {
nextNode=iterator.next();
if(fmap.get(nextNode)<fmap.get(thisNode))
thisNode=nextNode;
}
return thisNode;
}
//Get the Neighbors of a Node
/**
* Gets the neighbors of the parameter node, based on the Heuristic mode
*
* #param current the current node
* #param mode the Heuristic mode. See {#link #heuristicMode}
* #return a HashSet of the neighboring nodes
*/
private HashSet<Node> getNeighbors(Node current, int mode) {
HashSet<Node> result = new HashSet<Node>();
HashSet<Node> keySet=(HashSet<Node>) gmap.keySet();
Iterator<Node> iterator=keySet.iterator();
while(iterator.hasNext()) {
Node thisNode=iterator.next();
//Manhattan movement
if(thisNode.getX()+1 == current.getX() && thisNode.getY() == current.getY())
result.add(thisNode);
else if(thisNode.getX()-1 == current.getX() && thisNode.getY() == current.getY())
result.add(thisNode);
else if(thisNode.getX() == current.getX() && thisNode.getY()+1 == current.getY())
result.add(thisNode);
else if(thisNode.getX() == current.getX() && thisNode.getY()-1 == current.getY())
result.add(thisNode);
//Diagonal movement
else if(mode==1) {
if(thisNode.getX()+1 == current.getX() && thisNode.getY()+1 == current.getY())
result.add(thisNode);
else if(thisNode.getX()+1 == current.getX() && thisNode.getY()-1 == current.getY())
result.add(thisNode);
else if(thisNode.getX()-1 == current.getX() && thisNode.getY()+1 == current.getY())
result.add(thisNode);
else if(thisNode.getX()-1 == current.getX() && thisNode.getY()-1 == current.getY())
result.add(thisNode);
}
}
return result;
}
//Same Nodes
//Checks whether the nodes are the same, based on their x and y
/**
* Checks for node similarity. This is included here in order to reduce class inter-dependency
* <p>
* Compares the results from getX() and getY() for each node.
* #param thisNode the first compared node
* #param otherNode the second compared node
* #return TRUE if their coordinates are the same
*/
private boolean sameNode(Node thisNode, Node otherNode) {
return thisNode.getX()==otherNode.getX() && thisNode.getY()==otherNode.getY();
}
//Path Constructor
//Returns String of Nodes in the form of (0,0)(0,1)...
/**
* Constructs a path from the parameter node and the now valued {#link #navmap}
* <p>
* Returns String with tokens built in {#link #toNodeString(Node)}
*
* #param hashmap the navmap
* #param last the goal -or- last node
* #return the String of nodes leading to the parameter node
*/
private String constructPath(HashMap<Node,Node> hashmap, Node last) {
String t;
if(hashmap.containsKey(last)) {
t = constructPath(hashmap, hashmap.get(last));
return (t + last);
}
else {
return toNodeString(last);
}
}
//Nodes to Strings in the form of "(x,y)"
/**
* Turns a node into a String
*
* #param a the parameter node
* #return the node represented in the String form of "(x,y)"
*/
private String toNodeString(Node a) {
return "("+a.getX()+","+a.getY()+")";
}
}
Node.java (Node class)
public class Node {
private int x;
private int y;
private boolean isEmpty, isWall, isRobot, isGoal;
/**
* Basic constructor.
* <p>
* Takes coordinates x, y, and box type.
*
* #param x the X coordinate
* #param y the Y coordinate
* #param type String for the type: "empty", "wall", "robot", "goal"
*/
public Node(int x, int y, String type) {
this.x = x;
this.y = y;
isEmpty=false;
isWall=false;
isRobot=false;
isGoal=false;
if(type.equalsIgnoreCase("empty"))
isEmpty=true;
else if(type.equalsIgnoreCase("wall"))
isWall=true;
else if(type.equalsIgnoreCase("robot"))
isRobot=true;
else if(type.equalsIgnoreCase("goal"))
isGoal=true;
}
/**
* Gets the x coordinate.
*
* #return the X coordinate
*/
public int getX() {
return x;
}
/**
* Gets the y coordinate.
*
* #return the Y coordinate
*/
public int getY() {
return y;
}
/**
* Checks if empty.
*
* #return true if empty
*/
public boolean isEmpty() {
return isEmpty;
}
/**
* Checks if is wall.
*
* #return true if wall
*/
public boolean isWall() {
return isWall;
}
/**
* Checks if is robot.
*
* #return true if robot
*/
public boolean isRobot() {
return isRobot;
}
/**
* Checks if is goal.
* #return true if goal
*/
public boolean isGoal() {
return isGoal;
}
/**
* Sets the type of the box
* <p>
* Does nothing with invalid input
*
* #param type String representing the type: "empty", "wall", "robot", "goal"
*/
public void setType(String type) {
if(type.equalsIgnoreCase("empty")) {
isWall=false;
isEmpty=true;
isRobot=false;
isGoal=false;
}
else if(type.equalsIgnoreCase("wall")) {
isWall=true;
isEmpty=false;
isRobot=false;
isGoal=false;
}
else if(type.equalsIgnoreCase("robot")) {
isWall=false;
isEmpty=false;
isRobot=true;
isGoal=false;
}
else if(type.equalsIgnoreCase("goal")) {
isWall=false;
isEmpty=false;
isRobot=false;
isGoal=true;
}
}
}

Java uses a dedicated thread to manage GUI and related events, which is called Event Dispatch Thread. You can't execute an intensive task directly inside a ActionListener callback because this would execute on the Swing thread and make the GUI unresponsive.
You should use a different thread to manage this, there are many possible solution, like using a normal Thread that calls repaint() or a SwingWorker. Take a look here and here for further informations.

Related

The width of the shelf minus the sum of the boxes widths inside it >= the width of the new box

I have a warehouse project I can create boxes with length, width and height and create shelves with length width height too. And I have an option to add that box to this shelf but I want to compare that the length, width and height of the box are smaller or equals the shelves (L, W, H) but for width has to be like this w - w2 >= w3, so I want to count it but I don't know how to find the sum of the already added boxes widths inside the shelf.
w = the width of the shelf
w2 = the sum of the already added boxes widths inside the shelf
w3 = the width of the new box
I am really beginner in java and need help please. this is the code so far I did for comparing:
private boolean fitsIntoShelf(int place, Box box) {
int w = getwidth()-"the sum of the boxes widths inside it";
if (this.getlength() >= box.getLength() && w >= box.getWidth() && this.getheight() >= box.getHeight()) {
return true;
} else {
return false;
}
}
Shelf class code:
package de.majed.warehouse;
import javax.swing.JOptionPane;
public class Shelf {
// the array of boxes stored in the shelf
private Box[] places;
// The properties of the shelf.
// size of the shelf
private int capacity = 10;
private int length, width, height;
private String ID;
/**
*
* #param capacity
* #param newid
*/
public Shelf(int cap, int newlength, int newwidth, int newheight, String newid) {
//
this.capacity = cap;
places = new Box[this.capacity];
setlength(newlength);
setwidth(newwidth);
setheight(newheight);
setid(newid);
}
/**
*
*/
public Shelf(int newlength, int newwidth, int newheight, String newid) {
setlength(newlength);
setwidth(newwidth);
setheight(newheight);
setid(newid);
}
public int getCapacity() {
return places.length;
}
public String getid() {
return ID;
}
private void setid(String newid) {
ID = newid;
}
public int getlength() {
return length;
}
private void setlength(int newlength) {
length = newlength;
}
public int getwidth() {
return width;
}
private void setwidth(int newwidth) {
width = newwidth;
}
public int getheight() {
return height;
}
private void setheight(int newheight) {
height = newheight;
}
/**
*
* #param place
* #return
*/
public Box getContentOf(int place) {
return places[place];
}
//
/**
* Add box if a place empty.
*
* #param box
* The Box which should be added
*/
public void addBox(Box box) {
/*
* check one time if box fits into shelf. if not >>>
* JOptionPane.showMessageDialog(null,
* "The box is bigger than the shelf.");
**/
for (int f = 0; f < places.length; f++) {
if (fitsIntoShelf(f, box)) {
} else {
JOptionPane.showMessageDialog(null, "The box is bigger than the shelf.");
break;
}
// check if there isa an empty place. if not
// JOptionPane.showMessageDialog(null, "No place found in the
// shelf.");
if (isPlaceEmpty(f)) {
places[f] = box;
return;
}
JOptionPane.showMessageDialog(null, "No place found in the shelf.");
}
}
/**
* Add the box if the place is empty.
*
* #param place
* #param box
*/
public void putBoxTo(int place, Box box) {
if (isPlaceEmpty(place)) {
places[place] = box;
} else {
// Message to user
JOptionPane.showInputDialog("There is no empty place");
}
}
// Check if place is empty.
public boolean isPlaceEmpty(int place) {
if (places[place] == null) {
return true;
} else {
return false;
}
}
private boolean fitsIntoShelf(int place, Box box) {
if (this.getlength() >= box.getLength() && this.getwidth() >= box.getWidth() && this.getheight() >= box.getHeight())
return true;
else {
return false;
}
}
// return index if found otherwise -1
/**
*
* #param box
* #return the index of the box, -1 if not found
*/
public int findBox(Box box, Shelf str) {
for (int i = 0; i < places.length; i++) {
if (places[i].equals(box)) {
JOptionPane.showMessageDialog(null, "The item has been found in: | " + str + " | ");
}
}
return -1;
}
public String toString() {
// To show the list of the shelves and the boxes inside it.
String str = "";
for (int i = 0; i < places.length; i++) {
if (!isPlaceEmpty(i)) {
str += places[i].toString() + "; ";
}
}
return "Shelf [ " + getid() + " ] >>>" + " boxes in shelf:" + str;
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Shelf))
return false;
Shelf b = (Shelf) obj;
if (b == null)
return false;
if (this.ID == null)
return b.ID == null;
return this.ID.equals(b.getid());
}
}
Box class code:
package de.majed.warehouse;
public class Box {
// The properties of the Box
private int Length, Width, Height;
private String id;
public Box() {
}
// To set the new properties values.
public Box(int newLength, int newWidth, int newHeight, String newID) {
setLength(newLength);
setWidth(newWidth);
setHeight(newHeight);
setID(newID);
}
public Box(Box newWidth) {
}
public int getLength() {
return Length;
}
public int getWidth() {
return Width;
}
public int getHeight() {
return Height;
}
public String getID() {
return id;
}
private void setLength(int newLength) {
Length = newLength;
}
private void setWidth(int newWidth) {
Width = newWidth;
}
private void setHeight(int newHeight) {
Height = newHeight;
}
private void setID(String newID) {
id = newID;
}
#Override
// To get the list of the new added boxes with their properties values.
public String toString() {
return "Box ID: " + getID() + " (L: " + getLength() + " - W: " + getWidth() + " - H: " + getHeight() + ")";
}
#Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if (!(obj instanceof Box))
return false;
Box b = (Box) obj;
if (b == null)
return false;
if (this.id == null)
return b.id == null;
return this.id.equals(b.getID());
}
}
Probably the simplest solution is to add a new property to Shelf called "availableWidth". Every time a box gets added to a shelf, you subtract the width of that box from availableWidth. In other words, it would be done in your addBox method. Then when you're writing your fitsIntoShelf method, you can just call getAvailableWidth() assuming you write a getter for it. Also, I'm wondering why you're passing in an int for place into the fitsIntoShelf() method since you're not doing anything with it. Not a huge deal, but it just seems to be dead code.

Mapping mouse event coordinates

I've implemented a JLayer<JPanel> component which paint a zoomed Graphics of itself, so all his descending components will be zoomed too.
This JLayer is applied as ContentPane to a JFrame component.
The main problem is that all the zoom applies, indeed, only to the graphic and the actual size and position of the components remain the same. This implies that all the mouse events happen in the wrong position respectively to what the user see.
I've king of tweaked it defining a GlassPane JComponent at the top of the frame which has it's own MouseInputAdapter which redispatch the MouseEvents to the underlying components using SwingUtilities.getDeepestComponentAt(). This is accomplished by creating a new MouseEvent with the mouse coordinates mapped depending on the zoom value. (obtained modifying the How to use RootPanes tutorial)
This method is obviously not satisfying because a lot of events simply can't be fired (for example the MOUSE_ENTERED events fired by all the descending components).
On the other hand using the LayerUI.paint() override implies that i have to have something which remap all the mouse coordinates.
Is there a way to map the mouse coordinates without breaking the inner MouseEvent processing?
or
Is there another way to zoom in the bitmap of the components while modifying also the real position and size? I've kind of tried this but calling the setSize() of an inner component seems to someway call the LayerUi.paint() a second time so all that i get is some bigger graphic disconnected from the actual widget position
This is what i've done so far thanks to MadProgrammer and PBar extensions, working with JLayer in java 8.
The TransformUI include in itself the PBar's org.pbjar.jxlayer.plaf.ext.MouseEventUI and org.pbjar.jxlayer.plaf.ext.TransformUI:
package jzoom.transform;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JComponent;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.plaf.LayerUI;
/**
*
* This UI apply an {#link AffineTransform} to all the visible objects, and apply the inversion
* of the same transform to all the mouse event's coordinates
*
* #author Andrea.Maracci based on the pbjar JXLayer extension
*
*/
public class TransformUI extends LayerUI<JComponent> {
private static final long serialVersionUID = 1L;
private Component lastEnteredTarget, lastPressedTarget;
private final Set<JComponent> originalDoubleBuffered = new HashSet<JComponent>();
private AffineTransform transform = new AffineTransform();
private JLayer<JComponent> installedLayer;
private boolean dispatchingMode = false;
/**
* Process the mouse events and map the mouse coordinates inverting the internal affine transformation.
*
* #param
* event the event to be dispatched
* layer the layer this LayerUI is set to
*
*/
#Override
public void eventDispatched(AWTEvent event, final JLayer<? extends JComponent> layer) {
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
/*
* The if discriminate between generated and original event.
* Removing it cause a stack overflow caused by the event being redispatched to this class.
*/
if (!dispatchingMode) {
// Process an original mouse event
dispatchingMode = true;
try {
redispatchMouseEvent(mouseEvent, layer);
} finally {
dispatchingMode = false;
}
} else {
/*
* Process generated mouse events
* Added a check, because on mouse entered or exited, the cursor
* may be set to specific dragging cursors.
*/
if (MouseEvent.MOUSE_ENTERED == mouseEvent.getID() || MouseEvent.MOUSE_EXITED == mouseEvent.getID()) {
layer.getGlassPane().setCursor(null);
} else {
Component component = mouseEvent.getComponent();
layer.getGlassPane().setCursor(component.getCursor());
}
}
} else {
super.eventDispatched(event, layer);
}
layer.repaint();
}
/**
* Set the affine transformation applied to the graphics
* #param transform the transformation
*/
public void setTransform(AffineTransform transform) {
if (transform != null) {
this.transform = transform;
}
}
/**
* Return the affine transformation applied to the graphics
* #return the transformation
*/
public AffineTransform getTransform() {
return transform;
}
/**
* Paint the specified component {#code c} applying the transformation on it's graphic
*
* #param
* g - the Graphics context in which to paint
* c - the component being painted
*/
#Override
public void paint(Graphics g, JComponent c) {
if (g instanceof Graphics2D) {
Graphics2D g2 = (Graphics2D) g.create();
JLayer<? extends JComponent> l = (JLayer<? extends JComponent>) c;
g2.transform(transform);
paintLayer(g2, l);
g2.dispose();
}
}
/**
* Paint the view decorated by the JLayer {#code layer} and the JLayer itself
*
* #param g2
* #param layer the layer this LayerUI is set to
*/
private final void paintLayer(Graphics2D g2, JLayer<? extends JComponent> layer) {
JComponent view = layer.getView();
if (view != null) {
if (view.getX() < 0 || view.getY() < 0) {
setToNoDoubleBuffering(view);
g2.translate(view.getX(), view.getY());
view.paint(g2);
for (JComponent jComp : originalDoubleBuffered) {
jComp.setDoubleBuffered(true);
}
originalDoubleBuffered.clear();
return;
}
}
layer.paint(g2);
}
/**
* Disable the double buffering for the {#code component} and for all of it's children
*
* #param component
*/
private void setToNoDoubleBuffering(Component component) {
if (component instanceof JComponent) {
JComponent jComp = (JComponent) component;
if (jComp.isDoubleBuffered()) {
originalDoubleBuffered.add(jComp);
jComp.setDoubleBuffered(false);
}
}
if (component instanceof Container) {
Container container = (Container) component;
for (int index = 0; index < container.getComponentCount(); index++) {
setToNoDoubleBuffering(container.getComponent(index));
}
}
}
/**
* {#inheritDoc}
*/
#Override
public void uninstallUI(JComponent component) {
if (!(component instanceof JLayer<?>)) {
throw new IllegalArgumentException(
this.getClass().getName() + " invalid class, must be a JLayer component");
}
JLayer<JComponent> jlayer = (JLayer<JComponent>) component;
jlayer.setLayerEventMask(0);
super.uninstallUI(component);
}
/**
* {#inheritDoc}
*/
#Override
public void installUI(JComponent component) throws IllegalStateException {
super.installUI(component);
if (installedLayer != null) {
throw new IllegalStateException(this.getClass().getName() + " cannot be shared between multiple layers");
}
if (!(component instanceof JLayer<?>)) {
throw new IllegalArgumentException(
this.getClass().getName() + " invalid class, must be a JLayer component");
}
// component.getClass().getDeclaringClass();
installedLayer = (JLayer<JComponent>) component;
installedLayer.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK
| AWTEvent.MOUSE_WHEEL_EVENT_MASK | AWTEvent.KEY_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
}
/**
* Process the mouse events and map the mouse coordinates inverting the internal affine transformation.
* It consume the original event, calculates the mapped mouse coordinates and find the real target of the mouse event.
* It than create a new event with the correct informations in it and redispatch it to the target event
*
* #param originalEvent the event to be dispatched
* #param layer the layer this LayerUI is set to
*/
private void redispatchMouseEvent(MouseEvent originalEvent, JLayer<? extends JComponent> layer) {
if (layer.getView() != null) {
if (originalEvent.getComponent() != layer.getGlassPane()) {
originalEvent.consume();
}
MouseEvent newEvent = null;
Point realPoint = calculateTargetPoint(layer, originalEvent);
Component realTarget = getTarget(layer, realPoint);
// Component realTarget =
// SwingUtilities.getDeepestComponentAt(layer.getView(),
// realPoint.x, realPoint.y);
if (realTarget != null) {
//System.out.println(realTarget.getClass().getName());
realTarget = getListeningComponent(originalEvent, realTarget);
}
switch (originalEvent.getID()) {
case MouseEvent.MOUSE_PRESSED:
newEvent = transformMouseEvent(layer, originalEvent, realTarget, realPoint);
if (newEvent != null) {
lastPressedTarget = newEvent.getComponent();
}
break;
case MouseEvent.MOUSE_RELEASED:
newEvent = transformMouseEvent(layer, originalEvent, lastPressedTarget, realPoint);
lastPressedTarget = null;
break;
case MouseEvent.MOUSE_CLICKED:
newEvent = transformMouseEvent(layer, originalEvent, realTarget, realPoint);
lastPressedTarget = null;
break;
case MouseEvent.MOUSE_MOVED:
newEvent = transformMouseEvent(layer, originalEvent, realTarget, realPoint);
generateEnterExitEvents(layer, originalEvent, realTarget, realPoint);
break;
case MouseEvent.MOUSE_ENTERED:
generateEnterExitEvents(layer, originalEvent, realTarget, realPoint);
break;
case MouseEvent.MOUSE_EXITED:
generateEnterExitEvents(layer, originalEvent, realTarget, realPoint);
break;
case MouseEvent.MOUSE_DRAGGED:
newEvent = transformMouseEvent(layer, originalEvent, lastPressedTarget, realPoint);
generateEnterExitEvents(layer, originalEvent, realTarget, realPoint);
break;
case (MouseEvent.MOUSE_WHEEL):
// redispatchMouseWheelEvent((MouseWheelEvent) originalEvent,
// realTarget, realPoint);
newEvent = transformMouseWheelEvent(layer, (MouseWheelEvent) originalEvent, realTarget, realPoint);
break;/**/
}
dispatchMouseEvent(newEvent);
}
}
/**
* Apply the inverse transformation to {#code point}
*
* #param layer the layer this LayerUI is set to
* #param point the starting point
* #return the transformed point
*/
private Point transformPoint(JLayer<? extends JComponent> layer, Point point) {
if (transform != null) {
try {
transform.inverseTransform(point, point);
} catch (NoninvertibleTransformException e) {
e.printStackTrace();
}
}
return point;
}
/**
* Find the deepest component in the AWT hierarchy
*
* #param layer the layer to which this UI is installed
* #param targetPoint the point in layer's coordinates
* #return the component in the specified point
*/
private Component getTarget(JLayer<? extends JComponent> layer, Point targetPoint) {
Component view = layer.getView();
if (view == null) {
return null;
} else {
Point viewPoint = SwingUtilities.convertPoint(layer, targetPoint, view);
return SwingUtilities.getDeepestComponentAt(view, viewPoint.x, viewPoint.y);
}
}
/**
* Convert the {#code mouseEvent}'s coordinates to the {#code layer}'s space
* #param layer the layer this LayerUI is set to
* #param mouseEvent the original mouse event
* #return the {#code mouseEvent}'s point transformed to the {#code layer}'s coordinate space
*/
private Point calculateTargetPoint(JLayer<? extends JComponent> layer,
MouseEvent mouseEvent) {
Point point = mouseEvent.getPoint();
//SwingUtilities.convertPointToScreen(point, mouseEvent.getComponent());
//SwingUtilities.convertPointFromScreen(point, layer);
point = SwingUtilities.convertPoint(mouseEvent.getComponent(), point, layer);
return transformPoint(layer, point);
}
private MouseEvent transformMouseEvent(JLayer<? extends JComponent> layer, MouseEvent mouseEvent, Component target, Point realPoint) {
return transformMouseEvent( layer, mouseEvent, target, realPoint, mouseEvent.getID());
}
/**
* Create the new event to being dispatched
*/
private MouseEvent transformMouseEvent(JLayer<? extends JComponent> layer, MouseEvent mouseEvent, Component target, Point targetPoint, int id) {
if (target == null) {
return null;
} else {
Point newPoint = SwingUtilities.convertPoint(layer, targetPoint, target);
return new MouseEvent(target, //
id, //
mouseEvent.getWhen(), //
mouseEvent.getModifiers(), //
newPoint.x, //
newPoint.y, //
mouseEvent.getClickCount(), //
mouseEvent.isPopupTrigger(), //
mouseEvent.getButton());
}
}
/**
* Create the new mouse wheel event to being dispached
*/
private MouseWheelEvent transformMouseWheelEvent( JLayer<? extends JComponent> layer, MouseWheelEvent mouseWheelEvent, Component target,
Point targetPoint) {
if (target == null) {
return null;
} else {
Point newPoint = SwingUtilities.convertPoint(layer, targetPoint, target);
return new MouseWheelEvent(target, //
mouseWheelEvent.getID(), //
mouseWheelEvent.getWhen(), //
mouseWheelEvent.getModifiers(), //
newPoint.x, //
newPoint.y, //
mouseWheelEvent.getClickCount(), //
mouseWheelEvent.isPopupTrigger(), //
mouseWheelEvent.getScrollType(), //
mouseWheelEvent.getScrollAmount(), //
mouseWheelEvent.getWheelRotation() //
);
}
}
/**
* dispatch the {#code mouseEvent}
* #param mouseEvent the event to be dispatched
*/
private void dispatchMouseEvent(MouseEvent mouseEvent) {
if (mouseEvent != null) {
Component target = mouseEvent.getComponent();
target.dispatchEvent(mouseEvent);
}
}
/**
* Get the listening component associated to the {#code component}'s {#code event}
*/
private Component getListeningComponent(MouseEvent event, Component component) {
switch (event.getID()) {
case (MouseEvent.MOUSE_CLICKED):
case (MouseEvent.MOUSE_ENTERED):
case (MouseEvent.MOUSE_EXITED):
case (MouseEvent.MOUSE_PRESSED):
case (MouseEvent.MOUSE_RELEASED):
return getMouseListeningComponent(component);
case (MouseEvent.MOUSE_DRAGGED):
case (MouseEvent.MOUSE_MOVED):
return getMouseMotionListeningComponent(component);
case (MouseEvent.MOUSE_WHEEL):
return getMouseWheelListeningComponent(component);
}
return null;
}
/**
* Cycles through the {#code component}'s parents to find the {#link Component} with associated {#link MouseListener}
*/
private Component getMouseListeningComponent(Component component) {
if (component.getMouseListeners().length > 0) {
return component;
} else {
Container parent = component.getParent();
if (parent != null) {
return getMouseListeningComponent(parent);
} else {
return null;
}
}
}
/**
* Cycles through the {#code component}'s parents to find the {#link Component} with associated {#link MouseMotionListener}
*/
private Component getMouseMotionListeningComponent(Component component) {
/*
* Mouse motion events may result in MOUSE_ENTERED and MOUSE_EXITED.
*
* Therefore, components with MouseListeners registered should be
* returned as well.
*/
if (component.getMouseMotionListeners().length > 0 || component.getMouseListeners().length > 0) {
return component;
} else {
Container parent = component.getParent();
if (parent != null) {
return getMouseMotionListeningComponent(parent);
} else {
return null;
}
}
}
/**
* Cycles through the {#code component}'s parents to find the {#link Component} with associated {#link MouseWheelListener}
*/
private Component getMouseWheelListeningComponent(Component component) {
if (component.getMouseWheelListeners().length > 0) {
return component;
} else {
Container parent = component.getParent();
if (parent != null) {
return getMouseWheelListeningComponent(parent);
} else {
return null;
}
}
}
/**
* Generate a {#code MOUSE_ENTERED} and {#code MOUSE_EXITED} event when the target component is changed
*/
private void generateEnterExitEvents( JLayer<? extends JComponent> layer,MouseEvent originalEvent, Component newTarget, Point realPoint) {
if (lastEnteredTarget != newTarget) {
dispatchMouseEvent(
transformMouseEvent(layer, originalEvent, lastEnteredTarget, realPoint, MouseEvent.MOUSE_EXITED));
lastEnteredTarget = newTarget;
//System.out.println("Last " + lastEnteredTarget.getClass().getName());
dispatchMouseEvent(
transformMouseEvent(layer, originalEvent, lastEnteredTarget, realPoint, MouseEvent.MOUSE_ENTERED));
}
}
}
ZoomPanel is a JPanel which include a JLayer with a TransformUI in it. The JLayer contains a JPanel with a SpringLayout in which the constraints are updated with the scale factor to layout correctly the components (JXLayer had it's own LayoutManager but in JLayer you can't set it)
package jzoom.transform;
import ...
public class ZoomPanel extends JPanel {
private static final long serialVersionUID = 1L;
private AffineTransform transform;
private TransformUI layerUI;
private JLayer<JComponent> layer;
private SpringLayout layout;
private JPanel springPanel;
private Container view = null;
public ZoomPanel() {
this(null);
}
public ZoomPanel(Container view) {
setLayout(new BorderLayout());
this.view = view;
transform = new AffineTransform();
layout = new SpringLayout();
springPanel = new JPanel(layout);
if (view != null) {
updateConstraints();
springPanel.add(view);
}
layerUI = new TransformUI();
layerUI.setTransform(transform);
layer = new JLayer<JComponent>(springPanel, layerUI);
super.add(layer);
}
private void updateConstraints() {
Spring width = layout.getConstraint(SpringLayout.WIDTH, springPanel);
Spring height = layout.getConstraint(SpringLayout.HEIGHT, springPanel);
SpringLayout.Constraints constraints = layout.getConstraints(view);
constraints.setX(Spring.constant(0));
constraints.setY(Spring.constant(0));
constraints.setWidth(Spring.scale(width, (float) (1 / transform.getScaleX())));
constraints.setHeight(Spring.scale(height, (float) (1 / transform.getScaleX())));
}
public void setView(Container view) {
if (this.view != null) {
throw new IllegalStateException(
this.getClass().getName() + " cannot be shared between multiple containers");
}
if (view != null) {
this.view = view;
updateConstraints();
springPanel.add(view);
} else {
throw new IllegalArgumentException("Can't set a null view");
}
}
public double getScale() {
return transform.getScaleX();
}
public void zoomIn() {
setScale(transform.getScaleX() + 0.1);
}
public void zoomOut() {
setScale(transform.getScaleX() - 0.1);
}
public void setScale(double scale) {
if (!(scale < 1)) {
transform.setToIdentity();
transform.scale(scale, scale);
updateConstraints();
springPanel.updateUI();
}
}
protected Component addToView(Component comp, Object constraints, int index) {
if (view != null) {
view.add(comp, constraints, index);
return comp;
}
if (comp instanceof Container) {
setView((Container) comp);
return view;
}
throw new IllegalStateException("You need to add or set a Container view before adding Components");
}
#Override
public Component add(Component comp) {
// TODO Auto-generated method stub
return addToView(comp, null, this.getComponentCount());
}
#Override
public Component add(Component comp, int index) {
// TODO Auto-generated method stub
return addToView(comp, null, index);
}
#Override
public void add(Component comp, Object constraints) {
// TODO Auto-generated method stub
addToView(comp, constraints, this.getComponentCount());
}
#Override
public void add(Component comp, Object constraints, int index) {
// TODO Auto-generated method stub
addToView(comp, constraints, index);
}
private void inspectView(Container view) {
PrintStream ps = null;
try {
ps = new PrintStream("C:\\Users\\andrea.maracci\\Documents\\sicraReflectionTemp.txt");
inspectView(view, 0, ps);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
}
}
}
private static void inspectView(Component component, Integer level, PrintStream ps) {
for (Integer i = 0; i < level; i++) {
ps.print("\t");
}
ps.print(level + ")");
ps.println("Inspecting " + component.getClass().getName());
int accessibleCount = 0;
if (component.getAccessibleContext() != null) {
accessibleCount = component.getAccessibleContext().getAccessibleChildrenCount();
if (accessibleCount > 0) {
ps.println("*********************************************ACCESSIBLE CONTEXT*********************************************");
for (int i = 0; i < accessibleCount; i++) {
ps.println(i + ") " + component.getAccessibleContext().getAccessibleChild(i).getClass().getName());
}
ps.println("************************************************************************************************************");
}
}
if (component instanceof JComponent) {
JComponent jComponent = ((JComponent)component);
if (jComponent.getComponentCount() > 0) {
Component[] children = jComponent.getComponents();
for (Component child : children) {
inspectView(child, ++level, ps);
}
}
}
--level;
}
}
And here is an horrible test program
package jzoom.test;
import ...
public class TestFinal {
public static void main(String[] args) {
new TestFinal();
}
public TestFinal() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedLookAndFeelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setMinimumSize(new Dimension(400,500));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLayer<JComponent> layer;
private TransformUI layerUI;
private JPanel content;
private AffineTransform transform = new AffineTransform();
private ZoomPanel zoomPanel;
public TestPane() {
content = new JPanel(new GridBagLayout());
// content = new JPanel(new XYLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
gbc.weighty = 0;
gbc.weightx = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
JLabel label = new JLabel("Hello");
JTextField field = new JTextField("World", 20);
content.add(label, gbc);
gbc.weightx = 1;
content.add(field, gbc);
// content.add(label, new XYConstraints(50, 20, 50, 22));
// content.add(field, new XYConstraints(100, 20, 200, 22));
gbc.gridy++;
gbc.gridwidth = 2;
final JSlider slider = new JSlider(100, 200);
slider.setValue(100);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int value = slider.getValue();
double scale = value / 100d;
zoomPanel.setScale(scale);
}
});
content.add(slider, gbc);
// content.add(slider, new XYConstraints(75, 50, 200, 50));
gbc.gridy++;
gbc.gridwidth = 2;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
JTextArea textArea = new JTextArea();
textArea.setEditable(true);
textArea.setText(
"pollofritto\npalma\npalmipedone\ncaccoletta\namammata\na\nasd\nasdgfag\nasdafa\nasdfasf\nadsfasdf\nadfadsf\nadsfdasf\nasdfdas\npollofritto\npalma\npalmipedone\ncaccoletta\namammata\na\nasd\nasdgfag\nasdafa\nasdfasf\nadsfasdf\nadfadsf\nadsfdasf\nasdfdas");
// textArea.setPreferredSize(new Dimensions());
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setPreferredSize(new Dimension(200, 75));
content.add(scrollPane, gbc);
gbc.gridy++;
gbc.gridwidth = 2;
gbc.weighty = 0;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" };
JComboBox petList = new JComboBox(petStrings);
content.add(petList, gbc);
JButton zoomIn = new JButton("Zoom In");
// zoomIn.addMouseListener(new ZoomMouseListener());
zoomIn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// System.out.println("FIRST");
// layerUI.zoomIn();
//double zoom = transform.getScaleX();
//transform.setToIdentity();
//transform.scale(zoom + 0.1, zoom + 0.1);
zoomPanel.zoomIn();
// jLayer.repaint();
}
});
JButton zoomOut = new JButton("Zoom Out");
zoomOut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
zoomPanel.zoomOut();
//double zoom = transform.getScaleX();
//transform.setToIdentity();
//transform.scale(zoom - 0.1, zoom - 0.1);
// jLayer.repaint();
}
});
gbc.gridy++;
gbc.gridx = 0;
gbc.gridwidth = 0;
gbc.anchor = GridBagConstraints.LINE_END;
// content.add(zoomOut, new XYConstraints(50, 120, 100, 25));
// content.add(zoomIn, new XYConstraints(170, 120, 100, 25));
JPanel button = new JPanel();
button.setLayout(new BoxLayout(button, BoxLayout.LINE_AXIS));
button.add(zoomOut);
button.add(zoomIn);
gbc.fill = GridBagConstraints.NONE;
content.add(button, gbc);
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
zoomPanel = new ZoomPanel();
zoomPanel.setView(content);
add(zoomPanel);
}
}
}

Java multithreading application game never stops/ends

i am still learning java and i tried to program my first multithreading java application (horse race game) with NetBeans. It is a little bit frustrating at the moment, because the game starts, but never comes to an end/result. I hope somebody can help me. Thanks in advance. Here is my code:
package wettrennen;
import java.util.Random;
import javax.swing.Icon;
import javax.swing.ImageIcon;
public class Pferd extends Thread{
public int entfernung;
public int nr;
public int y;
public int zug = 0;
public boolean aktiv;
//Constructor
public Pferd (int zug, int entfernung, boolean aktiv){
this.zug = zug;
this.entfernung = entfernung;
this.aktiv = true;
}
/**
* method, which makes each horse run
*/
public void rennen(){
int zufallszahl = (int)(Math.random()*4);
this.zug = +(zufallszahl*10);
this.entfernung+ = zug;
}
/**
* main method, which is automatically called for starting the threads
*/
public void run(){
while (aktiv = true && entfernung <= 270){
try {
rennen();
try {
sleep(1000);
} catch (Exception e) {
System.err.println("Sleeping is not working.");
}
} catch (Exception e) {
System.err.println("Racing is not working");
}
}
}
public int getZug(){
return zug;
}
public int getEntfernung(){
return entfernung;
}
public boolean getStatus(){
return aktiv;
}
public boolean setStatus(){
this.aktiv = false;
return aktiv;
}
}
Second class including the GUI and the Main method
package wettrennen;
import java.awt.*;
import java.util.ArrayList;
import java.util.Vector;
import javax.swing.JFrame;
public class Wettrennen extends JFrame {
Thread thread;
Vector<Pferd> pferde;
Vector<Thread> threads;
public int numPlayer;
public int entfernung;
public int laenge;
public boolean aktiv;
public final int y = 50;
public Graphics g;
/**
* Creates new form Game
*/
public Wettrennen() {
initComponents();
}
/**
* GUI regenerated by the Form Editor.
*/
...
/**
* action event method which is called after pressing the start button
* #param evt
*/
private void StartActionPerformed(java.awt.event.ActionEvent evt) {
numPlayer = getNumPlayer();
initializeHorses();
initializeThreads();
startThreads();
while (aktiv = true) {
zeichnen();
}
}
/**
* action event method, which ist called after pressing the clear button
* #param evt
*/
private void ClearActionPerformed(java.awt.event.ActionEvent evt) {
destroy();
stop();
}
/**
* the (one and only) main method
* #param args the command line arguments
*/
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Wettrennen().setVisible(true);
}
});
}
/**
* method that gets the Number of Players from the input field
* #return
*/
public int getNumPlayer() {
String iputNumPlayer = InputNumPlayer.getText();
int numPlayer = Integer.parseInt(iputNumPlayer);
return numPlayer;
}
/**
* method that initializes the number of horses referring to the number players
*/
public void initializeHorses(){
pferde = new Vector<Pferd>();
for (int i = 0; i < numPlayer; i++) {
pferde.add(new Pferd(0, 0,true));
}
}
/**
* method which sorts every horse to a thread
*/
public void initializeThreads(){
threads = new Vector<Thread>();
for (int i = 0; i < pferde.size(); i++) {
threads.add(new Thread(pferde.get(i)));
}
}
/**
* method starting all initialized threads
*/
public void startThreads(){
for (int k = 0; k < threads.size(); k++) {
System.out.println("Methode startThreads()");
threads.get(k).start();
}
}
/**
* the paint method which organises that the course of each horse is displayed on canvas
* after the parameter entfernung of one horse reaches 270 the game ist over,
* the aktiv parameter of each horse is set false and
* the main thread (game) is set null
*/
public void zeichnen (){
Graphics g = canvas.getGraphics();
thread = new Thread();
if (threads != null) {
for (int j = 0; j < threads.size(); j++) {
int zug = pferde.get(j).getZug();
int entfernung = pferde.get(j).getEntfernung();
g.setColor(Color.red);
g.drawLine(50, y + j*50, 50 + zug, y + j*50);
if (entfernung >= 270) {
g.drawString("Horse" + threads.get(j) + "won.", 10, 10);
System.out.println("Horse" + threads.get(j) + "won.");
pferde.get(j).setStatus();
thread = null;
stop();
} else {
repaint();
}
}
} else {
g.drawString("Thread is null", 10, 10);
}
}
/**
* method quitting the game
*/
public void destroy(){
thread = null;
}
/**
* method for stopping and quitting the game
*/
public void stop(){
thread = null;
threads = null;
}
}

Making a jar manifest. Where does it come from do I make my own?

I recently created a simple tic tac toe program and I've been trying to make it into a jar and I using cmd. I manage to get the jar,but when I click it nothing happens. After more research I figure it's a manifest problem as I didn't do anything to it or can even find one. Now this manifest thing has been really confusing me I have created my own manifest several times as just a txt, but that didn't work and I read that it would create it's own manifest, but I couldn't find that anywhere. Can Someone clarify were this manifest comes from or how to create one.
Java code:
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.*;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
import javax.swing.JButton;
import javax.swing.JLabel;
public class TicTacToe extends JFrame {
// GUI constants
private static final int WIDTH = 400; // display width
private static final int HEIGHT = 300; // display height
// TicTacToe layout constants
private static final int NUM_ROWS = 3; // number of tic-tac-toe rows
private static final int NUM_COLS = 3; // number of tic-tac-toe columns
private static final int TOTAL_CELLS = NUM_ROWS * NUM_COLS;
private static final int MAX_MOVES = NUM_ROWS * NUM_COLS; // max number of moves
// TicTacToe image, message and default cell value constants
private static final ImageIcon X_IMAGE = new ImageIcon("images.jpg", ""); // image for X
private static final ImageIcon O_IMAGE = new ImageIcon("O.jpg", ""); // image for O
private static final String GAME_ENDS_IN_TIE = "nobody wins cause blame john wells"; // tie message
private static final String NON_PLAYER_CELL_VALUE = "-"; // "-" is a non player cell
// Private TicTacToe members
private JButton[] cells;
private CellButtonHandler[] cellHandlers;
private String gameWinner;
private int xGoesFirst;
// private int[] press = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// private String xMess = "Cross for you laymens out there";
// private String oMess = "Naught for you laymens out there";
private String pOne;
private String pTwo;
// private int pOneWins;
// private int pTwoWins;
private int count = 0;
/**
* Default Constructor
*/
public TicTacToe() {
// JFrame frame = new JFrame();
// SWING OPERATIONS
Container pane = getContentPane();
pane.setLayout(new GridLayout(NUM_ROWS, NUM_COLS));
cells = new JButton[TOTAL_CELLS];
cellHandlers = new CellButtonHandler[TOTAL_CELLS];
for (int i = 0; i < TOTAL_CELLS; i++) {
cells[i] = new JButton(NON_PLAYER_CELL_VALUE);
cellHandlers[i] = new CellButtonHandler();
cells[i].addActionListener(cellHandlers[i]);
pane.add(cells[i]);
}
setTitle("Tic Tac Toe");
setSize(WIDTH, HEIGHT);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
// END SWING OPERATIONS
// CLASS OPERATIONS
this.setGameWinner(GAME_ENDS_IN_TIE);
this.xGoesFirst = 1;
this.pOne = "X";
this.pTwo = "O";
// this.pOneWins = 0;
// this.pTwoWins = 0;
// END CLASS OPERATIONS
} // public TicTacToe()
/**
* setGameWinner
*
* #param who - the game winner as a String.
*/
private void setGameWinner(String who) {
this.gameWinner = who;
} // private void setGameWinner(String who)
/**
* getGameWinner
*
* #return the game winner
*/
public String getGameWinner() {
return this.gameWinner;
} // public String getGameWinner()
/**
* Entry point of the program.
*
* #param args - <code>String[]</code> of runtime arguments
*/
public static void main(String[] args) {
// Swing is not thread safe, use SwingUtilities#invokeLater
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TicTacToe();
}
});
} // public static void main(String[] args)
/**
* CellButtonHandler
*/
private class CellButtonHandler implements ActionListener {
/**
* actionPerformed
*/
#Override
public void actionPerformed(ActionEvent e) {
JButton pressed = (JButton) e.getSource();
pressed.setText(TicTacToe.this.getCurrentPlayer());
// pressed.setIcon(TicTacToe.this.getCurrentPlayerIcon());
TicTacToe.this.count++;
if (TicTacToe.this.gameOverWin() || TicTacToe.this.gameOverTie()) {
System.out.println(TicTacToe.this.getGameWinner());
winScreen();
}
for (MouseListener ml : pressed.getMouseListeners()) {
pressed.removeMouseListener(ml);
}
} // public void actionPerformed(ActionEvent e)
} // private class CellButtonHandler implements ActionListener
/**
* private String getCurrentPlayer()
*/
private String getCurrentPlayer() {
this.xGoesFirst = this.xGoesFirst * -1;
if (this.xGoesFirst == -1) {
return pOne;
}
return pTwo;
} // private String getCurrentPlayer()
/**
* getCurrentPlayerIcon
*/
private ImageIcon getCurrentPlayerIcon() {
this.xGoesFirst = this.xGoesFirst * -1;
if (this.xGoesFirst == -1) {
return X_IMAGE;
}
return O_IMAGE;
} // private ImageIcon getCurrentPlayerIcon()
/**
* Checks if the game ended in a win.
*
* #return true if someone has won the game.
*/
private boolean gameOverWin() {
if (rowChecker() || colomnChecker() || diagChecker()) {
return true;
}
return false;
} // private boolean gameOverWin()
/**
* Checks if the game ended in a tie.
*
* #return true if there are no more moves to be made.
*/
private boolean gameOverTie() {
if (this.count >= MAX_MOVES) {
return true;
}
return false;
} // private boolean gameOverTie()
/**
* Checks the rows for a win.
*
* #return true if one of the rows contains three X's or three O's.
*/
public boolean rowChecker() {
int row = 0; // row variable
int col = 0; // column variable
String mark = ""; // string to hold the first
// button in a row's text value
while (row != NUM_ROWS) {
col = row * NUM_ROWS;
mark = this.getCellText(col);
if (mark.equals(NON_PLAYER_CELL_VALUE)) {
row = row + 1;
continue;
}
if (this.cellsAreEqual(mark, col + 1)
&& this.cellsAreEqual(mark, col + 2)) {
this.setGameWinner("Row Winner: " + mark);
return true;
}
row = row + 1;
}
// no win across the rows so we return false
return false;
} // public boolean rowChecker()
/**
* Checks the diagonals for a win.
*
* #return true if one of the diagonals contains three X's or three O's.
*/
public boolean diagChecker() {
int leftToRight = 0; // start at the top left box
int rightToLeft = 0; // start at the top right box
int step = 0; // the number of cells to step over
String mark = ""; // string to hold the buttons mark
// first we'll start by checking the top-left to
// bottom-right diagonal
leftToRight = 0;
step = NUM_COLS + 1;
mark = this.getCellText(leftToRight);
if (!mark.equals(NON_PLAYER_CELL_VALUE)) {
if (this.cellsAreEqual(mark, step)
&& this.cellsAreEqual(mark, (step * 2))) {
this.setGameWinner("Diagonal Winner: " + mark);
return true;
}
}
// next we'll check the top-right to bottom-left diagonal
rightToLeft = NUM_COLS - 1;
step = NUM_COLS - 1;
mark = this.getCellText(rightToLeft);
if (!mark.equals(NON_PLAYER_CELL_VALUE)) {
if (this.cellsAreEqual(mark, rightToLeft + step)
&& this.cellsAreEqual(mark, rightToLeft + (step * 2))) {
this.setGameWinner("Diagonal Winner: " + mark);
return true;
}
}
// no win on the diagonals so we return false
return false;
} // public boolean diagChecker()
/**
* colomnChecker
*/
public boolean colomnChecker() {
int row = 0; // row variable
int col = 0; // column variable
String mark = ""; // string to hold the buttons mark
while (col != NUM_COLS) {
row = col;
mark = getCellText(row);
if (mark.equals(NON_PLAYER_CELL_VALUE)) {
col = col + 1;
continue;
}
if (this.cellsAreEqual(mark, row + 3)
&& this.cellsAreEqual(mark, row + 6)) {
this.setGameWinner("Column Winner: " + mark);
return true;
}
col = col + 1;
}
// no win down the columns so we return false
return false;
} // public boolean colomnChecker()
/**
* getCellText
*/
private String getCellText(int which) {
return this.cells[which].getText();
} // private String getCellText(int which)
/**
* cellsAreEqual
*/
private boolean cellsAreEqual(String mark, int index) {
return mark.equals(this.getCellText(index));
} // private boolean cellsAreEqual(String mark, int index)
// private class restart implements ActionListener
public boolean winScreen(){
Container pane = getContentPane();
pane.removeAll();
pane.repaint();
JLabel label=new JLabel(TicTacToe.this.getGameWinner()+" won");
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalAlignment(JLabel.BOTTOM);
JLabel back=new JLabel(new ImageIcon("moniz_475.JPEG"));
pane.add(back);
pane.add(label);
return true;
}
} // public class TicTacToe extends JFrame
manifest file:
Main-Class: Tic_Tac_Toe.TicTacToe
If you want to add a manifest file to a jar you have to use jar -cfm command.
For example you have a manifest file name Manifest.txt which you want to add to your jar tictactoe.jar the the command will be.
jar -cfm tictactoe.jar Manifest.txt *
If you want to mention main class you have to add an entry in the manifest file as follows.
Main-Class: <fully qualified name for the class>
Also check the default program for jar files is properly configured.
Update
Your Java class is not in a package so your manifest file should be.
Main-Class: TicTacToe
If you want the class in a package then add the package package tictactoe; in the first line above the import statements. The in manifest file should be.
Main-Class: tictactoe.TicTacToe
For more info check Setting an Application's Entry Point
Are you using an IDE? If so most IDEs have the option of directly exporting your Java program as a jar with Manifest created. What you need to have in your META-INF/MANIFEST.MF file is the main method and any dependent libraries in the class path.
Add the following in your MANIFEST.MF file :
Main-Class: com.example.MainClass
Class-Path: lib/library.jar
Give the full path to the class with main method in Main-Class and any dependent libraries in Class-Path.
Now you can try creating the jar.

Sluggish Performance Using JTable Displaying Streaming Data

The code I'm referencing is proprietary and requires a multicast server, so I can't post an SSCCE snippet. I understand this may preclude any helpful insight which would elicit viable responses...
I'm compiling with Java 7 u 9.
I'm currently using a JTable in a GUI app that listens to multicast data, and displays it as it arrives. When scrolling the table or resizing columns, the app responds excruciatingly slow.
I thought I structured my code appropriately.
I used a wrapper class, and in it's main() function, it creates an instance of itself, which processes command line arguments, creates listeners, creates the JFrame and calls the class that returns a JTable. This is all done outside of the event dispatch thread.
Then, in the next line, I used the invokeLater() method to create a thread that handles all of the GUI rendering. It creates a JScrollPane, adds the JTable to the scroll pane, sets the scroll bars, sets the viewport, sets the scroll mode, and adds the JScrollPane to a JFrame . This is all handled within the event dispatch thread.
The rows typically populate fairly quick, with the occasional screen freeze (some of the rows contain 30 or more lines), but the responsiveness seems acceptable. However, when scrolling or resizing columns, the response is very slow.
All of the examples I've seen, including the SwingX SwingLabs demo all refer to an initial dataset that is loaded up front. I need an example of using a JTable with streaming data.
Can anyone point me to such an example/demo?
This is my main() snippet...
public static void main(String args[])
{
final JXTrapTableGUI ttg = new JXTrapTableGUI(args);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
ttg.createAndShowGUI();
}
});
}
PS. I want to thank each and everyone who responded. I've been pulled off this project until March 11th, but I will review all responses on that date.
I do not think that JTable works well with streaming data at all. All of the optimization techniques you mentioned, like keeping processing off of the Event Dispatch Thread, are irrlevant if the TableModel does not contain a real list but instead some connection to a data stream.
Without seeing how you attempted to handle this, it's hard to know exactly why it's slow. But here is how I would make it repsonsive:
Create a ListModel that stores a List - not a reference to a stream, just a plain List.
Have another Thread capture Multicast Data from the stream, lets call it DataStreamCollector.
Then launch a Thread that runs on a timer (javax.swing.Timer) that checks with the DataStreamCollector and updates the ListModel as needed.
My design here is assuming that UI responsiveness is more important than 100% synchronization with the data stream. Adjusting the timers should let you trade off having an up-to-date table with having a responsive UI.
Somewhere lost in Oracles clean_up to the trash (old Suns tutorials),
This project was called ChristmastTree, is about JTable & Performance,
Standard Java code before crazy & messy SwingWorker invoked from black hole called Executor
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;
import javax.swing.table.*;
/**
* CTTable extends JTable doing the following: <ul> <li>The UI is forced to be
* CTTableUI so that a customer CellRendererPane can be installed.
* <li>getCellRenderer is overriden to return the TableCellRenderer passed into
* the constructor. <li>tableChanged is overriden to pass the call to super only
* if the cell is visible. </ul>
*/
public class CTTable extends JTable {
private static final long serialVersionUID = 1L;
private CTTableCellRenderer renderer;
public CTTable(CTTableCellRenderer renderer) {
super();
this.renderer = renderer;
renderer.setFont(getFont());
}
#Override
public void updateUI() {
super.updateUI();
//Force the UI to be an instanceof CTTableUI. This approach will not work
//if you need to support more than one look and feel in your application.
setUI(new CTTableUI());
}
#Override
public void setFont(Font font) {
super.setFont(font);
if (renderer != null) {
renderer.setFont(font);
}
}
#Override
public TableCellRenderer getCellRenderer(int row, int column) {
return renderer;
}
#Override
public void tableChanged(TableModelEvent e) {
if (e instanceof VisibleTableModelEvent && !((VisibleTableModelEvent) e).isVisible(this)) {
return;// Do nothing if this cell isn't visible.
}
super.tableChanged(e);
}
private static class CTTableUI extends BasicTableUI {
#Override
public void installUI(JComponent c) {
super.installUI(c);// Overriden to install our own CellRendererPane
c.remove(rendererPane);
rendererPane = new CTCellRendererPane();
c.add(rendererPane);
}
}
/**
* CTCellRendererPane overrides paintComponent to NOT clone the Graphics
* passed in and NOT validate the Component passed in. This will NOT work if
* the painting code of the Component clobbers the graphics (scales it,
* installs a Paint on it...) and will NOT work if the Component needs to be
* validated before being painted.
*/
private static class CTCellRendererPane extends CellRendererPane {
private static final long serialVersionUID = 1L;
private Rectangle tmpRect = new Rectangle();
#Override
public void repaint() {
// We can safely ignore this because we shouldn't be visible
}
#Override
public void repaint(int x, int y, int width, int height) {
}
#Override
public void paintComponent(Graphics g, Component c, Container p, int x,
int y, int w, int h, boolean shouldValidate) {
if (c == null) {
if (p != null) {
Color oldColor = g.getColor();
g.setColor(p.getBackground());
g.fillRect(x, y, w, h);
g.setColor(oldColor);
}
return;
}
if (c.getParent() != this) {
this.add(c);
}
c.setBounds(x, y, w, h);
// As we are only interested in using a JLabel as the renderer,
//which does nothing in validate we can override this to do nothing,
//if you need to support components that can do layout, this will
//need to be commented out, or conditionally validate.
shouldValidate = false;
if (shouldValidate) {
c.validate();
}
boolean wasDoubleBuffered = false;
JComponent jc = (c instanceof JComponent) ? (JComponent) c : null;
if (jc != null && jc.isDoubleBuffered()) {
wasDoubleBuffered = true;
jc.setDoubleBuffered(false);
}//Don't create a new Graphics, reset the clip and translate the origin.
Rectangle clip = g.getClipBounds(tmpRect);
g.clipRect(x, y, w, h);
g.translate(x, y);
c.paint(g);
g.translate(-x, -y);
g.setClip(clip.x, clip.y, clip.width, clip.height);
if (wasDoubleBuffered) {
jc.setDoubleBuffered(true);
}
c.setBounds(-w, -h, 0, 0);
}
}
}
.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
/**
* A custom TableCellRenderer that overrides a handful of methods: <ul>
* <li>isOpaque and setBackground are overridden to avoid filling the
* background, if possible. <li>firePropertyChange is overridden to do nothing.
* If you need to support HTML text in the renderer than this should NOT be
* overridden. <li>paint is overridden to forward the call directly to the UI,
* avoiding the creation of a Graphics. This will NOT work if you need the
* renderer to contain other childre or the Graphics is clobbered as part of
* painting the UI. </ul>
*/
public class CTTableCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
private Color background;
private Color foreground;
private Color editableForeground;
private Color editableBackground;
private Border focusBorder;
public CTTableCellRenderer() {
focusBorder = UIManager.getBorder("Table.focusCellHighlightBorder");
editableForeground = UIManager.getColor("Table.focusCellForeground");
editableBackground = UIManager.getColor("Table.focusCellBackground");
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
boolean negative = (value != null && ((Integer) value).intValue() < 0);
// Reset the background based on the sign of the value.
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
if (!negative) {
setBackground(null);
} else {
setBackground(Color.red);
}
}//NOTICE that we do NOT set the font here, because CTTable knows about
//us, it will set the font as appropriate.
if (hasFocus) {
setBorder(focusBorder);
if (table.isCellEditable(row, column)) {
setForeground(editableForeground);
setBackground(editableBackground);
}
} else {
setBorder(noFocusBorder);
}
setValue(value);
return this;
}
#Override
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
// As long as you don't have any HTML text, this override is ok.
}
#Override// This override is only appropriate if this will never contain
// any children AND the Graphics is not clobbered during painting.
public void paint(Graphics g) {
ui.update(g, this);
}
#Override
public void setBackground(Color c) {
this.background = c;
}
#Override
public Color getBackground() {
return background;
}
#Override
public void setForeground(Color c) {
this.foreground = c;
}
#Override
public Color getForeground() {
return foreground;
}
#Override
public boolean isOpaque() {
return (background != null);
}
#Override // This is generally ok for non-Composite components (like Labels)
public void invalidate() {
}
#Override // Can be ignored, we don't exist in the containment hierarchy.
public void repaint() {
}
}
.
import javax.swing.table.*;
import java.util.*;
/**
* CTTableModel, a TableModel, models a set of Datas as the rows. The data is
* stored in a List of Lists. As the changes come in against a particular Data
* object we also contain a map from Data to row. This can obviously be made
* faster by pushing the row to the Data, but this may not be feasable in
* applications of this sort.
*/
public class CTTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
/**
* Maps from Data to an integer id giving the row of the data.
*/
private Map rowMap;
/**
* Number of columns to display.
*/
private int columns;
/**
* A List of Lists.
*/
private java.util.List rowData;
/**
* If true, batch cell updates using sharedModelEvent.
*/
private boolean batchChange;
/**
* Shared model event.
*/
private VisibleTableModelEvent sharedModelEvent;
public CTTableModel(int columns) {
this.columns = columns;
// Notice how they are not synchronized, we do NOT access this class
// from another thread, and therefore do not have to worry about
// synchronization.
rowData = new ArrayList();
rowMap = new HashMap();
}
public void addRow(Data rowID) {
int row = rowData.size();
rowMap.put(rowID, new Integer(row));
ArrayList colData = new ArrayList();
for (int counter = 0; counter < columns; counter++) {
colData.add(null);
}
rowData.add(colData);
fireTableRowsInserted(row, row);
}
/**
* Toggles batch updates. When true and model changes are notified using a
* VisibleTableModelEvent.
*
* #param batch
*/
public void setBatchUpdates(boolean batch) {
this.batchChange = batch;
if (sharedModelEvent == null) {
sharedModelEvent = new VisibleTableModelEvent(this);
}
sharedModelEvent.reset();
}
public boolean getBatchUpdates() {
return batchChange;
}
/**
* Sets the display value for a particular Data item at a particular cell.
* If notify is true listeners are notified, otherwise no listeners are
* notified.
*
* #param rowID
* #param col
* #param data
* #param notify
*/
public void set(Data rowID, int col, Object data, boolean notify) {
int row = ((Integer) rowMap.get(rowID)).intValue();
((java.util.List) rowData.get(row)).set(col, data);
if (notify) {
if (batchChange) {
sharedModelEvent.set(row, col);
fireTableChanged(sharedModelEvent);
} else {
fireTableCellUpdated(row, col);
}
}
}
#Override
public int getRowCount() {
return rowData.size();
}
#Override
public int getColumnCount() {
return columns;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return ((java.util.List) rowData.get(rowIndex)).get(columnIndex);
}
}
.
/**
* Unique ID for the data.
*/
public class Data {
/**
* This is overriden to remind developers they should have an intelligent
* equals and hashCode. You do not need to override either of them, but if
* you override one you need to override the other. Additionaly, because
* they are used extensively to map the data that has changed to the table,
* equals and hashCode MUST be fast, cache data if you need to!
*
* #param x
*/
#Override
public boolean equals(Object x) {
return (this == x);
}
/**
* This is overriden to remind developers they should have an intelligent
* equals and hashCode. You do not need to override either of them, but if
* you override one you need to override the other. Additionaly, because
* they are used extensively to map the data that has changed to the table,
* equals and hashCode MUST be fast, cache data if you need to!
*/
#Override
public int hashCode() {
return super.hashCode();
}
}
.
import java.util.ArrayList;
/**
* DataChange is used to associate a Data Object with a column identifier that
* has changed. To avoid loads of garbage per update DataChanges are cached and
* reused.
*/
public class DataChange {
private static ArrayList sharedDataChanges = new ArrayList();
private Data data;
private int col;
private int hashCode;
/**
* Obtains a DataChange for the specified Data and column.
*
* #param data
* #param col
* #return
*/
public static DataChange getDataChange(Data data, int col) {
synchronized (sharedDataChanges) {
int size = sharedDataChanges.size();
if (size > 0) {
DataChange change = (DataChange) sharedDataChanges.remove(size - 1);
change.data = data;
change.col = col;
return change;
}
}
return new DataChange(data, col);
}
/**
* Indicates the DataChange is no longer needed and can be reused.
*
* #param change
*/
public static void releaseDataChange(DataChange change) {
synchronized (sharedDataChanges) {
sharedDataChanges.add(change);
}
}
DataChange(Data data, int col) {
this.data = data;
this.col = col;
hashCode = (data.hashCode() | col);
}
public Data getData() {
return data;
}
public int getColumn() {
return col;
}
#Override
public int hashCode() {
return hashCode;
}
public boolean equals(DataChange dc) {
if (dc == this) {
return true;
}
DataChange o = (DataChange) dc;
return (o.data == data && o.col == col);
}
}
.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
/**
* The Main controller, responsible for wiring everything together. Pressing
* return in any of the fields will trigger recreation of everything.
*/
public class Main implements ActionListener {
// properties: columnCount, rowCount, updateSleepTime, eqSleepTime,
// threshold, generateSleep, generatorBatchCount
private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
private JTextField columnCount;
private JTextField rowCount;
private JTextField updateSleepTime;
private JTextField eqSleepTime;
private JTextField threshold;
private JTextField generateSleep;
private JTextField generatorBatchCount;
private JFrame frame;
static JLabel totalUpdateTime;
static JLabel notifyTime;
static JLabel paintTime;
static JLabel updateCount;
private JTable table;
private UpdateThread updateThread;
private GeneratorThread generatorThread;
private CTTableModel tableModel;
private static int NUM_COLUMNS = 40;// Initial values for the 7 properties.
private static int NUM_ROWS = 3000;
private static int UPDATE_SLEEP_TIME = 500;
private static int EQ_SLEEP_TIME = 10;
private static int UPDATE_ALL_THRESHOLD = 400000;
private static int GENERATOR_SLEEP_TIME = 40;
private static int BATCH_SIZE = 1000;
Main() {
frame = new JFrame();
frame.getContentPane().setLayout(new GridBagLayout());
columnCount = add("Columns: ", NUM_COLUMNS);
rowCount = add("Rows: ", NUM_ROWS);
updateSleepTime = add("Update Sleep: ", UPDATE_SLEEP_TIME);
eqSleepTime = add("EQ Sleep: ", EQ_SLEEP_TIME);
threshold = add("Update All Threshold: ", UPDATE_ALL_THRESHOLD);
generateSleep = add("Generator Sleep: ", GENERATOR_SLEEP_TIME);
generatorBatchCount = add("Batch Size: ", BATCH_SIZE);
table = new CTTable(new CTTableCellRenderer());
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
JScrollPane sp = new JScrollPane(table);
frame.getContentPane().add(sp, new GridBagConstraints(0, 3, 6, 1, 1, 1,
GridBagConstraints.WEST, GridBagConstraints.BOTH, EMPTY_INSETS, 0, 0));
ChangeListener changeListener = new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
BoundedRangeModel m = (BoundedRangeModel) (e.getSource());
if (updateThread != null) {
updateThread.setUpdatesEnabled(!(m.getValueIsAdjusting()));
}
}
};
sp.getVerticalScrollBar().getModel().addChangeListener(changeListener);
sp.getHorizontalScrollBar().getModel().addChangeListener(changeListener);
totalUpdateTime = new JLabel(" ");
notifyTime = new JLabel(" ");
paintTime = new JLabel(" ");
updateCount = new JLabel(" ");
JPanel statusPanel = new JPanel(new GridBagLayout());
frame.getContentPane().add(statusPanel, new GridBagConstraints(0, 4, 6, 1, 1, 0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0));
statusPanel.add(totalUpdateTime, new GridBagConstraints(0, 0, 1, 1, 1, 0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0));
statusPanel.add(notifyTime, new GridBagConstraints(1, 0, 1, 1, 1, 0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0));
statusPanel.add(paintTime, new GridBagConstraints(2, 0, 1, 1, 1, 0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0));
statusPanel.add(updateCount, new GridBagConstraints(3, 0, 1, 1, 1, 0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0));
frame.setTitle("Christmas Tree Demo Application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(0, 0, 1000, 800);
frame.setVisible(true);
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
reset();
}
#Override
public void actionPerformed(ActionEvent ae) {
reset();
}
private JTextField add(String name, int defaultValue) {
Container parent = frame.getContentPane();
int row = parent.getComponentCount() / 6;
int col = parent.getComponentCount() % 6;
parent.add(new JLabel(name), new GridBagConstraints(col, row, 1, 1, 0, 0,
GridBagConstraints.WEST, 0, EMPTY_INSETS, 0, 0));
JTextField tf = new JTextField(Integer.toString(defaultValue));
tf.addActionListener(this);
parent.add(tf, new GridBagConstraints(col + 1, row, 1, 1, 1, 0,
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0));
return tf;
}
private void reset() {
System.out.println("Columns: " + getInt(columnCount));
System.out.println("Rows: " + getInt(rowCount));
System.out.println("Update Sleep: " + getInt(updateSleepTime));
System.out.println("EQ Sleep: " + getInt(eqSleepTime));
System.out.println("Update All Threshold: " + getInt(threshold));
System.out.println("Generator Sleep: " + getInt(generateSleep));
System.out.println("Batch Size: " + getInt(generatorBatchCount));
if (updateThread != null) {
System.out.println("interrupting!");
updateThread.interrupt();
generatorThread.interrupt();
}
int cols = getInt(columnCount);
tableModel = new CTTableModel(cols);
ArrayList<Data> data = new ArrayList<Data>();
for (int counter = getInt(rowCount) - 1; counter >= 0; counter--) {
Data dataID = new Data();
data.add(dataID);
tableModel.addRow(dataID);
for (int colCounter = 0; colCounter < cols; colCounter++) {
if (colCounter % 2 == 0) {
tableModel.set(dataID, colCounter,
new Integer(counter * 100 + colCounter), false);
} else {
tableModel.set(dataID, colCounter,
new Integer(counter * -100 + colCounter), false);
}
}
}
table.setModel(tableModel);
generatorThread = new GeneratorThread(data, getInt(generateSleep),
getInt(generatorBatchCount), getInt(columnCount));
updateThread = new UpdateThread(generatorThread, tableModel,
getInt(updateSleepTime), getInt(eqSleepTime), getInt(threshold));
generatorThread.start();
updateThread.start();
}
private int getInt(JTextField tf) {
try {
return Integer.parseInt(tf.getText());
} catch (NumberFormatException nfe) {
System.out.println("exception getting int: " + nfe);
}
return 0;
}
public static void main(String[] args) {
Main main = new Main();
}
}
... will be continue
rest of methods
import java.awt.*;
import java.lang.reflect.*;
import java.util.*;
import javax.swing.*;
/**
* Thread responsible for publishing changes to the Model. Sleeps for a defined
* amount of time, waits for no activity in the UI and then users invokeAndWait
* to publish changes.
*/
public class UpdateThread extends Thread {
private int sleepTime;
private int eqSleepTime;
private int threshhold;
private boolean updatesEnabled;
private Runnable publishRunnable;
private Runnable emptyRunnable;
private GeneratorThread generator;
private CTTableModel model;
private Map<?, ?> lastData;
private long notifyTime;
private long paintTime;
private int updateCount;
private boolean done;
public UpdateThread(GeneratorThread generator, CTTableModel model,
int sleepTime, int eqSleepTime, int threshhold) {
super();
setPriority(Thread.MIN_PRIORITY);
this.sleepTime = sleepTime;
this.eqSleepTime = eqSleepTime;
updatesEnabled = true;
this.threshhold = threshhold;
this.generator = generator;
this.model = model;
publishRunnable = new Runnable() {
// Runnable used to publish changes to the event dispatching thread
#Override
public void run() {
publishChangesOnEventDispatchingThread();
}
};
// Empty runnable, used to wait until the event dispatching thread
// has finished processing any pending events.
emptyRunnable = new Runnable() {
#Override
public void run() {
}
};
}
#Override
public void interrupt() {
done = true;
super.interrupt();
}
#Override
public void run() {
while (!isInterrupted() && !done) {
try {
sleep(sleepTime);
publishChanges();
} catch (InterruptedException ie) {
}
}
System.out.println("UpdateThread done");
}
/**
* Publishes changes on the event dispatching thread when the system isn't
* busy. This blocks the caller until the changes have been published.
*/
private void publishChanges() {
synchronized (this) {// Wait until the user isn't scrolling
while (!updatesEnabled) {
try {
wait();
} catch (InterruptedException ie) {
}
}
}
EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
// And wait until there are no pending events.
while (queue.peekEvent() != null) {
try {
sleep(eqSleepTime);
} catch (InterruptedException ie) {
}
}
final long start = System.currentTimeMillis();
try {
SwingUtilities.invokeAndWait(publishRunnable);
// publish the changes on the event dispatching thread
} catch (InterruptedException ie) {
} catch (InvocationTargetException ite) {
}
try {
// Wait until the system has completed processing of any events we
// triggered as part of publishing changes.
SwingUtilities.invokeAndWait(emptyRunnable);
} catch (InterruptedException ie) {
} catch (InvocationTargetException ite) {
}
final long end = System.currentTimeMillis();
try {// Update the display
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
Main.totalUpdateTime.setText("Total: "
+ Integer.toString((int) (end - start)));
Main.notifyTime.setText("Notify: "
+ Integer.toString((int) notifyTime));
Main.paintTime.setText("Paint: "
+ Integer.toString((int) paintTime));
Main.updateCount.setText("Updated: "
+ Integer.toString((int) updateCount));
}
});
} catch (InterruptedException ie) {
} catch (InvocationTargetException ite) {
}
}
/**
* Does the actual publishing of changes.
*/
private void publishChangesOnEventDispatchingThread() {
long start = System.currentTimeMillis();
model.setBatchUpdates(true);
Map<?, ?> data = generator.getData();
boolean notify = !(data.size() > threshhold ||
(lastData != null && lastData.size() + data.size() > threshhold));
updateCount = data.size();
if (lastData != null) {
updateCount += lastData.size();
}//Reset the data for the last set of changes we did, this forces the cells to change color.
if (lastData != null) {
publishData(lastData, true, notify);
Iterator<?> dataIterator = lastData.keySet().iterator();
while (dataIterator.hasNext()) {
DataChange.releaseDataChange((DataChange) dataIterator.next());
}
lastData.clear();
}
publishData(data, false, notify);// Publish the current set of data.
model.setBatchUpdates(false);
if (!notify) {
model.fireTableDataChanged();
}
lastData = data;
long end = System.currentTimeMillis();
notifyTime = (end - start);
start = System.currentTimeMillis();
RepaintManager.currentManager(null).paintDirtyRegions();
end = System.currentTimeMillis();
paintTime = (end - start);
}
/**
* Publish the passed in set of data.
*/
private void publishData(Map<?, ?> data, boolean negate, boolean notify) {
Iterator<?> dataIterator = data.keySet().iterator();
while (dataIterator.hasNext()) {
DataChange change = (DataChange) dataIterator.next();
Object value = data.get(change);
if (negate) {
value = new Integer(((Integer) value).intValue() * -1);
}
model.set(change.getData(), change.getColumn(), value, notify);
}
}
/**
* If enable is true, we are allowed to publish changes, otherwise we
* aren't.
*
* #param enable
*/
public void setUpdatesEnabled(boolean enable) {
synchronized (this) {
updatesEnabled = enable;
if (updatesEnabled) {
notify();
}
}
}
public boolean getUpdatesEnabled() {
return updatesEnabled;
}
}
.
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
/**
* VisibleTableModelEvent adds the method isVisible to test if the cell
* identified by the event is visible.
*/
public class VisibleTableModelEvent extends TableModelEvent {
private static final long serialVersionUID = 1L;
private Point tmpPoint;
// This implementation caches the information for one JTable, it is
// certainly possible to cache it for more than one should
// you have this need.
private boolean valid;
private int firstVisRow;
private int lastVisRow;
private int firstVisCol;
private int lastVisCol;
public VisibleTableModelEvent(TableModel source) {
super(source, 0, 0, 0, UPDATE);
tmpPoint = new Point();
}
/**
* Resets the underlying fields of the TableModelEvent. This assumes no ONE
* is going to cache the TableModelEvent.
*
* #param row
* #param col
*/
public void set(int row, int col) {
firstRow = row;
lastRow = row;
column = col;
}
/**
* Invoked to indicate the visible rows/columns need to be recalculated
* again.
*/
public void reset() {
valid = false;
}
public boolean isVisible(JTable table) {
if (!valid) {// Determine the visible region of the table.
Rectangle visRect = table.getVisibleRect();
tmpPoint.x = visRect.x;
tmpPoint.y = visRect.y;
firstVisCol = table.columnAtPoint(tmpPoint);
firstVisRow = table.rowAtPoint(tmpPoint);
tmpPoint.x += visRect.width;
tmpPoint.y += visRect.height;
lastVisCol = table.columnAtPoint(tmpPoint);
if (lastVisCol == -1) {
lastVisCol = table.getColumnCount() - 1;
}
if ((lastVisRow = table.rowAtPoint(tmpPoint)) == -1) {
lastVisRow = table.getRowCount();
}
valid = true;
}
return (firstRow >= firstVisRow && firstRow <= lastVisRow && column
>= firstVisCol && column <= lastVisCol);
}
}

Categories