diff --git a/src/main/java/listfix/model/BatchMatchItem.java b/src/main/java/listfix/model/BatchMatchItem.java index 6d60cb03..03111592 100644 --- a/src/main/java/listfix/model/BatchMatchItem.java +++ b/src/main/java/listfix/model/BatchMatchItem.java @@ -14,24 +14,17 @@ public class BatchMatchItem /** * Playlist position */ - private final int _entryIx; private final PlaylistEntry _entry; private final List matches; private PotentialPlaylistEntryMatch selectedMatch; - public BatchMatchItem(int ix, PlaylistEntry entry, List matches) + public BatchMatchItem(PlaylistEntry entry, List matches) { - this._entryIx = ix; this._entry = entry; this.matches = matches; this.selectedMatch = matches.size() > 0 ? matches.get(0) : null; } - public int getEntryIx() - { - return this._entryIx; - } - public PlaylistEntry getEntry() { return _entry; diff --git a/src/main/java/listfix/model/playlists/Playlist.java b/src/main/java/listfix/model/playlists/Playlist.java index 6b866821..8e14aae0 100644 --- a/src/main/java/listfix/model/playlists/Playlist.java +++ b/src/main/java/listfix/model/playlists/Playlist.java @@ -578,23 +578,21 @@ public void changeEntryFileName(int ix, String newName) * @param mediaLibrary Media library used to reference existing media files * @param observer Progress observer */ - public List repair(IMediaLibrary mediaLibrary, IProgressObserver observer) + public List repair(IMediaLibrary mediaLibrary, IProgressObserver observer) { ProgressAdapter progress = ProgressAdapter.make(observer); progress.setTotal(this._entries.size()); - List fixed = new ArrayList<>(); - for (int ix = 0; ix < _entries.size(); ix++) - { + final long start = System.currentTimeMillis(); + + List fixed = _entries.stream().filter(entry -> { if (observer.getCancelled()) { _logger.info(markerPlaylistRepair, "Observer cancelled, quit repair"); - return null; + return false; } progress.stepCompleted(); - PlaylistEntry entry = this._entries.get(ix); - final boolean caseInsensitiveExactMatching = this.playListOptions.getCaseInsensitiveExactMatching(); final boolean relativePaths = this.playListOptions.getSavePlaylistsWithRelativePaths(); @@ -607,8 +605,8 @@ public List repair(IMediaLibrary mediaLibrary, IProgressObserver repair(IMediaLibrary mediaLibrary, IProgressObserver findClosestMatches(List entries, Coll progress.setTotal(entries.size()); List fixed = new ArrayList<>(); - int ix = 0; for (PlaylistEntry entry : entries) { if (observer.getCancelled()) return null; @@ -703,10 +703,9 @@ public List findClosestMatches(List entries, Coll List matches = entry.findClosestMatches(libraryFiles, null, this.playListOptions); if (!matches.isEmpty()) { - fixed.add(new BatchMatchItem(ix, entry, matches)); + fixed.add(new BatchMatchItem(entry, matches)); } } - ix++; progress.stepCompleted(); } diff --git a/src/main/java/listfix/view/controls/PlaylistEditCtrl.java b/src/main/java/listfix/view/controls/PlaylistEditCtrl.java index c9932ac7..e3335da2 100644 --- a/src/main/java/listfix/view/controls/PlaylistEditCtrl.java +++ b/src/main/java/listfix/view/controls/PlaylistEditCtrl.java @@ -17,7 +17,7 @@ import listfix.view.IListFixGui; import listfix.view.dialogs.*; import listfix.view.support.IPlaylistModifiedListener; -import listfix.view.support.ImageIcons; + import listfix.view.support.ProgressWorker; import listfix.view.support.ZebraJTable; import org.apache.commons.io.FilenameUtils; @@ -58,6 +58,7 @@ public class PlaylistEditCtrl extends JPanel private final FolderChooser _destDirFileChooser = new FolderChooser(); private Playlist _playlist; protected final IListFixGui listFixGui; + private boolean refreshPending = false; private final IPlaylistModifiedListener listener = this::onPlaylistModified; @@ -90,7 +91,25 @@ private void onPlaylistModified(Playlist list) _btnPlay.setEnabled(_playlist != null); _btnNextMissing.setEnabled(_playlist != null && _playlist.getMissingCount() > 0); _btnPrevMissing.setEnabled(_playlist != null && _playlist.getMissingCount() > 0); - getTableModel().fireTableDataChanged(); + this.fireTableDataChanged(); + } + + /** + * Call fireTableDataChanged() on the GUI thread, and do not call when a pending call is present + */ + private void fireTableDataChanged() { + if (!refreshPending) { + synchronized (this.playlistTableModel) { + refreshPending = true; + SwingUtilities.invokeLater(() -> { + synchronized (this.playlistTableModel) + { + refreshPending = false; + } + getTableModel().fireTableDataChanged(); + }); + } + } } private void addItems() @@ -222,19 +241,19 @@ private void moveSelectedRowsDown() } /** - * Locate missing files + * Repair playlist * @return false if cancelled, otherwise true */ public boolean locateMissingFiles() { _logger.debug(markerRepair, "Start locateMissingFiles()"); - ProgressWorker, String> worker = new ProgressWorker<>() + ProgressWorker, String> worker = new ProgressWorker<>() { @Override - protected List doInBackground() + protected List doInBackground() { _logger.debug(markerRepairWorker, "Start repairing in background...."); - List result = _playlist.repair(PlaylistEditCtrl.this.getMediaLibrary(), this); + List result = _playlist.repair(PlaylistEditCtrl.this.getMediaLibrary(), this); _logger.debug(markerRepairWorker, "Repair completed."); return result; } @@ -247,12 +266,13 @@ protected void done() { _logger.debug(markerRepair, "Updating UI-table..."); _uiTable.clearSelection(); - List fixed = this.get(); - for (Integer fixIx : fixed) + + for (Integer fixIx : this.getIndexList(this.get())) { int viewIx = _uiTable.convertRowIndexToView(fixIx); _uiTable.addRowSelectionInterval(viewIx, viewIx); } + playlistTableModel.fireTableDataChanged(); _logger.debug(markerRepair, "Completed updating UI-table"); } catch (CancellationException | InterruptedException exception) @@ -264,6 +284,21 @@ protected void done() _logger.error(markerRepair, "Error processing missing files", ex); } } + + private List getIndexList(List fixedEntries) { + final List fixedIndexes = new LinkedList<>(); + final List copiedList = new LinkedList<>(fixedEntries); + + // Lookup the playlist index of the fixed playlist entries + for (int fixIx = 0; fixIx < _playlist.size() && !copiedList.isEmpty(); ++fixIx) { + if (_playlist.get(fixIx) == copiedList.get(0)) { + copiedList.remove(0); + fixedIndexes.add(fixIx); + } + } + assert fixedIndexes.size() == fixedEntries.size(); + return fixedIndexes; + } }; ProgressDialog pd = new ProgressDialog(getParentFrame(), true, worker, "Repairing..."); @@ -271,6 +306,7 @@ protected void done() return !worker.isCancelled(); } + private void reorderList() { Playlist.SortIx sortIx = Playlist.SortIx.None; @@ -771,7 +807,8 @@ public void mouseClicked(MouseEvent evt) _uiTableScrollPane.setBorder(BorderFactory.createEtchedBorder()); _uiTable.setAutoCreateRowSorter(true); - _uiTable.setModel(new PlaylistTableModel()); + + _uiTable.setModel(playlistTableModel); _uiTable.setDragEnabled(true); _uiTable.setFillsViewportHeight(true); _uiTable.setGridColor(new Color(153, 153, 153)); @@ -1113,6 +1150,7 @@ private void _miNewPlaylistFromSelectedActionPerformed() private JMenuItem _miFindClosest; private JMenuItem _miReplace; private JPopupMenu _playlistEntryRightClickMenu; + private final PlaylistTableModel playlistTableModel= new PlaylistTableModel(); private ZebraJTable _uiTable; private JScrollPane _uiTableScrollPane; @@ -1134,7 +1172,7 @@ public void setPlaylist(Playlist list, boolean force) } _playlist = list; - ((PlaylistTableModel) _uiTable.getModel()).fireTableDataChanged(); + this.playlistTableModel.changePlaylist(_playlist); boolean hasPlaylist = _playlist != null; @@ -1695,96 +1733,6 @@ private void initFolderChooser() _destDirFileChooser.setDialogTitle("Choose a destination directory..."); } - private class PlaylistTableModel extends AbstractTableModel - { - @Override - public int getRowCount() - { - if (_playlist != null) - { - return _playlist.size(); - } - else - { - return 0; - } - } - - @Override - public int getColumnCount() - { - return 4; - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) - { - PlaylistEntry entry = _playlist.get(rowIndex); - switch (columnIndex) - { - case 0 -> - { - return rowIndex + 1; - } - case 1 -> - { - if (entry.isURL()) - { - return ImageIcons.IMG_URL; - } - else if (entry.isFixed()) - { - return ImageIcons.IMG_FIXED; - } - else if (entry.isFound()) - { - return ImageIcons.IMG_FOUND; - } - else - { - return ImageIcons.IMG_MISSING; - } - } - case 2 -> - { - return entry.getTrackFileName(); - } - case 3 -> - { - return entry.getTrackFolder(); - } - default -> - { - return null; - } - } - } - - @Override - public String getColumnName(int column) - { - return switch (column) - { - case 0 -> "#"; - case 1 -> ""; - case 2 -> "File Name"; - case 3 -> "Location"; - default -> null; - }; - } - - @Override - public Class getColumnClass(int columnIndex) - { - return switch (columnIndex) - { - case 0 -> Integer.class; - case 1 -> ImageIcon.class; - default -> Object.class; - }; - } - } - private static class IntRenderer extends DefaultTableCellRenderer { IntRenderer() diff --git a/src/main/java/listfix/view/controls/PlaylistTableModel.java b/src/main/java/listfix/view/controls/PlaylistTableModel.java new file mode 100644 index 00000000..61634abe --- /dev/null +++ b/src/main/java/listfix/view/controls/PlaylistTableModel.java @@ -0,0 +1,109 @@ +package listfix.view.controls; + +import listfix.model.playlists.Playlist; +import listfix.model.playlists.PlaylistEntry; +import listfix.view.support.ImageIcons; + +import javax.swing.*; +import javax.swing.table.AbstractTableModel; + +class PlaylistTableModel extends AbstractTableModel +{ + private Playlist playlist; + + PlaylistTableModel() { + this.playlist = null; + } + + public void changePlaylist(Playlist playlist) { + this.playlist = playlist; + this.fireTableDataChanged(); + } + + @Override + public int getRowCount() + { + if (playlist != null) + { + return playlist.size(); + } + else + { + return 0; + } + } + + @Override + public int getColumnCount() + { + return 4; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) + { + PlaylistEntry entry = playlist.get(rowIndex); + switch (columnIndex) + { + case 0 -> + { + return rowIndex + 1; + } + case 1 -> + { + if (entry.isURL()) + { + return ImageIcons.IMG_URL; + } + else if (entry.isFixed()) + { + return ImageIcons.IMG_FIXED; + } + else if (entry.isFound()) + { + return ImageIcons.IMG_FOUND; + } + else + { + return ImageIcons.IMG_MISSING; + } + } + case 2 -> + { + return entry.getTrackFileName(); + } + case 3 -> + { + return entry.getTrackFolder(); + } + default -> + { + return null; + } + } + } + + @Override + public String getColumnName(int column) + { + return switch (column) + { + case 0 -> "#"; + case 1 -> ""; + case 2 -> "File Name"; + case 3 -> "Location"; + default -> null; + }; + } + + @Override + public Class getColumnClass(int columnIndex) + { + return switch (columnIndex) + { + case 0 -> Integer.class; + case 1 -> ImageIcon.class; + default -> Object.class; + }; + } +} diff --git a/src/main/java/listfix/view/controls/PlaylistsList.java b/src/main/java/listfix/view/controls/PlaylistsList.java index 937ff376..e32de392 100644 --- a/src/main/java/listfix/view/controls/PlaylistsList.java +++ b/src/main/java/listfix/view/controls/PlaylistsList.java @@ -1,11 +1,3 @@ - - -/* - * PlaylistsList.java - * - * Created on Apr 2, 2011, 12:11:40 PM - */ - package listfix.view.controls; import listfix.model.BatchRepair; @@ -26,7 +18,6 @@ import java.util.ArrayList; import java.util.List; - public class PlaylistsList extends JPanel { private BatchRepair _batch; @@ -41,7 +32,6 @@ public PlaylistsList() initComponents(); } - public PlaylistsList(BatchRepair batch) { _batch = batch;