diff --git a/documentation/SongFormat.txt b/documentation/SongFormat.txt deleted file mode 100644 index 31b202dd6..000000000 --- a/documentation/SongFormat.txt +++ /dev/null @@ -1,124 +0,0 @@ -openlp.org 2.x Song Database Structure -======================================================================== - -Introduction ------------- -The song database in openlp.org 2.x is similar to the 1.x format. The -biggest differences are the addition of extra tables, and the use of -SQLite version 3. - -The song database contains the following tables: -- authors -- authors_songs -- song_books -- songs -- songs_topics -- topics - - -"authors" Table ---------------- -This table holds the names of all the authors. It has the following -columns: - -* id -* first_name -* last_name -* display_name - - -"authors_songs" Table ---------------------- -This is a bridging table between the "authors" and "songs" tables, which -serves to create a many-to-many relationship between the two tables. It -has the following columns: - -* author_id -* song_id - - -"song_books" Table ------------------- -The "song_books" table holds a list of books that a congregation gets -their songs from, or old hymnals now no longer used. This table has the -following columns: - -* id -* name -* publisher - - -"songs" Table -------------- -This table contains the songs, and each song has a list of attributes. -The "songs" table has the following columns: - -* id -* song_book_id -* title -* lyrics -* verse_order -* copyright -* comments -* ccli_number -* song_number -* theme_name -* search_title -* search_lyrics - - -"songs_topics" Table --------------------- -This is a bridging table between the "songs" and "topics" tables, which -serves to create a many-to-many relationship between the two tables. It -has the following columns: - -* song_id -* topic_id - - -"topics" Table --------------- -The topics table holds a selection of topics that songs can cover. This -is useful when a worship leader wants to select songs with a certain -theme. This table has the following columns: - -* id -* name - - -The lyrics definition (more or less similar to interformat to/from ChangingSong -The tags can also be used within the lyrics test. - -! Please note that this format has been checked at http://validator.w3.org/#validate_by_upload - - - Amazing Grace - - name of verse specific theme (optional) - any text (optional) - - Amazing grace, how ... - - - A b c - D e f - - ... - - - name of verse specific theme (optional) - any text (optional) - ... - - - - Erstaunliche Anmut - - Erstaunliche Anmut, wie - ... - - - ... - - diff --git a/openlp/core/lib/dockwidget.py b/openlp/core/lib/dockwidget.py index 24841ec33..32d6ce765 100644 --- a/openlp/core/lib/dockwidget.py +++ b/openlp/core/lib/dockwidget.py @@ -31,6 +31,8 @@ import logging from PyQt4 import QtGui +from openlp.core.lib import build_icon + log = logging.getLogger(__name__) class OpenLPDockWidget(QtGui.QDockWidget): @@ -47,4 +49,4 @@ class OpenLPDockWidget(QtGui.QDockWidget): if name: self.setObjectName(name) if icon: - self.setWindowIcon(icon) + self.setWindowIcon(build_icon(icon)) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 39c37e2a9..683459825 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -366,33 +366,34 @@ class MediaManagerItem(QtGui.QWidget): count += 1 return filelist - def validate(self, file, thumb): + def validate(self, image, thumb): """ - Validates to see if the file still exists or thumbnail is up to date + Validates whether an image still exists and, if it does, is the + thumbnail representation of the image up to date. """ - if not os.path.exists(file): + if not os.path.exists(image): return False if os.path.exists(thumb): - filedate = os.stat(file).st_mtime - thumbdate = os.stat(thumb).st_mtime - # if file updated rebuild icon - if filedate > thumbdate: - self.iconFromFile(file, thumb) + imageDate = os.stat(image).st_mtime + thumbDate = os.stat(thumb).st_mtime + # If image has been updated rebuild icon + if imageDate > thumbDate: + self.iconFromFile(image, thumb) else: - self.iconFromFile(file, thumb) + self.iconFromFile(image, thumb) return True - def iconFromFile(self, file, thumb): + def iconFromFile(self, image, thumb): """ - Create a thumbnail icon from a given file + Create a thumbnail icon from a given image. - ``file`` - The file to create the icon from + ``image`` + The image file to create the icon from. ``thumb`` The filename to save the thumbnail to """ - icon = build_icon(unicode(file)) + icon = build_icon(unicode(image)) pixmap = icon.pixmap(QtCore.QSize(88, 50)) ext = os.path.splitext(thumb)[1].lower() pixmap.save(thumb, ext[1:]) @@ -403,12 +404,16 @@ class MediaManagerItem(QtGui.QWidget): u'defined by the plugin') def onNewClick(self): - raise NotImplementedError(u'MediaManagerItem.onNewClick needs to be ' - u'defined by the plugin') + """ + Hook for plugins to define behaviour for adding new items. + """ + pass def onEditClick(self): - raise NotImplementedError(u'MediaManagerItem.onEditClick needs to be ' - u'defined by the plugin') + """ + Hook for plugins to define behaviour for editing items. + """ + pass def onDeleteClick(self): raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to ' diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 8cfffef45..f388e03b4 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -30,7 +30,7 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate +from openlp.core.lib import build_icon, Receiver, translate log = logging.getLogger(__name__) @@ -108,3 +108,77 @@ def media_item_combo_box(parent, name): combo.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToMinimumContentsLength) combo.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) return combo + +def delete_push_button(parent, icon=None): + """ + Return a standard push button with delete label. + """ + delete_button = QtGui.QPushButton(parent) + delete_button.setObjectName(u'deleteButton') + delete_icon = icon if icon else u':/general/general_delete.png' + delete_button.setIcon(build_icon(delete_icon)) + delete_button.setText(translate('OpenLP.Ui', '&Delete')) + delete_button.setToolTip( + translate('OpenLP.Ui', 'Delete the selected item.')) + QtCore.QObject.connect(delete_button, + QtCore.SIGNAL(u'clicked()'), parent.onDeleteButtonClicked) + return delete_button + +def up_down_push_button_set(parent): + """ + Return a standard set of two push buttons for up and down use with lists. + """ + up_button = QtGui.QPushButton(parent) + up_button.setIcon(build_icon(u':/services/service_up.png')) + up_button.setObjectName(u'upButton') + up_button.setToolTip( + translate('OpenLP.Ui', 'Move selection up one position.')) + down_button = QtGui.QPushButton(parent) + down_button.setIcon(build_icon(u':/services/service_down.png')) + down_button.setObjectName(u'downButton') + down_button.setToolTip( + translate('OpenLP.Ui', 'Move selection down one position.')) + QtCore.QObject.connect(up_button, + QtCore.SIGNAL(u'clicked()'), parent.onUpButtonClicked) + QtCore.QObject.connect(down_button, + QtCore.SIGNAL(u'clicked()'), parent.onDownButtonClicked) + return up_button, down_button + +def base_action(parent, name): + """ + Return the most basic action with the object name set. + """ + action = QtGui.QAction(parent) + action.setObjectName(name) + return action + +def checkable_action(parent, name, checked=None): + """ + Return a standard action with the checkable attribute set. + """ + action = base_action(parent, name) + action.setCheckable(True) + if checked is not None: + action.setChecked(checked) + return action + +def icon_action(parent, name, icon, checked=None): + """ + Return a standard action with an icon. + """ + if checked is not None: + action = checkable_action(parent, name, checked) + else: + action = base_action(parent, name) + action.setIcon(build_icon(icon)) + return action + +def shortcut_action(parent, text, shortcuts, function): + """ + Return a shortcut enabled action. + """ + action = QtGui.QAction(text, parent) + action.setShortcuts(shortcuts) + action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) + QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), function) + return action diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 6f57861c4..9af4931fb 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -28,11 +28,12 @@ import logging from PyQt4 import QtCore, QtGui +from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \ + SettingsManager, PluginManager, Receiver, translate +from openlp.core.lib.ui import base_action, checkable_action, icon_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ ThemeManager, SlideController, PluginForm, MediaDockManager, \ ShortcutListForm -from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \ - SettingsManager, PluginManager, Receiver, translate from openlp.core.utils import AppLocation, add_actions, LanguageManager, \ ActionList @@ -124,9 +125,8 @@ class Ui_MainWindow(object): self.DefaultThemeLabel.setObjectName(u'DefaultThemeLabel') self.StatusBar.addPermanentWidget(self.DefaultThemeLabel) # Create the MediaManager - self.MediaManagerDock = OpenLPDockWidget( - mainWindow, u'MediaManagerDock', - build_icon(u':/system/system_mediamanager.png')) + self.MediaManagerDock = OpenLPDockWidget(mainWindow, + u'MediaManagerDock', u':/system/system_mediamanager.png') self.MediaManagerDock.setStyleSheet(MEDIA_MANAGER_STYLE) self.MediaManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_left) @@ -137,9 +137,8 @@ class Ui_MainWindow(object): mainWindow.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.MediaManagerDock) # Create the service manager - self.ServiceManagerDock = OpenLPDockWidget( - mainWindow, u'ServiceManagerDock', - build_icon(u':/system/system_servicemanager.png')) + self.ServiceManagerDock = OpenLPDockWidget(mainWindow, + u'ServiceManagerDock', u':/system/system_servicemanager.png') self.ServiceManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_right) self.ServiceManagerContents = ServiceManager(mainWindow, @@ -148,9 +147,8 @@ class Ui_MainWindow(object): mainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.ServiceManagerDock) # Create the theme manager - self.ThemeManagerDock = OpenLPDockWidget( - mainWindow, u'ThemeManagerDock', - build_icon(u':/system/system_thememanager.png')) + self.ThemeManagerDock = OpenLPDockWidget(mainWindow, + u'ThemeManagerDock', u':/system/system_thememanager.png') self.ThemeManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_right) self.ThemeManagerContents = ThemeManager(mainWindow, @@ -160,103 +158,70 @@ class Ui_MainWindow(object): mainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.ThemeManagerDock) # Create the menu items - self.FileNewItem = QtGui.QAction(mainWindow) - self.FileNewItem.setIcon(build_icon(u':/general/general_new.png')) - self.FileNewItem.setObjectName(u'FileNewItem') + self.FileNewItem = icon_action(mainWindow, u'FileNewItem', + u':/general/general_new.png') mainWindow.actionList.add_action(self.FileNewItem, u'File') - self.FileOpenItem = QtGui.QAction(mainWindow) - self.FileOpenItem.setIcon(build_icon(u':/general/general_open.png')) - self.FileOpenItem.setObjectName(u'FileOpenItem') + self.FileOpenItem = icon_action(mainWindow, u'FileOpenItem', + u':/general/general_open.png') mainWindow.actionList.add_action(self.FileOpenItem, u'File') - self.FileSaveItem = QtGui.QAction(mainWindow) - self.FileSaveItem.setIcon(build_icon(u':/general/general_save.png')) - self.FileSaveItem.setObjectName(u'FileSaveItem') + self.FileSaveItem = icon_action(mainWindow, u'FileSaveItem', + u':/general/general_save.png') mainWindow.actionList.add_action(self.FileSaveItem, u'File') - self.FileSaveAsItem = QtGui.QAction(mainWindow) - self.FileSaveAsItem.setObjectName(u'FileSaveAsItem') + self.FileSaveAsItem = base_action(mainWindow, u'FileSaveAsItem') mainWindow.actionList.add_action(self.FileSaveAsItem, u'File') - self.printServiceOrderItem = QtGui.QAction(mainWindow) - self.printServiceOrderItem.setObjectName(u'printServiceItem') + self.printServiceOrderItem = base_action( + mainWindow, u'printServiceItem') mainWindow.actionList.add_action( self.printServiceOrderItem, u'Print Service Order') - self.FileExitItem = QtGui.QAction(mainWindow) - self.FileExitItem.setIcon(build_icon(u':/system/system_exit.png')) - self.FileExitItem.setObjectName(u'FileExitItem') + self.FileExitItem = icon_action(mainWindow, u'FileExitItem', + u':/system/system_exit.png') mainWindow.actionList.add_action(self.FileExitItem, u'File') - self.ImportThemeItem = QtGui.QAction(mainWindow) - self.ImportThemeItem.setObjectName(u'ImportThemeItem') + self.ImportThemeItem = base_action(mainWindow, u'ImportThemeItem') mainWindow.actionList.add_action(self.ImportThemeItem, u'Import') - self.ImportLanguageItem = QtGui.QAction(mainWindow) - self.ImportLanguageItem.setObjectName(u'ImportLanguageItem') + self.ImportLanguageItem = base_action(mainWindow, u'ImportLanguageItem') mainWindow.actionList.add_action(self.ImportLanguageItem, u'Import') - self.ExportThemeItem = QtGui.QAction(mainWindow) - self.ExportThemeItem.setObjectName(u'ExportThemeItem') + self.ExportThemeItem = base_action(mainWindow, u'ExportThemeItem') mainWindow.actionList.add_action(self.ExportThemeItem, u'Export') - self.ExportLanguageItem = QtGui.QAction(mainWindow) - self.ExportLanguageItem.setObjectName(u'ExportLanguageItem') + self.ExportLanguageItem = base_action(mainWindow, u'ExportLanguageItem') mainWindow.actionList.add_action(self.ExportLanguageItem, u'Export') - self.ViewMediaManagerItem = QtGui.QAction(mainWindow) - self.ViewMediaManagerItem.setCheckable(True) - self.ViewMediaManagerItem.setChecked(self.MediaManagerDock.isVisible()) - self.ViewMediaManagerItem.setIcon( - build_icon(u':/system/system_mediamanager.png')) - self.ViewMediaManagerItem.setObjectName(u'ViewMediaManagerItem') - self.ViewThemeManagerItem = QtGui.QAction(mainWindow) - self.ViewThemeManagerItem.setCheckable(True) - self.ViewThemeManagerItem.setChecked(self.ThemeManagerDock.isVisible()) - self.ViewThemeManagerItem.setIcon( - build_icon(u':/system/system_thememanager.png')) - self.ViewThemeManagerItem.setObjectName(u'ViewThemeManagerItem') + self.ViewMediaManagerItem = icon_action(mainWindow, + u'ViewMediaManagerItem', u':/system/system_mediamanager.png', + self.MediaManagerDock.isVisible()) + self.ViewThemeManagerItem = icon_action(mainWindow, + u'ViewThemeManagerItem', u':/system/system_thememanager.png', + self.ThemeManagerDock.isVisible()) mainWindow.actionList.add_action(self.ViewMediaManagerItem, u'View') - self.ViewServiceManagerItem = QtGui.QAction(mainWindow) - self.ViewServiceManagerItem.setCheckable(True) - self.ViewServiceManagerItem.setChecked( + self.ViewServiceManagerItem = icon_action(mainWindow, + u'ViewServiceManagerItem', u':/system/system_servicemanager.png', self.ServiceManagerDock.isVisible()) - self.ViewServiceManagerItem.setIcon( - build_icon(u':/system/system_servicemanager.png')) - self.ViewServiceManagerItem.setObjectName(u'ViewServiceManagerItem') mainWindow.actionList.add_action(self.ViewServiceManagerItem, u'View') - self.ViewPreviewPanel = QtGui.QAction(mainWindow) - self.ViewPreviewPanel.setCheckable(True) - self.ViewPreviewPanel.setChecked(previewVisible) - self.ViewPreviewPanel.setObjectName(u'ViewPreviewPanel') + self.ViewPreviewPanel = checkable_action(mainWindow, + u'ViewPreviewPanel', previewVisible) mainWindow.actionList.add_action(self.ViewPreviewPanel, u'View') - self.ViewLivePanel = QtGui.QAction(mainWindow) - self.ViewLivePanel.setCheckable(True) - self.ViewLivePanel.setChecked(liveVisible) - self.ViewLivePanel.setObjectName(u'ViewLivePanel') + self.ViewLivePanel = checkable_action(mainWindow, u'ViewLivePanel', + liveVisible) mainWindow.actionList.add_action(self.ViewLivePanel, u'View') - self.ModeDefaultItem = QtGui.QAction(mainWindow) - self.ModeDefaultItem.setCheckable(True) - self.ModeDefaultItem.setObjectName(u'ModeDefaultItem') + self.ModeDefaultItem = checkable_action(mainWindow, u'ModeDefaultItem') mainWindow.actionList.add_action(self.ModeDefaultItem, u'View Mode') - self.ModeSetupItem = QtGui.QAction(mainWindow) - self.ModeSetupItem.setCheckable(True) - self.ModeSetupItem.setObjectName(u'ModeLiveItem') + self.ModeSetupItem = checkable_action(mainWindow, u'ModeLiveItem') mainWindow.actionList.add_action(self.ModeSetupItem, u'View Mode') - self.ModeLiveItem = QtGui.QAction(mainWindow) - self.ModeLiveItem.setCheckable(True) - self.ModeLiveItem.setObjectName(u'ModeLiveItem') + self.ModeLiveItem = checkable_action(mainWindow, u'ModeLiveItem') mainWindow.actionList.add_action(self.ModeLiveItem, u'View Mode') self.ModeGroup = QtGui.QActionGroup(mainWindow) self.ModeGroup.addAction(self.ModeDefaultItem) self.ModeGroup.addAction(self.ModeSetupItem) self.ModeGroup.addAction(self.ModeLiveItem) self.ModeDefaultItem.setChecked(True) - self.ToolsAddToolItem = QtGui.QAction(mainWindow) - self.ToolsAddToolItem.setIcon(build_icon(u':/tools/tools_add.png')) - self.ToolsAddToolItem.setObjectName(u'ToolsAddToolItem') + self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', + u':/tools/tools_add.png') mainWindow.actionList.add_action(self.ToolsAddToolItem, u'Tools') - self.SettingsPluginListItem = QtGui.QAction(mainWindow) - self.SettingsPluginListItem.setIcon( - build_icon(u':/system/settings_plugin_list.png')) - self.SettingsPluginListItem.setObjectName(u'SettingsPluginListItem') + self.SettingsPluginListItem = icon_action(mainWindow, + u'SettingsPluginListItem', u':/system/settings_plugin_list.png') mainWindow.actionList.add_action(self.SettingsPluginListItem, u'Settings') # i18n Language Items - self.AutoLanguageItem = QtGui.QAction(mainWindow) - self.AutoLanguageItem.setObjectName(u'AutoLanguageItem') - self.AutoLanguageItem.setCheckable(True) + self.AutoLanguageItem = checkable_action(mainWindow, + u'AutoLanguageItem') mainWindow.actionList.add_action(self.AutoLanguageItem, u'Settings') self.LanguageGroup = QtGui.QActionGroup(mainWindow) self.LanguageGroup.setExclusive(True) @@ -266,39 +231,28 @@ class Ui_MainWindow(object): qmList = LanguageManager.get_qm_list() savedLanguage = LanguageManager.get_language() for key in sorted(qmList.keys()): - languageItem = QtGui.QAction(mainWindow) - languageItem.setObjectName(key) - languageItem.setCheckable(True) + languageItem = checkable_action(mainWindow, key) if qmList[key] == savedLanguage: languageItem.setChecked(True) add_actions(self.LanguageGroup, [languageItem]) - self.SettingsShortcutsItem = QtGui.QAction(mainWindow) - self.SettingsShortcutsItem.setIcon( - build_icon(u':/system/system_configure_shortcuts.png')) - self.SettingsShortcutsItem.setObjectName(u'SettingsShortcutsItem') - self.SettingsConfigureItem = QtGui.QAction(mainWindow) - self.SettingsConfigureItem.setIcon( - build_icon(u':/system/system_settings.png')) - self.SettingsConfigureItem.setObjectName(u'SettingsConfigureItem') + self.SettingsShortcutsItem = icon_action(mainWindow, + u'SettingsShortcutsItem', + u':/system/system_configure_shortcuts.png') + self.SettingsConfigureItem = icon_action(mainWindow, + u'SettingsConfigureItem', u':/system/system_settings.png') mainWindow.actionList.add_action(self.SettingsShortcutsItem, u'Settings') - self.HelpDocumentationItem = QtGui.QAction(mainWindow) - self.HelpDocumentationItem.setIcon( - build_icon(u':/system/system_help_contents.png')) - self.HelpDocumentationItem.setObjectName(u'HelpDocumentationItem') + self.HelpDocumentationItem = icon_action(mainWindow, + u'HelpDocumentationItem', u':/system/system_help_contents.png') self.HelpDocumentationItem.setEnabled(False) mainWindow.actionList.add_action(self.HelpDocumentationItem, u'Help') - self.HelpAboutItem = QtGui.QAction(mainWindow) - self.HelpAboutItem.setIcon( - build_icon(u':/system/system_about.png')) - self.HelpAboutItem.setObjectName(u'HelpAboutItem') + self.HelpAboutItem = icon_action(mainWindow, u'HelpAboutItem', + u':/system/system_about.png') mainWindow.actionList.add_action(self.HelpAboutItem, u'Help') - self.HelpOnlineHelpItem = QtGui.QAction(mainWindow) - self.HelpOnlineHelpItem.setObjectName(u'HelpOnlineHelpItem') + self.HelpOnlineHelpItem = base_action(mainWindow, u'HelpOnlineHelpItem') self.HelpOnlineHelpItem.setEnabled(False) mainWindow.actionList.add_action(self.HelpOnlineHelpItem, u'Help') - self.HelpWebSiteItem = QtGui.QAction(mainWindow) - self.HelpWebSiteItem.setObjectName(u'HelpWebSiteItem') + self.HelpWebSiteItem = base_action(mainWindow, u'HelpWebSiteItem') mainWindow.actionList.add_action(self.HelpWebSiteItem, u'Help') add_actions(self.FileImportMenu, (self.ImportThemeItem, self.ImportLanguageItem)) @@ -999,8 +953,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): u'advanced/recent file count', QtCore.QVariant(4)).toInt()[0] self.FileMenu.clear() add_actions(self.FileMenu, self.FileMenuActions[:-1]) - existingRecentFiles = [file for file in self.recentFiles - if QtCore.QFile.exists(file)] + existingRecentFiles = [recentFile for recentFile in self.recentFiles + if QtCore.QFile.exists(recentFile)] recentFilesToDisplay = existingRecentFiles[0:recentFileCount] if recentFilesToDisplay: self.FileMenu.addSeparator() diff --git a/openlp/core/ui/serviceitemeditdialog.py b/openlp/core/ui/serviceitemeditdialog.py index d4c86fa61..4046e94ab 100644 --- a/openlp/core/ui/serviceitemeditdialog.py +++ b/openlp/core/ui/serviceitemeditdialog.py @@ -26,8 +26,9 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate, build_icon -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib import translate +from openlp.core.lib.ui import save_cancel_button_box, delete_push_button, \ + up_down_push_button_set class Ui_ServiceItemEditDialog(object): def setupUi(self, serviceItemEditDialog): @@ -40,17 +41,12 @@ class Ui_ServiceItemEditDialog(object): self.dialogLayout.addWidget(self.listWidget, 0, 0) self.buttonLayout = QtGui.QVBoxLayout() self.buttonLayout.setObjectName(u'buttonLayout') - self.deleteButton = QtGui.QPushButton(serviceItemEditDialog) - self.deleteButton.setObjectName(u'deleteButton') + self.deleteButton = delete_push_button(serviceItemEditDialog) self.buttonLayout.addWidget(self.deleteButton) self.buttonLayout.addStretch() - self.upButton = QtGui.QPushButton(serviceItemEditDialog) - self.upButton.setIcon(build_icon(u':/services/service_up.png')) - self.upButton.setObjectName(u'upButton') + self.upButton, self.downButton = up_down_push_button_set( + serviceItemEditDialog) self.buttonLayout.addWidget(self.upButton) - self.downButton = QtGui.QPushButton(serviceItemEditDialog) - self.downButton.setIcon(build_icon(u':/services/service_down.png')) - self.downButton.setObjectName(u'downButton') self.buttonLayout.addWidget(self.downButton) self.dialogLayout.addLayout(self.buttonLayout, 0, 1) self.dialogLayout.addWidget( @@ -61,5 +57,3 @@ class Ui_ServiceItemEditDialog(object): def retranslateUi(self, serviceItemEditDialog): serviceItemEditDialog.setWindowTitle( translate('OpenLP.ServiceItemEditForm', 'Reorder Service Item')) - self.deleteButton.setText(translate('OpenLP.ServiceItemEditForm', - 'Delete')) diff --git a/openlp/core/ui/serviceitemeditform.py b/openlp/core/ui/serviceitemeditform.py index 0621a44bd..70518f30c 100644 --- a/openlp/core/ui/serviceitemeditform.py +++ b/openlp/core/ui/serviceitemeditform.py @@ -39,13 +39,6 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) self.itemList = [] - # enable drop - QtCore.QObject.connect(self.upButton, - QtCore.SIGNAL(u'clicked()'), self.onItemUp) - QtCore.QObject.connect(self.downButton, - QtCore.SIGNAL(u'clicked()'), self.onItemDown) - QtCore.QObject.connect(self.deleteButton, - QtCore.SIGNAL(u'clicked()'), self.onItemDelete) QtCore.QObject.connect(self.listWidget, QtCore.SIGNAL(u'currentRowChanged(int)'), self.onCurrentRowChanged) @@ -77,7 +70,7 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog): item_name = QtGui.QListWidgetItem(frame[u'title']) self.listWidget.addItem(item_name) - def onItemDelete(self): + def onDeleteButtonClicked(self): """ Delete the current row. """ @@ -92,13 +85,13 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog): else: self.listWidget.setCurrentRow(row) - def onItemUp(self): + def onUpButtonClicked(self): """ Move the current row up in the list. """ self.__moveItem(u'up') - def onItemDown(self): + def onDownButtonClicked(self): """ Move the current row down in the list """ diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index ab8a656ff..bf0453e05 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -30,9 +30,10 @@ import os from PyQt4 import QtCore, QtGui from PyQt4.phonon import Phonon -from openlp.core.ui import HideMode, MainDisplay from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \ ItemCapabilities, translate +from openlp.core.lib.ui import shortcut_action +from openlp.core.ui import HideMode, MainDisplay log = logging.getLogger(__name__) @@ -317,18 +318,8 @@ class SlideController(QtGui.QWidget): self.mediabar.setVisible(False) if self.isLive: self.setLiveHotkeys(self) - self.previewListWidget.addActions( - [self.previousItem, - self.nextItem, - self.previousService, - self.nextService, - self.escapeItem]) - self.display.addActions( - [self.previousItem, - self.nextItem, - self.previousService, - self.nextService, - self.escapeItem]) + self.__addActionsToWidget(self.previewListWidget) + self.__addActionsToWidget(self.display) else: self.setPreviewHotkeys() self.previewListWidget.addActions( @@ -390,28 +381,17 @@ class SlideController(QtGui.QWidget): self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown]) self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) actionList.add_action(self.nextItem, u'Live') - self.previousService = QtGui.QAction(translate( - 'OpenLP.SlideController', 'Previous Service'), parent) - self.previousService.setShortcuts([QtCore.Qt.Key_Left, 0]) - self.previousService.setShortcutContext( - QtCore.Qt.WidgetWithChildrenShortcut) - QtCore.QObject.connect(self.previousService, - QtCore.SIGNAL(u'triggered()'), self.servicePrevious) + self.previousService = shortcut_action(parent, + translate('OpenLP.SlideController', 'Previous Service'), + [QtCore.Qt.Key_Left, 0], self.servicePrevious) actionList.add_action(self.previousService, u'Live') - self.nextService = QtGui.QAction(translate( - 'OpenLP.SlideController', 'Next Service'), parent) - self.nextService.setShortcuts([QtCore.Qt.Key_Right, 0]) - self.nextService.setShortcutContext( - QtCore.Qt.WidgetWithChildrenShortcut) - QtCore.QObject.connect(self.nextService, - QtCore.SIGNAL(u'triggered()'), self.serviceNext) + self.nextService = shortcut_action(parent, + translate('OpenLP.SlideController', 'Next Service'), + [QtCore.Qt.Key_Right, 0], self.serviceNext) actionList.add_action(self.nextService, u'Live') - self.escapeItem = QtGui.QAction(translate( - 'OpenLP.SlideController', 'Escape Item'), parent) - self.escapeItem.setShortcuts([QtCore.Qt.Key_Escape, 0]) - self.escapeItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) - QtCore.QObject.connect(self.escapeItem, - QtCore.SIGNAL(u'triggered()'), self.liveEscape) + self.escapeItem = shortcut_action(parent, + translate('OpenLP.SlideController', 'Escape Item'), + [QtCore.Qt.Key_Escape, 0], self.liveEscape) actionList.add_action(self.escapeItem, u'Live') def liveEscape(self): @@ -435,12 +415,7 @@ class SlideController(QtGui.QWidget): self.display.alertTab = self.alertTab self.display.setup() if self.isLive: - self.display.addActions( - [self.previousItem, - self.nextItem, - self.previousService, - self.nextService, - self.escapeItem]) + self.__addActionsToWidget(self.display) # The SlidePreview's ratio. self.ratio = float(self.screens.current[u'size'].width()) / \ float(self.screens.current[u'size'].height()) @@ -448,6 +423,12 @@ class SlideController(QtGui.QWidget): if self.serviceItem: self.refreshServiceItem() + def __addActionsToWidget(self, widget): + widget.addActions([ + self.previousItem, self.nextItem, + self.previousService, self.nextService, + self.escapeItem]) + def previewSizeChanged(self): """ Takes care of the SlidePreview's size. Is called when one of the the @@ -575,10 +556,7 @@ class SlideController(QtGui.QWidget): slideno = 0 # If service item is the same as the current on only change slide if item.__eq__(self.serviceItem): - if slideno + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(slideno + 1, 0)) - self.previewListWidget.selectRow(slideno) + self.__checkUpdateSelectedSlide(slideno) self.onSlideSelected() return self._processItem(item, slideno) @@ -677,10 +655,7 @@ class SlideController(QtGui.QWidget): self.previewListWidget.selectRow( self.previewListWidget.rowCount() - 1) else: - if slideno + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(slideno + 1, 0)) - self.previewListWidget.selectRow(slideno) + self.__checkUpdateSelectedSlide(slideno) def onTextRequest(self): """ @@ -729,10 +704,7 @@ class SlideController(QtGui.QWidget): [self.serviceItem, self.isLive, index]) self.updatePreview() else: - if index + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(index + 1, 0)) - self.previewListWidget.selectRow(index) + self.__checkUpdateSelectedSlide(index) self.onSlideSelected() def mainDisplaySetBackground(self): @@ -891,10 +863,7 @@ class SlideController(QtGui.QWidget): """ The slide has been changed. Update the slidecontroller accordingly """ - if row + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(row + 1, 0)) - self.previewListWidget.selectRow(row) + self.__checkUpdateSelectedSlide(row) self.updatePreview() Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix, row) @@ -945,10 +914,7 @@ class SlideController(QtGui.QWidget): else: Receiver.send_message('servicemanager_next_item') return - if row + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(row + 1, 0)) - self.previewListWidget.selectRow(row) + self.__checkUpdateSelectedSlide(row) self.onSlideSelected() def onSlideSelectedPreviousNoloop(self): @@ -971,12 +937,15 @@ class SlideController(QtGui.QWidget): row = self.previewListWidget.rowCount() - 1 else: row = 0 - if row + 1 < self.previewListWidget.rowCount(): - self.previewListWidget.scrollToItem( - self.previewListWidget.item(row + 1, 0)) - self.previewListWidget.selectRow(row) + self.__checkUpdateSelectedSlide(row) self.onSlideSelected() + def __checkUpdateSelectedSlide(self, row): + if row + 1 < self.previewListWidget.rowCount(): + self.previewListWidget.scrollToItem( + self.previewListWidget.item(row + 1, 0)) + self.previewListWidget.selectRow(row) + def onSlideSelectedLast(self): """ Go to the last slide. diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index e8765b9e2..49522df70 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -36,6 +36,8 @@ class Ui_ThemeWizard(object): themeWizard.setWizardStyle(QtGui.QWizard.ModernStyle) themeWizard.setOptions(QtGui.QWizard.IndependentPages | QtGui.QWizard.NoBackButtonOnStartPage) + self.spacer = QtGui.QSpacerItem(10, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) # Welcome Page add_welcome_page(themeWizard, u':/wizards/wizard_createtheme.bmp') # Background Page @@ -52,10 +54,8 @@ class Ui_ThemeWizard(object): self.backgroundComboBox.setObjectName(u'BackgroundComboBox') self.backgroundTypeLayout.addRow(self.backgroundLabel, self.backgroundComboBox) - self.backgroundTypeSpacer = QtGui.QSpacerItem(10, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) self.backgroundTypeLayout.setItem(1, QtGui.QFormLayout.LabelRole, - self.backgroundTypeSpacer) + self.spacer) self.backgroundLayout.addLayout(self.backgroundTypeLayout) self.backgroundStack = QtGui.QStackedLayout() self.backgroundStack.setObjectName(u'BackgroundStack') @@ -69,10 +69,7 @@ class Ui_ThemeWizard(object): self.colorButton = QtGui.QPushButton(self.colorWidget) self.colorButton.setObjectName(u'ColorButton') self.colorLayout.addRow(self.colorLabel, self.colorButton) - self.colorSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.colorLayout.setItem(1, QtGui.QFormLayout.LabelRole, - self.colorSpacer) + self.colorLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.backgroundStack.addWidget(self.colorWidget) self.gradientWidget = QtGui.QWidget(self.backgroundPage) self.gradientWidget.setObjectName(u'GradientWidget') @@ -98,10 +95,7 @@ class Ui_ThemeWizard(object): self.gradientComboBox.addItems([u'', u'', u'', u'', u'']) self.gradientLayout.addRow(self.gradientTypeLabel, self.gradientComboBox) - self.gradientSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.gradientLayout.setItem(3, QtGui.QFormLayout.LabelRole, - self.gradientSpacer) + self.gradientLayout.setItem(3, QtGui.QFormLayout.LabelRole, self.spacer) self.backgroundStack.addWidget(self.gradientWidget) self.imageWidget = QtGui.QWidget(self.backgroundPage) self.imageWidget.setObjectName(u'ImageWidget') @@ -121,10 +115,7 @@ class Ui_ThemeWizard(object): build_icon(u':/general/general_open.png')) self.imageFileLayout.addWidget(self.imageBrowseButton) self.imageLayout.addRow(self.imageLabel, self.imageFileLayout) - self.imageSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.imageLayout.setItem(1, QtGui.QFormLayout.LabelRole, - self.imageSpacer) + self.imageLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.backgroundStack.addWidget(self.imageWidget) self.backgroundLayout.addLayout(self.backgroundStack) themeWizard.addPage(self.backgroundPage) @@ -236,6 +227,8 @@ class Ui_ThemeWizard(object): self.footerSizeSpinBox.setObjectName(u'FooterSizeSpinBox') self.footerAreaLayout.addRow(self.footerSizeLabel, self.footerSizeSpinBox) + self.footerAreaLayout.setItem(3, QtGui.QFormLayout.LabelRole, + self.spacer) themeWizard.addPage(self.footerAreaPage) # Alignment Page self.alignmentPage = QtGui.QWizardPage() @@ -261,6 +254,8 @@ class Ui_ThemeWizard(object): self.transitionsCheckBox.setObjectName(u'TransitionsCheckBox') self.alignmentLayout.addRow(self.transitionsLabel, self.transitionsCheckBox) + self.alignmentLayout.setItem(3, QtGui.QFormLayout.LabelRole, + self.spacer) themeWizard.addPage(self.alignmentPage) # Area Position Page self.areaPositionPage = QtGui.QWizardPage() @@ -552,16 +547,6 @@ class Ui_ThemeWizard(object): translate('OpenLP.ThemeWizard', 'Theme name:')) # Align all QFormLayouts towards each other. labelWidth = max(self.backgroundLabel.minimumSizeHint().width(), - self.colorLabel.minimumSizeHint().width(), - self.gradientStartLabel.minimumSizeHint().width(), - self.gradientEndLabel.minimumSizeHint().width(), - self.gradientTypeLabel.minimumSizeHint().width(), - self.imageLabel.minimumSizeHint().width()) - self.backgroundTypeSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.colorSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.gradientSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.imageSpacer.changeSize(labelWidth, 0, + self.horizontalLabel.minimumSizeHint().width()) + self.spacer.changeSize(labelWidth, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) diff --git a/openlp/plugins/alerts/forms/alertdialog.py b/openlp/plugins/alerts/forms/alertdialog.py index 272a96f3d..1a8a1fc58 100644 --- a/openlp/plugins/alerts/forms/alertdialog.py +++ b/openlp/plugins/alerts/forms/alertdialog.py @@ -27,6 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate +from openlp.core.lib.ui import delete_push_button class Ui_AlertDialog(object): def setupUi(self, alertDialog): @@ -65,9 +66,8 @@ class Ui_AlertDialog(object): self.saveButton.setIcon(build_icon(u':/general/general_save.png')) self.saveButton.setObjectName(u'saveButton') self.manageButtonLayout.addWidget(self.saveButton) - self.deleteButton = QtGui.QPushButton(alertDialog) - self.deleteButton.setIcon(build_icon(u':/general/general_delete.png')) - self.deleteButton.setObjectName(u'deleteButton') + self.deleteButton = delete_push_button(alertDialog) + self.deleteButton.setEnabled(False) self.manageButtonLayout.addWidget(self.deleteButton) self.manageButtonLayout.addStretch() self.alertDialogLayout.addLayout(self.manageButtonLayout, 1, 1) @@ -75,11 +75,13 @@ class Ui_AlertDialog(object): self.buttonBox.addButton(QtGui.QDialogButtonBox.Close) displayIcon = build_icon(u':/general/general_live.png') self.displayButton = QtGui.QPushButton(alertDialog) + self.displayButton.setEnabled(False) self.displayButton.setIcon(displayIcon) self.displayButton.setObjectName(u'displayButton') self.buttonBox.addButton(self.displayButton, QtGui.QDialogButtonBox.ActionRole) self.displayCloseButton = QtGui.QPushButton(alertDialog) + self.displayCloseButton.setEnabled(False) self.displayCloseButton.setIcon(displayIcon) self.displayCloseButton.setObjectName(u'displayCloseButton') self.buttonBox.addButton(self.displayCloseButton, @@ -101,8 +103,6 @@ class Ui_AlertDialog(object): translate('AlertsPlugin.AlertForm', '&New')) self.saveButton.setText( translate('AlertsPlugin.AlertForm', '&Save')) - self.deleteButton.setText( - translate('AlertsPlugin.AlertForm', '&Delete')) self.displayButton.setText( translate('AlertsPlugin.AlertForm', 'Displ&ay')) self.displayCloseButton.setText( diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index f1a0f7fea..0639f2bb1 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -44,22 +44,22 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): self.item_id = None QtGui.QDialog.__init__(self, plugin.formparent) self.setupUi(self) - QtCore.QObject.connect(self.displayButton, QtCore.SIGNAL(u'clicked()'), - self.onDisplayClicked) + QtCore.QObject.connect(self.displayButton, + QtCore.SIGNAL(u'clicked()'), self.onDisplayClicked) QtCore.QObject.connect(self.displayCloseButton, QtCore.SIGNAL(u'clicked()'), self.onDisplayCloseClicked) QtCore.QObject.connect(self.alertTextEdit, QtCore.SIGNAL(u'textChanged(const QString&)'), self.onTextChanged) - QtCore.QObject.connect(self.newButton, QtCore.SIGNAL(u'clicked()'), - self.onNewClick) - QtCore.QObject.connect(self.deleteButton, QtCore.SIGNAL(u'clicked()'), - self.onDeleteClick) - QtCore.QObject.connect(self.saveButton, QtCore.SIGNAL(u'clicked()'), - self.onSaveClick) + QtCore.QObject.connect(self.newButton, + QtCore.SIGNAL(u'clicked()'), self.onNewClick) + QtCore.QObject.connect(self.saveButton, + QtCore.SIGNAL(u'clicked()'), self.onSaveClick) QtCore.QObject.connect(self.alertListWidget, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onDoubleClick) QtCore.QObject.connect(self.alertListWidget, QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSingleClick) + QtCore.QObject.connect(self.alertListWidget, + QtCore.SIGNAL(u'currentRowChanged(int)'), self.onCurrentRowChanged) def loadList(self): """ @@ -72,18 +72,15 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): item_name = QtGui.QListWidgetItem(alert.text) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(alert.id)) self.alertListWidget.addItem(item_name) - self.saveButton.setEnabled(False) - self.deleteButton.setEnabled(False) def onDisplayClicked(self): - if self.triggerAlert(unicode(self.alertTextEdit.text())): - self.loadList() + self.triggerAlert(unicode(self.alertTextEdit.text())) def onDisplayCloseClicked(self): if self.triggerAlert(unicode(self.alertTextEdit.text())): self.close() - def onDeleteClick(self): + def onDeleteButtonClicked(self): """ Deletes the selected item. """ @@ -95,8 +92,6 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): self.alertListWidget.takeItem(row) self.item_id = None self.alertTextEdit.setText(u'') - self.saveButton.setEnabled(False) - self.deleteButton.setEnabled(False) def onNewClick(self): if len(self.alertTextEdit.text()) == 0: @@ -135,30 +130,26 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): """ List item has been double clicked to display it """ - items = self.alertListWidget.selectedIndexes() - for item in items: - bitem = self.alertListWidget.item(item.row()) - self.triggerAlert(unicode(bitem.text())) - self.alertTextEdit.setText(unicode(bitem.text())) - self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0] + item = self.alertListWidget.selectedIndexes()[0] + bitem = self.alertListWidget.item(item.row()) + self.triggerAlert(unicode(bitem.text())) + self.alertTextEdit.setText(unicode(bitem.text())) + self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0] self.saveButton.setEnabled(False) - self.deleteButton.setEnabled(True) def onSingleClick(self): """ List item has been single clicked to add it to the edit field so it can be changed. """ - items = self.alertListWidget.selectedIndexes() - for item in items: - bitem = self.alertListWidget.item(item.row()) - self.alertTextEdit.setText(unicode(bitem.text())) - self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0] + item = self.alertListWidget.selectedIndexes()[0] + bitem = self.alertListWidget.item(item.row()) + self.alertTextEdit.setText(unicode(bitem.text())) + self.item_id = (bitem.data(QtCore.Qt.UserRole)).toInt()[0] # If the alert does not contain '<>' we clear the ParameterEdit field. if unicode(self.alertTextEdit.text()).find(u'<>') == -1: self.parameterEdit.setText(u'') self.saveButton.setEnabled(False) - self.deleteButton.setEnabled(True) def triggerAlert(self, text): """ @@ -167,30 +158,49 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): ``text`` The alert text (unicode). """ - if text: - # We found '<>' in the alert text, but the ParameterEdit field is - # empty. - if text.find(u'<>') != -1 and not self.parameterEdit.text() and \ - QtGui.QMessageBox.question(self, - translate('AlertPlugin.AlertForm', 'No Parameter found'), - translate('AlertPlugin.AlertForm', 'You have not entered a ' - 'parameter to be replaced.\nDo you want to continue anyway?'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | - QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: - self.parameterEdit.setFocus() - return False - # The ParameterEdit field is not empty, but we have not found '<>' - # in the alert text. - elif text.find(u'<>') == -1 and self.parameterEdit.text() and \ - QtGui.QMessageBox.question(self, - translate('AlertPlugin.AlertForm', 'No Placeholder found'), - translate('AlertPlugin.AlertForm', 'The alert text does not' - ' contain \'<>\'.\nDo want to continue anyway?'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | - QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: - self.parameterEdit.setFocus() - return False - text = text.replace(u'<>', unicode(self.parameterEdit.text())) - self.parent.alertsmanager.displayAlert(text) - return True - return False + if not text: + return False + # We found '<>' in the alert text, but the ParameterEdit field is empty. + if text.find(u'<>') != -1 and not self.parameterEdit.text() and \ + QtGui.QMessageBox.question(self, + translate('AlertPlugin.AlertForm', 'No Parameter found'), + translate('AlertPlugin.AlertForm', 'You have not entered a ' + 'parameter to be replaced.\nDo you want to continue anyway?'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | + QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: + self.parameterEdit.setFocus() + return False + # The ParameterEdit field is not empty, but we have not found '<>' + # in the alert text. + elif text.find(u'<>') == -1 and self.parameterEdit.text() and \ + QtGui.QMessageBox.question(self, + translate('AlertPlugin.AlertForm', 'No Placeholder found'), + translate('AlertPlugin.AlertForm', 'The alert text does not' + ' contain \'<>\'.\nDo want to continue anyway?'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | + QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: + self.parameterEdit.setFocus() + return False + text = text.replace(u'<>', unicode(self.parameterEdit.text())) + self.parent.alertsmanager.displayAlert(text) + return True + + def onCurrentRowChanged(self, row): + """ + Called when the *alertListWidget*'s current row has been changed. This + enables or disables buttons which require an item to act on. + + ``row`` + The row (int). If there is no current row, the value is -1. + """ + if row == -1: + self.displayButton.setEnabled(False) + self.displayCloseButton.setEnabled(False) + self.saveButton.setEnabled(False) + self.deleteButton.setEnabled(False) + else: + self.displayButton.setEnabled(True) + self.displayCloseButton.setEnabled(True) + self.deleteButton.setEnabled(True) + # We do not need to enable the save button, as it is only enabled + # when typing text in the "alertTextEdit". diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index a2509ba97..f21dd0e07 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -157,10 +157,9 @@ class BibleImportForm(OpenLPWizard): self.formatComboBox.addItems([u'', u'', u'', u'', u'']) self.formatComboBox.setObjectName(u'FormatComboBox') self.formatLayout.addRow(self.formatLabel, self.formatComboBox) - self.formatSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, + self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) - self.formatLayout.setItem(1, QtGui.QFormLayout.LabelRole, - self.formatSpacer) + self.formatLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.selectPageLayout.addLayout(self.formatLayout) self.selectStack = QtGui.QStackedLayout() self.selectStack.setObjectName(u'SelectStack') @@ -181,9 +180,7 @@ class BibleImportForm(OpenLPWizard): self.osisBrowseButton.setObjectName(u'OsisBrowseButton') self.osisFileLayout.addWidget(self.osisBrowseButton) self.osisLayout.addRow(self.osisFileLabel, self.osisFileLayout) - self.osisSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.osisLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.osisSpacer) + self.osisLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.selectStack.addWidget(self.osisWidget) self.csvWidget = QtGui.QWidget(self.selectPage) self.csvWidget.setObjectName(u'CsvWidget') @@ -226,9 +223,7 @@ class BibleImportForm(OpenLPWizard): self.csvVersesButton.setObjectName(u'CsvVersesButton') self.csvVersesLayout.addWidget(self.csvVersesButton) self.csvLayout.addRow(self.csvVersesLabel, self.csvVersesLayout) - self.csvSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.csvLayout.setItem(3, QtGui.QFormLayout.LabelRole, self.csvSpacer) + self.csvLayout.setItem(3, QtGui.QFormLayout.LabelRole, self.spacer) self.selectStack.addWidget(self.csvWidget) self.openSongWidget = QtGui.QWidget(self.selectPage) self.openSongWidget.setObjectName(u'OpenSongWidget') @@ -248,10 +243,7 @@ class BibleImportForm(OpenLPWizard): self.openSongFileLayout.addWidget(self.openSongBrowseButton) self.openSongLayout.addRow(self.openSongFileLabel, self.openSongFileLayout) - self.openSongSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.openSongLayout.setItem(1, QtGui.QFormLayout.LabelRole, - self.openSongSpacer) + self.openSongLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.selectStack.addWidget(self.openSongWidget) self.webTabWidget = QtGui.QTabWidget(self.selectPage) self.webTabWidget.setObjectName(u'WebTabWidget') @@ -330,10 +322,7 @@ class BibleImportForm(OpenLPWizard): self.openlp1DisabledLabel.setWordWrap(True) self.openlp1DisabledLabel.setObjectName(u'Openlp1DisabledLabel') self.openlp1Layout.addRow(self.openlp1DisabledLabel) - self.openlp1Spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Minimum) - self.openlp1Layout.setItem(1, QtGui.QFormLayout.LabelRole, - self.openlp1Spacer) + self.openlp1Layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) self.selectStack.addWidget(self.openlp1Widget) self.selectPageLayout.addLayout(self.selectStack) self.addPage(self.selectPage) @@ -401,17 +390,17 @@ class BibleImportForm(OpenLPWizard): self.formatComboBox.setItemText(4, translate('BiblesPlugin.ImportWizardForm', 'openlp.org 1.x')) self.openlp1FileLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'File location:')) + translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.osisFileLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'File location:')) + translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.csvTestamentsLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'Testaments location:')) + translate('BiblesPlugin.ImportWizardForm', 'Testaments file:')) self.csvBooksLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'Books location:')) + translate('BiblesPlugin.ImportWizardForm', 'Books file:')) self.csvVersesLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'Verse location:')) + translate('BiblesPlugin.ImportWizardForm', 'Verses file:')) self.openSongFileLabel.setText( - translate('BiblesPlugin.ImportWizardForm', 'Bible filename:')) + translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.webSourceLabel.setText( translate('BiblesPlugin.ImportWizardForm', 'Location:')) self.webSourceComboBox.setItemText(0, @@ -462,19 +451,12 @@ 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(), self.openlp1FileLabel.minimumSizeHint().width()) - self.formatSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.osisSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.csvSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.openSongSpacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.openlp1Spacer.changeSize(labelWidth, 0, + self.spacer.changeSize(labelWidth, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) def validateCurrentPage(self): diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 34e1bf5a5..e6fff1cc8 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -77,9 +77,9 @@ def parse_reference(reference): - After a verse reference all further single values are treat as verse in the last selected chapter. ``John 3:16-18`` refers to John chapter 3 verses 16 to 18 - - After a list separator it is possible to refer to additional verses. They - are build analog to the first ones. This way it is possible to define - each number of verse references. It is not possible to refer to verses in + - After a list separator it is possible to refer to additional verses. They + are build analog to the first ones. This way it is possible to define each + number of verse references. It is not possible to refer to verses in additional books. ``John 3:16,18`` refers to John chapter 3 verses 16 and 18 ``John 3:16-18,20`` refers to John chapter 3 verses 16 to 18 and 20 @@ -91,7 +91,7 @@ def parse_reference(reference): ``range_string`` is a regular expression which matches for verse range declarations: - 1. ``(?:(?P[0-9]+)%(sep_v)s)?' + 1. ``(?:(?P[0-9]+)%(sep_v)s)?`` It starts with a optional chapter reference ``from_chapter`` followed by a verse separator. 2. ``(?P[0-9]+)`` @@ -105,7 +105,7 @@ def parse_reference(reference): 5. ``(?P[0-9]+)`` The ``to_verse`` reference is equivalent to group 2. - The full reference is matched against get_reference_match(u'full'). This + The full reference is matched against get_reference_match(u'full'). This regular expression looks like this: 1. ``^\s*(?!\s)(?P[\d]*[^\d]+)(?(?:`` + range_string + ``(?:%(sep_l)s|(?=\s*$)))+)\s*$`` - The second group contains all ``ranges``. This can be multiple + The second group contains all ``ranges``. This can be multiple declarations of a range_string separated by a list separator. The reference list is a list of tuples, with each tuple structured like diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index 75717c082..b73899057 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -27,7 +27,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate -from openlp.core.lib.ui import save_cancel_button_box +from openlp.core.lib.ui import save_cancel_button_box, delete_push_button, \ + up_down_push_button_set class Ui_CustomEditDialog(object): def setupUi(self, customEditDialog): @@ -64,17 +65,12 @@ class Ui_CustomEditDialog(object): self.editAllButton = QtGui.QPushButton(customEditDialog) self.editAllButton.setObjectName(u'editAllButton') self.buttonLayout.addWidget(self.editAllButton) - self.deleteButton = QtGui.QPushButton(customEditDialog) - self.deleteButton.setObjectName(u'deleteButton') + self.deleteButton = delete_push_button(customEditDialog) self.buttonLayout.addWidget(self.deleteButton) self.buttonLayout.addStretch() - self.upButton = QtGui.QPushButton(customEditDialog) - self.upButton.setIcon(build_icon(u':/services/service_up.png')) - self.upButton.setObjectName(u'upButton') + self.upButton, self.downButton = up_down_push_button_set( + customEditDialog) self.buttonLayout.addWidget(self.upButton) - self.downButton = QtGui.QPushButton(customEditDialog) - self.downButton.setIcon(build_icon(u':/services/service_down.png')) - self.downButton.setObjectName(u'downButton') self.buttonLayout.addWidget(self.downButton) self.centralLayout.addLayout(self.buttonLayout) self.dialogLayout.addLayout(self.centralLayout) @@ -102,12 +98,6 @@ class Ui_CustomEditDialog(object): def retranslateUi(self, customEditDialog): customEditDialog.setWindowTitle( translate('CustomPlugin.EditCustomForm', 'Edit Custom Slides')) - self.upButton.setToolTip( - translate('CustomPlugin.EditCustomForm', 'Move slide up one ' - 'position.')) - self.downButton.setToolTip( - translate('CustomPlugin.EditCustomForm', 'Move slide down one ' - 'position.')) self.titleLabel.setText( translate('CustomPlugin.EditCustomForm', '&Title:')) self.addButton.setText( @@ -125,11 +115,6 @@ class Ui_CustomEditDialog(object): self.editAllButton.setToolTip( translate('CustomPlugin.EditCustomForm', 'Edit all the slides at ' 'once.')) - self.deleteButton.setText( - translate('CustomPlugin.EditCustomForm', '&Delete')) - self.deleteButton.setToolTip( - translate('CustomPlugin.EditCustomForm', 'Delete the selected ' - 'slide.')) self.themeLabel.setText( translate('CustomPlugin.EditCustomForm', 'The&me:')) self.creditLabel.setText( diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index e8dfa20aa..5a32d6cc1 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -62,12 +62,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): QtCore.SIGNAL(u'pressed()'), self.onEditButtonPressed) QtCore.QObject.connect(self.editAllButton, QtCore.SIGNAL(u'pressed()'), self.onEditAllButtonPressed) - QtCore.QObject.connect(self.deleteButton, - QtCore.SIGNAL(u'pressed()'), self.onDeleteButtonPressed) - QtCore.QObject.connect(self.upButton, - QtCore.SIGNAL(u'pressed()'), self.onUpButtonPressed) - QtCore.QObject.connect(self.downButton, - QtCore.SIGNAL(u'pressed()'), self.onDownButtonPressed) QtCore.QObject.connect(self.slideListView, QtCore.SIGNAL(u'itemClicked(QListWidgetItem*)'), self.onSlideListViewPressed) @@ -168,14 +162,14 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): self.customSlide.theme_name = unicode(self.themeComboBox.currentText()) return self.manager.save_object(self.customSlide) - def onUpButtonPressed(self): + def onUpButtonClicked(self): selectedRow = self.slideListView.currentRow() if selectedRow != 0: qw = self.slideListView.takeItem(selectedRow) self.slideListView.insertItem(selectedRow - 1, qw) self.slideListView.setCurrentRow(selectedRow - 1) - def onDownButtonPressed(self): + def onDownButtonClicked(self): selectedRow = self.slideListView.currentRow() # zero base arrays if selectedRow != self.slideListView.count() - 1: @@ -243,7 +237,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): self.slideListView.addItem(slide) self.slideListView.repaint() - def onDeleteButtonPressed(self): + def onDeleteButtonClicked(self): self.slideListView.takeItem(self.slideListView.currentRow()) self.editButton.setEnabled(True) self.editAllButton.setEnabled(True) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index cbefc7171..71027881c 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -123,19 +123,19 @@ class ImageMediaItem(MediaManagerItem): self.settingsSection, self.getFileList()) def loadList(self, list): - for file in list: - filename = os.path.split(unicode(file))[1] + for imageFile in list: + filename = os.path.split(unicode(imageFile))[1] thumb = os.path.join(self.servicePath, filename) if os.path.exists(thumb): - if self.validate(file, thumb): + if self.validate(imageFile, thumb): icon = build_icon(thumb) else: icon = build_icon(u':/general/general_delete.png') else: - icon = self.iconFromFile(file, thumb) + icon = self.iconFromFile(imageFile, thumb) item_name = QtGui.QListWidgetItem(filename) item_name.setIcon(icon) - item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) + item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(imageFile)) self.listView.addItem(item_name) def generateSlideData(self, service_item, item=None, xmlVersion=False): diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index f1e1f82ed..a9af7f6b2 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -162,6 +162,8 @@ class SongImportForm(OpenLPWizard): self.formatLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.formatSpacer) self.sourceLayout.addLayout(self.formatLayout) + self.stackSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, + QtGui.QSizePolicy.Expanding) self.formatStack = QtGui.QStackedLayout() self.formatStack.setObjectName(u'FormatStack') # OpenLP 2.0 @@ -807,10 +809,8 @@ class SongImportForm(OpenLPWizard): browseButton.setIcon(self.openIcon) browseButton.setObjectName(obj_prefix + u'BrowseButton') fileLayout.addWidget(browseButton) - formSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Expanding) importLayout.addLayout(fileLayout) - importLayout.addSpacerItem(formSpacer) + importLayout.addSpacerItem(self.stackSpacer) else: fileListWidget = QtGui.QListWidget(importWidget) fileListWidget.setSelectionMode( @@ -859,6 +859,7 @@ class SongImportForm(OpenLPWizard): disabledLabel.setWordWrap(True) disabledLabel.setObjectName(obj_prefix + u'DisabledLabel') disabledLayout.addWidget(disabledLabel) + disabledLayout.addSpacerItem(self.stackSpacer) layout.addWidget(disabledWidget) importWidget = QtGui.QWidget(page) importWidget.setObjectName(obj_prefix + u'ImportWidget') diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 2aeb30228..1eb63fbf4 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -483,19 +483,19 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onAuthorsListRowChanged(self, row): """ - Called when the *authorsListWidget*s current row has changed. + Called when the *authorsListWidget*'s current row has changed. """ self.__rowChange(row, self.authorsEditButton, self.authorsDeleteButton) def onTopicsListRowChanged(self, row): """ - Called when the *topicsListWidget*s current row has changed. + Called when the *topicsListWidget*'s current row has changed. """ self.__rowChange(row, self.topicsEditButton, self.topicsDeleteButton) def onBooksListRowChanged(self, row): """ - Called when the *booksListWidget*s current row has changed. + Called when the *booksListWidget*'s current row has changed. """ self.__rowChange(row, self.booksEditButton, self.booksDeleteButton) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 838172893..2bbf818b3 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -39,6 +39,7 @@ class Author(BaseModel): """ pass + class Book(BaseModel): """ Book model @@ -47,30 +48,115 @@ class Book(BaseModel): return u'' % ( str(self.id), self.name, self.publisher) + class MediaFile(BaseModel): """ MediaFile model """ pass + class Song(BaseModel): """ Song model """ pass + class Topic(BaseModel): """ Topic model """ pass + def init_schema(url): + """ - Setup the songs database connection and initialise the database schema + Setup the songs database connection and initialise the database schema. ``url`` The database to setup + + The song database contains the following tables: + + * authors + * authors_songs + * media_files + * media_files_songs + * song_books + * songs + * songs_topics + * topics + + **authors** Table + This table holds the names of all the authors. It has the following + columns: + + * id + * first_name + * last_name + * display_name + + **authors_songs Table** + This is a bridging table between the *authors* and *songs* tables, which + serves to create a many-to-many relationship between the two tables. It + has the following columns: + + * author_id + * song_id + + **media_files Table** + * id + * file_name + * type + + **media_files_songs Table** + * media_file_id + * song_id + + **song_books Table** + The *song_books* table holds a list of books that a congregation gets + their songs from, or old hymnals now no longer used. This table has the + following columns: + + * id + * name + * publisher + + **songs Table** + This table contains the songs, and each song has a list of attributes. + The *songs* table has the following columns: + + * id + * song_book_id + * title + * alternate_title + * lyrics + * verse_order + * copyright + * comments + * ccli_number + * song_number + * theme_name + * search_title + * search_lyrics + + **songs_topics Table** + This is a bridging table between the *songs* and *topics* tables, which + serves to create a many-to-many relationship between the two tables. It + has the following columns: + + * song_id + * topic_id + + **topics Table** + The topics table holds a selection of topics that songs can cover. This + is useful when a worship leader wants to select songs with a certain + theme. This table has the following columns: + + * id + * name """ session, metadata = init_db(url) diff --git a/scripts/bible-1to2-converter.py b/scripts/bible-1to2-converter.py deleted file mode 100755 index ebf246608..000000000 --- a/scripts/bible-1to2-converter.py +++ /dev/null @@ -1,308 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Meinert Jordan, 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 # -############################################################################### - -import sys -import os -import sqlite -import sqlite3 - -from optparse import OptionParser -from traceback import format_tb as get_traceback - -# Some global options to be used throughout the import process -verbose = False -debug = False -old_cursor = None -new_cursor = None - -# SQL create statments -create_statements = [ - (u'table "book"', u"""CREATE TABLE book ( - id INTEGER NOT NULL, - testament_id INTEGER, - name VARCHAR(30), - abbreviation VARCHAR(5), - PRIMARY KEY (id), - FOREIGN KEY(testament_id) REFERENCES testament (id) -)"""), - (u'table "metadata"', u"""CREATE TABLE metadata ( - "key" VARCHAR(255) NOT NULL, - value VARCHAR(255), - PRIMARY KEY ("key") -)"""), - (u'table "testament"', u"""CREATE TABLE testament ( - id INTEGER NOT NULL, - name VARCHAR(30), - PRIMARY KEY (id) -)"""), - (u'table "verse"', u"""CREATE TABLE verse ( - id INTEGER NOT NULL, - book_id INTEGER, - chapter INTEGER, - verse INTEGER, - text TEXT, - PRIMARY KEY (id), - FOREIGN KEY(book_id) REFERENCES book (id) -)"""), - (u'index "idx_abbrev"', - u"""CREATE INDEX idx_abbrev ON book (abbreviation, id)"""), - (u'index "idx_chapter_verse_book', - u"""CREATE INDEX idx_chapter_verse_book ON verse (chapter, verse, book_id, id)"""), - (u'index "idx_chapter_verse_text"', - u"""CREATE INDEX idx_chapter_verse_text ON verse (text, verse, book_id, id)"""), - (u'index "idx_name"', - u"""CREATE INDEX idx_name ON book (name, id)""") -] - -def display_sql(sql, params): - prepared_params = [] - for param in params: - if isinstance(param, basestring): - prepared_params.append(u'"%s"' % param) - elif isinstance(param, (int, long)): - prepared_params.append(u'%d' % param) - elif isinstance(param, (float, complex)): - prepared_params.append(u'%f' % param) - else: - prepared_params.append(u'"%s"' % str(param)) - for prepared_param in prepared_params: - sql = sql.replace(u'?', prepared_param, 1) - return sql - -def create_database(): - global new_cursor, create_statements - if debug or verbose: - print 'Creating new database:' - else: - print 'Creating new database...', - for statement_type, sql_create in create_statements: - if debug: - print '... ', sql_create.replace('\n', ' ').replace(' ', ' ') - elif verbose: - print '... creating %s...' % statement_type, - new_cursor.execute(sql_create) - if verbose and not debug: - print 'done.' - if not verbose and not debug: - print 'done.' - -def import_bible(): - global old_cursor, new_cursor, debug, verbose - if debug or verbose: - print 'Importing metadata:' - else: - print 'Importing metadata...', - if debug: - print '... SELECT "key", "value" FROM metadata' - elif verbose: - print '... fetching metadata from old database...', - old_cursor.execute(u'SELECT "key", "value" FROM metadata') - rows = old_cursor.fetchall() - if not debug and verbose: - print 'done.' - for row in rows: - key = unicode(row[0], u'cp1252') - value = unicode(row[1], u'cp1252') - if key == u'Permission': - key = u'Permissions' - sql_insert = u'INSERT INTO metadata '\ - '("key", "value") '\ - 'VALUES (?, ?)' - sql_params = (key, value) - if debug: - print '...', display_sql(sql_insert, sql_params) - elif verbose: - print '... importing "%s"' % key - new_cursor.execute(sql_insert, sql_params) - if not verbose and not debug: - print 'done.' - if debug or verbose: - print 'Importing testaments:' - else: - print 'Importing testaments...', - if debug: - print '... SELECT id, name FROM testament' - elif verbose: - print '... fetching testaments from old database...', - old_cursor.execute(u'SELECT id, name FROM testament') - rows = old_cursor.fetchall() - if not debug and verbose: - print 'done.' - for row in rows: - id = int(row[0]) - name = unicode(row[1], u'cp1252') - sql_insert = u'INSERT INTO testament '\ - '(id, name) '\ - 'VALUES (?, ?)' - sql_params = (id, name) - if debug: - print '...', display_sql(sql_insert, sql_params) - elif verbose: - print '... importing "%s"' % name - new_cursor.execute(sql_insert, sql_params) - if not verbose and not debug: - print 'done.' - if debug or verbose: - print 'Importing books:' - else: - print 'Importing books...', - if debug: - print '... SELECT id, testament_id, name, abbreviation FROM book' - elif verbose: - print '... fetching books from old database...', - old_cursor.execute(u'SELECT id, testament_id, name, abbreviation FROM book') - rows = old_cursor.fetchall() - if not debug and verbose: - print 'done.' - book_map = {} - for row in rows: - testament_id = int(row[1]) - name = unicode(row[2], u'cp1252') - abbreviation = unicode(row[3], u'cp1252') - sql_insert = u'INSERT INTO book '\ - '(id, testament_id, name, abbreviation) '\ - 'VALUES (NULL, ?, ?, ?)' - sql_params = (testament_id, name, abbreviation) - if debug: - print '...', display_sql(sql_insert, sql_params) - elif verbose: - print '... importing "%s"' % name - new_cursor.execute(sql_insert, sql_params) - book_map[row[0]] = new_cursor.lastrowid - if debug: - print ' >>> (old) books.id =', row[0], ' (new) books.id =', book_map[row[0]] - if not verbose and not debug: - print 'done.' - if debug or verbose: - print 'Importing verses:' - else: - print 'Importing verses...', - if debug: - print '... SELECT id, book_id, chapter, verse, text || \'\' AS text FROM verse...', - elif verbose: - print '... fetching verses from old database...', - old_cursor.execute(u'SELECT id, book_id, chapter, verse, text || \'\' AS text FROM verse') - rows = old_cursor.fetchall() - if debug or verbose: - print 'done.' - for row in rows: - book_id = int(row[1]) - chapter = int(row[2]) - verse = int(row[3]) - text = unicode(row[4], u'cp1252') - sql_insert = u'INSERT INTO verse '\ - '(id, book_id, chapter, verse, text) '\ - 'VALUES (NULL, ?, ?, ?, ?)' - sql_params = (book_map[book_id], chapter, verse, text) - if debug: - print '...', display_sql(sql_insert, sql_params) - elif verbose: - print '... importing "%s..."' % text[:17] - new_cursor.execute(sql_insert, sql_params) - if not verbose and not debug: - print 'done.' - -def main(old_db, new_db): - global old_cursor, new_cursor, debug - old_connection = None - new_connection = None - try: - old_connection = sqlite.connect(old_db) - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem connecting to the old database:', errormsg - return 1 - try: - new_connection = sqlite3.connect(new_db) - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem creating the new database:', errormsg - return 1 - old_cursor = old_connection.cursor() - new_cursor = new_connection.cursor() - try: - create_database() - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem creating the database:', errormsg - return 1 - try: - import_bible() - new_connection.commit() - except: - new_connection.rollback() - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem importing songs:', errormsg - return 1 - print 'Import complete.' - -if __name__ == u'__main__': - option_parser = OptionParser(usage='Usage: %prog [options] OLDDATABASE NEWDATABASE') - option_parser.add_option('-o', '--overwrite', dest='overwrite', default=False, - action=u'store_true', help='Overwrite database file if it already exists.') - option_parser.add_option('-v', '--verbose', dest='verbose', default=False, - action=u'store_true', help='Outputs additional progress data.') - option_parser.add_option('-d', '--debug', dest='debug', default=False, - action=u'store_true', help='Outputs raw SQL statements (overrides verbose).') - options, arguments = option_parser.parse_args() - if len(arguments) < 2: - if len(arguments) == 0: - option_parser.error('Please specify an old database and a new database.') - else: - option_parser.error('Please specify a new database.') - old_db = os.path.abspath(arguments[0]) - new_db = os.path.abspath(arguments[1]) - if not os.path.isfile(old_db): - option_parser.error('Old database file ("%s") is not a file.' % old_db) - if not os.path.exists(old_db): - option_parser.error('Old database file ("%s") does not exist.' % old_db) - if os.path.exists(new_db): - if not options.overwrite: - option_parser.error('New database file ("%s") exists. If you want to overwrite it, specify the --overwrite option.' % new_db) - else: - if not os.path.isfile(new_db): - option_parser.error('New database file ("%s") is not a file.' % new_db) - os.unlink(new_db) - verbose = options.verbose - debug = options.debug - main(old_db, new_db) diff --git a/scripts/openlp-1to2-converter.py b/scripts/openlp-1to2-converter.py deleted file mode 100755 index bd554aa70..000000000 --- a/scripts/openlp-1to2-converter.py +++ /dev/null @@ -1,323 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Meinert Jordan, 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 # -############################################################################### - -import sys -import os -import sqlite -import sqlite3 -import re -from optparse import OptionParser -from traceback import format_tb as get_traceback - -# Some global options to be used throughout the import process -dirty_chars = re.compile(r'\W ', re.UNICODE) -verbose = False -debug = False -old_cursor = None -new_cursor = None - -# SQL create statments -create_statements = [ - (u'table "authors"', u"""CREATE TABLE authors ( - id INTEGER NOT NULL, - first_name VARCHAR(128), - last_name VARCHAR(128), - display_name VARCHAR(255) NOT NULL, - PRIMARY KEY (id) -)"""), - (u'table "song_books"', u"""CREATE TABLE song_books ( - id INTEGER NOT NULL, - name VARCHAR(128) NOT NULL, - publisher VARCHAR(128), - PRIMARY KEY (id) -)"""), - (u'table "songs"', u"""CREATE TABLE songs ( - id INTEGER NOT NULL, - song_book_id INTEGER, - title VARCHAR(255) NOT NULL, - alternate_title VARCHAR(255), - lyrics TEXT NOT NULL, - verse_order VARCHAR(128), - copyright VARCHAR(255), - comments TEXT, - ccli_number VARCHAR(64), - song_number VARCHAR(64), - theme_name VARCHAR(128), - search_title VARCHAR(255) NOT NULL, - search_lyrics TEXT NOT NULL, - PRIMARY KEY (id), - FOREIGN KEY(song_book_id) REFERENCES song_books (id) -)"""), - (u'table "topics"', u"""CREATE TABLE topics ( - id INTEGER NOT NULL, - name VARCHAR(128) NOT NULL, - PRIMARY KEY (id) -)"""), - (u'index "ix_songs_search_lyrics"', - u"""CREATE INDEX ix_songs_search_lyrics ON songs (search_lyrics)"""), - (u'index "ix_songs_search_title', - u"""CREATE INDEX ix_songs_search_title ON songs (search_title)"""), - (u'table "authors_songs"', u"""CREATE TABLE authors_songs ( - author_id INTEGER NOT NULL, - song_id INTEGER NOT NULL, - PRIMARY KEY (author_id, song_id), - FOREIGN KEY(author_id) REFERENCES authors (id), - FOREIGN KEY(song_id) REFERENCES songs (id) -)"""), - (u'table "songs_topics"', u"""CREATE TABLE songs_topics ( - song_id INTEGER NOT NULL, - topic_id INTEGER NOT NULL, - PRIMARY KEY (song_id, topic_id), - FOREIGN KEY(song_id) REFERENCES songs (id), - FOREIGN KEY(topic_id) REFERENCES topics (id) -)""") -] - -def prepare_string(dirty): - return dirty_chars.sub(u'', dirty.replace(u'\r\n', u' ').replace(u'\n', u' ')) - -def display_sql(sql, params): - prepared_params = [] - for param in params: - if isinstance(param, basestring): - prepared_params.append(u'"%s"' % param) - elif isinstance(param, (int, long)): - prepared_params.append(u'%d' % param) - elif isinstance(param, (float, complex)): - prepared_params.append(u'%f' % param) - else: - prepared_params.append(u'"%s"' % str(param)) - for prepared_param in prepared_params: - sql = sql.replace(u'?', prepared_param, 1) - return sql - -def create_database(): - global new_cursor, create_statements - if debug or verbose: - print 'Creating new database:' - else: - print 'Creating new database...', - for statement_type, sql_create in create_statements: - if debug: - print '... ', sql_create.replace('\n', ' ').replace(' ', ' ') - elif verbose: - print '... creating %s...' % statement_type, - new_cursor.execute(sql_create) - if verbose and not debug: - print 'done.' - if not verbose and not debug: - print 'done.' - -def import_songs(): - global old_cursor, new_cursor, debug, verbose - if debug or verbose: - print 'Importing authors:' - else: - print 'Importing authors...', - if debug: - print '... SELECT authorid AS id, authorname AS displayname FROM authors' - elif verbose: - print '... fetching authors from old database...', - old_cursor.execute(u'SELECT authorid AS id, authorname AS displayname FROM authors') - rows = old_cursor.fetchall() - if not debug and verbose: - print 'done.' - author_map = {} - for row in rows: - display_name = unicode(row[1], u'cp1252') - names = display_name.split(u' ') - first_name = names[0] - last_name = u' '.join(names[1:]) - if last_name is None: - last_name = u'' - sql_insert = u'INSERT INTO authors '\ - '(id, first_name, last_name, display_name) '\ - 'VALUES (NULL, ?, ?, ?)' - sql_params = (first_name, last_name, display_name) - if debug: - print '...', display_sql(sql_insert, sql_params) - elif verbose: - print '... importing "%s"' % display_name - new_cursor.execute(sql_insert, sql_params) - author_map[row[0]] = new_cursor.lastrowid - if debug: - print ' >>> authors.authorid =', row[0], 'authors.id =', author_map[row[0]] - if not verbose and not debug: - print 'done.' - if debug or verbose: - print 'Importing songs:' - else: - print 'Importing songs...', - if debug: - print '... SELECT songid AS id, songtitle AS title, lyrics || \'\' AS lyrics, copyrightinfo AS copyright FROM songs...', - elif verbose: - print '... fetching songs from old database...', - old_cursor.execute(u'SELECT songid AS id, songtitle AS title, lyrics || \'\' AS lyrics, copyrightinfo AS copyright FROM songs') - rows = old_cursor.fetchall() - if debug or verbose: - print 'done.' - song_map = {} - xml_lyrics_template = u'%s' - xml_verse_template = u'' - for row in rows: - clean_title = unicode(row[1], u'cp1252') - clean_lyrics = unicode(row[2], u'cp1252').replace(u'\r\n', u'\n') - clean_copyright = unicode(row[3], u'cp1252') - verse_order = u'' - text_lyrics = clean_lyrics.split(u'\n\n') - xml_verse = u'' - verses = [] - for line, verse in enumerate(text_lyrics): - if not verse: - continue - xml_verse += (xml_verse_template % (line + 1, verse)) - verses.append(u'V%d' % (line + 1)) - verse_order = u' '.join(verses) - xml_lyrics = xml_lyrics_template % xml_verse - search_title = prepare_string(clean_title) - search_lyrics = prepare_string(clean_lyrics) - sql_insert = u'INSERT INTO songs '\ - '(id, song_book_id, title, lyrics, verse_order, copyright, search_title, search_lyrics) '\ - 'VALUES (NULL, 0, ?, ?, ?, ?, ?, ?)' - sql_params = (clean_title, xml_lyrics, verse_order, clean_copyright, search_title, search_lyrics) - if debug: - print '...', display_sql(sql_insert, (sql_params[0], u'%s...' % clean_lyrics[:7], sql_params[2], sql_params[3], sql_params[4], u'%s...' % search_lyrics[:7])) - elif verbose: - print '... importing "%s"' % clean_title - new_cursor.execute(sql_insert, sql_params) - song_map[row[0]] = new_cursor.lastrowid - if debug: - print ' >>> songs.songid =', row[0], 'songs.id =', song_map[row[0]] - if not verbose and not debug: - print 'done.' - if debug or verbose: - print 'Importing song-to-author mapping:' - else: - print 'Importing song-to-author mapping...', - if debug: - print '... SELECT authorid AS author_id, songid AS song_id FROM songauthors' - elif verbose: - print '... fetching song-to-author mapping from old database...', - old_cursor.execute(u'SELECT authorid AS author_id, songid AS song_id FROM songauthors') - rows = old_cursor.fetchall() - if not debug and verbose: - print 'done.' - for row in rows: - sql_insert = u'INSERT INTO authors_songs '\ - '(author_id, song_id) '\ - 'VALUES (?, ?)' - sql_params = (author_map[row[0]], song_map[row[1]]) - if debug: - print '... ', display_sql(sql_insert, sql_params) - elif verbose: - print '... Author %d (was %d) => Song %d (was %d)'\ - % (int(row[0]), author_map[row[0]], - int(row[1]), song_map[row[1]]) - new_cursor.execute(sql_insert, sql_params) - if not verbose and not debug: - print 'done.' - -def main(old_db, new_db): - global old_cursor, new_cursor, debug - old_connection = None - new_connection = None - try: - old_connection = sqlite.connect(old_db) - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem connecting to the old database:', errormsg - return 1 - try: - new_connection = sqlite3.connect(new_db) - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem creating the new database:', errormsg - return 1 - old_cursor = old_connection.cursor() - new_cursor = new_connection.cursor() - try: - create_database() - except: - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem creating the database:', errormsg - return 1 - try: - import_songs() - new_connection.commit() - except: - new_connection.rollback() - if debug: - errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ - + str(sys.exc_info()[1]) - else: - errormsg = sys.exc_info()[1] - print 'There was a problem importing songs:', errormsg - return 1 - print 'Import complete.' - -if __name__ == u'__main__': - option_parser = OptionParser(usage='Usage: %prog [options] OLDDATABASE NEWDATABASE') - option_parser.add_option('-o', '--overwrite', dest='overwrite', default=False, - action=u'store_true', help='Overwrite database file if it already exists.') - option_parser.add_option('-v', '--verbose', dest='verbose', default=False, - action=u'store_true', help='Outputs additional progress data.') - option_parser.add_option('-d', '--debug', dest='debug', default=False, - action=u'store_true', help='Outputs raw SQL statements (overrides verbose).') - options, arguments = option_parser.parse_args() - if len(arguments) < 2: - if len(arguments) == 0: - option_parser.error('Please specify an old database and a new database.') - else: - option_parser.error('Please specify a new database.') - old_db = os.path.abspath(arguments[0]) - new_db = os.path.abspath(arguments[1]) - if not os.path.isfile(old_db): - option_parser.error('Old database file ("%s") is not a file.' % old_db) - if not os.path.exists(old_db): - option_parser.error('Old database file ("%s") does not exist.' % old_db) - if os.path.exists(new_db): - if not options.overwrite: - option_parser.error('New database file ("%s") exists. If you want to overwrite it, specify the --overwrite option.' % new_db) - else: - if not os.path.isfile(new_db): - option_parser.error('New database file ("%s") is not a file.' % new_db) - os.unlink(new_db) - verbose = options.verbose - debug = options.debug - main(old_db, new_db)