JTable row selectable but cell unselectable - java

By default, a JTable is editable and when we select any row, we get this kind of a GUI
Here we have two borders, one around the entire row, and one around the specific cell using which we have selected the row. I need the outer border (around entire row) but don't want the inner (per cell) border. Is there a way to achieve this.

I guess, you would need to make a customized Border.
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
public class AroundEntireRowFocusTest {
String[] columnNames = {"A", "B", "C"};
Object[][] data = {
{"aaa", 12, "ddd"}, {"bbb", 5, "eee"}, {"CCC", 92, "fff"}
};
DefaultTableModel model = new DefaultTableModel(data, columnNames) {
#Override public Class<?> getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
};
public JComponent makeUI() {
UIManager.put("Table.focusCellHighlightBorder", new DotBorder(2, 2, 2, 2));
JTable table = new JTable(model) {
private final DotBorder dotBorder = new DotBorder(2, 2, 2, 2);
void updateBorderType(DotBorder border, boolean isLeadRow, int column) {
border.type = EnumSet.noneOf(DotBorder.Type.class);
if (isLeadRow) {
border.type.add(DotBorder.Type.LEAD);
if (column == 0) {
border.type.add(DotBorder.Type.WEST);
}
if (column == getColumnCount() - 1) {
border.type.add(DotBorder.Type.EAST);
}
}
}
#Override public Component prepareRenderer(
TableCellRenderer tcr, int row, int column) {
JComponent c = (JComponent) super.prepareRenderer(tcr, row, column);
c.setBorder(dotBorder);
updateBorderType(
dotBorder, row == getSelectionModel().getLeadSelectionIndex(), column);
return c;
}
};
table.setShowGrid(false);
table.setIntercellSpacing(new Dimension());
JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(table));
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new AroundEntireRowFocusTest().makeUI());
frame.setSize(320, 240);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class DotBorder extends EmptyBorder {
public enum Type { LEAD, WEST, EAST; }
public EnumSet<Type> type = EnumSet.noneOf(Type.class);
private static final BasicStroke dashed = new BasicStroke(
1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
10f, (new float[] {1f}), 0f);
private static final Color DOT_COLOR = new Color(200, 150, 150);
public DotBorder(int top, int left, int bottom, int right) {
super(top, left, bottom, right);
}
#Override public boolean isBorderOpaque() {
return true;
}
#Override public void paintBorder(
Component c, Graphics g, int x, int y, int w, int h) {
Graphics2D g2 = (Graphics2D) g.create();
g2.translate(x, y);
g2.setPaint(DOT_COLOR);
g2.setStroke(dashed);
if (type.contains(Type.WEST)) {
g2.drawLine(0, 0, 0, h);
}
if (type.contains(Type.EAST)) {
g2.drawLine(w - 1, 0, w - 1, h);
}
if (type.contains(Type.LEAD)) {
if (c.getBounds().x % 2 == 0) {
g2.drawLine(0, 0, w, 0);
g2.drawLine(0, h - 1, w, h - 1);
} else {
g2.drawLine(1, 0, w, 0);
g2.drawLine(1, h - 1, w, h - 1);
}
}
g2.dispose();
}
}

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.

Filter JTable with search word without losing background color

I have followed the tutorial to filter and highlight text in a JTable here.
The only thing I added is the LookAndFeel, which is set to Nimbus. The code works, except when I select a row, the back and foreground colors of the row are lost.
Without renderer:
With renderer:
In the code, the renderer creates a new label (LabelHighlighted extends JLabel) which overwrites the painComponent method. I guess somehow this method should take the background color of the row in the table.
#Override
protected void paintComponent(Graphics g) {
if (rectangles.size() > 0) {
Graphics2D g2d = (Graphics2D) g;
Color c = g2d.getColor();
for (Rectangle2D rectangle : rectangles) {
g2d.setColor(colorHighlight);
g2d.fill(rectangle);
g2d.setColor(Color.LIGHT_GRAY);
g2d.draw(rectangle);
}
g2d.setColor(c);
}
super.paintComponent(g);
}
Note: I know the JXTable variant of JTable has some more options for filtering and highlighting rows but I did not find a solution yet...
Renderer:
public class RendererHighlighted extends DefaultTableCellRenderer {
private JTextField searchField;
public RendererHighlighted(JTextField searchField) {
this.searchField = searchField;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean selected, boolean hasFocus,
int row, int column) {
Component c = super.getTableCellRendererComponent(table, value, selected, hasFocus, row, column);
JLabel original = (JLabel) c;
LabelHighlighted label = new LabelHighlighted();
label.setFont(original.getFont());
label.setText(original.getText());
label.setBackground(original.getForeground());
label.setForeground(original.getForeground());
label.setHorizontalTextPosition(original.getHorizontalTextPosition());
label.highlightText(searchField.getText());
return label;
}
}
LabelHighlighted:
public class LabelHighlighted extends JLabel {
private List<Rectangle2D> rectangles = new ArrayList<>();
private Color colorHighlight = Color.YELLOW;
public void reset() {
rectangles.clear();
repaint();
}
public void highlightText(String textToHighlight) {
if (textToHighlight == null) {
return;
}
reset();
final String textToMatch = textToHighlight.toLowerCase().trim();
if (textToMatch.length() == 0) {
return;
}
textToHighlight = textToHighlight.trim();
final String labelText = getText().toLowerCase();
if (labelText.contains(textToMatch)) {
FontMetrics fm = getFontMetrics(getFont());
float w = -1;
final float h = fm.getHeight() - 1;
int i = 0;
while (true) {
i = labelText.indexOf(textToMatch, i);
if (i == -1) {
break;
}
if (w == -1) {
String matchingText = getText().substring(i,
i + textToHighlight.length());
w = fm.stringWidth(matchingText);
}
String preText = getText().substring(0, i);
float x = fm.stringWidth(preText);
rectangles.add(new Rectangle2D.Float(x, 1, w, h));
i = i + textToMatch.length();
}
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
if (rectangles.size() > 0) {
Graphics2D g2d = (Graphics2D) g;
Color c = g2d.getColor();
for (Rectangle2D rectangle : rectangles) {
g2d.setColor(colorHighlight);
g2d.fill(rectangle);
g2d.setColor(Color.LIGHT_GRAY);
g2d.draw(rectangle);
}
g2d.setColor(c);
}
super.paintComponent(g);
}
}
It might be easy to use HTML tags for JLabel.
<span style='color:#000000; background-color:#FFFF00'>%s</span>
import java.awt.*;
import java.util.Objects;
import java.util.regex.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
class RendererHighlighted extends DefaultTableCellRenderer {
private final JTextField searchField;
public RendererHighlighted(JTextField searchField) {
this.searchField = searchField;
}
#Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
String txt = Objects.toString(value, "");
String pattern = searchField.getText();
if (Objects.nonNull(pattern) && !pattern.isEmpty()) {
Matcher matcher = Pattern.compile(pattern).matcher(txt);
int pos = 0;
StringBuilder buf = new StringBuilder("<html>");
while (matcher.find(pos)) {
int start = matcher.start();
int end = matcher.end();
buf.append(String.format(
"%s<span style='color:#000000; background-color:#FFFF00'>%s</span>",
txt.substring(pos, start), txt.substring(start, end)));
pos = end;
}
buf.append(txt.substring(pos));
txt = buf.toString();
}
super.getTableCellRendererComponent(table, txt, isSelected, hasFocus, row, column);
return this;
}
}
public class HtmlHighlightCellTest {
public JComponent makeUI() {
String[] columnNames = {"A", "B"};
Object[][] data = {
{"aaa", "bbaacc"}, {"bbb", "defg"},
{"ccccbbbbaaabbbbaaeabee", "xxx"}, {"dddaaabbbbb", "ccbbaa"},
{"cc cc bbbb aaa bbbb e", "xxx"}, {"ddd aaa b bbbb", "cc bbaa"}
};
TableModel model = new DefaultTableModel(data, columnNames) {
#Override public boolean isCellEditable(int row, int column) {
return false;
}
#Override public Class<?> getColumnClass(int column) {
return String.class;
}
};
JTable table = new JTable(model);
table.setFillsViewportHeight(true);
TableRowSorter<? extends TableModel> sorter = new TableRowSorter<>(model);
table.setRowSorter(sorter);
JTextField field = new JTextField();
RendererHighlighted renderer = new RendererHighlighted(field);
table.setDefaultRenderer(String.class, renderer);
field.getDocument().addDocumentListener(new DocumentListener() {
#Override public void insertUpdate(DocumentEvent e) {
update();
}
#Override public void removeUpdate(DocumentEvent e) {
update();
}
#Override public void changedUpdate(DocumentEvent e) {}
private void update() {
String pattern = field.getText().trim();
if (pattern.isEmpty()) {
sorter.setRowFilter(null);
} else {
sorter.setRowFilter(RowFilter.regexFilter("(?i)" + pattern));
}
}
});
JPanel sp = new JPanel(new BorderLayout(2, 2));
sp.add(new JLabel("regex pattern:"), BorderLayout.WEST);
sp.add(field);
sp.add(Box.createVerticalStrut(2), BorderLayout.SOUTH);
sp.setBorder(BorderFactory.createTitledBorder("Search"));
JPanel p = new JPanel(new BorderLayout(2, 2));
p.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
p.add(sp, BorderLayout.NORTH);
p.add(new JScrollPane(table));
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception ex) {
ex.printStackTrace();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new HtmlHighlightCellTest().makeUI());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}

JTabbedPane custom component layout

I'm looking at this example Using Swing Components; Examples. This example shows JTabbedPane with custom tab component which has close button in each tab.
If JTabbedPane is in WRAP_TAB_LAYOUT the tab title and the close button is in the middle of the wide tab. How to change this example that the tab title still would be visible in the center of the tab, but the close button would appear next to right tab border?
Here is a picture:
Here is one possible implementation using UIManager.put("TabbedPane.tabInsets", insets) and JLayer
Paint a close button on the inner margin of the tab using the JLayer
I didn't test on NumbusLookAndFeel
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.plaf.*;
public class RightCloseTabButtonTest {
public JComponent makeUI() {
JTabbedPane tabs = new JTabbedPane();
tabs.addTab("Tab 1", new JLabel("aaa"));
tabs.addTab("Tab 2", new JLabel("bbb"));
tabs.addTab("Tab 3", new JLabel("ccc"));
tabs.addTab("Tab 4", new JLabel("ddd"));
return new JLayer<JTabbedPane>(tabs, new CloseableTabbedPaneLayerUI());
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
UIManager.put("TabbedPane.tabInsets", new Insets(2, 2 + 16, 2, 2 + 16));
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new RightCloseTabButtonTest().makeUI());
f.setSize(300, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class CloseTabIcon implements Icon {
#Override public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D) g.create();
g2.translate(x, y);
g2.setPaint(Color.BLACK);
if (c instanceof AbstractButton) {
ButtonModel m = ((AbstractButton) c).getModel();
if (m.isRollover()) {
g2.setPaint(Color.ORANGE);
}
}
g2.drawLine(4, 4, 11, 11);
g2.drawLine(4, 5, 10, 11);
g2.drawLine(5, 4, 11, 10);
g2.drawLine(11, 4, 4, 11);
g2.drawLine(11, 5, 5, 11);
g2.drawLine(10, 4, 4, 10);
g2.dispose();
}
#Override public int getIconWidth() {
return 16;
}
#Override public int getIconHeight() {
return 16;
}
}
class CloseableTabbedPaneLayerUI extends LayerUI<JTabbedPane> {
private final JComponent rubberStamp = new JPanel();
private final Point pt = new Point();
private final JButton button = new JButton(new CloseTabIcon()) {
#Override public void updateUI() {
super.updateUI();
setBorder(BorderFactory.createEmptyBorder());
setFocusPainted(false);
setBorderPainted(false);
setContentAreaFilled(false);
setRolloverEnabled(false);
}
};
#Override public void paint(Graphics g, JComponent c) {
super.paint(g, c);
if (c instanceof JLayer) {
JLayer jlayer = (JLayer) c;
JTabbedPane tabPane = (JTabbedPane) jlayer.getView();
Dimension d = button.getPreferredSize();
for (int i = 0; i < tabPane.getTabCount(); i++) {
Rectangle rect = tabPane.getBoundsAt(i);
int x = rect.x + rect.width - d.width - 2;
int y = rect.y + (rect.height - d.height) / 2;
Rectangle r = new Rectangle(x, y, d.width, d.height);
button.getModel().setRollover(r.contains(pt));
SwingUtilities.paintComponent(g, button, rubberStamp, r);
}
}
}
#Override public void installUI(JComponent c) {
super.installUI(c);
if (c instanceof JLayer) {
((JLayer) c).setLayerEventMask(
AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
}
#Override public void uninstallUI(JComponent c) {
if (c instanceof JLayer) {
((JLayer) c).setLayerEventMask(0);
}
super.uninstallUI(c);
}
#Override protected void processMouseEvent(
MouseEvent e, JLayer<? extends JTabbedPane> l) {
if (e.getID() == MouseEvent.MOUSE_CLICKED) {
pt.setLocation(e.getPoint());
JTabbedPane tabbedPane = (JTabbedPane) l.getView();
int index = tabbedPane.indexAtLocation(pt.x, pt.y);
if (index >= 0) {
Rectangle rect = tabbedPane.getBoundsAt(index);
Dimension d = button.getPreferredSize();
int x = rect.x + rect.width - d.width - 2;
int y = rect.y + (rect.height - d.height) / 2;
Rectangle r = new Rectangle(x, y, d.width, d.height);
if (r.contains(pt)) {
tabbedPane.removeTabAt(index);
}
}
}
}
#Override protected void processMouseMotionEvent(
MouseEvent e, JLayer<? extends JTabbedPane> l) {
pt.setLocation(e.getPoint());
JTabbedPane t = (JTabbedPane) l.getView();
if (t.indexAtLocation(pt.x, pt.y) >= 0) {
Point loc = e.getPoint();
loc.translate(-16, -16);
l.repaint(new Rectangle(loc, new Dimension(32, 32)));
}
}
}

Having multi-line text on a ListCell using JList and ListCellRenderer

This class instance is returned by my ListCellRenderer:
public class SessionQALine extends JPanel {
private JTextArea question;
private JLabel answer;
public SessionQALine() {
setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
setOpaque(false);
question = new JTextArea();
question.setLineWrap(true);
question.setWrapStyleWord(true);
question.setFont(new Font(Font.SERIF, Font.ITALIC|Font.BOLD, 14));
question.setOpaque(false);
answer = new JLabel();
answer.setFont(new Font(Font.SERIF, Font.BOLD, 10));
answer.setOpaque(false);
add(question);
add(Box.createHorizontalGlue());
add(answer);
}
public void setQuestion(String q) {
question.setText(q);
}
public void setAnswer(String q) {
answer.setText(q);
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.drawLine(0, getHeight()-1, getWidth(), getHeight()-1);
}
}
This is part of my ListCellRenderer:
private SessionQALine qaLine = new SessionQALine();
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
qaLine.setQuestion(questions.get(index));
qaLine.setAnswer(answers.get(index));
return qaLine;
}
everything works fine when the strings fit in one line, though if it needs more then one line, the second line and on seems not to be taken under consideration, the JList paints the cells and these line just never show, do you know how i can fix that?
for JList or JTable is easiest to use doLayout(), rather than getPreferredSize from java.swing.text.FieldView
most comfortable will be to put JTextArea to the JScrollPane, for quite nicer output to the GUI,
notice then have to redirect MouseScrollEvents from parent JScrollPane to the JScrollPane under Mouse Cursor, (five-six code_lines moreover a few times solved here)
use Borders or JSeparator instead of drawLine()
do you meaning
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.*;
//http://tips4java.wordpress.com/2008/10/26/text-utilities/
public class AutoWrapTest {
public JComponent makeUI() {
String[] columnNames = {" Text Area Cell Renderer "};
Object[][] data = {
{"123456789012345678901234567890"},
{"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddx"},
{"----------------------------------------------0"},
{">>>>>>>>>>>>>dddddddddddddddddddddddddddddddddddddddddddddddddd"
+ "dddddddxdddddddddddddddddddddddddddddddddddddddddddddd"
+ "dddddddddddx>>>>>>>>>>>>>>>>>>>>>>>>>|"},
{">>>>>>>>>>>>ddddddddddddddddddddddddddddddddddddddddddddddddddd"
+ "ddddddx>>>>>>>>>>>>>>>>>>>>>>>>>>|"},
{"a|"},
{">>>>>>>>bbbb>>>>>>>>>>>>>>>>>>>|"},
{">>>>>>>>>>>>>>>>>>|"},
{">>>>>>>>>>>>>dddddddddddddddddddddddddddddddddddddddddddddddddd"
+ "dddddddxdddddddddddddd123456789012345678901234567890dddddd"
+ "dddddddddddddddddddddddddddddddddddddx>>>>>>>>>>>>>>>>>>>>"
+ ">>>>>|"},
{">>>>>>>>>>>>>dddddddddddddd123456789012345678901234567890dddddd"
+ "dddddddddddddddddddddddddddddddddddddxdddddddddddddd123456"
+ "789012345678901234567890dddddddddddddddddddddddddddddddddd"
+ "ddddd123456789012345678901234567890ddddx>>>>>>>>>>>>>>>>>>"
+ ">>>>>>>|"},};
TableModel model = new DefaultTableModel(data, columnNames) {
private static final long serialVersionUID = 1L;
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
JTable table = new JTable(model) {
private static final long serialVersionUID = 1L;
#Override
public void doLayout() {
TableColumn col = getColumnModel().getColumn(0);
for (int row = 0; row < getRowCount(); row++) {
Component c = prepareRenderer(col.getCellRenderer(), row, 0);
if (c instanceof JTextArea) {
JTextArea a = (JTextArea) c;
int h = getPreferredHeight(a) + getIntercellSpacing().height;
if (getRowHeight(row) != h) {
setRowHeight(row, h);
}
}
}
super.doLayout();
}
private int getPreferredHeight(JTextComponent c) {
Insets insets = c.getInsets();
View view = c.getUI().getRootView(c).getView(0);
int preferredHeight = (int) view.getPreferredSpan(View.Y_AXIS);
return preferredHeight + insets.top + insets.bottom;
}
};
table.setEnabled(false);
table.setShowGrid(false);
table.setTableHeader(null);
table.getColumnModel().getColumn(0).setCellRenderer(new TextAreaCellRenderer());
//table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane sp = new JScrollPane(table);
sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
sp.setPreferredSize(new Dimension(250, 533));
JPanel p = new JPanel(new BorderLayout());
p.add(sp);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new AutoWrapTest().makeUI());
f.setLocation(100, 100);
f.pack();
f.setVisible(true);
}
}
class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {
private static final long serialVersionUID = 1L;
private final Color evenColor = new Color(230, 240, 255);
public TextAreaCellRenderer() {
super();
setLineWrap(true);
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
setBackground((row % 2 == 0) ? evenColor : getBackground());
}
setFont(table.getFont());
setText((value == null) ? "" : value.toString());
return this;
}
}

How to add background image in JTable when printing?

I want to print a JTable with background image or water mark. My code is:
public void actionPerformed(ActionEvent ae)
{
boolean status=false;
MessageFormat header = null;
header = new MessageFormat("Header");
MessageFormat footer = null;
footer = new MessageFormat("Page");
boolean fitWidth = true;
boolean showPrintDialog = true;
boolean interactive = true;
/* determine the print mode */
JTable.PrintMode mode = fitWidth ? JTable.PrintMode.FIT_WIDTH
: JTable.PrintMode.NORMAL;
try
{
status = jt.print(mode, header, footer,showPrintDialog,null,interactive);
if(status ==true)
{
frame.dispose();
}
}
catch(Exception ee)
{
System.out.println(ee.getMessage());
}
}
How can I pass or set the background image in this method?
there no easy way to set whatever for BackGround for whole JTable, but JViewPort from JScrollPane can do that easilly, then doesn't matter if is inside JScrollPane a JTable or another JComponents
for example
import java.awt.*;
import javax.swing.*;
class ImageAsTableBackround {
private JScrollPane sp;
private JTable table;
private String[] head = {"One", "Two", "Three", "Four", "Five", "Six"};
private String[][] data = new String[25][6];
public void buildGUI() {
sp = new JScrollPane();
// uncomment these codes lines for panting an image from package,
// but then block code table = new TableBackroundPaint0(data, head);
//sp.setViewport(new ImageViewport());
//table = new JTable(data, head);
//table.setOpaque(false);
// code for painting from generated code
table = new TableBackroundPaint0(data, head);
table.setBackground(new Color(0, 0, 0, 0));
table.setFillsViewportHeight(true);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
sp.setViewportView(table);
JFrame frame = new JFrame();
frame.getContentPane().add(sp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class ImageViewport extends JViewport {
private static final long serialVersionUID = 1L;
private Image img;
public ImageViewport() {
try {
ImageIcon image = new ImageIcon(getClass().getResource("resources/PICT6090.jpg"));
img = image.getImage();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
} else {
g.drawString("This space for rent", 50, 50);
}
}
}
class TableBackroundPaint0 extends JTable {
private static final long serialVersionUID = 1L;
TableBackroundPaint0(Object[][] data, Object[] head) {
super(data, head);
setOpaque(false);
((JComponent) getDefaultRenderer(Object.class)).setOpaque(false);
}
#Override
public void paintComponent(Graphics g) {
Color background = new Color(168, 210, 241);
Color controlColor = new Color(230, 240, 230);
int width = getWidth();
int height = getHeight();
Graphics2D g2 = (Graphics2D) g;
Paint oldPaint = g2.getPaint();
g2.setPaint(new GradientPaint(0, 0, background, width, 0, controlColor));
g2.fillRect(0, 0, width, height);
g2.setPaint(oldPaint);
for (int row : getSelectedRows()) {
Rectangle start = getCellRect(row, 0, true);
Rectangle end = getCellRect(row, getColumnCount() - 1, true);
g2.setPaint(new GradientPaint(start.x, 0, controlColor, (int) ((end.x + end.width - start.x) * 1.25), 0, Color.orange));
g2.fillRect(start.x, start.y, end.x + end.width - start.x, start.height);
}
super.paintComponent(g);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ImageAsTableBackround().buildGUI();
}
});
}
}
The problem has two parts
print an image an each page
be sure the image "shines-through" the table
the second is addressed by #mKorbel (though not really solved because there is no nice solution :-)
To solve the first, I would go for a custom Printable and subclass JTable to return it, something like
public class BackgroundPrintable implements Printable {
Printable tablePrintable;
JTable table;
MessageFormat header;
MessageFormat footer;
BufferedImage background;
public BackgroundPrintable(MessageFormat header, MessageFormat footer) {
this.header = header;
this.footer = footer;
}
public void setTablePrintable(JTable table, Printable printable) {
tablePrintable = printable;
this.table = table;
}
#Override
public int print(Graphics graphics, PageFormat pageFormat,
int pageIndex) throws PrinterException {
printImage(graphics, pageFormat, pageIndex);
int exists = tablePrintable.print(graphics, pageFormat, pageIndex);
if (exists != PAGE_EXISTS) {
return exists;
}
return PAGE_EXISTS;
}
private void printImage(Graphics graphics, PageFormat pageFormat,
int pageIndex) {
// grab an untainted graphics
Graphics2D g2d = (Graphics2D)graphics.create();
// do the image painting
....
// cleanup
g2d.dispose();
}
}
// use in JTable subclass
#Override
public Printable getPrintable(PrintMode printMode,
MessageFormat headerFormat, MessageFormat footerFormat) {
Printable printable = super.getPrintable(printMode, null, null);
BackgroundPrintable custom = new BackgroundPrintable(headerFormat, footerFormat);
custom.setTablePrintable(this, printable);
return custom;
}
To achieve the second, both the JTable and its renderers must be transparent. Tricky, because:
probably only if printing - otherwise they should have their usual opacity
all of the renderers must be transparent, and there is no completely safe way to get hold of them all
A custom JTable could try to achieve that by forcing the rendering component's opacity in its prepareRenderer:
#Override
public Component prepareRenderer(TableCellRenderer renderer,
int row, int column) {
JComponent comp = (JComponent) super.prepareRenderer(renderer, row, column);
if (isPaintingForPrint()) {
comp.setOpaque(false);
} else {
comp.setOpaque(true);
}
return comp;
}
Actually, that's not entirely valid: the code in the else block might be the wrong-thing-to-do for naturally transparent components. No really reliable solution available, I'm afraid.

Categories

Resources