JTable AutoScrolling like IntelliJ Console - java

I want to add a Scroll To End (Autoscroll) function to a jtable with maximum up to 50 rows.
AutoScroll function should work as IntelliJ Scroll To End.
Initially there are 0 rows. In several milliseconds (say 100 ms.) a few item (1-5 items) is added to the end of the table. If the table row count reaches to the max (50), the first row will be deleted before the next insertion.
When user scrolls to the end with mouse dragging or scroll wheel, scroll to end checkbox selected and function will be enabled. From that moment, the scrollpane should be scrolled to the bottom of the table like:
scrollRectToVisible(getCellRect(getRowCount() - 1, 0, true));
When user changes scrollbar position other than the bottom of viewport by dragging or mouse scroll wheel, scroll to end checkbox will deselected and function will be disabled.
While scroll to end checkbox is not selected, the table will react to scroll bar movements which are sourced by dragging or mouse wheel. But the viewport should show same data.
If it is impossible to show same data in the viewport because of deletions of the consequent first rows, the visible first data in the viewport will go to the up.
This is the exact behavior of the IntelliJ console log screen.
I wrote Scroll To End enable functionality (90% percent working).
I could not write Scroll To End disable functionality, I don't know to preserve current viewport as stated above.
Thanks for your help.
package org.example.table;
import com.bsbls.home.gui.test.GuiTester;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class MyTable extends JTable {
JScrollPane scrollPane;
boolean autoScroll;
private Consumer<Boolean> scrollToEndListener;
private Object lastValue;
private ScheduledFuture<?> future;
public MyTable(int max) {
this.setFillsViewportHeight(true);
this.setRowHeight(24);
this.setModel(new MyTableModel(max));
this.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
if (autoScroll) {
scrollToEnd();
}
}
});
}
public MyTableModel getTableModel() {
return (MyTableModel) getModel();
}
private void scrollToEnd() {
scrollRectToVisible(getCellRect(getRowCount() - 1, 0, true));
}
public void setScrollToEnd(boolean autoScroll) {
this.autoScroll = autoScroll;
if (autoScroll) {
scrollToEnd();
}
if (scrollToEndListener != null) {
scrollToEndListener.accept(autoScroll);
}
}
public void setScrollToEndListener(Consumer<Boolean> scrollToEndListener) {
this.scrollToEndListener = scrollToEndListener;
}
public JScrollPane wrap() {
if (scrollPane == null) {
scrollPane = new JScrollPane(this);
scrollPane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);
JScrollBar scrollBar = scrollPane.getVerticalScrollBar();
scrollBar.addAdjustmentListener(e -> {
if (e.getValue() + scrollBar.getVisibleAmount() == scrollBar.getMaximum()) {
setScrollToEnd(true);
} else {
setScrollToEnd(false);
}
});
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scrollPane.addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (future != null) {
future.cancel(false);
}
future = scheduler.schedule(() -> {
EventQueue.invokeLater(() -> {
System.out.println("Yes");
JViewport viewport = scrollPane.getViewport();
Point p = viewport.getViewPosition();
Dimension extentSize = viewport.getExtentSize();
//p.translate(extentSize.width, extentSize.height);
int rowIndex = rowAtPoint(p);
if (rowIndex >= 0) {
lastValue = getValueAt(rowIndex, 0);
}
});
}, 500, TimeUnit.MILLISECONDS);
}
});
scrollBar.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
//System.out.println(e);
}
#Override
public void mousePressed(MouseEvent e) {
//System.out.println(e);
lastValue = null;
}
#Override
public void mouseReleased(MouseEvent e) {
//System.out.println(e);
JViewport viewport = scrollPane.getViewport();
Point p = viewport.getViewPosition();
Dimension extentSize = viewport.getExtentSize();
//p.translate(extentSize.width, extentSize.height);
int rowIndex = rowAtPoint(p);
if (rowIndex >= 0) {
lastValue = getValueAt(rowIndex, 0);
}
}
#Override
public void mouseEntered(MouseEvent e) {
//System.out.println(e);
}
#Override
public void mouseExited(MouseEvent e) {
//System.out.println(e);
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
//System.out.println(e);
}
#Override
public void mouseDragged(MouseEvent e) {
// System.out.println(e);
}
#Override
public void mouseMoved(MouseEvent e) {
//System.out.println(e);
}
});
getTableModel().addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
if (lastValue != null && !autoScroll) {
int rowCount = getRowCount();
int newIndex = -1;
for (int i = 0; i < rowCount; i++) {
Object indexValue = getValueAt(i, 0);
if (indexValue == lastValue) {
newIndex = i;
break;
}
}
System.out.println(lastValue + " " + newIndex);
if (newIndex > 1) {
scrollRectToVisible(getCellRect(newIndex - 1, 0, true));
} else {
lastValue = null;
}
}
}
});
}
return scrollPane;
}
static int counter;
public static void main(String[] args) {
GuiTester.test(f -> {
JPanel panel = new JPanel(new BorderLayout());
MyTable table = new MyTable(50);
MyTableModel model = table.getTableModel();
Random random = new Random();
Timer timer = new Timer(100, e -> {
Data data = new Data();
data.setName(++counter + "");
data.setX(random.nextInt());
data.setY(random.nextInt());
data.setZ(random.nextInt());
data.setFlag(random.nextBoolean());
model.addRow(data.toObjectArray());
});
timer.start();
DataPanel dataPanel = new DataPanel();
table.getSelectionModel().addListSelectionListener(e -> {
int index = e.getFirstIndex();
if (index >= 0) {
Data data = (Data) table.getValueAt(index, 5);
dataPanel.setData(data);
}
});
JCheckBox checkBox = new JCheckBox("Scroll To End");
table.setScrollToEndListener(flag -> {
checkBox.setSelected(flag);
});
checkBox.addItemListener(e -> {
table.setScrollToEnd(checkBox.isSelected());
});
panel.add(checkBox, BorderLayout.NORTH);
panel.add(table.wrap(), BorderLayout.CENTER);
panel.add(dataPanel.getPanel(), BorderLayout.EAST);
return panel;
});
}
}
Table Model
package org.example.table;
import javax.swing.table.DefaultTableModel;
public class MyTableModel extends DefaultTableModel {
private int max = -1;
public MyTableModel() {
this(-1);
}
public MyTableModel(int max) {
super(new Object[]{
"Name", "Flag", "X", "Y", "Z", "Data"
}, 0);
this.max = max;
}
#Override
public void addRow(Object[] rowData) {
if (getRowCount() == max) {
super.removeRow(0);
}
super.addRow(rowData);
}
}
Dummy data
package org.example.table;
public class Data {
private String name;
private boolean flag;
private int x;
private int y;
private int z;
public Object[] toObjectArray() {
Object[] array = new Object[6];
array[0] = name;
array[1] = flag;
array[2] = x;
array[3] = y;
array[4] = z;
array[5] = this;
return array;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
}
DatPanel which is a dummy IntelliJ form:
package org.example.table;
import javax.swing.*;
public class DataPanel {
private JTextField fieldName;
private JCheckBox flagCheckBox;
private JTextField fieldX;
private JTextField fieldY;
private JTextField fieldZ;
private JPanel panel;
public JPanel getPanel() {
return panel;
}
public void setData(Data data) {
fieldName.setText(data.getName());
fieldX.setText(data.getX() + "");
fieldY.setText(data.getY() + "");
fieldZ.setText(data.getZ() + " ");
flagCheckBox.setSelected(data.isFlag());
}
}
EDIT:
I have added to mouse and mouse wheel listeners, and find the first visible row.
And changed scroll mode to simple or backingstore.
scrollPane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);
In that case I have got a better function.
I am still open for better approaches.

Related

Is there a simpler approach to have a table like (sorting, changing columns width) for read-only but interactive cells

I would a Swing component that almost behave like a JTable for read-only data, but with interactive cells. The table might have hundreds of rows so adding components to the swing tree might not be the right choice.
Currently I'm hacking around the JTable by making the interactive cells editable. This fills hacky and misusing the API, however there's no choice there.
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Objects;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
public final class InteractiveTableCells extends JPanel {
private InteractiveTableCells() {
super(new BorderLayout());
String wagonsData = "";
Object[][] data = {
{124, wagonsData},
{13, wagonsData},
{78, wagonsData},
{103, wagonsData}
};
var model = new DefaultTableModel(data, new String[] {"Seats", "Train"}) {
#Override
public Class<?> getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
#Override
public boolean isCellEditable(int row, int column) {
return column == 1;
}
};
var table = new JTable(model);
table.setRowHeight(30);
table.setColumnSelectionAllowed(false);
var trainColumn = table.getColumnModel().getColumn(1);
trainColumn.setCellRenderer(new TrainChartPanelRenderer());
trainColumn.setCellEditor(new TrainChartPanelEditor(table));
add(new JScrollPane(table));
setPreferredSize(new Dimension(320, 240));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
var frame = new JFrame("Read-only Interactive Table Cells");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new InteractiveTableCells());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
class InteractiveChartPanel extends JPanel {
private final Rectangle wagon1 = new Rectangle(4, 2, 30, 16);
private final Rectangle wagon2 = new Rectangle(4 + 30 + 4, 2, 30, 16);
private final Rectangle wagon3 = new Rectangle(38 + 30 + 4, 2, 30, 16);
private final Rectangle wagon4 = new Rectangle(72 + 30 + 4, 2, 30, 16);
private Rectangle hoveredWagon = null;
protected InteractiveChartPanel() {
super();
setOpaque(true);
addMouseMotionListener(new MouseInputAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
var location = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(location, InteractiveChartPanel.this);
var oldHoveredWagon = hoveredWagon;
if (wagon1.contains(location)) {
hoveredWagon = wagon1;
} else if (wagon2.contains(location)) {
hoveredWagon = wagon2;
} else if (wagon3.contains(location)) {
hoveredWagon = wagon3;
} else if (wagon4.contains(location)) {
hoveredWagon = wagon4;
} else {
hoveredWagon = null;
}
if (!Objects.equals(oldHoveredWagon, hoveredWagon)) {
repaint();
}
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.ORANGE);
g2.fill(wagon1);
g2.fill(wagon2);
g2.fill(wagon3);
g2.fill(wagon4);
if (hoveredWagon != null) {
g2.setColor(Color.ORANGE.darker());
g2.fill(hoveredWagon);
}
}
}
class TrainChartPanelRenderer extends InteractiveChartPanel implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
return this;
}
}
class TrainChartPanelEditor extends AbstractCellEditor implements TableCellEditor {
private final InteractiveChartPanel panel = new InteractiveChartPanel();
protected TrainChartPanelEditor(JTable table) {
table.addMouseMotionListener(new MouseInputAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
int r = table.rowAtPoint(e.getPoint());
int c = table.columnAtPoint(e.getPoint());
if (table.isCellEditable(r, c)
&& (table.getEditingRow() != r || table.getEditingColumn() != c) // avoid flickering, when the mouse mouve over the same cell
) {
// Cancel previous, otherwise editCellAt will invoke stopCellEditing which
// actually get the current value from the editor and set it to the model (see editingStopped)
if (table.isEditing() && r >= 0 && c >= 0) {
table.getCellEditor().cancelCellEditing();
}
table.editCellAt(r, c);
} else {
if (table.isEditing() || r < 0 || c < 0) {
table.getCellEditor().cancelCellEditing();
}
}
}
});
panel.addMouseListener(new MouseInputAdapter() {
#Override
public void mouseExited(MouseEvent e) {
SwingUtilities.invokeLater(TrainChartPanelEditor.this::fireEditingCanceled);
}
});
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
panel.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
return panel;
}
#Override
public Object getCellEditorValue() {
throw new IllegalStateException("Editing should have been cancelled");
}
}
Bonus, in my case the last row might have longer content, and I'm not quite sure how to adjust the row height dynamically when the JTable is resized for example. And without triggering an infinite loop as calling setRowHeight(row) in the cell renderer can trigger a relayout and then invoke the cell renderer again.
You don’t need to deal with cell editors if you only want a hover highlighting. Just a mouse(motion)listener on the table itself, to update the affected cells, is enough.
For example
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import java.util.List;
public final class InteractiveTableCells {
public static void main(String... args) {
if(!EventQueue.isDispatchThread()) {
EventQueue.invokeLater(InteractiveTableCells::main);
return;
}
var frame = new JFrame("Read-only Interactive Table Cells");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setContentPane(createContent());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static Container createContent() {
Integer[] data = { 124, 13, 78, 103 };
var model = new AbstractTableModel() {
#Override
public int getRowCount() {
return data.length;
}
#Override
public int getColumnCount() {
return 2;
}
#Override
public String getColumnName(int column) {
return column == 0? "Seats": "Train";
}
#Override
public Class<?> getColumnClass(int column) {
return column == 0? Integer.class: String.class;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return columnIndex == 0? data[rowIndex]: "";
}
};
var table = new JTable(model);
table.setRowHeight(30);
table.setColumnSelectionAllowed(false);
var trainRenderer = new TrainRenderer();
var defCellRenderer = new DefaultTableCellRenderer();
defCellRenderer.setIcon(trainRenderer);
table.getColumnModel().getColumn(1).setCellRenderer(
(comp, value, selected, focus, row, column) -> {
trainRenderer.paintingActive = row == trainRenderer.activeRow;
return defCellRenderer.getTableCellRendererComponent(
comp, value, selected, focus, row, column);
});
var mouseListener = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
check(table.rowAtPoint(p), table.columnAtPoint(p), p);
}
#Override
public void mouseExited(MouseEvent e) {
check(-1, -1, null);
}
private void check(int r, int c, Point p) {
int lastActive = trainRenderer.activeRow;
if(c != 1) {
trainRenderer.activeRow = -1;
r = -1;
}
if(r < 0 && lastActive < 0) return;
if(r >= 0) {
var rect = table.getCellRect(r, c, false);
p.x -= rect.x;
p.y -= rect.y;
int oldCar = trainRenderer.activeCar;
if(!trainRenderer.check(p, r)) r = -1;
else if(r != lastActive || trainRenderer.activeCar != oldCar)
table.repaint(rect);
}
if(r != lastActive && lastActive >= 0) {
table.repaint(table.getCellRect(lastActive, 1, false));
}
}
};
table.addMouseMotionListener(mouseListener);
table.addMouseListener(mouseListener);
var sp = new JScrollPane(table);
sp.setPreferredSize(new Dimension(320, 240));
return sp;
}
}
class TrainRenderer implements Icon {
private final Rectangle wagon1 = new Rectangle(4, 2, 30, 16);
private final Rectangle wagon2 = new Rectangle(4 + 30 + 4, 2, 30, 16);
private final Rectangle wagon3 = new Rectangle(38 + 30 + 4, 2, 30, 16);
private final Rectangle wagon4 = new Rectangle(72 + 30 + 4, 2, 30, 16);
private final List<Rectangle> allWagons = List.of(wagon1, wagon2, wagon3, wagon4);
int activeRow = -1, activeCar = -1;
boolean paintingActive;
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D)g;
g2.translate(x, y);
for(int i = 0, num = allWagons.size(); i < num; i++) {
g2.setColor(
paintingActive && activeCar == i? Color.ORANGE.darker(): Color.ORANGE);
g2.fill(allWagons.get(i));
}
g2.translate(-x, -y);
}
boolean check(Point p, int row) {
for(int i = 0, num = allWagons.size(); i < num; i++) {
if(allWagons.get(i).contains(p)) {
activeRow = row;
activeCar = i;
return true;
}
}
activeRow = -1;
activeCar = -1;
return false;
}
#Override
public int getIconWidth() {
return wagon4.x + wagon4.width;
}
#Override
public int getIconHeight() {
return wagon4.y + wagon4.height;
}
}
Note that I also removed all unnecessary subclass relationships, compare with Prefer composition over inheritance?
By using the default cell renderer, you get the look&feel’s highlighting, as well as some performance optimizations for free.

Java: Graphics2D - JPopupMenu - Color Changing

I’m writing a simple graphics editor, thus far I can draw some figures, move and enlarge them. I’m trying to allow user to change color of figure. After I right click in a shape, there appears a popup menu with colors to choose. But no matter what I do - the shape’s color doesn’t change. :/ I hope to get help, I spent a of of time on it but no idea how to solve it. :/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
public class PaintPanel extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener
{
public RadioMenu radio = new RadioMenu();
private ArrayList <Point2D.Double> points = new ArrayList<>();
private ArrayList <Shape> figures = new ArrayList<>();
private Color mainColor = Color.blue;
private Color bgColor = Color.white;
private Color special = Color.red;
private double scrollSpeed = 5;
private int pointsize = 4;
private int near = 15;
private int index;
private ColorMenu colorMenu = new ColorMenu();
public PaintPanel()
{
super();
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setLayout(new BorderLayout());
setBackground(bgColor);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
drawGraphics(g2d);
}
private void drawGraphics(Graphics2D g2d)
{
int i = 0;
for (Shape s : figures)
{
g2d.setColor(mainColor);
if (s instanceof MyEllipse2D)
{
g2d.setColor(((MyEllipse2D) s).color);
System.out.println(g2d.getColor());
}
else if (s instanceof MyRectangle2D)
{
g2d.setColor(((MyRectangle2D) s).color);
System.out.println(g2d.getColor());
}
else if (s instanceof MyPolygon2D)
{
g2d.setColor(((MyPolygon2D) s).color);
System.out.println(g2d.getColor());
}
if (g2d.getColor() != bgColor)
{
g2d.setColor(mainColor);
} else
{
g2d.setColor(mainColor);
g2d.draw(s);
}
++i;
}
i = 0;
for (Point2D.Double p : points)
{
if (i == 0)
{
g2d.setColor(special);
g2d.fillOval((int) p.getX(), (int) p.getY(), pointsize, pointsize);
g2d.setColor(mainColor);
} else
{
g2d.fillOval((int) p.getX(), (int) p.getY(), pointsize, pointsize);
}
++i;
}
}
#Override
public void mousePressed(MouseEvent e)
{
if(e.getButton()==MouseEvent.BUTTON1)
switch (radio.getChoice())
{
case "Okrag":
points.clear();
points.add(new Point2D.Double(e.getX(),e.getY()));
repaint();
break;
case "Prostokat":
points.clear();
points.add(new Point2D.Double(e.getX(),e.getY()));
repaint();
break;
case "Edycja":
index = isSelected(e);
break;
}
else if(e.getButton()==MouseEvent.BUTTON3 && radio.getChoice().equals("Edycja"))
{
index = isSelected(e);
if(index >= 0)
{
colorMenu.doPop(e);
}
}
}
#Override
public void mouseReleased(MouseEvent e)
{
if(e.getButton()==MouseEvent.BUTTON1)
switch (radio.getChoice())
{
case "Okrag":
points.add(new Point2D.Double(e.getX(),e.getY()));
figures.add(new MyEllipse2D(points.get(0),points.get(1),bgColor));
points.clear();
break;
case "Prostokat":
points.add(new Point2D.Double(e.getX(),e.getY()));
figures.add(new MyRectangle2D(points.get(0),points.get(1),bgColor));
points.clear();
break;
case "Wielokat":
if(points.size() != 0 && points.get(0).distance(e.getX(),e.getY())<=near)
{
figures.add(new MyPolygon2D(points,bgColor));
points.clear();
}
else
{
points.add(new Point2D.Double(e.getX(),e.getY()));
}
break;
case "Edycja":
points.clear();
}
repaint();
}
#Override
public void mouseDragged(MouseEvent e)
{
if(index>=0 && radio.getChoice().equals("Edycja"))
{
if (figures.get(index) instanceof MyEllipse2D)
{
((MyEllipse2D) figures.get(index)).move(new Point2D.Double(e.getX(),e.getY()));
}
else if (figures.get(index) instanceof MyRectangle2D)
{
((MyRectangle2D) figures.get(index)).move(new Point2D.Double(e.getX(),e.getY()));
}
else if(figures.get(index) instanceof MyPolygon2D)
{
((MyPolygon2D) figures.get(index)).move(new Point2D.Double(e.getX(),e.getY()));
}
repaint();
}
}
#Override
public void mouseWheelMoved(MouseWheelEvent e)
{
index = isSelected(e);
if(radio.getChoice().equals("Edycja"))
{
if (index>=0)
{
if (figures.get(index) instanceof MyEllipse2D)
{
((MyEllipse2D) figures.get(index)).scale(e.getPreciseWheelRotation(), scrollSpeed);
}
else if (figures.get(index) instanceof MyRectangle2D)
{
((MyRectangle2D) figures.get(index)).scale(e.getPreciseWheelRotation(), scrollSpeed);
}
else if(figures.get(index) instanceof MyPolygon2D)
{
((MyPolygon2D) figures.get(index)).scale(e.getPreciseWheelRotation(), scrollSpeed);
}
repaint();
}
}
}
private int isSelected(MouseEvent e)
{
int i;
for(i=figures.size()-1;i>=0;--i)
{
if(figures.get(i).contains(e.getPoint()))
{
return i;
}
}
return -1;
}
#Override
public void mouseClicked(MouseEvent e)
{
index = isSelected(e);
if(index >= 0 )
{
colorMenu.doPop(e);
if(e.getButton()==MouseEvent.BUTTON3 && radio.getChoice().equals("Edycja"))
{
colorMenu.doPop(e);
if(figures.get(index) instanceof MyEllipse2D)
((MyEllipse2D) figures.get(index)).color = colorMenu.color;
else if(figures.get(index) instanceof MyRectangle2D)
((MyRectangle2D) figures.get(index)).color = colorMenu.color;
else if(figures.get(index) instanceof MyPolygon2D)
((MyPolygon2D) figures.get(index)).color = colorMenu.color;
System.out.println(colorMenu.color);
//colorMenu.color = bgColor;
}
repaint();
}
}
#Override
public void mouseEntered(MouseEvent e)
{
}
#Override
public void mouseExited(MouseEvent e)
{
}
#Override
public void mouseMoved(MouseEvent e)
{
}
}
ColorMenu
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
public class ColorMenu extends JPopupMenu implements ActionListener
{
private ArrayList<JMenuItem> items = new ArrayList<JMenuItem>();
private ArrayList<Color> colors = new ArrayList<Color>();
public Color color;
public ColorMenu()
{
super();
colors.add(Color.black);
colors.add(Color.blue);
colors.add(Color.cyan);
colors.add(Color.gray);
colors.add(Color.green);
colors.add(Color.magenta);
colors.add(Color.orange);
colors.add(Color.red);
colors.add(Color.yellow);
colors.add(Color.white);
for (Color c : colors)
{
items.add(new JMenuItem(c.toString()));
}
for(JMenuItem i: items)
{
i.addActionListener(this);
add(i);
}
}
public void doPop(MouseEvent e)
{
show(e.getComponent(), e.getX(), e.getY());
}
#Override
public void actionPerformed(ActionEvent e)
{
Object source = e.getSource();
int j=0;
for(JMenuItem i: items)
{
if(i == source)
{
break;
}
++j;
}
this.color = colors.get(j);
}
}
MyRectangle2D
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
public class MyRectangle2D extends Rectangle2D.Double implements Shape
{
private Point2D.Double center;
private double width,hight;
public Color color;
public MyRectangle2D()
{}
public MyRectangle2D(Point2D.Double p1, Point2D.Double p2, Color color)
{
super();
this.color = color;
double x1 = p1.getX();
double y1 = p1.getY();
double x2 = p2.getX();
double y2 = p2.getY();
if(x1<=x2 && y1>=y2)
{
width=x2-x1;
hight=y1-y2;
setRect(x1,y2,width,hight);
}
else if(x1<=x2 && y1<=y2)
{
width=x2-x1;
hight=y2-y1;
setRect(x1,y1,width,hight);
}
else if (x1>=x2 && y1<=y2)
{
width=x1-x2;
hight=y2-y1;
setRect(x2,y1,width,hight);
}
else if(x1>=x2 && y1>=y2)
{
width=x1-x2;
hight=y1-y2;
setRect(x2,y2,width,hight);
}
center = new Point2D.Double(x1 + (x2-x1)/2,y1+(y2-y1)/2);
}
public void scale(double amount, double scale)
{
double change = -1*amount*scale;
width += change;
hight += change;
setRect(center.getX()-width/2,center.getY()-hight/2,width,hight);
}
public void move (Point2D.Double p)
{
center = p;
setRect();
}
private void setRect()
{
setRect(center.getX()-width/2,center.getY()-hight/2,width,hight);
}
}
MyPolygon2D
import java.awt.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
public class MyPolygon2D extends Polygon implements Shape
{
private ArrayList<MyVector> vectors = new ArrayList<>();
private Point2D.Double center;
private int size;
public Color color;
public MyPolygon2D()
{}
public MyPolygon2D(ArrayList<Point2D.Double> points, Color color)
{
super();
this.color = color;
size = points.size();
for(int i=0; i<size;++i)
{
addPoint((int)points.get(i).getX(),(int)points.get(i).getY());
}
center();
setVectors();
}
public void scale(double amount, double scale)
{
double change = -1*amount*scale;
for (int i=0;i<size;++i)
{
vectors.get(i).x *= (100.0+change)/100.0;
vectors.get(i).y *= (100.0+change)/100.0;
Point2D.Double curr = new Point2D.Double(center.getX()+vectors.get(i).x,center.getY()+vectors.get(i).y);
xpoints[i] = (int)curr.getX();
ypoints[i] = (int)curr.getY();
}
invalidate();
}
public void move (Point2D.Double p)
{
MyVector change = new MyVector(center,p);
center = p;
for(int i=0;i<size;++i)
{
xpoints[i] += (int)change.x;
ypoints[i] += (int)change.y;
}
invalidate();
}
public void setColor(Color color)
{
this.color = color;
}
public Color getColor()
{
return this.color;
}
#Override
public boolean contains(Point p)
{
int maxx=0, maxy=0, minx=Integer.MAX_VALUE, miny=Integer.MAX_VALUE;
for (int i=0;i<size;++i)
{
if(xpoints[i]>=maxx)
maxx = xpoints[i];
if(xpoints[i]<=minx)
minx = xpoints[i];
if(ypoints[i]>=maxy)
maxy = ypoints[i];
if(ypoints[i]<=miny)
miny = ypoints[i];
}
if(p.getX() <= maxx && p.getX() >= minx && p.getY() <= maxy && p.getY() >=miny)
return true;
else
return false;
}
private void setVectors()
{
for(int i=0; i<size;++i)
{
vectors.add(new MyVector(center,new Point2D.Double(xpoints[i],ypoints[i])));
}
}
private void center()
{
center = new Point2D.Double(getBounds2D().getX()+getBounds2D().getWidth()/2,getBounds2D().getY()+getBounds2D().getHeight()/2);
}
private class MyVector
{
public double x, y;
public MyVector(Point2D.Double p1, Point2D.Double p2)
{
x=p2.getX()-p1.getX();
y=p2.getY() - p1.getY();
}
}
}
MyEllipse2D
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
public class MyEllipse2D extends Ellipse2D.Double implements Shape
{
private double radius;
private Point2D.Double center;
public Color color;
public MyEllipse2D(Point2D.Double p1, Point2D.Double p2, Color color)
{
super();
this.color = color;
center = p1;
radius = (p1.distance(p2));
setFrame();
}
public void scale(double amount, double scale)
{
double change = -1*amount*scale;
radius += change;
setFrame();
}
public void move (Point2D.Double p)
{
center = p;
setFrame();
}
public void setColor(Color color)
{
this.color = color;
System.out.println(this.color);
}
public Color getColor()
{
return this.color;
}
private void setFrame()
{
setFrame(center.getX()-radius,center.getY()-radius,2*radius,2*radius);
}
}
RadioMenu
import javax.swing.*;
import java.awt.*;
public class RadioMenu extends JPanel
{
private int amount = 4;
private JRadioButton[] options = new JRadioButton[amount];
private ButtonGroup group = new ButtonGroup();
private String[] names = {"Okrag","Prostokat","Wielokat","Edycja"};
private Font font = new Font("Times New Roman",Font.BOLD,16);
public RadioMenu()
{
super();
setLayout(new GridLayout(1,amount));
for(int i=0;i<amount;++i)
{
if(i!=0)
options[i] = new JRadioButton(names[i],false);
else
options[i] = new JRadioButton(names[i],true);
group.add(options[i]);
add(options[i]);
options[i].setFont(font);
}
}
public String getChoice()
{
for(int i=0; i<amount; ++i)
{
if(options[i].isSelected())
return options[i].getText();
}
return "";
}
}
Frame
import javax.swing.*;
import java.awt.*;
public class Frame extends JFrame
{
private Dimension prefsize = new Dimension(800,600);
private Dimension minSize = new Dimension(400,200);
private Menu menu = new Menu();
private PaintPanel panel = new PaintPanel();
public Frame()
{
super();
setVisible(true);
setPreferredSize(prefsize);
setMinimumSize(minSize);
setLayout(new BorderLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(panel,BorderLayout.CENTER);
JPanel upper = new JPanel();
upper.setLayout(new GridLayout(2,1));
upper.add(menu);
upper.add(panel.radio);
add(upper,BorderLayout.NORTH);
pack();
}
}
MyAplet
import javax.swing.*;
public class MyAplet extends JApplet
{
public void init()
{
Frame main = new Frame();
}
}
Menu
import javax.swing.*;
public class Menu extends JMenuBar
{
private JMenu info;
//private JMenuItem x;
public Menu()
{
super();
info = new JMenu("info");
//info.add(x);
add(info);
}
}
First, you should be displaying a JPopupMenu in response to only one type of event. You are currently calling colorMenu.doPop(e) in the mousePressed method and twice in the mouseClicked method.
To make sure you only display a JPopupMenu when you’re supposed to, you should use the JPopupMenu.isPopupTrigger method. This way, the menu will be displayed on mousePress or mouseClick, but never both. You do not want to display a JPopupMenu more than once in response to a single mouse action!
private int selectedIndex = -1;
// ...
#Override
public void mousePressed(MouseEvent e)
{
selectedIndex = isSelected(e);
if (colorMenu.isPopupTrigger(e)) {
colorMenu.doPop(e);
} else if (e.getButton() == MouseEvent.BUTTON1) {
// ...
#Override
public void mouseClicked(MouseEvent e)
{
if (selectedIndex >= 0 && colorMenu.isPopupTrigger(e) && radio.getChoice().equals("Edycja"))
{
colorMenu.doPop(e);
The next problem is that you’re trying to read colorMenu.color before the user has made a color selection. When you call colorMenu.doPopup(e), the menu is displayed and the method returns immediately. It does not wait for the user to make a selection and dismiss the menu. Your code is trying to immediately make use of colorMenu.color, but it hasn’t been set yet.
The only way to know when the user has selected a color is with an ActionListener. Your ColorMenu class has an ActionListener, but currently there is no way for the PaintPanel class to know when the ActionListener has been triggered.
You can give your ColorMenu class the capability of notifying listeners in other classes, using the inherited listenerList object:
public void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
public void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
public ChangeListener[] getChangeListeners() {
return listenerList.getListeners(ChangeListener.class);
}
protected void fireChangeListeners() {
ChangeEvent event = new ChangeEvent(this);
for (ChangeListener listener : getChangeListeners()) {
listener.stateChanged(event);
}
}
(ChangeListener and ChangeEvent are in the javax.swing.event package.)
This allows other classes to listen for a user selection. You want to change ColorMenu to actually notify those listeners:
#Override
public void actionPerformed(ActionEvent e)
{
// (other code) ...
this.color = colors.get(j);
fireChangeListeners();
}
Now, you can make PaintPanel listen for a user selection:
public PaintPanel()
{
super();
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setLayout(new BorderLayout());
setBackground(bgColor);
colorMenu.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent event) {
if (selectedIndex >= 0)
{
Shape figure = figures.get(selectedIndex);
if (figure instanceof MyEllipse2D)
((MyEllipse2D) figure).color = colorMenu.color;
else if (figure instanceof MyRectangle2D)
((MyRectangle2D) figure).color = colorMenu.color;
else if (figure instanceof MyPolygon2D)
((MyPolygon2D) figure).color = colorMenu.color;
}
}
});
}
You should remove that color changing code from mouseClicked, as it will never be valid to call it there, since the user hasn’t selected a color yet:
#Override
public void mouseClicked(MouseEvent e)
{
if (selectedIndex >= 0 && colorMenu.isPopupTrigger(e) && radio.getChoice().equals("Edycja"))
{
colorMenu.doPop(e);
// Nothing else to do here, since the user has not selected a color yet.
}
}
The basic problem is, you need to know when the color is selected from the menu. The solution to the problem can be achieved in a number of different ways.
You could continue to use a MouseListener to monitor the mousePressed, mouseReleased and mouseClicked events, but you should be making using of the MouseEvent#isPopupTrigger property to determine when the popup should be displayed, as the triggers are different for different platforms.
Another solution would be to use JComponent#setComponentPopupMenu all the component to take care of the action itself. This, however, would require a slight change in design.
This approach places the onus back onto the component, rather then been separated into a different class, like you have the ColorMenu right now.
To start with, we need to know the selected shape, so, to the PaintPanel, we need to add a new property
private Shape selectedShape;
All this does is determines which shape was selected by the user.
Next we need someway to update the shape color. This can be achieved by simply adding a new method to the paintPanel method, which is called when the color is to be changed.
public void setShapeColor(Color color) {
//...
}
Next, we take advantage of the Action API and create a simple ColorAction.
This is self contained unit of work, which contains the information need to display it on a menu (or button) and which determines the actions to take when triggered.
public class ColorAction extends AbstractAction {
private Color color;
public ColorAction(String name, Color color) {
super(name);
this.color = color;
}
public Color getColor() {
return color;
}
#Override
public boolean isEnabled() {
return getSelectedShape() != null;
}
#Override
public void actionPerformed(ActionEvent e) {
setShapeColor(color);
}
}
In this case, this simple inner class to PaintPanel will call setShapeColor when triggered.
"But wait", I hear you call, "The colors can only be selected when a shape is selected!" Ah, we can control the Action through the isEnabled method, which will only return true when a shape is actually selected.
This might seem somewhat counterintuitive, but by not displaying a popup menu when nothing is displayed, it causes the user to think that no popup menu will be displayed ever, at least this way, we can display a popup menu with it's options disabled.
The next trick is trying to determine what is selected. Unfortunately, during my testing, the OS consumed the mousePressed event, which would have been most useful to make this determination, however, not all is lost.
The JComponent will call getPopupLocation just before the popup is made visible, passing in the MouseEvent which triggered it, we can take advantage of this to determine which shape would be selected
#Override
public Point getPopupLocation(MouseEvent event) {
selectedShape = null;
for (int i = figures.size() - 1; i >= 0; --i) {
if (figures.get(i).contains(event.getPoint())) {
selectedShape = figures.get(i);
}
}
return super.getPopupLocation(event);
}
For a more complete example...
public class PaintPanel extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener {
private ArrayList<Point2D.Double> points = new ArrayList<>();
private ArrayList<Shape> figures = new ArrayList<>();
private Shape selectedShape;
private Color mainColor = Color.blue;
private Color bgColor = Color.white;
private Color special = Color.red;
private double scrollSpeed = 5;
private int pointsize = 4;
private int near = 15;
private int index;
public PaintPanel() {
super();
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setLayout(new BorderLayout());
setBackground(bgColor);
setComponentPopupMenu(makePopupMenu());
}
protected JPopupMenu makePopupMenu() {
JPopupMenu menu = new JPopupMenu();
menu.add(new ColorAction("Black", Color.black));
menu.add(new ColorAction("Blue", Color.blue));
menu.add(new ColorAction("Cyan", Color.cyan));
menu.add(new ColorAction("Grey", Color.gray));
menu.add(new ColorAction("Green", Color.green));
menu.add(new ColorAction("Megenta", Color.magenta));
menu.add(new ColorAction("Orange", Color.orange));
menu.add(new ColorAction("Red", Color.red));
menu.add(new ColorAction("Yellow", Color.yellow));
menu.add(new ColorAction("White", Color.white));
return menu;
}
public Shape getSelectedShape() {
return selectedShape;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public Point getPopupLocation(MouseEvent event) {
selectedShape = null;
for (int i = figures.size() - 1; i >= 0; --i) {
if (figures.get(i).contains(event.getPoint())) {
selectedShape = figures.get(i);
}
}
return super.getPopupLocation(event);
}
#Override
public void mouseClicked(MouseEvent e) {
//...
}
#Override
public void mousePressed(MouseEvent e) {
//...
}
#Override
public void mouseReleased(MouseEvent e) {
//...
}
#Override
public void mouseEntered(MouseEvent e) {
//...
}
#Override
public void mouseExited(MouseEvent e) {
//...
}
#Override
public void mouseDragged(MouseEvent e) {
//...
}
#Override
public void mouseMoved(MouseEvent e) {
//...
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
//...
}
public void setShapeColor(Color color) {
//...
}
public class ColorAction extends AbstractAction {
private Color color;
public ColorAction(String name, Color color) {
super(name);
this.color = color;
}
public Color getColor() {
return color;
}
#Override
public boolean isEnabled() {
return getSelectedShape() != null;
}
#Override
public void actionPerformed(ActionEvent e) {
setShapeColor(color);
}
}
}
There are a couple of other ways you might achieve this, but I'd be using these basic principles are the cornerstone of the design.
For example, you could have a "color model" which get's passed to the popup menu class, which would be updated when a color is selected

KeyAdapter input in java not working

I just wrote some code to make my player move in my little maze game, but nothing happens. Also my maze is not drawn correct as in the matrix input. I don't figure out why is wrong this code...any help is well appeciated.
Thank you!
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import javax.swing.*;
public class Main extends JPanel {
private static Image white;
private static Image black;
private static Image finish;
private static Image player;
private static int x = 1;
private static int y = 1;
private String matrix[][];
public Main() {
addKeyListener(new Keys());
setFocusable(true);
}
public static String[][] load(String input) {
List<String[]> rows = new ArrayList<>();
try (Scanner scanner = new Scanner(input)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String[] cols = new String[line.length()];
for (int i = 0; i < cols.length; i++) {
cols[i] = line.substring(i, i + 1);
}
rows.add(cols);
}
}
return rows.toArray(new String[rows.size()][]);
}
public static JFrame buildFrame() {
JFrame frame = new JFrame("Labyrinth Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(900, 950);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
return frame;
}
public void moveUp() {
x += 0;
y += -1;
}
public void moveLeft() {
x += -1;
y += 0;
}
public void moveDown() {
x += 0;
y += 1;
}
public void moveRight() {
x += 1;
y += 0;
}
public class Keys extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();
// repaint();
if (keycode == KeyEvent.VK_W) {
if (!matrix[getX()][getY() - 1].equals("1")) {
moveUp();
}
}
if (keycode == KeyEvent.VK_A) {
if (!matrix[getX() - 1][getY()].equals("1")) {
moveLeft();
}
}
if (keycode == KeyEvent.VK_S) {
if (!matrix[getX()][getY() + 1].equals("1")) {
moveDown();
}
}
if (keycode == KeyEvent.VK_D) {
if (!matrix[getX() + 1][getY()].equals("1")) {
moveRight();
}
}
}
#Override
public void keyReleased(KeyEvent event) {
}
}
public static void main(String[] args) {
String input = "1111111111111111111111111111111111111111111\n"
+ "1000000010001000001000000010000000100000001\n"
+ "1010111010101010101111101011111010111111101\n"
+ "1010001010100010100000001010000010000010001\n"
+ "1011101010111110101111111010111111111010111\n"
+ "1000101010100000101000001000100010000010001\n"
+ "1011101011101011111011101111111010111110101\n"
+ "1010001000001010000010100000001010000010101\n"
+ "1010111111111010111110111111101011111011101\n"
+ "1010100000100010100000000000101000000000101\n"
+ "1110101111101110111110111011101011111110101\n"
+ "1000100000000010000010100010001000100010001\n"
+ "1011111111111111111011101010111111101011101\n"
+ "1000000000000000100010001010000000001010001\n"
+ "1011111111111011101110111011111111111010111\n"
+ "1000100010001000001010001000100000001010101\n"
+ "1110101011101111111010101110111110111010101\n"
+ "1000101010001000100000101000100000100010001\n"
+ "1011101010111010101111101011101110101111111\n"
+ "1000001010000010000000101000001000100010001\n"
+ "1111111011111110111111101111111011111010101\n"
+ "1000001010000010100010001000000010000010101\n"
+ "1011111010111011101010111011111110101110101\n"
+ "1010000010001010001010001000100000101010101\n"
+ "1010111111101010111011101111101111101011101\n"
+ "1000100000001010101010001000100010101000101\n"
+ "1011111011111010101010111010111010101011101\n"
+ "1010000010001000101010000010001010001000001\n"
+ "1010101110101111101011101111101011111010101\n"
+ "1010101000101000001000101000001000000010101\n"
+ "1011101011111010111110111011101111111110111\n"
+ "1000001000000010000000000010000000000010021\n"
+ "1111111111111111111111111111111111111111111\n";
String[][] matrix = load(input);
JFrame frame = buildFrame();
ImageIcon img = new ImageIcon("C:/Users/Desktop/black20.png");
black = img.getImage();
img = new ImageIcon("C:/Users/Desktop/gri20.png");
white = img.getImage();
img = new ImageIcon("C:/Users/Desktop/finish20.png");
finish = img.getImage();
img = new ImageIcon("C:/Users/Desktop/smiley20.png");
player = img.getImage();
// frame.add(new Player());
JPanel pane = new JPanel() {
#Override
public void paint(Graphics g) {
super.paint(g);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (matrix[i][j].equals("1")) {
g.drawImage(black, i * 20, j * 20, null);
}
if (matrix[i][j].equals("0")) {
g.drawImage(white, i * 20, j * 20, null);
}
if (matrix[i][j].equals("2")) {
g.drawImage(finish, i * 20, j * 20, null);
}
}
}
g.drawImage(player, x * 20, y * 20, null);
}
};
frame.add(pane);
frame.add(new Main());
}
}
It should look like:
Problems/Suggestions:
You're adding more than one component to the JFrame in a default fashion. Since the JFrame's contentPane uses BorderLayout, only one component will display, the last one added, and the other will be completely covered and will remain invisible.
Your Main JPanel is not being used as a true JPanel. Nothing is being added to it, and in fact it looks like it should be a logical class and not a component class.
Rather than using KeyListeners, which are very fidgety when it comes to focus problems, use Key Bindings which will allow you to get around focus issues in a clean and higher level way.
Don't override the JPanel's paint method but rather its paintComponent method as this will give you several advantages, including default use of double buffering for your animation.
Don't give component classes a public int getX() and public int getY() method without care since these override key methods that place the component within its container. Since the Main class shouldn't even extend JPanel, this will end up to be a non-issue for your code, but in the future, it would mess your program up.
When you run into similar problems, such as a KeyListener not working, remove the issue from your big program and try to reproduce it in a small separate program. This will give you a much cleaner environment for helping you to isolate and understand your problem and thereby help you fix it.
Your program is mis-using the static modifier. Your x and y fields should not be static and should not be accessed in a static way.
You've got way too much code within your main method, which is one of the reasons why you likely made x and y static, because you were forced to do so since you're trying to access them within main. The solution is not to make the fields static but to get all that code out of the static world and into the instance world.
Edit
For some reason the question intrigued me, and so I decided to try to M-V-C or Model-View-Controller it if possible and see what I could come up with. So here goes a bunch of classes that sort of work, beginning with a text file that holds the data.
The GUI looks like:
It must be in the same location as the class files since it is obtained as a resource and must have the file name "input.txt"
1111111111111111111111111111111111111111111
1000000010001000001000000010000000100000001
1010111010101010101111101011111010111111101
1010001010100010100000001010000010000010001
1011101010111110101111111010111111111010111
1000101010100000101000001000100010000010001
1011101011101011111011101111111010111110101
1010001000001010000010100000001010000010101
1010111111111010111110111111101011111011101
1010100000100010100000000000101000000000101
1110101111101110111110111011101011111110101
1000100000000010000010100010001000100010001
1011111111111111111011101010111111101011101
1000000000000000100010001010000000001010001
1011111111111011101110111011111111111010111
1000100010001000001010001000100000001010101
1110101011101111111010101110111110111010101
1000101010001000100000101000100000100010001
1011101010111010101111101011101110101111111
1000001010000010000000101000001000100010001
1111111011111110111111101111111011111010101
1000001010000010100010001000000010000010101
1011111010111011101010111011111110101110101
1010000010001010001010001000100000101010101
1010111111101010111011101111101111101011101
1000100000001010101010001000100010101000101
1011111011111010101010111010111010101011101
1010000010001000101010000010001010001000001
1010101110101111101011101111101011111010101
1010101000101000001000101000001000000010101
1011101011111010111110111011101111111110111
1000001000000010000000000010000000000010021
1111111111111111111111111111111111111111111
Next the main program, the one that creates the model, the view, and the controller, hooks them all together, and then displays the GUI:
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**
* link: http://stackoverflow.com/a/41418250/522444
*
* #author Pete
*
*/
#SuppressWarnings("serial")
public class Main2 extends JPanel {
private View mainPanel;
public Main2(MatrixModel matrixModel) {
mainPanel = new View(matrixModel);
new Controller(matrixModel, mainPanel);
setLayout(new BorderLayout());
add(mainPanel, BorderLayout.CENTER);
}
private static void createAndShowGui(MatrixModel model) {
Main2 mainPanel = new Main2(model);
JFrame frame = new JFrame("Main2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
final MatrixModel model = MatrixUtil.getInput(MatrixUtil.PATH_TO_RSC);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui(model);
}
});
}
}
Next a utility class with static methods for reading in the text file as a resource and converting it into a Model object:
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class MatrixUtil {
// again this text file must be in the jar file or the code base
// at the same location as the class / java files
public static final String PATH_TO_RSC = "input.txt";
public static MatrixModel getInput(String resourcePath) {
InputStream is = MatrixUtil.class.getResourceAsStream(resourcePath);
if (is == null) {
String text = "resourcePath is not found and not loading text: " + resourcePath;
throw new IllegalArgumentException(text);
}
return getInput(is);
}
public static MatrixModel getInput(InputStream is) {
MatrixModel model = null;
try (Scanner scan = new Scanner(is)) {
List<List<MatrixPosition>> listOfLists = new ArrayList<>();
while (scan.hasNextLine()) {
String line = scan.nextLine();
if (line.trim().isEmpty()) {
continue;
}
List<MatrixPosition> list = new ArrayList<>();
for (char c : line.toCharArray()) {
list.add(MatrixPosition.getMatrixPosition(String.valueOf(c)));
}
listOfLists.add(list);
}
MatrixPosition[][] grid = new MatrixPosition[listOfLists.size()][];
for (int i = 0; i < grid.length; i++) {
List<MatrixPosition> list = listOfLists.get(i);
grid[i] = list.toArray(new MatrixPosition[] {});
}
model = new MatrixModel(grid, new SpritePosition(1, 1));
}
return model;
}
}
Basic enum to represent direction:
public enum Direction {
UP, DOWN, LEFT, RIGHT
}
Another enum to represent a location point in the grid, and whether it is a wall, a coridor or the end as well as static methods to convert from number to MatrixPosition:
public enum MatrixPosition {
WALL(1), CORRIDOR(0), END(2);
private int value;
private MatrixPosition(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static MatrixPosition getMatrixPosition(int value) {
for (MatrixPosition position : MatrixPosition.values()) {
if (value == position.getValue()) {
return position;
}
}
String text = "value of " + value;
throw new IllegalArgumentException(text);
}
public static MatrixPosition getMatrixPosition(String strValue) {
int value = -1;
try {
value = Integer.parseInt(strValue);
} catch (NumberFormatException e) {
String text = "NumberFormatException for strValue " + strValue;
throw new IllegalAccessError(text);
}
return getMatrixPosition(value);
}
}
A class to represent a position of our sprite, similar to the java.awt.Point class but with row and column fields instead of x and y:
public class SpritePosition {
int row;
int column;
public SpritePosition(int row, int column) {
this.row = row;
this.column = column;
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public int getColumn() {
return column;
}
public void setColumn(int column) {
this.column = column;
}
public void setRowColumn(int row, int column) {
this.row = row;
this.column = column;
}
}
The model has property change support code so that it can notify any classes listening to it of any changes in its state. The controller will be the class listening to the model
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
public class MatrixModel {
public static final String SPRITE_POINT = "sprite point";
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
private MatrixPosition[][] grid;
private SpritePosition spritePosition;
public MatrixModel(MatrixPosition[][] grid, SpritePosition spritePosition) {
this.grid = grid;
this.spritePosition = spritePosition;
}
public int getRows() {
return grid.length;
}
public int getColumns() {
return grid[0].length;
}
public MatrixPosition getPosition(SpritePosition p) {
return getPosition(p.row, p.column);
}
public MatrixPosition getPosition(int row, int col) {
return grid[row][col];
}
public void setSpritePoint(SpritePosition spritePosition) {
SpritePosition oldValue = this.spritePosition;
SpritePosition newValue = spritePosition;
this.spritePosition = spritePosition;
pcSupport.firePropertyChange(SPRITE_POINT, oldValue, newValue);
}
public boolean isPointValid(SpritePosition p) {
if (p.column < 0 || p.row < 0) {
return false;
}
if (p.column >= grid[0].length || p.row >= grid.length) {
return false;
}
return grid[p.row][p.column] == MatrixPosition.CORRIDOR;
}
public boolean isMoveValid(Direction direction) {
int row = spritePosition.row;
int column = spritePosition.column;
switch (direction) {
case UP:
return isPointValid(new SpritePosition(row - 1, column));
case DOWN:
return isPointValid(new SpritePosition(row + 1, column));
case LEFT:
return isPointValid(new SpritePosition(row, column - 1));
case RIGHT:
return isPointValid(new SpritePosition(row, column + 1));
default:
return false;
}
}
public void move(Direction direction) {
if (!isMoveValid(direction)) {
String text = "For move to " + direction + "spritePosition: " + spritePosition;
throw new IllegalArgumentException(text);
}
int row = spritePosition.row;
int column = spritePosition.column;
switch (direction) {
case UP:
setSpritePoint(new SpritePosition(row - 1, column));
break;
case DOWN:
setSpritePoint(new SpritePosition(row + 1, column));
break;
case LEFT:
setSpritePoint(new SpritePosition(row, column - 1));
break;
case RIGHT:
setSpritePoint(new SpritePosition(row, column + 1));
break;
default:
break;
}
}
public SpritePosition getSpritePosition() {
return spritePosition;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(name, listener);
}
public void removePropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(name, listener);
}
}
Controller class, one that sets up key bindings on the view so that it can listen for key presses, checks if they represent a valid move, and if so then tells the model to make the move. It also adds a listener to the model so that when its state changes, it will tell the view to move the sprite
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
public class Controller {
private MatrixModel model;
private View view;
private Map<Direction, KeyStroke> dirKeyMap = new EnumMap<>(Direction.class);
public Controller(MatrixModel model, View view) {
this.model = model;
this.view = view;
dirKeyMap.put(Direction.DOWN, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0));
dirKeyMap.put(Direction.UP, KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0));
dirKeyMap.put(Direction.LEFT, KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0));
dirKeyMap.put(Direction.RIGHT, KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0));
model.addPropertyChangeListener(new ModelListener());
setUpKeyBindings(view);
}
private void setUpKeyBindings(View view) {
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = view.getInputMap(condition);
ActionMap actionMap = view.getActionMap();
for (Direction dir : Direction.values()) {
KeyStroke keyStroke = dirKeyMap.get(dir);
hookUp(inputMap, actionMap, dir, keyStroke);
}
}
private void hookUp(InputMap inputMap, ActionMap actionMap, Direction dir, KeyStroke key) {
inputMap.put(key, key.toString());
actionMap.put(key.toString(), new MoveAction(dir, model));
}
public MatrixModel getModel() {
return model;
}
public View getView() {
return view;
}
class ModelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (MatrixModel.SPRITE_POINT.equals(evt.getPropertyName())) {
SpritePosition p = model.getSpritePosition();
view.setSpritePoint(p);
}
}
}
}
#SuppressWarnings("serial")
class MoveAction extends AbstractAction {
private Direction dir;
private MatrixModel model;
public MoveAction(Direction dir, MatrixModel model) {
super(dir.toString());
this.dir = dir;
this.model = model;
}
public void actionPerformed(ActionEvent e) {
if (model.isMoveValid(dir)) {
model.move(dir);
}
}
}
Finally the View class, that extends JPanel, that displays the maze and the sprite:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class View extends JPanel {
private static final int CELL_WIDTH = 20;
private static final Color CORRIDOR_COLOR = Color.LIGHT_GRAY;
private static final Color WALL_COLOR = Color.DARK_GRAY;
private static final Color END_COLOR = Color.ORANGE;
private static final Color SPRITE_COLOR = Color.RED;
private static final int GAP = 1;
private BufferedImage gridImg = null;
private SpritePosition spritePosition;
private JPanel mainPanel = new JPanel();
public View(MatrixModel matrixModel) {
gridImg = createImg(matrixModel);
spritePosition = matrixModel.getSpritePosition();
}
public JPanel getMainPanel() {
return mainPanel;
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || gridImg == null) {
return super.getPreferredSize();
}
int prefW = gridImg.getWidth();
int prefH = gridImg.getHeight();
return new Dimension(prefW, prefH);
}
public void setSpritePoint(SpritePosition spritePosition) {
this.spritePosition = spritePosition;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (gridImg != null) {
g.drawImage(gridImg, 0, 0, this);
}
g.setColor(SPRITE_COLOR);
int y = spritePosition.row * CELL_WIDTH + GAP;
int x = spritePosition.column * CELL_WIDTH + GAP;
g.fillRect(x, y, CELL_WIDTH - 2 * GAP, CELL_WIDTH - 2 * GAP);
}
private BufferedImage createImg(MatrixModel matrixModel) {
BufferedImage img = null;
if (matrixModel != null && matrixModel.getRows() > 0) {
int w = matrixModel.getColumns() * CELL_WIDTH;
int h = matrixModel.getRows() * CELL_WIDTH;
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
for (int row = 0; row < matrixModel.getRows(); row++) {
for (int col = 0; col < matrixModel.getColumns(); col++) {
MatrixPosition position = matrixModel.getPosition(row, col);
Color c = null;
switch (position) {
case CORRIDOR:
c = CORRIDOR_COLOR;
break;
case WALL:
c = WALL_COLOR;
break;
case END:
c = END_COLOR;
break;
}
g2.setColor(c);
int x = col * CELL_WIDTH;
int y = row * CELL_WIDTH;
g2.fillRect(x, y, CELL_WIDTH, CELL_WIDTH);
}
}
g2.dispose();
}
return img;
}
}

MouseClick Event - Multiple Selection

Currently in this project, I'm able to select the specific grid and change it to a blue background with the SHIFT key being hold.
Right now, I would like to select multiple grids and assign new background color all at once.
How can I do so?
Source code:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.border.LineBorder;
//based on #trashgod code from http://stackoverflow.com/a/7706684/714968
public class FocusingPanel extends JFrame {
private static final long serialVersionUID = 1L;
private int elements = 10;
private List<GridPanel> list = new ArrayList<GridPanel>();
private final JFrame mainFrame = new JFrame();
private final JPanel fatherPanel = new JPanel();
public FocusingPanel() {
fatherPanel.setLayout(new GridLayout(elements, elements));
for (int i = 0; i < elements * elements; i++) {
int row = i / elements;
int col = i % elements;
GridPanel gb = new GridPanel(row, col);
list.add(gb);
fatherPanel.add(gb);
}
mainFrame.setLayout(new BorderLayout(5, 5));
mainFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
mainFrame.add(fatherPanel, BorderLayout.CENTER);
mainFrame.pack();
mainFrame.setVisible(true);
}
private GridPanel getGridPanel(int r, int c) {
int index = r * elements + c;
return list.get(index);
}
private class GridPanel extends JPanel {
private int row;
private int col;
#Override
public Dimension getPreferredSize() {
return new Dimension(20, 20);
}
public GridPanel(int row, int col) {
this.row = row;
this.col = col;
this.setBackground(Color.red);
this.setBorder(new LineBorder(Color.black,1));
this.addMouseListener(new MouseListener() {
//*#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
if(e.isShiftDown()){
setBackground(Color.blue);
}
}
#Override
public void mouseReleased(MouseEvent e) {
//setBackground(Color.green);
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
//setBackground(Color.red);
}
});
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
FocusingPanel focusingPanel = new FocusingPanel();
}
});
}
}

java graphics2d animated gif update

The applet plays an animated gif file:
public class Applet1 extends Applet {
private Image m_image=null;
public void init() {
m_image=getImage(getDocumentBase(), "001.gif");
}
public void paint(Graphics g) {
g.drawImage(m_image,0,0,this);
}
public boolean imageUpdate( Image img, int flags, int x, int y, int w, int h ) {
System.out.println("Image update: flags="+flags+" x="+x+" y="+y+" w="+w+" h="+h);
repaint();
return true;
}
}
I need to add the updated image in another program:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.*;
public class HighlightExample {
public static void main(String[] args) {
JFrame f = new JFrame("Highlight example");
final JTextPane textPane = new JTextPane();
JPanel pane = new JPanel();
pane.setLayout(new BorderLayout());
final JTextField tf = new JTextField();
pane.add(tf, "Center");
f.getContentPane().add(pane, "South");
f.getContentPane().add(new JScrollPane(textPane), "Center");
textPane.setText("abсdefghijkl lmnop12345678");
final WordSearcher searcher = new WordSearcher(textPane);
tf.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
word = tf.getText().trim();
int offset = searcher.search(word);
if (offset != -1) {
try {
textPane.scrollRectToVisible(textPane.modelToView(offset));
} catch (BadLocationException e) {}
}
}
});
textPane.getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent evt) {
searcher.search(word);
}
public void removeUpdate(DocumentEvent evt) {
searcher.search(word);
}
public void changedUpdate(DocumentEvent evt) {
}
});
f.setSize(400, 400);
f.setVisible(true);
}
public static String word;
}
class WordSearcher {
protected JTextComponent comp;
protected Highlighter.HighlightPainter painter;
public WordSearcher(JTextComponent comp) {
this.comp = comp;
this.painter = new UnderlineHighlighter.UnderlineHighlightPainter(Color.red);
}
public int search(String word) {
int firstOffset = -1;
Highlighter highlighter = comp.getHighlighter();
Highlighter.Highlight[] highlights = highlighter.getHighlights();
for (int i = 0; i < highlights.length; i++) {
Highlighter.Highlight h = highlights[i];
if (h.getPainter() instanceof UnderlineHighlighter.UnderlineHighlightPainter) {
highlighter.removeHighlight(h);
}
}
if (word == null || word.equals("")) {
return -1;
}
String content;
try {
Document d = comp.getDocument();
content = d.getText(0, d.getLength()).toLowerCase();
} catch (BadLocationException e) {
return -1;
}
word = word.toLowerCase();
int lastIndex = 0;
int wordSize = word.length();
while ((lastIndex = content.indexOf(word, lastIndex)) != -1) {
int endIndex = lastIndex + wordSize;
try {
highlighter.addHighlight(lastIndex, endIndex, painter);
} catch (BadLocationException e) {}
if (firstOffset == -1) {
firstOffset = lastIndex;
}
lastIndex = endIndex;
}
return firstOffset;
}
}
class UnderlineHighlighter extends DefaultHighlighter{
protected static final Highlighter.HighlightPainter sharedPainter = new UnderlineHighlightPainter(null);
protected Highlighter.HighlightPainter painter;
public static class UnderlineHighlightPainter extends LayeredHighlighter.LayerPainter {
protected Color color; // The color for the underline
public UnderlineHighlightPainter(Color c) {
color = c;
}
public void paint(final Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
}
public Shape paintLayer(final Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
g.setColor(color == null ? c.getSelectionColor() : color);
Rectangle alloc;
try {
Shape shape = view.modelToView(offs0,Position.Bias.Forward, offs1,Position.Bias.Backward, bounds);
alloc = (shape instanceof Rectangle) ? (Rectangle) shape : shape.getBounds();
} catch (BadLocationException e) {
return null;
}
FontMetrics fm = c.getFontMetrics(c.getFont());
int baseline = alloc.y + alloc.height - fm.getDescent() + 1;
g.drawLine(alloc.x, baseline, alloc.x + alloc.width, baseline);
Toolkit kit=Toolkit.getDefaultToolkit();
Image im3 =kit.getImage("001.gif");
g.drawImage(im3,alloc.x+15,alloc.y-6,null);
return alloc;
}
}
}
In this program always displays only the first frame of the image im3. Sorry for a lot of code, it is fully compillable.
It seems like the problem is that you are passing null as the ImageObserver when drawing the image (near the end of your code):
g.drawImage(im3,alloc.x+15,alloc.y-6,null);
So, the image will fire an update when a new frame is ready, but that update just gets ignored because there are no listeners. You probably want to pass c as the ImageObserver so that it will repaint itself when the image changes.
Edit:
This approach will c entirely. If you wanted to be clever, you could create your own ImageObserver that remembers the exact rectangle in c that the icon was painted in, and repaints just that rectangle when notified of an update.

Categories

Resources