openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #18379
[Merge] lp:~m2j/openlp/formattingtags into lp:openlp
Meinert Jordan has proposed merging lp:~m2j/openlp/formattingtags into lp:openlp.
Requested reviews:
Tim Bentley (trb143)
For more details, see:
https://code.launchpad.net/~m2j/openlp/formattingtags/+merge/140652
I forgot about this branch, and didn't realized, that it was never merged. Now I merged it to the current trunk and did a rework (which ended up in a rewrite).
This branch modifies the formatting tag editor in several ways:
- the user gets restricted to valid HTML.
- the closing tag is derived from the opening tag
- editing happens inline in the table
The result looks rather minimalistic, as I would like to shift the editor to the settings dialog. But before I want to discuss this. I'd like to make such a change in a seperated merge request.
--
https://code.launchpad.net/~m2j/openlp/formattingtags/+merge/140652
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/ui/formattingtagdialog.py'
--- openlp/core/ui/formattingtagdialog.py 2012-12-01 07:57:54 +0000
+++ openlp/core/ui/formattingtagdialog.py 2012-12-19 12:44:20 +0000
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
@@ -29,121 +29,81 @@
from PyQt4 import QtCore, QtGui
-from openlp.core.lib import translate
-from openlp.core.lib.ui import UiStrings, create_button_box
+from openlp.core.lib import translate, build_icon
+from openlp.core.lib.ui import create_button_box, UiStrings
class Ui_FormattingTagDialog(object):
- def setupUi(self, formattingTagDialog):
- formattingTagDialog.setObjectName(u'formattingTagDialog')
- formattingTagDialog.resize(725, 548)
- self.listdataGridLayout = QtGui.QGridLayout(formattingTagDialog)
- self.listdataGridLayout.setMargin(8)
- self.listdataGridLayout.setObjectName(u'listdataGridLayout')
- self.tagTableWidget = QtGui.QTableWidget(formattingTagDialog)
- self.tagTableWidget.setHorizontalScrollBarPolicy(
- QtCore.Qt.ScrollBarAlwaysOff)
- self.tagTableWidget.setEditTriggers(
- QtGui.QAbstractItemView.NoEditTriggers)
- self.tagTableWidget.setAlternatingRowColors(True)
- self.tagTableWidget.setSelectionMode(
- QtGui.QAbstractItemView.SingleSelection)
- self.tagTableWidget.setSelectionBehavior(
- QtGui.QAbstractItemView.SelectRows)
- self.tagTableWidget.setCornerButtonEnabled(False)
- self.tagTableWidget.setObjectName(u'tagTableWidget')
- self.tagTableWidget.setColumnCount(4)
- self.tagTableWidget.setRowCount(0)
- self.tagTableWidget.horizontalHeader().setStretchLastSection(True)
- item = QtGui.QTableWidgetItem()
- self.tagTableWidget.setHorizontalHeaderItem(0, item)
- item = QtGui.QTableWidgetItem()
- self.tagTableWidget.setHorizontalHeaderItem(1, item)
- item = QtGui.QTableWidgetItem()
- self.tagTableWidget.setHorizontalHeaderItem(2, item)
- item = QtGui.QTableWidgetItem()
- self.tagTableWidget.setHorizontalHeaderItem(3, item)
- self.listdataGridLayout.addWidget(self.tagTableWidget, 0, 0, 1, 1)
- self.horizontalLayout = QtGui.QHBoxLayout()
- self.horizontalLayout.setObjectName(u'horizontalLayout')
- spacerItem = QtGui.QSpacerItem(40, 20,
- QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.horizontalLayout.addItem(spacerItem)
- self.deletePushButton = QtGui.QPushButton(formattingTagDialog)
- self.deletePushButton.setObjectName(u'deletePushButton')
- self.horizontalLayout.addWidget(self.deletePushButton)
- self.listdataGridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1)
- self.editGroupBox = QtGui.QGroupBox(formattingTagDialog)
- self.editGroupBox.setObjectName(u'editGroupBox')
- self.dataGridLayout = QtGui.QGridLayout(self.editGroupBox)
- self.dataGridLayout.setObjectName(u'dataGridLayout')
- self.descriptionLabel = QtGui.QLabel(self.editGroupBox)
- self.descriptionLabel.setAlignment(QtCore.Qt.AlignCenter)
- self.descriptionLabel.setObjectName(u'descriptionLabel')
- self.dataGridLayout.addWidget(self.descriptionLabel, 0, 0, 1, 1)
- self.descriptionLineEdit = QtGui.QLineEdit(self.editGroupBox)
- self.descriptionLineEdit.setObjectName(u'descriptionLineEdit')
- self.dataGridLayout.addWidget(self.descriptionLineEdit, 0, 1, 2, 1)
- self.newPushButton = QtGui.QPushButton(self.editGroupBox)
- self.newPushButton.setObjectName(u'newPushButton')
- self.dataGridLayout.addWidget(self.newPushButton, 0, 2, 2, 1)
- self.tagLabel = QtGui.QLabel(self.editGroupBox)
- self.tagLabel.setAlignment(QtCore.Qt.AlignCenter)
- self.tagLabel.setObjectName(u'tagLabel')
- self.dataGridLayout.addWidget(self.tagLabel, 2, 0, 1, 1)
- self.tagLineEdit = QtGui.QLineEdit(self.editGroupBox)
- self.tagLineEdit.setMaximumSize(QtCore.QSize(50, 16777215))
- self.tagLineEdit.setMaxLength(5)
- self.tagLineEdit.setObjectName(u'tagLineEdit')
- self.dataGridLayout.addWidget(self.tagLineEdit, 2, 1, 1, 1)
- self.startTagLabel = QtGui.QLabel(self.editGroupBox)
- self.startTagLabel.setAlignment(QtCore.Qt.AlignCenter)
- self.startTagLabel.setObjectName(u'startTagLabel')
- self.dataGridLayout.addWidget(self.startTagLabel, 3, 0, 1, 1)
- self.startTagLineEdit = QtGui.QLineEdit(self.editGroupBox)
- self.startTagLineEdit.setObjectName(u'startTagLineEdit')
- self.dataGridLayout.addWidget(self.startTagLineEdit, 3, 1, 1, 1)
- self.endTagLabel = QtGui.QLabel(self.editGroupBox)
- self.endTagLabel.setAlignment(QtCore.Qt.AlignCenter)
- self.endTagLabel.setObjectName(u'endTagLabel')
- self.dataGridLayout.addWidget(self.endTagLabel, 4, 0, 1, 1)
- self.endTagLineEdit = QtGui.QLineEdit(self.editGroupBox)
- self.endTagLineEdit.setObjectName(u'endTagLineEdit')
- self.dataGridLayout.addWidget(self.endTagLineEdit, 4, 1, 1, 1)
- self.savePushButton = QtGui.QPushButton(self.editGroupBox)
- self.savePushButton.setObjectName(u'savePushButton')
- self.dataGridLayout.addWidget(self.savePushButton, 4, 2, 1, 1)
- self.listdataGridLayout.addWidget(self.editGroupBox, 2, 0, 1, 1)
- self.buttonBox = create_button_box(formattingTagDialog, 'buttonBox',
- [u'close'])
- self.listdataGridLayout.addWidget(self.buttonBox, 3, 0, 1, 1)
-
- self.retranslateUi(formattingTagDialog)
-
- def retranslateUi(self, formattingTagDialog):
- formattingTagDialog.setWindowTitle(translate(
+ def setup_ui(self, formatting_tag_dialog):
+ formatting_tag_dialog.setObjectName(u'formatting_tag_dialog')
+ formatting_tag_dialog.resize(725, 548)
+ self.dialog_layout = QtGui.QVBoxLayout(formatting_tag_dialog)
+ self.dialog_layout.setMargin(8)
+ self.dialog_layout.setObjectName(u'dialog_layout')
+ self.tag_table_widget = FormattingTagTableWidget(formatting_tag_dialog)
+ self.tag_table_widget.setObjectName(u'tagTableWidget')
+ self.dialog_layout.addWidget(self.tag_table_widget)
+ self.edit_button_layout = QtGui.QHBoxLayout()
+ self.new_button = QtGui.QPushButton(formatting_tag_dialog)
+ self.new_button.setIcon(build_icon(u':/general/general_new.png'))
+ self.new_button.setObjectName(u'new_button')
+ self.edit_button_layout.addWidget(self.new_button)
+ self.delete_button = QtGui.QPushButton(formatting_tag_dialog)
+ self.delete_button.setIcon(build_icon(u':/general/general_delete.png'))
+ self.delete_button.setObjectName(u'delete_button')
+ self.edit_button_layout.addWidget(self.delete_button)
+ self.edit_button_layout.addStretch()
+ self.dialog_layout.addLayout(self.edit_button_layout)
+ self.button_box = create_button_box(formatting_tag_dialog, 'button_box',
+ [u'cancel', u'save', u'defaults'])
+ self.save_button = self.button_box.button(QtGui.QDialogButtonBox.Save)
+ self.save_button.setObjectName(u'save_button')
+ self.restore_button = self.button_box.button(QtGui.QDialogButtonBox.RestoreDefaults)
+ self.restore_button.setIcon(build_icon(u':/general/general_revert.png'))
+ self.restore_button.setObjectName(u'restore_button')
+ self.dialog_layout.addWidget(self.button_box)
+
+ self.retranslate_ui(formatting_tag_dialog)
+
+ def retranslate_ui(self, formatting_tag_dialog):
+ formatting_tag_dialog.setWindowTitle(translate(
'OpenLP.FormattingTagDialog', 'Configure Formatting Tags'))
- self.editGroupBox.setTitle(
- translate('OpenLP.FormattingTagDialog', 'Edit Selection'))
- self.savePushButton.setText(
- translate('OpenLP.FormattingTagDialog', 'Save'))
- self.descriptionLabel.setText(
- translate('OpenLP.FormattingTagDialog', 'Description'))
- self.tagLabel.setText(translate('OpenLP.FormattingTagDialog', 'Tag'))
- self.startTagLabel.setText(
- translate('OpenLP.FormattingTagDialog', 'Start HTML'))
- self.endTagLabel.setText(
- translate('OpenLP.FormattingTagDialog', 'End HTML'))
- self.deletePushButton.setText(UiStrings().Delete)
- self.newPushButton.setText(UiStrings().New)
- self.tagTableWidget.horizontalHeaderItem(0).setText(
- translate('OpenLP.FormattingTagDialog', 'Description'))
- self.tagTableWidget.horizontalHeaderItem(1).setText(
+ self.tag_table_widget.horizontalHeaderItem(0).setText(
translate('OpenLP.FormattingTagDialog', 'Tag'))
- self.tagTableWidget.horizontalHeaderItem(2).setText(
+ self.tag_table_widget.horizontalHeaderItem(1).setText(
+ translate('OpenLP.FormattingTagDialog', 'Description'))
+ self.tag_table_widget.horizontalHeaderItem(2).setText(
translate('OpenLP.FormattingTagDialog', 'Start HTML'))
- self.tagTableWidget.horizontalHeaderItem(3).setText(
+ self.tag_table_widget.horizontalHeaderItem(3).setText(
translate('OpenLP.FormattingTagDialog', 'End HTML'))
- self.tagTableWidget.setColumnWidth(0, 120)
- self.tagTableWidget.setColumnWidth(1, 80)
- self.tagTableWidget.setColumnWidth(2, 330)
+ self.new_button.setText(UiStrings().New)
+ self.delete_button.setText(UiStrings().Delete)
+
+class FormattingTagTableWidget(QtGui.QTableWidget):
+ """
+ Class to for the formatting tag table widget.
+ """
+ def __init__(self, parent):
+ """
+ Constructor
+ """
+ QtGui.QTableWidget.__init__(self, parent)
+ self.setEditTriggers(QtGui.QAbstractItemView.AllEditTriggers)
+ self.setAlternatingRowColors(True)
+ self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
+ self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+ self.verticalHeader().setVisible(False)
+ self.setColumnCount(4)
+ for index in range(0, 4):
+ self.setHorizontalHeaderItem(index, QtGui.QTableWidgetItem())
+
+ def resizeEvent(self, event):
+ """
+ Resize the start html column to cover remaining space.
+ """
+ QtGui.QTableWidget.resizeEvent(self, event)
+ if self.columnCount() == 4:
+ self.resizeColumnsToContents()
+ self.setColumnWidth(2, event.size().width() - self.columnWidth(0)
+ - self.columnWidth(1) - self.columnWidth(3))
+ self.resizeRowsToContents()
=== modified file 'openlp/core/ui/formattingtagform.py'
--- openlp/core/ui/formattingtagform.py 2012-12-01 07:57:54 +0000
+++ openlp/core/ui/formattingtagform.py 2012-12-19 12:44:20 +0000
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
@@ -32,10 +32,11 @@
The Custom Tag arrays are saved in a pickle so QSettings works on them. Base
Tags cannot be changed.
"""
+import re
+import cgi
from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate, FormattingTags
-from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog
@@ -48,174 +49,260 @@
Constructor
"""
QtGui.QDialog.__init__(self, parent)
- self.setupUi(self)
- QtCore.QObject.connect(self.tagTableWidget,
- QtCore.SIGNAL(u'itemSelectionChanged()'),self.onRowSelected)
- QtCore.QObject.connect(self.newPushButton,
- QtCore.SIGNAL(u'clicked()'), self.onNewClicked)
- QtCore.QObject.connect(self.savePushButton,
- QtCore.SIGNAL(u'clicked()'), self.onSavedClicked)
- QtCore.QObject.connect(self.deletePushButton,
- QtCore.SIGNAL(u'clicked()'), self.onDeleteClicked)
- QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'),
- self.close)
- QtCore.QObject.connect(self.descriptionLineEdit,
- QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
- QtCore.QObject.connect(self.tagLineEdit,
- QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
- QtCore.QObject.connect(self.startTagLineEdit,
- QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
- QtCore.QObject.connect(self.endTagLineEdit,
- QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
- # Forces reloading of tags from openlp configuration.
+ self.setup_ui(self)
+ self.html_tag_regex = re.compile(r'<(?:(?P<close>/(?=[^\s/>]+>))?'
+ r'(?P<tag>[^\s/!\?>]+)(?:\s+[^\s=]+="[^"]*")*\s*(?P<empty>/)?'
+ r'|(?P<cdata>!\[CDATA\[(?:(?!\]\]>).)*\]\])'
+ r'|(?P<procinst>\?(?:(?!\?>).)*\?)'
+ r'|(?P<comment>!--(?:(?!-->).)*--))>', re.UNICODE)
+ self.html_regex = re.compile(r'^(?:[^<>]*%s)*[^<>]*$' % self.html_tag_regex.pattern)
+ QtCore.QObject.connect(self.tag_table_widget, QtCore.SIGNAL(u'currentCellChanged(int, int, int, int)'),
+ self.on_current_cell_changed)
+ QtCore.QObject.connect(self.tag_table_widget, QtCore.SIGNAL(u'itemChanged(QTableWidgetItem*)'),
+ self.on_data_change)
+ QtCore.QObject.connect(self.new_button, QtCore.SIGNAL(u'clicked()'),
+ self.on_new_button_clicked)
+ QtCore.QObject.connect(self.delete_button, QtCore.SIGNAL(u'clicked()'),
+ self.on_delete_button_clicked)
+ QtCore.QObject.connect(self.restore_button, QtCore.SIGNAL(u'clicked()'),
+ self.load_tag_table)
+ QtCore.QObject.connect(self, QtCore.SIGNAL(u'accepted()'),
+ self.save_tags)
+ self.pause_validation = False
+
+ def exec_(self):
+ """
+ Run the dialog.
+ """
FormattingTags.load_tags()
-
- def exec_(self):
- """
- Load Display and set field state.
- """
- # Create initial copy from master
- self._reloadTable()
- self.selected = -1
+ self.load_tag_table()
return QtGui.QDialog.exec_(self)
- def onRowSelected(self):
- """
- Table Row selected so display items and set field state.
- """
- self.savePushButton.setEnabled(False)
- self.selected = self.tagTableWidget.currentRow()
- html = FormattingTags.get_html_tags()[self.selected]
- self.descriptionLineEdit.setText(html[u'desc'])
- self.tagLineEdit.setText(self._strip(html[u'start tag']))
- self.startTagLineEdit.setText(html[u'start html'])
- self.endTagLineEdit.setText(html[u'end html'])
- if html[u'protected']:
- self.descriptionLineEdit.setEnabled(False)
- self.tagLineEdit.setEnabled(False)
- self.startTagLineEdit.setEnabled(False)
- self.endTagLineEdit.setEnabled(False)
- self.deletePushButton.setEnabled(False)
- else:
- self.descriptionLineEdit.setEnabled(True)
- self.tagLineEdit.setEnabled(True)
- self.startTagLineEdit.setEnabled(True)
- self.endTagLineEdit.setEnabled(True)
- self.deletePushButton.setEnabled(True)
-
- def onTextEdited(self, text):
- """
- Enable the ``savePushButton`` when any of the selected tag's properties
- has been changed.
- """
- self.savePushButton.setEnabled(True)
-
- def onNewClicked(self):
- """
- Add a new tag to list only if it is not a duplicate.
- """
- for html in FormattingTags.get_html_tags():
- if self._strip(html[u'start tag']) == u'n':
- critical_error_message_box(
- translate('OpenLP.FormattingTagForm', 'Update Error'),
- translate('OpenLP.FormattingTagForm',
- 'Tag "n" already defined.'))
- return
- # Add new tag to list
- tag = {
- u'desc': translate('OpenLP.FormattingTagForm', 'New Tag'),
- u'start tag': u'{n}',
- u'start html': translate('OpenLP.FormattingTagForm', '<HTML here>'),
- u'end tag': u'{/n}',
- u'end html': translate('OpenLP.FormattingTagForm', '</and here>'),
- u'protected': False,
- u'temporary': False
- }
- FormattingTags.add_html_tags([tag])
- FormattingTags.save_html_tags()
- self._reloadTable()
- # Highlight new row
- self.tagTableWidget.selectRow(self.tagTableWidget.rowCount() - 1)
- self.onRowSelected()
- self.tagTableWidget.scrollToBottom()
- #self.savePushButton.setEnabled(False)
-
- def onDeleteClicked(self):
- """
- Delete selected custom tag.
- """
- if self.selected != -1:
- FormattingTags.remove_html_tag(self.selected)
- # As the first items are protected we should not have to take care
- # of negative indexes causing tracebacks.
- self.tagTableWidget.selectRow(self.selected - 1)
- self.selected = -1
- FormattingTags.save_html_tags()
- self._reloadTable()
-
- def onSavedClicked(self):
- """
- Update Custom Tag details if not duplicate and save the data.
- """
- html_expands = FormattingTags.get_html_tags()
- if self.selected != -1:
- html = html_expands[self.selected]
- tag = unicode(self.tagLineEdit.text())
- for linenumber, html1 in enumerate(html_expands):
- if self._strip(html1[u'start tag']) == tag and \
- linenumber != self.selected:
- critical_error_message_box(
- translate('OpenLP.FormattingTagForm', 'Update Error'),
- unicode(translate('OpenLP.FormattingTagForm',
- 'Tag %s already defined.')) % tag)
- return
- html[u'desc'] = unicode(self.descriptionLineEdit.text())
- html[u'start html'] = unicode(self.startTagLineEdit.text())
- html[u'end html'] = unicode(self.endTagLineEdit.text())
- html[u'start tag'] = u'{%s}' % tag
- html[u'end tag'] = u'{/%s}' % tag
- # Keep temporary tags when the user changes one.
- html[u'temporary'] = False
- self.selected = -1
- FormattingTags.save_html_tags()
- self._reloadTable()
-
- def _reloadTable(self):
- """
- Reset List for loading.
- """
- self.tagTableWidget.clearContents()
- self.tagTableWidget.setRowCount(0)
- self.newPushButton.setEnabled(True)
- self.savePushButton.setEnabled(False)
- self.deletePushButton.setEnabled(False)
- for linenumber, html in enumerate(FormattingTags.get_html_tags()):
- self.tagTableWidget.setRowCount(self.tagTableWidget.rowCount() + 1)
- self.tagTableWidget.setItem(linenumber, 0,
- QtGui.QTableWidgetItem(html[u'desc']))
- self.tagTableWidget.setItem(linenumber, 1,
- QtGui.QTableWidgetItem(self._strip(html[u'start tag'])))
- self.tagTableWidget.setItem(linenumber, 2,
- QtGui.QTableWidgetItem(html[u'start html']))
- self.tagTableWidget.setItem(linenumber, 3,
- QtGui.QTableWidgetItem(html[u'end html']))
- # Permanent (persistent) tags do not have this key.
- if u'temporary' not in html:
- html[u'temporary'] = False
- self.tagTableWidget.resizeRowsToContents()
- self.descriptionLineEdit.setText(u'')
- self.tagLineEdit.setText(u'')
- self.startTagLineEdit.setText(u'')
- self.endTagLineEdit.setText(u'')
- self.descriptionLineEdit.setEnabled(False)
- self.tagLineEdit.setEnabled(False)
- self.startTagLineEdit.setEnabled(False)
- self.endTagLineEdit.setEnabled(False)
-
- def _strip(self, tag):
- """
- Remove tag wrappers for editing.
- """
- tag = tag.replace(u'{', u'')
- tag = tag.replace(u'}', u'')
- return tag
+ def load_tag_table(self):
+ """
+ Load tags from the Settings into the table widget.
+ """
+ self.pause_validation = True
+ # clear table
+ self.tag_table_widget.setRowCount(0)
+ # add tags
+ tags = FormattingTags.get_html_tags()
+ for row, tag in enumerate(tags):
+ self.tag_table_widget.insertRow(row)
+ item = QtGui.QTableWidgetItem(tag[u'start tag'].strip(u'{}'))
+ flags = item.flags()
+ if not tag.get(u'protected'):
+ item.setData(QtCore.Qt.UserRole, {
+ u'tag':tag[u'start tag'].strip(u'{}'),
+ u'html':tag[u'start html'],
+ u'description':tag[u'desc']})
+ else:
+ flags &= ~QtCore.Qt.ItemIsEditable
+ item.setFlags(flags)
+ self.tag_table_widget.setItem(row, 0, item)
+ item = QtGui.QTableWidgetItem(tag[u'desc'])
+ item.setToolTip(cgi.escape(tag[u'desc']))
+ item.setFlags(flags)
+ self.tag_table_widget.setItem(row, 1, item)
+ item = QtGui.QTableWidgetItem(tag[u'start html'])
+ item.setToolTip(cgi.escape(tag[u'start html']))
+ item.setFlags(flags)
+ self.tag_table_widget.setItem(row, 2, item)
+ item = QtGui.QTableWidgetItem(tag[u'end html'])
+ item.setToolTip(cgi.escape(tag[u'end html']))
+ item.setFlags(flags & ~QtCore.Qt.ItemIsEditable)
+ self.tag_table_widget.setItem(row, 3, item)
+ # add a empty row to allow definition of new tags
+ row = len(tags)
+ self.tag_table_widget.insertRow(row)
+ item = QtGui.QTableWidgetItem(u'')
+ self.tag_table_widget.setItem(row, 0, item)
+ flags = item.flags() & ~QtCore.Qt.ItemIsEditable
+ for column in [1, 2, 3]:
+ item = QtGui.QTableWidgetItem(u'')
+ item.setFlags(flags)
+ self.tag_table_widget.setItem(row, column, item)
+ self.tag_table_widget.resizeRowsToContents()
+ self.pause_validation = False
+ self.restore_button.setEnabled(False)
+ self.save_button.setEnabled(False)
+
+ def on_current_cell_changed(self, cur_row, cur_col, pre_row, pre_col):
+ """
+ This function processes all user edits in the table. It is called on each cell change.
+ """
+ # only process for editable rows
+ pre_row_item = self.tag_table_widget.item(pre_row, 0)
+ edit_item = None
+ if pre_row_item and (pre_row_item.flags() & QtCore.Qt.ItemIsEditable) and not self.pause_validation:
+ data = self.item_to_data_dict(pre_row_item)
+ item = self.tag_table_widget.item(pre_row, pre_col)
+ text = unicode(item.text())
+ if pre_col is 0:
+ # Tag name edited
+ if text:
+ for row in range(self.tag_table_widget.rowCount()):
+ counting_item = self.tag_table_widget.item(row, 0)
+ if row != pre_row and counting_item and counting_item.text() == text:
+ answer = QtGui.QMessageBox.warning(self,
+ translate('OpenLP.FormattingTagForm', 'Validation Error'),
+ translate('OpenLP.FormattingTagForm',
+ 'Tag %s is already defined. Please pick a different one.' % text),
+ QtGui.QMessageBox.Discard|QtGui.QMessageBox.Ok)
+ if answer == QtGui.QMessageBox.Discard:
+ item.setText(data.get(u'tag'))
+ else:
+ edit_item = item
+ break
+ if not edit_item:
+ data[u'tag'] = text
+ data.setdefault(u'description', u'')
+ data.setdefault(u'html', u'')
+ pre_row_item.setData(QtCore.Qt.UserRole, data)
+ flags = self.tag_table_widget.item(pre_row, 1).flags()
+ if not (flags & QtCore.Qt.ItemIsEditable):
+ # if description cell is read only, the user is adding a new tag.
+ # So we add another empty row and enable editing for description and html.
+ new_row = self.tag_table_widget.rowCount()
+ self.tag_table_widget.insertRow(new_row)
+ for column in range(4):
+ new_item = QtGui.QTableWidgetItem(u'')
+ if column != 0:
+ new_item.setFlags(flags)
+ self.tag_table_widget.setItem(new_row, column, new_item)
+ for column in [1, 2]:
+ self.tag_table_widget.item(pre_row, column).setFlags(item.flags())
+ # trigger edit as editing might have been enabled after selecting
+ if cur_row == pre_row and cur_col in [1, 2]:
+ cur_item = self.tag_table_widget.item(cur_row, cur_col)
+ self.tag_table_widget.editItem(cur_item)
+ self.tag_table_widget.resizeRowsToContents()
+ else:
+ answer = None
+ if self.tag_table_widget.item(pre_row, 1).text() or self.tag_table_widget.item(pre_row, 2).text():
+ answer = QtGui.QMessageBox.warning(self,
+ translate('OpenLP.FormattingTagForm', 'Validation Error'),
+ translate('OpenLP.FormattingTagForm',
+ 'No tag name defined. Do you want to delete the whole tag?'),
+ QtGui.QMessageBox.Yes|QtGui.QMessageBox.Discard|QtGui.QMessageBox.Cancel)
+ if answer == QtGui.QMessageBox.Discard:
+ item.setText(data.get(u'tag'))
+ if answer == QtGui.QMessageBox.Cancel:
+ edit_item = item
+ elif pre_row < self.tag_table_widget.rowCount() - 1:
+ self.tag_table_widget.removeRow(pre_row)
+ elif pre_col is 1:
+ # Description edited
+ data[u'description'] = text
+ pre_row_item.setData(QtCore.Qt.UserRole, data)
+ elif pre_col is 2:
+ # HTML edited
+ end_html = self.start_html_to_end_html(text)
+ if end_html is not None:
+ item.setToolTip(cgi.escape(text))
+ if self.tag_table_widget.item(pre_row, 3) is None:
+ self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(end_html))
+ else:
+ self.tag_table_widget.item(pre_row, 3).setText(end_html)
+ self.tag_table_widget.item(pre_row, 3).setToolTip(cgi.escape(end_html))
+ data[u'html'] = text
+ pre_row_item.setData(QtCore.Qt.UserRole, data)
+ self.tag_table_widget.resizeRowsToContents()
+ elif QtGui.QMessageBox.question(self,
+ translate('OpenLP.FormattingTagForm', 'Validation Error'),
+ translate('OpenLP.FormattingTagForm', 'The entered HTML is not valid. Please enter valid HTML.'),
+ QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) == QtGui.QMessageBox.Cancel:
+ item.setText(data.get(u'html'))
+ else:
+ edit_item = item
+ if not edit_item:
+ # select the tag cell in a empty row
+ cur_row_item = self.tag_table_widget.item(cur_row, 0)
+ if cur_row_item and (cur_row_item.flags() & QtCore.Qt.ItemIsEditable) and cur_row_item.text().isEmpty():
+ edit_item = cur_row_item
+ if edit_item:
+ self.tag_table_widget.setCurrentItem(edit_item)
+ # enable delete_button for editable rows
+ cur_row = self.tag_table_widget.currentRow()
+ cur_row_item = self.tag_table_widget.item(cur_row, 0)
+ delete_enabled = bool(cur_row_item) and bool(cur_row_item.flags() & QtCore.Qt.ItemIsEditable)
+ delete_enabled &= cur_row < self.tag_table_widget.rowCount() - 1
+ self.delete_button.setEnabled(delete_enabled)
+
+ def on_data_change(self, item):
+ """
+ Enable restore and save button.
+ """
+ self.restore_button.setEnabled(True)
+ self.save_button.setEnabled(True)
+
+ def on_new_button_clicked(self):
+ """
+ Focus the tag cell of the last row.
+ """
+ last_row = self.tag_table_widget.rowCount() - 1
+ self.tag_table_widget.selectRow(last_row)
+ self.tag_table_widget.setCurrentCell(last_row, 0)
+
+ def on_delete_button_clicked(self):
+ """
+ Delete the current row.
+ """
+ self.pause_validation = True
+ cur_row = self.tag_table_widget.currentRow()
+ self.tag_table_widget.removeRow(cur_row)
+ self.on_data_change(None)
+ self.pause_validation = False
+
+ def save_tags(self):
+ """
+ Save the table to the settings.
+ """
+ for id, tag in reversed(list(enumerate(FormattingTags.get_html_tags()))):
+ if not tag.get(u'protected'):
+ FormattingTags.remove_html_tag(id)
+ tags = []
+ for row in range(self.tag_table_widget.rowCount()):
+ if self.tag_table_widget.item(row, 0).flags() & QtCore.Qt.ItemIsEditable:
+ data = self.item_to_data_dict(self.tag_table_widget.item(row, 0))
+ if data.get(u'tag'):
+ tags.append({
+ u'desc': data[u'description'],
+ u'start tag': u'{%s}' % data[u'tag'],
+ u'start html': data[u'html'],
+ u'end tag': u'{/%s}' % data[u'tag'],
+ u'end html': self.start_html_to_end_html(data[u'html']),
+ u'protected': False,
+ u'temporary': False})
+ FormattingTags.add_html_tags(tags)
+ FormattingTags.save_html_tags()
+ self.save_button.setEnabled(False)
+ self.restore_button.setEnabled(False)
+
+ def start_html_to_end_html(self, start_html):
+ """
+ Return the end HTML for a given start HTML or None if invalid.
+ """
+ end_tags = []
+ match = self.html_regex.match(start_html)
+ if match:
+ match = self.html_tag_regex.search(start_html)
+ while match:
+ if match.group(u'tag'):
+ tag = match.group(u'tag').lower()
+ if match.group(u'close'):
+ if match.group(u'empty') or not end_tags or end_tags.pop() != tag:
+ return
+ elif not match.group(u'empty'):
+ end_tags.append(tag)
+ match = self.html_tag_regex.search(start_html, match.end())
+ return u''.join(map(lambda tag: u'</%s>' % tag, reversed(end_tags)))
+
+ def item_to_data_dict(self, item):
+ """
+ Read the data from a QTableWidgetItem and returns a dict with unicode keys and values.
+ """
+ data = {}
+ for key, value in item.data(QtCore.Qt.UserRole).toMap().items():
+ data[unicode(key)] = unicode(value.toString())
+ return data
Follow ups