/*
Copyright (C) 2013 Tobias Baum <tbaum at tntinteractive.de>
This file is a part of Portalsammler.
Portalsammler 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.
Portalsammler 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 Portalsammler. If not, see <http://www.gnu.org/licenses/>.
*/
package de.tntinteractive.portalsammler.gui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import org.apache.commons.io.FileUtils;
import de.tntinteractive.portalsammler.engine.DocumentFilter;
import de.tntinteractive.portalsammler.engine.DocumentInfo;
import de.tntinteractive.portalsammler.engine.SecureStore;
import de.tntinteractive.portalsammler.engine.ShouldNotHappenException;
final class DocumentTable {
private final Gui gui;
private final JTable table;
private DocumentFilter currentFilter = DocumentFilter.NO_FILTER;
private SecureStore store;
public DocumentTable(final Gui gui, final SecureStore store) {
this.gui = gui;
this.store = store;
this.table = new JTable();
this.table.setRowSelectionAllowed(true);
this.refreshContents();
this.table.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent me) {
final int r = DocumentTable.this.table.rowAtPoint(me.getPoint());
if (!DocumentTable.this.table.getSelectionModel().isSelectedIndex(r)) {
if (r >= 0 && r < DocumentTable.this.table.getRowCount()) {
DocumentTable.this.table.setRowSelectionInterval(r, r);
} else {
DocumentTable.this.table.clearSelection();
}
}
if (me.isPopupTrigger()) {
DocumentTable.this.showPopup(me);
} else if (me.getClickCount() == 2) {
DocumentTable.this.openSelectedRows();
}
}
@Override
public void mouseReleased(final MouseEvent me) {
if (me.isPopupTrigger()) {
DocumentTable.this.showPopup(me);
}
}
});
this.table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
this.table.getColumnModel().getColumn(0).setPreferredWidth(120);
this.table.getColumnModel().getColumn(1).setPreferredWidth(80);
this.table.getColumnModel().getColumn(2).setPreferredWidth(100);
this.table.getColumnModel().getColumn(3).setPreferredWidth(500);
}
private void showPopup(final MouseEvent ev) {
final JMenuItem open = new JMenuItem("Anzeigen");
open.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
DocumentTable.this.openSelectedRows();
}
});
final JMenuItem export = new JMenuItem("Exportieren");
export.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
DocumentTable.this.exportSelectedRows();
}
});
final JPopupMenu menu = new JPopupMenu();
menu.add(open);
menu.add(export);
menu.show(ev.getComponent(), ev.getX(), ev.getY());
}
private static class DocumentTableModel extends AbstractTableModel {
private static final long serialVersionUID = 8074878200701440324L;
private final SecureStore store;
private final List<DocumentInfo> data;
public DocumentTableModel(final SecureStore store, final Collection<DocumentInfo> documents) {
this.store = store;
this.data = new ArrayList<DocumentInfo>(documents);
}
@Override
public int getRowCount() {
return this.data.size();
}
@Override
public int getColumnCount() {
return 4;
}
@Override
public String getColumnName(final int columnIndex) {
switch (columnIndex) {
case 0:
return "Datum";
case 1:
return "ungelesen?";
case 2:
return "Quelle";
case 3:
return "Stichworte";
default:
throw new ShouldNotHappenException("invalid index " + columnIndex);
}
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
final DocumentInfo di = this.data.get(rowIndex);
switch (columnIndex) {
case 0:
return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(di.getDate());
case 1:
return this.store.isRead(di) ? "" : "X";
case 2:
return di.getSourceId();
case 3:
return di.getKeywords();
default:
throw new ShouldNotHappenException("invalid index " + columnIndex);
}
}
}
void refreshContents() {
final int[] widths = this.saveColumnWidths();
final Collection<DocumentInfo> allDocuments = this.store.getIndex().getAllDocuments();
final List<DocumentInfo> filteredDocuments = new ArrayList<DocumentInfo>();
for (final DocumentInfo d : allDocuments) {
if (this.currentFilter.shallShow(d)) {
filteredDocuments.add(d);
}
}
this.table.setModel(new DocumentTableModel(this.store, filteredDocuments));
this.restoreColumnWidths(widths);
}
private int[] saveColumnWidths() {
final int[] widths = new int[this.table.getColumnCount()];
for (int i = 0; i < widths.length; i++) {
widths[i] = this.table.getColumnModel().getColumn(i).getPreferredWidth();
}
return widths;
}
private void restoreColumnWidths(final int[] widths) {
for (int i = 0; i < widths.length; i++) {
this.table.getColumnModel().getColumn(i).setPreferredWidth(widths[i]);
}
}
void openSelectedRows() {
for (final int row : this.table.getSelectedRows()) {
this.openDocument(row);
}
this.refreshContents();
}
protected void exportSelectedRows() {
final JFileChooser chooser = new JFileChooser();
for (final int row : this.table.getSelectedRows()) {
final boolean cancel = this.exportDocument(chooser, row);
if (cancel) {
break;
}
}
this.refreshContents();
}
private void openDocument(final int row) {
try {
final DocumentInfo di = this.getDocumentInRow(row);
final byte[] content = this.store.getDocument(di);
this.store.markAsRead(di);
this.gui.showDocument(di, content);
this.store.writeMetadata();
} catch (final IOException e) {
this.gui.showError(e);
} catch (final GeneralSecurityException e) {
this.gui.showError(e);
}
}
private boolean exportDocument(final JFileChooser chooser, final int row) {
try {
final DocumentInfo di = this.getDocumentInRow(row);
final byte[] content = this.store.getDocument(di);
chooser.setSelectedFile(new File(this.makeFilenameFor(di)));
final int result = chooser.showSaveDialog(this.table);
if (result != JFileChooser.APPROVE_OPTION) {
return true;
}
FileUtils.writeByteArrayToFile(chooser.getSelectedFile(), content);
this.store.markAsRead(di);
this.store.writeMetadata();
} catch (final GeneralSecurityException e) {
this.gui.showError(e);
} catch (final IOException e) {
this.gui.showError(e);
}
return false;
}
private String makeFilenameFor(final DocumentInfo di) {
final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
return this.makeValidFilename(di.getSourceId()
+ ", " + format.format(di.getDate())
+ ", " + this.cutToSize(di.getKeywords(), 30))
+ "." + di.getFormat().getExtension();
}
private String cutToSize(final String s, final int i) {
if (s.length() > i) {
return s.substring(0, i);
} else {
return s;
}
}
private String makeValidFilename(final String string) {
return string.replaceAll("[|:<>\\/\n\r\t]+", " ");
}
private DocumentInfo getDocumentInRow(final int row) {
return ((DocumentTableModel) this.table.getModel()).data.get(row);
}
public JScrollPane createWrappedPanel() {
return new JScrollPane(this.table);
}
public void changeFilter(final DocumentFilter newFilter) {
this.currentFilter = newFilter;
this.refreshContents();
}
public void setStore(final SecureStore newStore) {
this.store = newStore;
this.refreshContents();
}
SecureStore getStore() {
return this.store;
}
}