openlp/openlp/plugins/songs/forms/editverseform.py

246 lines
10 KiB
Python

# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# 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 <https://www.gnu.org/licenses/>. #
##########################################################################
import logging
import re
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common.i18n import translate
from openlp.core.common.registry import Registry
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.songs.forms.editversedialog import Ui_EditVerseDialog
from openlp.plugins.songs.lib import VerseType, transpose_lyrics
log = logging.getLogger(__name__)
VERSE_REGEX = re.compile(r'---\[(.+):\D*(\d*)\D*.*\]---')
class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog):
"""
This is the form that is used to edit the verses of the song.
"""
def __init__(self, parent=None):
"""
Constructor
"""
super(EditVerseForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint |
QtCore.Qt.WindowCloseButtonHint)
self.setup_ui(self)
self.has_single_verse = False
self.insert_button.clicked.connect(self.on_insert_button_clicked)
self.overflow_split_button.clicked.connect(self.on_overflow_split_button_clicked)
self.verse_text_edit.cursorPositionChanged.connect(self.on_cursor_position_changed)
self.verse_type_combo_box.currentIndexChanged.connect(self.on_verse_type_combo_box_changed)
self.forced_split_button.clicked.connect(self.on_forced_split_button_clicked)
if Registry().get('settings').value('songs/enable chords'):
self.transpose_down_button.clicked.connect(self.on_transpose_down_button_clicked)
self.transpose_up_button.clicked.connect(self.on_transpose_up_button_clicked)
def insert_verse(self, verse_tag, verse_num=1):
"""
Insert a verse
:param verse_tag: The verse tag
:param verse_num: The verse number
"""
if self.verse_text_edit.textCursor().columnNumber() != 0:
self.verse_text_edit.insertPlainText('\n')
verse_tag = VerseType.translated_name(verse_tag)
self.verse_text_edit.insertPlainText('---[{tag}:{number}]---\n'.format(tag=verse_tag, number=verse_num))
self.verse_text_edit.setFocus()
def on_overflow_split_button_clicked(self):
"""
The optional split button has been pressed so we need add the split
"""
self._add_splitter_to_text('[---]')
def on_forced_split_button_clicked(self):
"""
The force split button has been pressed so we need add the split
"""
self._add_splitter_to_text('[--}{--]')
def _add_splitter_to_text(self, insert_string):
"""
Add a custom splitter to the song text
:param insert_string: The string to insert
:return:
"""
text = self.verse_text_edit.toPlainText()
position = self.verse_text_edit.textCursor().position()
if position and text[position - 1] != '\n':
insert_string = '\n' + insert_string
if position == len(text) or text[position] != '\n':
insert_string += '\n'
self.verse_text_edit.insertPlainText(insert_string)
self.verse_text_edit.setFocus()
def on_insert_button_clicked(self):
"""
The insert button has been pressed
"""
verse_type_index = self.verse_type_combo_box.currentIndex()
self.insert_verse(VerseType.tags[verse_type_index], self.verse_number_box.value())
def on_verse_type_combo_box_changed(self):
"""
The verse type combo has been changed
"""
self.update_suggested_verse_number()
def on_cursor_position_changed(self):
"""
The cursor position has been changed
"""
self.update_suggested_verse_number()
def on_transpose_up_button_clicked(self):
"""
The transpose up button clicked
"""
try:
transposed_lyrics = transpose_lyrics(self.verse_text_edit.toPlainText(), 1)
self.verse_text_edit.setPlainText(transposed_lyrics)
except ValueError as ve:
# Transposing failed
critical_error_message_box(title=translate('SongsPlugin.EditVerseForm', 'Transposing failed'),
message=translate('SongsPlugin.EditVerseForm',
'Transposing failed because of invalid chord:\n{err_msg}'
.format(err_msg=ve)))
return
self.verse_text_edit.setFocus()
self.verse_text_edit.moveCursor(QtGui.QTextCursor.End)
def on_transpose_down_button_clicked(self):
"""
The transpose down button clicked
"""
try:
transposed_lyrics = transpose_lyrics(self.verse_text_edit.toPlainText(), -1)
self.verse_text_edit.setPlainText(transposed_lyrics)
except ValueError as ve:
# Transposing failed
critical_error_message_box(title=translate('SongsPlugin.EditVerseForm', 'Transposing failed'),
message=translate('SongsPlugin.EditVerseForm',
'Transposing failed because of invalid chord:\n{err_msg}'
.format(err_msg=ve)))
return
self.verse_text_edit.setPlainText(transposed_lyrics)
self.verse_text_edit.setFocus()
self.verse_text_edit.moveCursor(QtGui.QTextCursor.End)
def update_suggested_verse_number(self):
"""
Adjusts the verse number SpinBox in regard to the selected verse type and the cursor's position.
"""
if self.has_single_verse:
return
position = self.verse_text_edit.textCursor().position()
text = self.verse_text_edit.toPlainText()
verse_name = VerseType.translated_names[
self.verse_type_combo_box.currentIndex()]
if not text:
return
position = text.rfind('---[{verse}'.format(verse=verse_name), 0, position)
if position == -1:
self.verse_number_box.setValue(1)
return
text = text[position:]
position = text.find(']---')
if position == -1:
return
text = text[:position + 4]
match = VERSE_REGEX.match(text)
if match:
try:
verse_num = int(match.group(2)) + 1
except ValueError:
verse_num = 1
self.verse_number_box.setValue(verse_num)
def set_verse(self, text, single=False, tag='{verse}1'.format(verse=VerseType.tags[VerseType.Verse])):
"""
Save the verse
:param text: The text
:param single: is this a single verse
:param tag: The tag
"""
self.has_single_verse = single
if single:
verse_type_index = VerseType.from_tag(tag[0], None)
verse_number = tag[1:]
if verse_type_index is not None:
self.verse_type_combo_box.setCurrentIndex(verse_type_index)
self.verse_number_box.setValue(int(verse_number))
self.insert_button.setVisible(False)
else:
if not text:
text = '---[{tag}:1]---\n'.format(tag=VerseType.translated_names[VerseType.Verse])
self.verse_type_combo_box.setCurrentIndex(0)
self.verse_number_box.setValue(1)
self.insert_button.setVisible(True)
self.verse_text_edit.setPlainText(text)
self.verse_text_edit.setFocus()
self.verse_text_edit.moveCursor(QtGui.QTextCursor.End)
def get_verse(self):
"""
Extract the verse text
:return: The text
"""
return self.verse_text_edit.toPlainText(), VerseType.tags[self.verse_type_combo_box.currentIndex()], \
str(self.verse_number_box.value())
def get_all_verses(self):
"""
Extract all the verses
:return: The text
"""
text = self.verse_text_edit.toPlainText()
if not text.startswith('---['):
text = '---[{tag}:1]---\n{text}'.format(tag=VerseType.translated_names[VerseType.Verse], text=text)
return text
def accept(self):
"""
Test if any invalid chords has been entered before closing the verse editor
"""
if Registry().get('settings').value('songs/enable chords'):
try:
transpose_lyrics(self.verse_text_edit.toPlainText(), 0)
super(EditVerseForm, self).accept()
except ValueError as ve:
# Transposing failed
critical_error_message_box(title=translate('SongsPlugin.EditVerseForm', 'Invalid Chord'),
message=translate('SongsPlugin.EditVerseForm',
'An invalid chord was detected:\n{err_msg}'
.format(err_msg=ve)))
else:
super(EditVerseForm, self).accept()