Merge branch 'option-to-hide-custom-formatting-tag-content-in-main-window' into 'master'

Option to hide custom formatting tag content in main window

See merge request openlp/openlp!414
This commit is contained in:
Tim Bentley 2022-02-11 19:13:51 +00:00
commit 4297124c02
9 changed files with 78 additions and 34 deletions

View File

@ -157,8 +157,11 @@ def remove_tags(text, can_remove_chords=False):
text = text.replace('<em>', '') text = text.replace('<em>', '')
text = text.replace('</em>', '') text = text.replace('</em>', '')
for tag in FormattingTags.get_html_tags(): for tag in FormattingTags.get_html_tags():
text = text.replace(tag['start tag'], '') if tag['hidden']:
text = text.replace(tag['end tag'], '') text = re.sub(r'' + tag['start tag'] + ".*?" + tag['end tag'], '', text)
else:
text = text.replace(tag['start tag'], '')
text = text.replace(tag['end tag'], '')
# Remove ChordPro tags # Remove ChordPro tags
if can_remove_chords: if can_remove_chords:
text = remove_chords(text) text = remove_chords(text)

View File

@ -64,85 +64,86 @@ class FormattingTags(object):
'start tag': '{r}', 'start tag': '{r}',
'start html': '<span style="-webkit-text-fill-color:red">', 'start html': '<span style="-webkit-text-fill-color:red">',
'end tag': '{/r}', 'end html': '</span>', 'protected': True, 'end tag': '{/r}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Black'), 'desc': translate('OpenLP.FormattingTags', 'Black'),
'start tag': '{b}', 'start tag': '{b}',
'start html': '<span style="-webkit-text-fill-color:black">', 'start html': '<span style="-webkit-text-fill-color:black">',
'end tag': '{/b}', 'end html': '</span>', 'protected': True, 'end tag': '{/b}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Blue'), 'desc': translate('OpenLP.FormattingTags', 'Blue'),
'start tag': '{bl}', 'start tag': '{bl}',
'start html': '<span style="-webkit-text-fill-color:blue">', 'start html': '<span style="-webkit-text-fill-color:blue">',
'end tag': '{/bl}', 'end html': '</span>', 'protected': True, 'end tag': '{/bl}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Yellow'), 'desc': translate('OpenLP.FormattingTags', 'Yellow'),
'start tag': '{y}', 'start tag': '{y}',
'start html': '<span style="-webkit-text-fill-color:yellow">', 'start html': '<span style="-webkit-text-fill-color:yellow">',
'end tag': '{/y}', 'end html': '</span>', 'protected': True, 'end tag': '{/y}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Green'), 'desc': translate('OpenLP.FormattingTags', 'Green'),
'start tag': '{g}', 'start tag': '{g}',
'start html': '<span style="-webkit-text-fill-color:green">', 'start html': '<span style="-webkit-text-fill-color:green">',
'end tag': '{/g}', 'end html': '</span>', 'protected': True, 'end tag': '{/g}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Pink'), 'desc': translate('OpenLP.FormattingTags', 'Pink'),
'start tag': '{pk}', 'start tag': '{pk}',
'start html': '<span style="-webkit-text-fill-color:#FFC0CB">', 'start html': '<span style="-webkit-text-fill-color:#FFC0CB">',
'end tag': '{/pk}', 'end html': '</span>', 'protected': True, 'end tag': '{/pk}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Orange'), 'desc': translate('OpenLP.FormattingTags', 'Orange'),
'start tag': '{o}', 'start tag': '{o}',
'start html': '<span style="-webkit-text-fill-color:#FFA500">', 'start html': '<span style="-webkit-text-fill-color:#FFA500">',
'end tag': '{/o}', 'end html': '</span>', 'protected': True, 'end tag': '{/o}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Purple'), 'desc': translate('OpenLP.FormattingTags', 'Purple'),
'start tag': '{pp}', 'start tag': '{pp}',
'start html': '<span style="-webkit-text-fill-color:#800080">', 'start html': '<span style="-webkit-text-fill-color:#800080">',
'end tag': '{/pp}', 'end html': '</span>', 'protected': True, 'end tag': '{/pp}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'White'), 'desc': translate('OpenLP.FormattingTags', 'White'),
'start tag': '{w}', 'start tag': '{w}',
'start html': '<span style="-webkit-text-fill-color:white">', 'start html': '<span style="-webkit-text-fill-color:white">',
'end tag': '{/w}', 'end html': '</span>', 'protected': True, 'end tag': '{/w}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Superscript'), 'desc': translate('OpenLP.FormattingTags', 'Superscript'),
'start tag': '{su}', 'start html': '<sup>', 'start tag': '{su}', 'start html': '<sup>',
'end tag': '{/su}', 'end html': '</sup>', 'protected': True, 'end tag': '{/su}', 'end html': '</sup>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Subscript'), 'desc': translate('OpenLP.FormattingTags', 'Subscript'),
'start tag': '{sb}', 'start html': '<sub>', 'start tag': '{sb}', 'start html': '<sub>',
'end tag': '{/sb}', 'end html': '</sub>', 'protected': True, 'end tag': '{/sb}', 'end html': '</sub>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Paragraph'), 'desc': translate('OpenLP.FormattingTags', 'Paragraph'),
'start tag': '{p}', 'start html': '<p>', 'end tag': '{/p}', 'start tag': '{p}', 'start html': '<p>', 'end tag': '{/p}',
'end html': '</p>', 'protected': True, 'end html': '</p>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Bold'), 'desc': translate('OpenLP.FormattingTags', 'Bold'),
'start tag': '{st}', 'start html': '<strong>', 'start tag': '{st}', 'start html': '<strong>',
'end tag': '{/st}', 'end html': '</strong>', 'end tag': '{/st}', 'end html': '</strong>',
'protected': True, 'temporary': False 'protected': True, 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Italics'), 'desc': translate('OpenLP.FormattingTags', 'Italics'),
'start tag': '{it}', 'start html': '<em>', 'end tag': '{/it}', 'start tag': '{it}', 'start html': '<em>', 'end tag': '{/it}',
'end html': '</em>', 'protected': True, 'temporary': False 'end html': '</em>', 'protected': True, 'temporary': False,
'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'Underline'), 'desc': translate('OpenLP.FormattingTags', 'Underline'),
'start tag': '{u}', 'start tag': '{u}',
'start html': '<span style="text-decoration: underline;">', 'start html': '<span style="text-decoration: underline;">',
'end tag': '{/u}', 'end html': '</span>', 'protected': True, 'end tag': '{/u}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, { }, {
'desc': translate('OpenLP.FormattingTags', 'UpperCase'), 'desc': translate('OpenLP.FormattingTags', 'UpperCase'),
'start tag': '{uc}', 'start tag': '{uc}',
@ -153,7 +154,7 @@ class FormattingTags(object):
'desc': translate('OpenLP.FormattingTags', 'Break'), 'desc': translate('OpenLP.FormattingTags', 'Break'),
'start tag': '{br}', 'start html': '<br>', 'end tag': '', 'start tag': '{br}', 'start html': '<br>', 'end tag': '',
'end html': '', 'protected': True, 'end html': '', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}] }]
# Append the base tags. # Append the base tags.
FormattingTags.add_html_tags(base_tags) FormattingTags.add_html_tags(base_tags)
@ -162,6 +163,9 @@ class FormattingTags(object):
# If we have some user ones added them as well # If we have some user ones added them as well
if user_expands_string: if user_expands_string:
user_tags = json.loads(user_expands_string) user_tags = json.loads(user_expands_string)
for tag in user_tags:
if 'hidden' not in tag.keys():
tag['hidden'] = False
FormattingTags.add_html_tags(user_tags) FormattingTags.add_html_tags(user_tags)
@staticmethod @staticmethod

View File

@ -52,7 +52,7 @@ class FormattingTagController(object):
self.protected_tags = [tag for tag in FormattingTags.html_expands if tag.get('protected')] self.protected_tags = [tag for tag in FormattingTags.html_expands if tag.get('protected')]
self.custom_tags = [] self.custom_tags = []
def validate_for_save(self, desc, tag, start_html, end_html): def validate_for_save(self, desc, tag, start_html, end_html, hidden):
""" """
Validate a custom tag and add to the tags array if valid.. Validate a custom tag and add to the tags array if valid..
@ -68,6 +68,9 @@ class FormattingTagController(object):
`end_html` `end_html`
The end html tag. The end html tag.
`hidden`
option to hide tag content from main window.
""" """
for line_number, html1 in enumerate(self.protected_tags): for line_number, html1 in enumerate(self.protected_tags):
if self._strip(html1['start tag']) == tag: if self._strip(html1['start tag']) == tag:
@ -86,7 +89,8 @@ class FormattingTagController(object):
'end tag': '{{/{tag}}}'.format(tag=tag), 'end tag': '{{/{tag}}}'.format(tag=tag),
'end html': end_html, 'end html': end_html,
'protected': False, 'protected': False,
'temporary': False 'temporary': False,
'hidden': hidden
} }
self.custom_tags.append(tag) self.custom_tags.append(tag)

View File

@ -38,7 +38,7 @@ class Ui_FormattingTagDialog(object):
""" """
formatting_tag_dialog.setObjectName('formatting_tag_dialog') formatting_tag_dialog.setObjectName('formatting_tag_dialog')
formatting_tag_dialog.setWindowIcon(UiIcons().main_icon) formatting_tag_dialog.setWindowIcon(UiIcons().main_icon)
formatting_tag_dialog.resize(725, 548) formatting_tag_dialog.resize(835, 548)
self.list_data_grid_layout = QtWidgets.QVBoxLayout(formatting_tag_dialog) self.list_data_grid_layout = QtWidgets.QVBoxLayout(formatting_tag_dialog)
self.list_data_grid_layout.setContentsMargins(8, 8, 8, 8) self.list_data_grid_layout.setContentsMargins(8, 8, 8, 8)
self.list_data_grid_layout.setObjectName('list_data_grid_layout') self.list_data_grid_layout.setObjectName('list_data_grid_layout')
@ -72,7 +72,7 @@ class Ui_FormattingTagDialog(object):
self.tag_table_widget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) self.tag_table_widget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tag_table_widget.setCornerButtonEnabled(False) self.tag_table_widget.setCornerButtonEnabled(False)
self.tag_table_widget.setObjectName('tag_table_widget') self.tag_table_widget.setObjectName('tag_table_widget')
self.tag_table_widget.setColumnCount(4) self.tag_table_widget.setColumnCount(5)
self.tag_table_widget.setRowCount(0) self.tag_table_widget.setRowCount(0)
self.tag_table_widget.horizontalHeader().setStretchLastSection(True) self.tag_table_widget.horizontalHeader().setStretchLastSection(True)
item = QtWidgets.QTableWidgetItem() item = QtWidgets.QTableWidgetItem()
@ -83,6 +83,8 @@ class Ui_FormattingTagDialog(object):
self.tag_table_widget.setHorizontalHeaderItem(2, item) self.tag_table_widget.setHorizontalHeaderItem(2, item)
item = QtWidgets.QTableWidgetItem() item = QtWidgets.QTableWidgetItem()
self.tag_table_widget.setHorizontalHeaderItem(3, item) self.tag_table_widget.setHorizontalHeaderItem(3, item)
item = QtWidgets.QTableWidgetItem()
self.tag_table_widget.setHorizontalHeaderItem(4, item)
self.list_data_grid_layout.addWidget(self.tag_table_widget) self.list_data_grid_layout.addWidget(self.tag_table_widget)
self.edit_button_layout = QtWidgets.QHBoxLayout() self.edit_button_layout = QtWidgets.QHBoxLayout()
self.new_button = QtWidgets.QPushButton(formatting_tag_dialog) self.new_button = QtWidgets.QPushButton(formatting_tag_dialog)
@ -126,6 +128,9 @@ class Ui_FormattingTagDialog(object):
self.tag_table_widget.horizontalHeaderItem(1).setText(translate('OpenLP.FormattingTagDialog', 'Tag')) self.tag_table_widget.horizontalHeaderItem(1).setText(translate('OpenLP.FormattingTagDialog', 'Tag'))
self.tag_table_widget.horizontalHeaderItem(2).setText(translate('OpenLP.FormattingTagDialog', 'Start HTML')) self.tag_table_widget.horizontalHeaderItem(2).setText(translate('OpenLP.FormattingTagDialog', 'Start HTML'))
self.tag_table_widget.horizontalHeaderItem(3).setText(translate('OpenLP.FormattingTagDialog', 'End HTML')) self.tag_table_widget.horizontalHeaderItem(3).setText(translate('OpenLP.FormattingTagDialog', 'End HTML'))
self.tag_table_widget.setColumnWidth(0, 120) self.tag_table_widget.horizontalHeaderItem(4).setText(translate('OpenLP.FormattingTagDialog',
self.tag_table_widget.setColumnWidth(1, 80) 'Hide content from Live/Preview'))
self.tag_table_widget.setColumnWidth(2, 330) self.tag_table_widget.setColumnWidth(0, 80)
self.tag_table_widget.setColumnWidth(1, 40)
self.tag_table_widget.setColumnWidth(2, 320)
self.tag_table_widget.setColumnWidth(3, 150)

View File

@ -39,6 +39,7 @@ class EditColumn(object):
Tag = 1 Tag = 1
StartHtml = 2 StartHtml = 2
EndHtml = 3 EndHtml = 3
Hidden = 4
class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTagController): class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTagController):
@ -96,6 +97,15 @@ class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTag
self.tag_table_widget.setItem(new_row, 2, self.tag_table_widget.setItem(new_row, 2,
QtWidgets.QTableWidgetItem(translate('OpenLP.FormattingTagForm', '<HTML here>'))) QtWidgets.QTableWidgetItem(translate('OpenLP.FormattingTagForm', '<HTML here>')))
self.tag_table_widget.setItem(new_row, 3, QtWidgets.QTableWidgetItem('')) self.tag_table_widget.setItem(new_row, 3, QtWidgets.QTableWidgetItem(''))
hiddenwidget = QtWidgets.QWidget()
hiddencheckbox = QtWidgets.QCheckBox()
hiddenlayout = QtWidgets.QHBoxLayout()
hiddenlayout.addWidget(hiddencheckbox)
hiddenlayout.setAlignment(QtCore.Qt.AlignCenter)
hiddenlayout.setContentsMargins(0, 0, 0, 0)
hiddenwidget.setLayout(hiddenlayout)
hiddencheckbox.setCheckState(QtCore.Qt.Unchecked)
self.tag_table_widget.setCellWidget(new_row, 4, hiddenwidget)
self.tag_table_widget.resizeRowsToContents() self.tag_table_widget.resizeRowsToContents()
self.tag_table_widget.scrollToBottom() self.tag_table_widget.scrollToBottom()
self.tag_table_widget.selectRow(new_row) self.tag_table_widget.selectRow(new_row)
@ -119,7 +129,9 @@ class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTag
error = self.services.validate_for_save(self.tag_table_widget.item(count, 0).text(), error = self.services.validate_for_save(self.tag_table_widget.item(count, 0).text(),
self.tag_table_widget.item(count, 1).text(), self.tag_table_widget.item(count, 1).text(),
self.tag_table_widget.item(count, 2).text(), self.tag_table_widget.item(count, 2).text(),
self.tag_table_widget.item(count, 3).text()) self.tag_table_widget.item(count, 3).text(),
self.tag_table_widget.cellWidget(count, 4).children()[1].isChecked()
)
if error: if error:
QtWidgets.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), error) QtWidgets.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), error)
self.tag_table_widget.selectRow(count) self.tag_table_widget.selectRow(count)
@ -155,6 +167,15 @@ class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTag
self.tag_table_widget.setItem(line, 1, QtWidgets.QTableWidgetItem(self._strip(html['start tag']))) self.tag_table_widget.setItem(line, 1, QtWidgets.QTableWidgetItem(self._strip(html['start tag'])))
self.tag_table_widget.setItem(line, 2, QtWidgets.QTableWidgetItem(html['start html'])) self.tag_table_widget.setItem(line, 2, QtWidgets.QTableWidgetItem(html['start html']))
self.tag_table_widget.setItem(line, 3, QtWidgets.QTableWidgetItem(html['end html'])) self.tag_table_widget.setItem(line, 3, QtWidgets.QTableWidgetItem(html['end html']))
hiddenwidget = QtWidgets.QWidget()
hiddencheckbox = QtWidgets.QCheckBox()
hiddenlayout = QtWidgets.QHBoxLayout()
hiddenlayout.addWidget(hiddencheckbox)
hiddenlayout.setAlignment(QtCore.Qt.AlignCenter)
hiddenlayout.setContentsMargins(0, 0, 0, 0)
hiddenwidget.setLayout(hiddenlayout)
hiddencheckbox.setCheckState(QtCore.Qt.Checked if html['hidden'] else QtCore.Qt.Unchecked)
self.tag_table_widget.setCellWidget(line, 4, hiddenwidget)
self.tag_table_widget.resizeRowsToContents() self.tag_table_widget.resizeRowsToContents()
# Permanent (persistent) tags do not have this key # Permanent (persistent) tags do not have this key
html['temporary'] = False html['temporary'] = False
@ -172,7 +193,8 @@ class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTag
# only process for editable rows # only process for editable rows
if self.tag_table_widget.item(pre_row, 0): if self.tag_table_widget.item(pre_row, 0):
item = self.tag_table_widget.item(pre_row, pre_col) item = self.tag_table_widget.item(pre_row, pre_col)
text = item.text() if pre_col is not EditColumn.Hidden:
text = item.text()
errors = None errors = None
if pre_col is EditColumn.Description: if pre_col is EditColumn.Description:
if not text: if not text:

View File

@ -458,6 +458,8 @@ class OpenLyrics(object):
if tag['end html']: if tag['end html']:
element_close = self._add_text_to_element('close', element) element_close = self._add_text_to_element('close', element)
element_close.text = etree.CDATA(tag['end html']) element_close.text = etree.CDATA(tag['end html'])
element_hidden = self._add_text_to_element('hidden', element)
element_hidden.text = etree.CDATA(str(tag['hidden']))
def _add_text_with_tags_to_lines(self, verse_element, text, tags_element): def _add_text_with_tags_to_lines(self, verse_element, text, tags_element):
""" """
@ -602,7 +604,8 @@ class OpenLyrics(object):
'protected': False, 'protected': False,
# Add 'temporary' key in case the formatting tag should not be saved otherwise it is supposed that # Add 'temporary' key in case the formatting tag should not be saved otherwise it is supposed that
# formatting tag is permanent. # formatting tag is permanent.
'temporary': temporary 'temporary': temporary,
'hidden': True if hasattr(tag, 'hidden') and tag.hidden.text == 'True' else False
} }
found_tags.append(openlp_tag) found_tags.append(openlp_tag)
existing_tag_ids = [tag['start tag'] for tag in FormattingTags.get_html_tags()] existing_tag_ids = [tag['start tag'] for tag in FormattingTags.get_html_tags()]

View File

@ -76,21 +76,21 @@ def test_render_tags(mocked_get_tags, settings):
'start tag': '{b}', 'start tag': '{b}',
'start html': '<span style="-webkit-text-fill-color:black">', 'start html': '<span style="-webkit-text-fill-color:black">',
'end tag': '{/b}', 'end html': '</span>', 'protected': True, 'end tag': '{/b}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, },
{ {
'desc': 'Yellow', 'desc': 'Yellow',
'start tag': '{y}', 'start tag': '{y}',
'start html': '<span style="-webkit-text-fill-color:yellow">', 'start html': '<span style="-webkit-text-fill-color:yellow">',
'end tag': '{/y}', 'end html': '</span>', 'protected': True, 'end tag': '{/y}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
}, },
{ {
'desc': 'Green', 'desc': 'Green',
'start tag': '{g}', 'start tag': '{g}',
'start html': '<span style="-webkit-text-fill-color:green">', 'start html': '<span style="-webkit-text-fill-color:green">',
'end tag': '{/g}', 'end html': '</span>', 'protected': True, 'end tag': '{/g}', 'end html': '</span>', 'protected': True,
'temporary': False 'temporary': False, 'hidden': False
} }
] ]
string_to_pass = '{b}black{/b}{y}yellow{/y}' string_to_pass = '{b}black{/b}{y}yellow{/y}'

View File

@ -63,8 +63,11 @@ def test_on_new_clicked(tagform_env):
# WHEN: on_new_clicked is run (i.e. the Add new button was clicked) # WHEN: on_new_clicked is run (i.e. the Add new button was clicked)
with patch('openlp.core.ui.formattingtagform.QtWidgets.QTableWidgetItem') as MockedQTableWidgetItem: with patch('openlp.core.ui.formattingtagform.QtWidgets.QTableWidgetItem') as MockedQTableWidgetItem:
mockedwidget = patch('openlp.core.ui.formattingtagform.QtWidgets.QWidget')
mocked_table_widget = MagicMock() mocked_table_widget = MagicMock()
mocked_widget = MagicMock()
MockedQTableWidgetItem.return_value = mocked_table_widget MockedQTableWidgetItem.return_value = mocked_table_widget
mockedwidget.return_value = mocked_widget
form.on_new_clicked() form.on_new_clicked()
# THEN: A new row should be added to the table # THEN: A new row should be added to the table

View File

@ -31,12 +31,12 @@ from tests.utils.constants import RESOURCE_PATH
TEST_PATH = RESOURCE_PATH / 'songs' / 'openlyrics' TEST_PATH = RESOURCE_PATH / 'songs' / 'openlyrics'
START_TAGS = [{"protected": False, "desc": "z", "start tag": "{z}", "end html": "</strong>", "temporary": False, START_TAGS = [{"protected": False, "desc": "z", "start tag": "{z}", "end html": "</strong>", "temporary": False,
"end tag": "{/z}", "start html": "strong>"}] "end tag": "{/z}", "start html": "strong>", "hidden": False}]
RESULT_TAGS = [{"temporary": False, "protected": False, "desc": "z", "start tag": "{z}", "start html": "strong>", RESULT_TAGS = [{"temporary": False, "protected": False, "desc": "z", "start tag": "{z}", "start html": "strong>",
"end html": "</strong>", "end tag": "{/z}"}, "end html": "</strong>", "end tag": "{/z}", "hidden": False},
{"temporary": False, "end tag": "{/c}", "desc": "c", "start tag": "{c}", {"temporary": False, "end tag": "{/c}", "desc": "c", "start tag": "{c}",
"start html": "<span class=\"chord\" style=\"display:none\"><strong>", "end html": "</strong></span>", "start html": "<span class=\"chord\" style=\"display:none\"><strong>", "end html": "</strong></span>",
"protected": False}] "protected": False, "hidden": False}]
VERSE_LINES_07_XML = '<lines>\ VERSE_LINES_07_XML = '<lines>\
<line>Amazing grace, how sweet the sound</line>\ <line>Amazing grace, how sweet the sound</line>\
<line>That saved a wretch like me</line>\ <line>That saved a wretch like me</line>\