I've been wanting to design a generator for dragon curves.
(If you want info on that check this out, but it doesn't really matter for the issue)
A dragon curve is a repeating mathematical construct.
I've already written a generator for what the canvas should draw, it works by returning a char array consisting of 'r' or 'l', saying whether the line has to turn left or right next. In the code here, it's the method input(). This part works perfectly.
The problem is that whenever I want to draw it on the canvas (using drawLine), it only draws the first two lines as actual lines, the rest are just dots.
The dots are on the right positions and if you make the thing really big, you can't tell the difference anymore, but nevertheless, there are supposed to be lines there.
Image:
This is the code I used:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/**
*
* Description
*
* #version 1.0 from 4/20/2016
* #author
*/
public class CurveGen extends JFrame {
// start attributes
private Canvas display = new Canvas();
private JButton startButton = new JButton();
private JLabel jLabel1 = new JLabel();
private JTextArea outText = new JTextArea("");
private JScrollPane outTextScrollPane = new JScrollPane(outText);
private JLabel jLabel2 = new JLabel();
private JSlider xSlider = new JSlider();
private JSlider ySlider = new JSlider();
private JNumberField iterationsNF = new JNumberField();
private JNumberField sizeNF = new JNumberField();
// end attributes
public CurveGen(String title) {
// Frame-Init
super(title);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
int frameWidth = 1022;
int frameHeight = 731;
setSize(frameWidth, frameHeight);
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
int x = (d.width - getSize().width) / 2;
int y = (d.height - getSize().height) / 2;
setLocation(x, y);
setResizable(false);
Container cp = getContentPane();
cp.setLayout(null);
// start components
display.setBounds(16, 64, 601, 601);
cp.add(display);
startButton.setBounds(736, 464, 241, 129);
startButton.setText("START!");
startButton.setMargin(new Insets(2, 2, 2, 2));
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
startButton_ActionPerformed(evt);
}
});
startButton.setFont(new Font("Dialog", Font.BOLD, 36));
cp.add(startButton);
jLabel1.setBounds(760, 96, 75, 41);
jLabel1.setText("Iterations:");
cp.add(jLabel1);
outTextScrollPane.setBounds(728, 392, 257, 57);
cp.add(outTextScrollPane);
jLabel2.setBounds(768, 144, 67, 41);
jLabel2.setText("Size:");
cp.add(jLabel2);
xSlider.setBounds(0, 8, 633, 49);
xSlider.setMinorTickSpacing(25);
xSlider.setMajorTickSpacing(100);
xSlider.setPaintTicks(true);
xSlider.setPaintLabels(true);
xSlider.setToolTipText("Starting point y-coordinate");
xSlider.setMaximum(600);
xSlider.setValue(300);
cp.add(xSlider);
ySlider.setBounds(624, 56, 65, 625);
ySlider.setMinorTickSpacing(25);
ySlider.setMajorTickSpacing(100);
ySlider.setPaintTicks(true);
ySlider.setPaintLabels(true);
ySlider.setOrientation(SwingConstants.VERTICAL);
ySlider.setMaximum(600);
ySlider.setInverted(true);
ySlider.setValue(300);
ySlider.setToolTipText("Starting point x-coordinate");
cp.add(ySlider);
iterationsNF.setBounds(856, 96, 81, 41);
iterationsNF.setText("");
cp.add(iterationsNF);
sizeNF.setBounds(856, 144, 81, 41);
sizeNF.setText("");
cp.add(sizeNF);
// end components
setVisible(true);
} // end of public CurveGen
// start methods
public static void main(String[] args) {
new CurveGen("CurveGen");
} // end of main
public char[] input(int iter) {
char oldOut[] = new char[0];
for (int i=1;i<=iter;i++) {
char newOut[] = new char[((int)Math.pow(2, i))-1];
for (int n=0;n<oldOut.length;n++) {
newOut[n] = oldOut[n];
if (oldOut[n]=='r') {
newOut[newOut.length-n-1] = 'l';
}
if (oldOut[n]=='l') {
newOut[newOut.length-n-1] = 'r';
} // end of if
} // end of for
newOut[oldOut.length]='l';
oldOut = newOut;
} // end of for
return oldOut;
}
public void startButton_ActionPerformed(ActionEvent evt) {
int iterations = iterationsNF.getInt();
int size = sizeNF.getInt();
char com[] = input(iterations);
outText.setText(String.valueOf(com));
int dir = 0;
int newDir = 0;
int lastPos[] = {xSlider.getValue(),ySlider.getValue()-size};
int newPos[] = {0,0};
Graphics g = display.getGraphics();
g.clearRect(0,0,601,601);
g.drawLine(xSlider.getValue(),ySlider.getValue(),xSlider.getValue(),ySlider.getValue()-size);
for (int i=0;i<=com.length-1;i++) {
dir = newDir;
if (dir==0) {
if (com[i]=='l') {
newPos[0] = lastPos[0]-size;
newPos[1] = lastPos[1];
newDir = 3;
}
if (com[i]=='r') {
newPos[0] = lastPos[0]+size;
newPos[1] = lastPos[1];
newDir = 1;
}
}
if (dir==1) {
if (com[i]=='l') {
newPos[0] = lastPos[0];
newPos[1] = lastPos[1]-size;
newDir = 0;
}
if (com[i]=='r') {
newPos[0] = lastPos[0];
newPos[1] = lastPos[1]+size;
newDir = 2;
}
}
if (dir==2) {
if (com[i]=='l') {
newPos[0] = lastPos[0]+size;
newPos[1] = lastPos[1];
newDir = 1;
}
if (com[i]=='r') {
newPos[0] = lastPos[0]-size;
newPos[1] = lastPos[1];
newDir = 3;
}
}
if (dir==3) {
if (com[i]=='l') {
newPos[0] = lastPos[0];
newPos[1] = lastPos[1]+size;
newDir = 2;
}
if (com[i]=='r') {
newPos[0] = lastPos[0];
newPos[1] = lastPos[1]-size;
newDir = 0;
}
}
g.drawLine(lastPos[0],lastPos[1],newPos[0],newPos[1]);
lastPos=newPos;
} // end of for
} // end of startButton_ActionPerformed
// end methods
} // end of class CurveGen
Okay, so I've gone back over the code...
Mixing heavyweight (java.awt.Canvas) and lightweight (Swing) components is unadvisable as they can cause or sorts of painting issues
getGraphics is not how paint should be done. Instead, I'd start with a custom JPanel and override its paintComponent. See Painting in AWT and Swing and Performing Custom Painting for more details
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
I believe the problem is associated with this...
lastPos=newPos;
All you are doing is making lastPos point to the same place in memory as newPos, so when you assign values to newPos, lastPos will have the same values, hence the reason you're seeing dots.
What I would do first, is separate the responsible for the generation of the data from the display.
I'd start with some kind of model (note, you could create a model which took iterations instead and which generated the data itself, but I was focusing on solving the initial problem)
public class DragonModel {
private Point startPoint;
private int size;
private char[] values;
public DragonModel(Point startPoint, int size, char[] values) {
this.startPoint = startPoint;
this.size = size;
this.values = values;
}
public Point getStartPoint() {
return startPoint;
}
public int getSize() {
return size;
}
public char[] getValues() {
return values;
}
}
and then the display...
public class DragonPane extends JPanel {
private DragonModel model;
public void setModel(DragonModel model) {
this.model = model;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (model != null) {
Graphics2D g2d = (Graphics2D) g.create();
int size = model.getSize();
int dir = 0;
int newDir = 0;
Point lastPos = model.getStartPoint();
Point newPos = new Point(0, 0);
for (char value : model.values) {
if (dir == 0) {
if (value == 'l') {
newPos.x = lastPos.x - size;
newPos.y = lastPos.y;
newDir = 3;
}
if (value == 'r') {
newPos.x = lastPos.x + size;
newPos.y = lastPos.y;
newDir = 1;
}
}
if (dir == 1) {
if (value == 'l') {
newPos.x = lastPos.x;
newPos.y = lastPos.y - size;
newDir = 0;
}
if (value == 'r') {
newPos.x = lastPos.x;
newPos.y = lastPos.y + size;
newDir = 2;
}
}
if (dir == 2) {
if (value == 'l') {
newPos.x = lastPos.x + size;
newPos.y = lastPos.y;
newDir = 1;
}
if (value == 'r') {
newPos.x = lastPos.x - size;
newPos.y = lastPos.y;
newDir = 3;
}
}
if (dir == 3) {
if (value == 'l') {
newPos.x = lastPos.x;
newPos.y = lastPos.y + size;
newDir = 2;
}
if (value == 'r') {
newPos.x = lastPos.x;
newPos.y = lastPos.y - size;
newDir = 0;
}
}
g.drawLine(lastPos.x, lastPos.y, newPos.x, newPos.y);
dir = newDir;
lastPos = new Point(newPos);
}
}
}
}
The idea here is to try and decouple of the responsibility a little, the responsibility for the generation and displaying of the data sit firmly in two different areas.
Then in your actionPerformed method you could simply do...
public void startButton_ActionPerformed(ActionEvent evt) {
int iterations = Integer.parseInt(iterationsNF.getText());
int size = Integer.parseInt(sizeNF.getText());
char com[] = input(iterations);
outText.setText(String.valueOf(com));
DragonModel model = new DragonModel(new Point(xSlider.getValue(), ySlider.getValue()), size, com);
display.setModel(model);
} // end of startButton_ActionPerformed
which could result in something like...
The drawing code should be inside the paint(Graphics) method to synchronize with the rendering loop properly. In an event handler, update the data model of the component (calculate the lines and keep them in a data structure inside the component), then call the method repaint() to trigger the event rendering loop, which will call your paint method.
There are some other variations of this, but the general idea is that you change the data and then request rendering. The rendering engine may call your paint method in other cases as well, not only when you change the data, so ideally paint() has all the data it needs to render fast, meaning it should not do calculations or heavy operations other than rendering on the Graphics object.
This means that you have to subclass JComponent in a new class, and implement paint inside it. This class should have an internal data structure with the lines ready to render at any point in time. Then use your new class in the JFrame.
Related
I tried .equals() and ==, but nothing help. All labels I storage in ArrayList of my own class, which have JLabel.
How can I get the index of the label in ArrayList or something else?
Can my problem in using ArrayList?
MouseListener
private static MouseListener clicklvl1=new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
for (int i=0;i<shahtars.size();i+=1){
if (e.getSource()==shahtars.get(i).uiShahtar){
IDofClickedObject=i;
}
}
if (IDofClickedObject!=-1){
if (counter == 0) {
shahtars.get(IDofClickedObject).uiShahtar.setIcon(new ImageIcon("E:\\aaa\\ShahtarLvL1(Clicked).png"));
counter = 1;
} else if (counter == 1) {
// uiShahtar.setIcon(new ImageIcon("E:\\aaa\\ShahtarLvL1.png"));
shahtars.get(IDofClickedObject).uiShahtar.setIcon(new ImageIcon("E:\\aaa\\ShahtarLvL1.png"));
counter = 0;
}
}
System.out.print("x "+shahtars.get(IDofClickedObject).uiShahtar.getX()+" y "+shahtars.get(IDofClickedObject).uiShahtar.getY());
}
My class
class FillShahtar implements Cloneable {
static JLabel uiShahtar;
static int energy;
static double power;
static double speed;
String name;
And the last
FillShahtar(int chose) {
switch (chose){
case 1:{
plankaDown = 1;
plankaUp = 11;
int xRand = (int) ( 0+Math.random()*1000);
int yRand =(int) (0+Math.random()*600);
int counter=0;
/////////////////////////debug/////////////////
System.out.print(xRand+" "+yRand+"\n");
//////////////////////////////////////////////
energy = (int) (plankaDown + Math.random() * plankaUp );
power = (int) (plankaDown + Math.random() * plankaUp );
speed = (int) (plankaDown + Math.random() * plankaUp );
uiShahtar = new JLabel();
uiShahtar.setIcon(new ImageIcon("E:\\aaa\\ShahtarLvL1.png"));
uiShahtar.setLayout(new FlowLayout());
uiShahtar.setSize(50,50);
uiShahtar.setLocation(xRand,yRand);
uiShahtar.setVisible(true);
uiShahtar.addMouseListener(clicklvl1);
// mainPanel.add(shahtars.get(counter).uiShahtar);
counter+=1;
break;
}
Clicked image must change the image, but change only last Label.
Since JLabel inherits from swing's Component class, you can add a MouseListener (or MouseAdapter) to each of your clickable labels. Using this EventListener you can find the clicked label like this:
JLabel l = new JLabel();
l.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
JLabel clickedLabel = (JLabel) e.getComponent();
}
});
In order to get the index of the clicked label within the ArrayList, use the indexOf(Object) method provided by the ArrayList:
int index = list.indexOf(clickedLabel);
I want to print 2 titles with different font, and then print a table's header and all rows with another font. I have the table content in a JTable. When the user clicks a button, I want to send all the things to the PrinterServices and print them with LANDSCAPE orientation. There may be many rows so I need to do the pagination. The pagination is in the left bottom.
I see there's no possibility to use JTable.print because I need the title in font1, the subtitle in font2, and the table header/table data in font3.
How can I do that?
I have seen this page, shows me how to print with different style in several pages:
http://www.java2s.com/Code/Java/2D-Graphics-GUI/PrintinJavapageformatanddocument.htm
But I think this link place the different styles in different pages, and in my situation, the title1 and title2 are in the first page, and the rest of the page is filled with row data, and then page 2, page 3... So it contains several styles in the same page and is not exactly what the link does.
EDIT:
Now I have these code, but they are not working in two aspects:
I must set the orientation in the print dialog prompt (choose the "Horizontal" orientation). I want it by default, but I don't want the Java native page setup dialog. I want the system dependant one.
The left margin/indent increases as the page number increases. I want it static. I must have messed up the coordinates.
The pagination foot is not there. How can I get it?
So I have:
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
public class PrintingTest1 {
/**
* #param args
*/
public static void main(String[] args) {
PrinterJob job = PrinterJob.getPrinterJob();
//cross-platform print dialog, java l&f
// PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
//java page setup dialog
// PageFormat pf = job.getPageFormat(aset);
// job.setPrintable(new PrintingTest1().new HelloWorldPrinter(), pf);
//java print setup dialog
// boolean doPrint = job.printDialog(aset);
// if (doPrint) {
// try {
// job.print(aset);
// } catch (PrinterException e) {
// System.out.println("Cancelled. ");
// }
// }
//native print dialog
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
Font font1 = new Font("Courier New", Font.PLAIN, 12);
Font font2 = new Font("Courier New", Font.PLAIN, 10);
Font font3 = new Font("Courier New", Font.PLAIN, 9);
HelloWorldPrinter printer = new PrintingTest1().new HelloWorldPrinter();
printer.setFont1(font1);
printer.setFont2(font2);
printer.setFont3(font3);
printer.setTitle1("Title 1: Main title");
printer.setTitle2line1("Title 2 line 1: top");
printer.setTitle2line2("Title 2 line 2: bot");
printer.setHeaderline1("HeaderLine1: top");
printer.setHeaderline2("HeaderLine2: bot");
String[][] rows = new String[85][2];
for (int i=0; i<rows.length; i++) {
rows[i] = new String[] {"row " + i + ", line 1", " row " + i + ", line 2"};
}
printer.setRows(rows.length);
printer.setRowsData(rows);
job.setPrintable(printer);
boolean doPrint = job.printDialog();
if (doPrint) {
try {
job.print();
} catch (PrinterException e) {
System.out.println("Cancelled. ");
}
}
}
class HelloWorldPrinter implements Printable {
private Font font1;
private Font font2;
private Font font3;
private int lineHeight1;
private int lineHeight2;
private int lineHeight3;
private String title1;
private String title2line1;
private String title2line2;
private String headerline1;
private String headerline2;
private String[][] rowsData;
/**
* Number of rows in the table
*/
private int rows;
/**
* Number of lines of data to print. 2*rows.
*/
private int dataLines;
private int pageLimit;
private int linesDataOnPage0;
private int linesOnPage0;
private int linesPerPage;
/**
* The array index of row data when every new pages starts to print.
*/
private int newStartIndex;
public void setRows(int rows) {
this.rows = rows;
}
public void setFont1(Font font1) {
this.font1 = font1;
}
public void setFont2(Font font2) {
this.font2 = font2;
}
public void setFont3(Font font3) {
this.font3 = font3;
}
public void setTitle1(String title1) {
this.title1 = title1;
}
public void setTitle2line1(String title2line1) {
this.title2line1 = title2line1;
}
public void setTitle2line2(String title2line2) {
this.title2line2 = title2line2;
}
public void setHeaderline1(String headerline1) {
this.headerline1 = headerline1;
}
public void setHeaderline2(String headerline2) {
this.headerline2 = headerline2;
}
public void setRowsData(String[][] rowsData) {
this.rowsData = rowsData;
}
public HelloWorldPrinter() {
// TODO Auto-generated constructor stub
}
private int calculatePageLimit(int dataLines, Graphics g, PageFormat pf) {
FontMetrics metrics1 = g.getFontMetrics(font1);
FontMetrics metrics2 = g.getFontMetrics(font2);
FontMetrics metrics3 = g.getFontMetrics(font3);
lineHeight1 = metrics1.getHeight();
lineHeight2 = metrics2.getHeight();
lineHeight3 = metrics3.getHeight();
double pageHeight = pf.getImageableHeight();
//title 1, title 2 (2 lines), header (2 lines)
linesDataOnPage0 = (int)((pageHeight - lineHeight1 - lineHeight2*2 - lineHeight3*2) / lineHeight3);
linesOnPage0 = linesDataOnPage0 + 5;
linesPerPage = (int)pageHeight / lineHeight3;
int pageLimit0 = 0;
if (dataLines <= linesDataOnPage0) {
pageLimit0 = 1; //all data can be printed on the first page.
} else {
int lastPageLines = (dataLines - linesDataOnPage0) % linesPerPage;
pageLimit0 = (dataLines - linesDataOnPage0 - lastPageLines) / linesPerPage + 2;
}
return pageLimit0;
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (pageLimit == 0) { //not calculated yet.
this.dataLines = rows * 2;
pageLimit = calculatePageLimit(dataLines, graphics, pageFormat);
}
Paper papel = new Paper();
papel.setImageableArea(72, 72, 697, 450);
papel.setSize(841, 595);
pageFormat.setPaper(papel);
pageFormat.setOrientation(PageFormat.LANDSCAPE);
Graphics2D g2d = (Graphics2D)graphics;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
if (pageIndex < pageLimit) {
int y = 10;
if (pageIndex == 0) {
// 1. print the title 1
g2d.setFont(font1);
g2d.drawString(title1, 10, y);
y += lineHeight1;
// 2. print title 2
g2d.setFont(font2);
g2d.drawString(title2line1, 10, y);
y += lineHeight2;
g2d.drawString(title2line2, 10, y);
y += lineHeight2;
// 3. print header
g2d.setFont(font3);
g2d.drawString(headerline1, 10, y);
y += lineHeight3;
g2d.drawString(headerline2, 10, y);
y += lineHeight3;
// 4. print data
for (int i=0; i< (int)(linesDataOnPage0 / 2); i++) {
g2d.drawString(rowsData[i][0], 10, y);
y += lineHeight3;
g2d.drawString(rowsData[i][1], 10, y);
y += lineHeight3;
}
y = 0;
newStartIndex = (int)(linesDataOnPage0 / 2);
return PAGE_EXISTS;
} else {
int end;
if (newStartIndex + (int)(linesPerPage / 2) > rowsData.length) {
end = rowsData.length;
} else {
end = newStartIndex + (int)(linesPerPage / 2);
}
g2d.setFont(font3);
for (int i=newStartIndex; i< end; i++) {
g2d.drawString(rowsData[i][0], 10, y);
y += lineHeight3;
g2d.drawString(rowsData[i][1], 10, y);
y += lineHeight3;
}
y = 0;
newStartIndex +=(int)(linesPerPage / 2);
return PAGE_EXISTS;
}
} else {
return NO_SUCH_PAGE;
}
}
}
}
How to sort JTable alphabetically with separated lines showing the occurrence of the next letter? For example, consider how Windows Media Player 12 sorts it tracks,
Another example of this problem is for instance how Macintosh 'Finder' sorts recent opening applications by date. I can't figure out how they added additional row showing the date and lines enclosing the matching.
Any ideas of how to approach this problem?
Macintosh, Finder, example
To have the table rows appear in a place other than where JTable normally expects them to be, the methods in JTable you’ll need to override are rowAtPoint, getCellRect, and of course, paintComponent. To support those methods, I would keep track of the rows where section headings occur in a sorted Map, and I would keep track of the row positions in a second sorted Map.
I would then recompute those Maps whenever the table model or sort column changes, or whenever the table validates itself for any reason, which means override the tableChanged, sortedChanged, and validate methods, respectively.
The result isn’t as lengthy as you might expect:
public class SectionedTable
extends JTable {
private static final long serialVersionUID = 1;
private final NavigableMap<Integer, String> sectionHeadings =
new TreeMap<>();
private final NavigableMap<Integer, Integer> rowTopEdges =
new TreeMap<>();
// Used when calling SwingUtilities.layoutCompoundLabel.
private final Rectangle iconBounds = new Rectangle();
private final Rectangle textBounds = new Rectangle();
public SectionedTable() {
init();
}
public SectionedTable(TableModel model) {
super(model);
init();
}
private void init()
{
setShowGrid(false);
setAutoCreateRowSorter(true);
recomputeSections();
recomputeRowPositions();
}
private void recomputeSections() {
if (sectionHeadings == null) {
return;
}
sectionHeadings.clear();
RowSorter<? extends TableModel> sorter = getRowSorter();
if (sorter == null) {
return;
}
for (RowSorter.SortKey key : sorter.getSortKeys()) {
SortOrder order = key.getSortOrder();
if (order != SortOrder.UNSORTED) {
int sortColumn = key.getColumn();
String lastSectionStart = "";
int rowCount = getRowCount();
for (int row = 0; row < rowCount; row++) {
Object value = getValueAt(row, sortColumn);
if (value == null) {
value = "?";
}
String s = value.toString();
if (s.isEmpty()) {
s = "?";
}
String sectionStart = s.substring(0,
s.offsetByCodePoints(0, 1));
sectionStart = sectionStart.toUpperCase();
if (!sectionStart.equals(lastSectionStart)) {
sectionHeadings.put(row, sectionStart);
lastSectionStart = sectionStart;
}
}
break;
}
}
}
private void recomputeRowPositions() {
if (rowTopEdges == null) {
return;
}
rowTopEdges.clear();
int y = getInsets().top;
int rowCount = getRowCount();
int rowHeight = getRowHeight();
for (int row = 0; row < rowCount; row++) {
rowTopEdges.put(y, row);
y += getRowHeight(row);
if (sectionHeadings.containsKey(row)) {
y += rowHeight;
}
}
}
#Override
public void tableChanged(TableModelEvent event) {
recomputeSections();
recomputeRowPositions();
super.tableChanged(event);
}
#Override
public void sorterChanged(RowSorterEvent event) {
recomputeSections();
recomputeRowPositions();
super.sorterChanged(event);
}
#Override
public void validate() {
super.validate();
recomputeRowPositions();
}
#Override
public int rowAtPoint(Point location) {
Map.Entry<Integer, Integer> entry = rowTopEdges.floorEntry(location.y);
if (entry != null) {
int row = entry.getValue();
return row;
}
return -1;
}
#Override
public Rectangle getCellRect(int row,
int column,
boolean includeSpacing) {
Rectangle rect = super.getCellRect(row, column, includeSpacing);
int sectionHeadingsAbove = sectionHeadings.headMap(row, true).size();
rect.y += sectionHeadingsAbove * getRowHeight();
return rect;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
boolean ltr = getComponentOrientation().isLeftToRight();
int rowHeight = getRowHeight();
FontMetrics metrics = g.getFontMetrics();
int ascent = metrics.getAscent();
for (Map.Entry<Integer, String> entry : sectionHeadings.entrySet()) {
int row = entry.getKey();
String heading = entry.getValue();
Rectangle bounds = getCellRect(row, 0, true);
bounds.y -= rowHeight;
bounds.width = getWidth();
bounds.grow(-6, 0);
iconBounds.setBounds(0, 0, 0, 0);
textBounds.setBounds(0, 0, 0, 0);
String text = SwingUtilities.layoutCompoundLabel(this,
metrics, heading, null,
SwingConstants.CENTER, SwingConstants.LEADING,
SwingConstants.CENTER, SwingConstants.CENTER,
bounds, iconBounds, textBounds, 0);
g.drawString(text, textBounds.x, textBounds.y + ascent);
int lineY = textBounds.y + ascent / 2;
if (ltr) {
g.drawLine(textBounds.x + textBounds.width + 12, lineY,
getWidth() - getInsets().right - 12, lineY);
} else {
g.drawLine(textBounds.x - 12, lineY,
getInsets().left + 12, lineY);
}
}
}
}
You can adapt this class to make a Finder table, by changing how the headings are derived from the data. Merely examining one cell with getValueAt won’t be sufficient; instead, you’ll need to translate the sorted row to the corresponding model row, and obtain the data object for that row directly from the TableModel. Then you can compare the rows by age rather than by string data.
I would group my data alphabetically and then create a separate table for each letter included in my data.
You can customize the display of the header using a TableCellRenderer to get the desired look, if you are looking to add more columns as shown in your examples you can replace the header with a different component altogether.
public class MyTableFrame extends JFrame {
MyTableFrame() {
List<String> data = Arrays.asList(
"Alpha1",
"Beta1",
"Alpha2",
"Charlie2",
"Alpha3",
"Beta2",
"Charlie1"
);
// Sort alphabetically
data.sort(String::compareTo);
// Group by first letter
Map<Character, List<String>> collect = data.stream()
.collect(Collectors.groupingBy(string -> string.charAt(0)));
JPanel tablesContainer = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.weighty = 0;
c.gridy = 0;
// Create a table for each Letter
collect.entrySet().forEach(mapEntry -> {
Character letter = mapEntry.getKey();
List<String> startingWithLetter = mapEntry.getValue();
TableModel tableModel = new TableModel(startingWithLetter, letter);
JTable table = new JTable(tableModel);
// Table header must be added separately since there is no scroll pane
JTableHeader tableHeader = table.getTableHeader();
tablesContainer.add(tableHeader, c);
c.gridy++;
tablesContainer.add(table, c);
c.gridy++;
});
add(tablesContainer);
setVisible(true);
pack();
}
static class TableModel extends AbstractTableModel {
private List<String> data;
private Character columnName;
TableModel(List<String> data, Character columnName) {
this.data = data;
this.columnName = columnName;
}
#Override
public String getColumnName(int column) {
return columnName.toString();
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return data.get(rowIndex);
}
}
public static void main(String args[]) {
new MyTableFrame();
}
}
Please Help. When I run this GUI the numbers run off the frame. I know I have to use JTextArea and append but where do I put that in my code. can someone explain to me in simple terms and show me? I want to make it scroll vertically and horizontally and when I do add the JTextArea I get this error: error: cannot find symbol panel.setMessage(message);
import java.io.*;
import java.util.*;
import java.lang.*;
import java.text.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class prime extends JFrame
{
public static void main(String[] args)
{
prime frame = new prime();
}
private JTextArea panel;
private JPanel inPanel;
private JTextField inField;
public prime()
{
final int width = 500;
final int height = 500;
setSize(width, height);
setTitle("Find Prime Numbers");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JTextArea(20, 10);
add(new JScrollPane(panel), "Center");
inPanel = new JPanel();
inPanel.add(new JLabel("Enter Your Number", SwingConstants.RIGHT));
inField = new JTextField(20);
ActionListener inListener = new TextListener();
inField.addActionListener(inListener);
inPanel.add(inField);
add(inPanel, "South");
setVisible(true);
}
private class TextListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String message = inField.getText();
inField.setText("");
panel.setMessage(message);
}
}
class TextPanel extends JPanel
{
private String message;
private Color backGroundColor;
public TextPanel()
{
message = "";
backGroundColor = Color.white;
}
public TextPanel(String x, Color background)
{
message = x;
backGroundColor = background;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
int width = getWidth();
int height = getHeight();
setBackground(backGroundColor);
g2.setColor(Color.black);
Font x = new Font("TimesNewRoman", Font.BOLD,20);
g2.setFont(x);
FontMetrics fm = g2.getFontMetrics(x);
g2.drawString(message,50, 50);
if(!(message.equals("")))
g2.drawString(previousPrime(message),50,78);
}
public void setMessage(String message) {
if (isPrime(Integer.parseInt(message))){
this.message = message + " is a prime number.";
}
else
this.message = message + " is not a prime number.";
repaint();
}
public boolean isPrime(int num){
for(int i = 2; i < num; i++){
if (num % i == 0)
return false;
}
if(num < 2)
return false;
return true;
}
public String previousPrime(String message){
String totalPrimeNum = "";
int finalNum = Integer.parseInt(message.substring(0,message.indexOf(" ")));
int count = 0;
for(int i = 2; i < finalNum; i++){
if(isPrime(i)) {
totalPrimeNum += " " + i;
count++;
}
if(count == 10) {
totalPrimeNum += "\n";
count = 0;
}
}
if (isPrime(Integer.parseInt(message.substring(0,message.indexOf(" ")))))
totalPrimeNum += " " + finalNum;
System.out.println(totalPrimeNum);
return totalPrimeNum;
}}}
Look at what you have and what you want to achieve. You have a method which can verify if a value is a prime or not, but it does not produce any output. You have a JTextArea which allows you to add content to it.
You have a square peg and a round hole. One of these things needs to change. You have control over the setMessage method, but you don't have control over the JTextArea, this would suggest that the setMessage needs to change. While you could pass the JTextArea to the setMessage method, a better solution would be to change the setMessage to return some kind of meaningful value or simply get rid of it in favor of using the isPrime method instead
Take your TestPane and move the functionality used to calculate and test for a prime number to a new class, for example...
public class PrimeCalculator {
public boolean isPrime(int num) {
for (int i = 2; i < num; i++) {
if (num % i == 0) {
return false;
}
}
if (num < 2) {
return false;
}
return true;
}
public String previousPrime(String message) {
StringBuilder totalPrimeNum = new StringBuilder(128);
int finalNum = Integer.parseInt(message.substring(0, message.indexOf(" ")));
int count = 0;
for (int i = 2; i < finalNum; i++) {
if (isPrime(i)) {
totalPrimeNum.append(" ").append(i);
count++;
}
if (count == 10) {
totalPrimeNum.append("\n");
count = 0;
}
}
if (isPrime(Integer.parseInt(message.substring(0, message.indexOf(" "))))) {
totalPrimeNum.append(" ").append(finalNum);
}
System.out.println(totalPrimeNum);
return totalPrimeNum.toString();
}
}
This now makes no assumptions over what you want to do and simply produces output. You can now use these methods and make decisions about how to use the output, for example...
private class TextListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
PrimeCalculator calc = new PrimeCalculator();
String message = inField.getText();
inField.setText("");
String text = message + " is not a prime number\n";
try {
if (calc.isPrime(Integer.parseInt(message))) {
text = message + " is a prime number\n";
}
} catch (NumberFormatException exp) {
text = message + " is not a valid integer\n";
}
panel.append(message);
panel.setCaretPosition(panel.getDocument().getLength());
}
}
Sometimes, you need to rethink your design to support what it is you want to achieve.
The process of verifying and calculating the prime numbers should do just that, they should not do anything else (like update the screen), this way you can decouple the code and re-use it in different ways which you didn't original think of (like outputting to a file or the web)
See How to Use Text Areas and How to Use Scroll Panes for more details
I have a game and I'm having trouble implementing the scores.
Prof stated that it must be recursively done without a for-loop. But I'm having trouble thinking of the algorithm that is used. This is the game.
The score system works so it starts with the valve pipe (Pipe.ValvePipe) and then it checks to see how many pipes are connected to it. Is there a certain algorithm/strategy for doing this type of recursion? Thank you in advance.
pipe.java
public class Pipe {
private boolean openAtTop;
private boolean openAtRight;
private boolean openAtBottom;
private boolean openAtLeft;
private boolean isValve;
private static Pipe VALVE_PIPE;
public Pipe(boolean t, boolean r, boolean b, boolean l, boolean valve) {
openAtTop = t; openAtBottom = b; openAtRight = r; openAtLeft = l;
isValve = valve;
}
public boolean isValve() { return isValve; }
public static Pipe ValvePipe() { return new Pipe(false, false, true, false, true); }
public boolean isValid() { return !(!openAtTop&&!openAtBottom&&!openAtLeft&&!openAtRight); }
public static Pipe RandomPipe() {
Pipe p;
do {
p = new Pipe(Math.random() < 0.5, Math.random() < 0.5,
Math.random() < 0.5, Math.random() < 0.5, false);
} while (!p.isValid());
return p;
}
public boolean isOpenAtTop() { return openAtTop; }
public boolean isOpenAtBottom() { return openAtBottom; }
public boolean isOpenAtLeft() { return openAtLeft; }
public boolean isOpenAtRight() { return openAtRight; }
public boolean fitsBelow(Pipe p) {
return (p == null) || (!openAtTop && !p.isOpenAtBottom()) || (openAtTop && p.isOpenAtBottom());
}
public boolean fitsAbove(Pipe p) {
return (p == null) || (!openAtBottom && !p.isOpenAtTop()) || (openAtBottom && p.isOpenAtTop());
}
public boolean fitsToLeftOf(Pipe p) {
return (p == null) || (!openAtRight && !p.isOpenAtLeft()) || (openAtRight && p.isOpenAtLeft());
}
public boolean fitsToRightOf(Pipe p) {
return (p == null) || (!openAtLeft && !p.isOpenAtRight()) || (openAtLeft && p.isOpenAtRight());
}
public String toString() {
String s = "";
if (openAtTop) s+="1"; else s+="0";
if (openAtRight) s+="1"; else s+="0";
if (openAtBottom) s+="1"; else s+="0";
if (openAtLeft) s+="1"; else s+="0";
return s;
}
public int toInt() {
int s = 0;
if (openAtTop) s+=8;
if (openAtRight) s+=4;
if (openAtBottom) s+=2;
if (openAtLeft) s+=1;
return s;
}
}
PipeGameView.java
import java.awt.*;
import javax.swing.*;
// Subclass JFrame so you can display a window
public class PipeGameView extends JPanel {
private PipeGame game; // The model
private BoardPanel tiles;
private JButton[][] buttons;
private JProgressBar timeBar;
private JButton startStop;
private JRadioButton twoMinButton, tenMinButton, noLimitButton;
private JLabel statusLabel;
// This constructor builds the window
public PipeGameView(PipeGame g) {
game = g; // Store the model for access in update()
// Set up the components
tiles = new BoardPanel();
tiles.setLayout(new GridLayout(game.getRows(), game.getRows()));
buttons = new JButton[game.getRows()][game.getRows()];
// Add the buttons to the tile panel
ImageIcon ic = new ImageIcon("Pipes0000.GIF");
for (int r=0; r<game.getRows(); r++) {
for (int c=0; c<game.getRows(); c++) {
buttons[r][c] = new JButton(ic);
tiles.add(buttons[r][c]);
}
}
// Now layout the components using a gridbag layout
GridBagLayout layout = new GridBagLayout();
GridBagConstraints layoutConstraints = new GridBagConstraints();
this.setLayout(layout);
// Add the Start/Stop Button
startStop = new JButton("Start Game");
layoutConstraints.gridx = 0; layoutConstraints.gridy = 0;
layoutConstraints.gridwidth = 1; layoutConstraints.gridheight = 1;
layoutConstraints.fill = GridBagConstraints.BOTH;
layoutConstraints.insets = new Insets(2, 2, 2, 2);
layoutConstraints.anchor = GridBagConstraints.NORTHWEST;
layoutConstraints.weightx = 0.0; layoutConstraints.weighty = 0.0;
layout.setConstraints(startStop, layoutConstraints);
this.add(startStop);
// Add the JRadioButtons
twoMinButton = new JRadioButton("2 minutes");
tenMinButton = new JRadioButton("10 minutes");
noLimitButton = new JRadioButton("No Time Limit");
ButtonGroup group = new ButtonGroup();
group.add(twoMinButton); group.add(tenMinButton); group.add(noLimitButton);
layoutConstraints.gridx = 1;
layout.setConstraints(twoMinButton, layoutConstraints);
this.add(twoMinButton);
layoutConstraints.gridx = 2;
layout.setConstraints(tenMinButton, layoutConstraints);
this.add(tenMinButton);
layoutConstraints.gridx = 3;
layout.setConstraints(noLimitButton, layoutConstraints);
this.add(noLimitButton);
// Add the tiles
layoutConstraints.gridx = 0; layoutConstraints.gridy = 1;
layoutConstraints.gridwidth = 4; layoutConstraints.gridheight = 1;
layoutConstraints.fill = GridBagConstraints.BOTH;
layoutConstraints.insets = new Insets(2, 2, 2, 2);
layoutConstraints.anchor = GridBagConstraints.NORTHWEST;
layoutConstraints.weightx = 0.0; layoutConstraints.weighty = 0.0;
layout.setConstraints(tiles, layoutConstraints);
this.add(tiles);
// Add the label
statusLabel = new JLabel("Time Left: ");
statusLabel.setVisible(false);
layoutConstraints.gridx = 0; layoutConstraints.gridy = 2;
layoutConstraints.gridwidth = 1; layoutConstraints.gridheight = 1;
layoutConstraints.fill = GridBagConstraints.NONE;
layoutConstraints.insets = new Insets(2, 2, 2, 2);
layoutConstraints.anchor = GridBagConstraints.WEST;
layoutConstraints.weightx = 0.0; layoutConstraints.weighty = 0.0;
layout.setConstraints(statusLabel, layoutConstraints);
this.add(statusLabel);
timeBar = new JProgressBar(0, 100);
timeBar.setValue(100);
timeBar.setVisible(false);
layoutConstraints.gridx = 1; layoutConstraints.gridy = 2;
layoutConstraints.gridwidth = 3; layoutConstraints.gridheight = 1;
layoutConstraints.fill = GridBagConstraints.HORIZONTAL;
layoutConstraints.insets = new Insets(2, 2, 2, 2);
layoutConstraints.anchor = GridBagConstraints.EAST;
layoutConstraints.weightx = 5.0; layoutConstraints.weighty = 0.0;
layout.setConstraints(timeBar, layoutConstraints);
this.add(timeBar);
update();
}
// Get methods for the components
public JButton getButton(int r, int c) { return buttons[r][c]; }
public JButton getStartStopButton() { return startStop; }
public JProgressBar getTimeBar() { return timeBar; }
public JRadioButton getTwoMinButton() { return twoMinButton; }
public JRadioButton getTenMinButton() { return tenMinButton; }
public JRadioButton getNoLimitButton() { return noLimitButton; }
public void setGame(PipeGame g) { game = g; }
// This is called whenever the model has changed. Note that this code is not efficient. All ICONS should really
// be loaded upon game start and stored into a static array of ImageIcons. Then, these static icons should be
// used instead of re-creating icons each time.
public void update() {
// Update the look of the buttons
for (int r=0; r<game.getRows(); r++) {
for (int c=0; c<game.getRows(); c++) {
if (game.isOver())
buttons[r][c].setEnabled(false);
else
buttons[r][c].setEnabled(true);
if (game.getPipe(r,c) == null)
buttons[r][c].setSelected(false);
else {
// Determine the portion of the icon's filename that matches the pipe
String currentPipeCodes = "Start";
if (!game.getPipe(r,c).isValve())
currentPipeCodes = game.getPipe(r,c).toString();
buttons[r][c].setSelectedIcon(new ImageIcon("pipes"+ currentPipeCodes +".GIF"));
buttons[r][c].setSelected(true);
}
}
}
// Update the start/stop button
if (game.isOver())
startStop.setText("Start");
else
startStop.setText("Stop");
// Update the radio buttons
if (game.isOver()) {
twoMinButton.setEnabled(true);
tenMinButton.setEnabled(true);
noLimitButton.setEnabled(true);
}
else {
twoMinButton.setEnabled(false);
tenMinButton.setEnabled(false);
noLimitButton.setEnabled(false);
}
// Update the status label
if (game.isOver()) {
statusLabel.setText("Final Score: " + game.getPlacedPipes());
statusLabel.setVisible(true);
}
else {
if (game.getStyle() == 2) {
statusLabel.setVisible(false);
}
else {
statusLabel.setText("Time Left: ");
statusLabel.setVisible(true);
}
}
// Update the timer bar
if (game.isOver() || (game.getStyle() == 2)) {
timeBar.setVisible(false);
}
else {
timeBar.setValue((int)(game.getTimeRemaining() / (float)game.getTimeLimit() * 100));
timeBar.setVisible(true);
}
// Update the cursor
ImageIcon i;
if (game.isOver()) {
tiles.setCursor(Cursor.getDefaultCursor());
}
else {
if (game.getNextPipe().isValve())
i = new ImageIcon("pipesStart.GIF");
else
i = new ImageIcon("pipes"+ game.getNextPipe().toString() +".GIF");
tiles.setCursor(Toolkit.getDefaultToolkit().createCustomCursor(i.getImage(), new Point(0,0), "pipe"));
}
}
}
I didnt go through all your code but as a hint the function would look like this:
getScore( Pipe p ){
if ( p == null ) return 0;
int res = 1; // size of this pipe
res += getScore(leftPipe);
res += getScore(rightPipe);
...
return res;
}
hope this helps
I think you need to includes the row and col to calculate the pipes that are connected to the main starting pipe
public void connectedPipes(int row, int col){
if(row == null) return 0;
int res = 1; // size of this pipe
res += getScore(leftPipe);
res += getScore(rightPipe);
return res;
}