/**************************************************************************
OmegaT - Computer Assisted Translation (CAT) tool
with fuzzy matching, translation memory, keyword search,
glossaries, and translation leveraging into updated projects.
Copyright (C) 2015 Aaron Madlon-Kay
Home page: http://www.omegat.org/
Support center: http://groups.yahoo.com/group/OmegaT/
This file is part of OmegaT.
OmegaT is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OmegaT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
package org.omegat.util.gui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.JViewport;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.AttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import org.omegat.util.Platform;
/**
*
* @author Aaron Madlon-Kay
*/
public final class DataTableStyling {
private DataTableStyling() {
}
public static final Color COLOR_STANDARD_FG = Color.BLACK;
public static final Color COLOR_STANDARD_BG = Color.WHITE;
public static final Color COLOR_SELECTION_FG = Color.WHITE;
public static final Color COLOR_SELECTION_BG = new Color(0x2F77DA);
public static final Color COLOR_ALTERNATING_HILITE = new Color(245, 245, 245);
public static final Border TABLE_FOCUS_BORDER = new MatteBorder(1, 1, 1, 1, new Color(0x76AFE8));
public static final Border TABLE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
public static final DecimalFormat NUMBER_FORMAT = new DecimalFormat(",##0");
public static final int FONT_NO_CHANGE = -1;
public static final int LINE_SPACING = 6;
public static void applyColors(JTable table) {
if (!Platform.isMacOSX()) {
// Windows needs some extra colors set for consistency, but these
// ruin native LAF on OS X.
if (table.getParent() instanceof JViewport) {
table.getParent().setBackground(COLOR_STANDARD_BG);
}
if (table.getParent().getParent() instanceof JScrollPane) {
table.getParent().getParent().setBackground(COLOR_STANDARD_BG);
}
if (table.getTableHeader() != null) {
table.getTableHeader().setBackground(COLOR_STANDARD_BG);
}
}
table.setForeground(COLOR_STANDARD_FG);
table.setBackground(COLOR_STANDARD_BG);
table.setSelectionForeground(COLOR_SELECTION_FG);
table.setSelectionBackground(COLOR_SELECTION_BG);
table.setGridColor(COLOR_STANDARD_BG);
}
public static void applyFont(JTable table, Font font) {
table.setFont(font);
table.setRowHeight(font.getSize() + DataTableStyling.LINE_SPACING);
}
public static TableCellRenderer getNumberCellRenderer() {
return new AlternatingHighlightRenderer().setAlignment(SwingConstants.RIGHT).setNumberFormat(NUMBER_FORMAT);
}
public static TableCellRenderer getTextCellRenderer() {
return new AlternatingHighlightRenderer();
}
public static TableCellRenderer getHeaderTextCellRenderer() {
return new AlternatingHighlightRenderer().setFontStyle(Font.BOLD);
}
public abstract static class FancyRenderer<T extends JComponent> implements TableCellRenderer {
private NumberFormat numberFormat = null;
private boolean doHighlight = true;
private int fontStyle = FONT_NO_CHANGE;
public FancyRenderer<T> setNumberFormat(NumberFormat numberFormat) {
this.numberFormat = numberFormat;
return this;
}
public FancyRenderer<T> setDoHighlight(boolean doHighlight) {
this.doHighlight = doHighlight;
return this;
}
public FancyRenderer<T> setFontStyle(int fontStyle) {
this.fontStyle = fontStyle;
return this;
}
protected String transformValue(Object value) {
if (value == null) {
return null;
}
if (numberFormat != null) {
if (value instanceof Number) {
return numberFormat.format((Number) value);
}
if (value instanceof String) {
try {
long lVal = Long.parseLong((String) value);
return numberFormat.format(lVal);
} catch (NumberFormatException ignore) {
}
}
}
return value.toString();
}
protected abstract T getComponent();
protected abstract void applyValue(String value);
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
T result = getComponent();
Font font = table.getFont();
if (fontStyle != FONT_NO_CHANGE) {
font = font.deriveFont(fontStyle);
}
result.setFont(font);
if (isSelected) {
result.setForeground(table.getSelectionForeground());
result.setBackground(table.getSelectionBackground());
} else if (row % 2 != 0 && doHighlight) {
result.setForeground(table.getForeground());
result.setBackground(COLOR_ALTERNATING_HILITE);
} else {
result.setForeground(table.getForeground());
result.setBackground(table.getBackground());
}
result.setBorder(hasFocus ? TABLE_FOCUS_BORDER : TABLE_NO_FOCUS_BORDER);
applyValue(transformValue(value));
return result;
}
}
public static class AlternatingHighlightRenderer extends FancyRenderer<DefaultTableCellRenderer> {
private final DefaultTableCellRenderer component = new DefaultTableCellRenderer();
public AlternatingHighlightRenderer setAlignment(int alignment) {
component.setHorizontalAlignment(alignment);
return this;
}
@Override
protected DefaultTableCellRenderer getComponent() {
return component;
}
@Override
protected void applyValue(String value) {
component.setText(value);
}
}
public static class PatternHighlightRenderer extends FancyRenderer<JTextPane> {
private static final Color HIGHLIGHT_FG_COLOR = Color.BLACK;
private static final Color HIGHLIGHT_BG_COLOR = Color.YELLOW;
private static final AttributeSet EMPTY_ATTR = new SimpleAttributeSet();
private static final AttributeSet HIGHLIGHT_ATTR;
static {
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setBackground(sas, HIGHLIGHT_BG_COLOR);
StyleConstants.setForeground(sas, HIGHLIGHT_FG_COLOR);
HIGHLIGHT_ATTR = sas;
}
private final JTextPane component;
private Pattern pattern;
/**
* Create a new PatternHighlightRenderer.
* <p>
* Disable line wrap when using this as a single-line renderer. This is because this renderer uses a
* {@link JTextPane} as its rendering component, and the default JTextPane wrapping results in words simply
* disappearing when used as a single-line renderer.
*
* @param lineWrapEnabled
* Whether to allow line wrapping
* @see <a href="https://sourceforge.net/p/omegat/bugs/862/">Bug #862</a>
*/
public PatternHighlightRenderer(boolean lineWrapEnabled) {
component = new JTextPane();
if (!lineWrapEnabled) {
component.setEditorKit(new NoWrapEditorKit());
}
}
public PatternHighlightRenderer setPattern(Pattern pattern) {
this.pattern = pattern;
return this;
}
@Override
protected JTextPane getComponent() {
return component;
}
@Override
protected void applyValue(String value) {
component.setText(value);
if (value != null) {
doHighlighting(value);
}
}
void doHighlighting(String text) {
StyledDocument doc = component.getStyledDocument();
doc.setCharacterAttributes(0, text.length(), EMPTY_ATTR, true);
if (pattern != null) {
Matcher m = pattern.matcher(text);
while (m.find()) {
doc.setCharacterAttributes(m.start(), m.end() - m.start(), HIGHLIGHT_ATTR, true);
}
}
}
}
}