forked from openlp/openlp
Head + conflicts
This commit is contained in:
commit
73fb71b4ce
|
@ -186,10 +186,8 @@ class OpenLP(QtGui.QApplication):
|
||||||
``traceback``
|
``traceback``
|
||||||
A traceback object with the details of where the exception occurred.
|
A traceback object with the details of where the exception occurred.
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, u'mainWindow'):
|
log.exception(''.join(format_exception(exctype, value, traceback)))
|
||||||
log.exception(''.join(format_exception(exctype, value, traceback)))
|
if not hasattr(self, u'exception_form'):
|
||||||
return
|
|
||||||
if not hasattr(self, u'exceptionForm'):
|
|
||||||
self.exception_form = ExceptionForm(self.main_window)
|
self.exception_form = ExceptionForm(self.main_window)
|
||||||
self.exception_form.exceptionTextEdit.setPlainText(''.join(format_exception(exctype, value, traceback)))
|
self.exception_form.exceptionTextEdit.setPlainText(''.join(format_exception(exctype, value, traceback)))
|
||||||
self.set_normal_cursor()
|
self.set_normal_cursor()
|
||||||
|
|
|
@ -393,7 +393,6 @@ from settings import Settings
|
||||||
from listwidgetwithdnd import ListWidgetWithDnD
|
from listwidgetwithdnd import ListWidgetWithDnD
|
||||||
from formattingtags import FormattingTags
|
from formattingtags import FormattingTags
|
||||||
from spelltextedit import SpellTextEdit
|
from spelltextedit import SpellTextEdit
|
||||||
from settingsmanager import SettingsManager
|
|
||||||
from plugin import PluginStatus, StringContent, Plugin
|
from plugin import PluginStatus, StringContent, Plugin
|
||||||
from pluginmanager import PluginManager
|
from pluginmanager import PluginManager
|
||||||
from settingstab import SettingsTab
|
from settingstab import SettingsTab
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# OpenLP - Open Source Lyrics Projection #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
|
||||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
|
||||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
|
||||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
|
||||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
|
||||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
|
||||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
|
||||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# 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; version 2 of the License. #
|
|
||||||
# #
|
|
||||||
# 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, write to the Free Software Foundation, Inc., 59 #
|
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
|
||||||
###############################################################################
|
|
||||||
"""
|
|
||||||
Provide handling for persisting OpenLP settings. OpenLP uses QSettings to manage settings persistence. QSettings
|
|
||||||
provides a single API for saving and retrieving settings from the application but writes to disk in an OS dependant
|
|
||||||
format.
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
|
|
||||||
from openlp.core.utils import AppLocation
|
|
||||||
|
|
||||||
|
|
||||||
class SettingsManager(object):
|
|
||||||
"""
|
|
||||||
Class to provide helper functions for the loading and saving of application settings.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_files(section=None, extension=None):
|
|
||||||
"""
|
|
||||||
Get a list of files from the data files path.
|
|
||||||
|
|
||||||
``section``
|
|
||||||
Defaults to *None*. The section of code getting the files - used to load from a section's data subdirectory.
|
|
||||||
|
|
||||||
``extension``
|
|
||||||
Defaults to *None*. The extension to search for.
|
|
||||||
"""
|
|
||||||
path = AppLocation.get_data_path()
|
|
||||||
if section:
|
|
||||||
path = os.path.join(path, section)
|
|
||||||
try:
|
|
||||||
files = os.listdir(path)
|
|
||||||
except OSError:
|
|
||||||
return []
|
|
||||||
if extension:
|
|
||||||
return [filename for filename in files if extension == os.path.splitext(filename)[1]]
|
|
||||||
else:
|
|
||||||
# no filtering required
|
|
||||||
return files
|
|
|
@ -406,7 +406,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
||||||
# Only continue when we include the song's text.
|
# Only continue when we include the song's text.
|
||||||
if not self.slideTextCheckBox.isChecked():
|
if not self.slideTextCheckBox.isChecked():
|
||||||
return
|
return
|
||||||
for item in self.service_manager.serviceItems:
|
for item in self.service_manager.service_items:
|
||||||
# Trigger Audit requests
|
# Trigger Audit requests
|
||||||
Registry().register_function(u'print_service_started', [item[u'service_item']])
|
Registry().register_function(u'print_service_started', [item[u'service_item']])
|
||||||
|
|
||||||
|
|
|
@ -399,19 +399,19 @@ class SlideController(DisplayController):
|
||||||
verse_type = sender_name[15:] if sender_name[:15] == u'shortcutAction_' else u''
|
verse_type = sender_name[15:] if sender_name[:15] == u'shortcutAction_' else u''
|
||||||
if SONGS_PLUGIN_AVAILABLE:
|
if SONGS_PLUGIN_AVAILABLE:
|
||||||
if verse_type == u'V':
|
if verse_type == u'V':
|
||||||
self.current_shortcut = VerseType.TranslatedTags[VerseType.Verse]
|
self.current_shortcut = VerseType.translated_tags[VerseType.Verse]
|
||||||
elif verse_type == u'C':
|
elif verse_type == u'C':
|
||||||
self.current_shortcut = VerseType.TranslatedTags[VerseType.Chorus]
|
self.current_shortcut = VerseType.translated_tags[VerseType.Chorus]
|
||||||
elif verse_type == u'B':
|
elif verse_type == u'B':
|
||||||
self.current_shortcut = VerseType.TranslatedTags[VerseType.Bridge]
|
self.current_shortcut = VerseType.translated_tags[VerseType.Bridge]
|
||||||
elif verse_type == u'P':
|
elif verse_type == u'P':
|
||||||
self.current_shortcut = VerseType.TranslatedTags[VerseType.PreChorus]
|
self.current_shortcut = VerseType.translated_tags[VerseType.PreChorus]
|
||||||
elif verse_type == u'I':
|
elif verse_type == u'I':
|
||||||
self.current_shortcut = VerseType.TranslatedTags[VerseType.Intro]
|
self.current_shortcut = VerseType.translated_tags[VerseType.Intro]
|
||||||
elif verse_type == u'E':
|
elif verse_type == u'E':
|
||||||
self.current_shortcut = VerseType.TranslatedTags[VerseType.Ending]
|
self.current_shortcut = VerseType.translated_tags[VerseType.Ending]
|
||||||
elif verse_type == u'O':
|
elif verse_type == u'O':
|
||||||
self.current_shortcut = VerseType.TranslatedTags[VerseType.Other]
|
self.current_shortcut = VerseType.translated_tags[VerseType.Other]
|
||||||
elif verse_type.isnumeric():
|
elif verse_type.isnumeric():
|
||||||
self.current_shortcut += verse_type
|
self.current_shortcut += verse_type
|
||||||
self.current_shortcut = self.current_shortcut.upper()
|
self.current_shortcut = self.current_shortcut.upper()
|
||||||
|
|
|
@ -38,9 +38,8 @@ import re
|
||||||
from xml.etree.ElementTree import ElementTree, XML
|
from xml.etree.ElementTree import ElementTree, XML
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, SettingsManager, Settings, UiStrings, \
|
from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, Settings, UiStrings, get_text_file_string, \
|
||||||
get_text_file_string, build_icon, translate, check_item_selected, check_directory_exists, create_thumb, \
|
build_icon, translate, check_item_selected, check_directory_exists, create_thumb, validate_thumb
|
||||||
validate_thumb
|
|
||||||
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, BackgroundGradientType
|
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, BackgroundGradientType
|
||||||
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
|
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
|
||||||
from openlp.core.theme import Theme
|
from openlp.core.theme import Theme
|
||||||
|
@ -378,7 +377,6 @@ class ThemeManager(QtGui.QWidget):
|
||||||
theme_zip.close()
|
theme_zip.close()
|
||||||
self.application.set_normal_cursor()
|
self.application.set_normal_cursor()
|
||||||
|
|
||||||
|
|
||||||
def on_import_theme(self):
|
def on_import_theme(self):
|
||||||
"""
|
"""
|
||||||
Opens a file dialog to select the theme file(s) to import before
|
Opens a file dialog to select the theme file(s) to import before
|
||||||
|
@ -404,12 +402,12 @@ class ThemeManager(QtGui.QWidget):
|
||||||
Imports any themes on start up and makes sure there is at least one theme
|
Imports any themes on start up and makes sure there is at least one theme
|
||||||
"""
|
"""
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
files = SettingsManager.get_files(self.settingsSection, u'.otz')
|
files = AppLocation.get_files(self.settingsSection, u'.otz')
|
||||||
for theme_file in files:
|
for theme_file in files:
|
||||||
theme_file = os.path.join(self.path, theme_file)
|
theme_file = os.path.join(self.path, theme_file)
|
||||||
self.unzip_theme(theme_file, self.path)
|
self.unzip_theme(theme_file, self.path)
|
||||||
delete_file(theme_file)
|
delete_file(theme_file)
|
||||||
files = SettingsManager.get_files(self.settingsSection, u'.png')
|
files = AppLocation.get_files(self.settingsSection, u'.otz')
|
||||||
# No themes have been found so create one
|
# No themes have been found so create one
|
||||||
if not files:
|
if not files:
|
||||||
theme = ThemeXML()
|
theme = ThemeXML()
|
||||||
|
@ -429,8 +427,8 @@ class ThemeManager(QtGui.QWidget):
|
||||||
log.debug(u'Load themes from dir')
|
log.debug(u'Load themes from dir')
|
||||||
self.theme_list = []
|
self.theme_list = []
|
||||||
self.theme_list_widget.clear()
|
self.theme_list_widget.clear()
|
||||||
files = SettingsManager.get_files(self.settingsSection, u'.png')
|
files = AppLocation.get_files(self.settingsSection, u'.png')
|
||||||
# Sort the themes by its name considering language specific
|
# Sort the themes by its name considering language specific
|
||||||
files.sort(key=lambda file_name: unicode(file_name), cmp=locale_compare)
|
files.sort(key=lambda file_name: unicode(file_name), cmp=locale_compare)
|
||||||
# now process the file list of png files
|
# now process the file list of png files
|
||||||
for name in files:
|
for name in files:
|
||||||
|
|
|
@ -39,9 +39,10 @@ from subprocess import Popen, PIPE
|
||||||
import sys
|
import sys
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
|
from PyQt4 import QtGui, QtCore
|
||||||
|
|
||||||
from openlp.core.lib import Registry, Settings
|
from openlp.core.lib import Registry, Settings
|
||||||
|
|
||||||
from PyQt4 import QtGui, QtCore
|
|
||||||
|
|
||||||
if sys.platform != u'win32' and sys.platform != u'darwin':
|
if sys.platform != u'win32' and sys.platform != u'darwin':
|
||||||
try:
|
try:
|
||||||
|
@ -50,8 +51,7 @@ if sys.platform != u'win32' and sys.platform != u'darwin':
|
||||||
except ImportError:
|
except ImportError:
|
||||||
XDG_BASE_AVAILABLE = False
|
XDG_BASE_AVAILABLE = False
|
||||||
|
|
||||||
import openlp
|
from openlp.core.lib import translate
|
||||||
from openlp.core.lib import translate, check_directory_exists
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
APPLICATION_VERSION = {}
|
APPLICATION_VERSION = {}
|
||||||
|
@ -78,107 +78,6 @@ class VersionThread(QtCore.QThread):
|
||||||
if LooseVersion(str(version)) > LooseVersion(str(app_version[u'full'])):
|
if LooseVersion(str(version)) > LooseVersion(str(app_version[u'full'])):
|
||||||
Registry().execute(u'openlp_version_check', u'%s' % version)
|
Registry().execute(u'openlp_version_check', u'%s' % version)
|
||||||
|
|
||||||
class AppLocation(object):
|
|
||||||
"""
|
|
||||||
The :class:`AppLocation` class is a static class which retrieves a
|
|
||||||
directory based on the directory type.
|
|
||||||
"""
|
|
||||||
AppDir = 1
|
|
||||||
ConfigDir = 2
|
|
||||||
DataDir = 3
|
|
||||||
PluginsDir = 4
|
|
||||||
VersionDir = 5
|
|
||||||
CacheDir = 6
|
|
||||||
LanguageDir = 7
|
|
||||||
|
|
||||||
# Base path where data/config/cache dir is located
|
|
||||||
BaseDir = None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_directory(dir_type=1):
|
|
||||||
"""
|
|
||||||
Return the appropriate directory according to the directory type.
|
|
||||||
|
|
||||||
``dir_type``
|
|
||||||
The directory type you want, for instance the data directory.
|
|
||||||
"""
|
|
||||||
if dir_type == AppLocation.AppDir:
|
|
||||||
return _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0])
|
|
||||||
elif dir_type == AppLocation.PluginsDir:
|
|
||||||
app_path = os.path.abspath(os.path.split(sys.argv[0])[0])
|
|
||||||
return _get_frozen_path(os.path.join(app_path, u'plugins'),
|
|
||||||
os.path.join(os.path.split(openlp.__file__)[0], u'plugins'))
|
|
||||||
elif dir_type == AppLocation.VersionDir:
|
|
||||||
return _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0])
|
|
||||||
elif dir_type == AppLocation.LanguageDir:
|
|
||||||
app_path = _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), _get_os_dir_path(dir_type))
|
|
||||||
return os.path.join(app_path, u'i18n')
|
|
||||||
elif dir_type == AppLocation.DataDir and AppLocation.BaseDir:
|
|
||||||
return os.path.join(AppLocation.BaseDir, 'data')
|
|
||||||
else:
|
|
||||||
return _get_os_dir_path(dir_type)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_data_path():
|
|
||||||
"""
|
|
||||||
Return the path OpenLP stores all its data under.
|
|
||||||
"""
|
|
||||||
# Check if we have a different data location.
|
|
||||||
if Settings().contains(u'advanced/data path'):
|
|
||||||
path = Settings().value(u'advanced/data path')
|
|
||||||
else:
|
|
||||||
path = AppLocation.get_directory(AppLocation.DataDir)
|
|
||||||
check_directory_exists(path)
|
|
||||||
return os.path.normpath(path)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_section_data_path(section):
|
|
||||||
"""
|
|
||||||
Return the path a particular module stores its data under.
|
|
||||||
"""
|
|
||||||
data_path = AppLocation.get_data_path()
|
|
||||||
path = os.path.join(data_path, section)
|
|
||||||
check_directory_exists(path)
|
|
||||||
return path
|
|
||||||
|
|
||||||
|
|
||||||
def _get_os_dir_path(dir_type):
|
|
||||||
"""
|
|
||||||
Return a path based on which OS and environment we are running in.
|
|
||||||
"""
|
|
||||||
encoding = sys.getfilesystemencoding()
|
|
||||||
if sys.platform == u'win32':
|
|
||||||
if dir_type == AppLocation.DataDir:
|
|
||||||
return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), u'openlp', u'data')
|
|
||||||
elif dir_type == AppLocation.LanguageDir:
|
|
||||||
return os.path.split(openlp.__file__)[0]
|
|
||||||
return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), u'openlp')
|
|
||||||
elif sys.platform == u'darwin':
|
|
||||||
if dir_type == AppLocation.DataDir:
|
|
||||||
return os.path.join(unicode(os.getenv(u'HOME'), encoding),
|
|
||||||
u'Library', u'Application Support', u'openlp', u'Data')
|
|
||||||
elif dir_type == AppLocation.LanguageDir:
|
|
||||||
return os.path.split(openlp.__file__)[0]
|
|
||||||
return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'Library', u'Application Support', u'openlp')
|
|
||||||
else:
|
|
||||||
if dir_type == AppLocation.LanguageDir:
|
|
||||||
prefixes = [u'/usr/local', u'/usr']
|
|
||||||
for prefix in prefixes:
|
|
||||||
directory = os.path.join(prefix, u'share', u'openlp')
|
|
||||||
if os.path.exists(directory):
|
|
||||||
return directory
|
|
||||||
return os.path.join(u'/usr', u'share', u'openlp')
|
|
||||||
if XDG_BASE_AVAILABLE:
|
|
||||||
if dir_type == AppLocation.ConfigDir:
|
|
||||||
return os.path.join(unicode(BaseDirectory.xdg_config_home, encoding), u'openlp')
|
|
||||||
elif dir_type == AppLocation.DataDir:
|
|
||||||
return os.path.join(unicode(BaseDirectory.xdg_data_home, encoding), u'openlp')
|
|
||||||
elif dir_type == AppLocation.CacheDir:
|
|
||||||
return os.path.join(unicode(BaseDirectory.xdg_cache_home, encoding), u'openlp')
|
|
||||||
if dir_type == AppLocation.DataDir:
|
|
||||||
return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'.openlp', u'data')
|
|
||||||
return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'.openlp')
|
|
||||||
|
|
||||||
|
|
||||||
def _get_frozen_path(frozen_option, non_frozen_option):
|
def _get_frozen_path(frozen_option, non_frozen_option):
|
||||||
"""
|
"""
|
||||||
|
@ -497,9 +396,11 @@ def locale_compare(string1, string2):
|
||||||
locale_direct_compare = locale.strcoll
|
locale_direct_compare = locale.strcoll
|
||||||
|
|
||||||
|
|
||||||
|
from applocation import AppLocation
|
||||||
from languagemanager import LanguageManager
|
from languagemanager import LanguageManager
|
||||||
from actions import ActionList
|
from actions import ActionList
|
||||||
|
|
||||||
|
|
||||||
__all__ = [u'AppLocation', u'ActionList', u'LanguageManager', u'get_application_version', u'check_latest_version',
|
__all__ = [u'AppLocation', u'ActionList', u'LanguageManager', u'get_application_version', u'check_latest_version',
|
||||||
u'add_actions', u'get_filesystem_encoding', u'get_web_page', u'get_uno_command', u'get_uno_instance',
|
u'add_actions', u'get_filesystem_encoding', u'get_web_page', u'get_uno_command', u'get_uno_instance',
|
||||||
u'delete_file', u'clean_filename', u'format_time', u'locale_compare', u'locale_direct_compare']
|
u'delete_file', u'clean_filename', u'format_time', u'locale_compare', u'locale_direct_compare']
|
||||||
|
|
|
@ -411,7 +411,7 @@ class ActionList(object):
|
||||||
for existing_action in existing_actions:
|
for existing_action in existing_actions:
|
||||||
if action is existing_action:
|
if action is existing_action:
|
||||||
continue
|
continue
|
||||||
if not global_context or existing_action in affected_actions:
|
if existing_action in affected_actions:
|
||||||
return False
|
return False
|
||||||
if existing_action.shortcutContext() in [QtCore.Qt.WindowShortcut, QtCore.Qt.ApplicationShortcut]:
|
if existing_action.shortcutContext() in [QtCore.Qt.WindowShortcut, QtCore.Qt.ApplicationShortcut]:
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# 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; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# 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, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
"""
|
||||||
|
The :mod:`openlp.core.utils.applocation` module provides an utility for OpenLP receiving the data path etc.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from openlp.core.lib import Settings
|
||||||
|
from openlp.core.utils import _get_frozen_path
|
||||||
|
|
||||||
|
|
||||||
|
if sys.platform != u'win32' and sys.platform != u'darwin':
|
||||||
|
try:
|
||||||
|
from xdg import BaseDirectory
|
||||||
|
XDG_BASE_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
XDG_BASE_AVAILABLE = False
|
||||||
|
|
||||||
|
import openlp
|
||||||
|
from openlp.core.lib import check_directory_exists
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AppLocation(object):
|
||||||
|
"""
|
||||||
|
The :class:`AppLocation` class is a static class which retrieves a
|
||||||
|
directory based on the directory type.
|
||||||
|
"""
|
||||||
|
AppDir = 1
|
||||||
|
ConfigDir = 2
|
||||||
|
DataDir = 3
|
||||||
|
PluginsDir = 4
|
||||||
|
VersionDir = 5
|
||||||
|
CacheDir = 6
|
||||||
|
LanguageDir = 7
|
||||||
|
|
||||||
|
# Base path where data/config/cache dir is located
|
||||||
|
BaseDir = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_directory(dir_type=1):
|
||||||
|
"""
|
||||||
|
Return the appropriate directory according to the directory type.
|
||||||
|
|
||||||
|
``dir_type``
|
||||||
|
The directory type you want, for instance the data directory.
|
||||||
|
"""
|
||||||
|
if dir_type == AppLocation.AppDir:
|
||||||
|
return _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0])
|
||||||
|
elif dir_type == AppLocation.PluginsDir:
|
||||||
|
app_path = os.path.abspath(os.path.split(sys.argv[0])[0])
|
||||||
|
return _get_frozen_path(os.path.join(app_path, u'plugins'),
|
||||||
|
os.path.join(os.path.split(openlp.__file__)[0], u'plugins'))
|
||||||
|
elif dir_type == AppLocation.VersionDir:
|
||||||
|
return _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0])
|
||||||
|
elif dir_type == AppLocation.LanguageDir:
|
||||||
|
app_path = _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), _get_os_dir_path(dir_type))
|
||||||
|
return os.path.join(app_path, u'i18n')
|
||||||
|
elif dir_type == AppLocation.DataDir and AppLocation.BaseDir:
|
||||||
|
return os.path.join(AppLocation.BaseDir, 'data')
|
||||||
|
else:
|
||||||
|
return _get_os_dir_path(dir_type)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_data_path():
|
||||||
|
"""
|
||||||
|
Return the path OpenLP stores all its data under.
|
||||||
|
"""
|
||||||
|
# Check if we have a different data location.
|
||||||
|
if Settings().contains(u'advanced/data path'):
|
||||||
|
path = Settings().value(u'advanced/data path')
|
||||||
|
else:
|
||||||
|
path = AppLocation.get_directory(AppLocation.DataDir)
|
||||||
|
check_directory_exists(path)
|
||||||
|
return os.path.normpath(path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_files(section=None, extension=None):
|
||||||
|
"""
|
||||||
|
Get a list of files from the data files path.
|
||||||
|
|
||||||
|
``section``
|
||||||
|
Defaults to *None*. The section of code getting the files - used to load from a section's data subdirectory.
|
||||||
|
|
||||||
|
``extension``
|
||||||
|
Defaults to *None*. The extension to search for. For example::
|
||||||
|
|
||||||
|
u'.png'
|
||||||
|
"""
|
||||||
|
path = AppLocation.get_data_path()
|
||||||
|
if section:
|
||||||
|
path = os.path.join(path, section)
|
||||||
|
try:
|
||||||
|
files = os.listdir(path)
|
||||||
|
except OSError:
|
||||||
|
return []
|
||||||
|
if extension:
|
||||||
|
return [filename for filename in files if extension == os.path.splitext(filename)[1]]
|
||||||
|
else:
|
||||||
|
# no filtering required
|
||||||
|
return files
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_section_data_path(section):
|
||||||
|
"""
|
||||||
|
Return the path a particular module stores its data under.
|
||||||
|
"""
|
||||||
|
data_path = AppLocation.get_data_path()
|
||||||
|
path = os.path.join(data_path, section)
|
||||||
|
check_directory_exists(path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def _get_os_dir_path(dir_type):
|
||||||
|
"""
|
||||||
|
Return a path based on which OS and environment we are running in.
|
||||||
|
"""
|
||||||
|
encoding = sys.getfilesystemencoding()
|
||||||
|
if sys.platform == u'win32':
|
||||||
|
if dir_type == AppLocation.DataDir:
|
||||||
|
return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), u'openlp', u'data')
|
||||||
|
elif dir_type == AppLocation.LanguageDir:
|
||||||
|
return os.path.split(openlp.__file__)[0]
|
||||||
|
return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), u'openlp')
|
||||||
|
elif sys.platform == u'darwin':
|
||||||
|
if dir_type == AppLocation.DataDir:
|
||||||
|
return os.path.join(unicode(os.getenv(u'HOME'), encoding),
|
||||||
|
u'Library', u'Application Support', u'openlp', u'Data')
|
||||||
|
elif dir_type == AppLocation.LanguageDir:
|
||||||
|
return os.path.split(openlp.__file__)[0]
|
||||||
|
return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'Library', u'Application Support', u'openlp')
|
||||||
|
else:
|
||||||
|
if dir_type == AppLocation.LanguageDir:
|
||||||
|
prefixes = [u'/usr/local', u'/usr']
|
||||||
|
for prefix in prefixes:
|
||||||
|
directory = os.path.join(prefix, u'share', u'openlp')
|
||||||
|
if os.path.exists(directory):
|
||||||
|
return directory
|
||||||
|
return os.path.join(u'/usr', u'share', u'openlp')
|
||||||
|
if XDG_BASE_AVAILABLE:
|
||||||
|
if dir_type == AppLocation.ConfigDir:
|
||||||
|
return os.path.join(unicode(BaseDirectory.xdg_config_home, encoding), u'openlp')
|
||||||
|
elif dir_type == AppLocation.DataDir:
|
||||||
|
return os.path.join(unicode(BaseDirectory.xdg_data_home, encoding), u'openlp')
|
||||||
|
elif dir_type == AppLocation.CacheDir:
|
||||||
|
return os.path.join(unicode(BaseDirectory.xdg_cache_home, encoding), u'openlp')
|
||||||
|
if dir_type == AppLocation.DataDir:
|
||||||
|
return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'.openlp', u'data')
|
||||||
|
return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'.openlp')
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from openlp.core.lib import Registry, SettingsManager, Settings, translate
|
from openlp.core.lib import Registry, Settings, translate
|
||||||
from openlp.core.utils import AppLocation, delete_file
|
from openlp.core.utils import AppLocation, delete_file
|
||||||
from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection
|
from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection
|
||||||
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
|
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
|
||||||
|
@ -137,7 +137,7 @@ class BibleManager(object):
|
||||||
BibleDB class.
|
BibleDB class.
|
||||||
"""
|
"""
|
||||||
log.debug(u'Reload bibles')
|
log.debug(u'Reload bibles')
|
||||||
files = SettingsManager.get_files(self.settingsSection, self.suffix)
|
files = AppLocation.get_files(self.settingsSection, self.suffix)
|
||||||
if u'alternative_book_names.sqlite' in files:
|
if u'alternative_book_names.sqlite' in files:
|
||||||
files.remove(u'alternative_book_names.sqlite')
|
files.remove(u'alternative_book_names.sqlite')
|
||||||
log.debug(u'Bible Files %s', files)
|
log.debug(u'Bible Files %s', files)
|
||||||
|
|
|
@ -255,7 +255,7 @@ class HttpConnection(object):
|
||||||
current_unique_identifier = self.parent.current_item.unique_identifier
|
current_unique_identifier = self.parent.current_item.unique_identifier
|
||||||
else:
|
else:
|
||||||
current_unique_identifier = None
|
current_unique_identifier = None
|
||||||
for item in self.service_manager.serviceItems:
|
for item in self.service_manager.service_items:
|
||||||
service_item = item[u'service_item']
|
service_item = item[u'service_item']
|
||||||
service_items.append({
|
service_items.append({
|
||||||
u'id': unicode(service_item.unique_identifier),
|
u'id': unicode(service_item.unique_identifier),
|
||||||
|
|
|
@ -291,7 +291,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||||
verse_tags_translated = True
|
verse_tags_translated = True
|
||||||
if index is None:
|
if index is None:
|
||||||
index = VerseType.from_tag(verse_tag)
|
index = VerseType.from_tag(verse_tag)
|
||||||
verse[0][u'type'] = VerseType.Tags[index]
|
verse[0][u'type'] = VerseType.tags[index]
|
||||||
if verse[0][u'label'] == u'':
|
if verse[0][u'label'] == u'':
|
||||||
verse[0][u'label'] = u'1'
|
verse[0][u'label'] = u'1'
|
||||||
verse_def = u'%s%s' % (verse[0][u'type'], verse[0][u'label'])
|
verse_def = u'%s%s' % (verse[0][u'type'], verse[0][u'label'])
|
||||||
|
@ -303,7 +303,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||||
for count, verse in enumerate(verses):
|
for count, verse in enumerate(verses):
|
||||||
self.verseListWidget.setRowCount(self.verseListWidget.rowCount() + 1)
|
self.verseListWidget.setRowCount(self.verseListWidget.rowCount() + 1)
|
||||||
item = QtGui.QTableWidgetItem(verse)
|
item = QtGui.QTableWidgetItem(verse)
|
||||||
verse_def = u'%s%s' % (VerseType.Tags[VerseType.Verse], unicode(count + 1))
|
verse_def = u'%s%s' % (VerseType.tags[VerseType.Verse], unicode(count + 1))
|
||||||
item.setData(QtCore.Qt.UserRole, verse_def)
|
item.setData(QtCore.Qt.UserRole, verse_def)
|
||||||
self.verseListWidget.setItem(count, 0, item)
|
self.verseListWidget.setItem(count, 0, item)
|
||||||
if self.song.verse_order:
|
if self.song.verse_order:
|
||||||
|
@ -315,7 +315,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||||
verse_index = VerseType.from_translated_tag(verse_def[0], None)
|
verse_index = VerseType.from_translated_tag(verse_def[0], None)
|
||||||
if verse_index is None:
|
if verse_index is None:
|
||||||
verse_index = VerseType.from_tag(verse_def[0])
|
verse_index = VerseType.from_tag(verse_def[0])
|
||||||
verse_tag = VerseType.TranslatedTags[verse_index].upper()
|
verse_tag = VerseType.translated_tags[verse_index].upper()
|
||||||
translated.append(u'%s%s' % (verse_tag, verse_def[1:]))
|
translated.append(u'%s%s' % (verse_tag, verse_def[1:]))
|
||||||
self.verseOrderEdit.setText(u' '.join(translated))
|
self.verseOrderEdit.setText(u' '.join(translated))
|
||||||
else:
|
else:
|
||||||
|
@ -547,7 +547,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||||
verse_name = parts
|
verse_name = parts
|
||||||
verse_num = u'1'
|
verse_num = u'1'
|
||||||
verse_index = VerseType.from_loose_input(verse_name)
|
verse_index = VerseType.from_loose_input(verse_name)
|
||||||
verse_tag = VerseType.Tags[verse_index]
|
verse_tag = VerseType.tags[verse_index]
|
||||||
# Later we need to handle v1a as well.
|
# Later we need to handle v1a as well.
|
||||||
#regex = re.compile(r'(\d+\w.)')
|
#regex = re.compile(r'(\d+\w.)')
|
||||||
regex = re.compile(r'\D*(\d+)\D*')
|
regex = re.compile(r'\D*(\d+)\D*')
|
||||||
|
@ -599,7 +599,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||||
if len(item) == 1:
|
if len(item) == 1:
|
||||||
verse_index = VerseType.from_translated_tag(item, None)
|
verse_index = VerseType.from_translated_tag(item, None)
|
||||||
if verse_index is not None:
|
if verse_index is not None:
|
||||||
order.append(VerseType.Tags[verse_index] + u'1')
|
order.append(VerseType.tags[verse_index] + u'1')
|
||||||
else:
|
else:
|
||||||
# it matches no verses anyway
|
# it matches no verses anyway
|
||||||
order.append(u'')
|
order.append(u'')
|
||||||
|
@ -609,7 +609,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||||
# it matches no verses anyway
|
# it matches no verses anyway
|
||||||
order.append(u'')
|
order.append(u'')
|
||||||
else:
|
else:
|
||||||
verse_tag = VerseType.Tags[verse_index]
|
verse_tag = VerseType.tags[verse_index]
|
||||||
verse_num = item[1:].lower()
|
verse_num = item[1:].lower()
|
||||||
order.append(verse_tag + verse_num)
|
order.append(verse_tag + verse_num)
|
||||||
return order
|
return order
|
||||||
|
@ -831,7 +831,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||||
ordertext = self.verseOrderEdit.text()
|
ordertext = self.verseOrderEdit.text()
|
||||||
order = []
|
order = []
|
||||||
for item in ordertext.split():
|
for item in ordertext.split():
|
||||||
verse_tag = VerseType.Tags[VerseType.from_translated_tag(item[0])]
|
verse_tag = VerseType.tags[VerseType.from_translated_tag(item[0])]
|
||||||
verse_num = item[1:].lower()
|
verse_num = item[1:].lower()
|
||||||
order.append(u'%s%s' % (verse_tag, verse_num))
|
order.append(u'%s%s' % (verse_tag, verse_num))
|
||||||
self.song.verse_order = u' '.join(order)
|
self.song.verse_order = u' '.join(order)
|
||||||
|
|
|
@ -74,13 +74,13 @@ class Ui_EditVerseDialog(object):
|
||||||
def retranslateUi(self, editVerseDialog):
|
def retranslateUi(self, editVerseDialog):
|
||||||
editVerseDialog.setWindowTitle(translate('SongsPlugin.EditVerseForm', 'Edit Verse'))
|
editVerseDialog.setWindowTitle(translate('SongsPlugin.EditVerseForm', 'Edit Verse'))
|
||||||
self.verseTypeLabel.setText(translate('SongsPlugin.EditVerseForm', '&Verse type:'))
|
self.verseTypeLabel.setText(translate('SongsPlugin.EditVerseForm', '&Verse type:'))
|
||||||
self.verseTypeComboBox.setItemText(VerseType.Verse, VerseType.TranslatedNames[VerseType.Verse])
|
self.verseTypeComboBox.setItemText(VerseType.Verse, VerseType.translated_names[VerseType.Verse])
|
||||||
self.verseTypeComboBox.setItemText(VerseType.Chorus, VerseType.TranslatedNames[VerseType.Chorus])
|
self.verseTypeComboBox.setItemText(VerseType.Chorus, VerseType.translated_names[VerseType.Chorus])
|
||||||
self.verseTypeComboBox.setItemText(VerseType.Bridge, VerseType.TranslatedNames[VerseType.Bridge])
|
self.verseTypeComboBox.setItemText(VerseType.Bridge, VerseType.translated_names[VerseType.Bridge])
|
||||||
self.verseTypeComboBox.setItemText(VerseType.PreChorus, VerseType.TranslatedNames[VerseType.PreChorus])
|
self.verseTypeComboBox.setItemText(VerseType.PreChorus, VerseType.translated_names[VerseType.PreChorus])
|
||||||
self.verseTypeComboBox.setItemText(VerseType.Intro, VerseType.TranslatedNames[VerseType.Intro])
|
self.verseTypeComboBox.setItemText(VerseType.Intro, VerseType.translated_names[VerseType.Intro])
|
||||||
self.verseTypeComboBox.setItemText(VerseType.Ending, VerseType.TranslatedNames[VerseType.Ending])
|
self.verseTypeComboBox.setItemText(VerseType.Ending, VerseType.translated_names[VerseType.Ending])
|
||||||
self.verseTypeComboBox.setItemText(VerseType.Other, VerseType.TranslatedNames[VerseType.Other])
|
self.verseTypeComboBox.setItemText(VerseType.Other, VerseType.translated_names[VerseType.Other])
|
||||||
self.splitButton.setText(UiStrings().Split)
|
self.splitButton.setText(UiStrings().Split)
|
||||||
self.splitButton.setToolTip(UiStrings().SplitToolTip)
|
self.splitButton.setToolTip(UiStrings().SplitToolTip)
|
||||||
self.insertButton.setText(translate('SongsPlugin.EditVerseForm', '&Insert'))
|
self.insertButton.setText(translate('SongsPlugin.EditVerseForm', '&Insert'))
|
||||||
|
|
|
@ -82,8 +82,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||||
|
|
||||||
def onInsertButtonClicked(self):
|
def onInsertButtonClicked(self):
|
||||||
verse_type_index = self.verseTypeComboBox.currentIndex()
|
verse_type_index = self.verseTypeComboBox.currentIndex()
|
||||||
self.insertVerse(VerseType.Tags[verse_type_index],
|
self.insertVerse(VerseType.tags[verse_type_index], self.verseNumberBox.value())
|
||||||
self.verseNumberBox.value())
|
|
||||||
|
|
||||||
def onVerseTypeComboBoxChanged(self):
|
def onVerseTypeComboBoxChanged(self):
|
||||||
self.updateSuggestedVerseNumber()
|
self.updateSuggestedVerseNumber()
|
||||||
|
@ -93,12 +92,11 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||||
|
|
||||||
def updateSuggestedVerseNumber(self):
|
def updateSuggestedVerseNumber(self):
|
||||||
"""
|
"""
|
||||||
Adjusts the verse number SpinBox in regard to the selected verse type
|
Adjusts the verse number SpinBox in regard to the selected verse type and the cursor's position.
|
||||||
and the cursor's position.
|
|
||||||
"""
|
"""
|
||||||
position = self.verseTextEdit.textCursor().position()
|
position = self.verseTextEdit.textCursor().position()
|
||||||
text = self.verseTextEdit.toPlainText()
|
text = self.verseTextEdit.toPlainText()
|
||||||
verse_name = VerseType.TranslatedNames[
|
verse_name = VerseType.translated_names[
|
||||||
self.verseTypeComboBox.currentIndex()]
|
self.verseTypeComboBox.currentIndex()]
|
||||||
if not text:
|
if not text:
|
||||||
return
|
return
|
||||||
|
@ -120,8 +118,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||||
verse_num = 1
|
verse_num = 1
|
||||||
self.verseNumberBox.setValue(verse_num)
|
self.verseNumberBox.setValue(verse_num)
|
||||||
|
|
||||||
def setVerse(self, text, single=False,
|
def setVerse(self, text, single=False, tag=u'%s1' % VerseType.tags[VerseType.Verse]):
|
||||||
tag=u'%s1' % VerseType.Tags[VerseType.Verse]):
|
|
||||||
self.hasSingleVerse = single
|
self.hasSingleVerse = single
|
||||||
if single:
|
if single:
|
||||||
verse_type_index = VerseType.from_tag(tag[0], None)
|
verse_type_index = VerseType.from_tag(tag[0], None)
|
||||||
|
@ -132,7 +129,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||||
self.insertButton.setVisible(False)
|
self.insertButton.setVisible(False)
|
||||||
else:
|
else:
|
||||||
if not text:
|
if not text:
|
||||||
text = u'---[%s:1]---\n' % VerseType.TranslatedNames[VerseType.Verse]
|
text = u'---[%s:1]---\n' % VerseType.translated_names[VerseType.Verse]
|
||||||
self.verseTypeComboBox.setCurrentIndex(0)
|
self.verseTypeComboBox.setCurrentIndex(0)
|
||||||
self.verseNumberBox.setValue(1)
|
self.verseNumberBox.setValue(1)
|
||||||
self.insertButton.setVisible(True)
|
self.insertButton.setVisible(True)
|
||||||
|
@ -141,12 +138,12 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||||
self.verseTextEdit.moveCursor(QtGui.QTextCursor.End)
|
self.verseTextEdit.moveCursor(QtGui.QTextCursor.End)
|
||||||
|
|
||||||
def getVerse(self):
|
def getVerse(self):
|
||||||
return self.verseTextEdit.toPlainText(), VerseType.Tags[self.verseTypeComboBox.currentIndex()], \
|
return self.verseTextEdit.toPlainText(), VerseType.tags[self.verseTypeComboBox.currentIndex()], \
|
||||||
unicode(self.verseNumberBox.value())
|
unicode(self.verseNumberBox.value())
|
||||||
|
|
||||||
def getVerseAll(self):
|
def getVerseAll(self):
|
||||||
text = self.verseTextEdit.toPlainText()
|
text = self.verseTextEdit.toPlainText()
|
||||||
if not text.startswith(u'---['):
|
if not text.startswith(u'---['):
|
||||||
text = u'---[%s:1]---\n%s' % (VerseType.TranslatedNames[VerseType.Verse], text)
|
text = u'---[%s:1]---\n%s' % (VerseType.translated_names[VerseType.Verse], text)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ import os
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import Settings, UiStrings, translate
|
from openlp.core.lib import Registry, Settings, UiStrings, translate
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
||||||
from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect
|
from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect
|
||||||
|
@ -489,6 +489,16 @@ class SongImportForm(OpenLPWizard):
|
||||||
self.formatWidgets[this_format][u'importWidget'] = importWidget
|
self.formatWidgets[this_format][u'importWidget'] = importWidget
|
||||||
return importWidget
|
return importWidget
|
||||||
|
|
||||||
|
def _get_main_window(self):
|
||||||
|
"""
|
||||||
|
Adds the main window to the class dynamically
|
||||||
|
"""
|
||||||
|
if not hasattr(self, u'_main_window'):
|
||||||
|
self._main_window = Registry().get(u'main_window')
|
||||||
|
return self._main_window
|
||||||
|
|
||||||
|
main_window = property(_get_main_window)
|
||||||
|
|
||||||
|
|
||||||
class SongImportSourcePage(QtGui.QWizardPage):
|
class SongImportSourcePage(QtGui.QWizardPage):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -37,8 +37,7 @@ from ui import SongStrings
|
||||||
|
|
||||||
WHITESPACE = re.compile(r'[\W_]+', re.UNICODE)
|
WHITESPACE = re.compile(r'[\W_]+', re.UNICODE)
|
||||||
APOSTROPHE = re.compile(u'[\'`’ʻ′]', re.UNICODE)
|
APOSTROPHE = re.compile(u'[\'`’ʻ′]', re.UNICODE)
|
||||||
PATTERN = re.compile(r"\\([a-z]{1,32})(-?\d{1,10})?[ ]?|\\'"
|
PATTERN = re.compile(r"\\([a-z]{1,32})(-?\d{1,10})?[ ]?|\\'([0-9a-f]{2})|\\([^a-z])|([{}])|[\r\n]+|(.)", re.I)
|
||||||
r"([0-9a-f]{2})|\\([^a-z])|([{}])|[\r\n]+|(.)", re.I)
|
|
||||||
# RTF control words which specify a "destination" to be ignored.
|
# RTF control words which specify a "destination" to be ignored.
|
||||||
DESTINATIONS = frozenset((
|
DESTINATIONS = frozenset((
|
||||||
u'aftncn', u'aftnsep', u'aftnsepc', u'annotation', u'atnauthor',
|
u'aftncn', u'aftnsep', u'aftnsepc', u'annotation', u'atnauthor',
|
||||||
|
@ -138,8 +137,7 @@ CHARSET_MAPPING = {
|
||||||
|
|
||||||
class VerseType(object):
|
class VerseType(object):
|
||||||
"""
|
"""
|
||||||
VerseType provides an enumeration for the tags that may be associated
|
VerseType provides an enumeration for the tags that may be associated with verses in songs.
|
||||||
with verses in songs.
|
|
||||||
"""
|
"""
|
||||||
Verse = 0
|
Verse = 0
|
||||||
Chorus = 1
|
Chorus = 1
|
||||||
|
@ -149,7 +147,7 @@ class VerseType(object):
|
||||||
Ending = 5
|
Ending = 5
|
||||||
Other = 6
|
Other = 6
|
||||||
|
|
||||||
Names = [
|
names = [
|
||||||
u'Verse',
|
u'Verse',
|
||||||
u'Chorus',
|
u'Chorus',
|
||||||
u'Bridge',
|
u'Bridge',
|
||||||
|
@ -157,9 +155,9 @@ class VerseType(object):
|
||||||
u'Intro',
|
u'Intro',
|
||||||
u'Ending',
|
u'Ending',
|
||||||
u'Other']
|
u'Other']
|
||||||
Tags = [name[0].lower() for name in Names]
|
tags = [name[0].lower() for name in names]
|
||||||
|
|
||||||
TranslatedNames = [
|
translated_names = [
|
||||||
translate('SongsPlugin.VerseType', 'Verse'),
|
translate('SongsPlugin.VerseType', 'Verse'),
|
||||||
translate('SongsPlugin.VerseType', 'Chorus'),
|
translate('SongsPlugin.VerseType', 'Chorus'),
|
||||||
translate('SongsPlugin.VerseType', 'Bridge'),
|
translate('SongsPlugin.VerseType', 'Bridge'),
|
||||||
|
@ -167,13 +165,12 @@ class VerseType(object):
|
||||||
translate('SongsPlugin.VerseType', 'Intro'),
|
translate('SongsPlugin.VerseType', 'Intro'),
|
||||||
translate('SongsPlugin.VerseType', 'Ending'),
|
translate('SongsPlugin.VerseType', 'Ending'),
|
||||||
translate('SongsPlugin.VerseType', 'Other')]
|
translate('SongsPlugin.VerseType', 'Other')]
|
||||||
TranslatedTags = [name[0].lower() for name in TranslatedNames]
|
translated_tags = [name[0].lower() for name in translated_names]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def translated_tag(verse_tag, default=Other):
|
def translated_tag(verse_tag, default=Other):
|
||||||
"""
|
"""
|
||||||
Return the translated UPPERCASE tag for a given tag,
|
Return the translated UPPERCASE tag for a given tag, used to show translated verse tags in UI
|
||||||
used to show translated verse tags in UI
|
|
||||||
|
|
||||||
``verse_tag``
|
``verse_tag``
|
||||||
The string to return a VerseType for
|
The string to return a VerseType for
|
||||||
|
@ -182,11 +179,11 @@ class VerseType(object):
|
||||||
Default return value if no matching tag is found
|
Default return value if no matching tag is found
|
||||||
"""
|
"""
|
||||||
verse_tag = verse_tag[0].lower()
|
verse_tag = verse_tag[0].lower()
|
||||||
for num, tag in enumerate(VerseType.Tags):
|
for num, tag in enumerate(VerseType.tags):
|
||||||
if verse_tag == tag:
|
if verse_tag == tag:
|
||||||
return VerseType.TranslatedTags[num].upper()
|
return VerseType.translated_tags[num].upper()
|
||||||
if default in VerseType.TranslatedTags:
|
if default in VerseType.translated_tags:
|
||||||
return VerseType.TranslatedTags[default].upper()
|
return VerseType.translated_tags[default].upper()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def translated_name(verse_tag, default=Other):
|
def translated_name(verse_tag, default=Other):
|
||||||
|
@ -200,11 +197,11 @@ class VerseType(object):
|
||||||
Default return value if no matching tag is found
|
Default return value if no matching tag is found
|
||||||
"""
|
"""
|
||||||
verse_tag = verse_tag[0].lower()
|
verse_tag = verse_tag[0].lower()
|
||||||
for num, tag in enumerate(VerseType.Tags):
|
for num, tag in enumerate(VerseType.tags):
|
||||||
if verse_tag == tag:
|
if verse_tag == tag:
|
||||||
return VerseType.TranslatedNames[num]
|
return VerseType.translated_names[num]
|
||||||
if default in VerseType.TranslatedNames:
|
if default in VerseType.translated_names:
|
||||||
return VerseType.TranslatedNames[default]
|
return VerseType.translated_names[default]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_tag(verse_tag, default=Other):
|
def from_tag(verse_tag, default=Other):
|
||||||
|
@ -218,7 +215,7 @@ class VerseType(object):
|
||||||
Default return value if no matching tag is found
|
Default return value if no matching tag is found
|
||||||
"""
|
"""
|
||||||
verse_tag = verse_tag[0].lower()
|
verse_tag = verse_tag[0].lower()
|
||||||
for num, tag in enumerate(VerseType.Tags):
|
for num, tag in enumerate(VerseType.tags):
|
||||||
if verse_tag == tag:
|
if verse_tag == tag:
|
||||||
return num
|
return num
|
||||||
return default
|
return default
|
||||||
|
@ -235,7 +232,7 @@ class VerseType(object):
|
||||||
Default return value if no matching tag is found
|
Default return value if no matching tag is found
|
||||||
"""
|
"""
|
||||||
verse_tag = verse_tag[0].lower()
|
verse_tag = verse_tag[0].lower()
|
||||||
for num, tag in enumerate(VerseType.TranslatedTags):
|
for num, tag in enumerate(VerseType.translated_tags):
|
||||||
if verse_tag == tag:
|
if verse_tag == tag:
|
||||||
return num
|
return num
|
||||||
return default
|
return default
|
||||||
|
@ -252,7 +249,7 @@ class VerseType(object):
|
||||||
Default return value if no matching tag is found
|
Default return value if no matching tag is found
|
||||||
"""
|
"""
|
||||||
verse_name = verse_name.lower()
|
verse_name = verse_name.lower()
|
||||||
for num, name in enumerate(VerseType.Names):
|
for num, name in enumerate(VerseType.names):
|
||||||
if verse_name == name.lower():
|
if verse_name == name.lower():
|
||||||
return num
|
return num
|
||||||
return default
|
return default
|
||||||
|
@ -266,7 +263,7 @@ class VerseType(object):
|
||||||
The string to return a VerseType for
|
The string to return a VerseType for
|
||||||
"""
|
"""
|
||||||
verse_name = verse_name.lower()
|
verse_name = verse_name.lower()
|
||||||
for num, translation in enumerate(VerseType.TranslatedNames):
|
for num, translation in enumerate(VerseType.translated_names):
|
||||||
if verse_name == translation.lower():
|
if verse_name == translation.lower():
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
@ -296,13 +293,11 @@ class VerseType(object):
|
||||||
|
|
||||||
def retrieve_windows_encoding(recommendation=None):
|
def retrieve_windows_encoding(recommendation=None):
|
||||||
"""
|
"""
|
||||||
Determines which encoding to use on an information source. The process uses
|
Determines which encoding to use on an information source. The process uses both automated detection, which is
|
||||||
both automated detection, which is passed to this method as a
|
passed to this method as a recommendation, and user confirmation to return an encoding.
|
||||||
recommendation, and user confirmation to return an encoding.
|
|
||||||
|
|
||||||
``recommendation``
|
``recommendation``
|
||||||
A recommended encoding discovered programmatically for the user to
|
A recommended encoding discovered programmatically for the user to confirm.
|
||||||
confirm.
|
|
||||||
"""
|
"""
|
||||||
# map chardet result to compatible windows standard code page
|
# map chardet result to compatible windows standard code page
|
||||||
codepage_mapping = {'IBM866': u'cp866', 'TIS-620': u'cp874',
|
codepage_mapping = {'IBM866': u'cp866', 'TIS-620': u'cp874',
|
||||||
|
@ -355,24 +350,22 @@ def retrieve_windows_encoding(recommendation=None):
|
||||||
|
|
||||||
def clean_string(string):
|
def clean_string(string):
|
||||||
"""
|
"""
|
||||||
Strips punctuation from the passed string to assist searching
|
Strips punctuation from the passed string to assist searching.
|
||||||
"""
|
"""
|
||||||
return WHITESPACE.sub(u' ', APOSTROPHE.sub(u'', string)).lower()
|
return WHITESPACE.sub(u' ', APOSTROPHE.sub(u'', string)).lower()
|
||||||
|
|
||||||
|
|
||||||
def clean_title(title):
|
def clean_title(title):
|
||||||
"""
|
"""
|
||||||
Cleans the song title by removing Unicode control chars groups C0 & C1,
|
Cleans the song title by removing Unicode control chars groups C0 & C1, as well as any trailing spaces.
|
||||||
as well as any trailing spaces
|
|
||||||
"""
|
"""
|
||||||
return CONTROL_CHARS.sub(u'', title).rstrip()
|
return CONTROL_CHARS.sub(u'', title).rstrip()
|
||||||
|
|
||||||
|
|
||||||
def clean_song(manager, song):
|
def clean_song(manager, song):
|
||||||
"""
|
"""
|
||||||
Cleans the search title, rebuilds the search lyrics, adds a default author
|
Cleans the search title, rebuilds the search lyrics, adds a default author if the song does not have one and other
|
||||||
if the song does not have one and other clean ups. This should always
|
clean ups. This should always called when a new song is added or changed.
|
||||||
called when a new song is added or changed.
|
|
||||||
|
|
||||||
``manager``
|
``manager``
|
||||||
The song's manager.
|
The song's manager.
|
||||||
|
@ -397,21 +390,20 @@ def clean_song(manager, song):
|
||||||
song.search_title = clean_string(song.title) + u'@' + clean_string(song.alternate_title)
|
song.search_title = clean_string(song.title) + u'@' + clean_string(song.alternate_title)
|
||||||
# Only do this, if we the song is a 1.9.4 song (or older).
|
# Only do this, if we the song is a 1.9.4 song (or older).
|
||||||
if song.lyrics.find(u'<lyrics language="en">') != -1:
|
if song.lyrics.find(u'<lyrics language="en">') != -1:
|
||||||
# Remove the old "language" attribute from lyrics tag (prior to 1.9.5).
|
# Remove the old "language" attribute from lyrics tag (prior to 1.9.5). This is not very important, but this
|
||||||
# This is not very important, but this keeps the database clean. This
|
# keeps the database clean. This can be removed when everybody has cleaned his songs.
|
||||||
# can be removed when everybody has cleaned his songs.
|
|
||||||
song.lyrics = song.lyrics.replace(u'<lyrics language="en">', u'<lyrics>')
|
song.lyrics = song.lyrics.replace(u'<lyrics language="en">', u'<lyrics>')
|
||||||
verses = SongXML().get_verses(song.lyrics)
|
verses = SongXML().get_verses(song.lyrics)
|
||||||
song.search_lyrics = u' '.join([clean_string(verse[1])
|
song.search_lyrics = u' '.join([clean_string(verse[1])
|
||||||
for verse in verses])
|
for verse in verses])
|
||||||
# We need a new and clean SongXML instance.
|
# We need a new and clean SongXML instance.
|
||||||
sxml = SongXML()
|
sxml = SongXML()
|
||||||
# Rebuild the song's verses, to remove any wrong verse names (for
|
# Rebuild the song's verses, to remove any wrong verse names (for example translated ones), which might have
|
||||||
# example translated ones), which might have been added prior to 1.9.5.
|
# been added prior to 1.9.5.
|
||||||
# List for later comparison.
|
# List for later comparison.
|
||||||
compare_order = []
|
compare_order = []
|
||||||
for verse in verses:
|
for verse in verses:
|
||||||
verse_type = VerseType.Tags[VerseType.from_loose_input(verse[0][u'type'])]
|
verse_type = VerseType.tags[VerseType.from_loose_input(verse[0][u'type'])]
|
||||||
sxml.add_verse_to_lyrics(
|
sxml.add_verse_to_lyrics(
|
||||||
verse_type,
|
verse_type,
|
||||||
verse[0][u'label'],
|
verse[0][u'label'],
|
||||||
|
@ -422,15 +414,14 @@ def clean_song(manager, song):
|
||||||
if verse[0][u'label'] == u'1':
|
if verse[0][u'label'] == u'1':
|
||||||
compare_order.append(verse_type.upper())
|
compare_order.append(verse_type.upper())
|
||||||
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||||
# Rebuild the verse order, to convert translated verse tags, which might
|
# Rebuild the verse order, to convert translated verse tags, which might have been added prior to 1.9.5.
|
||||||
# have been added prior to 1.9.5.
|
|
||||||
if song.verse_order:
|
if song.verse_order:
|
||||||
order = CONTROL_CHARS.sub(u'', song.verse_order).strip().split()
|
order = CONTROL_CHARS.sub(u'', song.verse_order).strip().split()
|
||||||
else:
|
else:
|
||||||
order = []
|
order = []
|
||||||
new_order = []
|
new_order = []
|
||||||
for verse_def in order:
|
for verse_def in order:
|
||||||
verse_type = VerseType.Tags[
|
verse_type = VerseType.tags[
|
||||||
VerseType.from_loose_input(verse_def[0])]
|
VerseType.from_loose_input(verse_def[0])]
|
||||||
if len(verse_def) > 1:
|
if len(verse_def) > 1:
|
||||||
new_order.append((u'%s%s' % (verse_type, verse_def[1:])).upper())
|
new_order.append((u'%s%s' % (verse_type, verse_def[1:])).upper())
|
||||||
|
@ -463,16 +454,16 @@ def get_encoding(font, font_table, default_encoding, failed=False):
|
||||||
Finds an encoding to use. Asks user, if necessary.
|
Finds an encoding to use. Asks user, if necessary.
|
||||||
|
|
||||||
``font``
|
``font``
|
||||||
The number of currently active font.
|
The number of currently active font.
|
||||||
|
|
||||||
``font_table``
|
``font_table``
|
||||||
Dictionary of fonts and respective encodings.
|
Dictionary of fonts and respective encodings.
|
||||||
|
|
||||||
``default_encoding``
|
``default_encoding``
|
||||||
The default encoding to use when font_table is empty or no font is used.
|
The default encoding to use when font_table is empty or no font is used.
|
||||||
|
|
||||||
``failed``
|
``failed``
|
||||||
A boolean indicating whether the previous encoding didn't work.
|
A boolean indicating whether the previous encoding didn't work.
|
||||||
"""
|
"""
|
||||||
encoding = None
|
encoding = None
|
||||||
if font in font_table:
|
if font in font_table:
|
||||||
|
@ -494,10 +485,10 @@ def strip_rtf(text, default_encoding=None):
|
||||||
http://stackoverflow.com/questions/188545
|
http://stackoverflow.com/questions/188545
|
||||||
|
|
||||||
``text``
|
``text``
|
||||||
RTF-encoded text, a string.
|
RTF-encoded text, a string.
|
||||||
|
|
||||||
``default_encoding``
|
``default_encoding``
|
||||||
Default encoding to use when no encoding is specified.
|
Default encoding to use when no encoding is specified.
|
||||||
"""
|
"""
|
||||||
# Current font is the font tag we last met.
|
# Current font is the font tag we last met.
|
||||||
font = u''
|
font = u''
|
||||||
|
@ -589,8 +580,7 @@ def strip_rtf(text, default_encoding=None):
|
||||||
|
|
||||||
def natcmp(a, b):
|
def natcmp(a, b):
|
||||||
"""
|
"""
|
||||||
Natural string comparison which mimics the behaviour of Python's internal
|
Natural string comparison which mimics the behaviour of Python's internal cmp function.
|
||||||
cmp function.
|
|
||||||
"""
|
"""
|
||||||
if len(a) <= len(b):
|
if len(a) <= len(b):
|
||||||
for i, key in enumerate(a):
|
for i, key in enumerate(a):
|
||||||
|
|
|
@ -188,13 +188,13 @@ class CCLIFileImport(SongImport):
|
||||||
words_list = song_words.split(u'/t')
|
words_list = song_words.split(u'/t')
|
||||||
for counter in range(len(field_list)):
|
for counter in range(len(field_list)):
|
||||||
if field_list[counter].startswith(u'Ver'):
|
if field_list[counter].startswith(u'Ver'):
|
||||||
verse_type = VerseType.Tags[VerseType.Verse]
|
verse_type = VerseType.tags[VerseType.Verse]
|
||||||
elif field_list[counter].startswith(u'Ch'):
|
elif field_list[counter].startswith(u'Ch'):
|
||||||
verse_type = VerseType.Tags[VerseType.Chorus]
|
verse_type = VerseType.tags[VerseType.Chorus]
|
||||||
elif field_list[counter].startswith(u'Br'):
|
elif field_list[counter].startswith(u'Br'):
|
||||||
verse_type = VerseType.Tags[VerseType.Bridge]
|
verse_type = VerseType.tags[VerseType.Bridge]
|
||||||
else:
|
else:
|
||||||
verse_type = VerseType.Tags[VerseType.Other]
|
verse_type = VerseType.tags[VerseType.Other]
|
||||||
check_first_verse_line = True
|
check_first_verse_line = True
|
||||||
verse_text = unicode(words_list[counter])
|
verse_text = unicode(words_list[counter])
|
||||||
verse_text = verse_text.replace(u'/n', u'\n')
|
verse_text = verse_text.replace(u'/n', u'\n')
|
||||||
|
@ -202,15 +202,15 @@ class CCLIFileImport(SongImport):
|
||||||
verse_lines = verse_text.split(u'\n', 1)
|
verse_lines = verse_text.split(u'\n', 1)
|
||||||
if check_first_verse_line:
|
if check_first_verse_line:
|
||||||
if verse_lines[0].startswith(u'(PRE-CHORUS'):
|
if verse_lines[0].startswith(u'(PRE-CHORUS'):
|
||||||
verse_type = VerseType.Tags[VerseType.PreChorus]
|
verse_type = VerseType.tags[VerseType.PreChorus]
|
||||||
log.debug(u'USR verse PRE-CHORUS: %s', verse_lines[0])
|
log.debug(u'USR verse PRE-CHORUS: %s', verse_lines[0])
|
||||||
verse_text = verse_lines[1]
|
verse_text = verse_lines[1]
|
||||||
elif verse_lines[0].startswith(u'(BRIDGE'):
|
elif verse_lines[0].startswith(u'(BRIDGE'):
|
||||||
verse_type = VerseType.Tags[VerseType.Bridge]
|
verse_type = VerseType.tags[VerseType.Bridge]
|
||||||
log.debug(u'USR verse BRIDGE')
|
log.debug(u'USR verse BRIDGE')
|
||||||
verse_text = verse_lines[1]
|
verse_text = verse_lines[1]
|
||||||
elif verse_lines[0].startswith(u'('):
|
elif verse_lines[0].startswith(u'('):
|
||||||
verse_type = VerseType.Tags[VerseType.Other]
|
verse_type = VerseType.tags[VerseType.Other]
|
||||||
verse_text = verse_lines[1]
|
verse_text = verse_lines[1]
|
||||||
if verse_text:
|
if verse_text:
|
||||||
self.addVerse(verse_text, verse_type)
|
self.addVerse(verse_text, verse_type)
|
||||||
|
@ -292,31 +292,31 @@ class CCLIFileImport(SongImport):
|
||||||
verse_desc_parts = clean_line.split(u' ')
|
verse_desc_parts = clean_line.split(u' ')
|
||||||
if len(verse_desc_parts) == 2:
|
if len(verse_desc_parts) == 2:
|
||||||
if verse_desc_parts[0].startswith(u'Ver'):
|
if verse_desc_parts[0].startswith(u'Ver'):
|
||||||
verse_type = VerseType.Tags[VerseType.Verse]
|
verse_type = VerseType.tags[VerseType.Verse]
|
||||||
elif verse_desc_parts[0].startswith(u'Ch'):
|
elif verse_desc_parts[0].startswith(u'Ch'):
|
||||||
verse_type = VerseType.Tags[VerseType.Chorus]
|
verse_type = VerseType.tags[VerseType.Chorus]
|
||||||
elif verse_desc_parts[0].startswith(u'Br'):
|
elif verse_desc_parts[0].startswith(u'Br'):
|
||||||
verse_type = VerseType.Tags[VerseType.Bridge]
|
verse_type = VerseType.tags[VerseType.Bridge]
|
||||||
else:
|
else:
|
||||||
# we need to analyse the next line for
|
# we need to analyse the next line for
|
||||||
# verse type, so set flag
|
# verse type, so set flag
|
||||||
verse_type = VerseType.Tags[VerseType.Other]
|
verse_type = VerseType.tags[VerseType.Other]
|
||||||
check_first_verse_line = True
|
check_first_verse_line = True
|
||||||
verse_number = verse_desc_parts[1]
|
verse_number = verse_desc_parts[1]
|
||||||
else:
|
else:
|
||||||
verse_type = VerseType.Tags[VerseType.Other]
|
verse_type = VerseType.tags[VerseType.Other]
|
||||||
verse_number = 1
|
verse_number = 1
|
||||||
verse_start = True
|
verse_start = True
|
||||||
else:
|
else:
|
||||||
# check first line for verse type
|
# check first line for verse type
|
||||||
if check_first_verse_line:
|
if check_first_verse_line:
|
||||||
if line.startswith(u'(PRE-CHORUS'):
|
if line.startswith(u'(PRE-CHORUS'):
|
||||||
verse_type = VerseType.Tags[VerseType.PreChorus]
|
verse_type = VerseType.tags[VerseType.PreChorus]
|
||||||
elif line.startswith(u'(BRIDGE'):
|
elif line.startswith(u'(BRIDGE'):
|
||||||
verse_type = VerseType.Tags[VerseType.Bridge]
|
verse_type = VerseType.tags[VerseType.Bridge]
|
||||||
# Handle all other misc types
|
# Handle all other misc types
|
||||||
elif line.startswith(u'('):
|
elif line.startswith(u'('):
|
||||||
verse_type = VerseType.Tags[VerseType.Other]
|
verse_type = VerseType.tags[VerseType.Other]
|
||||||
else:
|
else:
|
||||||
verse_text = verse_text + line
|
verse_text = verse_text + line
|
||||||
check_first_verse_line = False
|
check_first_verse_line = False
|
||||||
|
|
|
@ -175,12 +175,12 @@ class EasySlidesImport(SongImport):
|
||||||
# if the regions are inside verses
|
# if the regions are inside verses
|
||||||
regionsInVerses = (regions and regionlines[regionlines.keys()[0]] > 1)
|
regionsInVerses = (regions and regionlines[regionlines.keys()[0]] > 1)
|
||||||
MarkTypes = {
|
MarkTypes = {
|
||||||
u'CHORUS': VerseType.Tags[VerseType.Chorus],
|
u'CHORUS': VerseType.tags[VerseType.Chorus],
|
||||||
u'VERSE': VerseType.Tags[VerseType.Verse],
|
u'VERSE': VerseType.tags[VerseType.Verse],
|
||||||
u'INTRO': VerseType.Tags[VerseType.Intro],
|
u'INTRO': VerseType.tags[VerseType.Intro],
|
||||||
u'ENDING': VerseType.Tags[VerseType.Ending],
|
u'ENDING': VerseType.tags[VerseType.Ending],
|
||||||
u'BRIDGE': VerseType.Tags[VerseType.Bridge],
|
u'BRIDGE': VerseType.tags[VerseType.Bridge],
|
||||||
u'PRECHORUS': VerseType.Tags[VerseType.PreChorus]
|
u'PRECHORUS': VerseType.tags[VerseType.PreChorus]
|
||||||
}
|
}
|
||||||
verses = {}
|
verses = {}
|
||||||
# list as [region, versetype, versenum, instance]
|
# list as [region, versetype, versenum, instance]
|
||||||
|
|
|
@ -178,7 +178,7 @@ class EasyWorshipSongImport(SongImport):
|
||||||
if result is None:
|
if result is None:
|
||||||
return
|
return
|
||||||
words, self.encoding = result
|
words, self.encoding = result
|
||||||
verse_type = VerseType.Tags[VerseType.Verse]
|
verse_type = VerseType.tags[VerseType.Verse]
|
||||||
for verse in SLIDE_BREAK_REGEX.split(words):
|
for verse in SLIDE_BREAK_REGEX.split(words):
|
||||||
verse = verse.strip()
|
verse = verse.strip()
|
||||||
if not verse:
|
if not verse:
|
||||||
|
@ -187,17 +187,17 @@ class EasyWorshipSongImport(SongImport):
|
||||||
first_line_is_tag = False
|
first_line_is_tag = False
|
||||||
# EW tags: verse, chorus, pre-chorus, bridge, tag,
|
# EW tags: verse, chorus, pre-chorus, bridge, tag,
|
||||||
# intro, ending, slide
|
# intro, ending, slide
|
||||||
for type in VerseType.Names+[u'tag', u'slide']:
|
for tag in VerseType.tags + [u'tag', u'slide']:
|
||||||
type = type.lower()
|
tag = tag.lower()
|
||||||
ew_tag = verse_split[0].strip().lower()
|
ew_tag = verse_split[0].strip().lower()
|
||||||
if ew_tag.startswith(type):
|
if ew_tag.startswith(tag):
|
||||||
verse_type = type[0]
|
verse_type = tag[0]
|
||||||
if type == u'tag' or type == u'slide':
|
if tag == u'tag' or tag == u'slide':
|
||||||
verse_type = VerseType.Tags[VerseType.Other]
|
verse_type = VerseType.tags[VerseType.Other]
|
||||||
first_line_is_tag = True
|
first_line_is_tag = True
|
||||||
number_found = False
|
number_found = False
|
||||||
# check if tag is followed by number and/or note
|
# check if tag is followed by number and/or note
|
||||||
if len(ew_tag) > len(type):
|
if len(ew_tag) > len(tag):
|
||||||
match = NUMBER_REGEX.search(ew_tag)
|
match = NUMBER_REGEX.search(ew_tag)
|
||||||
if match:
|
if match:
|
||||||
number = match.group()
|
number = match.group()
|
||||||
|
@ -209,10 +209,7 @@ class EasyWorshipSongImport(SongImport):
|
||||||
if not number_found:
|
if not number_found:
|
||||||
verse_type += u'1'
|
verse_type += u'1'
|
||||||
break
|
break
|
||||||
self.addVerse(
|
self.addVerse(verse_split[-1].strip() if first_line_is_tag else verse, verse_type)
|
||||||
verse_split[-1].strip() \
|
|
||||||
if first_line_is_tag else verse,
|
|
||||||
verse_type)
|
|
||||||
if len(self.comments) > 5:
|
if len(self.comments) > 5:
|
||||||
self.comments += unicode(translate('SongsPlugin.EasyWorshipSongImport',
|
self.comments += unicode(translate('SongsPlugin.EasyWorshipSongImport',
|
||||||
'\n[above are Song Tags with notes imported from EasyWorship]'))
|
'\n[above are Song Tags with notes imported from EasyWorship]'))
|
||||||
|
@ -224,8 +221,7 @@ class EasyWorshipSongImport(SongImport):
|
||||||
self.memoFile.close()
|
self.memoFile.close()
|
||||||
|
|
||||||
def findField(self, field_name):
|
def findField(self, field_name):
|
||||||
return [i for i, x in enumerate(self.fieldDescs)
|
return [i for i, x in enumerate(self.fieldDescs) if x.name == field_name][0]
|
||||||
if x.name == field_name][0]
|
|
||||||
|
|
||||||
def setRecordStruct(self, field_descs):
|
def setRecordStruct(self, field_descs):
|
||||||
# Begin with empty field struct list
|
# Begin with empty field struct list
|
||||||
|
|
|
@ -412,13 +412,13 @@ class FoilPresenter(object):
|
||||||
temp_sortnr_backup = 1
|
temp_sortnr_backup = 1
|
||||||
temp_sortnr_liste = []
|
temp_sortnr_liste = []
|
||||||
verse_count = {
|
verse_count = {
|
||||||
VerseType.Tags[VerseType.Verse]: 1,
|
VerseType.tags[VerseType.Verse]: 1,
|
||||||
VerseType.Tags[VerseType.Chorus]: 1,
|
VerseType.tags[VerseType.Chorus]: 1,
|
||||||
VerseType.Tags[VerseType.Bridge]: 1,
|
VerseType.tags[VerseType.Bridge]: 1,
|
||||||
VerseType.Tags[VerseType.Ending]: 1,
|
VerseType.tags[VerseType.Ending]: 1,
|
||||||
VerseType.Tags[VerseType.Other]: 1,
|
VerseType.tags[VerseType.Other]: 1,
|
||||||
VerseType.Tags[VerseType.Intro]: 1,
|
VerseType.tags[VerseType.Intro]: 1,
|
||||||
VerseType.Tags[VerseType.PreChorus]: 1
|
VerseType.tags[VerseType.PreChorus]: 1
|
||||||
}
|
}
|
||||||
for strophe in foilpresenterfolie.strophen.strophe:
|
for strophe in foilpresenterfolie.strophen.strophe:
|
||||||
text = self._child(strophe.text_) if hasattr(strophe, u'text_') else u''
|
text = self._child(strophe.text_) if hasattr(strophe, u'text_') else u''
|
||||||
|
@ -438,25 +438,25 @@ class FoilPresenter(object):
|
||||||
temp_verse_name = re.compile(u'[0-9].*').sub(u'', verse_name)
|
temp_verse_name = re.compile(u'[0-9].*').sub(u'', verse_name)
|
||||||
temp_verse_name = temp_verse_name[:3].lower()
|
temp_verse_name = temp_verse_name[:3].lower()
|
||||||
if temp_verse_name == u'ref':
|
if temp_verse_name == u'ref':
|
||||||
verse_type = VerseType.Tags[VerseType.Chorus]
|
verse_type = VerseType.tags[VerseType.Chorus]
|
||||||
elif temp_verse_name == u'r':
|
elif temp_verse_name == u'r':
|
||||||
verse_type = VerseType.Tags[VerseType.Chorus]
|
verse_type = VerseType.tags[VerseType.Chorus]
|
||||||
elif temp_verse_name == u'':
|
elif temp_verse_name == u'':
|
||||||
verse_type = VerseType.Tags[VerseType.Verse]
|
verse_type = VerseType.tags[VerseType.Verse]
|
||||||
elif temp_verse_name == u'v':
|
elif temp_verse_name == u'v':
|
||||||
verse_type = VerseType.Tags[VerseType.Verse]
|
verse_type = VerseType.tags[VerseType.Verse]
|
||||||
elif temp_verse_name == u'bri':
|
elif temp_verse_name == u'bri':
|
||||||
verse_type = VerseType.Tags[VerseType.Bridge]
|
verse_type = VerseType.tags[VerseType.Bridge]
|
||||||
elif temp_verse_name == u'cod':
|
elif temp_verse_name == u'cod':
|
||||||
verse_type = VerseType.Tags[VerseType.Ending]
|
verse_type = VerseType.tags[VerseType.Ending]
|
||||||
elif temp_verse_name == u'sch':
|
elif temp_verse_name == u'sch':
|
||||||
verse_type = VerseType.Tags[VerseType.Ending]
|
verse_type = VerseType.tags[VerseType.Ending]
|
||||||
elif temp_verse_name == u'pre':
|
elif temp_verse_name == u'pre':
|
||||||
verse_type = VerseType.Tags[VerseType.PreChorus]
|
verse_type = VerseType.tags[VerseType.PreChorus]
|
||||||
elif temp_verse_name == u'int':
|
elif temp_verse_name == u'int':
|
||||||
verse_type = VerseType.Tags[VerseType.Intro]
|
verse_type = VerseType.tags[VerseType.Intro]
|
||||||
else:
|
else:
|
||||||
verse_type = VerseType.Tags[VerseType.Other]
|
verse_type = VerseType.tags[VerseType.Other]
|
||||||
verse_number = re.compile(u'[a-zA-Z.+-_ ]*').sub(u'', verse_name)
|
verse_number = re.compile(u'[a-zA-Z.+-_ ]*').sub(u'', verse_name)
|
||||||
# Foilpresenter allows e. g. "C", but we need "C1".
|
# Foilpresenter allows e. g. "C", but we need "C1".
|
||||||
if not verse_number:
|
if not verse_number:
|
||||||
|
@ -469,7 +469,7 @@ class FoilPresenter(object):
|
||||||
if value == u''.join((verse_type, verse_number)):
|
if value == u''.join((verse_type, verse_number)):
|
||||||
verse_number = unicode(int(verse_number) + 1)
|
verse_number = unicode(int(verse_number) + 1)
|
||||||
verse_type_index = VerseType.from_tag(verse_type[0])
|
verse_type_index = VerseType.from_tag(verse_type[0])
|
||||||
verse_type = VerseType.Names[verse_type_index]
|
verse_type = VerseType.tags[verse_type_index]
|
||||||
temp_verse_order[verse_sortnr] = u''.join((verse_type[0],
|
temp_verse_order[verse_sortnr] = u''.join((verse_type[0],
|
||||||
verse_number))
|
verse_number))
|
||||||
temp_verse_order_backup.append(u''.join((verse_type[0],
|
temp_verse_order_backup.append(u''.join((verse_type[0],
|
||||||
|
|
|
@ -430,7 +430,7 @@ class SongMediaItem(MediaManagerItem):
|
||||||
verse_index = VerseType.from_string(verse_tag, None)
|
verse_index = VerseType.from_string(verse_tag, None)
|
||||||
if verse_index is None:
|
if verse_index is None:
|
||||||
verse_index = VerseType.from_tag(verse_tag)
|
verse_index = VerseType.from_tag(verse_tag)
|
||||||
verse_tag = VerseType.TranslatedTags[verse_index].upper()
|
verse_tag = VerseType.translated_tags[verse_index].upper()
|
||||||
verse_def = u'%s%s' % (verse_tag, verse[0][u'label'])
|
verse_def = u'%s%s' % (verse_tag, verse[0][u'label'])
|
||||||
service_item.add_from_text(unicode(verse[1]), verse_def)
|
service_item.add_from_text(unicode(verse[1]), verse_def)
|
||||||
else:
|
else:
|
||||||
|
@ -445,7 +445,7 @@ class SongMediaItem(MediaManagerItem):
|
||||||
verse_index = VerseType.from_translated_tag(verse[0][u'type'])
|
verse_index = VerseType.from_translated_tag(verse[0][u'type'])
|
||||||
else:
|
else:
|
||||||
verse_index = VerseType.from_tag(verse[0][u'type'])
|
verse_index = VerseType.from_tag(verse[0][u'type'])
|
||||||
verse_tag = VerseType.TranslatedTags[verse_index]
|
verse_tag = VerseType.translated_tags[verse_index]
|
||||||
verse_def = u'%s%s' % (verse_tag, verse[0][u'label'])
|
verse_def = u'%s%s' % (verse_tag, verse[0][u'label'])
|
||||||
service_item.add_from_text(verse[1], verse_def)
|
service_item.add_from_text(verse[1], verse_def)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -160,7 +160,7 @@ class OpenSongImport(SongImport):
|
||||||
# keep track of verses appearance order
|
# keep track of verses appearance order
|
||||||
our_verse_order = []
|
our_verse_order = []
|
||||||
# default verse
|
# default verse
|
||||||
verse_tag = VerseType.Tags[VerseType.Verse]
|
verse_tag = VerseType.tags[VerseType.Verse]
|
||||||
verse_num = u'1'
|
verse_num = u'1'
|
||||||
# for the case where song has several sections with same marker
|
# for the case where song has several sections with same marker
|
||||||
inst = 1
|
inst = 1
|
||||||
|
@ -184,21 +184,18 @@ class OpenSongImport(SongImport):
|
||||||
# drop the square brackets
|
# drop the square brackets
|
||||||
right_bracket = this_line.find(u']')
|
right_bracket = this_line.find(u']')
|
||||||
content = this_line[1:right_bracket].lower()
|
content = this_line[1:right_bracket].lower()
|
||||||
# have we got any digits?
|
# have we got any digits? If so, verse number is everything from the digits to the end (openlp does not
|
||||||
# If so, verse number is everything from the digits
|
# have concept of part verses, so just ignore any non integers on the end (including floats))
|
||||||
# to the end (openlp does not have concept of part verses, so
|
|
||||||
# just ignore any non integers on the end (including floats))
|
|
||||||
match = re.match(u'(\D*)(\d+)', content)
|
match = re.match(u'(\D*)(\d+)', content)
|
||||||
if match is not None:
|
if match is not None:
|
||||||
verse_tag = match.group(1)
|
verse_tag = match.group(1)
|
||||||
verse_num = match.group(2)
|
verse_num = match.group(2)
|
||||||
else:
|
else:
|
||||||
# otherwise we assume number 1 and take the whole prefix as
|
# otherwise we assume number 1 and take the whole prefix as the verse tag
|
||||||
# the verse tag
|
|
||||||
verse_tag = content
|
verse_tag = content
|
||||||
verse_num = u'1'
|
verse_num = u'1'
|
||||||
verse_index = VerseType.from_loose_input(verse_tag) if verse_tag else 0
|
verse_index = VerseType.from_loose_input(verse_tag) if verse_tag else 0
|
||||||
verse_tag = VerseType.Tags[verse_index]
|
verse_tag = VerseType.tags[verse_index]
|
||||||
inst = 1
|
inst = 1
|
||||||
if [verse_tag, verse_num, inst] in our_verse_order and verse_num in verses.get(verse_tag, {}):
|
if [verse_tag, verse_num, inst] in our_verse_order and verse_num in verses.get(verse_tag, {}):
|
||||||
inst = len(verses[verse_tag][verse_num]) + 1
|
inst = len(verses[verse_tag][verse_num]) + 1
|
||||||
|
@ -236,8 +233,8 @@ class OpenSongImport(SongImport):
|
||||||
# figure out the presentation order, if present
|
# figure out the presentation order, if present
|
||||||
if u'presentation' in fields and root.presentation:
|
if u'presentation' in fields and root.presentation:
|
||||||
order = unicode(root.presentation)
|
order = unicode(root.presentation)
|
||||||
# We make all the tags in the lyrics lower case, so match that here
|
# We make all the tags in the lyrics lower case, so match that here and then split into a list on the
|
||||||
# and then split into a list on the whitespace
|
# whitespace.
|
||||||
order = order.lower().split()
|
order = order.lower().split()
|
||||||
for verse_def in order:
|
for verse_def in order:
|
||||||
match = re.match(u'(\D*)(\d+.*)', verse_def)
|
match = re.match(u'(\D*)(\d+.*)', verse_def)
|
||||||
|
@ -245,7 +242,7 @@ class OpenSongImport(SongImport):
|
||||||
verse_tag = match.group(1)
|
verse_tag = match.group(1)
|
||||||
verse_num = match.group(2)
|
verse_num = match.group(2)
|
||||||
if not verse_tag:
|
if not verse_tag:
|
||||||
verse_tag = VerseType.Tags[VerseType.Verse]
|
verse_tag = VerseType.tags[VerseType.Verse]
|
||||||
else:
|
else:
|
||||||
# Assume it's no.1 if there are no digits
|
# Assume it's no.1 if there are no digits
|
||||||
verse_tag = verse_def
|
verse_tag = verse_def
|
||||||
|
|
|
@ -27,8 +27,7 @@
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
"""
|
"""
|
||||||
The :mod:`songbeamerimport` module provides the functionality for importing
|
The :mod:`songbeamerimport` module provides the functionality for importing SongBeamer songs into the OpenLP database.
|
||||||
SongBeamer songs into the OpenLP database.
|
|
||||||
"""
|
"""
|
||||||
import chardet
|
import chardet
|
||||||
import codecs
|
import codecs
|
||||||
|
@ -43,32 +42,31 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
class SongBeamerTypes(object):
|
class SongBeamerTypes(object):
|
||||||
MarkTypes = {
|
MarkTypes = {
|
||||||
u'Refrain': VerseType.Tags[VerseType.Chorus],
|
u'Refrain': VerseType.tags[VerseType.Chorus],
|
||||||
u'Chorus': VerseType.Tags[VerseType.Chorus],
|
u'Chorus': VerseType.tags[VerseType.Chorus],
|
||||||
u'Vers': VerseType.Tags[VerseType.Verse],
|
u'Vers': VerseType.tags[VerseType.Verse],
|
||||||
u'Verse': VerseType.Tags[VerseType.Verse],
|
u'Verse': VerseType.tags[VerseType.Verse],
|
||||||
u'Strophe': VerseType.Tags[VerseType.Verse],
|
u'Strophe': VerseType.tags[VerseType.Verse],
|
||||||
u'Intro': VerseType.Tags[VerseType.Intro],
|
u'Intro': VerseType.tags[VerseType.Intro],
|
||||||
u'Coda': VerseType.Tags[VerseType.Ending],
|
u'Coda': VerseType.tags[VerseType.Ending],
|
||||||
u'Ending': VerseType.Tags[VerseType.Ending],
|
u'Ending': VerseType.tags[VerseType.Ending],
|
||||||
u'Bridge': VerseType.Tags[VerseType.Bridge],
|
u'Bridge': VerseType.tags[VerseType.Bridge],
|
||||||
u'Interlude': VerseType.Tags[VerseType.Bridge],
|
u'Interlude': VerseType.tags[VerseType.Bridge],
|
||||||
u'Zwischenspiel': VerseType.Tags[VerseType.Bridge],
|
u'Zwischenspiel': VerseType.tags[VerseType.Bridge],
|
||||||
u'Pre-Chorus': VerseType.Tags[VerseType.PreChorus],
|
u'Pre-Chorus': VerseType.tags[VerseType.PreChorus],
|
||||||
u'Pre-Refrain': VerseType.Tags[VerseType.PreChorus],
|
u'Pre-Refrain': VerseType.tags[VerseType.PreChorus],
|
||||||
u'Pre-Bridge': VerseType.Tags[VerseType.Other],
|
u'Pre-Bridge': VerseType.tags[VerseType.Other],
|
||||||
u'Pre-Coda': VerseType.Tags[VerseType.Other],
|
u'Pre-Coda': VerseType.tags[VerseType.Other],
|
||||||
u'Unbekannt': VerseType.Tags[VerseType.Other],
|
u'Unbekannt': VerseType.tags[VerseType.Other],
|
||||||
u'Unknown': VerseType.Tags[VerseType.Other],
|
u'Unknown': VerseType.tags[VerseType.Other],
|
||||||
u'Unbenannt': VerseType.Tags[VerseType.Other]
|
u'Unbenannt': VerseType.tags[VerseType.Other]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class SongBeamerImport(SongImport):
|
class SongBeamerImport(SongImport):
|
||||||
"""
|
"""
|
||||||
Import Song Beamer files(s)
|
Import Song Beamer files(s). Song Beamer file format is text based in the beginning are one or more control tags
|
||||||
Song Beamer file format is text based
|
written.
|
||||||
in the beginning are one or more control tags written
|
|
||||||
"""
|
"""
|
||||||
HTML_TAG_PAIRS = [
|
HTML_TAG_PAIRS = [
|
||||||
(re.compile(u'<b>'), u'{st}'),
|
(re.compile(u'<b>'), u'{st}'),
|
||||||
|
@ -113,7 +111,7 @@ class SongBeamerImport(SongImport):
|
||||||
return
|
return
|
||||||
self.setDefaults()
|
self.setDefaults()
|
||||||
self.currentVerse = u''
|
self.currentVerse = u''
|
||||||
self.currentVerseType = VerseType.Tags[VerseType.Verse]
|
self.currentVerseType = VerseType.tags[VerseType.Verse]
|
||||||
read_verses = False
|
read_verses = False
|
||||||
file_name = os.path.split(file)[1]
|
file_name = os.path.split(file)[1]
|
||||||
if os.path.isfile(file):
|
if os.path.isfile(file):
|
||||||
|
@ -137,7 +135,7 @@ class SongBeamerImport(SongImport):
|
||||||
self.replaceHtmlTags()
|
self.replaceHtmlTags()
|
||||||
self.addVerse(self.currentVerse, self.currentVerseType)
|
self.addVerse(self.currentVerse, self.currentVerseType)
|
||||||
self.currentVerse = u''
|
self.currentVerse = u''
|
||||||
self.currentVerseType = VerseType.Tags[VerseType.Verse]
|
self.currentVerseType = VerseType.tags[VerseType.Verse]
|
||||||
read_verses = True
|
read_verses = True
|
||||||
verse_start = True
|
verse_start = True
|
||||||
elif read_verses:
|
elif read_verses:
|
||||||
|
@ -155,8 +153,7 @@ class SongBeamerImport(SongImport):
|
||||||
|
|
||||||
def replaceHtmlTags(self):
|
def replaceHtmlTags(self):
|
||||||
"""
|
"""
|
||||||
This can be called to replace SongBeamer's specific (html) tags with
|
This can be called to replace SongBeamer's specific (html) tags with OpenLP's specific (html) tags.
|
||||||
OpenLP's specific (html) tags.
|
|
||||||
"""
|
"""
|
||||||
for pair in SongBeamerImport.HTML_TAG_PAIRS:
|
for pair in SongBeamerImport.HTML_TAG_PAIRS:
|
||||||
self.currentVerse = pair[0].sub(pair[1], self.currentVerse)
|
self.currentVerse = pair[0].sub(pair[1], self.currentVerse)
|
||||||
|
@ -166,8 +163,7 @@ class SongBeamerImport(SongImport):
|
||||||
Parses a meta data line.
|
Parses a meta data line.
|
||||||
|
|
||||||
``line``
|
``line``
|
||||||
The line in the file. It should consist of a tag and a value
|
The line in the file. It should consist of a tag and a value for this tag (unicode)::
|
||||||
for this tag (unicode)::
|
|
||||||
|
|
||||||
u'#Title=Nearer my God to Thee'
|
u'#Title=Nearer my God to Thee'
|
||||||
"""
|
"""
|
||||||
|
@ -272,8 +268,8 @@ class SongBeamerImport(SongImport):
|
||||||
|
|
||||||
def checkVerseMarks(self, line):
|
def checkVerseMarks(self, line):
|
||||||
"""
|
"""
|
||||||
Check and add the verse's MarkType. Returns ``True`` if the given line
|
Check and add the verse's MarkType. Returns ``True`` if the given linE contains a correct verse mark otherwise
|
||||||
contains a correct verse mark otherwise ``False``.
|
``False``.
|
||||||
|
|
||||||
``line``
|
``line``
|
||||||
The line to check for marks (unicode).
|
The line to check for marks (unicode).
|
||||||
|
|
|
@ -303,13 +303,13 @@ class SongImport(QtCore.QObject):
|
||||||
sxml = SongXML()
|
sxml = SongXML()
|
||||||
other_count = 1
|
other_count = 1
|
||||||
for (verse_def, verse_text, lang) in self.verses:
|
for (verse_def, verse_text, lang) in self.verses:
|
||||||
if verse_def[0].lower() in VerseType.Tags:
|
if verse_def[0].lower() in VerseType.tags:
|
||||||
verse_tag = verse_def[0].lower()
|
verse_tag = verse_def[0].lower()
|
||||||
else:
|
else:
|
||||||
new_verse_def = u'%s%d' % (VerseType.Tags[VerseType.Other], other_count)
|
new_verse_def = u'%s%d' % (VerseType.tags[VerseType.Other], other_count)
|
||||||
verses_changed_to_other[verse_def] = new_verse_def
|
verses_changed_to_other[verse_def] = new_verse_def
|
||||||
other_count += 1
|
other_count += 1
|
||||||
verse_tag = VerseType.Tags[VerseType.Other]
|
verse_tag = VerseType.tags[VerseType.Other]
|
||||||
log.info(u'Versetype %s changing to %s', verse_def, new_verse_def)
|
log.info(u'Versetype %s changing to %s', verse_def, new_verse_def)
|
||||||
verse_def = new_verse_def
|
verse_def = new_verse_def
|
||||||
sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)
|
sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)
|
||||||
|
|
|
@ -152,11 +152,11 @@ class SongShowPlusImport(SongImport):
|
||||||
elif block_key == CCLI_NO:
|
elif block_key == CCLI_NO:
|
||||||
self.ccliNumber = int(data)
|
self.ccliNumber = int(data)
|
||||||
elif block_key == VERSE:
|
elif block_key == VERSE:
|
||||||
self.addVerse(unicode(data, u'cp1252'), "%s%s" % (VerseType.Tags[VerseType.Verse], verse_no))
|
self.addVerse(unicode(data, u'cp1252'), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no))
|
||||||
elif block_key == CHORUS:
|
elif block_key == CHORUS:
|
||||||
self.addVerse(unicode(data, u'cp1252'), "%s%s" % (VerseType.Tags[VerseType.Chorus], verse_no))
|
self.addVerse(unicode(data, u'cp1252'), "%s%s" % (VerseType.tags[VerseType.Chorus], verse_no))
|
||||||
elif block_key == BRIDGE:
|
elif block_key == BRIDGE:
|
||||||
self.addVerse(unicode(data, u'cp1252'), "%s%s" % (VerseType.Tags[VerseType.Bridge], verse_no))
|
self.addVerse(unicode(data, u'cp1252'), "%s%s" % (VerseType.tags[VerseType.Bridge], verse_no))
|
||||||
elif block_key == TOPIC:
|
elif block_key == TOPIC:
|
||||||
self.topics.append(unicode(data, u'cp1252'))
|
self.topics.append(unicode(data, u'cp1252'))
|
||||||
elif block_key == COMMENTS:
|
elif block_key == COMMENTS:
|
||||||
|
@ -192,19 +192,19 @@ class SongShowPlusImport(SongImport):
|
||||||
verse_number = "1"
|
verse_number = "1"
|
||||||
verse_type = verse_type.lower()
|
verse_type = verse_type.lower()
|
||||||
if verse_type == "verse":
|
if verse_type == "verse":
|
||||||
verse_tag = VerseType.Tags[VerseType.Verse]
|
verse_tag = VerseType.tags[VerseType.Verse]
|
||||||
elif verse_type == "chorus":
|
elif verse_type == "chorus":
|
||||||
verse_tag = VerseType.Tags[VerseType.Chorus]
|
verse_tag = VerseType.tags[VerseType.Chorus]
|
||||||
elif verse_type == "bridge":
|
elif verse_type == "bridge":
|
||||||
verse_tag = VerseType.Tags[VerseType.Bridge]
|
verse_tag = VerseType.tags[VerseType.Bridge]
|
||||||
elif verse_type == "pre-chorus":
|
elif verse_type == "pre-chorus":
|
||||||
verse_tag = VerseType.Tags[VerseType.PreChorus]
|
verse_tag = VerseType.tags[VerseType.PreChorus]
|
||||||
else:
|
else:
|
||||||
if verse_name not in self.otherList:
|
if verse_name not in self.otherList:
|
||||||
if ignore_unique:
|
if ignore_unique:
|
||||||
return None
|
return None
|
||||||
self.otherCount += 1
|
self.otherCount += 1
|
||||||
self.otherList[verse_name] = str(self.otherCount)
|
self.otherList[verse_name] = str(self.otherCount)
|
||||||
verse_tag = VerseType.Tags[VerseType.Other]
|
verse_tag = VerseType.tags[VerseType.Other]
|
||||||
verse_number = self.otherList[verse_name]
|
verse_number = self.otherList[verse_name]
|
||||||
return verse_tag + verse_number
|
return verse_tag + verse_number
|
||||||
|
|
|
@ -52,8 +52,7 @@ class SundayPlusImport(SongImport):
|
||||||
"""
|
"""
|
||||||
Import Sunday Plus songs
|
Import Sunday Plus songs
|
||||||
|
|
||||||
The format examples can be found attached to bug report at
|
The format examples can be found attached to bug report at <http://support.openlp.org/issues/395>
|
||||||
<http://support.openlp.org/issues/395>
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, manager, **kwargs):
|
def __init__(self, manager, **kwargs):
|
||||||
|
@ -90,7 +89,7 @@ class SundayPlusImport(SongImport):
|
||||||
self.logError(u'File is malformed')
|
self.logError(u'File is malformed')
|
||||||
return False
|
return False
|
||||||
i = 1
|
i = 1
|
||||||
verse_type = VerseType.Tags[VerseType.Verse]
|
verse_type = VerseType.tags[VerseType.Verse]
|
||||||
while i < len(data):
|
while i < len(data):
|
||||||
# Data is held as #name: value pairs inside groups marked as [].
|
# Data is held as #name: value pairs inside groups marked as [].
|
||||||
# Now we are looking for the name.
|
# Now we are looking for the name.
|
||||||
|
@ -137,8 +136,7 @@ class SundayPlusImport(SongImport):
|
||||||
if name == 'MARKER_NAME':
|
if name == 'MARKER_NAME':
|
||||||
value = value.strip()
|
value = value.strip()
|
||||||
if len(value):
|
if len(value):
|
||||||
verse_type = VerseType.Tags[
|
verse_type = VerseType.tags[VerseType.from_loose_input(value[0])]
|
||||||
VerseType.from_loose_input(value[0])]
|
|
||||||
if len(value) >= 2 and value[-1] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
|
if len(value) >= 2 and value[-1] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
|
||||||
verse_type = "%s%s" % (verse_type, value[-1])
|
verse_type = "%s%s" % (verse_type, value[-1])
|
||||||
elif name == 'Hotkey':
|
elif name == 'Hotkey':
|
||||||
|
@ -168,8 +166,7 @@ class SundayPlusImport(SongImport):
|
||||||
self.copyright = u'Public Domain'
|
self.copyright = u'Public Domain'
|
||||||
continue
|
continue
|
||||||
processed_lines.append(line)
|
processed_lines.append(line)
|
||||||
self.addVerse('\n'.join(processed_lines).strip(),
|
self.addVerse('\n'.join(processed_lines).strip(), verse_type)
|
||||||
verse_type)
|
|
||||||
if end == -1:
|
if end == -1:
|
||||||
break
|
break
|
||||||
i = end + 1
|
i = end + 1
|
||||||
|
|
|
@ -97,10 +97,8 @@ class SongXML(object):
|
||||||
Add a verse to the ``<lyrics>`` tag.
|
Add a verse to the ``<lyrics>`` tag.
|
||||||
|
|
||||||
``type``
|
``type``
|
||||||
A string denoting the type of verse. Possible values are *v*,
|
A string denoting the type of verse. Possible values are *v*, *c*, *b*, *p*, *i*, *e* and *o*. Any other
|
||||||
*c*, *b*, *p*, *i*, *e* and *o*.
|
type is **not** allowed, this also includes translated types.
|
||||||
Any other type is **not** allowed, this also includes translated
|
|
||||||
types.
|
|
||||||
|
|
||||||
``number``
|
``number``
|
||||||
An integer denoting the number of the item, for example: verse 1.
|
An integer denoting the number of the item, for example: verse 1.
|
||||||
|
@ -109,8 +107,7 @@ class SongXML(object):
|
||||||
The actual text of the verse to be stored.
|
The actual text of the verse to be stored.
|
||||||
|
|
||||||
``lang``
|
``lang``
|
||||||
The verse's language code (ISO-639). This is not required, but
|
The verse's language code (ISO-639). This is not required, but should be added if available.
|
||||||
should be added if available.
|
|
||||||
"""
|
"""
|
||||||
verse = etree.Element(u'verse', type=unicode(type),
|
verse = etree.Element(u'verse', type=unicode(type),
|
||||||
label=unicode(number))
|
label=unicode(number))
|
||||||
|
@ -128,24 +125,21 @@ class SongXML(object):
|
||||||
|
|
||||||
def get_verses(self, xml):
|
def get_verses(self, xml):
|
||||||
"""
|
"""
|
||||||
Iterates through the verses in the XML and returns a list of verses
|
Iterates through the verses in the XML and returns a list of verses and their attributes.
|
||||||
and their attributes.
|
|
||||||
|
|
||||||
``xml``
|
``xml``
|
||||||
The XML of the song to be parsed.
|
The XML of the song to be parsed.
|
||||||
|
|
||||||
The returned list has the following format::
|
The returned list has the following format::
|
||||||
|
|
||||||
[[{'type': 'v', 'label': '1'},
|
[[{'type': 'v', 'label': '1'}, u"optional slide split 1[---]optional slide split 2"],
|
||||||
u"optional slide split 1[---]optional slide split 2"],
|
|
||||||
[{'lang': 'en', 'type': 'c', 'label': '1'}, u"English chorus"]]
|
[{'lang': 'en', 'type': 'c', 'label': '1'}, u"English chorus"]]
|
||||||
"""
|
"""
|
||||||
self.song_xml = None
|
self.song_xml = None
|
||||||
verse_list = []
|
verse_list = []
|
||||||
if not xml.startswith(u'<?xml') and not xml.startswith(u'<song'):
|
if not xml.startswith(u'<?xml') and not xml.startswith(u'<song'):
|
||||||
# This is an old style song, without XML. Let's handle it correctly
|
# This is an old style song, without XML. Let's handle it correctly by iterating through the verses, and
|
||||||
# by iterating through the verses, and then recreating the internal
|
# then recreating the internal xml object as well.
|
||||||
# xml object as well.
|
|
||||||
self.song_xml = objectify.fromstring(u'<song version="1.0" />')
|
self.song_xml = objectify.fromstring(u'<song version="1.0" />')
|
||||||
self.lyrics = etree.SubElement(self.song_xml, u'lyrics')
|
self.lyrics = etree.SubElement(self.song_xml, u'lyrics')
|
||||||
verses = xml.split(u'\n\n')
|
verses = xml.split(u'\n\n')
|
||||||
|
@ -176,11 +170,10 @@ class SongXML(object):
|
||||||
|
|
||||||
class OpenLyrics(object):
|
class OpenLyrics(object):
|
||||||
"""
|
"""
|
||||||
This class represents the converter for OpenLyrics XML (version 0.8)
|
This class represents the converter for OpenLyrics XML (version 0.8) to/from a song.
|
||||||
to/from a song.
|
|
||||||
|
|
||||||
As OpenLyrics has a rich set of different features, we cannot support them
|
As OpenLyrics has a rich set of different features, we cannot support them all. The following features are
|
||||||
all. The following features are supported by the :class:`OpenLyrics` class:
|
supported by the :class:`OpenLyrics` class:
|
||||||
|
|
||||||
``<authors>``
|
``<authors>``
|
||||||
OpenLP does not support the attribute *type* and *lang*.
|
OpenLP does not support the attribute *type* and *lang*.
|
||||||
|
@ -189,8 +182,7 @@ class OpenLyrics(object):
|
||||||
This property is not supported.
|
This property is not supported.
|
||||||
|
|
||||||
``<comments>``
|
``<comments>``
|
||||||
The ``<comments>`` property is fully supported. But comments in lyrics
|
The ``<comments>`` property is fully supported. But comments in lyrics are not supported.
|
||||||
are not supported.
|
|
||||||
|
|
||||||
``<copyright>``
|
``<copyright>``
|
||||||
This property is fully supported.
|
This property is fully supported.
|
||||||
|
@ -208,23 +200,20 @@ class OpenLyrics(object):
|
||||||
This property is not supported.
|
This property is not supported.
|
||||||
|
|
||||||
``<lines>``
|
``<lines>``
|
||||||
The attribute *part* is not supported. The *break* attribute is
|
The attribute *part* is not supported. The *break* attribute is supported.
|
||||||
supported.
|
|
||||||
|
|
||||||
``<publisher>``
|
``<publisher>``
|
||||||
This property is not supported.
|
This property is not supported.
|
||||||
|
|
||||||
``<songbooks>``
|
``<songbooks>``
|
||||||
As OpenLP does only support one songbook, we cannot consider more than
|
As OpenLP does only support one songbook, we cannot consider more than one songbook.
|
||||||
one songbook.
|
|
||||||
|
|
||||||
``<tempo>``
|
``<tempo>``
|
||||||
This property is not supported.
|
This property is not supported.
|
||||||
|
|
||||||
``<themes>``
|
``<themes>``
|
||||||
Topics, as they are called in OpenLP, are fully supported, whereby only
|
Topics, as they are called in OpenLP, are fully supported, whereby only the topic text (e. g. Grace) is
|
||||||
the topic text (e. g. Grace) is considered, but neither the *id* nor
|
considered, but neither the *id* nor *lang*.
|
||||||
*lang*.
|
|
||||||
|
|
||||||
``<transposition>``
|
``<transposition>``
|
||||||
This property is not supported.
|
This property is not supported.
|
||||||
|
@ -233,9 +222,8 @@ class OpenLyrics(object):
|
||||||
This property is not supported.
|
This property is not supported.
|
||||||
|
|
||||||
``<verse name="v1a" lang="he" translit="en">``
|
``<verse name="v1a" lang="he" translit="en">``
|
||||||
The attribute *translit* is not supported. Note, the attribute *lang* is
|
The attribute *translit* is not supported. Note, the attribute *lang* is considered, but there is not further
|
||||||
considered, but there is not further functionality implemented yet. The
|
functionality implemented yet. The following verse "types" are supported by OpenLP:
|
||||||
following verse "types" are supported by OpenLP:
|
|
||||||
|
|
||||||
* v
|
* v
|
||||||
* c
|
* c
|
||||||
|
@ -245,13 +233,10 @@ class OpenLyrics(object):
|
||||||
* e
|
* e
|
||||||
* o
|
* o
|
||||||
|
|
||||||
The verse "types" stand for *Verse*, *Chorus*, *Bridge*, *Pre-Chorus*,
|
The verse "types" stand for *Verse*, *Chorus*, *Bridge*, *Pre-Chorus*, *Intro*, *Ending* and *Other*. Any
|
||||||
*Intro*, *Ending* and *Other*. Any numeric value is allowed after the
|
numeric value is allowed after the verse type. The complete verse name in OpenLP always consists of the verse
|
||||||
verse type. The complete verse name in OpenLP always consists of the
|
type and the verse number. If not number is present *1* is assumed. OpenLP will merge verses which are split
|
||||||
verse type and the verse number. If not number is present *1* is
|
up by appending a letter to the verse name, such as *v1a*.
|
||||||
assumed.
|
|
||||||
OpenLP will merge verses which are split up by appending a letter to the
|
|
||||||
verse name, such as *v1a*.
|
|
||||||
|
|
||||||
``<verseOrder>``
|
``<verseOrder>``
|
||||||
OpenLP supports this property.
|
OpenLP supports this property.
|
||||||
|
@ -359,17 +344,14 @@ class OpenLyrics(object):
|
||||||
|
|
||||||
def _get_missing_tags(self, text):
|
def _get_missing_tags(self, text):
|
||||||
"""
|
"""
|
||||||
Tests the given text for not closed formatting tags and returns a tuple
|
Tests the given text for not closed formatting tags and returns a tuple consisting of two unicode strings::
|
||||||
consisting of two unicode strings::
|
|
||||||
|
|
||||||
(u'{st}{r}', u'{/r}{/st}')
|
(u'{st}{r}', u'{/r}{/st}')
|
||||||
|
|
||||||
The first unicode string are the start tags (for the next slide). The
|
The first unicode string are the start tags (for the next slide). The second unicode string are the end tags.
|
||||||
second unicode string are the end tags.
|
|
||||||
|
|
||||||
``text``
|
``text``
|
||||||
The text to test. The text must **not** contain html tags, only
|
The text to test. The text must **not** contain html tags, only OpenLP formatting tags are allowed::
|
||||||
OpenLP formatting tags are allowed::
|
|
||||||
|
|
||||||
{st}{r}Text text text
|
{st}{r}Text text text
|
||||||
"""
|
"""
|
||||||
|
@ -379,9 +361,8 @@ class OpenLyrics(object):
|
||||||
continue
|
continue
|
||||||
if text.count(tag[u'start tag']) != text.count(tag[u'end tag']):
|
if text.count(tag[u'start tag']) != text.count(tag[u'end tag']):
|
||||||
tags.append((text.find(tag[u'start tag']), tag[u'start tag'], tag[u'end tag']))
|
tags.append((text.find(tag[u'start tag']), tag[u'start tag'], tag[u'end tag']))
|
||||||
# Sort the lists, so that the tags which were opened first on the first
|
# Sort the lists, so that the tags which were opened first on the first slide (the text we are checking) will
|
||||||
# slide (the text we are checking) will be opened first on the next
|
# be opened first on the next slide as well.
|
||||||
# slide as well.
|
|
||||||
tags.sort(key=lambda tag: tag[0])
|
tags.sort(key=lambda tag: tag[0])
|
||||||
end_tags = []
|
end_tags = []
|
||||||
start_tags = []
|
start_tags = []
|
||||||
|
@ -393,16 +374,15 @@ class OpenLyrics(object):
|
||||||
|
|
||||||
def xml_to_song(self, xml, parse_and_temporary_save=False):
|
def xml_to_song(self, xml, parse_and_temporary_save=False):
|
||||||
"""
|
"""
|
||||||
Create and save a song from OpenLyrics format xml to the database. Since
|
Create and save a song from OpenLyrics format xml to the database. Since we also export XML from external
|
||||||
we also export XML from external sources (e. g. OpenLyrics import), we
|
sources (e. g. OpenLyrics import), we cannot ensure, that it completely conforms to the OpenLyrics standard.
|
||||||
cannot ensure, that it completely conforms to the OpenLyrics standard.
|
|
||||||
|
|
||||||
``xml``
|
``xml``
|
||||||
The XML to parse (unicode).
|
The XML to parse (unicode).
|
||||||
|
|
||||||
``parse_and_temporary_save``
|
``parse_and_temporary_save``
|
||||||
Switch to skip processing the whole song and storing the songs in
|
Switch to skip processing the whole song and storing the songs in the database with a temporary flag.
|
||||||
the database with a temporary flag. Defaults to ``False``.
|
Defaults to ``False``.
|
||||||
"""
|
"""
|
||||||
# No xml get out of here.
|
# No xml get out of here.
|
||||||
if not xml:
|
if not xml:
|
||||||
|
@ -448,8 +428,7 @@ class OpenLyrics(object):
|
||||||
|
|
||||||
def _add_tag_to_formatting(self, tag_name, tags_element):
|
def _add_tag_to_formatting(self, tag_name, tags_element):
|
||||||
"""
|
"""
|
||||||
Add new formatting tag to the element ``<format>`` if the tag is not
|
Add new formatting tag to the element ``<format>`` if the tag is not present yet.
|
||||||
present yet.
|
|
||||||
"""
|
"""
|
||||||
available_tags = FormattingTags.get_html_tags()
|
available_tags = FormattingTags.get_html_tags()
|
||||||
start_tag = '{%s}' % tag_name
|
start_tag = '{%s}' % tag_name
|
||||||
|
@ -469,8 +448,7 @@ class OpenLyrics(object):
|
||||||
|
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
Convert text with formatting tags from OpenLP format to OpenLyrics
|
Convert text with formatting tags from OpenLP format to OpenLyrics format and append it to element ``<lines>``.
|
||||||
format and append it to element ``<lines>``.
|
|
||||||
"""
|
"""
|
||||||
start_tags = OpenLyrics.START_TAGS_REGEX.findall(text)
|
start_tags = OpenLyrics.START_TAGS_REGEX.findall(text)
|
||||||
end_tags = OpenLyrics.END_TAGS_REGEX.findall(text)
|
end_tags = OpenLyrics.END_TAGS_REGEX.findall(text)
|
||||||
|
@ -478,8 +456,7 @@ class OpenLyrics(object):
|
||||||
for tag in start_tags:
|
for tag in start_tags:
|
||||||
# Tags already converted to xml structure.
|
# Tags already converted to xml structure.
|
||||||
xml_tags = tags_element.xpath(u'tag/attribute::name')
|
xml_tags = tags_element.xpath(u'tag/attribute::name')
|
||||||
# Some formatting tag has only starting part e.g. <br>.
|
# Some formatting tag has only starting part e.g. <br>. Handle this case.
|
||||||
# Handle this case.
|
|
||||||
if tag in end_tags:
|
if tag in end_tags:
|
||||||
text = text.replace(u'{%s}' % tag, u'<tag name="%s">' % tag)
|
text = text.replace(u'{%s}' % tag, u'<tag name="%s">' % tag)
|
||||||
else:
|
else:
|
||||||
|
@ -586,8 +563,8 @@ class OpenLyrics(object):
|
||||||
|
|
||||||
def _process_formatting_tags(self, song_xml, temporary):
|
def _process_formatting_tags(self, song_xml, temporary):
|
||||||
"""
|
"""
|
||||||
Process the formatting tags from the song and either add missing tags
|
Process the formatting tags from the song and either add missing tags temporary or permanently to the
|
||||||
temporary or permanently to the formatting tag list.
|
formatting tag list.
|
||||||
"""
|
"""
|
||||||
if not hasattr(song_xml, u'format'):
|
if not hasattr(song_xml, u'format'):
|
||||||
return
|
return
|
||||||
|
@ -608,8 +585,8 @@ class OpenLyrics(object):
|
||||||
u'end html': tag.close.text if hasattr(tag, 'close') else u'',
|
u'end html': tag.close.text if hasattr(tag, 'close') else u'',
|
||||||
u'protected': False,
|
u'protected': False,
|
||||||
}
|
}
|
||||||
# Add 'temporary' key in case the formatting tag should not be
|
# Add 'temporary' key in case the formatting tag should not be saved otherwise it is supposed that
|
||||||
# saved otherwise it is supposed that formatting tag is permanent.
|
# formatting tag is permanent.
|
||||||
if temporary:
|
if temporary:
|
||||||
openlp_tag[u'temporary'] = temporary
|
openlp_tag[u'temporary'] = temporary
|
||||||
found_tags.append(openlp_tag)
|
found_tags.append(openlp_tag)
|
||||||
|
@ -620,15 +597,14 @@ class OpenLyrics(object):
|
||||||
|
|
||||||
def _process_lines_mixed_content(self, element, newlines=True):
|
def _process_lines_mixed_content(self, element, newlines=True):
|
||||||
"""
|
"""
|
||||||
Converts the xml text with mixed content to OpenLP representation.
|
Converts the xml text with mixed content to OpenLP representation. Chords are skipped and formatting tags are
|
||||||
Chords are skipped and formatting tags are converted.
|
converted.
|
||||||
|
|
||||||
``element``
|
``element``
|
||||||
The property object (lxml.etree.Element).
|
The property object (lxml.etree.Element).
|
||||||
|
|
||||||
``newlines``
|
``newlines``
|
||||||
The switch to enable/disable processing of line breaks <br/>.
|
The switch to enable/disable processing of line breaks <br/>. The <br/> is used since OpenLyrics 0.8.
|
||||||
The <br/> is used since OpenLyrics 0.8.
|
|
||||||
"""
|
"""
|
||||||
text = u''
|
text = u''
|
||||||
use_endtag = True
|
use_endtag = True
|
||||||
|
@ -684,12 +660,10 @@ class OpenLyrics(object):
|
||||||
lines = etree.tostring(lines)
|
lines = etree.tostring(lines)
|
||||||
element = etree.XML(lines)
|
element = etree.XML(lines)
|
||||||
|
|
||||||
# OpenLyrics 0.8 uses <br/> for new lines.
|
# OpenLyrics 0.8 uses <br/> for new lines. Append text from "lines" element to verse text.
|
||||||
# Append text from "lines" element to verse text.
|
|
||||||
if version > '0.7':
|
if version > '0.7':
|
||||||
text = self._process_lines_mixed_content(element)
|
text = self._process_lines_mixed_content(element)
|
||||||
# OpenLyrics version <= 0.7 contais <line> elements to represent lines.
|
# OpenLyrics version <= 0.7 contais <line> elements to represent lines. First child element is tested.
|
||||||
# First child element is tested.
|
|
||||||
else:
|
else:
|
||||||
# Loop over the "line" elements removing comments and chords.
|
# Loop over the "line" elements removing comments and chords.
|
||||||
for line in element:
|
for line in element:
|
||||||
|
@ -742,16 +716,15 @@ class OpenLyrics(object):
|
||||||
text += u'\n[---]'
|
text += u'\n[---]'
|
||||||
verse_def = verse.get(u'name', u' ').lower()
|
verse_def = verse.get(u'name', u' ').lower()
|
||||||
verse_tag, verse_number, verse_part = OpenLyrics.VERSE_TAG_SPLITTER.search(verse_def).groups()
|
verse_tag, verse_number, verse_part = OpenLyrics.VERSE_TAG_SPLITTER.search(verse_def).groups()
|
||||||
if verse_tag not in VerseType.Tags:
|
if verse_tag not in VerseType.tags:
|
||||||
verse_tag = VerseType.Tags[VerseType.Other]
|
verse_tag = VerseType.tags[VerseType.Other]
|
||||||
# OpenLyrics allows e. g. "c", but we need "c1". However, this does
|
# OpenLyrics allows e. g. "c", but we need "c1". However, this does not correct the verse order.
|
||||||
# not correct the verse order.
|
|
||||||
if not verse_number:
|
if not verse_number:
|
||||||
verse_number = u'1'
|
verse_number = u'1'
|
||||||
lang = verse.get(u'lang')
|
lang = verse.get(u'lang')
|
||||||
translit = verse.get(u'translit')
|
translit = verse.get(u'translit')
|
||||||
# In OpenLP 1.9.6 we used v1a, v1b ... to represent visual slide
|
# In OpenLP 1.9.6 we used v1a, v1b ... to represent visual slide breaks. In OpenLyrics 0.7 an attribute has
|
||||||
# breaks. In OpenLyrics 0.7 an attribute has been added.
|
# been added.
|
||||||
if song_xml.get(u'modifiedIn') in (u'1.9.6', u'OpenLP 1.9.6') and \
|
if song_xml.get(u'modifiedIn') in (u'1.9.6', u'OpenLP 1.9.6') and \
|
||||||
song_xml.get(u'version') == u'0.7' and (verse_tag, verse_number, lang, translit) in verses:
|
song_xml.get(u'version') == u'0.7' and (verse_tag, verse_number, lang, translit) in verses:
|
||||||
verses[(verse_tag, verse_number, lang, translit, None)] += u'\n[---]\n' + text
|
verses[(verse_tag, verse_number, lang, translit, None)] += u'\n[---]\n' + text
|
||||||
|
|
|
@ -91,6 +91,7 @@ OPTIONAL_MODULES = [
|
||||||
('MySQLdb', ' (MySQL support)'),
|
('MySQLdb', ' (MySQL support)'),
|
||||||
('psycopg2', ' (PostgreSQL support)'),
|
('psycopg2', ' (PostgreSQL support)'),
|
||||||
('nose', ' (testing framework)'),
|
('nose', ' (testing framework)'),
|
||||||
|
('mock', ' (testing module)'),
|
||||||
]
|
]
|
||||||
|
|
||||||
w = sys.stdout.write
|
w = sys.stdout.write
|
||||||
|
|
|
@ -45,8 +45,8 @@ class TestFormattingTags(TestCase):
|
||||||
FormattingTags.load_tags()
|
FormattingTags.load_tags()
|
||||||
new_tags_list = FormattingTags.get_html_tags()
|
new_tags_list = FormattingTags.get_html_tags()
|
||||||
|
|
||||||
# THEN: Lists should be identically.
|
# THEN: Lists should be identical.
|
||||||
assert old_tags_list == new_tags_list, u'The formatting tag lists should be identically.'
|
assert old_tags_list == new_tags_list, u'The formatting tag lists should be identical.'
|
||||||
|
|
||||||
def get_html_tags_with_user_tags_test(self):
|
def get_html_tags_with_user_tags_test(self):
|
||||||
"""
|
"""
|
||||||
|
@ -69,16 +69,16 @@ class TestFormattingTags(TestCase):
|
||||||
FormattingTags.add_html_tags([TAG])
|
FormattingTags.add_html_tags([TAG])
|
||||||
new_tags_list = copy.deepcopy(FormattingTags.get_html_tags())
|
new_tags_list = copy.deepcopy(FormattingTags.get_html_tags())
|
||||||
|
|
||||||
# THEN: Lists should not be identically.
|
# THEN: Lists should not be identical.
|
||||||
assert old_tags_list != new_tags_list, u'The lists should be different.'
|
assert old_tags_list != new_tags_list, u'The lists should be different.'
|
||||||
|
|
||||||
# THEN: Added tag and last tag should be the same.
|
# THEN: Added tag and last tag should be the same.
|
||||||
new_tag = new_tags_list.pop()
|
new_tag = new_tags_list.pop()
|
||||||
assert TAG == new_tag, u'Tags should be identically.'
|
assert TAG == new_tag, u'Tags should be identical.'
|
||||||
|
|
||||||
# WHEN: Remove the new tag.
|
# WHEN: Remove the new tag.
|
||||||
FormattingTags.remove_html_tag(len(new_tags_list))
|
FormattingTags.remove_html_tag(len(new_tags_list))
|
||||||
|
|
||||||
# THEN: The lists should now be identically.
|
# THEN: The lists should now be identical.
|
||||||
assert old_tags_list == FormattingTags.get_html_tags(), u'The lists should be identically.'
|
assert old_tags_list == FormattingTags.get_html_tags(), u'The lists should be identical.'
|
||||||
|
|
||||||
|
|
|
@ -54,5 +54,5 @@ class TestScreenList(TestCase):
|
||||||
new_screens = self.screens.screen_list
|
new_screens = self.screens.screen_list
|
||||||
assert len(old_screens) + 1 == len(new_screens), u'The new_screens list should be bigger.'
|
assert len(old_screens) + 1 == len(new_screens), u'The new_screens list should be bigger.'
|
||||||
|
|
||||||
# THEN: The screens should be identically.
|
# THEN: The screens should be identical.
|
||||||
assert SCREEN == new_screens.pop(), u'The new screen should be identically to the screen defined above.'
|
assert SCREEN == new_screens.pop(), u'The new screen should be identical to the screen defined above.'
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
"""
|
"""
|
||||||
Functional tests to test the AppLocation class and related methods.
|
Functional tests to test the AppLocation class and related methods.
|
||||||
"""
|
"""
|
||||||
|
import copy
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from mock import patch
|
from mock import patch
|
||||||
|
|
||||||
from openlp.core.utils import AppLocation
|
from openlp.core.utils import AppLocation
|
||||||
|
|
||||||
|
|
||||||
|
FILE_LIST = [u'file1', u'file2', u'file3.txt', u'file4.txt', u'file5.mp3', u'file6.mp3']
|
||||||
|
|
||||||
|
|
||||||
class TestAppLocation(TestCase):
|
class TestAppLocation(TestCase):
|
||||||
"""
|
"""
|
||||||
A test suite to test out various methods around the AppLocation class.
|
A test suite to test out various methods around the AppLocation class.
|
||||||
|
@ -15,10 +20,10 @@ class TestAppLocation(TestCase):
|
||||||
"""
|
"""
|
||||||
Test the AppLocation.get_data_path() method
|
Test the AppLocation.get_data_path() method
|
||||||
"""
|
"""
|
||||||
with patch(u'openlp.core.utils.Settings') as mocked_class, \
|
with patch(u'openlp.core.utils.applocation.Settings') as mocked_class, \
|
||||||
patch(u'openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \
|
patch(u'openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \
|
||||||
patch(u'openlp.core.utils.check_directory_exists') as mocked_check_directory_exists, \
|
patch(u'openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \
|
||||||
patch(u'openlp.core.utils.os') as mocked_os:
|
patch(u'openlp.core.utils.applocation.os') as mocked_os:
|
||||||
# GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory()
|
# GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory()
|
||||||
mocked_settings = mocked_class.return_value
|
mocked_settings = mocked_class.return_value
|
||||||
mocked_settings.contains.return_value = False
|
mocked_settings.contains.return_value = False
|
||||||
|
@ -37,8 +42,8 @@ class TestAppLocation(TestCase):
|
||||||
"""
|
"""
|
||||||
Test the AppLocation.get_data_path() method when a custom location is set in the settings
|
Test the AppLocation.get_data_path() method when a custom location is set in the settings
|
||||||
"""
|
"""
|
||||||
with patch(u'openlp.core.utils.Settings') as mocked_class,\
|
with patch(u'openlp.core.utils.applocation.Settings') as mocked_class,\
|
||||||
patch(u'openlp.core.utils.os') as mocked_os:
|
patch(u'openlp.core.utils.applocation.os') as mocked_os:
|
||||||
# GIVEN: A mocked out Settings class which returns a custom data location
|
# GIVEN: A mocked out Settings class which returns a custom data location
|
||||||
mocked_settings = mocked_class.return_value
|
mocked_settings = mocked_class.return_value
|
||||||
mocked_settings.contains.return_value = True
|
mocked_settings.contains.return_value = True
|
||||||
|
@ -51,12 +56,47 @@ class TestAppLocation(TestCase):
|
||||||
mocked_settings.value.assert_called_with(u'advanced/data path')
|
mocked_settings.value.assert_called_with(u'advanced/data path')
|
||||||
assert data_path == u'custom/dir', u'Result should be "custom/dir"'
|
assert data_path == u'custom/dir', u'Result should be "custom/dir"'
|
||||||
|
|
||||||
|
def get_files_no_section_no_extension_test(self):
|
||||||
|
"""
|
||||||
|
Test the AppLocation.get_files() method with no parameters passed.
|
||||||
|
"""
|
||||||
|
with patch(u'openlp.core.utils.AppLocation.get_data_path') as mocked_get_data_path, \
|
||||||
|
patch(u'openlp.core.utils.applocation.os.listdir') as mocked_listdir:
|
||||||
|
# GIVEN: Our mocked modules/methods.
|
||||||
|
mocked_get_data_path.return_value = u'test/dir'
|
||||||
|
mocked_listdir.return_value = copy.deepcopy(FILE_LIST)
|
||||||
|
|
||||||
|
# When: Get the list of files.
|
||||||
|
result = AppLocation.get_files()
|
||||||
|
|
||||||
|
# Then: check if the file lists are identical.
|
||||||
|
assert result == FILE_LIST, u'The file lists should be identical.'
|
||||||
|
|
||||||
|
def get_files_test(self):
|
||||||
|
"""
|
||||||
|
Test the AppLocation.get_files() method with all parameters passed.
|
||||||
|
"""
|
||||||
|
with patch(u'openlp.core.utils.AppLocation.get_data_path') as mocked_get_data_path, \
|
||||||
|
patch(u'openlp.core.utils.applocation.os.listdir') as mocked_listdir:
|
||||||
|
# GIVEN: Our mocked modules/methods.
|
||||||
|
mocked_get_data_path.return_value = u'test/dir'
|
||||||
|
mocked_listdir.return_value = copy.deepcopy(FILE_LIST)
|
||||||
|
|
||||||
|
# When: Get the list of files.
|
||||||
|
result = AppLocation.get_files(u'section', u'.mp3')
|
||||||
|
|
||||||
|
# Then: Check if the section parameter was used correctly.
|
||||||
|
mocked_listdir.assert_called_with(u'test/dir/section')
|
||||||
|
|
||||||
|
# Then: check if the file lists are identical.
|
||||||
|
assert result == [u'file5.mp3', u'file6.mp3'], u'The file lists should be identical.'
|
||||||
|
|
||||||
def get_section_data_path_test(self):
|
def get_section_data_path_test(self):
|
||||||
"""
|
"""
|
||||||
Test the AppLocation.get_section_data_path() method
|
Test the AppLocation.get_section_data_path() method
|
||||||
"""
|
"""
|
||||||
with patch(u'openlp.core.utils.AppLocation.get_data_path') as mocked_get_data_path, \
|
with patch(u'openlp.core.utils.AppLocation.get_data_path') as mocked_get_data_path, \
|
||||||
patch(u'openlp.core.utils.check_directory_exists') as mocked_check_directory_exists:
|
patch(u'openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists:
|
||||||
# GIVEN: A mocked out AppLocation.get_data_path()
|
# GIVEN: A mocked out AppLocation.get_data_path()
|
||||||
mocked_get_data_path.return_value = u'test/dir'
|
mocked_get_data_path.return_value = u'test/dir'
|
||||||
mocked_check_directory_exists.return_value = True
|
mocked_check_directory_exists.return_value = True
|
||||||
|
@ -70,7 +110,7 @@ class TestAppLocation(TestCase):
|
||||||
"""
|
"""
|
||||||
Test the AppLocation.get_directory() method for AppLocation.AppDir
|
Test the AppLocation.get_directory() method for AppLocation.AppDir
|
||||||
"""
|
"""
|
||||||
with patch(u'openlp.core.utils._get_frozen_path') as mocked_get_frozen_path:
|
with patch(u'openlp.core.utils.applocation._get_frozen_path') as mocked_get_frozen_path:
|
||||||
mocked_get_frozen_path.return_value = u'app/dir'
|
mocked_get_frozen_path.return_value = u'app/dir'
|
||||||
# WHEN: We call AppLocation.get_directory
|
# WHEN: We call AppLocation.get_directory
|
||||||
directory = AppLocation.get_directory(AppLocation.AppDir)
|
directory = AppLocation.get_directory(AppLocation.AppDir)
|
||||||
|
@ -81,10 +121,10 @@ class TestAppLocation(TestCase):
|
||||||
"""
|
"""
|
||||||
Test the AppLocation.get_directory() method for AppLocation.PluginsDir
|
Test the AppLocation.get_directory() method for AppLocation.PluginsDir
|
||||||
"""
|
"""
|
||||||
with patch(u'openlp.core.utils._get_frozen_path') as mocked_get_frozen_path, \
|
with patch(u'openlp.core.utils.applocation._get_frozen_path') as mocked_get_frozen_path, \
|
||||||
patch(u'openlp.core.utils.os.path.abspath') as mocked_abspath, \
|
patch(u'openlp.core.utils.applocation.os.path.abspath') as mocked_abspath, \
|
||||||
patch(u'openlp.core.utils.os.path.split') as mocked_split, \
|
patch(u'openlp.core.utils.applocation.os.path.split') as mocked_split, \
|
||||||
patch(u'openlp.core.utils.sys') as mocked_sys:
|
patch(u'openlp.core.utils.applocation.sys') as mocked_sys:
|
||||||
mocked_abspath.return_value = u'plugins/dir'
|
mocked_abspath.return_value = u'plugins/dir'
|
||||||
mocked_split.return_value = [u'openlp']
|
mocked_split.return_value = [u'openlp']
|
||||||
mocked_get_frozen_path.return_value = u'plugins/dir'
|
mocked_get_frozen_path.return_value = u'plugins/dir'
|
||||||
|
|
Loading…
Reference in New Issue