diff --git a/documentation/manual.txt b/documentation/manual.txt new file mode 100644 index 000000000..55a3f7d98 --- /dev/null +++ b/documentation/manual.txt @@ -0,0 +1,7 @@ +OpenLP Manual +============= + +If you're reading this file, you're probably looking for the OpenLP manual. The +manual is hosted online at http://manual.openlp.org/. If you want to help with +the manual, contact the OpenLP team via IRC in the #openlp.org channel on the +Freenode network. diff --git a/openlp.pyw b/openlp.pyw index f796fa45f..0bd7c940d 100755 --- a/openlp.pyw +++ b/openlp.pyw @@ -133,6 +133,8 @@ class OpenLP(QtGui.QApplication): u'general/update check', QtCore.QVariant(True)).toBool() if update_check: VersionThread(self.mainWindow).start() + Receiver.send_message(u'maindisplay_blank_check') + self.mainWindow.appStartup() DelayStartThread(self.mainWindow).start() return self.exec_() diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 5b03e5fa4..3b7f633d9 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -32,8 +32,9 @@ import os from PyQt4 import QtCore from sqlalchemy import create_engine, MetaData -from sqlalchemy.exceptions import InvalidRequestError +from sqlalchemy.exc import InvalidRequestError from sqlalchemy.orm import scoped_session, sessionmaker +from sqlalchemy.pool import NullPool from openlp.core.utils import AppLocation, delete_file @@ -52,7 +53,7 @@ def init_db(url, auto_flush=True, auto_commit=False): ``auto_commit`` Sets the commit behaviour of the session """ - engine = create_engine(url) + engine = create_engine(url, poolclass=NullPool) metadata = MetaData(bind=engine) session = scoped_session(sessionmaker(autoflush=auto_flush, autocommit=auto_commit, bind=engine)) diff --git a/openlp/core/lib/dockwidget.py b/openlp/core/lib/dockwidget.py index 5c461bbf8..182b98c48 100644 --- a/openlp/core/lib/dockwidget.py +++ b/openlp/core/lib/dockwidget.py @@ -47,7 +47,6 @@ class OpenLPDockWidget(QtGui.QDockWidget): """ log.debug(u'Initialise the %s widget' % name) QtGui.QDockWidget.__init__(self, parent) - self.parent = parent if name: self.setObjectName(name) if icon: diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 9b76447ff..e6083240e 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -90,11 +90,10 @@ class MediaManagerItem(QtGui.QWidget): """ Constructor to create the media manager item. """ - QtGui.QWidget.__init__(self) - self.parent = parent - self.whitespace = re.compile(r'\W+', re.UNICODE) - #TODO: plugin should not be the parent in future - self.plugin = parent # plugin + QtGui.QWidget.__init__(self, parent) + self.hide() + self.whitespace = re.compile(r'[\W_]+', re.UNICODE) + self.plugin = plugin visible_title = self.plugin.getString(StringContent.VisibleName) self.title = unicode(visible_title[u'title']) self.settingsSection = self.plugin.name.lower() @@ -112,13 +111,10 @@ class MediaManagerItem(QtGui.QWidget): self.requiredIcons() self.setupUi() self.retranslateUi() - self.autoSelectItem = None + self.auto_select_id = -1 QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'%s_service_load' % self.parent.name.lower()), + QtCore.SIGNAL(u'%s_service_load' % self.plugin.name.lower()), self.serviceLoad) - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'%s_set_autoselect_item' % self.parent.name.lower()), - self.setAutoSelectItem) def requiredIcons(self): """ @@ -458,7 +454,8 @@ class MediaManagerItem(QtGui.QWidget): """ if QtCore.QSettings().value(u'advanced/single click preview', QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed \ - and self.listView.selectedIndexes(): + and self.listView.selectedIndexes() \ + and self.auto_select_id == -1: self.onPreviewClick(True) def onPreviewClick(self, keepFocus=False): @@ -475,13 +472,10 @@ class MediaManagerItem(QtGui.QWidget): serviceItem = self.buildServiceItem() if serviceItem: serviceItem.from_plugin = True - self.parent.previewController.addServiceItem(serviceItem) + self.plugin.previewController.addServiceItem(serviceItem) if keepFocus: self.listView.setFocus() - def setAutoSelectItem(self, itemToSelect=None): - self.autoSelectItem = itemToSelect - def onLiveClick(self): """ Send an item live by building a service item then adding that service @@ -503,7 +497,7 @@ class MediaManagerItem(QtGui.QWidget): if serviceItem: if not item_id: serviceItem.from_plugin = True - self.parent.liveController.addServiceItem(serviceItem) + self.plugin.liveController.addServiceItem(serviceItem) def createItemFromId(self, item_id): item = QtGui.QListWidgetItem() @@ -533,7 +527,7 @@ class MediaManagerItem(QtGui.QWidget): serviceItem = self.buildServiceItem(item, True) if serviceItem: serviceItem.from_plugin = False - self.parent.serviceManager.addServiceItem(serviceItem, + self.plugin.serviceManager.addServiceItem(serviceItem, replace=replace) def onAddEditClick(self): @@ -546,14 +540,14 @@ class MediaManagerItem(QtGui.QWidget): 'You must select one or more items.')) else: log.debug(u'%s Add requested', self.plugin.name) - serviceItem = self.parent.serviceManager.getServiceItem() + serviceItem = self.plugin.serviceManager.getServiceItem() if not serviceItem: QtGui.QMessageBox.information(self, UiStrings().NISs, translate('OpenLP.MediaManagerItem', 'You must select an existing service item to add to.')) elif self.plugin.name.lower() == serviceItem.name.lower(): self.generateSlideData(serviceItem) - self.parent.serviceManager.addServiceItem(serviceItem, + self.plugin.serviceManager.addServiceItem(serviceItem, replace=True) else: # Turn off the remote edit update message indicator @@ -567,8 +561,8 @@ class MediaManagerItem(QtGui.QWidget): """ Common method for generating a service item """ - serviceItem = ServiceItem(self.parent) - serviceItem.add_icon(self.parent.icon_path) + serviceItem = ServiceItem(self.plugin) + serviceItem.add_icon(self.plugin.icon_path) if self.generateSlideData(serviceItem, item, xmlVersion): return serviceItem else: @@ -617,6 +611,16 @@ class MediaManagerItem(QtGui.QWidget): item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] return item_id + def save_auto_select_id(self): + """ + Sorts out, what item to select after loading a list. + """ + # The item to select has not been set. + if self.auto_select_id == -1: + item = self.listView.currentItem() + if item: + self.auto_select_id = item.data(QtCore.Qt.UserRole).toInt()[0] + def search(self, string): """ Performs a plugin specific search for items containing ``string`` diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 0417446c1..910c52d2c 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -215,7 +215,8 @@ class Plugin(QtCore.QObject): you need, and return it for integration into openlp.org. """ if self.media_item_class: - return self.media_item_class(self, self, self.icon) + return self.media_item_class(self.mediadock.media_dock, self, + self.icon) return None def addImportMenuItem(self, importMenu): @@ -299,6 +300,12 @@ class Plugin(QtCore.QObject): if self.mediaItem: self.mediadock.remove_dock(self.mediaItem) + def appStartup(self): + """ + Perform tasks on application starup + """ + pass + def usesTheme(self, theme): """ Called to find out if a plugin is currently using a theme. diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 079a9aa9c..c6e729be8 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -67,7 +67,7 @@ class Renderer(object): ``theme_manager`` The ThemeManager instance, used to get the current theme details. """ - log.debug(u'Initilisation started') + log.debug(u'Initialisation started') self.theme_manager = theme_manager self.image_manager = image_manager self.screens = ScreenList.get_instance() @@ -77,7 +77,7 @@ class Renderer(object): self.theme_data = None self.bg_frame = None self.force_page = False - self.display = MainDisplay(self, self.image_manager, False) + self.display = MainDisplay(None, self.image_manager, False) self.display.setup() def update_display(self): @@ -86,7 +86,7 @@ class Renderer(object): """ log.debug(u'Update Display') self._calculate_default(self.screens.current[u'size']) - self.display = MainDisplay(self, self.image_manager, False) + self.display = MainDisplay(None, self.image_manager, False) self.display.setup() self.bg_frame = None self.theme_data = None diff --git a/openlp/core/ui/displaytagform.py b/openlp/core/ui/displaytagform.py index 6aaddf2b8..4b25f851a 100644 --- a/openlp/core/ui/displaytagform.py +++ b/openlp/core/ui/displaytagform.py @@ -147,6 +147,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog): DisplayTags.remove_html_tag(self.selected) self.selected = -1 self._resetTable() + self._saveTable() def onSavedPushed(self): """ @@ -171,14 +172,19 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog): html[u'end tag'] = u'{/%s}' % tag self.selected = -1 self._resetTable() - temp = [] + self._saveTable() + + def _saveTable(self): + """ + Saves all display tags except protected ones. + """ + tags = [] for tag in DisplayTags.get_html_tags(): if not tag[u'protected']: - temp.append(tag) - if temp: - ctemp = cPickle.dumps(temp) + tags.append(tag) + if tags: QtCore.QSettings().setValue(u'displayTags/html_tags', - QtCore.QVariant(ctemp)) + QtCore.QVariant(cPickle.dumps(tags))) else: QtCore.QSettings().setValue(u'displayTags/html_tags', QtCore.QVariant(u'')) diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 69f3c14af..f240561ff 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -106,7 +106,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog): """ Saving exception log and system informations to a file. """ - report = unicode(translate('OpenLP.ExceptionForm', + report_text = unicode(translate('OpenLP.ExceptionForm', '**OpenLP Bug Report**\n' 'Version: %s\n\n' '--- Details of the Exception. ---\n\n%s\n\n ' @@ -122,21 +122,21 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog): filename = unicode(QtCore.QDir.toNativeSeparators(filename)) SettingsManager.set_last_dir(self.settingsSection, os.path.dirname( filename)) - report = report % self._createReport() + report_text = report_text % self._createReport() try: - file = open(filename, u'w') + report_file = open(filename, u'w') try: - file.write(report) + report_file.write(report_text) except UnicodeError: - file.close() - file = open(filename, u'wb') - file.write(report.encode(u'utf-8')) + report_file.close() + report_file = open(filename, u'wb') + report_file.write(report_text.encode(u'utf-8')) finally: - file.close() + report_file.close() except IOError: log.exception(u'Failed to write crash report') finally: - file.close() + report_file.close() def onSendReportButtonPressed(self): """ diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index 2435b8dcc..2a9bd221e 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -223,9 +223,9 @@ class GeneralTab(SettingsTab): self.autoPreviewCheckBox.setText(translate('OpenLP.GeneralTab', 'Automatically preview next item in service')) self.enableLoopCheckBox.setText(translate('OpenLP.GeneralTab', - 'Enable slide loop')) + 'Enable slide wrap-around')) self.timeoutLabel.setText(translate('OpenLP.GeneralTab', - 'Slide loop delay:')) + 'Timed slide interval:')) self.timeoutSpinBox.setSuffix(translate('OpenLP.GeneralTab', ' sec')) self.ccliGroupBox.setTitle( translate('OpenLP.GeneralTab', 'CCLI Details')) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index dc7c28ded..e2117cc10 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -49,8 +49,7 @@ class MainDisplay(QtGui.QGraphicsView): This is the display screen. """ def __init__(self, parent, image_manager, live): - QtGui.QGraphicsView.__init__(self) - self.parent = parent + QtGui.QGraphicsView.__init__(self, parent) self.isLive = live self.image_manager = image_manager self.screens = ScreenList.get_instance() @@ -149,7 +148,6 @@ class MainDisplay(QtGui.QGraphicsView): self.__hideMouse() # To display or not to display? if not self.screen[u'primary']: - self.show() self.primary = False else: self.primary = True diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 82f1b9253..b4a3b3640 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -179,7 +179,7 @@ class Ui_MainWindow(object): u'printServiceItem', [QtGui.QKeySequence(u'Ctrl+P')], self.serviceManagerContents.printServiceOrder, category=UiStrings().File) - self.fileExitItem = shortcut_action(mainWindow, u'FileExitItem', + self.fileExitItem = shortcut_action(mainWindow, u'fileExitItem', [QtGui.QKeySequence(u'Alt+F4')], mainWindow.close, u':/system/system_exit.png', category=UiStrings().File) action_list.add_category(UiStrings().Import, CategoryOrder.standardMenu) @@ -651,6 +651,15 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.setViewMode(False, True, False, False, True) self.modeLiveItem.setChecked(True) + def appStartup(self): + # Give all the plugins a chance to perform some tasks at startup + Receiver.send_message(u'openlp_process_events') + for plugin in self.pluginManager.plugins: + if plugin.isActive(): + Receiver.send_message(u'openlp_process_events') + plugin.appStartup() + Receiver.send_message(u'openlp_process_events') + def firstTime(self): # Import themes if first time Receiver.send_message(u'openlp_process_events') @@ -669,13 +678,12 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): def blankCheck(self): """ Check and display message if screen blank on setup. - Triggered by delay thread. """ settings = QtCore.QSettings() + self.liveController.mainDisplaySetBackground() if settings.value(u'%s/screen blank' % self.generalSettingsSection, QtCore.QVariant(False)).toBool(): - self.liveController.mainDisplaySetBackground() - if settings.value(u'blank warning', + if settings.value(u'%s/blank warning' % self.generalSettingsSection, QtCore.QVariant(False)).toBool(): QtGui.QMessageBox.question(self, translate('OpenLP.MainWindow', diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 0f7f6dbeb..b4c87488c 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -40,7 +40,6 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog): """ def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) - self.parent = parent self.activePlugin = None self.programaticChange = False self.setupUi(self) @@ -65,7 +64,7 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog): self._clearDetails() self.programaticChange = True pluginListWidth = 0 - for plugin in self.parent.pluginManager.plugins: + for plugin in self.parent().pluginManager.plugins: item = QtGui.QListWidgetItem(self.pluginListWidget) # We do this just to make 100% sure the status is an integer as # sometimes when it's loaded from the config, it isn't cast to int. @@ -117,7 +116,7 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog): plugin_name_singular = \ self.pluginListWidget.currentItem().text().split(u' ')[0] self.activePlugin = None - for plugin in self.parent.pluginManager.plugins: + for plugin in self.parent().pluginManager.plugins: if plugin.nameStrings[u'singular'] == plugin_name_singular: self.activePlugin = plugin break @@ -133,6 +132,7 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog): Receiver.send_message(u'cursor_busy') self.activePlugin.toggleStatus(PluginStatus.Active) Receiver.send_message(u'cursor_normal') + self.activePlugin.appStartup() else: self.activePlugin.toggleStatus(PluginStatus.Inactive) status_text = unicode( diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 09ae8857f..ccd1eb18c 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -42,42 +42,44 @@ http://doc.trolltech.com/4.7/richtext-html-subset.html#css-properties */ .serviceTitle { - font-weight:600; - font-size:x-large; - color:black; + font-weight: 600; + font-size: x-large; + color: black; } .item { - color:black; + color: black; } .itemTitle { - font-weight:600; - font-size:large; + font-weight: 600; + font-size: large; } -.itemText {} +.itemText { + margin-top: 10px; +} .itemFooter { - font-size:8px; + font-size: 8px; } .itemNotes {} .itemNotesTitle { - font-weight:bold; - font-size:12px; + font-weight: bold; + font-size: 12px; } .itemNotesText { - font-size:11px; + font-size: 11px; } .media {} .mediaTitle { - font-weight:bold; - font-size:11px; + font-weight: bold; + font-size: 11px; } .mediaText {} @@ -89,16 +91,16 @@ http://doc.trolltech.com/4.7/richtext-html-subset.html#css-properties } .customNotesTitle { - font-weight:bold; - font-size:11px; + font-weight: bold; + font-size: 11px; } .customNotesText { - font-size:11px; + font-size: 11px; } .newPage { - page-break-before:always; + page-break-before: always; } """ @@ -212,11 +214,11 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): verse_def = None for slide in item.get_frames(): if not verse_def or verse_def != slide[u'verseTag']: - p = self._addElement(u'div', parent=div, + text_div = self._addElement(u'div', parent=div, classId=u'itemText') else: - self._addElement(u'br', parent=p) - self._addElement(u'p', slide[u'html'], p) + self._addElement(u'br', parent=text_div) + self._addElement(u'span', slide[u'html'], text_div) verse_def = slide[u'verseTag'] # Break the page before the div element. if index != 0 and self.pageBreakAfterText.isChecked(): diff --git a/openlp/core/ui/screen.py b/openlp/core/ui/screen.py index 2b6e10644..160080c5a 100644 --- a/openlp/core/ui/screen.py +++ b/openlp/core/ui/screen.py @@ -79,7 +79,7 @@ class ScreenList(object): ``number`` The number of the screen, which size has changed. """ - log.info(u'screenResolutionChanged %d' % number) + log.info(u'screen_resolution_changed %d' % number) for screen in self.screen_list: if number == screen[u'number']: newScreen = { @@ -104,6 +104,9 @@ class ScreenList(object): ``changed_screen`` The screen's number which has been (un)plugged. """ + # Do not log at start up. + if changed_screen != -1: + log.info(u'screen_count_changed %d' % number) # Remove unplugged screens. for screen in copy.deepcopy(self.screen_list): if screen[u'number'] == self.desktop.numScreens(): @@ -116,8 +119,7 @@ class ScreenList(object): u'size': self.desktop.screenGeometry(number), u'primary': (self.desktop.primaryScreen() == number) }) - # We do not want to send this message, when the method is called the - # first time. + # We do not want to send this message at start up. if changed_screen != -1: # Reload setting tabs to apply possible changes. Receiver.send_message(u'config_screen_changed') @@ -241,6 +243,7 @@ class ScreenList(object): height = settings.value(u'height', QtCore.QVariant(self.current[u'size'].height())).toInt()[0] self.override[u'size'] = QtCore.QRect(x, y, width, height) + self.override[u'primary'] = False settings.endGroup() if override_display: self.set_override_display() diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 7851e5e41..8ecd89bc7 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -676,8 +676,18 @@ class ServiceManager(QtGui.QWidget): .is_capable(ItemCapabilities.AllowsVariableStartTime): self.timeAction.setVisible(True) self.themeMenu.menuAction().setVisible(False) - if serviceItem[u'service_item'].is_text(): + # Set up the theme menu. + if serviceItem[u'service_item'].is_text() and \ + self.mainwindow.renderer.theme_level == ThemeLevel.Song: self.themeMenu.menuAction().setVisible(True) + # The service item does not have a theme, check the "Default". + if serviceItem[u'service_item'].theme is None: + themeAction = self.themeMenu.defaultAction() + else: + themeAction = self.themeMenu.findChild( + QtGui.QAction, serviceItem[u'service_item'].theme) + if themeAction is not None: + themeAction.setChecked(True) action = self.menu.exec_(self.serviceManagerList.mapToGlobal(point)) def onServiceItemNoteForm(self): @@ -949,6 +959,7 @@ class ServiceManager(QtGui.QWidget): treewidgetitem.setToolTip(0, serviceitem.notes) treewidgetitem.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(item[u'order'])) + treewidgetitem.setSelected(item[u'selected']) # Add the children to their parent treewidgetitem. for count, frame in enumerate(serviceitem.get_frames()): child = QtGui.QTreeWidgetItem(treewidgetitem) @@ -1020,16 +1031,34 @@ class ServiceManager(QtGui.QWidget): # force reset of renderer as theme data has changed self.mainwindow.renderer.themedata = None if self.serviceItems: + for item in self.serviceItems: + item[u'selected'] = False + serviceIterator = QtGui.QTreeWidgetItemIterator( + self.serviceManagerList) + while serviceIterator.value(): + if serviceIterator.value().isSelected(): + selectedItem = serviceIterator.value() + serviceIterator += 1 + if selectedItem is not None: + if selectedItem.parent() is None: + pos = selectedItem.data(0, QtCore.Qt.UserRole).toInt()[0] + else: + pos = selectedItem.parent().data(0, QtCore.Qt.UserRole). \ + toInt()[0] + self.serviceItems[pos - 1][u'selected'] = True tempServiceItems = self.serviceItems self.serviceManagerList.clear() self.serviceItems = [] self.isNew = True for item in tempServiceItems: self.addServiceItem( - item[u'service_item'], False, expand=item[u'expanded']) + item[u'service_item'], False, expand=item[u'expanded'], + repaint=False, selected=item[u'selected']) # Set to False as items may have changed rendering # does not impact the saved song so True may also be valid self.setModified() + # Repaint it once only at the end + self.repaintServiceList(-1, -1) Receiver.send_message(u'cursor_normal') def serviceItemUpdate(self, message): @@ -1059,7 +1088,7 @@ class ServiceManager(QtGui.QWidget): self.setModified() def addServiceItem(self, item, rebuild=False, expand=None, replace=False, - repaint=True): + repaint=True, selected=False): """ Add a Service item to the list @@ -1087,17 +1116,17 @@ class ServiceManager(QtGui.QWidget): for inditem in item: self.serviceItems.append({u'service_item': inditem, u'order': len(self.serviceItems) + 1, - u'expanded': expand}) + u'expanded': expand, u'selected': selected}) else: self.serviceItems.append({u'service_item': item, u'order': len(self.serviceItems) + 1, - u'expanded': expand}) + u'expanded': expand, u'selected': selected}) if repaint: self.repaintServiceList(len(self.serviceItems) - 1, -1) else: self.serviceItems.insert(self.dropPosition, {u'service_item': item, u'order': self.dropPosition, - u'expanded': expand}) + u'expanded': expand, u'selected': selected}) self.repaintServiceList(self.dropPosition, -1) # if rebuilding list make sure live is fixed. if rebuild: @@ -1280,16 +1309,33 @@ class ServiceManager(QtGui.QWidget): self.themeComboBox.clear() self.themeMenu.clear() self.themeComboBox.addItem(u'') + themeGroup = QtGui.QActionGroup(self.themeMenu) + themeGroup.setExclusive(True) + themeGroup.setObjectName(u'themeGroup') + # Create a "Default" theme, which allows the user to reset the item's + # theme to the service theme or global theme. + defaultTheme = context_menu_action(self.themeMenu, None, + UiStrings().Default, self.onThemeChangeAction) + defaultTheme.setCheckable(True) + self.themeMenu.setDefaultAction(defaultTheme) + themeGroup.addAction(defaultTheme) + context_menu_separator(self.themeMenu) for theme in theme_list: self.themeComboBox.addItem(theme) - context_menu_action(self.themeMenu, None, theme, + themeAction = context_menu_action(self.themeMenu, None, theme, self.onThemeChangeAction) + themeAction.setObjectName(theme) + themeAction.setCheckable(True) + themeGroup.addAction(themeAction) find_and_set_in_combo_box(self.themeComboBox, self.service_theme) self.mainwindow.renderer.set_service_theme(self.service_theme) self.regenerateServiceItems() def onThemeChangeAction(self): - theme = unicode(self.sender().text()) + theme = unicode(self.sender().objectName()) + # No object name means that the "Default" theme is supposed to be used. + if not theme: + theme = None item = self.findServiceItem()[0] self.serviceItems[item][u'service_item'].theme = theme self.regenerateServiceItems() diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index b1094bbe3..5b6212cf8 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -46,7 +46,6 @@ class SlideList(QtGui.QTableWidget): """ def __init__(self, parent=None, name=None): QtGui.QTableWidget.__init__(self, parent.controller) - self.parent = parent class SlideController(QtGui.QWidget): @@ -60,13 +59,12 @@ class SlideController(QtGui.QWidget): """ QtGui.QWidget.__init__(self, parent) self.isLive = isLive - self.parent = parent self.screens = ScreenList.get_instance() self.ratio = float(self.screens.current[u'size'].width()) / \ float(self.screens.current[u'size'].height()) - self.image_manager = self.parent.image_manager + self.image_manager = self.parent().image_manager self.loopList = [ - u'Start Loop', + u'Play Slides Menu', u'Loop Separator', u'Image SpinBox' ] @@ -140,19 +138,20 @@ class SlideController(QtGui.QWidget): self.previousItem = self.toolbar.addToolbarButton( translate('OpenLP.SlideController', 'Previous Slide'), u':/slides/slide_previous.png', - translate('OpenLP.SlideController', 'Move to previous'), + translate('OpenLP.SlideController', 'Move to previous.'), self.onSlideSelectedPrevious, shortcuts=[QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp], context=QtCore.Qt.WidgetWithChildrenShortcut) self.nextItem = self.toolbar.addToolbarButton( translate('OpenLP.SlideController', 'Next Slide'), u':/slides/slide_next.png', - translate('OpenLP.SlideController', 'Move to next'), + translate('OpenLP.SlideController', 'Move to next.'), self.onSlideSelectedNext, shortcuts=[QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown], context=QtCore.Qt.WidgetWithChildrenShortcut) self.toolbar.addToolbarSeparator(u'Close Separator') if self.isLive: + # Hide Menu self.hideMenu = QtGui.QToolButton(self.toolbar) self.hideMenu.setText(translate('OpenLP.SlideController', 'Hide')) self.hideMenu.setPopupMode(QtGui.QToolButton.MenuButtonPopup) @@ -180,65 +179,72 @@ class SlideController(QtGui.QWidget): self.hideMenu.menu().addAction(self.themeScreen) self.hideMenu.menu().addAction(self.desktopScreen) self.toolbar.addToolbarSeparator(u'Loop Separator') - startLoop = self.toolbar.addToolbarButton( - # Does not need translating - control string. - u'Start Loop', u':/media/media_time.png', - translate('OpenLP.SlideController', 'Start continuous loop'), - self.onStartLoop) - startLoop.setObjectName(u'startLoop') - action_list = ActionList.get_instance() - action_list.add_action(startLoop, UiStrings().LiveToolbar) - stopLoop = self.toolbar.addToolbarButton( - # Does not need translating - control string. - u'Stop Loop', u':/media/media_stop.png', - translate('OpenLP.SlideController', 'Stop continuous loop'), - self.onStopLoop) - stopLoop.setObjectName(u'stopLoop') - action_list.add_action(stopLoop, UiStrings().LiveToolbar) - self.toogleLoop = shortcut_action(self, u'toogleLoop', - [QtGui.QKeySequence(u'L')], self.onToggleLoop, - category=UiStrings().LiveToolbar) - self.toogleLoop.setText(translate('OpenLP.SlideController', - 'Start/Stop continuous loop')) - self.addAction(self.toogleLoop) + # Play Slides Menu + self.playSlidesMenu = QtGui.QToolButton(self.toolbar) + self.playSlidesMenu.setText(translate('OpenLP.SlideController', + 'Play Slides')) + self.playSlidesMenu.setPopupMode(QtGui.QToolButton.MenuButtonPopup) + self.toolbar.addToolbarWidget(u'Play Slides Menu', + self.playSlidesMenu) + self.playSlidesMenu.setMenu(QtGui.QMenu( + translate('OpenLP.SlideController', 'Play Slides'), + self.toolbar)) + self.playSlidesLoop = shortcut_action(self.playSlidesMenu, + u'playSlidesLoop', [], self.onPlaySlidesLoop, + u':/media/media_time.png', False, UiStrings().LiveToolbar) + self.playSlidesLoop.setText( + translate('OpenLP.SlideController', 'Play Slides in Loop')) + self.playSlidesOnce = shortcut_action(self.playSlidesMenu, + u'playSlidesOnce', [], self.onPlaySlidesOnce, + u':/media/media_time.png', False, UiStrings().LiveToolbar) + self.playSlidesOnce.setText( + translate('OpenLP.SlideController', 'Play Slides to End')) + if QtCore.QSettings().value(self.parent().generalSettingsSection + + u'/enable slide loop', QtCore.QVariant(True)).toBool(): + self.playSlidesMenu.setDefaultAction(self.playSlidesLoop) + else: + self.playSlidesMenu.setDefaultAction(self.playSlidesOnce) + self.playSlidesMenu.menu().addAction(self.playSlidesLoop) + self.playSlidesMenu.menu().addAction(self.playSlidesOnce) + # Loop Delay Spinbox self.delaySpinBox = QtGui.QSpinBox() self.delaySpinBox.setRange(1, 180) self.toolbar.addToolbarWidget(u'Image SpinBox', self.delaySpinBox) self.delaySpinBox.setSuffix(UiStrings().Seconds) self.delaySpinBox.setToolTip(translate('OpenLP.SlideController', - 'Delay between slides in seconds')) + 'Delay between slides in seconds.')) else: self.toolbar.addToolbarButton( # Does not need translating - control string. u'Go Live', u':/general/general_live.png', - translate('OpenLP.SlideController', 'Move to live'), + translate('OpenLP.SlideController', 'Move to live.'), self.onGoLive) self.toolbar.addToolbarButton( # Does not need translating - control string. u'Add to Service', u':/general/general_add.png', - translate('OpenLP.SlideController', 'Add to Service'), + translate('OpenLP.SlideController', 'Add to Service.'), self.onPreviewAddToService) self.toolbar.addToolbarSeparator(u'Close Separator') self.toolbar.addToolbarButton( # Does not need translating - control string. u'Edit Song', u':/general/general_edit.png', translate('OpenLP.SlideController', - 'Edit and reload song preview'), + 'Edit and reload song preview.'), self.onEditSong) self.controllerLayout.addWidget(self.toolbar) # Build a Media ToolBar self.mediabar = OpenLPToolbar(self) self.mediabar.addToolbarButton( u'Media Start', u':/slides/media_playback_start.png', - translate('OpenLP.SlideController', 'Start playing media'), + translate('OpenLP.SlideController', 'Start playing media.'), self.onMediaPlay) self.mediabar.addToolbarButton( u'Media Pause', u':/slides/media_playback_pause.png', - translate('OpenLP.SlideController', 'Start playing media'), + translate('OpenLP.SlideController', 'Start playing media.'), self.onMediaPause) self.mediabar.addToolbarButton( u'Media Stop', u':/slides/media_playback_stop.png', - translate('OpenLP.SlideController', 'Start playing media'), + translate('OpenLP.SlideController', 'Start playing media.'), self.onMediaStop) if self.isLive: # Build the Song Toolbar @@ -321,7 +327,6 @@ class SlideController(QtGui.QWidget): QtCore.SIGNAL(u'slidecontroller_live_spin_delay'), self.receiveSpinDelay) self.toolbar.makeWidgetsInvisible(self.loopList) - self.toolbar.actions[u'Stop Loop'].setVisible(False) else: QtCore.QObject.connect(self.previewListWidget, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), @@ -461,7 +466,7 @@ class SlideController(QtGui.QWidget): self.previewListWidget.resizeRowsToContents() else: # Sort out image heights. - width = self.parent.controlSplitter.sizes()[self.split] + width = self.parent().controlSplitter.sizes()[self.split] for framenumber in range(len(self.serviceItem.get_frames())): self.previewListWidget.setRowHeight( framenumber, width / self.ratio) @@ -496,21 +501,14 @@ class SlideController(QtGui.QWidget): self.mediabar.setVisible(False) self.toolbar.makeWidgetsInvisible([u'Song Menu']) self.toolbar.makeWidgetsInvisible(self.loopList) - self.toogleLoop.setEnabled(False) - self.toolbar.actions[u'Start Loop'].setEnabled(False) - self.toolbar.actions[u'Stop Loop'].setEnabled(False) - self.toolbar.actions[u'Stop Loop'].setVisible(False) if item.is_text(): if QtCore.QSettings().value( - self.parent.songsSettingsSection + u'/display songbar', + self.parent().songsSettingsSection + u'/display songbar', QtCore.QVariant(True)).toBool() and len(self.slideList) > 0: self.toolbar.makeWidgetsVisible([u'Song Menu']) if item.is_capable(ItemCapabilities.AllowsLoop) and \ len(item.get_frames()) > 1: self.toolbar.makeWidgetsVisible(self.loopList) - self.toogleLoop.setEnabled(True) - self.toolbar.actions[u'Start Loop'].setEnabled(True) - self.toolbar.actions[u'Stop Loop'].setEnabled(True) if item.is_media(): self.toolbar.setVisible(False) self.mediabar.setVisible(True) @@ -591,7 +589,7 @@ class SlideController(QtGui.QWidget): Receiver.send_message(u'%s_start' % serviceItem.name.lower(), [serviceItem, self.isLive, self.hideMode(), slideno]) self.slideList = {} - width = self.parent.controlSplitter.sizes()[self.split] + width = self.parent().controlSplitter.sizes()[self.split] self.previewListWidget.clear() self.previewListWidget.setRowCount(0) self.previewListWidget.setColumnWidth(0, width) @@ -625,8 +623,8 @@ class SlideController(QtGui.QWidget): label.setScaledContents(True) if self.serviceItem.is_command(): image = resize_image(frame[u'image'], - self.parent.renderer.width, - self.parent.renderer.height) + self.parent().renderer.width, + self.parent().renderer.height) else: # If current slide set background to image if framenumber == slideno: @@ -635,7 +633,7 @@ class SlideController(QtGui.QWidget): image = self.image_manager.get_image(frame[u'title']) label.setPixmap(QtGui.QPixmap.fromImage(image)) self.previewListWidget.setCellWidget(framenumber, 0, label) - slideHeight = width * self.parent.renderer.screen_ratio + slideHeight = width * self.parent().renderer.screen_ratio row += 1 text.append(unicode(row)) self.previewListWidget.setItem(framenumber, 0, item) @@ -736,7 +734,7 @@ class SlideController(QtGui.QWidget): """ log.debug(u'mainDisplaySetBackground live = %s' % self.isLive) display_type = QtCore.QSettings().value( - self.parent.generalSettingsSection + u'/screen blank', + self.parent().generalSettingsSection + u'/screen blank', QtCore.QVariant(u'')).toString() if not self.display.primary: # Order done to handle initial conversion @@ -744,8 +742,10 @@ class SlideController(QtGui.QWidget): self.onThemeDisplay(True) elif display_type == u'hidden': self.onHideDisplay(True) - else: + elif display_type == u'blanked': self.onBlankDisplay(True) + else: + Receiver.send_message(u'maindisplay_show') def onSlideBlank(self): """ @@ -772,11 +772,11 @@ class SlideController(QtGui.QWidget): self.desktopScreen.setChecked(False) if checked: QtCore.QSettings().setValue( - self.parent.generalSettingsSection + u'/screen blank', + self.parent().generalSettingsSection + u'/screen blank', QtCore.QVariant(u'blanked')) else: QtCore.QSettings().remove( - self.parent.generalSettingsSection + u'/screen blank') + self.parent().generalSettingsSection + u'/screen blank') self.blankPlugin() self.updatePreview() @@ -793,11 +793,11 @@ class SlideController(QtGui.QWidget): self.desktopScreen.setChecked(False) if checked: QtCore.QSettings().setValue( - self.parent.generalSettingsSection + u'/screen blank', + self.parent().generalSettingsSection + u'/screen blank', QtCore.QVariant(u'themed')) else: QtCore.QSettings().remove( - self.parent.generalSettingsSection + u'/screen blank') + self.parent().generalSettingsSection + u'/screen blank') self.blankPlugin() self.updatePreview() @@ -814,11 +814,11 @@ class SlideController(QtGui.QWidget): self.desktopScreen.setChecked(checked) if checked: QtCore.QSettings().setValue( - self.parent.generalSettingsSection + u'/screen blank', + self.parent().generalSettingsSection + u'/screen blank', QtCore.QVariant(u'hidden')) else: QtCore.QSettings().remove( - self.parent.generalSettingsSection + u'/screen blank') + self.parent().generalSettingsSection + u'/screen blank') self.hidePlugin(checked) self.updatePreview() @@ -841,6 +841,11 @@ class SlideController(QtGui.QWidget): Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive]) + else: + if hide_mode: + Receiver.send_message(u'maindisplay_hide', hide_mode) + else: + Receiver.send_message(u'maindisplay_show') def hidePlugin(self, hide): """ @@ -859,6 +864,11 @@ class SlideController(QtGui.QWidget): Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive]) + else: + if hide: + Receiver.send_message(u'maindisplay_hide', HideMode.Screen) + else: + Receiver.send_message(u'maindisplay_show') def onSlideSelected(self, start=False): """ @@ -933,7 +943,7 @@ class SlideController(QtGui.QWidget): rect.y(), rect.width(), rect.height()) self.slidePreview.setPixmap(winimg) - def onSlideSelectedNext(self): + def onSlideSelectedNext(self, wrap=None): """ Go to the next slide. """ @@ -946,8 +956,11 @@ class SlideController(QtGui.QWidget): else: row = self.previewListWidget.currentRow() + 1 if row == self.previewListWidget.rowCount(): - if QtCore.QSettings().value(self.parent.generalSettingsSection + - u'/enable slide loop', QtCore.QVariant(True)).toBool(): + if wrap is None: + wrap = QtCore.QSettings().value( + self.parent().generalSettingsSection + + u'/enable slide loop', QtCore.QVariant(True)).toBool() + if wrap: row = 0 else: row = self.previewListWidget.rowCount() - 1 @@ -967,8 +980,8 @@ class SlideController(QtGui.QWidget): else: row = self.previewListWidget.currentRow() - 1 if row == -1: - if QtCore.QSettings().value(self.parent.generalSettingsSection + - u'/enable slide loop', QtCore.QVariant(True)).toBool(): + if QtCore.QSettings().value(self.parent().generalSettingsSection + + u'/enable slide loop', QtCore.QVariant(True)).toBool(): row = self.previewListWidget.rowCount() - 1 else: row = 0 @@ -996,11 +1009,11 @@ class SlideController(QtGui.QWidget): self.previewListWidget.rowCount() - 1) self.slideSelected() - def onToggleLoop(self, toggled): + def onToggleLoop(self): """ Toggles the loop state. """ - if self.toolbar.actions[u'Start Loop'].isVisible(): + if self.playSlidesLoop.isChecked() or self.playSlidesOnce.isChecked(): self.onStartLoop() else: self.onStopLoop() @@ -1012,8 +1025,6 @@ class SlideController(QtGui.QWidget): if self.previewListWidget.rowCount() > 1: self.timer_id = self.startTimer( int(self.delaySpinBox.value()) * 1000) - self.toolbar.actions[u'Stop Loop'].setVisible(True) - self.toolbar.actions[u'Start Loop'].setVisible(False) def onStopLoop(self): """ @@ -1022,15 +1033,39 @@ class SlideController(QtGui.QWidget): if self.timer_id != 0: self.killTimer(self.timer_id) self.timer_id = 0 - self.toolbar.actions[u'Start Loop'].setVisible(True) - self.toolbar.actions[u'Stop Loop'].setVisible(False) + + def onPlaySlidesLoop(self, checked=None): + """ + Start or stop 'Play Slides in Loop' + """ + if checked is None: + checked = self.playSlidesLoop.isChecked() + else: + self.playSlidesLoop.setChecked(checked) + log.debug(u'onPlaySlidesLoop %s' % checked) + self.playSlidesMenu.setDefaultAction(self.playSlidesLoop) + self.playSlidesOnce.setChecked(False) + self.onToggleLoop() + + def onPlaySlidesOnce(self, checked=None): + """ + Start or stop 'Play Slides to End' + """ + if checked is None: + checked = self.playSlidesOnce.isChecked() + else: + self.playSlidesOnce.setChecked(checked) + log.debug(u'onPlaySlidesOnce %s' % checked) + self.playSlidesMenu.setDefaultAction(self.playSlidesOnce) + self.playSlidesLoop.setChecked(False) + self.onToggleLoop() def timerEvent(self, event): """ If the timer event is for this window select next slide """ if event.timerId() == self.timer_id: - self.onSlideSelectedNext() + self.onSlideSelectedNext(self.playSlidesLoop.isChecked()) def onEditSong(self): """ @@ -1044,7 +1079,9 @@ class SlideController(QtGui.QWidget): """ From the preview display request the Item to be added to service """ - self.parent.serviceManagerContents.addServiceItem(self.serviceItem) + if self.serviceItem: + self.parent().serviceManagerContents.addServiceItem( + self.serviceItem) def onGoLiveClick(self): """ @@ -1072,7 +1109,7 @@ class SlideController(QtGui.QWidget): Receiver.send_message('servicemanager_preview_live', u'%s:%s' % (self.serviceItem._uuid, row)) else: - self.parent.liveController.addServiceManagerItem( + self.parent().liveController.addServiceManagerItem( self.serviceItem, row) def onMediaStart(self, item): diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 4dabcfdf7..72bdf4558 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -57,8 +57,6 @@ class ThemeManager(QtGui.QWidget): self.settingsSection = u'themes' self.themeForm = ThemeForm(self) self.fileRenameForm = FileRenameForm(self) - self.serviceComboBox = \ - self.mainwindow.serviceManagerContents.themeComboBox # start with the layout self.layout = QtGui.QVBoxLayout(self) self.layout.setSpacing(0) diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 709b3ad80..7e8188f42 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -145,10 +145,8 @@ class ThemesTab(SettingsTab): def save(self): settings = QtCore.QSettings() settings.beginGroup(self.settingsSection) - settings.setValue(u'theme level', - QtCore.QVariant(self.theme_level)) - settings.setValue(u'global theme', - QtCore.QVariant(self.global_theme)) + settings.setValue(u'theme level', QtCore.QVariant(self.theme_level)) + settings.setValue(u'global theme', QtCore.QVariant(self.global_theme)) settings.endGroup() self.mainwindow.renderer.set_global_theme( self.global_theme, self.theme_level) @@ -186,8 +184,7 @@ class ThemesTab(SettingsTab): self.settingsSection + u'/global theme', QtCore.QVariant(u'')).toString()) self.DefaultComboBox.clear() - for theme in theme_list: - self.DefaultComboBox.addItem(theme) + self.DefaultComboBox.addItems(theme_list) find_and_set_in_combo_box(self.DefaultComboBox, self.global_theme) self.mainwindow.renderer.set_global_theme( self.global_theme, self.theme_level) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 4ef445e36..ec20346d8 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -69,7 +69,6 @@ class VersionThread(QtCore.QThread): Run the thread. """ time.sleep(1) - Receiver.send_message(u'maindisplay_blank_check') app_version = get_application_version() version = check_latest_version(app_version) remote_version = {} diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index 0c0a4ed56..475d0a47a 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -77,6 +77,8 @@ class LanguageManager(object): AppLocation.LanguageDir)) file_names = trans_dir.entryList(QtCore.QStringList(u'*.qm'), QtCore.QDir.Files, QtCore.QDir.Name) + # Remove qm files from the list which start with "qt_". + file_names = file_names.filter(QtCore.QRegExp("^(?!qt_)")) for name in file_names: file_names.replaceInStrings(name, trans_dir.filePath(name)) return file_names diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index 13cb4503a..1415c809c 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -41,7 +41,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): Initialise the alert form """ self.manager = plugin.manager - self.parent = plugin + self.plugin = plugin self.item_id = None QtGui.QDialog.__init__(self, plugin.formparent) self.setupUi(self) @@ -195,7 +195,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): self.parameterEdit.setFocus() return False text = text.replace(u'<>', unicode(self.parameterEdit.text())) - self.parent.alertsmanager.displayAlert(text) + self.plugin.alertsmanager.displayAlert(text) return True def onCurrentRowChanged(self, row): diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index c55e76ef7..1d40d5dd3 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -40,8 +40,7 @@ class AlertsManager(QtCore.QObject): log.info(u'Alert Manager loaded') def __init__(self, parent): - QtCore.QObject.__init__(self) - self.parent = parent + QtCore.QObject.__init__(self, parent) self.screen = None self.timer_id = 0 self.alertList = [] @@ -85,8 +84,8 @@ class AlertsManager(QtCore.QObject): if len(self.alertList) == 0: return text = self.alertList.pop(0) - alertTab = self.parent.settings_tab - self.parent.liveController.display.alert(text) + alertTab = self.parent().settings_tab + self.parent().liveController.display.alert(text) # Check to see if we have a timer running. if self.timer_id == 0: self.timer_id = self.startTimer(int(alertTab.timeout) * 1000) @@ -101,7 +100,7 @@ class AlertsManager(QtCore.QObject): """ log.debug(u'timer event') if event.timerId() == self.timer_id: - self.parent.liveController.display.alert(u'') + self.parent().liveController.display.alert(u'') self.killTimer(self.timer_id) self.timer_id = 0 self.generateAlert() diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 2deb86cc1..5d4f1bdb1 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -33,6 +33,7 @@ from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.ui import base_action, UiStrings from openlp.core.utils.actions import ActionList from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem +from openlp.plugins.bibles.forms import BibleUpgradeForm log = logging.getLogger(__name__) @@ -59,6 +60,8 @@ class BiblePlugin(Plugin): #action_list.add_action(self.exportBibleItem, UiStrings().Export) # Set to invisible until we can export bibles self.exportBibleItem.setVisible(False) + if len(self.manager.old_bible_databases): + self.toolsUpgradeItem.setVisible(True) def finalise(self): """ @@ -73,6 +76,19 @@ class BiblePlugin(Plugin): #action_list.remove_action(self.exportBibleItem, UiStrings().Export) self.exportBibleItem.setVisible(False) + def appStartup(self): + """ + Perform tasks on application starup + """ + if len(self.manager.old_bible_databases): + if QtGui.QMessageBox.information(self.formparent, + translate('OpenLP', 'Information'), translate('OpenLP', + 'Bible format has changed.\nYou have to upgrade your ' + 'existing Bibles.\nShould OpenLP upgrade now?'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | + QtGui.QMessageBox.No)) == QtGui.QMessageBox.Yes: + self.onToolsUpgradeItemTriggered() + def addImportMenuItem(self, import_menu): self.importBibleItem = base_action(import_menu, u'importBibleItem') self.importBibleItem.setText(translate('BiblesPlugin', '&Bible')) @@ -88,6 +104,39 @@ class BiblePlugin(Plugin): export_menu.addAction(self.exportBibleItem) self.exportBibleItem.setVisible(False) + def addToolsMenuItem(self, tools_menu): + """ + Give the bible plugin the opportunity to add items to the + **Tools** menu. + + ``tools_menu`` + The actual **Tools** menu item, so that your actions can + use it as their parent. + """ + log.debug(u'add tools menu') + self.toolsUpgradeItem = QtGui.QAction(tools_menu) + self.toolsUpgradeItem.setObjectName(u'toolsUpgradeItem') + self.toolsUpgradeItem.setText( + translate('BiblePlugin', '&Upgrade older Bibles')) + self.toolsUpgradeItem.setStatusTip( + translate('BiblePlugin', 'Upgrade the Bible databases to the ' + 'latest format')) + tools_menu.addAction(self.toolsUpgradeItem) + QtCore.QObject.connect(self.toolsUpgradeItem, + QtCore.SIGNAL(u'triggered()'), self.onToolsUpgradeItemTriggered) + self.toolsUpgradeItem.setVisible(False) + + def onToolsUpgradeItemTriggered(self): + """ + Upgrade older bible databases. + """ + if not hasattr(self, u'upgrade_wizard'): + self.upgrade_wizard = BibleUpgradeForm(self.formparent, + self.manager, self) + # If the import was not cancelled then reload. + if self.upgrade_wizard.exec_(): + self.mediaItem.reloadBibles() + def onBibleImportClick(self): if self.mediaItem: self.mediaItem.onImportClick() @@ -149,4 +198,3 @@ class BiblePlugin(Plugin): 'Add the selected Bible to the service.') } self.setPluginUiTextStrings(tooltips) - diff --git a/openlp/plugins/bibles/forms/__init__.py b/openlp/plugins/bibles/forms/__init__.py index 83b444394..58ed5fa88 100644 --- a/openlp/plugins/bibles/forms/__init__.py +++ b/openlp/plugins/bibles/forms/__init__.py @@ -51,7 +51,10 @@ This allows OpenLP to use ``self.object`` for all the GUI elements while keeping them separate from the functionality, so that it is easier to recreate the GUI from the .ui files later if necessary. """ - +from booknameform import BookNameForm +from languageform import LanguageForm from bibleimportform import BibleImportForm +from bibleupgradeform import BibleUpgradeForm -__all__ = ['BibleImportForm'] +__all__ = [u'BookNameForm', u'LanguageForm', u'BibleImportForm', + u'BibleUpgradeForm'] diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index e05e29d57..367f1e1b3 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -41,6 +41,7 @@ from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.core.utils import AppLocation, string_is_unicode from openlp.plugins.bibles.lib.manager import BibleFormat +from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename log = logging.getLogger(__name__) @@ -125,9 +126,6 @@ class BibleImportForm(OpenLPWizard): QtCore.QObject.connect(self.osisBrowseButton, QtCore.SIGNAL(u'clicked()'), self.onOsisBrowseButtonClicked) - QtCore.QObject.connect(self.csvTestamentsButton, - QtCore.SIGNAL(u'clicked()'), - self.onCsvTestamentsBrowseButtonClicked) QtCore.QObject.connect(self.csvBooksButton, QtCore.SIGNAL(u'clicked()'), self.onCsvBooksBrowseButtonClicked) @@ -188,18 +186,6 @@ class BibleImportForm(OpenLPWizard): self.csvLayout = QtGui.QFormLayout(self.csvWidget) self.csvLayout.setMargin(0) self.csvLayout.setObjectName(u'CsvLayout') - self.csvTestamentsLabel = QtGui.QLabel(self.csvWidget) - self.csvTestamentsLabel.setObjectName(u'CsvTestamentsLabel') - self.csvTestamentsLayout = QtGui.QHBoxLayout() - self.csvTestamentsLayout.setObjectName(u'CsvTestamentsLayout') - self.csvTestamentsEdit = QtGui.QLineEdit(self.csvWidget) - self.csvTestamentsEdit.setObjectName(u'CsvTestamentsEdit') - self.csvTestamentsLayout.addWidget(self.csvTestamentsEdit) - self.csvTestamentsButton = QtGui.QToolButton(self.csvWidget) - self.csvTestamentsButton.setIcon(self.openIcon) - self.csvTestamentsButton.setObjectName(u'CsvTestamentsButton') - self.csvTestamentsLayout.addWidget(self.csvTestamentsButton) - self.csvLayout.addRow(self.csvTestamentsLabel, self.csvTestamentsLayout) self.csvBooksLabel = QtGui.QLabel(self.csvWidget) self.csvBooksLabel.setObjectName(u'CsvBooksLabel') self.csvBooksLayout = QtGui.QHBoxLayout() @@ -384,8 +370,6 @@ class BibleImportForm(OpenLPWizard): translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.osisFileLabel.setText( translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) - self.csvTestamentsLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'Testaments file:')) self.csvBooksLabel.setText( translate('BiblesPlugin.ImportWizardForm', 'Books file:')) self.csvVersesLabel.setText( @@ -436,7 +420,6 @@ class BibleImportForm(OpenLPWizard): # Align all QFormLayouts towards each other. labelWidth = max(self.formatLabel.minimumSizeHint().width(), self.osisFileLabel.minimumSizeHint().width(), - self.csvTestamentsLabel.minimumSizeHint().width(), self.csvBooksLabel.minimumSizeHint().width(), self.csvVersesLabel.minimumSizeHint().width(), self.openSongFileLabel.minimumSizeHint().width(), @@ -458,14 +441,6 @@ class BibleImportForm(OpenLPWizard): self.osisFileEdit.setFocus() return False elif self.field(u'source_format').toInt()[0] == BibleFormat.CSV: - if not self.field(u'csv_testamentsfile').toString(): - answer = critical_error_message_box(UiStrings().NFSs, - translate('BiblesPlugin.ImportWizardForm', - 'You have not specified a testaments file. Do you ' - 'want to proceed with the import?'), question=True) - if answer == QtGui.QMessageBox.No: - self.csvTestamentsEdit.setFocus() - return False if not self.field(u'csv_booksfile').toString(): critical_error_message_box(UiStrings().NFSs, translate('BiblesPlugin.ImportWizardForm', @@ -498,6 +473,7 @@ class BibleImportForm(OpenLPWizard): license_version = unicode(self.field(u'license_version').toString()) license_copyright = \ unicode(self.field(u'license_copyright').toString()) + path = AppLocation.get_section_data_path(u'bibles') if not license_version: critical_error_message_box(UiStrings().EmptyField, translate('BiblesPlugin.ImportWizardForm', @@ -519,6 +495,15 @@ class BibleImportForm(OpenLPWizard): 'a different Bible or first delete the existing one.')) self.versionNameEdit.setFocus() return False + elif os.path.exists(os.path.join(path, clean_filename( + license_version))): + critical_error_message_box( + translate('BiblesPlugin.ImportWizardForm', 'Bible Exists'), + translate('BiblesPlugin.ImportWizardForm', + 'This Bible already exists. Please import ' + 'a different Bible or first delete the existing one.')) + self.versionNameEdit.setFocus() + return False return True if self.currentPage() == self.progressPage: return True @@ -543,14 +528,6 @@ class BibleImportForm(OpenLPWizard): self.getFileName(WizardStrings.OpenTypeFile % WizardStrings.OSIS, self.osisFileEdit) - def onCsvTestamentsBrowseButtonClicked(self): - """ - Show the file open dialog for the testaments CSV file. - """ - self.getFileName(WizardStrings.OpenTypeFile % WizardStrings.CSV, - self.csvTestamentsEdit, u'%s (*.csv)' - % translate('BiblesPlugin.ImportWizardForm', 'CSV File')) - def onCsvBooksBrowseButtonClicked(self): """ Show the file open dialog for the books CSV file. @@ -589,8 +566,6 @@ class BibleImportForm(OpenLPWizard): """ self.selectPage.registerField(u'source_format', self.formatComboBox) self.selectPage.registerField(u'osis_location', self.osisFileEdit) - self.selectPage.registerField( - u'csv_testamentsfile', self.csvTestamentsEdit) self.selectPage.registerField(u'csv_booksfile', self.csvBooksEdit) self.selectPage.registerField(u'csv_versefile', self.csvVersesEdit) self.selectPage.registerField(u'opensong_file', self.openSongFileEdit) @@ -619,7 +594,6 @@ class BibleImportForm(OpenLPWizard): self.cancelButton.setVisible(True) self.setField(u'source_format', QtCore.QVariant(0)) self.setField(u'osis_location', QtCore.QVariant('')) - self.setField(u'csv_testamentsfile', QtCore.QVariant('')) self.setField(u'csv_booksfile', QtCore.QVariant('')) self.setField(u'csv_versefile', QtCore.QVariant('')) self.setField(u'opensong_file', QtCore.QVariant('')) @@ -646,46 +620,27 @@ class BibleImportForm(OpenLPWizard): """ Load the lists of Crosswalk, BibleGateway and Bibleserver bibles. """ - filepath = AppLocation.get_directory(AppLocation.PluginsDir) - filepath = os.path.join(filepath, u'bibles', u'resources') # Load Crosswalk Bibles. - self.loadBibleResourceFile( - os.path.join(filepath, u'crosswalkbooks.csv'), - WebDownload.Crosswalk) + self.loadBibleResource(WebDownload.Crosswalk) # Load BibleGateway Bibles. - self.loadBibleResourceFile(os.path.join(filepath, u'biblegateway.csv'), - WebDownload.BibleGateway) + self.loadBibleResource(WebDownload.BibleGateway) # Load and Bibleserver Bibles. - self.loadBibleResourceFile(os.path.join(filepath, u'bibleserver.csv'), - WebDownload.Bibleserver) + self.loadBibleResource(WebDownload.Bibleserver) - def loadBibleResourceFile(self, file_path_name, download_type): + def loadBibleResource(self, download_type): """ - Loads a web bible resource file. - - ``file_path_name`` - The file to load including the file's path. + Loads a web bible from bible_resources.sqlite. ``download_type`` - The WebDownload type this file is for. + The WebDownload type e.g. bibleserver. """ self.web_bible_list[download_type] = {} - books_file = None - try: - books_file = open(file_path_name, 'rb') - dialect = csv.Sniffer().sniff(books_file.read(1024)) - books_file.seek(0) - books_reader = csv.reader(books_file, dialect) - for line in books_reader: - ver = string_is_unicode(line[0]) - name = string_is_unicode(line[1]) - self.web_bible_list[download_type][ver] = name.strip() - except IOError: - log.exception(u'%s resources missing' % - WebDownload.Names[download_type]) - finally: - if books_file: - books_file.close() + bibles = BiblesResourcesDB.get_webbibles( + WebDownload.Names[download_type]) + for bible in bibles: + version = bible[u'name'] + name = bible[u'abbreviation'] + self.web_bible_list[download_type][version] = name.strip() def preWizard(self): """ @@ -720,8 +675,7 @@ class BibleImportForm(OpenLPWizard): elif bible_type == BibleFormat.CSV: # Import a CSV bible. importer = self.manager.import_bible(BibleFormat.CSV, - name=license_version, testamentsfile=unicode( - self.field(u'csv_testamentsfile').toString()), + name=license_version, booksfile=unicode(self.field(u'csv_booksfile').toString()), versefile=unicode(self.field(u'csv_versefile').toString()) ) @@ -752,7 +706,7 @@ class BibleImportForm(OpenLPWizard): name=license_version, filename=unicode(self.field(u'openlp1_location').toString()) ) - if importer.do_import(): + if importer.do_import(license_version): self.manager.save_meta_data(license_version, license_version, license_copyright, license_permissions) self.manager.reload_bibles() diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py new file mode 100644 index 000000000..7913aac31 --- /dev/null +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -0,0 +1,785 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2011 Raoul Snyman # +# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, # +# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund # +# --------------------------------------------------------------------------- # +# 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 bible import functions for OpenLP +""" +import logging +import os.path +import re +import shutil + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import Receiver, SettingsManager, translate, \ + check_directory_exists +from openlp.core.lib.db import delete_database +from openlp.core.lib.ui import UiStrings, critical_error_message_box +from openlp.core.ui.wizard import OpenLPWizard, WizardStrings +from openlp.core.utils import AppLocation, delete_file +from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB,\ + BiblesResourcesDB, clean_filename +from openlp.plugins.bibles.lib.http import BSExtract, BGExtract, CWExtract + +log = logging.getLogger(__name__) + + +class BibleUpgradeForm(OpenLPWizard): + """ + This is the Bible Upgrade Wizard, which allows easy importing of Bibles + into OpenLP from older OpenLP2 database versions. + """ + log.info(u'BibleUpgradeForm loaded') + + def __init__(self, parent, manager, bibleplugin): + """ + Instantiate the wizard, and run any extra setup we need to. + + ``parent`` + The QWidget-derived parent of the wizard. + + ``manager`` + The Bible manager. + + ``bibleplugin`` + The Bible plugin. + """ + self.manager = manager + self.mediaItem = bibleplugin.mediaItem + self.suffix = u'.sqlite' + self.settingsSection = u'bibles' + self.path = AppLocation.get_section_data_path( + self.settingsSection) + self.files = self.manager.old_bible_databases + self.success = {} + self.newbibles = {} + OpenLPWizard.__init__(self, parent, bibleplugin, u'bibleUpgradeWizard', + u':/wizards/wizard_importbible.bmp') + + def setupUi(self, image): + """ + Set up the UI for the bible wizard. + """ + OpenLPWizard.setupUi(self, image) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'openlp_stop_wizard'), self.stop_import) + + def stop_import(self): + """ + Stops the import of the Bible. + """ + log.debug(u'Stopping import') + self.stop_import_flag = True + + def onCheckBoxIndexChanged(self, index): + """ + Show/Hide warnings if CheckBox state has changed + """ + for number, filename in enumerate(self.files): + if not self.checkBox[number].checkState() == QtCore.Qt.Checked: + self.verticalWidget[number].hide() + self.formWidget[number].hide() + else: + version_name = unicode(self.versionNameEdit[number].text()) + if self.manager.exists(version_name): + self.verticalWidget[number].show() + self.formWidget[number].show() + + def reject(self): + """ + Stop the wizard on cancel button, close button or ESC key. + """ + log.debug(u'Wizard cancelled by user') + self.stop_import_flag = True + if not self.currentPage() == self.progressPage: + self.done(QtGui.QDialog.Rejected) + else: + self.postWizard() + + def onCurrentIdChanged(self, pageId): + """ + Perform necessary functions depending on which wizard page is active. + """ + if self.page(pageId) == self.progressPage: + self.preWizard() + self.performWizard() + self.postWizard() + elif self.page(pageId) == self.selectPage and self.maxBibles == 0: + self.next() + + def onBackupBrowseButtonClicked(self): + """ + Show the file open dialog for the OSIS file. + """ + filename = QtGui.QFileDialog.getExistingDirectory(self, translate( + 'BiblesPlugin.UpgradeWizardForm', 'Select a Backup Directory'), + os.path.dirname(SettingsManager.get_last_dir( + self.plugin.settingsSection, 1))) + if filename: + self.backupDirectoryEdit.setText(filename) + SettingsManager.set_last_dir(self.plugin.settingsSection, + filename, 1) + + def onNoBackupCheckBoxToggled(self, checked): + """ + Enable or disable the backup directory widgets. + """ + self.backupDirectoryEdit.setEnabled(not checked) + self.backupBrowseButton.setEnabled(not checked) + + def backupOldBibles(self, backup_directory): + """ + Backup old bible databases in a given folder. + """ + check_directory_exists(backup_directory) + success = True + for filename in self.files: + try: + shutil.copy(os.path.join(self.path, filename[0]), + backup_directory) + except: + success = False + return success + + def customInit(self): + """ + Perform any custom initialisation for bible upgrading. + """ + self.manager.set_process_dialog(self) + self.restart() + + def customSignals(self): + """ + Set up the signals used in the bible importer. + """ + QtCore.QObject.connect(self.backupBrowseButton, + QtCore.SIGNAL(u'clicked()'), self.onBackupBrowseButtonClicked) + QtCore.QObject.connect(self.noBackupCheckBox, + QtCore.SIGNAL(u'toggled(bool)'), self.onNoBackupCheckBoxToggled) + + def addCustomPages(self): + """ + Add the bible import specific wizard pages. + """ + # Backup Page + self.backupPage = QtGui.QWizardPage() + self.backupPage.setObjectName(u'BackupPage') + self.backupLayout = QtGui.QVBoxLayout(self.backupPage) + self.backupLayout.setObjectName(u'BackupLayout') + self.backupInfoLabel = QtGui.QLabel(self.backupPage) + self.backupInfoLabel.setOpenExternalLinks(True) + self.backupInfoLabel.setTextFormat(QtCore.Qt.RichText) + self.backupInfoLabel.setWordWrap(True) + self.backupInfoLabel.setObjectName(u'backupInfoLabel') + self.backupLayout.addWidget(self.backupInfoLabel) + self.selectLabel = QtGui.QLabel(self.backupPage) + self.selectLabel.setObjectName(u'selectLabel') + self.backupLayout.addWidget(self.selectLabel) + self.formLayout = QtGui.QFormLayout() + self.formLayout.setMargin(0) + self.formLayout.setObjectName(u'FormLayout') + self.backupDirectoryLabel = QtGui.QLabel(self.backupPage) + self.backupDirectoryLabel.setObjectName(u'backupDirectoryLabel') + self.backupDirectoryLayout = QtGui.QHBoxLayout() + self.backupDirectoryLayout.setObjectName(u'BackupDirectoryLayout') + self.backupDirectoryEdit = QtGui.QLineEdit(self.backupPage) + self.backupDirectoryEdit.setObjectName(u'BackupFolderEdit') + self.backupDirectoryLayout.addWidget(self.backupDirectoryEdit) + self.backupBrowseButton = QtGui.QToolButton(self.backupPage) + self.backupBrowseButton.setIcon(self.openIcon) + self.backupBrowseButton.setObjectName(u'BackupBrowseButton') + self.backupDirectoryLayout.addWidget(self.backupBrowseButton) + self.formLayout.addRow(self.backupDirectoryLabel, + self.backupDirectoryLayout) + self.backupLayout.addLayout(self.formLayout) + self.noBackupCheckBox = QtGui.QCheckBox(self.backupPage) + self.noBackupCheckBox.setObjectName('NoBackupCheckBox') + self.backupLayout.addWidget(self.noBackupCheckBox) + self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, + QtGui.QSizePolicy.Minimum) + self.backupLayout.addItem(self.spacer) + self.addPage(self.backupPage) + # Select Page + self.selectPage = QtGui.QWizardPage() + self.selectPage.setObjectName(u'SelectPage') + self.pageLayout = QtGui.QVBoxLayout(self.selectPage) + self.pageLayout.setObjectName(u'pageLayout') + self.scrollArea = QtGui.QScrollArea(self.selectPage) + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName(u'scrollArea') + self.scrollArea.setHorizontalScrollBarPolicy( + QtCore.Qt.ScrollBarAlwaysOff) + self.scrollAreaContents = QtGui.QWidget(self.scrollArea) + self.scrollAreaContents.setObjectName(u'scrollAreaContents') + self.formLayout = QtGui.QVBoxLayout(self.scrollAreaContents) + self.formLayout.setSpacing(2) + self.formLayout.setObjectName(u'formLayout') + self.addScrollArea() + self.pageLayout.addWidget(self.scrollArea) + self.addPage(self.selectPage) + + def addScrollArea(self): + """ + Add the content to the scrollArea. + """ + self.checkBox = {} + self.versionNameEdit = {} + self.versionNameLabel = {} + self.versionInfoLabel = {} + self.versionInfoPixmap = {} + self.verticalWidget = {} + self.horizontalLayout = {} + self.formWidget = {} + self.formLayoutAttention = {} + for number, filename in enumerate(self.files): + bible = OldBibleDB(self.mediaItem, path=self.path, file=filename[0]) + self.checkBox[number] = QtGui.QCheckBox(self.scrollAreaContents) + checkBoxName = u'checkBox[%d]' % number + self.checkBox[number].setObjectName(checkBoxName) + self.checkBox[number].setText(bible.get_name()) + self.checkBox[number].setCheckState(QtCore.Qt.Checked) + self.formLayout.addWidget(self.checkBox[number]) + self.verticalWidget[number] = QtGui.QWidget(self.scrollAreaContents) + verticalWidgetName = u'verticalWidget[%d]' % number + self.verticalWidget[number].setObjectName(verticalWidgetName) + self.horizontalLayout[number] = QtGui.QHBoxLayout( + self.verticalWidget[number]) + self.horizontalLayout[number].setContentsMargins(25, 0, 0, 0) + horizontalLayoutName = u'horizontalLayout[%d]' % number + self.horizontalLayout[number].setObjectName(horizontalLayoutName) + self.versionInfoPixmap[number] = QtGui.QLabel( + self.verticalWidget[number]) + versionInfoPixmapName = u'versionInfoPixmap[%d]' % number + self.versionInfoPixmap[number].setObjectName(versionInfoPixmapName) + self.versionInfoPixmap[number].setPixmap(QtGui.QPixmap( + u':/bibles/bibles_upgrade_alert.png')) + self.versionInfoPixmap[number].setAlignment(QtCore.Qt.AlignRight) + self.horizontalLayout[number].addWidget( + self.versionInfoPixmap[number]) + self.versionInfoLabel[number] = QtGui.QLabel( + self.verticalWidget[number]) + versionInfoLabelName = u'versionInfoLabel[%d]' % number + self.versionInfoLabel[number].setObjectName(versionInfoLabelName) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, + QtGui.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.versionInfoLabel[number].sizePolicy().hasHeightForWidth()) + self.versionInfoLabel[number].setSizePolicy(sizePolicy) + self.horizontalLayout[number].addWidget( + self.versionInfoLabel[number]) + self.formLayout.addWidget(self.verticalWidget[number]) + self.formWidget[number] = QtGui.QWidget(self.scrollAreaContents) + formWidgetName = u'formWidget[%d]' % number + self.formWidget[number].setObjectName(formWidgetName) + self.formLayoutAttention[number] = QtGui.QFormLayout( + self.formWidget[number]) + self.formLayoutAttention[number].setContentsMargins(25, 0, 0, 5) + formLayoutAttentionName = u'formLayoutAttention[%d]' % number + self.formLayoutAttention[number].setObjectName( + formLayoutAttentionName) + self.versionNameLabel[number] = QtGui.QLabel( + self.formWidget[number]) + self.versionNameLabel[number].setObjectName(u'VersionNameLabel') + self.formLayoutAttention[number].setWidget(0, + QtGui.QFormLayout.LabelRole, self.versionNameLabel[number]) + self.versionNameEdit[number] = QtGui.QLineEdit( + self.formWidget[number]) + self.versionNameEdit[number].setObjectName(u'VersionNameEdit') + self.formLayoutAttention[number].setWidget(0, + QtGui.QFormLayout.FieldRole, self.versionNameEdit[number]) + self.versionNameEdit[number].setText(bible.get_name()) + self.formLayout.addWidget(self.formWidget[number]) + # Set up the Signal for the checkbox. + QtCore.QObject.connect(self.checkBox[number], + QtCore.SIGNAL(u'stateChanged(int)'), + self.onCheckBoxIndexChanged) + self.spacerItem = QtGui.QSpacerItem(20, 5, QtGui.QSizePolicy.Minimum, + QtGui.QSizePolicy.Expanding) + self.formLayout.addItem(self.spacerItem) + self.scrollArea.setWidget(self.scrollAreaContents) + + def clearScrollArea(self): + """ + Remove the content from the scrollArea. + """ + for number, filename in enumerate(self.files): + self.formLayout.removeWidget(self.checkBox[number]) + self.checkBox[number].setParent(None) + self.horizontalLayout[number].removeWidget( + self.versionInfoPixmap[number]) + self.versionInfoPixmap[number].setParent(None) + self.horizontalLayout[number].removeWidget( + self.versionInfoLabel[number]) + self.versionInfoLabel[number].setParent(None) + self.formLayout.removeWidget(self.verticalWidget[number]) + self.verticalWidget[number].setParent(None) + self.formLayoutAttention[number].removeWidget( + self.versionNameLabel[number]) + self.versionNameLabel[number].setParent(None) + self.formLayoutAttention[number].removeWidget( + self.versionNameEdit[number]) + self.formLayoutAttention[number].deleteLater() + self.versionNameEdit[number].setParent(None) + self.formLayout.removeWidget(self.formWidget[number]) + self.formWidget[number].setParent(None) + self.formLayout.removeItem(self.spacerItem) + + def retranslateUi(self): + """ + Allow for localisation of the bible import wizard. + """ + self.setWindowTitle(translate('BiblesPlugin.UpgradeWizardForm', + 'Bible Upgrade Wizard')) + self.titleLabel.setText(WizardStrings.HeaderStyle % + translate('OpenLP.Ui', 'Welcome to the Bible Upgrade Wizard')) + self.informationLabel.setText( + translate('BiblesPlugin.UpgradeWizardForm', + 'This wizard will help you to upgrade your existing Bibles from a ' + 'prior version of OpenLP 2. Click the next button below to start ' + 'the upgrade process.')) + self.backupPage.setTitle( + translate('BiblesPlugin.UpgradeWizardForm', + 'Select Backup Directory')) + self.backupPage.setSubTitle( + translate('BiblesPlugin.UpgradeWizardForm', + 'Please select a backup directory for your Bibles')) + self.backupInfoLabel.setText(translate('BiblesPlugin.UpgradeWizardForm', + 'Previous releases of OpenLP 2.0 are unable to use upgraded Bibles.' + ' This will create a backup of your current Bibles so that you can ' + 'simply copy the files back to your OpenLP data directory if you ' + 'need to revert to a previous release of OpenLP. Instructions on ' + 'how to restore the files can be found in our Frequently Asked Questions.')) + self.selectLabel.setText(translate('BiblesPlugin.UpgradeWizardForm', + 'Please select a backup location for your Bibles.')) + self.backupDirectoryLabel.setText( + translate('BiblesPlugin.UpgradeWizardForm', 'Backup Directory:')) + self.noBackupCheckBox.setText( + translate('BiblesPlugin.UpgradeWizardForm', + 'There is no need to backup my Bibles')) + self.selectPage.setTitle( + translate('BiblesPlugin.UpgradeWizardForm', + 'Select Bibles')) + self.selectPage.setSubTitle( + translate('BiblesPlugin.UpgradeWizardForm', + 'Please select the Bibles to upgrade')) + for number, bible in enumerate(self.files): + self.versionNameLabel[number].setText( + translate('BiblesPlugin.UpgradeWizardForm', 'Version name:')) + self.versionInfoLabel[number].setText( + translate('BiblesPlugin.UpgradeWizardForm', 'This ' + 'Bible still exists. Please change the name or uncheck it.')) + self.progressPage.setTitle(translate('BiblesPlugin.UpgradeWizardForm', + 'Upgrading')) + self.progressPage.setSubTitle( + translate('BiblesPlugin.UpgradeWizardForm', + 'Please wait while your Bibles are upgraded.')) + self.progressLabel.setText(WizardStrings.Ready) + self.progressBar.setFormat(u'%p%') + + def validateCurrentPage(self): + """ + Validate the current page before moving on to the next page. + """ + if self.currentPage() == self.welcomePage: + return True + elif self.currentPage() == self.backupPage: + if not self.noBackupCheckBox.checkState() == QtCore.Qt.Checked: + backup_path = unicode(self.backupDirectoryEdit.text()) + if not backup_path: + critical_error_message_box(UiStrings().EmptyField, + translate('BiblesPlugin.UpgradeWizardForm', + 'You need to specify a Backup Directory for your ' + 'Bibles.')) + self.backupDirectoryEdit.setFocus() + return False + else: + if not self.backupOldBibles(backup_path): + critical_error_message_box(UiStrings().Error, + translate('BiblesPlugin.UpgradeWizardForm', + 'The backup was not successful.\nTo backup your ' + 'Bibles you need permission to write to the given ' + 'directory. If you have write permissions and this ' + 'error still occurs, please report a bug.')) + return False + return True + elif self.currentPage() == self.selectPage: + for number, filename in enumerate(self.files): + if not self.checkBox[number].checkState() == QtCore.Qt.Checked: + continue + version_name = unicode(self.versionNameEdit[number].text()) + if not version_name: + critical_error_message_box(UiStrings().EmptyField, + translate('BiblesPlugin.UpgradeWizardForm', + 'You need to specify a version name for your Bible.')) + self.versionNameEdit[number].setFocus() + return False + elif self.manager.exists(version_name): + critical_error_message_box( + translate('BiblesPlugin.UpgradeWizardForm', + 'Bible Exists'), + translate('BiblesPlugin.UpgradeWizardForm', + 'This Bible already exists. Please upgrade ' + 'a different Bible, delete the existing one or ' + 'uncheck.')) + self.versionNameEdit[number].setFocus() + return False + elif os.path.exists(os.path.join(self.path, clean_filename( + version_name))) and version_name == filename[1]: + newfilename = u'old_database_%s' % filename[0] + if not os.path.exists(os.path.join(self.path, + newfilename)): + os.rename(os.path.join(self.path, filename[0]), + os.path.join(self.path, newfilename)) + self.files[number] = [newfilename, filename[1]] + continue + else: + critical_error_message_box( + translate('BiblesPlugin.UpgradeWizardForm', + 'Bible Exists'), + translate('BiblesPlugin.UpgradeWizardForm', + 'This Bible already exists. Please upgrade ' + 'a different Bible, delete the existing one or ' + 'uncheck.')) + self.verticalWidget[number].show() + self.formWidget[number].show() + self.versionNameEdit[number].setFocus() + return False + elif os.path.exists(os.path.join(self.path, + clean_filename(version_name))): + critical_error_message_box( + translate('BiblesPlugin.UpgradeWizardForm', + 'Bible Exists'), + translate('BiblesPlugin.UpgradeWizardForm', + 'This Bible already exists. Please upgrade ' + 'a different Bible, delete the existing one or ' + 'uncheck.')) + self.versionNameEdit[number].setFocus() + return False + return True + if self.currentPage() == self.progressPage: + return True + + def setDefaults(self): + """ + Set default values for the wizard pages. + """ + log.debug(u'BibleUpgrade setDefaults') + settings = QtCore.QSettings() + settings.beginGroup(self.plugin.settingsSection) + self.stop_import_flag = False + self.success.clear() + self.newbibles.clear() + self.clearScrollArea() + self.files = self.manager.old_bible_databases + self.addScrollArea() + self.retranslateUi() + self.maxBibles = len(self.files) + for number, filename in enumerate(self.files): + self.checkBox[number].setCheckState(QtCore.Qt.Checked) + oldname = filename[1] + if self.manager.exists(oldname): + self.verticalWidget[number].show() + self.formWidget[number].show() + else: + self.verticalWidget[number].hide() + self.formWidget[number].hide() + self.progressBar.show() + self.restart() + self.finishButton.setVisible(False) + self.cancelButton.setVisible(True) + settings.endGroup() + + def preWizard(self): + """ + Prepare the UI for the upgrade. + """ + OpenLPWizard.preWizard(self) + self.progressLabel.setText(translate( + 'BiblesPlugin.UpgradeWizardForm', + 'Starting upgrading Bible(s)...')) + Receiver.send_message(u'openlp_process_events') + + def performWizard(self): + """ + Perform the actual upgrade. + """ + self.include_webbible = False + proxy_server = None + if self.maxBibles == 0: + self.progressLabel.setText( + translate('BiblesPlugin.UpgradeWizardForm', 'There are no ' + 'Bibles available to upgrade.')) + self.progressBar.hide() + return + self.maxBibles = 0 + for number, file in enumerate(self.files): + if self.checkBox[number].checkState() == QtCore.Qt.Checked: + self.maxBibles += 1 + number = 0 + for biblenumber, filename in enumerate(self.files): + if self.stop_import_flag: + bible_failed = True + break + bible_failed = False + self.success[biblenumber] = False + if not self.checkBox[biblenumber].checkState() == QtCore.Qt.Checked: + continue + self.progressBar.reset() + oldbible = OldBibleDB(self.mediaItem, path=self.path, + file=filename[0]) + name = filename[1] + if name is None: + delete_file(os.path.join(self.path, filename[0])) + self.incrementProgressBar(unicode(translate( + 'BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nFailed')) % + (number + 1, self.maxBibles, name), + self.progressBar.maximum() - self.progressBar.value()) + number += 1 + continue + self.progressLabel.setText(unicode(translate( + 'BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nUpgrading ...')) % + (number + 1, self.maxBibles, name)) + if os.path.exists(os.path.join(self.path, filename[0])): + name = unicode(self.versionNameEdit[biblenumber].text()) + self.newbibles[number] = BibleDB(self.mediaItem, path=self.path, + name=name) + self.newbibles[number].register(self.plugin.upgrade_wizard) + metadata = oldbible.get_metadata() + webbible = False + meta_data = {} + for meta in metadata: + meta_data[meta[u'key']] = meta[u'value'] + if not meta[u'key'] == u'Version' and not meta[u'key'] == \ + u'dbversion': + self.newbibles[number].create_meta(meta[u'key'], + meta[u'value']) + if meta[u'key'] == u'download source': + webbible = True + self.include_webbible = True + if meta.has_key(u'proxy server'): + proxy_server = meta[u'proxy server'] + if webbible: + if meta_data[u'download source'].lower() == u'crosswalk': + handler = CWExtract(proxy_server) + elif meta_data[u'download source'].lower() == u'biblegateway': + handler = BGExtract(proxy_server) + elif meta_data[u'download source'].lower() == u'bibleserver': + handler = BSExtract(proxy_server) + books = handler.get_books_from_http(meta_data[u'download name']) + if not books: + log.error(u'Upgrading books from %s - download '\ + u'name: "%s" failed' % ( + meta_data[u'download source'], + meta_data[u'download name'])) + delete_database(self.path, clean_filename(name)) + del self.newbibles[number] + critical_error_message_box( + translate('BiblesPlugin.UpgradeWizardForm', + 'Download Error'), + translate('BiblesPlugin.UpgradeWizardForm', + 'To upgrade your Web Bibles an Internet connection is ' + 'required. If you have a working Internet connection ' + 'and this error still occurs, please report a bug.')) + self.incrementProgressBar(unicode(translate( + 'BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nFailed')) % + (number + 1, self.maxBibles, name), + self.progressBar.maximum() - self.progressBar.value()) + number += 1 + continue + bible = BiblesResourcesDB.get_webbible( + meta_data[u'download name'], + meta_data[u'download source'].lower()) + if bible and bible[u'language_id']: + language_id = bible[u'language_id'] + self.newbibles[number].create_meta(u'language_id', + language_id) + else: + language_id = self.newbibles[number].get_language(name) + if not language_id: + log.warn(u'Upgrading from "%s" failed' % filename[0]) + delete_database(self.path, clean_filename(name)) + del self.newbibles[number] + self.incrementProgressBar(unicode(translate( + 'BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nFailed')) % + (number + 1, self.maxBibles, name), + self.progressBar.maximum() - self.progressBar.value()) + number += 1 + continue + self.progressBar.setMaximum(len(books)) + for book in books: + if self.stop_import_flag: + bible_failed = True + break + self.incrementProgressBar(unicode(translate( + 'BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\n' + 'Upgrading %s ...')) % + (number + 1, self.maxBibles, name, book)) + book_ref_id = self.newbibles[number].\ + get_book_ref_id_by_name(book, len(books), language_id) + if not book_ref_id: + log.warn(u'Upgrading books from %s - download '\ + u'name: "%s" aborted by user' % ( + meta_data[u'download source'], + meta_data[u'download name'])) + delete_database(self.path, clean_filename(name)) + del self.newbibles[number] + bible_failed = True + break + book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) + db_book = self.newbibles[number].create_book(book, + book_ref_id, book_details[u'testament_id']) + # Try to import still downloaded verses + oldbook = oldbible.get_book(book) + if oldbook: + verses = oldbible.get_verses(oldbook[u'id']) + if not verses: + log.warn(u'No verses found to import for book ' + u'"%s"', book) + continue + for verse in verses: + if self.stop_import_flag: + bible_failed = True + break + self.newbibles[number].create_verse(db_book.id, + int(verse[u'chapter']), + int(verse[u'verse']), unicode(verse[u'text'])) + Receiver.send_message(u'openlp_process_events') + self.newbibles[number].session.commit() + else: + language_id = self.newbibles[number].get_object(BibleMeta, + u'language_id') + if not language_id: + language_id = self.newbibles[number].get_language(name) + if not language_id: + log.warn(u'Upgrading books from "%s" failed' % name) + delete_database(self.path, clean_filename(name)) + del self.newbibles[number] + self.incrementProgressBar(unicode(translate( + 'BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nFailed')) % + (number + 1, self.maxBibles, name), + self.progressBar.maximum() - self.progressBar.value()) + number += 1 + continue + books = oldbible.get_books() + self.progressBar.setMaximum(len(books)) + for book in books: + if self.stop_import_flag: + bible_failed = True + break + self.incrementProgressBar(unicode(translate( + 'BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\n' + 'Upgrading %s ...')) % + (number + 1, self.maxBibles, name, book[u'name'])) + book_ref_id = self.newbibles[number].\ + get_book_ref_id_by_name(book[u'name'], len(books), + language_id) + if not book_ref_id: + log.warn(u'Upgrading books from %s " '\ + 'failed - aborted by user' % name) + delete_database(self.path, clean_filename(name)) + del self.newbibles[number] + bible_failed = True + break + book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) + db_book = self.newbibles[number].create_book(book[u'name'], + book_ref_id, book_details[u'testament_id']) + verses = oldbible.get_verses(book[u'id']) + if not verses: + log.warn(u'No verses found to import for book ' + u'"%s"', book[u'name']) + self.newbibles[number].delete_book(db_book) + continue + for verse in verses: + if self.stop_import_flag: + bible_failed = True + break + self.newbibles[number].create_verse(db_book.id, + int(verse[u'chapter']), + int(verse[u'verse']), unicode(verse[u'text'])) + Receiver.send_message(u'openlp_process_events') + self.newbibles[number].session.commit() + if not bible_failed: + self.newbibles[number].create_meta(u'Version', name) + delete_file(os.path.join(self.path, filename[0])) + self.incrementProgressBar(unicode(translate( + 'BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\n' + 'Done')) % + (number + 1, self.maxBibles, name)) + self.success[biblenumber] = True + else: + self.incrementProgressBar(unicode(translate( + 'BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nFailed')) % + (number + 1, self.maxBibles, name), + self.progressBar.maximum() - self.progressBar.value()) + delete_database(self.path, clean_filename(name)) + number += 1 + + def postWizard(self): + """ + Clean up the UI after the import has finished. + """ + successful_import = 0 + failed_import = 0 + for number, filename in enumerate(self.files): + if number in self.success and self.success[number] == True: + successful_import += 1 + elif self.checkBox[number].checkState() == QtCore.Qt.Checked: + failed_import += 1 + if failed_import > 0: + failed_import_text = unicode(translate( + 'BiblesPlugin.UpgradeWizardForm', + ', %s failed')) % failed_import + else: + failed_import_text = u'' + if successful_import > 0: + if self.include_webbible: + self.progressLabel.setText(unicode( + translate('BiblesPlugin.UpgradeWizardForm', 'Upgrading ' + 'Bible(s): %s successful%s\nPlease note, that verses from ' + 'Web Bibles will be downloaded\non demand and so an ' + 'Internet connection is required.')) % + (successful_import, failed_import_text)) + else: + self.progressLabel.setText(unicode( + translate('BiblesPlugin.UpgradeWizardForm', 'Upgrading ' + 'Bible(s): %s successful%s')) % (successful_import, + failed_import_text)) + else: + self.progressLabel.setText( + translate('BiblesPlugin.UpgradeWizardForm', 'Upgrade ' + 'failed.')) + OpenLPWizard.postWizard(self) diff --git a/openlp/plugins/bibles/forms/booknamedialog.py b/openlp/plugins/bibles/forms/booknamedialog.py new file mode 100644 index 000000000..e9211b3d5 --- /dev/null +++ b/openlp/plugins/bibles/forms/booknamedialog.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2011 Raoul Snyman # +# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Meinert Jordan, Armin Köhler, Andreas Preikschat, # +# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon # +# Tibble, Carsten Tinggaard, Frode Woldsund # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import translate + +class Ui_BookNameDialog(object): + def setupUi(self, bookNameDialog): + bookNameDialog.setObjectName(u'bookNameDialog') + bookNameDialog.resize(400, 271) + self.bookNameLayout = QtGui.QVBoxLayout(bookNameDialog) + self.bookNameLayout.setSpacing(8) + self.bookNameLayout.setMargin(8) + self.bookNameLayout.setObjectName(u'bookNameLayout') + self.infoLabel = QtGui.QLabel(bookNameDialog) + self.infoLabel.setWordWrap(True) + self.infoLabel.setObjectName(u'infoLabel') + self.bookNameLayout.addWidget(self.infoLabel) + self.correspondingLayout = QtGui.QGridLayout() + self.correspondingLayout.setColumnStretch(1, 1) + self.correspondingLayout.setSpacing(8) + self.correspondingLayout.setObjectName(u'correspondingLayout') + self.currentLabel = QtGui.QLabel(bookNameDialog) + self.currentLabel.setObjectName(u'currentLabel') + self.correspondingLayout.addWidget(self.currentLabel, 0, 0, 1, 1) + self.currentBookLabel = QtGui.QLabel(bookNameDialog) + self.currentBookLabel.setObjectName(u'currentBookLabel') + self.correspondingLayout.addWidget(self.currentBookLabel, 0, 1, 1, 1) + self.correspondingLabel = QtGui.QLabel(bookNameDialog) + self.correspondingLabel.setObjectName(u'correspondingLabel') + self.correspondingLayout.addWidget( + self.correspondingLabel, 1, 0, 1, 1) + self.correspondingComboBox = QtGui.QComboBox(bookNameDialog) + self.correspondingComboBox.setObjectName(u'correspondingComboBox') + self.correspondingLayout.addWidget( + self.correspondingComboBox, 1, 1, 1, 1) + self.bookNameLayout.addLayout(self.correspondingLayout) + self.optionsGroupBox = QtGui.QGroupBox(bookNameDialog) + self.optionsGroupBox.setObjectName(u'optionsGroupBox') + self.optionsLayout = QtGui.QVBoxLayout(self.optionsGroupBox) + self.optionsLayout.setSpacing(8) + self.optionsLayout.setMargin(8) + self.optionsLayout.setObjectName(u'optionsLayout') + self.oldTestamentCheckBox = QtGui.QCheckBox(self.optionsGroupBox) + self.oldTestamentCheckBox.setObjectName(u'oldTestamentCheckBox') + self.oldTestamentCheckBox.setCheckState(QtCore.Qt.Checked) + self.optionsLayout.addWidget(self.oldTestamentCheckBox) + self.newTestamentCheckBox = QtGui.QCheckBox(self.optionsGroupBox) + self.newTestamentCheckBox.setObjectName(u'newTestamentCheckBox') + self.newTestamentCheckBox.setCheckState(QtCore.Qt.Checked) + self.optionsLayout.addWidget(self.newTestamentCheckBox) + self.apocryphaCheckBox = QtGui.QCheckBox(self.optionsGroupBox) + self.apocryphaCheckBox.setObjectName(u'apocryphaCheckBox') + self.apocryphaCheckBox.setCheckState(QtCore.Qt.Checked) + self.optionsLayout.addWidget(self.apocryphaCheckBox) + self.bookNameLayout.addWidget(self.optionsGroupBox) + self.buttonBox = QtGui.QDialogButtonBox(bookNameDialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons( + QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName(u'buttonBox') + self.bookNameLayout.addWidget(self.buttonBox) + + self.retranslateUi(bookNameDialog) + QtCore.QObject.connect( + self.buttonBox, QtCore.SIGNAL(u'accepted()'), + bookNameDialog.accept) + QtCore.QObject.connect( + self.buttonBox, QtCore.SIGNAL(u'rejected()'), + bookNameDialog.reject) + QtCore.QMetaObject.connectSlotsByName(bookNameDialog) + + def retranslateUi(self, bookNameDialog): + bookNameDialog.setWindowTitle(translate('BiblesPlugin.BookNameDialog', + 'Select Book Name')) + self.infoLabel.setText(translate('BiblesPlugin.BookNameDialog', + 'The following book name cannot be matched up internally. Please ' + 'select the corresponding English name from the list.')) + self.currentLabel.setText(translate('BiblesPlugin.BookNameDialog', + 'Current name:')) + self.correspondingLabel.setText(translate( + 'BiblesPlugin.BookNameDialog', 'Corresponding name:')) + self.optionsGroupBox.setTitle(translate('BiblesPlugin.BookNameDialog', + 'Show Books From')) + self.oldTestamentCheckBox.setText(translate( + 'BiblesPlugin.BookNameDialog', 'Old Testament')) + self.newTestamentCheckBox.setText(translate( + 'BiblesPlugin.BookNameDialog', 'New Testament')) + self.apocryphaCheckBox.setText(translate('BiblesPlugin.BookNameDialog', + 'Apocrypha')) diff --git a/openlp/plugins/bibles/forms/booknameform.py b/openlp/plugins/bibles/forms/booknameform.py new file mode 100644 index 000000000..b07f28bf1 --- /dev/null +++ b/openlp/plugins/bibles/forms/booknameform.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2011 Raoul Snyman # +# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Meinert Jordan, Armin Köhler, Andreas Preikschat, # +# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon # +# Tibble, Carsten Tinggaard, Frode Woldsund # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### + +""" +Module implementing BookNameForm. +""" +import logging + +from PyQt4.QtGui import QDialog +from PyQt4 import QtCore + +from openlp.core.lib import translate +from openlp.core.lib.ui import critical_error_message_box +from openlp.plugins.bibles.forms.booknamedialog import \ + Ui_BookNameDialog +from openlp.plugins.bibles.lib.db import BiblesResourcesDB + +log = logging.getLogger(__name__) + +class BookNameForm(QDialog, Ui_BookNameDialog): + """ + Class to manage a dialog which help the user to refer a book name a + to a english book name + """ + log.info(u'BookNameForm loaded') + + def __init__(self, parent = None): + """ + Constructor + """ + QDialog.__init__(self, parent) + self.setupUi(self) + self.customSignals() + + def customSignals(self): + """ + Set up the signals used in the booknameform. + """ + QtCore.QObject.connect(self.oldTestamentCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), + self.onCheckBoxIndexChanged) + QtCore.QObject.connect(self.newTestamentCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), + self.onCheckBoxIndexChanged) + QtCore.QObject.connect(self.apocryphaCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), + self.onCheckBoxIndexChanged) + + def onCheckBoxIndexChanged(self, index): + """ + Reload Combobox if CheckBox state has changed + """ + self.reloadComboBox() + + def reloadComboBox(self): + """ + Reload the Combobox items + """ + self.correspondingComboBox.clear() + items = BiblesResourcesDB.get_books() + for item in items: + addBook = True + for book in self.books: + if book.book_reference_id == item[u'id']: + addBook = False + break + if self.oldTestamentCheckBox.checkState() == QtCore.Qt.Unchecked \ + and item[u'testament_id'] == 1: + addBook = False + elif self.newTestamentCheckBox.checkState() == QtCore.Qt.Unchecked \ + and item[u'testament_id'] == 2: + addBook = False + elif self.apocryphaCheckBox.checkState() == QtCore.Qt.Unchecked \ + and item[u'testament_id'] == 3: + addBook = False + if addBook: + self.correspondingComboBox.addItem(item[u'name']) + + def exec_(self, name, books, maxbooks): + self.books = books + log.debug(maxbooks) + if maxbooks <= 27: + self.oldTestamentCheckBox.setCheckState(QtCore.Qt.Unchecked) + self.apocryphaCheckBox.setCheckState(QtCore.Qt.Unchecked) + elif maxbooks <= 66: + self.apocryphaCheckBox.setCheckState(QtCore.Qt.Unchecked) + self.reloadComboBox() + self.currentBookLabel.setText(unicode(name)) + self.correspondingComboBox.setFocus() + return QDialog.exec_(self) + + def accept(self): + if self.correspondingComboBox.currentText() == u'': + critical_error_message_box( + message=translate('BiblesPlugin.BookNameForm', + 'You need to select a book.')) + self.correspondingComboBox.setFocus() + return False + else: + return QDialog.accept(self) diff --git a/openlp/plugins/bibles/forms/languagedialog.py b/openlp/plugins/bibles/forms/languagedialog.py new file mode 100644 index 000000000..5c1325a54 --- /dev/null +++ b/openlp/plugins/bibles/forms/languagedialog.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2011 Raoul Snyman # +# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Meinert Jordan, Armin Köhler, Andreas Preikschat, # +# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon # +# Tibble, Carsten Tinggaard, Frode Woldsund # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import translate + +class Ui_LanguageDialog(object): + def setupUi(self, languageDialog): + languageDialog.setObjectName(u'languageDialog') + languageDialog.resize(400, 165) + self.languageLayout = QtGui.QVBoxLayout(languageDialog) + self.languageLayout.setSpacing(8) + self.languageLayout.setMargin(8) + self.languageLayout.setObjectName(u'languageLayout') + self.bibleLabel = QtGui.QLabel(languageDialog) + self.bibleLabel.setObjectName(u'bibleLabel') + self.languageLayout.addWidget(self.bibleLabel) + self.infoLabel = QtGui.QLabel(languageDialog) + self.infoLabel.setWordWrap(True) + self.infoLabel.setObjectName(u'infoLabel') + self.languageLayout.addWidget(self.infoLabel) + self.languageHBoxLayout = QtGui.QHBoxLayout() + self.languageHBoxLayout.setSpacing(8) + self.languageHBoxLayout.setObjectName(u'languageHBoxLayout') + self.languageLabel = QtGui.QLabel(languageDialog) + self.languageLabel.setObjectName(u'languageLabel') + self.languageHBoxLayout.addWidget(self.languageLabel) + self.languageComboBox = QtGui.QComboBox(languageDialog) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, + QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth( + self.languageComboBox.sizePolicy().hasHeightForWidth()) + self.languageComboBox.setSizePolicy(sizePolicy) + self.languageComboBox.setObjectName(u'languageComboBox') + self.languageHBoxLayout.addWidget(self.languageComboBox) + self.languageLayout.addLayout(self.languageHBoxLayout) + self.buttonBox = QtGui.QDialogButtonBox(languageDialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel| + QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName(u'buttonBox') + self.languageLayout.addWidget(self.buttonBox) + + self.retranslateUi(languageDialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'accepted()'), + languageDialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'), + languageDialog.reject) + + def retranslateUi(self, languageDialog): + languageDialog.setWindowTitle( + translate('BiblesPlugin.LanguageDialog', 'Select Language')) + self.bibleLabel.setText(translate('BiblesPlugin.LanguageDialog', '')) + self.infoLabel.setText(translate('BiblesPlugin.LanguageDialog', + 'OpenLP is unable to determine the language of this translation ' + 'of the Bible. Please select the language from the list below.')) + self.languageLabel.setText(translate('BiblesPlugin.LanguageDialog', + 'Language:')) diff --git a/openlp/plugins/bibles/forms/languageform.py b/openlp/plugins/bibles/forms/languageform.py new file mode 100644 index 000000000..477c7ee1e --- /dev/null +++ b/openlp/plugins/bibles/forms/languageform.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2011 Raoul Snyman # +# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Meinert Jordan, Armin Köhler, Andreas Preikschat, # +# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon # +# Tibble, Carsten Tinggaard, Frode Woldsund # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### + +""" +Module implementing LanguageForm. +""" +import logging + +from PyQt4.QtGui import QDialog + +from openlp.core.lib import translate +from openlp.core.lib.ui import critical_error_message_box +from openlp.plugins.bibles.forms.languagedialog import \ + Ui_LanguageDialog +from openlp.plugins.bibles.lib.db import BiblesResourcesDB + +log = logging.getLogger(__name__) + +class LanguageForm(QDialog, Ui_LanguageDialog): + """ + Class to manage a dialog which ask the user for a language. + """ + log.info(u'LanguageForm loaded') + + def __init__(self, parent = None): + """ + Constructor + """ + QDialog.__init__(self, parent) + self.setupUi(self) + + def exec_(self, bible_name): + self.languageComboBox.addItem(u'') + if bible_name: + self.bibleLabel.setText(unicode(bible_name)) + items = BiblesResourcesDB.get_languages() + for item in items: + self.languageComboBox.addItem(item[u'name']) + return QDialog.exec_(self) + + def accept(self): + if self.languageComboBox.currentText() == u'': + critical_error_message_box( + message=translate('BiblesPlugin.LanguageForm', + 'You need to choose a language.')) + self.languageComboBox.setFocus() + return False + else: + return QDialog.accept(self) diff --git a/openlp/plugins/bibles/lib/csvbible.py b/openlp/plugins/bibles/lib/csvbible.py index 37764108a..fe72f1bfd 100644 --- a/openlp/plugins/bibles/lib/csvbible.py +++ b/openlp/plugins/bibles/lib/csvbible.py @@ -71,7 +71,7 @@ import chardet import csv from openlp.core.lib import Receiver, translate -from openlp.plugins.bibles.lib.db import BibleDB, Testament +from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB log = logging.getLogger(__name__) @@ -79,6 +79,8 @@ class CSVBible(BibleDB): """ This class provides a specialisation for importing of CSV Bibles. """ + log.info(u'CSVBible loaded') + def __init__(self, parent, **kwargs): """ Loads a Bible from a set of CVS files. @@ -87,48 +89,10 @@ class CSVBible(BibleDB): """ log.info(self.__class__.__name__) BibleDB.__init__(self, parent, **kwargs) - try: - self.testamentsfile = kwargs[u'testamentsfile'] - except KeyError: - self.testamentsfile = None self.booksfile = kwargs[u'booksfile'] self.versesfile = kwargs[u'versefile'] - def setup_testaments(self): - """ - Overrides parent method so we can handle importing a testament file. - """ - if self.testamentsfile: - self.wizard.progressBar.setMinimum(0) - self.wizard.progressBar.setMaximum(2) - self.wizard.progressBar.setValue(0) - testaments_file = None - try: - details = get_file_encoding(self.testamentsfile) - testaments_file = open(self.testamentsfile, 'rb') - testaments_reader = csv.reader(testaments_file, delimiter=',', - quotechar='"') - for line in testaments_reader: - if self.stop_import_flag: - break - self.wizard.incrementProgressBar(unicode( - translate('BibleDB.Wizard', - 'Importing testaments... %s')) % - unicode(line[1], details['encoding']), 0) - self.save_object(Testament.populate( - name=unicode(line[1], details['encoding']))) - Receiver.send_message(u'openlp_process_events') - except (IOError, IndexError): - log.exception(u'Loading testaments from file failed') - finally: - if testaments_file: - testaments_file.close() - self.wizard.incrementProgressBar(unicode(translate( - 'BibleDB.Wizard', 'Importing testaments... done.')), 2) - else: - BibleDB.setup_testaments(self) - - def do_import(self): + def do_import(self, bible_name=None): """ Import the bible books and verses. """ @@ -136,6 +100,10 @@ class CSVBible(BibleDB): self.wizard.progressBar.setMinimum(0) self.wizard.progressBar.setMaximum(66) success = True + language_id = self.get_language(bible_name) + if not language_id: + log.exception(u'Importing books from "%s" failed' % self.filename) + return False books_file = None book_list = {} # Populate the Tables @@ -149,8 +117,15 @@ class CSVBible(BibleDB): self.wizard.incrementProgressBar(unicode( translate('BibleDB.Wizard', 'Importing books... %s')) % unicode(line[2], details['encoding'])) + book_ref_id = self.get_book_ref_id_by_name( + unicode(line[2], details['encoding']), 67, language_id) + if not book_ref_id: + log.exception(u'Importing books from "%s" '\ + 'failed' % self.booksfile) + return False + book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) self.create_book(unicode(line[2], details['encoding']), - unicode(line[3], details['encoding']), int(line[1])) + book_ref_id, book_details[u'testament_id']) book_list[int(line[0])] = unicode(line[2], details['encoding']) Receiver.send_message(u'openlp_process_events') except (IOError, IndexError): diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index c896528ea..41dc947f9 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -27,16 +27,19 @@ import logging import chardet +import os import re +import sqlite3 from PyQt4 import QtCore from sqlalchemy import Column, ForeignKey, or_, Table, types from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm.exc import UnmappedClassError -from openlp.core.lib import Receiver, translate +from openlp.core.lib import Receiver, translate, check_directory_exists from openlp.core.lib.db import BaseModel, init_db, Manager from openlp.core.lib.ui import critical_error_message_box +from openlp.core.utils import AppLocation log = logging.getLogger(__name__) @@ -47,13 +50,6 @@ class BibleMeta(BaseModel): pass -class Testament(BaseModel): - """ - Bible Testaments - """ - pass - - class Book(BaseModel): """ Song model @@ -67,6 +63,18 @@ class Verse(BaseModel): """ pass +def clean_filename(filename): + """ + Clean up the version name of the Bible and convert it into a valid + file name. + + ``filename`` + The "dirty" file name or version name. + """ + if not isinstance(filename, unicode): + filename = unicode(filename, u'utf-8') + filename = re.sub(r'[^\w]+', u'_', filename).strip(u'_') + return filename + u'.sqlite' def init_schema(url): """ @@ -81,19 +89,17 @@ def init_schema(url): Column(u'key', types.Unicode(255), primary_key=True, index=True), Column(u'value', types.Unicode(255)), ) - testament_table = Table(u'testament', metadata, - Column(u'id', types.Integer, primary_key=True), - Column(u'name', types.Unicode(50)), - ) + book_table = Table(u'book', metadata, Column(u'id', types.Integer, primary_key=True), - Column(u'testament_id', types.Integer, ForeignKey(u'testament.id')), + Column(u'book_reference_id', types.Integer, index=True), + Column(u'testament_reference_id', types.Integer), Column(u'name', types.Unicode(50), index=True), - Column(u'abbreviation', types.Unicode(5), index=True), ) verse_table = Table(u'verse', metadata, Column(u'id', types.Integer, primary_key=True, index=True), - Column(u'book_id', types.Integer, ForeignKey(u'book.id'), index=True), + Column(u'book_id', types.Integer, ForeignKey( + u'book.id'), index=True), Column(u'chapter', types.Integer, index=True), Column(u'verse', types.Integer, index=True), Column(u'text', types.UnicodeText, index=True), @@ -103,11 +109,6 @@ def init_schema(url): class_mapper(BibleMeta) except UnmappedClassError: mapper(BibleMeta, meta_table) - try: - class_mapper(Testament) - except UnmappedClassError: - mapper(Testament, testament_table, - properties={'books': relation(Book, backref='testament')}) try: class_mapper(Book) except UnmappedClassError: @@ -129,6 +130,7 @@ class BibleDB(QtCore.QObject, Manager): methods, but benefit from the database methods in here via inheritance, rather than depending on yet another object. """ + log.info(u'BibleDB loaded') def __init__(self, parent, **kwargs): """ @@ -156,12 +158,14 @@ class BibleDB(QtCore.QObject, Manager): self.name = kwargs[u'name'] if not isinstance(self.name, unicode): self.name = unicode(self.name, u'utf-8') - self.file = self.clean_filename(self.name) + self.file = clean_filename(self.name) if u'file' in kwargs: self.file = kwargs[u'file'] Manager.__init__(self, u'bibles', init_schema, self.file) if u'file' in kwargs: self.get_name() + if u'path' in kwargs: + self.path = kwargs[u'path'] self.wizard = None QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'openlp_stop_wizard'), self.stop_import) @@ -181,19 +185,6 @@ class BibleDB(QtCore.QObject, Manager): self.name = version_name.value if version_name else None return self.name - def clean_filename(self, old_filename): - """ - Clean up the version name of the Bible and convert it into a valid - file name. - - ``old_filename`` - The "dirty" file name or version name. - """ - if not isinstance(old_filename, unicode): - old_filename = unicode(old_filename, u'utf-8') - old_filename = re.sub(r'[^\w]+', u'_', old_filename).strip(u'_') - return old_filename + u'.sqlite' - def register(self, wizard): """ This method basically just initialialises the database. It is called @@ -206,36 +197,40 @@ class BibleDB(QtCore.QObject, Manager): """ self.wizard = wizard self.create_meta(u'dbversion', u'2') - self.setup_testaments() return self.name - def setup_testaments(self): - """ - Initialise the testaments section of a bible with suitable defaults. - """ - self.save_object(Testament.populate(name=u'Old Testament')) - self.save_object(Testament.populate(name=u'New Testament')) - self.save_object(Testament.populate(name=u'Apocrypha')) - - def create_book(self, name, abbrev, testament=1): + def create_book(self, name, bk_ref_id, testament=1): """ Add a book to the database. ``name`` The name of the book. - ``abbrev`` - The abbreviation of the book. + ``bk_ref_id`` + The book_reference_id from bibles_resources.sqlite of the book. ``testament`` - *Defaults to 1.* The id of the testament this book belongs to. + *Defaults to 1.* The testament_reference_id from + bibles_resources.sqlite of the testament this book belongs to. """ - log.debug(u'create_book %s,%s', name, abbrev) - book = Book.populate(name=name, abbreviation=abbrev, - testament_id=testament) + log.debug(u'BibleDB.create_book("%s", "%s")', name, bk_ref_id) + book = Book.populate(name=name, book_reference_id=bk_ref_id, + testament_reference_id=testament) self.save_object(book) return book + def delete_book(self, db_book): + """ + Delete a book from the database. + + ``db_book`` + The book object. + """ + log.debug(u'BibleDB.delete_book("%s")', db_book.name) + if self.delete_object(Book, db_book.id): + return True + return False + def create_chapter(self, book_id, chapter, textlist): """ Add a chapter and its verses to a book. @@ -250,7 +245,7 @@ class BibleDB(QtCore.QObject, Manager): A dict of the verses to be inserted. The key is the verse number, and the value is the verse text. """ - log.debug(u'create_chapter %s,%s', book_id, chapter) + log.debug(u'BibleDBcreate_chapter("%s", "%s")', book_id, chapter) # Text list has book and chapter as first two elements of the array. for verse_number, verse_text in textlist.iteritems(): verse = Verse.populate( @@ -300,7 +295,9 @@ class BibleDB(QtCore.QObject, Manager): ``value`` The value for this instance. """ - log.debug(u'save_meta %s/%s', key, value) + if not isinstance(value, unicode): + value = unicode(value) + log.debug(u'BibleDB.save_meta("%s/%s")', key, value) self.save_object(BibleMeta.populate(key=key, value=value)) def get_book(self, book): @@ -310,20 +307,60 @@ class BibleDB(QtCore.QObject, Manager): ``book`` The name of the book to return. """ - log.debug(u'BibleDb.get_book("%s")', book) - db_book = self.get_object_filtered(Book, Book.name.like(book + u'%')) - if db_book is None: - db_book = self.get_object_filtered(Book, - Book.abbreviation.like(book + u'%')) - return db_book + log.debug(u'BibleDB.get_book("%s")', book) + return self.get_object_filtered(Book, Book.name.like(book + u'%')) def get_books(self): """ A wrapper so both local and web bibles have a get_books() method that manager can call. Used in the media manager advanced search tab. """ + log.debug(u'BibleDB.get_books()') return self.get_all_objects(Book, order_by_ref=Book.id) + def get_book_by_book_ref_id(self, id): + """ + Return a book object from the database. + + ``id`` + The reference id of the book to return. + """ + log.debug(u'BibleDB.get_book_by_book_ref_id("%s")', id) + return self.get_object_filtered(Book, Book.book_reference_id.like(id)) + + def get_book_ref_id_by_name(self, book, maxbooks, language_id=None): + log.debug(u'BibleDB.get_book_ref_id_by_name:("%s", "%s")', book, + language_id) + if BiblesResourcesDB.get_book(book, True): + book_temp = BiblesResourcesDB.get_book(book, True) + book_id = book_temp[u'id'] + elif BiblesResourcesDB.get_alternative_book_name(book): + book_id = BiblesResourcesDB.get_alternative_book_name(book) + elif AlternativeBookNamesDB.get_book_reference_id(book): + book_id = AlternativeBookNamesDB.get_book_reference_id(book) + else: + from openlp.plugins.bibles.forms import BookNameForm + book_ref = None + book_name = BookNameForm(self.wizard) + if book_name.exec_(book, self.get_books(), maxbooks): + book_ref = unicode( + book_name.correspondingComboBox.currentText()) + if not book_ref: + return None + else: + book_temp = BiblesResourcesDB.get_book(book_ref) + if book_temp: + book_id = book_temp[u'id'] + else: + return None + if book_id: + AlternativeBookNamesDB.create_alternative_book_name( + book, book_id, language_id) + if book_id: + return book_id + else: + return None + def get_verses(self, reference_list, show_error=True): """ This is probably the most used function. It retrieves the list of @@ -333,24 +370,25 @@ class BibleDB(QtCore.QObject, Manager): This is the list of references the media manager item wants. It is a list of tuples, with the following format:: - (book, chapter, start_verse, end_verse) + (book_reference_id, chapter, start_verse, end_verse) Therefore, when you are looking for multiple items, simply break them up into references like this, bundle them into a list. This function then runs through the list, and returns an amalgamated list of ``Verse`` objects. For example:: - [(u'Genesis', 1, 1, 1), (u'Genesis', 2, 2, 3)] + [(u'35', 1, 1, 1), (u'35', 2, 2, 3)] """ - log.debug(u'BibleDB.get_verses: %s', reference_list) + log.debug(u'BibleDB.get_verses("%s")', reference_list) verse_list = [] - for book, chapter, start_verse, end_verse in reference_list: - db_book = self.get_book(book) + book_error = False + for book_id, chapter, start_verse, end_verse in reference_list: + db_book = self.get_book_by_book_ref_id(book_id) if db_book: - book = db_book.name - log.debug(u'Book name corrected to "%s"', book) + book_id = db_book.book_reference_id + log.debug(u'Book name corrected to "%s"', db_book.name) if end_verse == -1: - end_verse = self.get_verse_count(book, chapter) + end_verse = self.get_verse_count(book_id, chapter) verses = self.session.query(Verse)\ .filter_by(book_id=db_book.id)\ .filter_by(chapter=chapter)\ @@ -360,13 +398,14 @@ class BibleDB(QtCore.QObject, Manager): .all() verse_list.extend(verses) else: - log.debug(u'OpenLP failed to find book %s', book) - if show_error: - critical_error_message_box( - translate('BiblesPlugin', 'No Book Found'), - translate('BiblesPlugin', 'No matching book ' - 'could be found in this Bible. Check that you ' - 'have spelled the name of the book correctly.')) + log.debug(u'OpenLP failed to find book with id "%s"', book_id) + book_error = True + if book_error and show_error: + critical_error_message_box( + translate('BiblesPlugin', 'No Book Found'), + translate('BiblesPlugin', 'No matching book ' + 'could be found in this Bible. Check that you ' + 'have spelled the name of the book correctly.')) return verse_list def verse_search(self, text): @@ -399,18 +438,18 @@ class BibleDB(QtCore.QObject, Manager): Return the number of chapters in a book. ``book`` - The book to get the chapter count for. + The book object to get the chapter count for. """ - log.debug(u'BibleDB.get_chapter_count("%s")', book) + log.debug(u'BibleDB.get_chapter_count("%s")', book.name) count = self.session.query(Verse.chapter).join(Book)\ - .filter(Book.name == book)\ + .filter(Book.book_reference_id==book.book_reference_id)\ .distinct().count() if not count: return 0 else: return count - def get_verse_count(self, book, chapter): + def get_verse_count(self, book_id, chapter): """ Return the number of verses in a chapter. @@ -420,16 +459,49 @@ class BibleDB(QtCore.QObject, Manager): ``chapter`` The chapter to get the verse count for. """ - log.debug(u'BibleDB.get_verse_count("%s", %s)', book, chapter) + log.debug(u'BibleDB.get_verse_count("%s", "%s")', book_id, chapter) count = self.session.query(Verse).join(Book)\ - .filter(Book.name == book)\ - .filter(Verse.chapter == chapter)\ + .filter(Book.book_reference_id==book_id)\ + .filter(Verse.chapter==chapter)\ .count() if not count: return 0 else: return count + def get_language(self, bible_name=None): + """ + If no language is given it calls a dialog window where the user could + select the bible language. + Return the language id of a bible. + + ``book`` + The language the bible is. + """ + log.debug(u'BibleDB.get_language()') + from openlp.plugins.bibles.forms import LanguageForm + language = None + language_form = LanguageForm(self.wizard) + if language_form.exec_(bible_name): + language = unicode(language_form.languageComboBox.currentText()) + if not language: + return False + language = BiblesResourcesDB.get_language(language) + language_id = language[u'id'] + self.create_meta(u'language_id', language_id) + return language_id + + def is_old_database(self): + """ + Returns ``True`` if it is a bible database, which has been created + prior to 1.9.6. + """ + try: + columns = self.session.query(Book).all() + except: + return True + return False + def dump_bible(self): """ Utility debugging method to dump the contents of a bible. @@ -441,3 +513,591 @@ class BibleDB(QtCore.QObject, Manager): log.debug(u'...............................Verses ') verses = self.session.query(Verse).all() log.debug(verses) + + +class BiblesResourcesDB(QtCore.QObject, Manager): + """ + This class represents the database-bound Bible Resources. It provide + some resources which are used in the Bibles plugin. + A wrapper class around a small SQLite database which contains the download + resources, a biblelist from the different download resources, the books, + chapter counts and verse counts for the web download Bibles, a language + reference, the testament reference and some alternative book names. This + class contains a singleton "cursor" so that only one connection to the + SQLite database is ever used. + """ + cursor = None + + @staticmethod + def get_cursor(): + """ + Return the cursor object. Instantiate one if it doesn't exist yet. + """ + if BiblesResourcesDB.cursor is None: + filepath = os.path.join( + AppLocation.get_directory(AppLocation.PluginsDir), u'bibles', + u'resources', u'bibles_resources.sqlite') + conn = sqlite3.connect(filepath) + BiblesResourcesDB.cursor = conn.cursor() + return BiblesResourcesDB.cursor + + @staticmethod + def run_sql(query, parameters=()): + """ + Run an SQL query on the database, returning the results. + + ``query`` + The actual SQL query to run. + + ``parameters`` + Any variable parameters to add to the query. + """ + cursor = BiblesResourcesDB.get_cursor() + cursor.execute(query, parameters) + return cursor.fetchall() + + @staticmethod + def get_books(): + """ + Return a list of all the books of the Bible. + """ + log.debug(u'BiblesResourcesDB.get_books()') + books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, ' + u'abbreviation, chapters FROM book_reference ORDER BY id') + return [ + { + u'id': book[0], + u'testament_id': book[1], + u'name': unicode(book[2]), + u'abbreviation': unicode(book[3]), + u'chapters': book[4] + } + for book in books + ] + + @staticmethod + def get_book(name, lower=False): + """ + Return a book by name or abbreviation. + + ``name`` + The name or abbreviation of the book. + + ``lower`` + True if the comparsion should be only lowercase + """ + log.debug(u'BiblesResourcesDB.get_book("%s")', name) + if not isinstance(name, unicode): + name = unicode(name) + if lower: + books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, ' + u'abbreviation, chapters FROM book_reference WHERE ' + u'LOWER(name) = ? OR LOWER(abbreviation) = ?', + (name.lower(), name.lower())) + else: + books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, ' + u'abbreviation, chapters FROM book_reference WHERE name = ?' + u' OR abbreviation = ?', (name, name)) + if books: + return { + u'id': books[0][0], + u'testament_id': books[0][1], + u'name': unicode(books[0][2]), + u'abbreviation': unicode(books[0][3]), + u'chapters': books[0][4] + } + else: + return None + + @staticmethod + def get_book_by_id(id): + """ + Return a book by id. + + ``id`` + The id of the book. + """ + log.debug(u'BiblesResourcesDB.get_book_by_id("%s")', id) + if not isinstance(id, int): + id = int(id) + books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, ' + u'abbreviation, chapters FROM book_reference WHERE id = ?', + (id, )) + if books: + return { + u'id': books[0][0], + u'testament_id': books[0][1], + u'name': unicode(books[0][2]), + u'abbreviation': unicode(books[0][3]), + u'chapters': books[0][4] + } + else: + return None + + @staticmethod + def get_chapter(book_id, chapter): + """ + Return the chapter details for a specific chapter of a book. + + ``book_id`` + The id of a book. + + ``chapter`` + The chapter number. + """ + log.debug(u'BiblesResourcesDB.get_chapter("%s", "%s")', book_id, + chapter) + if not isinstance(chapter, int): + chapter = int(chapter) + chapters = BiblesResourcesDB.run_sql(u'SELECT id, book_reference_id, ' + u'chapter, verse_count FROM chapters WHERE book_reference_id = ?', + (book_id,)) + if chapters: + return { + u'id': chapters[chapter-1][0], + u'book_reference_id': chapters[chapter-1][1], + u'chapter': chapters[chapter-1][2], + u'verse_count': chapters[chapter-1][3] + } + else: + return None + + @staticmethod + def get_chapter_count(book_id): + """ + Return the number of chapters in a book. + + ``book_id`` + The id of the book. + """ + log.debug(u'BiblesResourcesDB.get_chapter_count("%s")', book_id) + details = BiblesResourcesDB.get_book_by_id(book_id) + if details: + return details[u'chapters'] + return 0 + + @staticmethod + def get_verse_count(book_id, chapter): + """ + Return the number of verses in a chapter. + + ``book`` + The id of the book. + + ``chapter`` + The number of the chapter. + """ + log.debug(u'BiblesResourcesDB.get_verse_count("%s", "%s")', book_id, + chapter) + details = BiblesResourcesDB.get_chapter(book_id, chapter) + if details: + return details[u'verse_count'] + return 0 + + @staticmethod + def get_download_source(source): + """ + Return a download_source_id by source. + + ``name`` + The name or abbreviation of the book. + """ + log.debug(u'BiblesResourcesDB.get_download_source("%s")', source) + if not isinstance(source, unicode): + source = unicode(source) + source = source.title() + dl_source = BiblesResourcesDB.run_sql(u'SELECT id, source FROM ' + u'download_source WHERE source = ?', (source.lower(),)) + if dl_source: + return { + u'id': dl_source[0][0], + u'source': dl_source[0][1] + } + else: + return None + + @staticmethod + def get_webbibles(source): + """ + Return the bibles a webbible provide for download. + + ``source`` + The source of the webbible. + """ + log.debug(u'BiblesResourcesDB.get_webbibles("%s")', source) + if not isinstance(source, unicode): + source = unicode(source) + source = BiblesResourcesDB.get_download_source(source) + bibles = BiblesResourcesDB.run_sql(u'SELECT id, name, abbreviation, ' + u'language_id, download_source_id FROM webbibles WHERE ' + u'download_source_id = ?', (source[u'id'],)) + if bibles: + return [ + { + u'id': bible[0], + u'name': bible[1], + u'abbreviation': bible[2], + u'language_id': bible[3], + u'download_source_id': bible[4] + } + for bible in bibles + ] + else: + return None + + @staticmethod + def get_webbible(abbreviation, source): + """ + Return the bibles a webbible provide for download. + + ``abbreviation`` + The abbreviation of the webbible. + + ``source`` + The source of the webbible. + """ + log.debug(u'BiblesResourcesDB.get_webbibles("%s", "%s")', abbreviation, + source) + if not isinstance(abbreviation, unicode): + abbreviation = unicode(abbreviation) + if not isinstance(source, unicode): + source = unicode(source) + source = BiblesResourcesDB.get_download_source(source) + bible = BiblesResourcesDB.run_sql(u'SELECT id, name, abbreviation, ' + u'language_id, download_source_id FROM webbibles WHERE ' + u'download_source_id = ? AND abbreviation = ?', (source[u'id'], + abbreviation)) + if bible: + return { + u'id': bible[0][0], + u'name': bible[0][1], + u'abbreviation': bible[0][2], + u'language_id': bible[0][3], + u'download_source_id': bible[0][4] + } + else: + return None + + @staticmethod + def get_alternative_book_name(name, language_id=None): + """ + Return a book_reference_id if the name matches. + + ``name`` + The name to search the id. + + ``language_id`` + The language_id for which language should be searched + """ + log.debug(u'BiblesResourcesDB.get_alternative_book_name("%s", "%s")', + name, language_id) + if language_id: + books = BiblesResourcesDB.run_sql(u'SELECT book_reference_id, name ' + u'FROM alternative_book_names WHERE language_id = ? ORDER BY ' + u'id', (language_id, )) + else: + books = BiblesResourcesDB.run_sql(u'SELECT book_reference_id, name ' + u'FROM alternative_book_names ORDER BY id') + for book in books: + if book[1].lower() == name.lower(): + return book[0] + return None + + @staticmethod + def get_language(name): + """ + Return a dict containing the language id, name and code by name or + abbreviation. + + ``name`` + The name or abbreviation of the language. + """ + log.debug(u'BiblesResourcesDB.get_language("%s")', name) + if not isinstance(name, unicode): + name = unicode(name) + language = BiblesResourcesDB.run_sql(u'SELECT id, name, code FROM ' + u'language WHERE name = ? OR code = ?', (name, name.lower())) + if language: + return { + u'id': language[0][0], + u'name': unicode(language[0][1]), + u'code': unicode(language[0][2]) + } + else: + return None + + @staticmethod + def get_languages(): + """ + Return a dict containing all languages with id, name and code. + """ + log.debug(u'BiblesResourcesDB.get_languages()') + languages = BiblesResourcesDB.run_sql(u'SELECT id, name, code FROM ' + u'language ORDER by name') + if languages: + return [ + { + u'id': language[0], + u'name': unicode(language[1]), + u'code': unicode(language[2]) + } + for language in languages + ] + else: + return None + + @staticmethod + def get_testament_reference(): + """ + Return a list of all testaments and their id of the Bible. + """ + log.debug(u'BiblesResourcesDB.get_testament_reference()') + testaments = BiblesResourcesDB.run_sql(u'SELECT id, name FROM ' + u'testament_reference ORDER BY id') + return [ + { + u'id': testament[0], + u'name': unicode(testament[1]) + } + for testament in testaments + ] + + +class AlternativeBookNamesDB(QtCore.QObject, Manager): + """ + This class represents a database-bound alternative book names system. + """ + cursor = None + conn = None + + @staticmethod + def get_cursor(): + """ + Return the cursor object. Instantiate one if it doesn't exist yet. + If necessary loads up the database and creates the tables if the + database doesn't exist. + """ + if AlternativeBookNamesDB.cursor is None: + filepath = os.path.join( + AppLocation.get_directory(AppLocation.DataDir), u'bibles', + u'alternative_book_names.sqlite') + if not os.path.exists(filepath): + #create new DB, create table alternative_book_names + AlternativeBookNamesDB.conn = sqlite3.connect(filepath) + AlternativeBookNamesDB.conn.execute(u'CREATE TABLE ' + u'alternative_book_names(id INTEGER NOT NULL, ' + u'book_reference_id INTEGER, language_id INTEGER, name ' + u'VARCHAR(50), PRIMARY KEY (id))') + else: + #use existing DB + AlternativeBookNamesDB.conn = sqlite3.connect(filepath) + AlternativeBookNamesDB.cursor = AlternativeBookNamesDB.conn.cursor() + return AlternativeBookNamesDB.cursor + + @staticmethod + def run_sql(query, parameters=(), commit=None): + """ + Run an SQL query on the database, returning the results. + + ``query`` + The actual SQL query to run. + + ``parameters`` + Any variable parameters to add to the query + + ``commit`` + If a commit statement is necessary this should be True. + """ + cursor = AlternativeBookNamesDB.get_cursor() + cursor.execute(query, parameters) + if commit: + AlternativeBookNamesDB.conn.commit() + return cursor.fetchall() + + @staticmethod + def get_book_reference_id(name, language_id=None): + """ + Return a book_reference_id if the name matches. + + ``name`` + The name to search the id. + + ``language_id`` + The language_id for which language should be searched + """ + log.debug(u'AlternativeBookNamesDB.get_book_reference_id("%s", "%s")', + name, language_id) + if language_id: + books = AlternativeBookNamesDB.run_sql(u'SELECT book_reference_id, ' + u'name FROM alternative_book_names WHERE language_id = ?', + (language_id, )) + else: + books = AlternativeBookNamesDB.run_sql(u'SELECT book_reference_id, ' + u'name FROM alternative_book_names') + for book in books: + if book[1].lower() == name.lower(): + return book[0] + return None + + @staticmethod + def create_alternative_book_name(name, book_reference_id, language_id): + """ + Add an alternative book name to the database. + + ``name`` + The name of the alternative book name. + + ``book_reference_id`` + The book_reference_id of the book. + + ``language_id`` + The language to which the alternative book name belong. + """ + log.debug(u'AlternativeBookNamesDB.create_alternative_book_name("%s", ' + '"%s", "%s"', name, book_reference_id, language_id) + return AlternativeBookNamesDB.run_sql(u'INSERT INTO ' + u'alternative_book_names(book_reference_id, language_id, name) ' + u'VALUES (?, ?, ?)', (book_reference_id, language_id, name), True) + + +class OldBibleDB(QtCore.QObject, Manager): + """ + This class conects to the old bible databases to reimport them to the new + database scheme. + """ + cursor = None + + def __init__(self, parent, **kwargs): + """ + The constructor loads up the database and creates and initialises the + tables if the database doesn't exist. + + **Required keyword arguments:** + + ``path`` + The path to the bible database file. + + ``name`` + The name of the database. This is also used as the file name for + SQLite databases. + """ + log.info(u'OldBibleDB loaded') + QtCore.QObject.__init__(self) + if u'path' not in kwargs: + raise KeyError(u'Missing keyword argument "path".') + if u'file' not in kwargs: + raise KeyError(u'Missing keyword argument "file".') + if u'path' in kwargs: + self.path = kwargs[u'path'] + if u'file' in kwargs: + self.file = kwargs[u'file'] + + def get_cursor(self): + """ + Return the cursor object. Instantiate one if it doesn't exist yet. + """ + if self.cursor is None: + filepath = os.path.join(self.path, self.file) + self.connection = sqlite3.connect(filepath) + self.cursor = self.connection.cursor() + return self.cursor + + def run_sql(self, query, parameters=()): + """ + Run an SQL query on the database, returning the results. + + ``query`` + The actual SQL query to run. + + ``parameters`` + Any variable parameters to add to the query. + """ + cursor = self.get_cursor() + cursor.execute(query, parameters) + return cursor.fetchall() + + def get_name(self): + """ + Returns the version name of the Bible. + """ + version_name = self.run_sql(u'SELECT value FROM ' + u'metadata WHERE key = "Version"') + if version_name: + self.name = version_name[0][0] + else: + self.name = None + return self.name + + def get_metadata(self): + """ + Returns the metadata of the Bible. + """ + metadata = self.run_sql(u'SELECT key, value FROM metadata ' + u'ORDER BY rowid') + if metadata: + return [ + { + u'key': unicode(meta[0]), + u'value': unicode(meta[1]) + } + for meta in metadata + ] + else: + return None + + def get_book(self, name): + """ + Return a book by name or abbreviation. + + ``name`` + The name or abbreviation of the book. + """ + if not isinstance(name, unicode): + name = unicode(name) + books = self.run_sql(u'SELECT id, testament_id, name, ' + u'abbreviation FROM book WHERE LOWER(name) = ? OR ' + u'LOWER(abbreviation) = ?', (name.lower(), name.lower())) + if books: + return { + u'id': books[0][0], + u'testament_id': books[0][1], + u'name': unicode(books[0][2]), + u'abbreviation': unicode(books[0][3]) + } + else: + return None + + def get_books(self): + """ + Returns the books of the Bible. + """ + books = self.run_sql(u'SELECT name, id FROM book ORDER BY id') + if books: + return [ + { + u'name': unicode(book[0]), + u'id':int(book[1]) + } + for book in books + ] + else: + return None + + def get_verses(self, book_id): + """ + Returns the verses of the Bible. + """ + verses = self.run_sql(u'SELECT book_id, chapter, verse, text FROM ' + u'verse WHERE book_id = ? ORDER BY id', (book_id, )) + if verses: + return [ + { + u'book_id': int(verse[0]), + u'chapter': int(verse[1]), + u'verse': int(verse[2]), + u'text': unicode(verse[3]) + } + for verse in verses + ] + else: + return None diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index b66aa0373..76a99ea50 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -42,161 +42,26 @@ from openlp.core.lib import Receiver, translate from openlp.core.lib.ui import critical_error_message_box from openlp.core.utils import AppLocation, get_web_page from openlp.plugins.bibles.lib import SearchResults -from openlp.plugins.bibles.lib.db import BibleDB, Book +from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB, \ + Book log = logging.getLogger(__name__) -class HTTPBooks(object): - """ - A wrapper class around a small SQLite database which contains the books, - chapter counts and verse counts for the web download Bibles. This class - contains a singleton "cursor" so that only one connection to the SQLite - database is ever used. - """ - cursor = None - - @staticmethod - def get_cursor(): - """ - Return the cursor object. Instantiate one if it doesn't exist yet. - """ - if HTTPBooks.cursor is None: - filepath = os.path.join( - AppLocation.get_directory(AppLocation.PluginsDir), u'bibles', - u'resources', u'httpbooks.sqlite') - conn = sqlite3.connect(filepath) - HTTPBooks.cursor = conn.cursor() - return HTTPBooks.cursor - - @staticmethod - def run_sql(query, parameters=()): - """ - Run an SQL query on the database, returning the results. - - ``query`` - The actual SQL query to run. - - ``parameters`` - Any variable parameters to add to the query. - """ - cursor = HTTPBooks.get_cursor() - cursor.execute(query, parameters) - return cursor.fetchall() - - @staticmethod - def get_books(): - """ - Return a list of all the books of the Bible. - """ - books = HTTPBooks.run_sql(u'SELECT id, testament_id, name, ' - u'abbreviation, chapters FROM books ORDER BY id') - book_list = [] - for book in books: - book_list.append({ - u'id': book[0], - u'testament_id': book[1], - u'name': unicode(book[2]), - u'abbreviation': unicode(book[3]), - u'chapters': book[4] - }) - return book_list - - @staticmethod - def get_book(name): - """ - Return a book by name or abbreviation. - - ``name`` - The name or abbreviation of the book. - """ - if not isinstance(name, unicode): - name = unicode(name) - name = name.title() - books = HTTPBooks.run_sql(u'SELECT id, testament_id, name, ' - u'abbreviation, chapters FROM books WHERE name = ? OR ' - u'abbreviation = ?', (name, name)) - if books: - return { - u'id': books[0][0], - u'testament_id': books[0][1], - u'name': unicode(books[0][2]), - u'abbreviation': unicode(books[0][3]), - u'chapters': books[0][4] - } - else: - return None - - @staticmethod - def get_chapter(name, chapter): - """ - Return the chapter details for a specific chapter of a book. - - ``name`` - The name or abbreviation of a book. - - ``chapter`` - The chapter number. - """ - if not isinstance(name, int): - chapter = int(chapter) - book = HTTPBooks.get_book(name) - chapters = HTTPBooks.run_sql(u'SELECT id, book_id, chapter, ' - u'verses FROM chapters WHERE book_id = ?', (book[u'id'],)) - if chapters: - return { - u'id': chapters[chapter-1][0], - u'book_id': chapters[chapter-1][1], - u'chapter': chapters[chapter-1][2], - u'verses': chapters[chapter-1][3] - } - else: - return None - - @staticmethod - def get_chapter_count(book): - """ - Return the number of chapters in a book. - - ``book`` - The name or abbreviation of the book. - """ - details = HTTPBooks.get_book(book) - if details: - return details[u'chapters'] - return 0 - - @staticmethod - def get_verse_count(book, chapter): - """ - Return the number of verses in a chapter. - - ``book`` - The name or abbreviation of the book. - - ``chapter`` - The number of the chapter. - """ - details = HTTPBooks.get_chapter(book, chapter) - if details: - return details[u'verses'] - return 0 - - class BGExtract(object): """ Extract verses from BibleGateway """ def __init__(self, proxyurl=None): - log.debug(u'init %s', proxyurl) + log.debug(u'BGExtract.init("%s")', proxyurl) self.proxyurl = proxyurl socket.setdefaulttimeout(30) def get_bible_chapter(self, version, bookname, chapter): """ - Access and decode bibles via the BibleGateway website. + Access and decode Bibles via the BibleGateway website. ``version`` - The version of the bible like 31 for New International version. + The version of the Bible like 31 for New International version. ``bookname`` Name of the Book. @@ -204,10 +69,11 @@ class BGExtract(object): ``chapter`` Chapter number. """ - log.debug(u'get_bible_chapter %s, %s, %s', version, bookname, chapter) - url_params = urllib.urlencode( - {u'search': u'%s %s' % (bookname, chapter), - u'version': u'%s' % version}) + log.debug(u'BGExtract.get_bible_chapter("%s", "%s", "%s")', version, + bookname, chapter) + urlbookname = urllib.quote(bookname.encode("utf-8")) + url_params = u'search=%s+%s&version=%s' % (urlbookname, chapter, + version) cleaner = [(re.compile(' |
|\'\+\''), lambda match: '')] soup = get_soup_for_bible_ref( u'http://www.biblegateway.com/passage/?%s' % url_params, @@ -230,10 +96,10 @@ class BGExtract(object): verse_list = {} # Cater for inconsistent mark up in the first verse of a chapter. first_verse = verses.find(u'versenum') - if first_verse: + if first_verse and len(first_verse.contents): verse_list[1] = unicode(first_verse.contents[0]) for verse in verses(u'sup', u'versenum'): - raw_verse_num = verse.next + raw_verse_num = verse.next clean_verse_num = 0 # Not all verses exist in all translations and may or may not be # represented by a verse number. If they are not fine, if they are @@ -243,7 +109,7 @@ class BGExtract(object): try: clean_verse_num = int(str(raw_verse_num)) except ValueError: - log.exception(u'Illegal verse number in %s %s %s:%s', + log.warn(u'Illegal verse number in %s %s %s:%s', version, bookname, chapter, unicode(raw_verse_num)) if clean_verse_num: verse_text = raw_verse_num.next @@ -264,13 +130,60 @@ class BGExtract(object): return None return SearchResults(bookname, chapter, verse_list) + def get_books_from_http(self, version): + """ + Load a list of all books a Bible contaions from BibleGateway website. + + ``version`` + The version of the Bible like NIV for New International Version + """ + log.debug(u'BGExtract.get_books_from_http("%s")', version) + url_params = urllib.urlencode( + {u'action': 'getVersionInfo', u'vid': u'%s' % version}) + reference_url = u'http://www.biblegateway.com/versions/?%s#books' % \ + url_params + page = get_web_page(reference_url) + if not page: + send_error_message(u'download') + return None + page_source = page.read() + page_source = unicode(page_source, 'utf8') + page_source_temp = re.search(u'.*?'\ + u'
', page_source, re.DOTALL) + if page_source_temp: + soup = page_source_temp.group(0) + else: + soup = None + try: + soup = BeautifulSoup(soup) + except HTMLParseError: + log.error(u'BeautifulSoup could not parse the Bible page.') + send_error_message(u'parse') + return None + if not soup: + send_error_message(u'parse') + return None + Receiver.send_message(u'openlp_process_events') + content = soup.find(u'table', {u'class': u'infotable'}) + content = content.findAll(u'tr') + if not content: + log.error(u'No books found in the Biblegateway response.') + send_error_message(u'parse') + return None + books = [] + for book in content: + book = book.find(u'td') + if book: + books.append(book.contents[0]) + return books + class BSExtract(object): """ Extract verses from Bibleserver.com """ def __init__(self, proxyurl=None): - log.debug(u'init %s', proxyurl) + log.debug(u'BSExtract.init("%s")', proxyurl) self.proxyurl = proxyurl socket.setdefaulttimeout(30) @@ -287,9 +200,12 @@ class BSExtract(object): ``chapter`` Chapter number """ - log.debug(u'get_bible_chapter %s,%s,%s', version, bookname, chapter) - chapter_url = u'http://m.bibleserver.com/text/%s/%s%s' % \ - (version, bookname, chapter) + log.debug(u'BSExtract.get_bible_chapter("%s", "%s", "%s")', version, + bookname, chapter) + urlversion = urllib.quote(version.encode("utf-8")) + urlbookname = urllib.quote(bookname.encode("utf-8")) + chapter_url = u'http://m.bibleserver.com/text/%s/%s%d' % \ + (urlversion, urlbookname, chapter) header = (u'Accept-Language', u'en') soup = get_soup_for_bible_ref(chapter_url, header) if not soup: @@ -297,7 +213,7 @@ class BSExtract(object): Receiver.send_message(u'openlp_process_events') content = soup.find(u'div', u'content') if not content: - log.exception(u'No verses found in the Bibleserver response.') + log.error(u'No verses found in the Bibleserver response.') send_error_message(u'parse') return None content = content.find(u'div').findAll(u'div') @@ -309,13 +225,38 @@ class BSExtract(object): verses[versenumber] = verse.contents[1].rstrip(u'\n') return SearchResults(bookname, chapter, verses) + def get_books_from_http(self, version): + """ + Load a list of all books a Bible contains from Bibleserver mobile + website. + + ``version`` + The version of the Bible like NIV for New International Version + """ + log.debug(u'BSExtract.get_books_from_http("%s")', version) + urlversion = urllib.quote(version.encode("utf-8")) + chapter_url = u'http://m.bibleserver.com/overlay/selectBook?'\ + 'translation=%s' % (urlversion) + soup = get_soup_for_bible_ref(chapter_url) + if not soup: + return None + content = soup.find(u'ul') + if not content: + log.error(u'No books found in the Bibleserver response.') + send_error_message(u'parse') + return None + content = content.findAll(u'li') + return [ + book.contents[0].contents[0] for book in content + ] + class CWExtract(object): """ Extract verses from CrossWalk/BibleStudyTools """ def __init__(self, proxyurl=None): - log.debug(u'init %s', proxyurl) + log.debug(u'CWExtract.init("%s")', proxyurl) self.proxyurl = proxyurl socket.setdefaulttimeout(30) @@ -324,7 +265,7 @@ class CWExtract(object): Access and decode bibles via the Crosswalk website ``version`` - The version of the bible like niv for New International Version + The version of the Bible like niv for New International Version ``bookname`` Text name of in english e.g. 'gen' for Genesis @@ -332,17 +273,20 @@ class CWExtract(object): ``chapter`` Chapter number """ - log.debug(u'get_bible_chapter %s,%s,%s', version, bookname, chapter) + log.debug(u'CWExtract.get_bible_chapter("%s", "%s", "%s")', version, + bookname, chapter) urlbookname = bookname.replace(u' ', u'-') + urlbookname = urlbookname.lower() + urlbookname = urllib.quote(urlbookname.encode("utf-8")) chapter_url = u'http://www.biblestudytools.com/%s/%s/%s.html' % \ - (version, urlbookname.lower(), chapter) + (version, urlbookname, chapter) soup = get_soup_for_bible_ref(chapter_url) if not soup: return None Receiver.send_message(u'openlp_process_events') htmlverses = soup.findAll(u'span', u'versetext') if not htmlverses: - log.debug(u'No verses found in the CrossWalk response.') + log.error(u'No verses found in the CrossWalk response.') send_error_message(u'parse') return None verses = {} @@ -378,6 +322,32 @@ class CWExtract(object): verses[versenumber] = versetext return SearchResults(bookname, chapter, verses) + def get_books_from_http(self, version): + """ + Load a list of all books a Bible contain from the Crosswalk website. + + ``version`` + The version of the bible like NIV for New International Version + """ + log.debug(u'CWExtract.get_books_from_http("%s")', version) + chapter_url = u'http://www.biblestudytools.com/%s/'\ + % (version) + soup = get_soup_for_bible_ref(chapter_url) + if not soup: + return None + content = soup.find(u'div', {u'class': u'Body'}) + content = content.find(u'ul', {u'class': u'parent'}) + if not content: + log.error(u'No books found in the Crosswalk response.') + send_error_message(u'parse') + return None + content = content.findAll(u'li') + books = [] + for book in content: + book = book.find(u'a') + books.append(book.contents[0]) + return books + class HTTPBible(BibleDB): log.info(u'%s HTTPBible loaded' , __name__) @@ -400,6 +370,8 @@ class HTTPBible(BibleDB): self.proxy_server = None self.proxy_username = None self.proxy_password = None + if u'path' in kwargs: + self.path = kwargs[u'path'] if u'proxy_server' in kwargs: self.proxy_server = kwargs[u'proxy_server'] if u'proxy_username' in kwargs: @@ -407,13 +379,15 @@ class HTTPBible(BibleDB): if u'proxy_password' in kwargs: self.proxy_password = kwargs[u'proxy_password'] - def do_import(self): + def do_import(self, bible_name=None): """ Run the import. This method overrides the parent class method. Returns ``True`` on success, ``False`` on failure. """ - self.wizard.progressBar.setMaximum(2) - self.wizard.incrementProgressBar('Registering bible...') + self.wizard.progressBar.setMaximum(68) + self.wizard.incrementProgressBar(unicode(translate( + 'BiblesPlugin.HTTPBible', + 'Registering Bible and loading books...'))) self.create_meta(u'download source', self.download_source) self.create_meta(u'download name', self.download_name) if self.proxy_server: @@ -424,7 +398,51 @@ class HTTPBible(BibleDB): if self.proxy_password: # Store the proxy password. self.create_meta(u'proxy password', self.proxy_password) - return True + if self.download_source.lower() == u'crosswalk': + handler = CWExtract(self.proxy_server) + elif self.download_source.lower() == u'biblegateway': + handler = BGExtract(self.proxy_server) + elif self.download_source.lower() == u'bibleserver': + handler = BSExtract(self.proxy_server) + books = handler.get_books_from_http(self.download_name) + if not books: + log.exception(u'Importing books from %s - download name: "%s" '\ + 'failed' % (self.download_source, self.download_name)) + return False + self.wizard.progressBar.setMaximum(len(books)+2) + self.wizard.incrementProgressBar(unicode(translate( + 'BiblesPlugin.HTTPBible', 'Registering Language...'))) + bible = BiblesResourcesDB.get_webbible(self.download_name, + self.download_source.lower()) + if bible[u'language_id']: + language_id = bible[u'language_id'] + self.create_meta(u'language_id', language_id) + else: + language_id = self.get_language(bible_name) + if not language_id: + log.exception(u'Importing books from %s " '\ + 'failed' % self.filename) + return False + for book in books: + if self.stop_import_flag: + break + self.wizard.incrementProgressBar(unicode(translate( + 'BiblesPlugin.HTTPBible', 'Importing %s...', + 'Importing ...')) % book) + book_ref_id = self.get_book_ref_id_by_name(book, len(books), + language_id) + if not book_ref_id: + log.exception(u'Importing books from %s - download name: "%s" '\ + 'failed' % (self.download_source, self.download_name)) + return False + book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) + log.debug(u'Book details: Name:%s; id:%s; testament_id:%s', + book, book_ref_id, book_details[u'testament_id']) + self.create_book(book, book_ref_id, book_details[u'testament_id']) + if self.stop_import_flag: + return False + else: + return True def get_verses(self, reference_list, show_error=True): """ @@ -438,34 +456,29 @@ class HTTPBible(BibleDB): This is the list of references the media manager item wants. It is a list of tuples, with the following format:: - (book, chapter, start_verse, end_verse) + (book_reference_id, chapter, start_verse, end_verse) Therefore, when you are looking for multiple items, simply break them up into references like this, bundle them into a list. This function then runs through the list, and returns an amalgamated list of ``Verse`` objects. For example:: - [(u'Genesis', 1, 1, 1), (u'Genesis', 2, 2, 3)] + [(u'35', 1, 1, 1), (u'35', 2, 2, 3)] """ + log.debug(u'HTTPBible.get_verses("%s")', reference_list) for reference in reference_list: - log.debug(u'Reference: %s', reference) - book = reference[0] - db_book = self.get_book(book) + book_id = reference[0] + db_book = self.get_book_by_book_ref_id(book_id) if not db_book: - book_details = HTTPBooks.get_book(book) - if not book_details: - if show_error: - critical_error_message_box( - translate('BiblesPlugin', 'No Book Found'), - translate('BiblesPlugin', 'No matching ' - 'book could be found in this Bible. Check that you ' - 'have spelled the name of the book correctly.')) - return [] - db_book = self.create_book(book_details[u'name'], - book_details[u'abbreviation'], - book_details[u'testament_id']) + if show_error: + critical_error_message_box( + translate('BiblesPlugin', 'No Book Found'), + translate('BiblesPlugin', 'No matching ' + 'book could be found in this Bible. Check that you ' + 'have spelled the name of the book correctly.')) + return [] book = db_book.name - if BibleDB.get_verse_count(self, book, reference[1]) == 0: + if BibleDB.get_verse_count(self, book_id, reference[1]) == 0: Receiver.send_message(u'cursor_busy') search_results = self.get_chapter(book, reference[1]) if search_results and search_results.has_verselist(): @@ -488,7 +501,7 @@ class HTTPBible(BibleDB): """ Receive the request and call the relevant handler methods. """ - log.debug(u'get_chapter %s, %s', book, chapter) + log.debug(u'HTTPBible.get_chapter("%s", "%s")', book, chapter) log.debug(u'source = %s', self.download_source) if self.download_source.lower() == u'crosswalk': handler = CWExtract(self.proxy_server) @@ -502,16 +515,20 @@ class HTTPBible(BibleDB): """ Return the list of books. """ - return [Book.populate(name=book['name']) - for book in HTTPBooks.get_books()] + log.debug(u'HTTPBible.get_books("%s")', Book.name) + return self.get_all_objects(Book, order_by_ref=Book.id) def get_chapter_count(self, book): """ Return the number of chapters in a particular book. + + ``book`` + The book object to get the chapter count for. """ - return HTTPBooks.get_chapter_count(book) + log.debug(u'HTTPBible.get_chapter_count("%s")', book.name) + return BiblesResourcesDB.get_chapter_count(book.book_reference_id) - def get_verse_count(self, book, chapter): + def get_verse_count(self, book_id, chapter): """ Return the number of verses for the specified chapter and book. @@ -521,7 +538,8 @@ class HTTPBible(BibleDB): ``chapter`` The chapter whose verses are being counted. """ - return HTTPBooks.get_verse_count(book, chapter) + log.debug(u'HTTPBible.get_verse_count("%s", %s)', book_id, chapter) + return BiblesResourcesDB.get_verse_count(book_id, chapter) def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, pre_parse_substitute=None, cleaner=None): diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index e367e091e..2a3858afc 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -31,9 +31,10 @@ import os from PyQt4 import QtCore from openlp.core.lib import Receiver, SettingsManager, translate +from openlp.core.lib.ui import critical_error_message_box from openlp.core.utils import AppLocation, delete_file from openlp.plugins.bibles.lib import parse_reference -from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta +from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB from csvbible import CSVBible from http import HTTPBible from opensong import OpenSongBible @@ -140,8 +141,11 @@ class BibleManager(object): """ log.debug(u'Reload bibles') files = SettingsManager.get_files(self.settingsSection, self.suffix) + if u'alternative_book_names.sqlite' in files: + files.remove(u'alternative_book_names.sqlite') log.debug(u'Bible Files %s', files) self.db_cache = {} + self.old_bible_databases = [] for filename in files: bible = BibleDB(self.parent, path=self.path, file=filename) name = bible.get_name() @@ -149,6 +153,11 @@ class BibleManager(object): if name is None: delete_file(os.path.join(self.path, filename)) continue + # Find old database versions + if bible.is_old_database(): + self.old_bible_databases.append([filename, name]) + bible.session.close() + continue log.debug(u'Bible Name: "%s"', name) self.db_cache[name] = bible # Look to see if lazy load bible exists and get create getter. @@ -211,7 +220,8 @@ class BibleManager(object): return [ { u'name': book.name, - u'chapters': self.db_cache[bible].get_chapter_count(book.name) + u'book_reference_id': book.book_reference_id, + u'chapters': self.db_cache[bible].get_chapter_count(book) } for book in self.db_cache[bible].get_books() ] @@ -219,8 +229,15 @@ class BibleManager(object): def get_chapter_count(self, bible, book): """ Returns the number of Chapters for a given book. + + ``bible`` + Unicode. The Bible to get the list of books from. + + ``book`` + The book object to get the chapter count for. """ - log.debug(u'get_book_chapter_count %s', book) + log.debug(u'BibleManager.get_book_chapter_count ("%s", "%s")', bible, + book.name) return self.db_cache[bible].get_chapter_count(book) def get_verse_count(self, bible, book, chapter): @@ -230,9 +247,11 @@ class BibleManager(object): """ log.debug(u'BibleManager.get_verse_count("%s", "%s", %s)', bible, book, chapter) - return self.db_cache[bible].get_verse_count(book, chapter) + db_book = self.db_cache[bible].get_book(book) + book_ref_id = db_book.book_reference_id + return self.db_cache[bible].get_verse_count(book_ref_id, chapter) - def get_verses(self, bible, versetext, show_error=True): + def get_verses(self, bible, versetext, firstbible=False, show_error=True): """ Parses a scripture reference, fetches the verses from the Bible specified, and returns a list of ``Verse`` objects. @@ -264,6 +283,28 @@ class BibleManager(object): return None reflist = parse_reference(versetext) if reflist: + new_reflist = [] + for item in reflist: + if item: + if firstbible: + db_book = self.db_cache[firstbible].get_book(item[0]) + db_book = self.db_cache[bible].get_book_by_book_ref_id( + db_book.book_reference_id) + else: + db_book = self.db_cache[bible].get_book(item[0]) + if db_book: + book_id = db_book.book_reference_id + log.debug(u'Book name corrected to "%s"', db_book.name) + new_reflist.append((book_id, item[1], item[2], + item[3])) + else: + log.debug(u'OpenLP failed to find book %s', item[0]) + critical_error_message_box( + translate('BiblesPlugin', 'No Book Found'), + translate('BiblesPlugin', 'No matching book ' + 'could be found in this Bible. Check that you have ' + 'spelled the name of the book correctly.')) + reflist = new_reflist return self.db_cache[bible].get_verses(reflist, show_error) else: if show_error: diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 41059f942..bd84e2bb5 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -61,7 +61,7 @@ class BibleMediaItem(MediaManagerItem): self.unlockIcon = QtGui.QIcon(u':/bibles/bibles_search_unlock.png') MediaManagerItem.__init__(self, parent, plugin, icon) # Place to store the search results for both bibles. - self.settings = self.parent.settings_tab + self.settings = self.plugin.settings_tab self.quickPreviewAllowed = True self.hasSearch = True self.search_results = {} @@ -239,8 +239,14 @@ class BibleMediaItem(MediaManagerItem): self.advancedLayout.addWidget(self.advancedToVerse, 4, 2) self.addSearchFields(u'advanced', UiStrings().Advanced) # Combo Boxes + QtCore.QObject.connect(self.quickVersionComboBox, + QtCore.SIGNAL(u'activated(int)'), self.onQuickVersionComboBox) + QtCore.QObject.connect(self.quickSecondComboBox, + QtCore.SIGNAL(u'activated(int)'), self.onQuickSecondComboBox) QtCore.QObject.connect(self.advancedVersionComboBox, QtCore.SIGNAL(u'activated(int)'), self.onAdvancedVersionComboBox) + QtCore.QObject.connect(self.advancedSecondComboBox, + QtCore.SIGNAL(u'activated(int)'), self.onAdvancedSecondComboBox) QtCore.QObject.connect(self.advancedBookComboBox, QtCore.SIGNAL(u'activated(int)'), self.onAdvancedBookComboBox) QtCore.QObject.connect(self.advancedFromChapter, @@ -338,7 +344,7 @@ class BibleMediaItem(MediaManagerItem): def initialise(self): log.debug(u'bible manager initialise') - self.parent.manager.media = self + self.plugin.manager.media = self self.loadBibles() bible = QtCore.QSettings().value( self.settingsSection + u'/quick bible', QtCore.QVariant( @@ -365,7 +371,7 @@ class BibleMediaItem(MediaManagerItem): self.quickSecondComboBox.addItem(u'') self.advancedSecondComboBox.addItem(u'') # Get all bibles and sort the list. - bibles = self.parent.manager.get_bibles().keys() + bibles = self.plugin.manager.get_bibles().keys() bibles.sort(cmp=locale.strcoll) # Load the bibles into the combo boxes. for bible in bibles: @@ -386,7 +392,7 @@ class BibleMediaItem(MediaManagerItem): def reloadBibles(self): log.debug(u'Reloading Bibles') - self.parent.manager.reload_bibles() + self.plugin.manager.reload_bibles() self.loadBibles() def initialiseAdvancedBible(self, bible): @@ -400,7 +406,17 @@ class BibleMediaItem(MediaManagerItem): The bible to initialise (unicode). """ log.debug(u'initialiseAdvancedBible %s', bible) - book_data = self.parent.manager.get_books(bible) + book_data = self.plugin.manager.get_books(bible) + secondbible = unicode(self.advancedSecondComboBox.currentText()) + if secondbible != u'': + secondbook_data = self.plugin.manager.get_books(secondbible) + book_data_temp = [] + for book in book_data: + for secondbook in secondbook_data: + if book['book_reference_id'] == \ + secondbook['book_reference_id']: + book_data_temp.append(book) + book_data = book_data_temp self.advancedBookComboBox.clear() first = True for book in book_data: @@ -416,7 +432,7 @@ class BibleMediaItem(MediaManagerItem): def initialiseChapterVerse(self, bible, book, chapter_count): log.debug(u'initialiseChapterVerse %s, %s', bible, book) self.chapter_count = chapter_count - verse_count = self.parent.manager.get_verse_count(bible, book, 1) + verse_count = self.plugin.manager.get_verse_count(bible, book, 1) if verse_count == 0: self.advancedSearchButton.setEnabled(False) critical_error_message_box( @@ -445,18 +461,34 @@ class BibleMediaItem(MediaManagerItem): books = [] # We have to do a 'Reference Search'. if self.quickSearchEdit.currentSearchType() == BibleSearch.Reference: - bibles = self.parent.manager.get_bibles() + bibles = self.plugin.manager.get_bibles() bible = unicode(self.quickVersionComboBox.currentText()) if bible: book_data = bibles[bible].get_books() + secondbible = unicode(self.quickSecondComboBox.currentText()) + if secondbible != u'': + secondbook_data = bibles[secondbible].get_books() + book_data_temp = [] + for book in book_data: + for secondbook in secondbook_data: + if book.book_reference_id == \ + secondbook.book_reference_id: + book_data_temp.append(book) + book_data = book_data_temp books = [book.name + u' ' for book in book_data] books.sort(cmp=locale.strcoll) add_widget_completer(books, self.quickSearchEdit) + def onQuickVersionComboBox(self): + self.updateAutoCompleter() + + def onQuickSecondComboBox(self): + self.updateAutoCompleter() + def onImportClick(self): if not hasattr(self, u'import_wizard'): - self.import_wizard = BibleImportForm(self, self.parent.manager, - self.parent) + self.import_wizard = BibleImportForm(self, self.plugin.manager, + self.plugin) # If the import was not cancelled then reload. if self.import_wizard.exec_(): self.reloadBibles() @@ -501,6 +533,10 @@ class BibleMediaItem(MediaManagerItem): self.initialiseAdvancedBible( unicode(self.advancedVersionComboBox.currentText())) + def onAdvancedSecondComboBox(self): + self.initialiseAdvancedBible( + unicode(self.advancedVersionComboBox.currentText())) + def onAdvancedBookComboBox(self): item = int(self.advancedBookComboBox.currentIndex()) self.initialiseChapterVerse( @@ -515,7 +551,7 @@ class BibleMediaItem(MediaManagerItem): bible = unicode(self.advancedVersionComboBox.currentText()) book = unicode(self.advancedBookComboBox.currentText()) verse_from = int(self.advancedFromVerse.currentText()) - verse_count = self.parent.manager.get_verse_count(bible, book, + verse_count = self.plugin.manager.get_verse_count(bible, book, chapter_to) self.adjustComboBox(verse_from, verse_count, self.advancedToVerse, True) @@ -527,7 +563,7 @@ class BibleMediaItem(MediaManagerItem): chapter_to = int(self.advancedToChapter.currentText()) verse_from = int(self.advancedFromVerse.currentText()) verse_to = int(self.advancedToVerse.currentText()) - verse_count = self.parent.manager.get_verse_count(bible, book, + verse_count = self.plugin.manager.get_verse_count(bible, book, chapter_to) if chapter_from == chapter_to and verse_from > verse_to: self.adjustComboBox(verse_from, verse_count, self.advancedToVerse) @@ -539,7 +575,7 @@ class BibleMediaItem(MediaManagerItem): book = unicode(self.advancedBookComboBox.currentText()) chapter_from = int(self.advancedFromChapter.currentText()) chapter_to = int(self.advancedToChapter.currentText()) - verse_count = self.parent.manager.get_verse_count(bible, book, + verse_count = self.plugin.manager.get_verse_count(bible, book, chapter_from) self.adjustComboBox(1, verse_count, self.advancedFromVerse) if chapter_from > chapter_to: @@ -599,10 +635,10 @@ class BibleMediaItem(MediaManagerItem): range_separator + chapter_to + verse_separator + verse_to versetext = u'%s %s' % (book, verse_range) Receiver.send_message(u'cursor_busy') - self.search_results = self.parent.manager.get_verses(bible, versetext) + self.search_results = self.plugin.manager.get_verses(bible, versetext) if second_bible: - self.second_search_results = self.parent.manager.get_verses( - second_bible, versetext) + self.second_search_results = self.plugin.manager.get_verses( + second_bible, versetext, bible) if not self.advancedLockButton.isChecked(): self.listView.clear() if self.listView.count() != 0: @@ -627,21 +663,44 @@ class BibleMediaItem(MediaManagerItem): text = unicode(self.quickSearchEdit.text()) if self.quickSearchEdit.currentSearchType() == BibleSearch.Reference: # We are doing a 'Reference Search'. - self.search_results = self.parent.manager.get_verses(bible, text) + self.search_results = self.plugin.manager.get_verses(bible, text) if second_bible and self.search_results: - self.second_search_results = self.parent.manager.get_verses( - second_bible, text) + self.second_search_results = self.plugin.manager.get_verses( + second_bible, text, bible) else: # We are doing a 'Text Search'. Receiver.send_message(u'cursor_busy') - bibles = self.parent.manager.get_bibles() - self.search_results = self.parent.manager.verse_search(bible, + bibles = self.plugin.manager.get_bibles() + self.search_results = self.plugin.manager.verse_search(bible, second_bible, text) if second_bible and self.search_results: text = [] + new_search_results = [] + count = 0 + passage_not_found = False for verse in self.search_results: - text.append((verse.book.name, verse.chapter, verse.verse, - verse.verse)) + db_book = bibles[second_bible].get_book_by_book_ref_id( + verse.book.book_reference_id) + if not db_book: + log.debug(u'Passage "%s %d:%d" not found in Second ' + u'Bible' % (verse.book.name, verse.chapter, + verse.verse)) + passage_not_found = True + count += 1 + continue + new_search_results.append(verse) + text.append((verse.book.book_reference_id, verse.chapter, + verse.verse, verse.verse)) + if passage_not_found: + QtGui.QMessageBox.information(self, + translate('BiblePlugin.MediaItem', 'Information'), + unicode(translate('BiblePlugin.MediaItem', + 'The second Bibles does not contain all the verses ' + 'that are in the main Bible. Only verses found in both ' + 'Bibles will be shown. %d verses have not been ' + 'included in the results.')) % count, + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) + self.search_results = new_search_results self.second_search_results = \ bibles[second_bible].get_verses(text) if not self.quickLockButton.isChecked(): @@ -674,19 +733,19 @@ class BibleMediaItem(MediaManagerItem): further action is saved for/in each row. """ verse_separator = get_reference_match(u'sep_v_display') - version = self.parent.manager.get_meta_data(bible, u'Version').value - copyright = self.parent.manager.get_meta_data(bible, u'Copyright').value + version = self.plugin.manager.get_meta_data(bible, u'Version').value + copyright = self.plugin.manager.get_meta_data(bible, u'Copyright').value permissions = \ - self.parent.manager.get_meta_data(bible, u'Permissions').value + self.plugin.manager.get_meta_data(bible, u'Permissions').value second_version = u'' second_copyright = u'' second_permissions = u'' if second_bible: - second_version = self.parent.manager.get_meta_data( + second_version = self.plugin.manager.get_meta_data( second_bible, u'Version').value - second_copyright = self.parent.manager.get_meta_data( + second_copyright = self.plugin.manager.get_meta_data( second_bible, u'Copyright').value - second_permissions = self.parent.manager.get_meta_data( + second_permissions = self.plugin.manager.get_meta_data( second_bible, u'Permissions').value items = [] for count, verse in enumerate(search_results): @@ -809,7 +868,8 @@ class BibleMediaItem(MediaManagerItem): service_item.theme = None else: service_item.theme = self.settings.bible_theme - [service_item.add_from_text(slide[:30], slide) for slide in raw_slides] + for slide in raw_slides: + service_item.add_from_text(slide[:30], slide) return True def formatTitle(self, start_bitem, old_bitem): @@ -879,7 +939,7 @@ class BibleMediaItem(MediaManagerItem): # We are still in the same chapter, but a verse has been skipped. return True elif old_chapter + 1 == chapter and (verse != 1 or - old_verse != self.parent.manager.get_verse_count( + old_verse != self.plugin.manager.get_verse_count( old_bible, old_book, old_chapter)): # We are in the following chapter, but the last verse was not the # last verse of the chapter or the current verse is not the @@ -923,7 +983,7 @@ class BibleMediaItem(MediaManagerItem): Search for some Bible verses (by reference). """ bible = unicode(self.quickVersionComboBox.currentText()) - search_results = self.parent.manager.get_verses(bible, string, False) + search_results = self.plugin.manager.get_verses(bible, string, False) results = [] if search_results: versetext = u' '.join([verse.text for verse in search_results]) @@ -933,6 +993,6 @@ class BibleMediaItem(MediaManagerItem): def createItemFromId(self, item_id): item = QtGui.QListWidgetItem() bible = unicode(self.quickVersionComboBox.currentText()) - search_results = self.parent.manager.get_verses(bible, item_id, False) + search_results = self.plugin.manager.get_verses(bible, item_id, False) items = self.buildDisplayResults(bible, u'', search_results) return items diff --git a/openlp/plugins/bibles/lib/openlp1.py b/openlp/plugins/bibles/lib/openlp1.py index c3a1c9f68..d66b3c96c 100644 --- a/openlp/plugins/bibles/lib/openlp1.py +++ b/openlp/plugins/bibles/lib/openlp1.py @@ -30,7 +30,7 @@ import sqlite from openlp.core.lib import Receiver from openlp.core.ui.wizard import WizardStrings -from openlp.plugins.bibles.lib.db import BibleDB +from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB log = logging.getLogger(__name__) @@ -46,7 +46,7 @@ class OpenLP1Bible(BibleDB): BibleDB.__init__(self, parent, **kwargs) self.filename = kwargs[u'filename'] - def do_import(self): + def do_import(self, bible_name=None): """ Imports an openlp.org v1 bible. """ @@ -57,6 +57,11 @@ class OpenLP1Bible(BibleDB): cursor = connection.cursor() except: return False + #Create the bible language + language_id = self.get_language(bible_name) + if not language_id: + log.exception(u'Importing books from "%s" failed' % self.filename) + return False # Create all books. cursor.execute(u'SELECT id, testament_id, name, abbreviation FROM book') books = cursor.fetchall() @@ -69,7 +74,15 @@ class OpenLP1Bible(BibleDB): testament_id = int(book[1]) name = unicode(book[2], u'cp1252') abbreviation = unicode(book[3], u'cp1252') - self.create_book(name, abbreviation, testament_id) + book_ref_id = self.get_book_ref_id_by_name(name, len(books), + language_id) + if not book_ref_id: + log.exception(u'Importing books from "%s" '\ + 'failed' % self.filename) + return False + book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) + db_book = self.create_book(name, book_ref_id, + book_details[u'testament_id']) # Update the progess bar. self.wizard.incrementProgressBar(WizardStrings.ImportingType % name) # Import the verses for this book. @@ -83,7 +96,7 @@ class OpenLP1Bible(BibleDB): chapter = int(verse[0]) verse_number = int(verse[1]) text = unicode(verse[2], u'cp1252') - self.create_verse(book_id, chapter, verse_number, text) + self.create_verse(db_book.id, chapter, verse_number, text) Receiver.send_message(u'openlp_process_events') self.session.commit() connection.close() diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py index 115b08e42..e84c04f06 100644 --- a/openlp/plugins/bibles/lib/opensong.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -29,7 +29,7 @@ import logging from lxml import objectify from openlp.core.lib import Receiver, translate -from openlp.plugins.bibles.lib.db import BibleDB +from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB log = logging.getLogger(__name__) @@ -46,7 +46,7 @@ class OpenSongBible(BibleDB): BibleDB.__init__(self, parent, **kwargs) self.filename = kwargs['filename'] - def do_import(self): + def do_import(self, bible_name=None): """ Loads a Bible from file. """ @@ -62,11 +62,23 @@ class OpenSongBible(BibleDB): file = open(self.filename, u'r') opensong = objectify.parse(file) bible = opensong.getroot() + language_id = self.get_language(bible_name) + if not language_id: + log.exception(u'Importing books from "%s" '\ + 'failed' % self.filename) + return False for book in bible.b: if self.stop_import_flag: break - db_book = self.create_book(unicode(book.attrib[u'n']), - unicode(book.attrib[u'n'][:4])) + book_ref_id = self.get_book_ref_id_by_name( + unicode(book.attrib[u'n']), len(bible.b), language_id) + if not book_ref_id: + log.exception(u'Importing books from "%s" '\ + 'failed' % self.filename) + return False + book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) + db_book = self.create_book(unicode(book.attrib[u'n']), + book_ref_id, book_details[u'testament_id']) for chapter in book.c: if self.stop_import_flag: break diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py index 958d70d90..e014e5d0a 100644 --- a/openlp/plugins/bibles/lib/osis.py +++ b/openlp/plugins/bibles/lib/osis.py @@ -34,7 +34,7 @@ import re from openlp.core.lib import Receiver, translate from openlp.core.utils import AppLocation -from openlp.plugins.bibles.lib.db import BibleDB +from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB log = logging.getLogger(__name__) @@ -86,7 +86,7 @@ class OSISBible(BibleDB): if fbibles: fbibles.close() - def do_import(self): + def do_import(self, bible_name=None): """ Loads a Bible from file. """ @@ -96,7 +96,6 @@ class OSISBible(BibleDB): osis = None success = True last_chapter = 0 - testament = 1 match_count = 0 self.wizard.incrementProgressBar(translate('BiblesPlugin.OsisImport', 'Detecting encoding (this may take a few minutes)...')) @@ -109,6 +108,11 @@ class OSISBible(BibleDB): finally: if detect_file: detect_file.close() + # Set meta language_id + language_id = self.get_language(bible_name) + if not language_id: + log.exception(u'Importing books from "%s" failed' % self.filename) + return False try: osis = codecs.open(self.filename, u'r', details['encoding']) repl = replacement @@ -123,13 +127,19 @@ class OSISBible(BibleDB): verse = int(match.group(3)) verse_text = match.group(4) if not db_book or db_book.name != self.books[book][0]: - log.debug(u'New book: "%s"', self.books[book][0]) - if book == u'Matt' or book == u'Jdt': - testament += 1 + log.debug(u'New book: "%s"' % self.books[book][0]) + book_ref_id = self.get_book_ref_id_by_name(unicode( + self.books[book][0]), 67, language_id) + if not book_ref_id: + log.exception(u'Importing books from "%s" '\ + 'failed' % self.filename) + return False + book_details = BiblesResourcesDB.get_book_by_id( + book_ref_id) db_book = self.create_book( unicode(self.books[book][0]), - unicode(self.books[book][1]), - testament) + book_ref_id, + book_details[u'testament_id']) if last_chapter == 0: if book == u'Gen': self.wizard.progressBar.setMaximum(1188) diff --git a/openlp/plugins/bibles/resources/biblegateway.csv b/openlp/plugins/bibles/resources/biblegateway.csv deleted file mode 100644 index ad8052704..000000000 --- a/openlp/plugins/bibles/resources/biblegateway.csv +++ /dev/null @@ -1,81 +0,0 @@ -João Ferreira de Almeida Atualizada,AA -التÙسير التطبيقى للكتاب المقدس,ALAB -Shqip,ALB -Amplified Bible,AMP -Amuzgo de Guerrero,AMU -American Standard Version,ASV -La Bible du Semeur,BDS -БългарÑки 1940,BG1940 -БългарÑки,BULG -Chinanteco de Comaltepec,CCO -Contemporary English Version,CEV -Cakchiquel Occidental,CKW -Hrvatski,CRO -Castilian,CST -è–經和åˆæœ¬ (简体中文),CUVS -è–經和åˆæœ¬ (ç¹ä½“中文),CUV -Darby Translation,DARBY -Dette er Biblen pÃ¥ dansk,DN1933 -Det Norsk Bibelselskap 1930,DNB1930 -English Standard Version,ESV -GOD’S WORD Translation,GW -Holman Christian Standard Bible,HCSB -Kreyòl ayisyen bib,HCV -Hiligaynon Bible,HLGN -Hoffnung für Alle,HOF -Het Boek,HTB -Icelandic Bible,ICELAND -Jacalteco – Oriental,JAC -Károlyi-biblia,KAR -Kekchi,KEK -21st Century King James Version,KJ21 -King James Version,KJV -La Biblia de las Américas,LBLA -Levande Bibeln,LB -La Parola è Vita,LM -La Nuova Diodati,LND -Louis Segond,LSG -Luther Bibel 1545,LUTH1545 -MÄori Bible,MAORI -МакедонÑки Ðовиот Завет,MNT -The Message,MSG -Mam de Comitancillo Central,MVC -Mam de Todos Santos Cuchumatán,MVJ -New American Standard Bible,NASB -New Century Version,NCV -Náhuatl de Guerrero,NGU -New International Reader's Version,NIRV -New International Version 1984,NIV1984 -New International Version 2010,NIV -New International Version - UK,NIVUK -New King James Version,NKJV -New Living Translation,NLT -Nádej pre kazdého,NPK -Nueva Versión Internacional,NVI -O Livro,OL -Quiché – Centro Occidental,QUT -Reimer 2001,REIMER -Română Cornilescu,RMNN -Ðовый перевод на руÑÑкий Ñзык,RUSV -Reina-Valera Antigua,RVA -Reina-Valera 1960,RVR1960 -Reina-Valera 1995,RVR1995 -Slovo na cestu,SNC -Ang Salita ng Diyos,SND -Swahili New Testament,SNT -Svenska 1917,SV1917 -Levande Bibeln,SVL -Создать Ñтраницу,SZ -Traducción en lenguaje actual,TLA -New Romanian Translation,TLCR -Today’s New International Version 2005,TNIV -Textus Receptus Stephanus 1550,TR1550 -Textus Receptus Scrivener 1894,TR1894 -УкраїнÑька БібліÑ. Переклад Івана Огієнка,UKR -Uspanteco,USP -Kinh Thánh tiếng Việt 1934,VIET -Worldwide English (New Testament),WE -Codex Vaticanus Westcott-Hort 1881,WHNU -Westminster Leningrad Codex,WLC -Wycliffe New Testament,WYC -Young's Literal Translation,YLT diff --git a/openlp/plugins/bibles/resources/bibles_resources.sqlite b/openlp/plugins/bibles/resources/bibles_resources.sqlite new file mode 100644 index 000000000..3235c9562 Binary files /dev/null and b/openlp/plugins/bibles/resources/bibles_resources.sqlite differ diff --git a/openlp/plugins/bibles/resources/bibleserver.csv b/openlp/plugins/bibles/resources/bibleserver.csv deleted file mode 100644 index 942d43116..000000000 --- a/openlp/plugins/bibles/resources/bibleserver.csv +++ /dev/null @@ -1,39 +0,0 @@ -عربي, ARA -Bible – pÅ™eklad 21. století, B21 -Bible du Semeur, BDS -БългарÑката БиблиÑ, BLG -ÄŒeský ekumenický pÅ™eklad, CEP -Hrvatski, CRO -СвÑщенное ПиÑание, CRS -Version La Biblia al Dia, CST -中文和åˆæœ¬ï¼ˆç®€ä½“), CUVS -Bibelen pÃ¥ hverdagsdansk, DK -Revidierte Elberfelder, ELB -Einheitsübersetzung, EU -Gute Nachricht Bibel, GNB -Hoffnung für alle, HFA -Hungarian, HUN -Het Boek, HTB -La Parola è Vita, ITA -IBS-fordítás (Új Károli), KAR -King James Version, KJV -Luther 1984, LUT -Septuaginta, LXX -Neue Genfer Ãœbersetzung, NGU -New International Readers Version, NIRV -New International Version, NIV -Neues Leben, NL -En Levende Bok (NOR), NOR -Nádej pre kazdého, NPK -Noua traducere în limba românã, NTR -Nueva Versión Internacional, NVI -הברית הישנה, OT -SÅ‚owo Å»ycia, POL -O Livro, PRT -Ðовый перевод на руÑÑкий Ñзык, RUS -Slovo na cestu, SNC -Schlachter 2000, SLT -En Levande Bok (SWE), SVL -Today's New International Version, TNIV -Türkçe, TR -Biblia Vulgata, VUL diff --git a/openlp/plugins/bibles/resources/crosswalkbooks.csv b/openlp/plugins/bibles/resources/crosswalkbooks.csv deleted file mode 100644 index 7957bfdc8..000000000 --- a/openlp/plugins/bibles/resources/crosswalkbooks.csv +++ /dev/null @@ -1,27 +0,0 @@ -New American Standard,nas -American Standard Version,asv -English Standard Version,esv -New King James Version,nkj -King James Version,kjv -Holman Christian Standard Bible,csb -Third Millennium Bible,tmb -New International Version,niv -New Living Translation,nlt -New Revised Standard,nrs -Revised Standard Version,rsv -Good News Translation,gnt -Douay-Rheims Bible,rhe -The Message,msg -The Complete Jewish Bible,cjb -New Century Version,ncv -GOD'S WORD Translation,gwd -Hebrew Names Version,hnv -World English Bible,web -The Bible in Basic English,bbe -Young's Literal Translation,ylt -Today's New International Version,tnv -New International Reader's Version,nrv -The Darby Translation,dby -The Webster Bible,wbt -The Latin Vulgate,vul -Weymouth New Testament,wnt diff --git a/openlp/plugins/bibles/resources/httpbooks.sqlite b/openlp/plugins/bibles/resources/httpbooks.sqlite deleted file mode 100644 index 406914b63..000000000 Binary files a/openlp/plugins/bibles/resources/httpbooks.sqlite and /dev/null differ diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index 887f813fa..e790a2449 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -27,8 +27,6 @@ import logging -from forms import EditCustomForm - from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.db import Manager from openlp.plugins.custom.lib import CustomMediaItem, CustomTab @@ -52,7 +50,6 @@ class CustomPlugin(Plugin): CustomMediaItem, CustomTab) self.weight = -5 self.manager = Manager(u'custom', init_schema) - self.edit_custom_form = EditCustomForm(self) self.icon_path = u':/plugins/plugin_custom.png' self.icon = build_icon(self.icon_path) diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index 5a23e2066..4a7585f11 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -43,13 +43,13 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): Class documentation goes here. """ log.info(u'Custom Editor loaded') - def __init__(self, parent): + def __init__(self, mediaitem, parent, manager): """ Constructor """ - QtGui.QDialog.__init__(self) - self.parent = parent - self.manager = self.parent.manager + QtGui.QDialog.__init__(self, parent) + self.manager = manager + self.mediaitem = mediaitem self.setupUi(self) # Create other objects and forms. self.editSlideForm = EditCustomSlideForm(self) @@ -115,9 +115,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): def accept(self): log.debug(u'accept') if self.saveCustom(): - Receiver.send_message(u'custom_set_autoselect_item', - self.customSlide.title) - Receiver.send_message(u'custom_load_list') QtGui.QDialog.accept(self) def saveCustom(self): @@ -138,7 +135,9 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): self.customSlide.text = unicode(sxml.extract_xml(), u'utf-8') self.customSlide.credits = unicode(self.creditEdit.text()) self.customSlide.theme_name = unicode(self.themeComboBox.currentText()) - return self.manager.save_object(self.customSlide) + success = self.manager.save_object(self.customSlide) + self.mediaitem.auto_select_id = self.customSlide.id + return success def onUpButtonClicked(self): selectedRow = self.slideListView.currentRow() diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 91af98bea..94ee6d94e 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -35,6 +35,7 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ check_item_selected, translate from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import UiStrings +from openlp.plugins.custom.forms import EditCustomForm from openlp.plugins.custom.lib import CustomXMLParser from openlp.plugins.custom.lib.db import CustomSlide @@ -56,14 +57,16 @@ class CustomMediaItem(MediaManagerItem): def __init__(self, parent, plugin, icon): self.IconPath = u'custom/custom' - MediaManagerItem.__init__(self, parent, self, icon) + MediaManagerItem.__init__(self, parent, plugin, icon) + self.edit_custom_form = EditCustomForm(self, self.plugin.formparent, + self.plugin.manager) self.singleServiceItem = False self.quickPreviewAllowed = True self.hasSearch = True # Holds information about whether the edit is remotly triggered and # which Custom is required. self.remoteCustom = -1 - self.manager = parent.manager + self.manager = plugin.manager def addEndHeaderBar(self): self.addToolbarSeparator() @@ -106,7 +109,7 @@ class CustomMediaItem(MediaManagerItem): QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'custom_edit_clear'), self.onRemoteEditClear) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'custom_load_list'), self.initialise) + QtCore.SIGNAL(u'custom_load_list'), self.loadList) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'custom_preview'), self.onPreviewClick) @@ -126,16 +129,10 @@ class CustomMediaItem(MediaManagerItem): self.searchTextEdit.setCurrentSearchType(QtCore.QSettings().value( u'%s/last search type' % self.settingsSection, QtCore.QVariant(CustomSearch.Titles)).toInt()[0]) - # Called to redisplay the custom list screen edith from a search - # or from the exit of the Custom edit dialog. If remote editing is - # active trigger it and clean up so it will not update again. - if self.remoteTriggered == u'L': - self.onAddClick() - if self.remoteTriggered == u'P': - self.onPreviewClick() - self.onRemoteEditClear() def loadList(self, custom_slides): + # Sort out what custom we want to select after loading the list. + self.save_auto_select_id() self.listView.clear() # Sort the customs by its title considering language specific # characters. lower() is needed for windows! @@ -146,33 +143,45 @@ class CustomMediaItem(MediaManagerItem): custom_name.setData( QtCore.Qt.UserRole, QtCore.QVariant(custom_slide.id)) self.listView.addItem(custom_name) - # Auto-select the item if name has been set - if custom_slide.title == self.autoSelectItem: + # Auto-select the custom. + if custom_slide.id == self.auto_select_id: self.listView.setCurrentItem(custom_name) + self.auto_select_id = -1 + # Called to redisplay the custom list screen edith from a search + # or from the exit of the Custom edit dialog. If remote editing is + # active trigger it and clean up so it will not update again. + if self.remoteTriggered == u'L': + self.onAddClick() + if self.remoteTriggered == u'P': + self.onPreviewClick() + self.onRemoteEditClear() def onNewClick(self): - self.parent.edit_custom_form.loadCustom(0) - self.parent.edit_custom_form.exec_() - self.initialise() + self.edit_custom_form.loadCustom(0) + self.edit_custom_form.exec_() + self.onClearTextButtonClick() + self.onSelectionChange() def onRemoteEditClear(self): self.remoteTriggered = None self.remoteCustom = -1 - def onRemoteEdit(self, customid): + def onRemoteEdit(self, message): """ Called by ServiceManager or SlideController by event passing - the Song Id in the payload along with an indicator to say which + the custom Id in the payload along with an indicator to say which type of display is required. """ - fields = customid.split(u':') - valid = self.manager.get_object(CustomSlide, fields[1]) + remote_type, custom_id = message.split(u':') + custom_id = int(custom_id) + valid = self.manager.get_object(CustomSlide, custom_id) if valid: - self.remoteCustom = fields[1] - self.remoteTriggered = fields[0] - self.parent.edit_custom_form.loadCustom(fields[1], - (fields[0] == u'P')) - self.parent.edit_custom_form.exec_() + self.remoteCustom = custom_id + self.remoteTriggered = remote_type + self.edit_custom_form.loadCustom(custom_id, (remote_type == u'P')) + self.edit_custom_form.exec_() + self.auto_select_id = -1 + self.onSearchTextButtonClick() def onEditClick(self): """ @@ -181,9 +190,10 @@ class CustomMediaItem(MediaManagerItem): if check_item_selected(self.listView, UiStrings().SelectEdit): item = self.listView.currentItem() item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] - self.parent.edit_custom_form.loadCustom(item_id, False) - self.parent.edit_custom_form.exec_() - self.initialise() + self.edit_custom_form.loadCustom(item_id, False) + self.edit_custom_form.exec_() + self.auto_select_id = -1 + self.onSearchTextButtonClick() def onDeleteClick(self): """ @@ -195,7 +205,7 @@ class CustomMediaItem(MediaManagerItem): id_list = [(item.data(QtCore.Qt.UserRole)).toInt()[0] for item in self.listView.selectedIndexes()] for id in id_list: - self.parent.manager.delete_object(CustomSlide, id) + self.plugin.manager.delete_object(CustomSlide, id) for row in row_list: self.listView.takeItem(row) @@ -203,7 +213,6 @@ class CustomMediaItem(MediaManagerItem): self.searchTextEdit.setFocus() def generateSlideData(self, service_item, item=None, xmlVersion=False): - raw_slides = [] raw_footer = [] slide = None theme = None @@ -212,7 +221,7 @@ class CustomMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.AllowsPreview) service_item.add_capability(ItemCapabilities.AllowsLoop) service_item.add_capability(ItemCapabilities.AllowsVirtualSplit) - customSlide = self.parent.manager.get_object(CustomSlide, item_id) + customSlide = self.plugin.manager.get_object(CustomSlide, item_id) title = customSlide.title credit = customSlide.credits service_item.edit_id = item_id @@ -221,8 +230,7 @@ class CustomMediaItem(MediaManagerItem): service_item.theme = theme customXML = CustomXMLParser(customSlide.text) verseList = customXML.get_verses() - for verse in verseList: - raw_slides.append(verse[1]) + raw_slides = [verse[1] for verse in verseList] service_item.title = title for slide in raw_slides: service_item.add_from_text(slide[:30], slide) @@ -245,13 +253,13 @@ class CustomMediaItem(MediaManagerItem): search_type = self.searchTextEdit.currentSearchType() if search_type == CustomSearch.Titles: log.debug(u'Titles Search') - search_results = self.parent.manager.get_all_objects(CustomSlide, + search_results = self.plugin.manager.get_all_objects(CustomSlide, CustomSlide.title.like(u'%' + self.whitespace.sub(u' ', search_keywords) + u'%'), order_by_ref=CustomSlide.title) self.loadList(search_results) elif search_type == CustomSearch.Themes: log.debug(u'Theme Search') - search_results = self.parent.manager.get_all_objects(CustomSlide, + search_results = self.plugin.manager.get_all_objects(CustomSlide, CustomSlide.theme_name.like(u'%' + self.whitespace.sub(u' ', search_keywords) + u'%'), order_by_ref=CustomSlide.title) self.loadList(search_results) @@ -260,7 +268,7 @@ class CustomMediaItem(MediaManagerItem): def onSearchTextEditChanged(self, text): """ If search as type enabled invoke the search on each key press. - If the Title is being searched do not start till 2 characters + If the Title is being searched do not start until 2 characters have been entered. """ search_length = 2 @@ -283,8 +291,5 @@ class CustomMediaItem(MediaManagerItem): func.lower(CustomSlide.text).like(u'%' + string.lower() + u'%')), order_by_ref=CustomSlide.title) - results = [] - for custom in search_results: - results.append([custom.id, custom.title]) - return results + return [[custom.id, custom.title] for custom in search_results] diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index f760a984e..921a52ede 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -47,7 +47,7 @@ class ImageMediaItem(MediaManagerItem): def __init__(self, parent, plugin, icon): self.IconPath = u'images/image' - MediaManagerItem.__init__(self, parent, self, icon) + MediaManagerItem.__init__(self, parent, plugin, icon) self.quickPreviewAllowed = True self.hasSearch = True QtCore.QObject.connect(Receiver.get_receiver(), @@ -112,14 +112,14 @@ class ImageMediaItem(MediaManagerItem): def loadList(self, list, initialLoad=False): if not initialLoad: - self.parent.formparent.displayProgressBar(len(list)) + self.plugin.formparent.displayProgressBar(len(list)) # Sort the themes by its filename considering language specific # characters. lower() is needed for windows! list.sort(cmp=locale.strcoll, key=lambda filename: os.path.split(unicode(filename))[1].lower()) for imageFile in list: if not initialLoad: - self.parent.formparent.incrementProgressBar() + self.plugin.formparent.incrementProgressBar() filename = os.path.split(unicode(imageFile))[1] thumb = os.path.join(self.servicePath, filename) if os.path.exists(thumb): @@ -134,7 +134,7 @@ class ImageMediaItem(MediaManagerItem): item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(imageFile)) self.listView.addItem(item_name) if not initialLoad: - self.parent.formparent.finishedProgressBar() + self.plugin.formparent.finishedProgressBar() def generateSlideData(self, service_item, item=None, xmlVersion=False): if item: @@ -188,7 +188,7 @@ class ImageMediaItem(MediaManagerItem): Called to reset the Live backgound with the image selected, """ self.resetAction.setVisible(False) - self.parent.liveController.display.resetImage() + self.plugin.liveController.display.resetImage() def liveThemeChanged(self): """ @@ -208,7 +208,7 @@ class ImageMediaItem(MediaManagerItem): filename = unicode(bitem.data(QtCore.Qt.UserRole).toString()) if os.path.exists(filename): (path, name) = os.path.split(filename) - self.parent.liveController.display.directImage(name, filename) + self.plugin.liveController.display.directImage(name, filename) self.resetAction.setVisible(True) else: critical_error_message_box(UiStrings().LiveBGError, diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 9dc17f4d3..96b97d25a 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -50,7 +50,7 @@ class MediaMediaItem(MediaManagerItem): self.background = False self.PreviewFunction = QtGui.QPixmap( u':/media/media_video.png').toImage() - MediaManagerItem.__init__(self, parent, self, icon) + MediaManagerItem.__init__(self, parent, plugin, icon) self.singleServiceItem = False self.hasSearch = True self.mediaObject = None @@ -65,8 +65,8 @@ class MediaMediaItem(MediaManagerItem): self.onNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media') self.onNewFileMasks = unicode(translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)')) % ( - u' '.join(self.parent.video_extensions_list), - u' '.join(self.parent.audio_extensions_list), UiStrings().AllFiles) + u' '.join(self.plugin.video_extensions_list), + u' '.join(self.plugin.audio_extensions_list), UiStrings().AllFiles) self.replaceAction.setText(UiStrings().ReplaceBG) self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG) self.resetAction.setText(UiStrings().ResetBG) @@ -95,7 +95,7 @@ class MediaMediaItem(MediaManagerItem): Called to reset the Live backgound with the media selected, """ self.resetAction.setVisible(False) - self.parent.liveController.display.resetVideo() + self.plugin.liveController.display.resetVideo() def videobackgroundReplaced(self): """ @@ -114,7 +114,7 @@ class MediaMediaItem(MediaManagerItem): filename = unicode(item.data(QtCore.Qt.UserRole).toString()) if os.path.exists(filename): (path, name) = os.path.split(filename) - self.parent.liveController.display.video(filename, 0, True) + self.plugin.liveController.display.video(filename, 0, True) self.resetAction.setVisible(True) else: critical_error_message_box(UiStrings().LiveBGError, diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 0d0cc43a3..ee49948ac 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -125,7 +125,7 @@ class ImpressController(PresentationController): try: uno_instance = get_uno_instance(resolver) except: - log.exception(u'Unable to find running instance ') + log.warn(u'Unable to find running instance ') self.start_process() loop += 1 try: @@ -136,7 +136,7 @@ class ImpressController(PresentationController): "com.sun.star.frame.Desktop", uno_instance) return desktop except: - log.exception(u'Failed to get UNO desktop') + log.warn(u'Failed to get UNO desktop') return None def get_com_desktop(self): @@ -151,7 +151,7 @@ class ImpressController(PresentationController): try: desktop = self.manager.createInstance(u'com.sun.star.frame.Desktop') except AttributeError: - log.exception(u'Failure to find desktop - Impress may have closed') + log.warn(u'Failure to find desktop - Impress may have closed') return desktop if desktop else None def get_com_servicemanager(self): @@ -162,7 +162,7 @@ class ImpressController(PresentationController): try: return Dispatch(u'com.sun.star.ServiceManager') except pywintypes.com_error: - log.exception(u'Failed to get COM service manager. ' + log.warn(u'Failed to get COM service manager. ' u'Impress Controller has been disabled') return None @@ -180,7 +180,7 @@ class ImpressController(PresentationController): else: desktop = self.get_com_desktop() except: - log.exception(u'Failed to find an OpenOffice desktop to terminate') + log.warn(u'Failed to find an OpenOffice desktop to terminate') if not desktop: return docs = desktop.getComponents() @@ -191,7 +191,7 @@ class ImpressController(PresentationController): desktop.terminate() log.debug(u'OpenOffice killed') except: - log.exception(u'Failed to terminate OpenOffice') + log.warn(u'Failed to terminate OpenOffice') class ImpressDocument(PresentationDocument): @@ -244,7 +244,7 @@ class ImpressDocument(PresentationDocument): self.document = desktop.loadComponentFromURL(url, u'_blank', 0, properties) except: - log.exception(u'Failed to load presentation %s' % url) + log.warn(u'Failed to load presentation %s' % url) return False if os.name == u'nt': # As we can't start minimized the Impress window gets in the way. @@ -323,7 +323,7 @@ class ImpressDocument(PresentationDocument): self.presentation = None self.document.dispose() except: - log.exception("Closing presentation failed") + log.warn("Closing presentation failed") self.document = None self.controller.remove_doc(self) @@ -341,7 +341,7 @@ class ImpressDocument(PresentationDocument): log.debug("getPresentation failed to find a presentation") return False except: - log.exception("getPresentation failed to find a presentation") + log.warn("getPresentation failed to find a presentation") return False return True diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index b757b5bfd..e138d4ef9 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -46,14 +46,14 @@ class PresentationMediaItem(MediaManagerItem): """ log.info(u'Presentations Media Item loaded') - def __init__(self, parent, icon, title, controllers): + def __init__(self, parent, plugin, icon, controllers): """ Constructor. Setup defaults """ self.controllers = controllers self.IconPath = u'presentations/presentation' self.Automatic = u'' - MediaManagerItem.__init__(self, parent, self, icon) + MediaManagerItem.__init__(self, parent, plugin, icon) self.message_listener = MessageListener(self) self.hasSearch = True QtCore.QObject.connect(Receiver.get_receiver(), @@ -82,7 +82,7 @@ class PresentationMediaItem(MediaManagerItem): for type in types: if fileType.find(type) == -1: fileType += u'*.%s ' % type - self.parent.serviceManager.supportedSuffixes(type) + self.plugin.serviceManager.supportedSuffixes(type) self.onNewFileMasks = unicode(translate('PresentationPlugin.MediaItem', 'Presentations (%s)')) % fileType @@ -161,14 +161,14 @@ class PresentationMediaItem(MediaManagerItem): titles = [os.path.split(file)[1] for file in currlist] Receiver.send_message(u'cursor_busy') if not initialLoad: - self.parent.formparent.displayProgressBar(len(files)) + self.plugin.formparent.displayProgressBar(len(files)) # Sort the themes by its filename considering language specific # characters. lower() is needed for windows! files.sort(cmp=locale.strcoll, key=lambda filename: os.path.split(unicode(filename))[1].lower()) for file in files: if not initialLoad: - self.parent.formparent.incrementProgressBar() + self.plugin.formparent.incrementProgressBar() if currlist.count(file) > 0: continue filename = os.path.split(unicode(file))[1] @@ -208,7 +208,7 @@ class PresentationMediaItem(MediaManagerItem): self.listView.addItem(item_name) Receiver.send_message(u'cursor_normal') if not initialLoad: - self.parent.formparent.finishedProgressBar() + self.plugin.formparent.finishedProgressBar() def onDeleteClick(self): """ diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 171039d15..aa9e5ba24 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -77,7 +77,7 @@ class PresentationPlugin(Plugin): try: self.controllers[controller].start_process() except: - log.exception(u'Failed to start controller process') + log.warn(u'Failed to start controller process') self.controllers[controller].available = False self.mediaItem.buildFileMaskString() @@ -99,7 +99,7 @@ class PresentationPlugin(Plugin): Create the Media Manager List """ return PresentationMediaItem( - self, self.icon, self.name, self.controllers) + self.mediadock.media_dock, self, self.icon, self.controllers) def registerControllers(self, controller): """ @@ -128,7 +128,7 @@ class PresentationPlugin(Plugin): try: __import__(modulename, globals(), locals(), []) except ImportError: - log.exception(u'Failed to import %s on path %s', + log.warn(u'Failed to import %s on path %s', modulename, path) controller_classes = PresentationController.__subclasses__() for controller_class in controller_classes: diff --git a/openlp/plugins/remotes/html/index.html b/openlp/plugins/remotes/html/index.html index 6390b9446..4d3076e3a 100644 --- a/openlp/plugins/remotes/html/index.html +++ b/openlp/plugins/remotes/html/index.html @@ -27,91 +27,109 @@ --> - OpenLP 2.0 Remote + ${app_title} +
-

OpenLP 2.0 Remote

+

${app_title}

- Service Manager - Slide Controller - Alerts - Search + ${service_manager} + ${slide_controller} + ${alerts} + ${search}
- Back -

Service Manager

- Refresh + ${back} +

${service_manager}

+ ${refresh}
- Back -

Slide Controller

- Refresh + ${back} +

${slide_controller}

+ ${refresh}
- Back -

Alerts

+ ${back} +

${alerts}

- +
- Show Alert + ${show_alert}
+
+
+

${options}

+
+
+ + ${go_live} + ${add_to_service} +
+
diff --git a/openlp/plugins/remotes/html/json2.js b/openlp/plugins/remotes/html/json2.js old mode 100755 new mode 100644 diff --git a/openlp/plugins/remotes/html/openlp.js b/openlp/plugins/remotes/html/openlp.js index 3f7c331b9..038763fe5 100644 --- a/openlp/plugins/remotes/html/openlp.js +++ b/openlp/plugins/remotes/html/openlp.js @@ -47,7 +47,7 @@ window.OpenLP = { var select = $("#search-plugin"); select.html(""); $.each(data.results.items, function (idx, value) { - select.append(""); + select.append(""); }); select.selectmenu("refresh"); } @@ -215,16 +215,15 @@ window.OpenLP = { var ul = $("#search > div[data-role=content] > ul[data-role=listview]"); ul.html(""); if (data.results.items.length == 0) { - var li = $("
  • ").text('No results'); + var li = $("
  • ").text(translationStrings["no_results"]); ul.append(li); } else { $.each(data.results.items, function (idx, value) { - var item = $("
  • ").text(value[1]); - var golive = $("Go Live").attr("value", value[0]).click(OpenLP.goLive); - var additem = $("Add To Service").attr("value", value[0]).click(OpenLP.addToService); - item.append($("