diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py index 28097e938..49e2c8151 100644 --- a/openlp/core/ui/exceptiondialog.py +++ b/openlp/core/ui/exceptiondialog.py @@ -73,7 +73,7 @@ class Ui_ExceptionDialog(object): def retranslateUi(self, exceptionDialog): exceptionDialog.setWindowTitle( - translate('OpenLP.ExceptionDialog', 'Error Occured')) + translate('OpenLP.ExceptionDialog', 'Error Occurred')) self.messageLabel.setText(translate('OpenLP.ExceptionDialog', 'Oops! ' 'OpenLP hit a problem, and couldn\'t recover. The text in the box ' 'below contains information that might be helpful to the OpenLP ' diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index abf035d65..60a6ae2b7 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -250,7 +250,7 @@ class Ui_MainWindow(object): self.LanguageGroup = QtGui.QActionGroup(MainWindow) qmList = LanguageManager.get_qm_list() savedLanguage = LanguageManager.get_language() - self.AutoLanguageItem.setChecked(LanguageManager.AutoLanguage) + self.AutoLanguageItem.setChecked(LanguageManager.auto_language) for key in sorted(qmList.keys()): languageItem = QtGui.QAction(MainWindow) languageItem.setObjectName(key) @@ -258,7 +258,7 @@ class Ui_MainWindow(object): if qmList[key] == savedLanguage: languageItem.setChecked(True) add_actions(self.LanguageGroup, [languageItem]) - self.LanguageGroup.setDisabled(LanguageManager.AutoLanguage) + self.LanguageGroup.setDisabled(LanguageManager.auto_language) self.ToolsAddToolItem = QtGui.QAction(MainWindow) self.ToolsAddToolItem.setIcon(build_icon(u':/tools/tools_add.png')) self.ToolsAddToolItem.setObjectName(u'ToolsAddToolItem') @@ -640,7 +640,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): def setAutoLanguage(self, value): self.LanguageGroup.setDisabled(value) - LanguageManager.AutoLanguage = value + LanguageManager.auto_language = value LanguageManager.set_language(self.LanguageGroup.checkedAction()) def versionNotice(self, version): diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index ea14b2259..119bf6b55 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -102,6 +102,7 @@ class AppLocation(object): PluginsDir = 4 VersionDir = 5 CacheDir = 6 + LanguageDir = 7 @staticmethod def get_directory(dir_type=1): @@ -173,6 +174,13 @@ class AppLocation(object): except ImportError: path = os.path.join(os.getenv(u'HOME'), u'.openlp') return path + if dir_type == AppLocation.LanguageDir: + if hasattr(sys, u'frozen') and sys.frozen == 1: + app_path = os.path.abspath(os.path.split(sys.argv[0])[0]) + else: + app_path = os.path.split(openlp.__file__)[0] + return os.path.join(app_path, u'i18n') + @staticmethod def get_data_path(): diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index a264e15a9..36e6330a6 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -35,14 +35,14 @@ from PyQt4 import QtCore, QtGui from openlp.core.utils import AppLocation from openlp.core.lib import translate -log = logging.getLogger() +log = logging.getLogger(__name__) class LanguageManager(object): """ Helper for Language selection """ - __qmList__ = None - AutoLanguage = False + __qm_list__ = {} + auto_language = False @staticmethod def get_translator(language): @@ -52,12 +52,11 @@ class LanguageManager(object): ``language`` The language to load into the translator """ - if LanguageManager.AutoLanguage: + if LanguageManager.auto_language: language = QtCore.QLocale.system().name() - lang_path = AppLocation.get_directory(AppLocation.AppDir) - lang_path = os.path.join(lang_path, u'i18n') + lang_path = AppLocation.get_directory(AppLocation.LanguageDir) app_translator = QtCore.QTranslator() - if app_translator.load("openlp_" + language, lang_path): + if app_translator.load(language, lang_path): return app_translator @staticmethod @@ -65,8 +64,8 @@ class LanguageManager(object): """ Find all available language files in this OpenLP install """ - trans_dir = AppLocation.get_directory(AppLocation.AppDir) - trans_dir = QtCore.QDir(os.path.join(trans_dir, u'i18n')) + trans_dir = QtCore.QDir(AppLocation.get_directory( + AppLocation.LanguageDir)) file_names = trans_dir.entryList(QtCore.QStringList("*.qm"), QtCore.QDir.Files, QtCore.QDir.Name) for name in file_names: @@ -96,7 +95,7 @@ class LanguageManager(object): log.info(u'Language file: \'%s\' Loaded from conf file' % language) reg_ex = QtCore.QRegExp("^\[(.*)\]") if reg_ex.exactMatch(language): - LanguageManager.AutoLanguage = True + LanguageManager.auto_language = True language = reg_ex.cap(1) return language @@ -110,7 +109,7 @@ class LanguageManager(object): """ action_name = u'%s' % action.objectName() qm_list = LanguageManager.get_qm_list() - if LanguageManager.AutoLanguage: + if LanguageManager.auto_language: language = u'[%s]' % qm_list[action_name] else: language = u'%s' % qm_list[action_name] @@ -127,20 +126,18 @@ class LanguageManager(object): """ Initialise the list of available translations """ - LanguageManager.__qmList__ = {} + LanguageManager.__qm_list__ = {} qm_files = LanguageManager.find_qm_files() - for i, qmf in enumerate(qm_files): - reg_ex = QtCore.QRegExp("^.*openlp_(.*).qm") - if reg_ex.exactMatch(qmf): - lang_name = reg_ex.cap(1) - LanguageManager.__qmList__[u'%#2i %s' % (i+1, - LanguageManager.language_name(qmf))] = lang_name + for counter, qmf in enumerate(qm_files): + name = unicode(qmf).split(u'.')[0] + LanguageManager.__qm_list__[u'%#2i %s' % (counter + 1, + LanguageManager.language_name(qmf))] = name @staticmethod def get_qm_list(): """ Return the list of available translations """ - if LanguageManager.__qmList__ is None: + if not LanguageManager.__qm_list__: LanguageManager.init_qm_list() - return LanguageManager.__qmList__ + return LanguageManager.__qm_list__ diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 458e7200c..ae9938ab4 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -621,6 +621,10 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.close() def saveSong(self): + """ + Get all the data from the widgets on the form, and then save it to the + database. + """ self.song.title = unicode(self.TitleEditItem.text()) self.song.alternate_title = unicode(self.AlternativeEdit.text()) self.song.copyright = unicode(self.CopyrightEditItem.text()) @@ -646,6 +650,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.song.topics.append(self.songmanager.get_object(Topic, topicId)) self.songmanager.save_object(self.song) + self.song = None return True return False diff --git a/openlp/plugins/songs/forms/songimportwizard.py b/openlp/plugins/songs/forms/songimportwizard.py index 9ae6d7348..37ee10612 100644 --- a/openlp/plugins/songs/forms/songimportwizard.py +++ b/openlp/plugins/songs/forms/songimportwizard.py @@ -519,7 +519,7 @@ class Ui_SongImportWizard(object): self.openLyricsDisabledLabel.setText( translate('SongsPlugin.ImportWizardForm', 'The OpenLyrics ' 'importer has not yet been developed, but as you can see, we are ' - 'still intendeding to do so. Hopefully it will be in the next ' + 'still intending to do so. Hopefully it will be in the next ' 'release.')) self.openSongAddButton.setText( translate('SongsPlugin.ImportWizardForm', 'Add Files...')) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index a2c12a6d6..48f9a5a55 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -324,8 +324,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Could not save your modified author, because he ' - 'already exists.')) + 'Could not save your modified author, because the ' + 'author already exists.')) def onTopicEditButtonClick(self): topic_id = self._getCurrentItemId(self.TopicsListWidget) diff --git a/openlp/plugins/songs/lib/olp1import.py b/openlp/plugins/songs/lib/olp1import.py index 692c6792e..de750cb24 100644 --- a/openlp/plugins/songs/lib/olp1import.py +++ b/openlp/plugins/songs/lib/olp1import.py @@ -41,6 +41,8 @@ class OpenLP1SongImport(SongImport): The :class:`OpenLP1SongImport` class provides OpenLP with the ability to import song databases from installations of openlp.org 1.x. """ + last_encoding = u'windows-1252' + def __init__(self, manager, **kwargs): """ Initialise the import. @@ -54,20 +56,33 @@ class OpenLP1SongImport(SongImport): SongImport.__init__(self, manager) self.import_source = kwargs[u'filename'] - def decode_string(self, raw): + def decode_string(self, raw, guess): """ Use chardet to detect the encoding of the raw string, and convert it to unicode. ``raw`` The raw bytestring to decode. + ``guess`` + What chardet guessed the encoding to be. """ - detection = chardet.detect(raw) - if detection[u'confidence'] < 0.8: + if guess[u'confidence'] < 0.8: codec = u'windows-1252' else: - codec = detection[u'encoding'] - return unicode(raw, codec) + codec = guess[u'encoding'] + try: + decoded = unicode(raw, codec) + self.last_encoding = codec + except UnicodeDecodeError: + log.exception(u'Error in detecting openlp.org 1.x database encoding.') + try: + decoded = unicode(raw, self.last_encoding) + except UnicodeDecodeError: + # possibly show an error form + #self.import_wizard.showError(u'There was a problem ' + # u'detecting the encoding of a string') + decoded = raw + return decoded def do_import(self): """ @@ -103,9 +118,10 @@ class OpenLP1SongImport(SongImport): success = False break song_id = song[0] - title = self.decode_string(song[1]) - lyrics = self.decode_string(song[2]).replace(u'\r', u'') - copyright = self.decode_string(song[3]) + guess = chardet.detect(song[2]) + title = self.decode_string(song[1], guess) + lyrics = self.decode_string(song[2], guess).replace(u'\r', u'') + copyright = self.decode_string(song[3], guess) self.import_wizard.incrementProgressBar( unicode(translate('SongsPlugin.ImportWizardForm', 'Importing "%s"...')) % title) @@ -121,7 +137,7 @@ class OpenLP1SongImport(SongImport): break for author in authors: if author[0] == author_id[0]: - self.parse_author(self.decode_string(author[1])) + self.parse_author(self.decode_string(author[1], guess)) break if self.stop_import_flag: success = False @@ -136,7 +152,7 @@ class OpenLP1SongImport(SongImport): break for track in tracks: if track[0] == track_id[0]: - self.add_media_file(self.decode_string(track[1])) + self.add_media_file(self.decode_string(track[1], guess)) break if self.stop_import_flag: success = False diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index 86ccb5933..f4be0dc87 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -29,6 +29,7 @@ import os from zipfile import ZipFile from lxml import objectify from lxml.etree import Error, LxmlError +import re from openlp.core.lib import translate from openlp.plugins.songs.lib.songimport import SongImport @@ -113,12 +114,22 @@ class OpenSongImport(SongImport): def do_import(self): """ - Import either a single opensong file, or a zipfile containing multiple - opensong files. If `self.commit` is set False, the import will not be - committed to the database (useful for test scripts). + Import either each of the files in self.filenames - each element of + which can be either a single opensong file, or a zipfile containing + multiple opensong files. If `self.commit` is set False, the + import will not be committed to the database (useful for test scripts). """ success = True - self.import_wizard.importProgressBar.setMaximum(len(self.filenames)) + numfiles = 0 + for filename in self.filenames: + ext = os.path.splitext(filename)[1] + if ext.lower() == u'.zip': + z = ZipFile(filename, u'r') + numfiles += len(z.infolist()) + else: + numfiles += 1 + log.debug("Total number of files: %d", numfiles) + self.import_wizard.importProgressBar.setMaximum(numfiles) for filename in self.filenames: if self.stop_import_flag: success = False @@ -127,9 +138,6 @@ class OpenSongImport(SongImport): if ext.lower() == u'.zip': log.debug(u'Zipfile found %s', filename) z = ZipFile(filename, u'r') - self.import_wizard.importProgressBar.setMaximum( - self.import_wizard.importProgressBar.maximum() + - len(z.infolist())) for song in z.infolist(): if self.stop_import_flag: success = False @@ -138,6 +146,7 @@ class OpenSongImport(SongImport): if parts[-1] == u'': #No final part => directory continue + log.info(u'Zip importing %s', parts[-1]) self.import_wizard.incrementProgressBar( unicode(translate('SongsPlugin.ImportWizardForm', 'Importing %s...')) % parts[-1]) @@ -145,11 +154,11 @@ class OpenSongImport(SongImport): self.do_import_file(songfile) if self.commit: self.finish() - self.set_defaults() if self.stop_import_flag: success = False break else: + # not a zipfile log.info('Direct import %s', filename) self.import_wizard.incrementProgressBar( unicode(translate('SongsPlugin.ImportWizardForm', @@ -158,9 +167,7 @@ class OpenSongImport(SongImport): self.do_import_file(file) if self.commit: self.finish() - self.set_defaults() - if not self.commit: - self.finish() + return success def do_import_file(self, file): @@ -168,7 +175,7 @@ class OpenSongImport(SongImport): Process the OpenSong file - pass in a file-like object, not a filename """ - self.authors = [] + self.set_defaults() try: tree = objectify.parse(file) except (Error, LxmlError): @@ -197,7 +204,6 @@ class OpenSongImport(SongImport): self.topics.append(unicode(root.alttheme)) # data storage while importing verses = {} - lyrics = unicode(root.lyrics) # keep track of a "default" verse order, in case none is specified our_verse_order = [] verses_seen = {} @@ -205,6 +211,7 @@ class OpenSongImport(SongImport): # erm, versetype! versetype = u'V' versenum = None + lyrics = unicode(root.lyrics) for thisline in lyrics.split(u'\n'): # remove comments semicolon = thisline.find(u';') @@ -219,16 +226,18 @@ class OpenSongImport(SongImport): continue # verse/chorus/etc. marker if thisline[0] == u'[': - versetype = thisline[1].upper() - if versetype.isdigit(): - versenum = versetype - versetype = u'V' - elif thisline[2] != u']': - # there's a number to go with it - extract that as well - right_bracket = thisline.find(u']') - versenum = thisline[2:right_bracket] + # drop the square brackets + right_bracket = thisline.find(u']') + content = thisline[1:right_bracket].upper() + # have we got any digits? If so, versenumber is everything from the digits + # to the end (even if there are some alpha chars on the end) + match = re.match(u'(.*)(\d+.*)', content) + if match is not None: + versetype = match.group(1) + versenum = match.group(2) else: - # if there's no number, assume it's no.1 + # otherwise we assume number 1 and take the whole prefix as versetype + versetype = content versenum = u'1' continue words = None @@ -236,10 +245,10 @@ class OpenSongImport(SongImport): if thisline[0].isdigit(): versenum = thisline[0] words = thisline[1:].strip() - if words is None and \ - versenum is not None and \ - versetype is not None: + if words is None: words = thisline + if not versenum: + versenum = u'1' if versenum is not None: versetag = u'%s%s' % (versetype, versenum) if not verses.has_key(versetype): @@ -260,10 +269,13 @@ class OpenSongImport(SongImport): versetypes.sort() versetags = {} for versetype in versetypes: + our_verse_type = versetype + if our_verse_type == u'': + our_verse_type = u'V' versenums = verses[versetype].keys() versenums.sort() for num in versenums: - versetag = u'%s%s' % (versetype, num) + versetag = u'%s%s' % (our_verse_type, num) lines = u'\n'.join(verses[versetype][num]) self.verses.append([versetag, lines]) # Keep track of what we have for error checking later @@ -272,7 +284,9 @@ class OpenSongImport(SongImport): order = [] if u'presentation' in fields and root.presentation != u'': order = unicode(root.presentation) - order = order.split() + # We make all the tags in the lyrics upper case, so match that here + # and then split into a list on the whitespace + order = order.upper().split() else: if len(our_verse_order) > 0: order = our_verse_order @@ -280,9 +294,13 @@ class OpenSongImport(SongImport): log.warn(u'No verse order available for %s, skipping.', self.title) for tag in order: - if len(tag) == 1: - tag = tag + u'1' # Assume it's no.1 if it's not there + if tag[0].isdigit(): + # Assume it's a verse if it has no prefix + tag = u'V' + tag + elif not re.search('\d+', tag): + # Assume it's no.1 if there's no digits + tag = tag + u'1' if not versetags.has_key(tag): - log.warn(u'Got order %s but not in versetags, skipping', tag) + log.info(u'Got order %s but not in versetags, dropping this item from presentation order', tag) else: self.verse_order_list.append(tag) diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 30c1865a7..dcf4ed8d8 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -55,8 +55,12 @@ class SongImport(QtCore.QObject): self.set_defaults() QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'songs_stop_import'), self.stop_import) - def set_defaults(self): + """ + Create defaults for properties - call this before each song + if importing many songs at once to ensure a clean beginning + """ + self.authors = [] self.title = u'' self.song_number = u'' self.alternate_title = u'' @@ -255,13 +259,16 @@ class SongImport(QtCore.QObject): """ Write the song and its fields to disk """ + log.info(u'commiting song %s to database', self.title) song = Song() song.title = self.title song.search_title = self.remove_punctuation(self.title) \ + '@' + self.alternate_title song.song_number = self.song_number song.search_lyrics = u'' + verses_changed_to_other = {} sxml = SongXMLBuilder() + other_count = 1 for (versetag, versetext) in self.verses: if versetag[0] == u'C': versetype = VerseType.to_string(VerseType.Chorus) @@ -276,10 +283,18 @@ class SongImport(QtCore.QObject): elif versetag[0] == u'E': versetype = VerseType.to_string(VerseType.Ending) else: + newversetag = u'O%d' % other_count + verses_changed_to_other[versetag] = newversetag + other_count += 1 versetype = VerseType.to_string(VerseType.Other) + log.info(u'Versetype %s changing to %s' , versetag, newversetag) + versetag = newversetag sxml.add_verse_to_lyrics(versetype, versetag[1:], versetext) song.search_lyrics += u' ' + self.remove_punctuation(versetext) song.lyrics = unicode(sxml.extract_xml(), u'utf-8') + for i, current_verse_tag in enumerate(self.verse_order_list): + if verses_changed_to_other.has_key(current_verse_tag): + self.verse_order_list[i] = verses_changed_to_other[current_verse_tag] song.verse_order = u' '.join(self.verse_order_list) song.copyright = self.copyright song.comments = self.comments diff --git a/openlp/plugins/songs/lib/test/test.opensong b/openlp/plugins/songs/lib/test/test.opensong index 1ce60cf3b..c75951492 100644 --- a/openlp/plugins/songs/lib/test/test.opensong +++ b/openlp/plugins/songs/lib/test/test.opensong @@ -1,10 +1,10 @@ Martins Test - MartiÑ Thómpson + MartiÑ & Martin2 Thómpson 2010 Martin Thompson 1 - V1 C V2 C2 V3 B1 V1 + V1 C V2 C2 3a B1 V1 T U Rap1 Rap2 Rap3 Blah @@ -17,7 +17,12 @@ TestAltTheme - ;Comment + [3a] +. G A B + V3 Line 1 +. G A B + V3 Line 2 + . A B C 1 v1 Line 1___ 2 v2 Line 1___ @@ -25,10 +30,6 @@ 1 V1 Line 2 2 V2 Line 2 -[3] - V3 Line 1 - V3 Line 2 - [b1] Bridge 1 --- @@ -36,12 +37,29 @@ Bridge 1 line 2 [C] -. A B + . A B Chorus 1 [C2] . A B Chorus 2 + +[T] + T Line 1 + +[Rap] +1 Rap 1 Line 1 +2 Rap 2 Line 1 +1 Rap 1 Line 2 +2 Rap 2 Line 2 + +[rap3] + Rap 3 Line 1 + Rap 3 Line 2 + + +[X] + Unreferenced verse line 1