From dd55cd1b43001a1ba95fd33d9152fc9e6aef0acb Mon Sep 17 00:00:00 2001 From: 4pr0n Date: Sun, 11 Jan 2015 04:31:30 -0800 Subject: [PATCH] Adding features to "History" Table-based view. Tracks # of images in a rip, and dates. Sortable. Double-clicking URL allows copying the URL to clipboard. Can selectively re-rip only albums that are 'checked' 'Checked' albums are persisted when RipMe is closed & reopened. For #147 (selective re-ripping) --- .../rarchives/ripme/ripper/AlbumRipper.java | 2 +- .../java/com/rarchives/ripme/ui/History.java | 60 +++++++ .../com/rarchives/ripme/ui/MainWindow.java | 147 +++++++++++++----- 3 files changed, 167 insertions(+), 42 deletions(-) diff --git a/src/main/java/com/rarchives/ripme/ripper/AlbumRipper.java b/src/main/java/com/rarchives/ripme/ripper/AlbumRipper.java index 8d40086d..96031b1c 100644 --- a/src/main/java/com/rarchives/ripme/ripper/AlbumRipper.java +++ b/src/main/java/com/rarchives/ripme/ripper/AlbumRipper.java @@ -35,7 +35,7 @@ public abstract class AlbumRipper extends AbstractRipper { @Override public int getCount() { - return itemsCompleted.size(); + return itemsCompleted.size() + itemsErrored.size(); } public boolean addURLToDownload(URL url, File saveAs, String referrer, Map cookies) { diff --git a/src/main/java/com/rarchives/ripme/ui/History.java b/src/main/java/com/rarchives/ripme/ui/History.java index e0159be2..dfcfa2ef 100644 --- a/src/main/java/com/rarchives/ripme/ui/History.java +++ b/src/main/java/com/rarchives/ripme/ui/History.java @@ -5,7 +5,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; import org.apache.commons.io.IOUtils; @@ -15,6 +17,13 @@ import org.json.JSONObject; public class History { private List list = new ArrayList(); + private static final String[] COLUMNS = new String[] { + "URL", + "created", + "modified", + "#", + "" + }; public void add(HistoryEntry entry) { list.add(entry); @@ -22,9 +31,60 @@ public class History { public void remove(HistoryEntry entry) { list.remove(entry); } + public void remove(int index) { + list.remove(index); + } public void clear() { list.clear(); } + public HistoryEntry get(int index) { + return list.get(index); + } + public String getColumnName(int index) { + return COLUMNS[index]; + } + public int getColumnCount() { + return COLUMNS.length; + } + public Object getValueAt(int row, int col) { + HistoryEntry entry = this.list.get(row); + switch (col) { + case 0: + return entry.url; + case 1: + return dateToHumanReadable(entry.startDate); + case 2: + return dateToHumanReadable(entry.modifiedDate); + case 3: + return entry.count; + case 4: + return entry.selected; + default: + return null; + } + } + private String dateToHumanReadable(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); + return sdf.format(date); + } + + public boolean containsURL(String url) { + for (HistoryEntry entry : this.list) { + if (entry.url.equals(url)) { + return true; + } + } + return false; + } + + public HistoryEntry getEntryByURL(String url) { + for (HistoryEntry entry : this.list) { + if (entry.url.equals(url)) { + return entry; + } + } + throw new RuntimeException("Could not find URL " + url + " in History"); + } public void fromJSON(JSONArray jsonArray) { JSONObject json; diff --git a/src/main/java/com/rarchives/ripme/ui/MainWindow.java b/src/main/java/com/rarchives/ripme/ui/MainWindow.java index fb9a0863..ffc148b8 100644 --- a/src/main/java/com/rarchives/ripme/ui/MainWindow.java +++ b/src/main/java/com/rarchives/ripme/ui/MainWindow.java @@ -43,6 +43,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; +import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.ListSelectionModel; @@ -53,6 +54,7 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; +import javax.swing.table.AbstractTableModel; import javax.swing.text.BadLocationException; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; @@ -89,10 +91,11 @@ public class MainWindow implements Runnable, RipStatusHandler { // History private static JButton optionHistory; + private static final History HISTORY = new History(); private static JPanel historyPanel; - private static JList historyList; - private static DefaultListModel historyListModel; - private static JScrollPane historyListScroll; + private static JTable historyTable; + private static AbstractTableModel historyTableModel; + private static JScrollPane historyTableScrollPane; private static JPanel historyButtonPanel; private static JButton historyButtonRemove, historyButtonClear, @@ -305,20 +308,66 @@ public class MainWindow implements Runnable, RipStatusHandler { historyPanel.setBorder(emptyBorder); historyPanel.setVisible(false); historyPanel.setPreferredSize(new Dimension(300, 250)); - historyListModel = new DefaultListModel(); - historyList = new JList(historyListModel); - historyList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); - historyListScroll = new JScrollPane(historyList, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, - JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + historyTableModel = new AbstractTableModel() { + private static final long serialVersionUID = 1L; + @Override + public String getColumnName(int col) { + return HISTORY.getColumnName(col); + } + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + @Override + public Object getValueAt(int row, int col) { + return HISTORY.getValueAt(row, col); + } + @Override + public int getRowCount() { + return HISTORY.toList().size(); + } + @Override + public int getColumnCount() { + return HISTORY.getColumnCount(); + } + @Override + public boolean isCellEditable(int row, int col) { + return (col == 0 || col == 4); + } + @Override + public void setValueAt(Object value, int row, int col) { + if (col == 4) { + HISTORY.get(row).selected = (Boolean) value; + historyTableModel.fireTableDataChanged(); + } + } + }; + historyTable = new JTable(historyTableModel); + historyTable.setAutoCreateRowSorter(true); + for (int i = 0; i < historyTable.getColumnModel().getColumnCount(); i++) { + int width = 130; // Default + switch (i) { + case 0: // URL + width = 270; + break; + case 3: + width = 40; + break; + case 4: + width = 15; + break; + } + historyTable.getColumnModel().getColumn(i).setPreferredWidth(width); + } + historyTableScrollPane = new JScrollPane(historyTable); historyButtonRemove = new JButton("Remove"); historyButtonClear = new JButton("Clear"); - historyButtonRerip = new JButton("Re-rip All"); + historyButtonRerip = new JButton("Re-rip Checked"); gbc.gridx = 0; - JPanel historyListPanel = new JPanel(new GridBagLayout()); - historyListPanel.add(historyListScroll, gbc); + // History List Panel + JPanel historyTablePanel = new JPanel(new GridBagLayout()); + historyTablePanel.add(historyTableScrollPane, gbc); gbc.ipady = 180; - historyPanel.add(historyListPanel, gbc); + historyPanel.add(historyTablePanel, gbc); gbc.ipady = 0; historyButtonPanel = new JPanel(new GridBagLayout()); historyButtonPanel.setPreferredSize(new Dimension(300, 10)); @@ -531,17 +580,24 @@ public class MainWindow implements Runnable, RipStatusHandler { historyButtonRemove.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { - int[] indices = historyList.getSelectedIndices(); + int[] indices = historyTable.getSelectedRows(); for (int i = indices.length - 1; i >= 0; i--) { - historyListModel.remove(indices[i]); + int modelIndex = historyTable.convertRowIndexToModel(indices[i]); + HISTORY.remove(modelIndex); } + try { + historyTableModel.fireTableDataChanged(); + } catch (Exception e) { } saveHistory(); } }); historyButtonClear.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { - historyListModel.clear(); + HISTORY.clear(); + try { + historyTableModel.fireTableDataChanged(); + } catch (Exception e) { } saveHistory(); } }); @@ -550,9 +606,27 @@ public class MainWindow implements Runnable, RipStatusHandler { historyButtonRerip.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { - for (int i = 0; i < historyListModel.size(); i++) { - HistoryEntry entry = (HistoryEntry) historyListModel.get(i); - queueListModel.addElement(entry.url); + if (HISTORY.toList().size() == 0) { + JOptionPane.showMessageDialog(null, + "There are no history entries to re-rip. Rip some albums first", + "RipMe Error", + JOptionPane.ERROR_MESSAGE); + return; + } + int added = 0; + for (HistoryEntry entry : HISTORY.toList()) { + if (entry.selected) { + added++; + queueListModel.addElement(entry.url); + } + } + if (added == 0) { + JOptionPane.showMessageDialog(null, + "No history entries have been 'Checked'\n" + + "Check an entry by clicking the checkbox to the right of the URL", + "RipMe Error", + JOptionPane.ERROR_MESSAGE); + return; } } }); @@ -794,32 +868,25 @@ public class MainWindow implements Runnable, RipStatusHandler { } private void loadHistory() { - History history = new History(); File historyFile = new File("history.json"); + HISTORY.clear(); if (historyFile.exists()) { try { logger.info("Loading history from history.json"); - history.fromFile("history.json"); + HISTORY.fromFile("history.json"); } catch (IOException e) { - logger.error("Failed to load history from file history.json", e); + logger.error("Failed to load history from file " + historyFile, e); } } else { logger.info("Loading history from configuration"); - history.fromList(Utils.getConfigList("download.history")); - } - for (HistoryEntry entry : history.toList()) { - historyListModel.addElement(entry); + HISTORY.fromList(Utils.getConfigList("download.history")); } } private void saveHistory() { - History history = new History(); - for (int i = 0; i < historyListModel.size(); i++) { - history.add( (HistoryEntry) historyListModel.get(i) ); - } try { - history.toFile("history.json"); + HISTORY.toFile("history.json"); } catch (IOException e) { logger.error("Failed to save history to file history.json", e); } @@ -977,18 +1044,15 @@ public class MainWindow implements Runnable, RipStatusHandler { break; case RIP_COMPLETE: - boolean alreadyInHistory = false; RipStatusComplete rsc = (RipStatusComplete) msg.getObject(); String url = ripper.getURL().toExternalForm(); - for (int i = 0; i < historyListModel.size(); i++) { - HistoryEntry entry = (HistoryEntry) historyListModel.get(i); - if (entry.url.equals(url)) { - alreadyInHistory = true; - entry.modifiedDate = new Date(); - break; - } + if (HISTORY.containsURL(url)) { + // TODO update "modifiedDate" of entry in HISTORY + HistoryEntry entry = HISTORY.getEntryByURL(url); + entry.count = rsc.count; + entry.modifiedDate = new Date(); } - if (!alreadyInHistory) { + else { HistoryEntry entry = new HistoryEntry(); entry.url = url; entry.dir = rsc.getDir(); @@ -996,7 +1060,8 @@ public class MainWindow implements Runnable, RipStatusHandler { try { entry.title = ripper.getAlbumTitle(ripper.getURL()); } catch (MalformedURLException e) { } - historyListModel.addElement(entry); + HISTORY.add(entry); + historyTableModel.fireTableDataChanged(); } if (configPlaySound.isSelected()) { Utils.playSound("camera.wav");