package org.rr.jeborker.gui.cell;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.TableCellRenderer;
import org.rr.commons.collection.VolatileHashMap;
import org.rr.commons.log.LoggerFactory;
import org.rr.commons.mufs.IResourceHandler;
import org.rr.commons.mufs.ResourceHandlerFactory;
import org.rr.commons.swing.SwingUtils;
import org.rr.commons.swing.layout.VerticalLayout;
import org.rr.commons.utils.CommonUtils;
import org.rr.commons.utils.ListUtils;
import org.rr.commons.utils.StringUtil;
import org.rr.jeborker.metadata.IMetadataReader;
import org.rr.jeborker.metadata.download.MetadataDownloadEntry;
import org.rr.pm.image.IImageProvider;
import org.rr.pm.image.ImageProviderFactory;
import org.rr.pm.image.ImageUtils;
public class MetadataDownloadTableCellRenderer extends JPanel implements TableCellRenderer, Serializable {
private static final long serialVersionUID = -4684790158985895647L;
private static final int LEFT_WIDTH = 80;
private static final VolatileHashMap<String, ImageIcon> thumbnailCache = new VolatileHashMap<String, ImageIcon>(20, 20);
private JLabel imagelabel;
private JPanel mainPanel;
private JCheckBox imageCheck;
private Map<IMetadataReader.COMMON_METADATA_TYPES, List<Map.Entry<JCheckBox, String>>> editingValues = new HashMap<IMetadataReader.COMMON_METADATA_TYPES, List<Map.Entry<JCheckBox, String>>>();
private MetadataDownloadEntry editingEntry;
private List<JCheckBox> checkboxCache = new ArrayList<>();
private List<JLabel> labelCache = new ArrayList<>();
private List<JPanel> panelCache = new ArrayList<>();
public MetadataDownloadTableCellRenderer(boolean coverSupport) {
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[]{LEFT_WIDTH, 0, 0};
gridBagLayout.rowHeights = new int[]{344, 0};
gridBagLayout.columnWeights = new double[]{0.0, 1.0, Double.MIN_VALUE};
gridBagLayout.rowWeights = new double[]{1.0, Double.MIN_VALUE};
setLayout(gridBagLayout);
imagelabel = new JLabel();
imagelabel.setOpaque(false);
imagelabel.setHorizontalAlignment(SwingConstants.CENTER);
GridBagConstraints gbc_imagelabel = new GridBagConstraints();
gbc_imagelabel.insets = new Insets(0, 0, 0, 5);
gbc_imagelabel.fill = GridBagConstraints.BOTH;
gbc_imagelabel.gridx = 0;
gbc_imagelabel.gridy = 0;
imagelabel.setBounds(0, 0, LEFT_WIDTH, 200);
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.add(imagelabel, Integer.valueOf(0));
imageCheck = new JCheckBox();
imageCheck.setOpaque(false);
imageCheck.setSelected(false);
if(!coverSupport) {
imageCheck.setEnabled(false);
}
imageCheck.setBounds(0, 0, imageCheck.getPreferredSize().width, imageCheck.getPreferredSize().height);
layeredPane.add(imageCheck, Integer.valueOf(1));
add(layeredPane, gbc_imagelabel);
mainPanel = new JPanel();
mainPanel.setOpaque(false);
GridBagConstraints gbc_mainPanel = new GridBagConstraints();
gbc_mainPanel.fill = GridBagConstraints.BOTH;
gbc_mainPanel.gridx = 1;
gbc_mainPanel.gridy = 0;
add(mainPanel, gbc_mainPanel);
mainPanel.setLayout(new VerticalLayout(0, VerticalLayout.LEFT));
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
final Component tableCellComponent = this.getTableCellComponent(table, (MetadataDownloadEntry) value, isSelected, hasFocus, row, column);
return tableCellComponent;
}
Component getTableCellComponent(JTable table, MetadataDownloadEntry entry, boolean isSelected, boolean hasFocus, int row, final int column) {
final int addEntryLabels = this.addMetadataComponentEntries(entry, isSelected);
final int rowHeight = Math.max(80, addEntryLabels * 25);
table.setRowHeight(row, rowHeight);
ImageIcon imageIconCover = getImageIconCover(table, entry, new Dimension(LEFT_WIDTH, rowHeight));
imagelabel.setIcon(imageIconCover);
if(imageIconCover == null) {
imageCheck.setVisible(false);
} else {
imageCheck.setVisible(true);
}
imagelabel.setBounds(0, 0, LEFT_WIDTH, rowHeight);
Component[] allComponents = SwingUtils.getAllComponents(null, this);
for(Component c : allComponents) {
SwingUtils.setColor(c, isSelected);
}
SwingUtils.setColor(this, isSelected);
return this;
}
/**
* Add a component for each valid value in the {@link MetadataDownloadEntry}. In common author and title
* are two of the values to be present and created.
* @return The number of components that was created and added.
*/
private int addMetadataComponentEntries(MetadataDownloadEntry entry, boolean isEditing) {
int componentCount = 0;
mainPanel.removeAll();
if(isEditing) {
//create a new one because the old is used as result.
editingValues = new HashMap<IMetadataReader.COMMON_METADATA_TYPES, List<Map.Entry<JCheckBox, String>>>();
editingEntry = entry;
}
for(String author : entry.getAuthors()) {
JComponent metadataEntryAuthorView = getMetadataEntryViewPanel(Bundle.getString("MetadataDownloadTableCellRenderer.label.author"), author, componentCount, IMetadataReader.COMMON_METADATA_TYPES.AUTHOR);
mainPanel.add(metadataEntryAuthorView);
componentCount++;
}
if(StringUtil.isNotEmpty(entry.getTitle())) {
JComponent metadataEntryTitleView = getMetadataEntryViewPanel(Bundle.getString("MetadataDownloadTableCellRenderer.label.title"), entry.getTitle(), componentCount, IMetadataReader.COMMON_METADATA_TYPES.TITLE);
mainPanel.add(metadataEntryTitleView);
componentCount++;
}
if(StringUtil.isNotEmpty(entry.getAgeSuggestion())) {
JComponent metadataEntryAgeSuggestionView = getMetadataEntryViewPanel(Bundle.getString("MetadataDownloadTableCellRenderer.label.ageSuggestion"), entry.getAgeSuggestion(), componentCount, IMetadataReader.COMMON_METADATA_TYPES.AGE_SUGGESTION);
mainPanel.add(metadataEntryAgeSuggestionView);
componentCount++;
}
//no isbn 10 if isbn 13 is available
if(StringUtil.isNotEmpty(entry.getIsbn13())) {
JComponent metadataEntryISBN13View = getMetadataEntryViewPanel(Bundle.getString("MetadataDownloadTableCellRenderer.label.isbn13"), entry.getIsbn13(), componentCount, IMetadataReader.COMMON_METADATA_TYPES.ISBN);
mainPanel.add(metadataEntryISBN13View);
componentCount++;
} else if(StringUtil.isNotEmpty(entry.getIsbn10())) {
JComponent metadataEntryISBN10View = getMetadataEntryViewPanel(Bundle.getString("MetadataDownloadTableCellRenderer.label.isbn10"), entry.getIsbn10(), componentCount, IMetadataReader.COMMON_METADATA_TYPES.ISBN);
mainPanel.add(metadataEntryISBN10View);
componentCount++;
}
if(StringUtil.isNotEmpty(entry.getLanguage())) {
JComponent metadataEntryLanguageView = getMetadataEntryViewPanel(Bundle.getString("MetadataDownloadTableCellRenderer.label.language"), entry.getLanguage(), componentCount, IMetadataReader.COMMON_METADATA_TYPES.LANGUAGE);
mainPanel.add(metadataEntryLanguageView);
componentCount++;
}
if(StringUtil.isNotEmpty(entry.getDescription())) {
JComponent metadataEntryDescriptionView = getMetadataEntryViewPanel(Bundle.getString("MetadataDownloadTableCellRenderer.label.description"), entry.getDescription(), componentCount, IMetadataReader.COMMON_METADATA_TYPES.DESCRIPTION);
mainPanel.add(metadataEntryDescriptionView);
componentCount++;
}
return componentCount;
}
private JComponent getMetadataEntryViewPanel(String labelText, final String value, int index, IMetadataReader.COMMON_METADATA_TYPES type) {
JPanel panel = getOrCreatePanel(index);
JLabel valueLabel = getOrCreateLabel("<html><b>" + labelText + "</b>: " + value + "</html>", index);
panel.add(valueLabel, BorderLayout.CENTER);
final JCheckBox check = getOrCreateCheckbox(index);
check.setSelected(true);
panel.add(check, BorderLayout.WEST);
List<Entry<JCheckBox, String>> valuesList = editingValues.get(type);
if(valuesList == null) {
valuesList = new ArrayList<Map.Entry<JCheckBox,String>>();
}
valuesList.add(new Map.Entry<JCheckBox, String>() {
@Override
public String setValue(String value) {
return null;
}
@Override
public String getValue() {
return value;
}
@Override
public JCheckBox getKey() {
return check;
}
});
editingValues.put(type, valuesList);
return panel;
}
private JPanel getOrCreatePanel(int index) {
JPanel panel = ListUtils.get(panelCache, index);
if(panel == null) {
panel = new JPanel();
panel.setOpaque(true);
panel.setLayout(new BorderLayout(3, 0));
ListUtils.set(panelCache, panel, index);
}
return panel;
}
private JCheckBox getOrCreateCheckbox(int index) {
JCheckBox check = ListUtils.get(checkboxCache, index);
if(check == null) {
check = new JCheckBox();
ListUtils.set(checkboxCache, check, index);
}
return check;
}
private JLabel getOrCreateLabel(String text, int index) {
JLabel label = ListUtils.get(labelCache, index);
if(label == null) {
label = new JLabel();
label.setOpaque(true);
ListUtils.set(labelCache, label, index);
}
label.setText(text);
return label;
}
/**
* Gets the thumbnail image to be displayed in the renderer.
* @param table The JTable instance.
* @param entry The item to be rendered.
* @return The thumbnail image to be displayed in the renderer.
*/
private ImageIcon getImageIconCover(final JTable table, final MetadataDownloadEntry entry, Dimension dim) {
if(entry == null) {
return null;
}
byte[] coverThumbnail = entry.getThumbnailImageBytes();
if(entry != null && coverThumbnail != null && coverThumbnail.length > 0) {
final String coverThumbnailCRC32 = String.valueOf(CommonUtils.calculateCrc(coverThumbnail));
if(thumbnailCache.containsKey(coverThumbnailCRC32)) {
return thumbnailCache.get(coverThumbnailCRC32);
}
try {
final byte[] coverData = coverThumbnail;
if (coverData != null) {
final IResourceHandler virtualImageResourceLoader = ResourceHandlerFactory.getVirtualResourceHandler("TableCellRendererImageData", coverData);
final IImageProvider imageProvider = ImageProviderFactory.getImageProvider(virtualImageResourceLoader);
final BufferedImage image = imageProvider.getImage();
if(image != null) {
BufferedImage croped = ImageUtils.crop(imageProvider.getImage());
BufferedImage scaleToMatch = ImageUtils.scaleToMatch(croped, dim, true);
ImageIcon imageIcon = new ImageIcon(scaleToMatch);
thumbnailCache.put(coverThumbnailCRC32, imageIcon);
return imageIcon;
} else {
return null;
}
}
} catch (Exception e) {
LoggerFactory.logInfo(this, "Could not render thumbnail", e);
}
}
return null;
}
/**
* Get the values for the editing component.
*/
Map<IMetadataReader.COMMON_METADATA_TYPES, List<Map.Entry<JCheckBox, String>>> getEditingValues() {
return editingValues;
}
/**
* Get the MetadataDownloadEntry for the editing component.
*/
MetadataDownloadEntry getEditingMetadataDownloadEntry() {
return editingEntry;
}
/**
* Tells if the user has selected the cover image checkbox for set/replace
* the cover of the ebook file with the downloaded one.
*/
boolean isCoverImageChecked() {
return this.imageCheck.isSelected();
}
}