Skip to content

Commit

Permalink
Fix #180
Browse files Browse the repository at this point in the history
  • Loading branch information
berry120 committed Dec 28, 2023
1 parent b36a7ad commit c145972
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 124 deletions.
1 change: 1 addition & 0 deletions Quelea/changelogs/changelog-2024.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ quelea (2024.0) stable
* Prevent blacked stage view option
* Logo display now supports video files
* Unsupported videos now have clear unsupported preview image
* Allow multiple selection in song database
* Critical fix: Using the French language no longer crashes Quelea when opening the options dialog
* Various minor bugfixes
1 change: 1 addition & 0 deletions Quelea/languages/gb.lang
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ cant.save.schedule.text=Sorry, an error occurred and the schedule couldn't be sa
error.removing.song.db=There was an error removing the song from the database.
confirm.remove.text=Confirm remove
confirm.remove.question=Really remove $1 from the database? This action cannot be undone.
confirm.remove.bulk.question=Really remove $1 songs from the database? This action cannot be undone.
quick.edit.text=Quick Edit
library.preview.song.text=Preview song
video.error.unsupported=Sorry, the video file you selected isn't supported.
Expand Down
47 changes: 37 additions & 10 deletions Quelea/src/main/java/org/quelea/data/db/SongManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import javafx.application.Platform;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.Session;
Expand All @@ -42,6 +44,7 @@
/**
* Manage songs persistent operations.
* <p/>
*
* @author Michael
*/
public final class SongManager {
Expand All @@ -66,6 +69,7 @@ private SongManager() {
* Get the singleton instance of this class. Return null if there was an
* error with the database.
* <p/>
*
* @return the singleton instance of this class.
*/
public static synchronized SongManager get() {
Expand All @@ -82,6 +86,7 @@ public static synchronized SongManager get() {
/**
* Get the underlying search index used by this database.
* <p/>
*
* @return the search index.
*/
public SongSearchIndex getIndex() {
Expand All @@ -91,6 +96,7 @@ public SongSearchIndex getIndex() {
/**
* Register a database listener with this database.
* <p/>
*
* @param listener the listener.
*/
public void registerDatabaseListener(DatabaseListener listener) {
Expand All @@ -113,6 +119,7 @@ public synchronized SongDisplayable[] getSongs() {
/**
* Get all the songs in the database.
* <p/>
*
* @return an array of all the songs in the database.
*/
public synchronized SongDisplayable[] getSongs(LoadingPane loadingPane) {
Expand Down Expand Up @@ -190,9 +197,10 @@ public boolean addSong(final Collection<SongDisplayable> song, final boolean fir
/**
* Add a song to the database.
* <p/>
* @param songs the songs to add.
*
* @param songs the songs to add.
* @param fireUpdate true if the update should be fired to listeners when
* adding this song, false otherwise.
* adding this song, false otherwise.
* @return true if the operation succeeded, false otherwise.
*/
public synchronized boolean addSong(final SongDisplayable[] songs, final boolean fireUpdate) {
Expand Down Expand Up @@ -240,6 +248,7 @@ public synchronized boolean addSong(final SongDisplayable[] songs, final boolean
/**
* Update a song in the database.
* <p/>
*
* @param song the song to update.
* @return true if the operation succeeded, false otherwise.
*/
Expand All @@ -250,9 +259,10 @@ public synchronized boolean updateSong(final SongDisplayable song) {
/**
* Update a song in the database.
* <p/>
* @param song the song to update.
*
* @param song the song to update.
* @param addIfNotFound true if the song should be added if it's not found,
* false otherwise.
* false otherwise.
* @return true if the operation succeeded, false otherwise.
*/
public synchronized boolean updateSong(final SongDisplayable song, boolean addIfNotFound) {
Expand Down Expand Up @@ -302,24 +312,41 @@ public synchronized boolean updateSong(final SongDisplayable song, boolean addIf
/**
* Remove a song from the database.
* <p/>
*
* @param song the song to remove.
* @return true if the operation succeeded, false otherwise.
*/
public synchronized boolean removeSong(final SongDisplayable song) {
LOGGER.log(Level.INFO, "Removing song {0}", song.getID());
return removeSongs(List.of(song));
}

/**
* Remove songs from the database.
* <p/>
*
* @param songs the songs to remove.
* @return true if the operation succeeded, false otherwise.
*/
public synchronized boolean removeSongs(final List<SongDisplayable> songs) {
List<Long> ids = songs.stream().map(SongDisplayable::getID).collect(Collectors.toList());
LOGGER.log(Level.INFO, "Removing songs {0}", ids);
cacheSongs.clear();
try {
HibernateUtil.execute((Session session) -> {
Song deletedSong = new SongDao(session).getSongById(song.getID());
session.delete(deletedSong);
for (SongDisplayable song : songs) {
Song deletedSong = new SongDao(session).getSongById(song.getID());
session.delete(deletedSong);
}
});
} catch (IllegalStateException ex) {
LOGGER.log(Level.WARNING, "Couldn't remove song " + song.getID(), ex);
LOGGER.log(Level.WARNING, "Couldn't remove songs " + ids, ex);
return false;
}
index.remove(song);
for (SongDisplayable song : songs) {
index.remove(song);
}
fireUpdate();
LOGGER.log(Level.INFO, "Removed song {0}", song.getID());
LOGGER.log(Level.INFO, "Removed song {0}", ids);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/*
/*
* This file is part of Quelea, free projection software for churches.
*
*
*
*
* This program 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.
*
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
Expand Down Expand Up @@ -67,29 +67,13 @@ public LibraryPopupMenu() {
exportToPDF = new MenuItem(LabelGrabber.INSTANCE.getLabel("export.pdf.button"), new ImageView(new Image("file:icons/fileexport.png", 16, 16, false, true)));
exportToPDF.setOnAction(new ExportPDFSongActionHandler());
print = new MenuItem(LabelGrabber.INSTANCE.getLabel("library.print.song.text"), new ImageView(new Image("file:icons/fileprint.png", 16, 16, false, true)));
print.setOnAction(new EventHandler<ActionEvent>() {

@Override
public void handle(ActionEvent t) {
final SongDisplayable song = QueleaApp.get().getMainWindow().getMainPanel().getLibraryPanel().getLibrarySongPanel().getSongList().getSelectedValue();
if (song != null) {
if (song.hasChords()) {
Dialog.buildConfirmation(LabelGrabber.INSTANCE.getLabel("printing.options.text"), LabelGrabber.INSTANCE.getLabel("print.chords.question")).addYesButton(new EventHandler<ActionEvent>() {

@Override
public void handle(ActionEvent t) {
song.setPrintChords(true);
}
}).addNoButton(new EventHandler<ActionEvent>() {

@Override
public void handle(ActionEvent t) {
song.setPrintChords(false);
}
}).build().showAndWait();
}
Printer.getInstance().print(song);
print.setOnAction(t -> {
final SongDisplayable song = QueleaApp.get().getMainWindow().getMainPanel().getLibraryPanel().getLibrarySongPanel().getSongList().getSelectedValues().get(0);
if (song != null) {
if (song.hasChords()) {
Dialog.buildConfirmation(LabelGrabber.INSTANCE.getLabel("printing.options.text"), LabelGrabber.INSTANCE.getLabel("print.chords.question")).addYesButton(t12 -> song.setPrintChords(true)).addNoButton(t1 -> song.setPrintChords(false)).build().showAndWait();
}
Printer.getInstance().print(song);
}
});

Expand All @@ -101,4 +85,14 @@ public void handle(ActionEvent t) {
getItems().add(exportToPDF);
getItems().add(print);
}

public void setMultipleSelected(boolean multipleSelected) {
addToSchedule.setDisable(false);
copyToSchedule.setDisable(false);
preview.setDisable(multipleSelected);
editDB.setDisable(multipleSelected);
removeFromDB.setDisable(false);
exportToPDF.setDisable(multipleSelected);
print.setDisable(multipleSelected);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package org.quelea.windows.library;

import java.util.List;
import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -31,6 +32,7 @@
import javafx.geometry.Pos;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.KeyCode;
Expand Down Expand Up @@ -60,21 +62,20 @@ public class LibrarySongList extends StackPane {

private static final Logger LOGGER = LoggerUtils.getLogger();
private final LibraryPopupMenu popupMenu;
private ListView<SongDisplayable> songList;
private LoadingPane loadingOverlay;
private LibrarySongPreviewCanvas previewCanvas;
private AddSongPromptOverlay addSongOverlay;
private final ListView<SongDisplayable> songList;
private final LoadingPane loadingOverlay;
private final LibrarySongPreviewCanvas previewCanvas;
private final AddSongPromptOverlay addSongOverlay;

/**
* Create a new library song list.
* <p/>
* @param popup true if we want a popup menu to appear on items in this list
* when right clicked, false if not.
* @popup true if this list should popup a context menu when right clicked,
* false otherwise.
* when right-clicked, false if not.
*/
public LibrarySongList(boolean popup) {
songList = new ListView<>();
songList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
loadingOverlay = new LoadingPane();
addSongOverlay = new AddSongPromptOverlay();
setAlignment(Pos.CENTER);
Expand All @@ -101,7 +102,7 @@ public LibrarySongList(boolean popup) {
}
});
Callback<ListView<SongDisplayable>, ListCell<SongDisplayable>> callback = (lv) -> {
final ListCell<SongDisplayable> cell = new ListCell<SongDisplayable>() {
final ListCell<SongDisplayable> cell = new ListCell<>() {
@Override
protected void updateItem(SongDisplayable item, boolean empty) {
super.updateItem(item, empty);
Expand Down Expand Up @@ -158,6 +159,7 @@ protected void updateItem(SongDisplayable item, boolean empty) {
}
});
songList.selectionModelProperty().get().selectedItemProperty().addListener((observable, oldSong, song) -> {
popupMenu.setMultipleSelected(songList.getSelectionModel().getSelectedIndices().size()>1);
if (previewCanvas != null) {
previewCanvas.setSong(song);
if (song != null && QueleaProperties.get().getShowDBSongPreview()) {
Expand All @@ -178,14 +180,8 @@ protected void updateItem(SongDisplayable item, boolean empty) {
if (popup) {
songList.setCellFactory(DisplayableListCell.forListView(popupMenu, callback, null));
}
new Thread() {
public void run() {
refresh();
}
}.start();
SongManager.get().registerDatabaseListener(() -> {
refresh();
});
new Thread(this::refresh).start();
SongManager.get().registerDatabaseListener(this::refresh);
}
private ExecutorService filterService = Executors.newSingleThreadExecutor();
private Future<?> filterFuture;
Expand Down Expand Up @@ -268,12 +264,12 @@ public void filter(final String search) {
}

/**
* Get the currently selected song.
* Get the currently selected songs.
* <p/>
* @return the currently selected song, or null if none is selected.
* @return the currently selected song, or an empty list if none is selected.
*/
public SongDisplayable getSelectedValue() {
return songList.selectionModelProperty().get().getSelectedItem();
public List<SongDisplayable> getSelectedValues() {
return songList.selectionModelProperty().get().getSelectedItems();
}

/**
Expand All @@ -299,15 +295,16 @@ private void refresh() {
setLoading(true);
});
final ObservableList<SongDisplayable> songs = FXCollections.observableArrayList(SongManager.get().getSongs(loadingOverlay));
Platform.runLater(new Runnable() {
@Override
public void run() {
songList.itemsProperty().set(songs);
setLoading(false);
}
Platform.runLater(() -> {
songList.itemsProperty().set(songs);
setLoading(false);
});
}

private boolean hasMultipleSelected() {
return songList.getSelectionModel().getSelectedItems().size() > 1;
}

public void setLoading(boolean loading) {
if (loading) {
loadingOverlay.show();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.quelea.windows.main.QueleaApp;
import org.quelea.windows.main.schedule.SchedulePanel;

import java.util.List;

/**
* The action listener for adding a song, called when something fires off an
* action that adds a song from the library to the schedule.
Expand All @@ -50,21 +52,23 @@ public AddSongActionHandler(boolean updateInDB) {
public void handle(ActionEvent t) {
LibraryPanel libraryPanel = QueleaApp.get().getMainWindow().getMainPanel().getLibraryPanel();
SchedulePanel schedulePanel = QueleaApp.get().getMainWindow().getMainPanel().getSchedulePanel();
SongDisplayable song = libraryPanel.getLibrarySongPanel().getSongList().getSelectedValue();
if(QueleaProperties.get().getSongOverflow() || !updateInDB) {
song = new SongDisplayable(song);
}
if(!updateInDB) {
song.setID(-1);
song.setNoDBUpdate();
}
if(QueleaProperties.get().getUseDefaultTranslation()) {
String defaultTranslation = QueleaProperties.get().getDefaultTranslationName();
if(defaultTranslation!=null && !defaultTranslation.trim().isEmpty()) {
song.setCurrentTranslationLyrics(defaultTranslation);
List<SongDisplayable> songs = libraryPanel.getLibrarySongPanel().getSongList().getSelectedValues();
for(SongDisplayable song : songs) {
if (QueleaProperties.get().getSongOverflow() || !updateInDB) {
song = new SongDisplayable(song);
}
if (!updateInDB) {
song.setID(-1);
song.setNoDBUpdate();
}
if (QueleaProperties.get().getUseDefaultTranslation()) {
String defaultTranslation = QueleaProperties.get().getDefaultTranslationName();
if (defaultTranslation != null && !defaultTranslation.trim().isEmpty()) {
song.setCurrentTranslationLyrics(defaultTranslation);
}
}
schedulePanel.getScheduleList().add(song);
libraryPanel.getLibrarySongPanel().getSearchBox().clear();
}
schedulePanel.getScheduleList().add(song);
libraryPanel.getLibrarySongPanel().getSearchBox().clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class EditSongDBActionHandler implements EventHandler<ActionEvent> {
public void handle(ActionEvent t) {
Platform.runLater(() -> {
SongEntryWindow songEntryWindow = QueleaApp.get().getMainWindow().getSongEntryWindow();
SongDisplayable song = QueleaApp.get().getMainWindow().getMainPanel().getLibraryPanel().getLibrarySongPanel().getSongList().getSelectedValue();
SongDisplayable song = QueleaApp.get().getMainWindow().getMainPanel().getLibraryPanel().getLibrarySongPanel().getSongList().getSelectedValues().get(0);
if(song != null) {
songEntryWindow.resetEditSong(song);
songEntryWindow.show();
Expand Down
Loading

0 comments on commit c145972

Please sign in to comment.