/************************************************************************** OmegaT - Computer Assisted Translation (CAT) tool with fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated projects. Copyright (C) 2016 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.gui.properties; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; import java.util.MissingResourceException; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableModel; import org.omegat.core.Core; import org.omegat.util.OStrings; import org.omegat.util.Preferences; import org.omegat.util.gui.DataTableStyling; import org.omegat.util.gui.StaticUIUtils; import org.omegat.util.gui.TableColumnSizer; import org.omegat.util.gui.UIThreadsUtil; /** * A table-based view of key=value properties of the current segment * * @author Aaron Madlon-Kay */ @SuppressWarnings("serial") public class SegmentPropertiesTableView implements ISegmentPropertiesView { private SegmentPropertiesArea parent; private FlashingTable table; private PropertiesTableModel model; private int mouseoverRow = -1; private int mouseoverCol = -1; @Override public void install(final SegmentPropertiesArea parent) { this.parent = parent; model = new PropertiesTableModel(); table = new FlashingTable(model); table.setForeground(parent.scrollPane.getForeground()); table.setBackground(parent.scrollPane.getBackground()); table.addMouseListener(parent.contextMenuListener); table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); table.setGridColor(Color.WHITE); table.setFillsViewportHeight(true); table.getColumnModel().getColumn(0).setCellRenderer(new SingleLineCellRenderer()); table.getColumnModel().getColumn(1).setCellRenderer(new MultilineCellRenderer()); table.getColumnModel().getColumn(2).setCellRenderer(new SingleLineCellRenderer()); DataTableStyling.applyFont(table, Core.getMainWindow().getApplicationFont()); TableColumnSizer.autoSize(table, 1, true).addColumnAdjustmentListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { adjustRowHeights(); } }); table.addMouseListener(mouseAdapter); table.addMouseMotionListener(mouseAdapter); parent.scrollPane.setViewportView(table); } private final MouseAdapter mouseAdapter = new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (mouseoverCol == 2) { parent.showContextMenu(SwingUtilities.convertPoint(table, e.getPoint(), parent.scrollPane)); } } @Override public void mouseExited(MouseEvent e) { updateRollover(); } @Override public void mouseMoved(MouseEvent e) { updateRollover(); } }; private void updateRollover() { Point point = table.getMousePosition(); int oldRow = mouseoverRow; int oldCol = mouseoverCol; int newRow = point == null ? -1 : table.rowAtPoint(point); int newCol = point == null ? -1 : table.columnAtPoint(point); boolean doRepaint = newRow != oldRow || newCol != oldCol; mouseoverRow = newRow; mouseoverCol = newCol; if (doRepaint) { Rectangle rect = table.getCellRect(oldRow, 2, true); table.repaint(rect); rect = table.getCellRect(newRow, 2, true); table.repaint(rect); } } @Override public JComponent getViewComponent() { return table; } @Override public void update() { UIThreadsUtil.mustBeSwingThread(); table.clearSelection(); table.clearHighlight(); model.fireTableDataChanged(); adjustRowHeights(); } private void adjustRowHeights() { int column1Width = table.getColumnModel().getColumn(1).getWidth(); for (int row = 0; row < table.getRowCount(); row++) { TableCellRenderer cellRenderer = table.getCellRenderer(row, 1); Component c = table.prepareRenderer(cellRenderer, row, 1); c.setBounds(0, 0, column1Width, Integer.MAX_VALUE); int height = c.getPreferredSize().height; table.setRowHeight(row, height); } } @Override public void notifyUser(List<Integer> notify) { UIThreadsUtil.mustBeSwingThread(); notify = translateIndices(notify); table.clearSelection(); table.scrollRectToVisible(table.getCellRect(notify.get(0), notify.get(notify.size() - 1), true)); table.flash(notify); } private List<Integer> translateIndices(List<Integer> indices) { List<Integer> result = new ArrayList<Integer>(indices.size()); for (int i : indices) { result.add(i / 2); } return result; } @Override public String getKeyAtPoint(Point p) { int clickedRow = table.rowAtPoint(SwingUtilities.convertPoint(parent.scrollPane, p, table)); if (clickedRow == -1) { return null; } return (String) model.getValueAt(clickedRow, 0); } private class PropertiesTableModel extends AbstractTableModel { @Override public int getColumnCount() { return 3; } @Override public String getColumnName(int columnIndex) { switch (columnIndex) { case 0: return OStrings.getString("SEGPROP_TABLE_HEADER_KEY"); case 1: return OStrings.getString("SEGPROP_TABLE_HEADER_VALUE"); case 2: return ""; } return null; } @Override public int getRowCount() { return parent.properties.size() / 2; } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (rowIndex < 0 || columnIndex < 0) { return null; } if (columnIndex == 2) { return rowIndex == mouseoverRow && columnIndex == mouseoverCol ? SETTINGS_ICON : rowIndex == mouseoverRow ? SETTINGS_ICON_INACTIVE : SETTINGS_ICON_INVISIBLE; } int realIndex = rowIndex * 2 + columnIndex; if (realIndex >= parent.properties.size()) { return null; } return parent.properties.get(rowIndex * 2 + columnIndex); } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } @Override public Class<?> getColumnClass(int columnIndex) { switch (columnIndex) { case 0: case 1: return String.class; case 2: return Icon.class; } return null; } } private static class FlashingTable extends JTable { private FlashColorInterpolator flasher; private List<Integer> rows; public FlashingTable(TableModel model) { super(model); } public void flash(List<Integer> rows) { this.rows = rows; flasher = new FlashColorInterpolator(); repaint(); } public boolean isHighlightedRow(int index) { return rows != null && rows.contains(index); } public void clearHighlight() { flasher = null; } public FlashColorInterpolator getFlasher() { return flasher; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (flasher != null && flasher.isFlashing()) { repaint(); } } } private static class SingleLineCellRenderer extends DefaultTableCellRenderer { private final Border noFocusBorder = new EmptyBorder(FOCUS_BORDER.getBorderInsets(this)); @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (!(comp instanceof JLabel)) { return comp; } JLabel result = (JLabel) comp; if (isSelected) { result.setBackground(table.getSelectionBackground()); result.setForeground(table.getSelectionForeground()); } else { Color bg = table.getBackground(); result.setBackground(row % 2 == 0 ? bg : StaticUIUtils.getHighlightColor(bg)); result.setForeground(table.getForeground()); } Border marginBorder = new EmptyBorder(1, column == 0 ? 5 : 1, 1, column == table.getColumnCount() - 1 ? 5 : 1); if (hasFocus) { result.setBorder(new CompoundBorder(marginBorder, FOCUS_BORDER)); } else { result.setBorder(new CompoundBorder(marginBorder, noFocusBorder)); } FlashingTable fTable = (FlashingTable) table; FlashColorInterpolator flasher = fTable.getFlasher(); if (flasher != null) { flasher.mark(); if (fTable.isHighlightedRow(row) && !isSelected) { setBackground(flasher.getColor()); } } result.setFont(table.getFont()); if (value instanceof String) { result.setVerticalAlignment(SwingConstants.TOP); result.setText(getText(value)); result.setIcon(null); } else if (value instanceof Icon) { result.setVerticalAlignment(SwingConstants.CENTER); result.setText(null); result.setIcon((Icon) value); } return result; } private String getText(Object value) { if (Preferences.isPreference(Preferences.SEGPROPS_SHOW_RAW_KEYS)) { return value.toString(); } try { return OStrings.getString(PROPERTY_TRANSLATION_KEY + value.toString().toUpperCase()); } catch (MissingResourceException ex) { return value.toString(); } } } // See: http://esus.com/creating-a-jtable-with-multiline-cells/ private static class MultilineCellRenderer extends JTextArea implements TableCellRenderer { private final Border noFocusBorder = new EmptyBorder(FOCUS_BORDER.getBorderInsets(this)); public MultilineCellRenderer() { setLineWrap(true); setWrapStyleWord(true); setOpaque(true); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (isSelected) { setBackground(table.getSelectionBackground()); setForeground(table.getSelectionForeground()); } else { Color bg = table.getBackground(); setBackground(row % 2 == 0 ? bg : StaticUIUtils.getHighlightColor(bg)); setForeground(table.getForeground()); } Border marginBorder = new EmptyBorder(1, column == 0 ? 5 : 1, 1, column == table.getColumnCount() - 1 ? 5 : 1); if (hasFocus) { setBorder(new CompoundBorder(marginBorder, FOCUS_BORDER)); } else { setBorder(new CompoundBorder(marginBorder, noFocusBorder)); } FlashingTable fTable = (FlashingTable) table; FlashColorInterpolator flasher = fTable.getFlasher(); if (flasher != null) { flasher.mark(); if (fTable.isHighlightedRow(row) && !isSelected) { setBackground(flasher.getColor()); } } setFont(table.getFont()); setText(value.toString()); return this; } } }