package org.jabref.gui.importer; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.util.LinkedHashMap; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.BorderFactory; import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableModel; import org.jabref.Globals; import org.jabref.gui.JabRefDialog; import org.jabref.gui.JabRefFrame; import org.jabref.gui.keyboard.KeyBinding; import org.jabref.logic.importer.OutputPrinter; import org.jabref.logic.l10n.Localization; import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.gui.TableFormat; import ca.odell.glazedlists.swing.DefaultEventSelectionModel; import ca.odell.glazedlists.swing.DefaultEventTableModel; import ca.odell.glazedlists.swing.GlazedListsSwing; import com.jgoodies.forms.builder.ButtonBarBuilder; import com.jgoodies.forms.builder.ButtonStackBuilder; /** * */ public class FetcherPreviewDialog extends JabRefDialog implements OutputPrinter { private final EventList<TableEntry> entries = new BasicEventList<>(); private final JTable glTable; private boolean okPressed; private final JabRefFrame frame; private final int warningLimit; public FetcherPreviewDialog(JabRefFrame frame, int warningLimit, int tableRowHeight) { super(frame, Localization.lang("Title"), true, FetcherPreviewDialog.class); this.frame = frame; this.warningLimit = warningLimit; JButton ok = new JButton(Localization.lang("OK")); ok.addActionListener(e -> { if (verifySelection()) { okPressed = true; dispose(); } }); JButton cancel = new JButton(Localization.lang("Cancel")); cancel.addActionListener(e -> { okPressed = false; dispose(); }); JButton selectAll = new JButton(Localization.lang("Select all")); selectAll.addActionListener(e -> setSelectionAll(true)); JButton deselectAll = new JButton(Localization.lang("Deselect all")); deselectAll.addActionListener(e -> setSelectionAll(false)); DefaultEventTableModel<TableEntry> tableModelGl = (DefaultEventTableModel<TableEntry>) GlazedListsSwing .eventTableModelWithThreadProxyList(entries, new EntryTableFormat()); glTable = new EntryTable(tableModelGl); glTable.setRowHeight(tableRowHeight); glTable.getColumnModel().getColumn(0).setMaxWidth(45); glTable.setPreferredScrollableViewportSize(new Dimension(1100, 600)); DefaultEventSelectionModel<TableEntry> selectionModel = (DefaultEventSelectionModel<TableEntry>) GlazedListsSwing .eventSelectionModelWithThreadProxyList(entries); glTable.setSelectionModel(selectionModel); ButtonStackBuilder builder = new ButtonStackBuilder(); builder.addButton(selectAll); builder.addButton(deselectAll); builder.getPanel().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); ButtonBarBuilder bb = new ButtonBarBuilder(); bb.addGlue(); bb.addButton(ok); bb.addButton(cancel); bb.addGlue(); bb.getPanel().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JPanel centerPan = new JPanel(); centerPan.setLayout(new BorderLayout()); centerPan.add(new JScrollPane(glTable), BorderLayout.CENTER); centerPan.add(builder.getPanel(), BorderLayout.WEST); getContentPane().add(centerPan, BorderLayout.CENTER); getContentPane().add(bb.getPanel(), BorderLayout.SOUTH); // Key bindings: Action closeAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { dispose(); } }; ActionMap am = centerPan.getActionMap(); InputMap im = centerPan.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close"); am.put("close", closeAction); pack(); } /** * Check whether a large number of entries are selected, and if so, ask the user whether * to go on. * @return true if we should go on */ private boolean verifySelection() { int selected = 0; for (TableEntry entry : entries) { if (entry.isWanted()) { selected++; } } if (selected > warningLimit) { int result = JOptionPane.showConfirmDialog(this, Localization.lang("You have selected more than %0 entries for download. Some web sites " + "might block you if you make too many rapid downloads. Do you want to continue?", String.valueOf(warningLimit)), Localization.lang("Confirm selection"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); return result == JOptionPane.YES_OPTION; } else { return true; } } public Map<String, Boolean> getSelection() { LinkedHashMap<String, Boolean> selection = new LinkedHashMap<>(); for (TableEntry e : entries) { selection.put(e.id, e.isWanted()); } return selection; } public void addEntry(String entryId, JLabel preview) { TableEntry entry = new TableEntry(entryId, preview); this.entries.getReadWriteLock().writeLock().lock(); this.entries.add(entry); this.entries.getReadWriteLock().writeLock().unlock(); glTable.repaint(); } private void setSelectionAll(boolean select) { for (int i = 0; i < glTable.getRowCount(); i++) { glTable.setValueAt(select, i, 0); } glTable.repaint(); } static class TableEntry { private final String id; private final JLabel preview; private boolean wanted; public TableEntry(String id, JLabel preview) { this.id = id; this.preview = preview; } public boolean isWanted() { return wanted; } public void setWanted(boolean wanted) { this.wanted = wanted; } public JLabel getPreview() { return preview; } } static class PreviewRenderer implements TableCellRenderer { private final JLabel label = new JLabel(); @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JLabel valueLabel = (JLabel) value; label.setText(valueLabel.getText()); return label; } } class EntryTable extends JTable { private final PreviewRenderer renderer = new PreviewRenderer(); public EntryTable(TableModel model) { super(model); getTableHeader().setReorderingAllowed(false); } @Override public TableCellRenderer getCellRenderer(int row, int column) { return column == 0 ? getDefaultRenderer(Boolean.class) : renderer; } /* * public TableCellEditor getCellEditor() { return * getDefaultEditor(Boolean.class); } */ @Override public Class<?> getColumnClass(int col) { if (col == 0) { return Boolean.class; } else { return JLabel.class; } } @Override public boolean isCellEditable(int row, int column) { return column == 0; } @Override public void setValueAt(Object value, int row, int column) { // Only column 0, which is controlled by BibEntry.searchHit, is // editable: entries.getReadWriteLock().writeLock().lock(); TableEntry entry = entries.get(row); entry.setWanted((Boolean) value); entries.getReadWriteLock().writeLock().unlock(); } } private static class EntryTableFormat implements TableFormat<TableEntry> { @Override public int getColumnCount() { return 2; } @Override public String getColumnName(int i) { if (i == 0) { return Localization.lang("Keep"); } else { return Localization.lang("Preview"); } } @Override public Object getColumnValue(TableEntry entry, int i) { if (i == 0) { return entry.isWanted() ? Boolean.TRUE : Boolean.FALSE; } else { return entry.getPreview(); } } } public boolean isOkPressed() { return okPressed; } @Override public void setStatus(String s) { frame.setStatus(s); } @Override public void showMessage(String message, String title, int msgType) { JOptionPane.showMessageDialog(this, message, title, msgType); } @Override public void showMessage(String message) { JOptionPane.showMessageDialog(this, message); } /** * Displays a dialog which tells the user that an error occurred while fetching entries */ public void showErrorMessage(String fetcherTitle, String localizedMessage) { showMessage(Localization.lang("Error while fetching from %0", fetcherTitle) + "\n" + Localization.lang("Please try again later and/or check your network connection.") + "\n" + localizedMessage, Localization.lang("Search %0", fetcherTitle), JOptionPane.ERROR_MESSAGE); } }