forked from openlp/openlp
Initial support for multiple songbooks
This commit is contained in:
parent
339a8e9cd6
commit
d2ab4862b4
@ -37,7 +37,7 @@ class Ui_EditSongDialog(object):
|
|||||||
def setupUi(self, edit_song_dialog):
|
def setupUi(self, edit_song_dialog):
|
||||||
edit_song_dialog.setObjectName('edit_song_dialog')
|
edit_song_dialog.setObjectName('edit_song_dialog')
|
||||||
edit_song_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
|
edit_song_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
|
||||||
edit_song_dialog.resize(650, 400)
|
edit_song_dialog.resize(900, 600)
|
||||||
edit_song_dialog.setModal(True)
|
edit_song_dialog.setModal(True)
|
||||||
self.dialog_layout = QtWidgets.QVBoxLayout(edit_song_dialog)
|
self.dialog_layout = QtWidgets.QVBoxLayout(edit_song_dialog)
|
||||||
self.dialog_layout.setSpacing(8)
|
self.dialog_layout.setSpacing(8)
|
||||||
@ -173,22 +173,33 @@ class Ui_EditSongDialog(object):
|
|||||||
self.topic_remove_layout.addWidget(self.topic_remove_button)
|
self.topic_remove_layout.addWidget(self.topic_remove_button)
|
||||||
self.topics_layout.addLayout(self.topic_remove_layout)
|
self.topics_layout.addLayout(self.topic_remove_layout)
|
||||||
self.authors_right_layout.addWidget(self.topics_group_box)
|
self.authors_right_layout.addWidget(self.topics_group_box)
|
||||||
self.song_book_group_box = QtWidgets.QGroupBox(self.authors_tab)
|
self.songbook_group_box = QtWidgets.QGroupBox(self.authors_tab)
|
||||||
self.song_book_group_box.setObjectName('song_book_group_box')
|
self.songbook_group_box.setObjectName('songbook_group_box')
|
||||||
self.song_book_layout = QtWidgets.QFormLayout(self.song_book_group_box)
|
self.songbooks_layout = QtWidgets.QVBoxLayout(self.songbook_group_box)
|
||||||
self.song_book_layout.setObjectName('song_book_layout')
|
self.songbooks_layout.setObjectName('songbooks_layout')
|
||||||
self.song_book_name_label = QtWidgets.QLabel(self.song_book_group_box)
|
self.songbook_add_layout = QtWidgets.QHBoxLayout()
|
||||||
self.song_book_name_label.setObjectName('song_book_name_label')
|
self.songbook_add_layout.setObjectName('songbook_add_layout')
|
||||||
self.song_book_combo_box = create_combo_box(self.song_book_group_box, 'song_book_combo_box')
|
self.songbooks_combo_box = create_combo_box(self.songbook_group_box, 'songbooks_combo_box')
|
||||||
self.song_book_name_label.setBuddy(self.song_book_combo_box)
|
self.songbook_add_layout.addWidget(self.songbooks_combo_box)
|
||||||
self.song_book_layout.addRow(self.song_book_name_label, self.song_book_combo_box)
|
self.songbook_entry_edit = QtWidgets.QLineEdit(self.songbook_group_box)
|
||||||
self.song_book_number_label = QtWidgets.QLabel(self.song_book_group_box)
|
self.songbook_entry_edit.setMaximumWidth(100)
|
||||||
self.song_book_number_label.setObjectName('song_book_number_label')
|
self.songbook_add_layout.addWidget(self.songbook_entry_edit)
|
||||||
self.song_book_number_edit = QtWidgets.QLineEdit(self.song_book_group_box)
|
self.songbook_add_button = QtWidgets.QPushButton(self.songbook_group_box)
|
||||||
self.song_book_number_edit.setObjectName('song_book_number_edit')
|
self.songbook_add_button.setObjectName('songbook_add_button')
|
||||||
self.song_book_number_label.setBuddy(self.song_book_number_edit)
|
self.songbook_add_layout.addWidget(self.songbook_add_button)
|
||||||
self.song_book_layout.addRow(self.song_book_number_label, self.song_book_number_edit)
|
self.songbooks_layout.addLayout(self.songbook_add_layout)
|
||||||
self.authors_right_layout.addWidget(self.song_book_group_box)
|
self.songbooks_list_view = QtWidgets.QListWidget(self.songbook_group_box)
|
||||||
|
self.songbooks_list_view.setAlternatingRowColors(True)
|
||||||
|
self.songbooks_list_view.setObjectName('songbooks_list_view')
|
||||||
|
self.songbooks_layout.addWidget(self.songbooks_list_view)
|
||||||
|
self.songbook_remove_layout = QtWidgets.QHBoxLayout()
|
||||||
|
self.songbook_remove_layout.setObjectName('songbook_remove_layout')
|
||||||
|
self.songbook_remove_layout.addStretch()
|
||||||
|
self.songbook_remove_button = QtWidgets.QPushButton(self.songbook_group_box)
|
||||||
|
self.songbook_remove_button.setObjectName('songbook_remove_button')
|
||||||
|
self.songbook_remove_layout.addWidget(self.songbook_remove_button)
|
||||||
|
self.songbooks_layout.addLayout(self.songbook_remove_layout)
|
||||||
|
self.authors_right_layout.addWidget(self.songbook_group_box)
|
||||||
self.authors_tab_layout.addLayout(self.authors_right_layout)
|
self.authors_tab_layout.addLayout(self.authors_right_layout)
|
||||||
self.song_tab_widget.addTab(self.authors_tab, '')
|
self.song_tab_widget.addTab(self.authors_tab, '')
|
||||||
# theme tab
|
# theme tab
|
||||||
@ -303,15 +314,15 @@ class Ui_EditSongDialog(object):
|
|||||||
self.author_add_button.setText(translate('SongsPlugin.EditSongForm', '&Add to Song'))
|
self.author_add_button.setText(translate('SongsPlugin.EditSongForm', '&Add to Song'))
|
||||||
self.author_edit_button.setText(translate('SongsPlugin.EditSongForm', '&Edit Author Type'))
|
self.author_edit_button.setText(translate('SongsPlugin.EditSongForm', '&Edit Author Type'))
|
||||||
self.author_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
|
self.author_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
|
||||||
self.maintenance_button.setText(translate('SongsPlugin.EditSongForm', '&Manage Authors, Topics, Song Books'))
|
self.maintenance_button.setText(translate('SongsPlugin.EditSongForm', '&Manage Authors, Topics, Songbooks'))
|
||||||
self.topics_group_box.setTitle(SongStrings.Topic)
|
self.topics_group_box.setTitle(SongStrings.Topics)
|
||||||
self.topic_add_button.setText(translate('SongsPlugin.EditSongForm', 'A&dd to Song'))
|
self.topic_add_button.setText(translate('SongsPlugin.EditSongForm', 'A&dd to Song'))
|
||||||
self.topic_remove_button.setText(translate('SongsPlugin.EditSongForm', 'R&emove'))
|
self.topic_remove_button.setText(translate('SongsPlugin.EditSongForm', 'R&emove'))
|
||||||
self.song_book_group_box.setTitle(SongStrings.SongBook)
|
self.songbook_group_box.setTitle(SongStrings.SongBooks)
|
||||||
self.song_book_name_label.setText(translate('SongsPlugin.EditSongForm', 'Book:'))
|
self.songbook_add_button.setText(translate('SongsPlugin.EditSongForm', 'Add &to Song'))
|
||||||
self.song_book_number_label.setText(translate('SongsPlugin.EditSongForm', 'Number:'))
|
self.songbook_remove_button.setText(translate('SongsPlugin.EditSongForm', 'Re&move'))
|
||||||
self.song_tab_widget.setTabText(self.song_tab_widget.indexOf(self.authors_tab),
|
self.song_tab_widget.setTabText(self.song_tab_widget.indexOf(self.authors_tab),
|
||||||
translate('SongsPlugin.EditSongForm', 'Authors, Topics && Song Book'))
|
translate('SongsPlugin.EditSongForm', 'Authors, Topics && Songbooks'))
|
||||||
self.theme_group_box.setTitle(UiStrings().Theme)
|
self.theme_group_box.setTitle(UiStrings().Theme)
|
||||||
self.theme_add_button.setText(translate('SongsPlugin.EditSongForm', 'New &Theme'))
|
self.theme_add_button.setText(translate('SongsPlugin.EditSongForm', 'New &Theme'))
|
||||||
self.rights_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Copyright Information'))
|
self.rights_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Copyright Information'))
|
||||||
|
@ -35,7 +35,7 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStri
|
|||||||
from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list
|
from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list
|
||||||
from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
|
from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
|
||||||
from openlp.plugins.songs.lib import VerseType, clean_song
|
from openlp.plugins.songs.lib import VerseType, clean_song
|
||||||
from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile
|
from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile, SongBookEntry
|
||||||
from openlp.plugins.songs.lib.ui import SongStrings
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
from openlp.plugins.songs.lib.openlyricsxml import SongXML
|
from openlp.plugins.songs.lib.openlyricsxml import SongXML
|
||||||
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
|
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
|
||||||
@ -69,6 +69,9 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
self.topic_add_button.clicked.connect(self.on_topic_add_button_clicked)
|
self.topic_add_button.clicked.connect(self.on_topic_add_button_clicked)
|
||||||
self.topic_remove_button.clicked.connect(self.on_topic_remove_button_clicked)
|
self.topic_remove_button.clicked.connect(self.on_topic_remove_button_clicked)
|
||||||
self.topics_list_view.itemClicked.connect(self.on_topic_list_view_clicked)
|
self.topics_list_view.itemClicked.connect(self.on_topic_list_view_clicked)
|
||||||
|
self.songbook_add_button.clicked.connect(self.on_songbook_add_button_clicked)
|
||||||
|
self.songbook_remove_button.clicked.connect(self.on_songbook_remove_button_clicked)
|
||||||
|
self.songbooks_list_view.itemClicked.connect(self.on_songbook_list_view_clicked)
|
||||||
self.copyright_insert_button.clicked.connect(self.on_copyright_insert_button_triggered)
|
self.copyright_insert_button.clicked.connect(self.on_copyright_insert_button_triggered)
|
||||||
self.verse_add_button.clicked.connect(self.on_verse_add_button_clicked)
|
self.verse_add_button.clicked.connect(self.on_verse_add_button_clicked)
|
||||||
self.verse_list_widget.doubleClicked.connect(self.on_verse_edit_button_clicked)
|
self.verse_list_widget.doubleClicked.connect(self.on_verse_edit_button_clicked)
|
||||||
@ -125,6 +128,11 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
author_item.setData(QtCore.Qt.UserRole, (author.id, author_type))
|
author_item.setData(QtCore.Qt.UserRole, (author.id, author_type))
|
||||||
self.authors_list_view.addItem(author_item)
|
self.authors_list_view.addItem(author_item)
|
||||||
|
|
||||||
|
def add_songbookentry_to_list(self, songbook_id, songbook_name, entry):
|
||||||
|
songbookentry_item = QtWidgets.QListWidgetItem(SongBookEntry.get_display_name(songbook_name, entry))
|
||||||
|
songbookentry_item.setData(QtCore.Qt.UserRole, (songbook_id, entry))
|
||||||
|
self.songbooks_list_view.addItem(songbookentry_item)
|
||||||
|
|
||||||
def _extract_verse_order(self, verse_order):
|
def _extract_verse_order(self, verse_order):
|
||||||
"""
|
"""
|
||||||
Split out the verse order
|
Split out the verse order
|
||||||
@ -219,17 +227,6 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount())
|
result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount())
|
||||||
if not result:
|
if not result:
|
||||||
return False
|
return False
|
||||||
text = self.song_book_combo_box.currentText()
|
|
||||||
if self.song_book_combo_box.findText(text, QtCore.Qt.MatchExactly) < 0:
|
|
||||||
if QtWidgets.QMessageBox.question(
|
|
||||||
self, translate('SongsPlugin.EditSongForm', 'Add Book'),
|
|
||||||
translate('SongsPlugin.EditSongForm', 'This song book does not exist, do you want to add it?'),
|
|
||||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
|
||||||
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
|
||||||
book = Book.populate(name=text, publisher='')
|
|
||||||
self.manager.save_object(book)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
# Validate tags (lp#1199639)
|
# Validate tags (lp#1199639)
|
||||||
misplaced_tags = []
|
misplaced_tags = []
|
||||||
verse_tags = []
|
verse_tags = []
|
||||||
@ -327,6 +324,9 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
if self.topics_combo_box.hasFocus() and self.topics_combo_box.currentText():
|
if self.topics_combo_box.hasFocus() and self.topics_combo_box.currentText():
|
||||||
self.on_topic_add_button_clicked()
|
self.on_topic_add_button_clicked()
|
||||||
return
|
return
|
||||||
|
if self.songbooks_combo_box.hasFocus() and self.songbooks_combo_box.currentText():
|
||||||
|
self.on_songbook_add_button_clicked()
|
||||||
|
return
|
||||||
QtWidgets.QDialog.keyPressEvent(self, event)
|
QtWidgets.QDialog.keyPressEvent(self, event)
|
||||||
|
|
||||||
def initialise(self):
|
def initialise(self):
|
||||||
@ -367,12 +367,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
self.topics = []
|
self.topics = []
|
||||||
self._load_objects(Topic, self.topics_combo_box, self.topics)
|
self._load_objects(Topic, self.topics_combo_box, self.topics)
|
||||||
|
|
||||||
def load_books(self):
|
def load_songbooks(self):
|
||||||
"""
|
"""
|
||||||
Load the song books into the combobox
|
Load the Songbooks into the combobox
|
||||||
"""
|
"""
|
||||||
self.books = []
|
self.songbooks = []
|
||||||
self._load_objects(Book, self.song_book_combo_box, self.books)
|
self._load_objects(Book, self.songbooks_combo_box, self.songbooks)
|
||||||
|
|
||||||
def load_themes(self, theme_list):
|
def load_themes(self, theme_list):
|
||||||
"""
|
"""
|
||||||
@ -413,12 +413,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
self.verse_list_widget.setRowCount(0)
|
self.verse_list_widget.setRowCount(0)
|
||||||
self.authors_list_view.clear()
|
self.authors_list_view.clear()
|
||||||
self.topics_list_view.clear()
|
self.topics_list_view.clear()
|
||||||
|
self.songbooks_list_view.clear()
|
||||||
self.audio_list_widget.clear()
|
self.audio_list_widget.clear()
|
||||||
self.title_edit.setFocus()
|
self.title_edit.setFocus()
|
||||||
self.song_book_number_edit.clear()
|
|
||||||
self.load_authors()
|
self.load_authors()
|
||||||
self.load_topics()
|
self.load_topics()
|
||||||
self.load_books()
|
self.load_songbooks()
|
||||||
self.load_media_files()
|
self.load_media_files()
|
||||||
self.theme_combo_box.setEditText('')
|
self.theme_combo_box.setEditText('')
|
||||||
self.theme_combo_box.setCurrentIndex(0)
|
self.theme_combo_box.setCurrentIndex(0)
|
||||||
@ -437,18 +437,11 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
self.song_tab_widget.setCurrentIndex(0)
|
self.song_tab_widget.setCurrentIndex(0)
|
||||||
self.load_authors()
|
self.load_authors()
|
||||||
self.load_topics()
|
self.load_topics()
|
||||||
self.load_books()
|
self.load_songbooks()
|
||||||
self.load_media_files()
|
self.load_media_files()
|
||||||
self.song = self.manager.get_object(Song, song_id)
|
self.song = self.manager.get_object(Song, song_id)
|
||||||
self.title_edit.setText(self.song.title)
|
self.title_edit.setText(self.song.title)
|
||||||
self.alternative_edit.setText(
|
self.alternative_edit.setText(self.song.alternate_title if self.song.alternate_title else '')
|
||||||
self.song.alternate_title if self.song.alternate_title else '')
|
|
||||||
if self.song.song_book_id != 0:
|
|
||||||
book_name = self.manager.get_object(Book, self.song.song_book_id)
|
|
||||||
find_and_set_in_combo_box(self.song_book_combo_box, str(book_name.name))
|
|
||||||
else:
|
|
||||||
self.song_book_combo_box.setEditText('')
|
|
||||||
self.song_book_combo_box.setCurrentIndex(0)
|
|
||||||
if self.song.theme_name:
|
if self.song.theme_name:
|
||||||
find_and_set_in_combo_box(self.theme_combo_box, str(self.song.theme_name))
|
find_and_set_in_combo_box(self.theme_combo_box, str(self.song.theme_name))
|
||||||
else:
|
else:
|
||||||
@ -458,7 +451,6 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
self.copyright_edit.setText(self.song.copyright if self.song.copyright else '')
|
self.copyright_edit.setText(self.song.copyright if self.song.copyright else '')
|
||||||
self.comments_edit.setPlainText(self.song.comments if self.song.comments else '')
|
self.comments_edit.setPlainText(self.song.comments if self.song.comments else '')
|
||||||
self.ccli_number_edit.setText(self.song.ccli_number if self.song.ccli_number else '')
|
self.ccli_number_edit.setText(self.song.ccli_number if self.song.ccli_number else '')
|
||||||
self.song_book_number_edit.setText(self.song.song_number if self.song.song_number else '')
|
|
||||||
# lazy xml migration for now
|
# lazy xml migration for now
|
||||||
self.verse_list_widget.clear()
|
self.verse_list_widget.clear()
|
||||||
self.verse_list_widget.setRowCount(0)
|
self.verse_list_widget.setRowCount(0)
|
||||||
@ -520,6 +512,9 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
topic_name = QtWidgets.QListWidgetItem(str(topic.name))
|
topic_name = QtWidgets.QListWidgetItem(str(topic.name))
|
||||||
topic_name.setData(QtCore.Qt.UserRole, topic.id)
|
topic_name.setData(QtCore.Qt.UserRole, topic.id)
|
||||||
self.topics_list_view.addItem(topic_name)
|
self.topics_list_view.addItem(topic_name)
|
||||||
|
self.songbooks_list_view.clear()
|
||||||
|
for songbookentry in self.song.songbookentries:
|
||||||
|
self.add_songbookentry_to_list(songbookentry.songbook.id, songbookentry.songbook.name, songbookentry.entry)
|
||||||
self.audio_list_widget.clear()
|
self.audio_list_widget.clear()
|
||||||
for media in self.song.media_files:
|
for media in self.song.media_files:
|
||||||
media_file = QtWidgets.QListWidgetItem(os.path.split(media.file_name)[1])
|
media_file = QtWidgets.QListWidgetItem(os.path.split(media.file_name)[1])
|
||||||
@ -678,6 +673,48 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
row = self.topics_list_view.row(item)
|
row = self.topics_list_view.row(item)
|
||||||
self.topics_list_view.takeItem(row)
|
self.topics_list_view.takeItem(row)
|
||||||
|
|
||||||
|
def on_songbook_add_button_clicked(self):
|
||||||
|
item = int(self.songbooks_combo_box.currentIndex())
|
||||||
|
text = self.songbooks_combo_box.currentText()
|
||||||
|
if item == 0 and text:
|
||||||
|
if QtWidgets.QMessageBox.question(
|
||||||
|
self, translate('SongsPlugin.EditSongForm', 'Add Songbook'),
|
||||||
|
translate('SongsPlugin.EditSongForm', 'This Songbook does not exist, do you want to add it?'),
|
||||||
|
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||||||
|
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
||||||
|
songbook = Book.populate(name=text)
|
||||||
|
self.manager.save_object(songbook)
|
||||||
|
self.add_songbookentry_to_list(songbook.id, songbook.name, self.songbook_entry_edit.text())
|
||||||
|
self.load_songbooks()
|
||||||
|
self.songbooks_combo_box.setCurrentIndex(0)
|
||||||
|
self.songbook_entry_edit.setText("")
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
elif item > 0:
|
||||||
|
item_id = (self.songbooks_combo_box.itemData(item))
|
||||||
|
songbook = self.manager.get_object(Book, item_id)
|
||||||
|
if self.songbooks_list_view.findItems(str(songbook.name), QtCore.Qt.MatchExactly):
|
||||||
|
critical_error_message_box(
|
||||||
|
message=translate('SongsPlugin.EditSongForm', 'This Songbook is already in the list.'))
|
||||||
|
else:
|
||||||
|
self.add_songbookentry_to_list(songbook.id, songbook.name, self.songbook_entry_edit.text())
|
||||||
|
self.songbooks_combo_box.setCurrentIndex(0)
|
||||||
|
self.songbook_entry_edit.setText("")
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, UiStrings().NISs,
|
||||||
|
translate('SongsPlugin.EditSongForm', 'You have not selected a valid Songbook. Either select a '
|
||||||
|
'Songbook from the list, or type in a new Songbook and click the "Add to Song" '
|
||||||
|
'button to add the new Songbook.'))
|
||||||
|
|
||||||
|
def on_songbook_list_view_clicked(self):
|
||||||
|
self.songbook_remove_button.setEnabled(True)
|
||||||
|
|
||||||
|
def on_songbook_remove_button_clicked(self):
|
||||||
|
self.songbook_remove_button.setEnabled(False)
|
||||||
|
row = self.songbooks_list_view.row(self.songbooks_list_view.currentItem())
|
||||||
|
self.songbooks_list_view.takeItem(row)
|
||||||
|
|
||||||
def on_verse_list_view_clicked(self):
|
def on_verse_list_view_clicked(self):
|
||||||
self.verse_edit_button.setEnabled(True)
|
self.verse_edit_button.setEnabled(True)
|
||||||
self.verse_delete_button.setEnabled(True)
|
self.verse_delete_button.setEnabled(True)
|
||||||
@ -838,17 +875,10 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Maintenance button pressed
|
Maintenance button pressed
|
||||||
"""
|
"""
|
||||||
temp_song_book = None
|
|
||||||
item = int(self.song_book_combo_box.currentIndex())
|
|
||||||
text = self.song_book_combo_box.currentText()
|
|
||||||
if item == 0 and text:
|
|
||||||
temp_song_book = text
|
|
||||||
self.media_item.song_maintenance_form.exec(True)
|
self.media_item.song_maintenance_form.exec(True)
|
||||||
self.load_authors()
|
self.load_authors()
|
||||||
self.load_books()
|
self.load_songbooks()
|
||||||
self.load_topics()
|
self.load_topics()
|
||||||
if temp_song_book:
|
|
||||||
self.song_book_combo_box.setEditText(temp_song_book)
|
|
||||||
|
|
||||||
def on_preview(self, button):
|
def on_preview(self, button):
|
||||||
"""
|
"""
|
||||||
@ -928,7 +958,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
log.debug('SongEditForm.clearCaches')
|
log.debug('SongEditForm.clearCaches')
|
||||||
self.authors = []
|
self.authors = []
|
||||||
self.themes = []
|
self.themes = []
|
||||||
self.books = []
|
self.songbooks = []
|
||||||
self.topics = []
|
self.topics = []
|
||||||
|
|
||||||
def reject(self):
|
def reject(self):
|
||||||
@ -977,12 +1007,6 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
order.append('%s%s' % (verse_tag, verse_num))
|
order.append('%s%s' % (verse_tag, verse_num))
|
||||||
self.song.verse_order = ' '.join(order)
|
self.song.verse_order = ' '.join(order)
|
||||||
self.song.ccli_number = self.ccli_number_edit.text()
|
self.song.ccli_number = self.ccli_number_edit.text()
|
||||||
self.song.song_number = self.song_book_number_edit.text()
|
|
||||||
book_name = self.song_book_combo_box.currentText()
|
|
||||||
if book_name:
|
|
||||||
self.song.book = self.manager.get_object_filtered(Book, Book.name == book_name)
|
|
||||||
else:
|
|
||||||
self.song.book = None
|
|
||||||
theme_name = self.theme_combo_box.currentText()
|
theme_name = self.theme_combo_box.currentText()
|
||||||
if theme_name:
|
if theme_name:
|
||||||
self.song.theme_name = theme_name
|
self.song.theme_name = theme_name
|
||||||
@ -1001,6 +1025,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
topic = self.manager.get_object(Topic, topic_id)
|
topic = self.manager.get_object(Topic, topic_id)
|
||||||
if topic is not None:
|
if topic is not None:
|
||||||
self.song.topics.append(topic)
|
self.song.topics.append(topic)
|
||||||
|
for row in range(self.songbooks_list_view.count()):
|
||||||
|
item = self.songbooks_list_view.item(row)
|
||||||
|
songbook_id = item.data(QtCore.Qt.UserRole)[0]
|
||||||
|
songbook = self.manager.get_object(Book, songbook_id)
|
||||||
|
entry = item.data(QtCore.Qt.UserRole)[1]
|
||||||
|
self.song.add_songbookentry(songbook, entry)
|
||||||
# Save the song here because we need a valid id for the audio files.
|
# Save the song here because we need a valid id for the audio files.
|
||||||
clean_song(self.manager, self.song)
|
clean_song(self.manager, self.song)
|
||||||
self.manager.save_object(self.song)
|
self.manager.save_object(self.song)
|
||||||
|
@ -160,6 +160,31 @@ class Song(BaseModel):
|
|||||||
self.authors_songs.remove(author_song)
|
self.authors_songs.remove(author_song)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def add_songbookentry(self, songbook, entry):
|
||||||
|
"""
|
||||||
|
Add a Songbook Entry to the song if it not yet exists
|
||||||
|
|
||||||
|
:param songbook_name: Name of the Songbook.
|
||||||
|
:param entry: Entry in the Songbook (usually a number)
|
||||||
|
"""
|
||||||
|
for songbookentry in self.songbookentries:
|
||||||
|
if songbookentry.songbook.name == songbook.name and songbookentry.entry == entry:
|
||||||
|
return
|
||||||
|
|
||||||
|
new_songbookentry = SongBookEntry()
|
||||||
|
new_songbookentry.songbook = songbook
|
||||||
|
new_songbookentry.entry = entry
|
||||||
|
self.songbookentries.append(new_songbookentry)
|
||||||
|
|
||||||
|
class SongBookEntry(BaseModel):
|
||||||
|
"""
|
||||||
|
SongBookEntry model
|
||||||
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def get_display_name(songbook_name, entry):
|
||||||
|
if entry:
|
||||||
|
return "%s #%s" % (songbook_name, entry)
|
||||||
|
return songbook_name
|
||||||
|
|
||||||
class Topic(BaseModel):
|
class Topic(BaseModel):
|
||||||
"""
|
"""
|
||||||
@ -182,6 +207,7 @@ def init_schema(url):
|
|||||||
* media_files_songs
|
* media_files_songs
|
||||||
* song_books
|
* song_books
|
||||||
* songs
|
* songs
|
||||||
|
* songs_songbooks
|
||||||
* songs_topics
|
* songs_topics
|
||||||
* topics
|
* topics
|
||||||
|
|
||||||
@ -222,7 +248,6 @@ def init_schema(url):
|
|||||||
The *songs* table has the following columns:
|
The *songs* table has the following columns:
|
||||||
|
|
||||||
* id
|
* id
|
||||||
* song_book_id
|
|
||||||
* title
|
* title
|
||||||
* alternate_title
|
* alternate_title
|
||||||
* lyrics
|
* lyrics
|
||||||
@ -230,11 +255,17 @@ def init_schema(url):
|
|||||||
* copyright
|
* copyright
|
||||||
* comments
|
* comments
|
||||||
* ccli_number
|
* ccli_number
|
||||||
* song_number
|
|
||||||
* theme_name
|
* theme_name
|
||||||
* search_title
|
* search_title
|
||||||
* search_lyrics
|
* search_lyrics
|
||||||
|
|
||||||
|
**songs_songsbooks Table**
|
||||||
|
This is a mapping table between the *songs* and the *song_books* tables. It has the following columns:
|
||||||
|
|
||||||
|
* songbook_id
|
||||||
|
* song_id
|
||||||
|
* entry # The song number, like 120 or 550A
|
||||||
|
|
||||||
**songs_topics Table**
|
**songs_topics Table**
|
||||||
This is a bridging table between the *songs* and *topics* tables, which
|
This is a bridging table between the *songs* and *topics* tables, which
|
||||||
serves to create a many-to-many relationship between the two tables. It
|
serves to create a many-to-many relationship between the two tables. It
|
||||||
@ -284,7 +315,6 @@ def init_schema(url):
|
|||||||
songs_table = Table(
|
songs_table = Table(
|
||||||
'songs', metadata,
|
'songs', metadata,
|
||||||
Column('id', types.Integer(), primary_key=True),
|
Column('id', types.Integer(), primary_key=True),
|
||||||
Column('song_book_id', types.Integer(), ForeignKey('song_books.id'), default=None),
|
|
||||||
Column('title', types.Unicode(255), nullable=False),
|
Column('title', types.Unicode(255), nullable=False),
|
||||||
Column('alternate_title', types.Unicode(255)),
|
Column('alternate_title', types.Unicode(255)),
|
||||||
Column('lyrics', types.UnicodeText, nullable=False),
|
Column('lyrics', types.UnicodeText, nullable=False),
|
||||||
@ -292,7 +322,6 @@ def init_schema(url):
|
|||||||
Column('copyright', types.Unicode(255)),
|
Column('copyright', types.Unicode(255)),
|
||||||
Column('comments', types.UnicodeText),
|
Column('comments', types.UnicodeText),
|
||||||
Column('ccli_number', types.Unicode(64)),
|
Column('ccli_number', types.Unicode(64)),
|
||||||
Column('song_number', types.Unicode(64)),
|
|
||||||
Column('theme_name', types.Unicode(128)),
|
Column('theme_name', types.Unicode(128)),
|
||||||
Column('search_title', types.Unicode(255), index=True, nullable=False),
|
Column('search_title', types.Unicode(255), index=True, nullable=False),
|
||||||
Column('search_lyrics', types.UnicodeText, nullable=False),
|
Column('search_lyrics', types.UnicodeText, nullable=False),
|
||||||
@ -316,6 +345,14 @@ def init_schema(url):
|
|||||||
Column('author_type', types.Unicode(255), primary_key=True, nullable=False, server_default=text('""'))
|
Column('author_type', types.Unicode(255), primary_key=True, nullable=False, server_default=text('""'))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Definition of the "songs_songbooks" table
|
||||||
|
songs_songbooks_table = Table(
|
||||||
|
'songs_songbooks', metadata,
|
||||||
|
Column('songbook_id', types.Integer(), ForeignKey('song_books.id'), primary_key=True),
|
||||||
|
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
|
||||||
|
Column('entry', types.Unicode(255), primary_key=True, nullable=False)
|
||||||
|
)
|
||||||
|
|
||||||
# Definition of the "songs_topics" table
|
# Definition of the "songs_topics" table
|
||||||
songs_topics_table = Table(
|
songs_topics_table = Table(
|
||||||
'songs_topics', metadata,
|
'songs_topics', metadata,
|
||||||
@ -329,6 +366,9 @@ def init_schema(url):
|
|||||||
mapper(AuthorSong, authors_songs_table, properties={
|
mapper(AuthorSong, authors_songs_table, properties={
|
||||||
'author': relation(Author)
|
'author': relation(Author)
|
||||||
})
|
})
|
||||||
|
mapper(SongBookEntry, songs_songbooks_table, properties={
|
||||||
|
'songbook': relation(Book)
|
||||||
|
})
|
||||||
mapper(Book, song_books_table)
|
mapper(Book, song_books_table)
|
||||||
mapper(MediaFile, media_files_table)
|
mapper(MediaFile, media_files_table)
|
||||||
mapper(Song, songs_table, properties={
|
mapper(Song, songs_table, properties={
|
||||||
@ -337,8 +377,8 @@ def init_schema(url):
|
|||||||
'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"),
|
'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"),
|
||||||
# Use lazy='joined' to always load authors when the song is fetched from the database (bug 1366198)
|
# Use lazy='joined' to always load authors when the song is fetched from the database (bug 1366198)
|
||||||
'authors': relation(Author, secondary=authors_songs_table, viewonly=True, lazy='joined'),
|
'authors': relation(Author, secondary=authors_songs_table, viewonly=True, lazy='joined'),
|
||||||
'book': relation(Book, backref='songs'),
|
|
||||||
'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
|
'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
|
||||||
|
'songbookentries': relation(SongBookEntry, cascade="all, delete-orphan"),
|
||||||
'topics': relation(Topic, backref='songs', secondary=songs_topics_table)
|
'topics': relation(Topic, backref='songs', secondary=songs_topics_table)
|
||||||
})
|
})
|
||||||
mapper(Topic, topics_table)
|
mapper(Topic, topics_table)
|
||||||
|
@ -266,13 +266,12 @@ class OpenLyrics(object):
|
|||||||
element.set('type', AuthorType.Music)
|
element.set('type', AuthorType.Music)
|
||||||
else:
|
else:
|
||||||
element.set('type', author_song.author_type)
|
element.set('type', author_song.author_type)
|
||||||
book = self.manager.get_object_filtered(Book, Book.id == song.song_book_id)
|
if song.songbookentries:
|
||||||
if book is not None:
|
|
||||||
book = book.name
|
|
||||||
songbooks = etree.SubElement(properties, 'songbooks')
|
songbooks = etree.SubElement(properties, 'songbooks')
|
||||||
element = self._add_text_to_element('songbook', songbooks, None, book)
|
for songbookentry in song.songbookentries:
|
||||||
if song.song_number:
|
element = self._add_text_to_element('songbook', songbooks, None, songbookentry.songbook)
|
||||||
element.set('entry', song.song_number)
|
if songbookentry.entry:
|
||||||
|
element.set('entry', songbookentry.entry)
|
||||||
if song.topics:
|
if song.topics:
|
||||||
themes = etree.SubElement(properties, 'themes')
|
themes = etree.SubElement(properties, 'themes')
|
||||||
for topic in song.topics:
|
for topic in song.topics:
|
||||||
@ -744,8 +743,6 @@ class OpenLyrics(object):
|
|||||||
:param properties: The property object (lxml.objectify.ObjectifiedElement).
|
:param properties: The property object (lxml.objectify.ObjectifiedElement).
|
||||||
:param song: The song object.
|
:param song: The song object.
|
||||||
"""
|
"""
|
||||||
song.song_book_id = None
|
|
||||||
song.song_number = ''
|
|
||||||
if hasattr(properties, 'songbooks'):
|
if hasattr(properties, 'songbooks'):
|
||||||
for songbook in properties.songbooks.songbook:
|
for songbook in properties.songbooks.songbook:
|
||||||
book_name = songbook.get('name', '')
|
book_name = songbook.get('name', '')
|
||||||
@ -755,10 +752,7 @@ class OpenLyrics(object):
|
|||||||
# We need to create a book, because it does not exist.
|
# We need to create a book, because it does not exist.
|
||||||
book = Book.populate(name=book_name, publisher='')
|
book = Book.populate(name=book_name, publisher='')
|
||||||
self.manager.save_object(book)
|
self.manager.save_object(book)
|
||||||
song.song_book_id = book.id
|
song.add_songbookentry(book, songbook.get('entry', ''))
|
||||||
song.song_number = songbook.get('entry', '')
|
|
||||||
# We only support one song book, so take the first one.
|
|
||||||
break
|
|
||||||
|
|
||||||
def _process_titles(self, properties, song):
|
def _process_titles(self, properties, song):
|
||||||
"""
|
"""
|
||||||
|
@ -35,8 +35,8 @@ class SongStrings(object):
|
|||||||
Authors = translate('OpenLP.Ui', 'Authors', 'Plural')
|
Authors = translate('OpenLP.Ui', 'Authors', 'Plural')
|
||||||
AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') # Used to populate the database.
|
AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') # Used to populate the database.
|
||||||
CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.')
|
CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.')
|
||||||
SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular')
|
SongBook = translate('OpenLP.Ui', 'Songbook', 'Singular')
|
||||||
SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural')
|
SongBooks = translate('OpenLP.Ui', 'Songbooks', 'Plural')
|
||||||
SongIncomplete = translate('OpenLP.Ui', 'Title and/or verses not found')
|
SongIncomplete = translate('OpenLP.Ui', 'Title and/or verses not found')
|
||||||
SongMaintenance = translate('OpenLP.Ui', 'Song Maintenance')
|
SongMaintenance = translate('OpenLP.Ui', 'Song Maintenance')
|
||||||
Topic = translate('OpenLP.Ui', 'Topic', 'Singular')
|
Topic = translate('OpenLP.Ui', 'Topic', 'Singular')
|
||||||
|
@ -29,10 +29,10 @@ from sqlalchemy import Table, Column, ForeignKey, types
|
|||||||
from sqlalchemy.sql.expression import func, false, null, text
|
from sqlalchemy.sql.expression import func, false, null, text
|
||||||
|
|
||||||
from openlp.core.lib.db import get_upgrade_op
|
from openlp.core.lib.db import get_upgrade_op
|
||||||
from openlp.core.common import trace_error_handler
|
from openlp.core.utils.db import drop_columns
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
__version__ = 4
|
__version__ = 5
|
||||||
|
|
||||||
|
|
||||||
# TODO: When removing an upgrade path the ftw-data needs updating to the minimum supported version
|
# TODO: When removing an upgrade path the ftw-data needs updating to the minimum supported version
|
||||||
@ -117,3 +117,29 @@ def upgrade_4(session, metadata):
|
|||||||
op.rename_table('authors_songs_tmp', 'authors_songs')
|
op.rename_table('authors_songs_tmp', 'authors_songs')
|
||||||
else:
|
else:
|
||||||
log.warning('Skipping upgrade_4 step of upgrading the song db')
|
log.warning('Skipping upgrade_4 step of upgrading the song db')
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade_5(session, metadata):
|
||||||
|
"""
|
||||||
|
Version 5 upgrade.
|
||||||
|
|
||||||
|
This upgrade adds support for multiple songbooks
|
||||||
|
"""
|
||||||
|
op = get_upgrade_op(session)
|
||||||
|
songs_table = Table('songs', metadata)
|
||||||
|
if 'song_book_id' in [col.name for col in songs_table.c.values()]:
|
||||||
|
log.warning('Skipping upgrade_5 step of upgrading the song db')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create the mapping table (songs <-> songbooks)
|
||||||
|
op.create_table('songs_songbooks',
|
||||||
|
Column('songbook_id', types.Integer(), ForeignKey('song_books.id'), primary_key=True),
|
||||||
|
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
|
||||||
|
Column('entry', types.Unicode(255), primary_key=True, nullable=False))
|
||||||
|
|
||||||
|
# Migrate old data
|
||||||
|
op.execute('INSERT INTO songs_songbooks SELECT song_book_id, id, song_number FROM songs\
|
||||||
|
WHERE song_book_id NOT NULL AND song_number NOT NULL')
|
||||||
|
|
||||||
|
# Drop old columns
|
||||||
|
drop_columns(op, 'songs', ['song_book_id', 'song_number'])
|
||||||
|
Loading…
Reference in New Issue
Block a user