diff --git a/copyright.txt b/copyright.txt index c99a64287..6882a7282 100644 --- a/copyright.txt +++ b/copyright.txt @@ -4,8 +4,8 @@ ############################################################################### # OpenLP - Open Source Lyrics Projection # # --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Copyright (c) 2008-2011 Raoul Snyman # +# Portions copyright (c) 2008-2011 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 # diff --git a/documentation/api/source/core/lib.rst b/documentation/api/source/core/lib.rst index 6ca952d7d..fa894875d 100644 --- a/documentation/api/source/core/lib.rst +++ b/documentation/api/source/core/lib.rst @@ -6,18 +6,18 @@ Object Library .. automodule:: openlp.core.lib :members: -:mod:`BaseListWithDnD` ----------------------- - -.. autoclass:: openlp.core.lib.baselistwithdnd.BaseListWithDnD - :members: - :mod:`EventReceiver` -------------------- .. autoclass:: openlp.core.lib.eventreceiver.EventReceiver :members: +:mod:`ListWidgetWithDnD` +---------------------- + +.. autoclass:: openlp.core.lib.listwidgetwithdnd.ListWidgetWithDnD + :members: + :mod:`MediaManagerItem` ----------------------- diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 33280f83b..5247ae938 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -319,6 +319,7 @@ def check_directory_exists(dir): if not os.path.exists(dir): os.makedirs(dir) +from listwidgetwithdnd import ListWidgetWithDnD from theme import ThemeLevel, ThemeXML, BackgroundGradientType, \ BackgroundType, HorizontalType, VerticalType from displaytags import DisplayTags @@ -339,4 +340,3 @@ from dockwidget import OpenLPDockWidget from renderer import Renderer from rendermanager import RenderManager from mediamanageritem import MediaManagerItem -from baselistwithdnd import BaseListWithDnD diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index ea830855c..34d583181 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -28,7 +28,8 @@ import logging from PyQt4 import QtWebKit -from openlp.core.lib import BackgroundType, BackgroundGradientType +from openlp.core.lib import BackgroundType, BackgroundGradientType, \ + VerticalType log = logging.getLogger(__name__) @@ -536,12 +537,7 @@ def build_lyrics_format_css(theme, width, height): align = u'right' else: align = u'left' - if theme.display_vertical_align == 2: - valign = u'bottom' - elif theme.display_vertical_align == 1: - valign = u'middle' - else: - valign = u'top' + valign = VerticalType.to_string(theme.display_vertical_align) if theme.font_main_outline: left_margin = int(theme.font_main_outline_size) * 2 else: @@ -634,13 +630,7 @@ def build_alert_css(alertTab, width): """ if not alertTab: return u'' - align = u'' - if alertTab.location == 2: - align = u'bottom' - elif alertTab.location == 1: - align = u'middle' - else: - align = u'top' + align = VerticalType.to_string(alertTab.location) alert = style % (width, align, alertTab.font_face, alertTab.font_size, alertTab.font_color, alertTab.bg_color) return alert diff --git a/openlp/core/lib/baselistwithdnd.py b/openlp/core/lib/listwidgetwithdnd.py similarity index 90% rename from openlp/core/lib/baselistwithdnd.py rename to openlp/core/lib/listwidgetwithdnd.py index 86535f6e7..a1a11217b 100644 --- a/openlp/core/lib/baselistwithdnd.py +++ b/openlp/core/lib/listwidgetwithdnd.py @@ -28,17 +28,17 @@ Extend QListWidget to handle drag and drop functionality """ from PyQt4 import QtCore, QtGui -class BaseListWithDnD(QtGui.QListWidget): +class ListWidgetWithDnD(QtGui.QListWidget): """ Provide a list widget to store objects and handle drag and drop events """ - def __init__(self, parent=None): + def __init__(self, parent=None, name=u''): """ Initialise the list widget """ QtGui.QListWidget.__init__(self, parent) - # this must be set by the class which is inheriting - assert(self.PluginName) + self.mimeDataText = name + assert(self.mimeDataText) def mouseMoveEvent(self, event): """ @@ -47,9 +47,10 @@ class BaseListWithDnD(QtGui.QListWidget): just tell it what plugin to call """ if event.buttons() != QtCore.Qt.LeftButton: + event.ignore() return drag = QtGui.QDrag(self) mimeData = QtCore.QMimeData() drag.setMimeData(mimeData) - mimeData.setText(self.PluginName) - drag.start(QtCore.Qt.CopyAction) \ No newline at end of file + mimeData.setText(self.mimeDataText) + drag.start(QtCore.Qt.CopyAction) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index cc80ad564..f74ba63a9 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import context_menu_action, context_menu_separator, \ SettingsManager, OpenLPToolbar, ServiceItem, StringContent, build_icon, \ - translate, Receiver + translate, Receiver, ListWidgetWithDnD log = logging.getLogger(__name__) @@ -73,11 +73,6 @@ class MediaManagerItem(QtGui.QWidget): assumes that the new action is to load a file. If not, you need to override the ``OnNew`` method. - ``self.ListViewWithDnD_class`` - This must be a **class**, not an object, descended from - ``openlp.core.lib.BaseListWithDnD`` that is not used in any - other part of OpenLP. - ``self.PreviewFunction`` This must be a method which returns a QImage to represent the item (usually a preview). No scaling is required, that is @@ -202,68 +197,50 @@ class MediaManagerItem(QtGui.QWidget): """ Create buttons for the media item toolbar """ + toolbar_actions = [] ## Import Button ## if self.hasImportIcon: - import_string = self.plugin.getString(StringContent.Import) - self.addToolbarButton( - import_string[u'title'], - import_string[u'tooltip'], - u':/general/general_import.png', self.onImportClick) + toolbar_actions.append([StringContent.Import, + u':/general/general_import.png', self.onImportClick]) ## Load Button ## if self.hasFileIcon: - load_string = self.plugin.getString(StringContent.Load) - self.addToolbarButton( - load_string[u'title'], - load_string[u'tooltip'], - u':/general/general_open.png', self.onFileClick) + toolbar_actions.append([StringContent.Load, + u':/general/general_open.png', self.onFileClick]) ## New Button ## if self.hasNewIcon: - new_string = self.plugin.getString(StringContent.New) - self.addToolbarButton( - new_string[u'title'], - new_string[u'tooltip'], - u':/general/general_new.png', self.onNewClick) + toolbar_actions.append([StringContent.New, + u':/general/general_new.png', self.onNewClick]) ## Edit Button ## if self.hasEditIcon: - edit_string = self.plugin.getString(StringContent.Edit) - self.addToolbarButton( - edit_string[u'title'], - edit_string[u'tooltip'], - u':/general/general_edit.png', self.onEditClick) + toolbar_actions.append([StringContent.Edit, + u':/general/general_edit.png', self.onEditClick]) ## Delete Button ## if self.hasDeleteIcon: - delete_string = self.plugin.getString(StringContent.Delete) - self.addToolbarButton( - delete_string[u'title'], - delete_string[u'tooltip'], - u':/general/general_delete.png', self.onDeleteClick) + toolbar_actions.append([StringContent.Delete, + u':/general/general_delete.png', self.onDeleteClick]) ## Separator Line ## self.addToolbarSeparator() ## Preview ## - preview_string = self.plugin.getString(StringContent.Preview) - self.addToolbarButton( - preview_string[u'title'], - preview_string[u'tooltip'], - u':/general/general_preview.png', self.onPreviewClick) + toolbar_actions.append([StringContent.Preview, + u':/general/general_preview.png', self.onPreviewClick]) ## Live Button ## - live_string = self.plugin.getString(StringContent.Live) - self.addToolbarButton( - live_string[u'title'], - live_string[u'tooltip'], - u':/general/general_live.png', self.onLiveClick) + toolbar_actions.append([StringContent.Live, + u':/general/general_live.png', self.onLiveClick]) ## Add to service Button ## - service_string = self.plugin.getString(StringContent.Service) - self.addToolbarButton( - service_string[u'title'], - service_string[u'tooltip'], - u':/general/general_add.png', self.onAddClick) + toolbar_actions.append([StringContent.Service, + u':/general/general_add.png', self.onAddClick]) + for action in toolbar_actions: + self.addToolbarButton( + self.plugin.getString(action[0])[u'title'], + self.plugin.getString(action[0])[u'tooltip'], + action[1], action[2]) def addListViewToToolBar(self): """ Creates the main widget for listing items the media item is tracking """ # Add the List widget - self.listView = self.ListViewWithDnD_class(self) + self.listView = ListWidgetWithDnD(self, self.title) self.listView.uniformItemSizes = True self.listView.setSpacing(1) self.listView.setSelectionMode( @@ -500,7 +477,7 @@ class MediaManagerItem(QtGui.QWidget): """ if not self.listView.selectedIndexes() and not self.remoteTriggered: QtGui.QMessageBox.information(self, - translate('OpenLP.MediaManagerItem', 'No items selected'), + translate('OpenLP.MediaManagerItem', 'No Items Selected'), translate('OpenLP.MediaManagerItem', 'You must select one or more items')) else: diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index cfd3fb4c2..730bb1a36 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -114,7 +114,8 @@ class Plugin(QtCore.QObject): """ log.info(u'loaded') - def __init__(self, name, version=None, pluginHelpers=None): + def __init__(self, name, version=None, pluginHelpers=None, + mediaItemClass=None, settingsTabClass=None): """ This is the constructor for the plugin object. This provides an easy way for descendent plugins to populate common data. This method *must* @@ -132,6 +133,12 @@ class Plugin(QtCore.QObject): ``pluginHelpers`` Defaults to *None*. A list of helper objects. + + ``mediaItemClass`` + The class name of the plugin's media item. + + ``settingsTabClass`` + The class name of the plugin's settings tab. """ QtCore.QObject.__init__(self) self.name = name @@ -141,6 +148,8 @@ class Plugin(QtCore.QObject): self.version = version self.settingsSection = self.name.lower() self.icon = None + self.mediaItemClass = mediaItemClass + self.settingsTabClass = settingsTabClass self.weight = 0 self.status = PluginStatus.Inactive # Set up logging @@ -199,7 +208,9 @@ class Plugin(QtCore.QObject): Construct a MediaManagerItem object with all the buttons and things you need, and return it for integration into openlp.org. """ - pass + if self.mediaItemClass: + return self.mediaItemClass(self, self, self.icon) + return None def addImportMenuItem(self, importMenu): """ @@ -230,9 +241,13 @@ class Plugin(QtCore.QObject): def getSettingsTab(self): """ - Create a tab for the settings window. + Create a tab for the settings window to display the configurable + options for this plugin to the user. """ - pass + if self.settingsTabClass: + return self.settingsTabClass(self.name, + self.getString(StringContent.VisibleName)[u'title']) + return None def addToMenu(self, menubar): """ @@ -320,37 +335,39 @@ class Plugin(QtCore.QObject): """ return self.textStrings[name] - def setPluginTextStrings(self): + def setPluginUiTextStrings(self, tooltips): """ Called to define all translatable texts of the plugin """ ## Load Action ## - self._setSingularTextString(StringContent.Load, - UiStrings.Load, UiStrings.LoadANew) + self.__setNameTextString(StringContent.Load, + UiStrings.Load, tooltips[u'load']) + ## Import Action ## + self.__setNameTextString(StringContent.Import, + UiStrings.Import, tooltips[u'import']) ## New Action ## - self._setSingularTextString(StringContent.New, - UiStrings.Add, UiStrings.AddANew) + self.__setNameTextString(StringContent.New, + UiStrings.Add, tooltips[u'new']) ## Edit Action ## - self._setSingularTextString(StringContent.Edit, - UiStrings.Edit, UiStrings.EditSelect) + self.__setNameTextString(StringContent.Edit, + UiStrings.Edit, tooltips[u'edit']) ## Delete Action ## - self._setSingularTextString(StringContent.Delete, - UiStrings.Delete, UiStrings.DeleteSelect) + self.__setNameTextString(StringContent.Delete, + UiStrings.Delete, tooltips[u'delete']) ## Preview Action ## - self._setSingularTextString(StringContent.Preview, - UiStrings.Preview, UiStrings.PreviewSelect) + self.__setNameTextString(StringContent.Preview, + UiStrings.Preview, tooltips[u'preview']) ## Send Live Action ## - self._setSingularTextString(StringContent.Live, - UiStrings.Live, UiStrings.SendSelectLive) + self.__setNameTextString(StringContent.Live, + UiStrings.Live, tooltips[u'live']) ## Add to Service Action ## - self._setSingularTextString(StringContent.Service, - UiStrings.Service, UiStrings.AddSelectService) + self.__setNameTextString(StringContent.Service, + UiStrings.Service, tooltips[u'service']) - def _setSingularTextString(self, name, title, tooltip): + def __setNameTextString(self, name, title, tooltip): """ Utility method for creating a plugin's textStrings. This method makes use of the singular name of the plugin object so must only be called after this has been set. """ - self.textStrings[name] = { u'title': title, u'tooltip': tooltip % - self.getString(StringContent.Name)[u'singular']} + self.textStrings[name] = {u'title': title, u'tooltip': tooltip} diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py index 5896ca4e6..32a29915f 100644 --- a/openlp/core/lib/rendermanager.py +++ b/openlp/core/lib/rendermanager.py @@ -68,7 +68,6 @@ class RenderManager(object): self.theme_level = u'' self.override_background = None self.theme_data = None - self.alertTab = None self.force_page = False def update_display(self): @@ -261,4 +260,4 @@ class RenderManager(object): log.debug(u'calculate default %d, %d, %f', self.width, self.height, self.screen_ratio ) # 90% is start of footer - self.footer_start = int(self.height * 0.90) \ No newline at end of file + self.footer_start = int(self.height * 0.90) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index c74b89144..31852709a 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -28,11 +28,13 @@ The :mod:`serviceitem` provides the service item functionality including the type and capability of an item. """ +import datetime import logging import os import uuid from openlp.core.lib import build_icon, clean_tags, expand_tags +from openlp.core.lib.ui import UiStrings log = logging.getLogger(__name__) @@ -60,6 +62,7 @@ class ItemCapabilities(object): AddIfNewItem = 9 ProvidesOwnDisplay = 10 AllowsDetailedTitleDisplay = 11 + AllowsVarableStartTime = 12 class ServiceItem(object): @@ -105,6 +108,8 @@ class ServiceItem(object): self.data_string = u'' self.edit_id = None self.xml_version = None + self.start_time = 0 + self.media_length = 0 self._new_item() def _new_item(self): @@ -257,7 +262,9 @@ class ServiceItem(object): u'capabilities': self.capabilities, u'search': self.search_string, u'data': self.data_string, - u'xml_version': self.xml_version + u'xml_version': self.xml_version, + u'start_time': self.start_time, + u'media_length': self.media_length } service_data = [] if self.service_item_type == ServiceItemType.Text: @@ -301,6 +308,10 @@ class ServiceItem(object): self.data_string = header[u'data'] if u'xml_version' in header: self.xml_version = header[u'xml_version'] + if u'start_time' in header: + self.start_time = header[u'start_time'] + if u'media_length' in header: + self.media_length = header[u'media_length'] if self.service_item_type == ServiceItemType.Text: for slide in serviceitem[u'serviceitem'][u'data']: self._raw_frames.append(slide) @@ -420,3 +431,25 @@ class ServiceItem(object): return self._raw_frames[row][u'path'] except IndexError: return u'' + + def get_media_time(self): + """ + Returns the start and finish time for a media item + """ + tooltip = None + start = None + end = None + if self.start_time != 0: + start = UiStrings.StartTimeCode % \ + unicode(datetime.timedelta(seconds=self.start_time)) + if self.media_length != 0: + end = UiStrings.LengthTime % \ + unicode(datetime.timedelta(seconds=self.media_length)) + if not start and not end: + return None + elif start and not end: + return start + elif not start and end: + return end + else: + return u'%s : %s' % (start, end) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index dca226069..4189452bc 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -164,13 +164,27 @@ class BackgroundGradientType(object): elif type_string == u'leftBottom': return BackgroundGradientType.LeftBottom + class HorizontalType(object): """ Type enumeration for horizontal alignment. """ Left = 0 - Center = 1 - Right = 2 + Center = 2 + Right = 1 + + @staticmethod + def to_string(horizontal_type): + """ + Return a string representation of a horizontal type. + """ + if horizontal_type == Horizontal.Right: + return u'right' + elif horizontal_type == Horizontal.Center: + return u'center' + else: + return u'left' + class VerticalType(object): """ @@ -180,6 +194,19 @@ class VerticalType(object): Middle = 1 Bottom = 2 + @staticmethod + def to_string(vertical_type): + """ + Return a string representation of a vertical type. + """ + if vertical_type == VerticalType.Bottom: + return u'bottom' + elif vertical_type == VerticalType.Middle: + return u'middle' + else: + return u'top' + + BOOLEAN_LIST = [u'bold', u'italics', u'override', u'outline', u'shadow', u'slide_transition'] diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index ec1c324fe..a98e2fb7f 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -41,36 +41,32 @@ class UiStrings(object): # These strings should need a good reason to be retranslated elsewhere. # Should some/more/less of these have an & attached? Add = translate('OpenLP.Ui', '&Add') - AddANew = unicode(translate('OpenLP.Ui', 'Add a new %s')) - AddSelectService = unicode(translate('OpenLP.Ui', - 'Add the selected %s to the service')) + Advanced = translate('OpenLP.Ui', 'Advanced') AllFiles = translate('OpenLP.Ui', 'All Files') Authors = translate('OpenLP.Ui', 'Authors') + CreateService = translate('OpenLP.Ui', 'Create a new service.') Delete = translate('OpenLP.Ui', '&Delete') - DeleteSelect = unicode(translate('OpenLP.Ui', 'Delete the selected %s')) - DeleteType = unicode(translate('OpenLP.Ui', 'Delete %s')) Edit = translate('OpenLP.Ui', '&Edit') - EditSelect = unicode(translate('OpenLP.Ui', 'Edit the selected %s')) - EditType = unicode(translate('OpenLP.Ui', 'Edit %s')) Error = translate('OpenLP.Ui', 'Error') - ExportType = unicode(translate('OpenLP.Ui', 'Export %s')) Import = translate('OpenLP.Ui', 'Import') - ImportType = unicode(translate('OpenLP.Ui', 'Import %s')) + LengthTime = unicode(translate('OpenLP.Ui', 'Length %s')) Live = translate('OpenLP.Ui', 'Live') Load = translate('OpenLP.Ui', 'Load') - LoadANew = unicode(translate('OpenLP.Ui', 'Load a new %s')) New = translate('OpenLP.Ui', 'New') - NewType = unicode(translate('OpenLP.Ui', 'New %s')) + NewService = translate('OpenLP.Ui', 'New Service') OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') + OpenService = translate('OpenLP.Ui', 'Open Service') Preview = translate('OpenLP.Ui', 'Preview') - PreviewSelect = unicode(translate('OpenLP.Ui', 'Preview the selected %s')) - SendSelectLive = unicode(translate('OpenLP.Ui', - 'Send the selected %s live')) + ReplaceBG = translate('OpenLP.Ui', 'Replace Background') + ReplaceLiveBG = translate('OpenLP.Ui', 'Replace Live Background') + ResetBG = translate('OpenLP.Ui', 'Reset Background') + ResetLiveBG = translate('OpenLP.Ui', 'Reset Live Background') + SaveService = translate('OpenLP.Ui', 'Save Service') Service = translate('OpenLP.Ui', 'Service') + StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s')) Theme = translate('OpenLP.Ui', 'Theme') Themes = translate('OpenLP.Ui', 'Themes') - def add_welcome_page(parent, image): """ Generate an opening welcome page for a wizard using a provided image. @@ -98,18 +94,25 @@ def add_welcome_page(parent, image): parent.welcomeLayout.addStretch() parent.addPage(parent.welcomePage) -def create_save_cancel_button_box(parent): +def create_accept_reject_button_box(parent, okay=False): """ - Creates a standard dialog button box with save and cancel buttons. The - button box is connected to the parent's ``accept()`` and ``reject()`` + Creates a standard dialog button box with two buttons. The buttons default + to save and cancel but the ``okay`` parameter can be used to make the + buttons okay and cancel instead. + The button box is connected to the parent's ``accept()`` and ``reject()`` methods to handle the default ``accepted()`` and ``rejected()`` signals. ``parent`` The parent object. This should be a ``QWidget`` descendant. + + ``okay`` + If true creates an okay/cancel combination instead of save/cancel. """ button_box = QtGui.QDialogButtonBox(parent) - button_box.setStandardButtons( - QtGui.QDialogButtonBox.Save | QtGui.QDialogButtonBox.Cancel) + accept_button = QtGui.QDialogButtonBox.Save + if okay: + accept_button = QtGui.QDialogButtonBox.Ok + button_box.setStandardButtons(accept_button | QtGui.QDialogButtonBox.Cancel) button_box.setObjectName(u'%sButtonBox' % parent) QtCore.QObject.connect(button_box, QtCore.SIGNAL(u'accepted()'), parent.accept) diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index eb0e89775..45218802e 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -28,7 +28,7 @@ The :mod:`ui` module provides the core user interface for OpenLP """ from PyQt4 import QtGui -from openlp.core.lib import translate, Receiver +from openlp.core.lib import translate class HideMode(object): """ @@ -53,6 +53,7 @@ class HideMode(object): from themeform import ThemeForm from filerenameform import FileRenameForm +from starttimeform import StartTimeForm from maindisplay import MainDisplay from servicenoteform import ServiceNoteForm from serviceitemeditform import ServiceItemEditForm diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py index 597d3fb24..bb09ab91f 100644 --- a/openlp/core/ui/aboutdialog.py +++ b/openlp/core/ui/aboutdialog.py @@ -164,9 +164,10 @@ class Ui_AboutDialog(object): self.licenseTextEdit.setPlainText(translate('OpenLP.AboutForm', 'Copyright \xa9 2004-2011 Raoul Snyman\n' 'Portions copyright \xa9 2004-2011 ' - 'Tim Bentley, Jonathan Corwin, Michael Gorven, Scott Guerrieri, ' - 'Christian Richter, Maikel Stuivenberg, Martin Thompson, Jon ' - 'Tibble, Carsten Tinggaard\n' + 'Tim Bentley, Jonathan Corwin, Michael Gorven, Scott Guerrieri,\n' + 'Meinert Jordan, Andreas Preikschat, Christian Richter, Philip\n' + 'Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Carstenn' + 'Tinggaard, Frode Woldsund\n' '\n' 'This program is free software; you can redistribute it and/or ' 'modify it under the terms of the GNU General Public License as ' diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 58b637bc2..918335b2e 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -29,6 +29,7 @@ The :mod:`advancedtab` provides an advanced settings facility. from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, translate +from openlp.core.lib.ui import UiStrings class AdvancedTab(SettingsTab): """ @@ -112,7 +113,7 @@ class AdvancedTab(SettingsTab): """ Setup the interface translation strings. """ - self.tabTitleVisible = translate('OpenLP.AdvancedTab', 'Advanced') + self.tabTitleVisible = UiStrings.Advanced self.uiGroupBox.setTitle(translate('OpenLP.AdvancedTab', 'UI Settings')) self.recentLabel.setText( translate('OpenLP.AdvancedTab', diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 7f9e23c61..f0c1c9ab0 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -56,6 +56,7 @@ except ImportError: from openlp.core.lib import translate, SettingsManager from openlp.core.lib.mailto import mailto +from openlp.core.lib.ui import UiStrings from exceptiondialog import Ui_ExceptionDialog @@ -176,8 +177,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog): self,translate('ImagePlugin.ExceptionDialog', 'Select Attachment'), SettingsManager.get_last_dir(u'exceptions'), - u'%s (*.*) (*)' % - unicode(translate('ImagePlugin.MediaItem', 'All Files'))) + u'%s (*.*) (*)' % UiStrings.AllFiles) log.info(u'New files(s) %s', unicode(files)) if files: self.fileAttachment = unicode(files) diff --git a/openlp/core/ui/filerenamedialog.py b/openlp/core/ui/filerenamedialog.py index c9a8a7633..147207494 100644 --- a/openlp/core/ui/filerenamedialog.py +++ b/openlp/core/ui/filerenamedialog.py @@ -27,30 +27,28 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_FileRenameDialog(object): - def setupUi(self, FileRenameDialog): - FileRenameDialog.setObjectName(u'FileRenameDialog') - FileRenameDialog.resize(300, 10) - self.dialogLayout = QtGui.QGridLayout(FileRenameDialog) + def setupUi(self, fileRenameDialog): + fileRenameDialog.setObjectName(u'fileRenameDialog') + fileRenameDialog.resize(300, 10) + self.dialogLayout = QtGui.QGridLayout(fileRenameDialog) self.dialogLayout.setObjectName(u'dialogLayout') - self.fileNameLabel = QtGui.QLabel(FileRenameDialog) + self.fileNameLabel = QtGui.QLabel(fileRenameDialog) self.fileNameLabel.setObjectName(u'fileNameLabel') self.dialogLayout.addWidget(self.fileNameLabel, 0, 0) - self.fileNameEdit = QtGui.QLineEdit(FileRenameDialog) + self.fileNameEdit = QtGui.QLineEdit(fileRenameDialog) self.fileNameEdit.setValidator(QtGui.QRegExpValidator( QtCore.QRegExp(r'[^/\\?*|<>\[\]":<>+%]+'), self)) self.fileNameEdit.setObjectName(u'fileNameEdit') self.dialogLayout.addWidget(self.fileNameEdit, 0, 1) - self.buttonBox = QtGui.QDialogButtonBox(FileRenameDialog) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | - QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(u'buttonBox') + self.buttonBox = create_accept_reject_button_box(fileRenameDialog, True) self.dialogLayout.addWidget(self.buttonBox, 1, 0, 1, 2) - self.retranslateUi(FileRenameDialog) + self.retranslateUi(fileRenameDialog) self.setMaximumHeight(self.sizeHint().height()) - QtCore.QMetaObject.connectSlotsByName(FileRenameDialog) + QtCore.QMetaObject.connectSlotsByName(fileRenameDialog) - def retranslateUi(self, FileRenameDialog): + def retranslateUi(self, fileRenameDialog): self.fileNameLabel.setText(translate('OpenLP.FileRenameForm', 'New File Name:')) diff --git a/openlp/core/ui/filerenameform.py b/openlp/core/ui/filerenameform.py index 86634e3b1..8b37cbc86 100644 --- a/openlp/core/ui/filerenameform.py +++ b/openlp/core/ui/filerenameform.py @@ -24,7 +24,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from filerenamedialog import Ui_FileRenameDialog @@ -37,10 +37,6 @@ class FileRenameForm(QtGui.QDialog, Ui_FileRenameDialog): def __init__(self, parent): QtGui.QDialog.__init__(self, parent) self.setupUi(self) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'accepted()'), - self.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'), - self.reject) def exec_(self, copy=False): """ @@ -51,5 +47,5 @@ class FileRenameForm(QtGui.QDialog, Ui_FileRenameDialog): 'File Copy')) else: self.setWindowTitle(translate('OpenLP.FileRenameForm', - 'File Rename')) + 'File Rename')) return QtGui.QDialog.exec_(self) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index c4ab75aac..90042f80b 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -106,6 +106,9 @@ class MainDisplay(DisplayWidget): self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject) Phonon.createPath(self.mediaObject, self.videoWidget) Phonon.createPath(self.mediaObject, self.audio) + QtCore.QObject.connect(self.mediaObject, + QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'), + self.videoStart) self.webView = QtWebKit.QWebView(self) self.webView.setGeometry(0, 0, self.screen[u'size'].width(), self.screen[u'size'].height()) @@ -145,7 +148,7 @@ class MainDisplay(DisplayWidget): serviceItem = ServiceItem() serviceItem.bg_image_bytes = image_to_byte(initialFrame) self.webView.setHtml(build_html(serviceItem, self.screen, - self.parent.alertTab, self.isLive, None)) + self.alertTab, self.isLive, None)) self.initialFrame = True # To display or not to display? if not self.screen[u'primary']: @@ -341,6 +344,13 @@ class MainDisplay(DisplayWidget): Receiver.send_message(u'maindisplay_active') return self.preview() + def videoStart(self, newState, oldState): + """ + Start the video at a predetermined point. + """ + if newState == Phonon.PlayingState: + self.mediaObject.seek(self.serviceItem.start_time * 1000) + def isWebLoaded(self): """ Called by webView event to show display is fully loaded @@ -406,7 +416,7 @@ class MainDisplay(DisplayWidget): if self.serviceItem.themedata.background_filename: self.serviceItem.bg_image_bytes = self.imageManager. \ get_image_bytes(self.serviceItem.themedata.theme_name) - html = build_html(self.serviceItem, self.screen, self.parent.alertTab, + html = build_html(self.serviceItem, self.screen, self.alertTab, self.isLive, background) log.debug(u'buildHtml - pre setHtml') self.webView.setHtml(html) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index c87a9544a..c691c006e 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -216,6 +216,9 @@ class Ui_MainWindow(object): self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', u':/tools/tools_add.png') mainWindow.actionList.add_action(self.ToolsAddToolItem, u'Tools') + self.ToolsOpenDataFolder = icon_action(mainWindow, + u'ToolsOpenDataFolder', u':/general/general_open.png') + mainWindow.actionList.add_action(self.ToolsOpenDataFolder, u'Tools') self.settingsPluginListItem = icon_action(mainWindow, u'settingsPluginListItem', u':/system/settings_plugin_list.png') mainWindow.actionList.add_action(self.settingsPluginListItem, @@ -276,6 +279,7 @@ class Ui_MainWindow(object): self.SettingsLanguageMenu.menuAction(), None, self.SettingsShortcutsItem, self.SettingsConfigureItem)) add_actions(self.ToolsMenu, (self.ToolsAddToolItem, None)) + add_actions(self.ToolsMenu, (self.ToolsOpenDataFolder, None)) add_actions(self.HelpMenu, (self.HelpDocumentationItem, self.HelpOnlineHelpItem, None, self.HelpWebSiteItem, self.HelpAboutItem)) @@ -315,20 +319,16 @@ class Ui_MainWindow(object): self.themeManagerDock.setWindowTitle( translate('OpenLP.MainWindow', 'Theme Manager')) self.FileNewItem.setText(translate('OpenLP.MainWindow', '&New')) - self.FileNewItem.setToolTip( - translate('OpenLP.MainWindow', 'New Service')) - self.FileNewItem.setStatusTip( - translate('OpenLP.MainWindow', 'Create a new service.')) + self.FileNewItem.setToolTip(UiStrings.NewService) + self.FileNewItem.setStatusTip(UiStrings.CreateService) self.FileNewItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+N')) self.FileOpenItem.setText(translate('OpenLP.MainWindow', '&Open')) - self.FileOpenItem.setToolTip( - translate('OpenLP.MainWindow', 'Open Service')) + self.FileOpenItem.setToolTip(UiStrings.OpenService) self.FileOpenItem.setStatusTip( translate('OpenLP.MainWindow', 'Open an existing service.')) self.FileOpenItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+O')) self.FileSaveItem.setText(translate('OpenLP.MainWindow', '&Save')) - self.FileSaveItem.setToolTip( - translate('OpenLP.MainWindow', 'Save Service')) + self.FileSaveItem.setToolTip(UiStrings.SaveService) self.FileSaveItem.setStatusTip( translate('OpenLP.MainWindow', 'Save the current service to disk.')) self.FileSaveItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+S')) @@ -433,6 +433,10 @@ class Ui_MainWindow(object): translate('OpenLP.MainWindow', 'Add &Tool...')) self.ToolsAddToolItem.setStatusTip(translate('OpenLP.MainWindow', 'Add an application to the list of tools.')) + self.ToolsOpenDataFolder.setText( + translate('OpenLP.MainWindow', 'Open &Data Folder...')) + self.ToolsOpenDataFolder.setStatusTip(translate('OpenLP.MainWindow', + 'Open the folder where songs, bibles and other data resides.')) self.ModeDefaultItem.setText( translate('OpenLP.MainWindow', '&Default')) self.ModeDefaultItem.setStatusTip(translate('OpenLP.MainWindow', @@ -515,6 +519,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): QtCore.SIGNAL(u'triggered()'), self.onHelpWebSiteClicked) QtCore.QObject.connect(self.HelpAboutItem, QtCore.SIGNAL(u'triggered()'), self.onHelpAboutItemClicked) + QtCore.QObject.connect(self.ToolsOpenDataFolder, + QtCore.SIGNAL(u'triggered()'), self.onToolsOpenDataFolderClicked) QtCore.QObject.connect(self.settingsPluginListItem, QtCore.SIGNAL(u'triggered()'), self.onPluginItemClicked) QtCore.QObject.connect(self.SettingsConfigureItem, @@ -703,6 +709,13 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.pluginForm.load() self.pluginForm.exec_() + def onToolsOpenDataFolderClicked(self): + """ + Open data folder + """ + path = AppLocation.get_data_path() + QtGui.QDesktopServices.openUrl(QtCore.QUrl("file:///" + path)) + def onSettingsConfigureItemClicked(self): """ Show the Settings dialog diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py index 3b01f9ac7..70128cb89 100644 --- a/openlp/core/ui/printserviceorderform.py +++ b/openlp/core/ui/printserviceorderform.py @@ -24,7 +24,6 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### import datetime -import mutagen import os from PyQt4 import QtCore, QtGui @@ -113,16 +112,9 @@ class PrintServiceOrderForm(QtGui.QDialog, Ui_PrintServiceOrderDialog): item.notes.replace(u'\n', u'
')) # Add play length of media files. if item.is_media() and self.printMetaDataCheckBox.isChecked(): - path = os.path.join(item.get_frames()[0][u'path'], - item.get_frames()[0][u'title']) - if not os.path.isfile(path): - continue - file = mutagen.File(path) - if file is not None: - length = int(file.info.length) - text += u'

%s %s

' % (translate( - 'OpenLP.ServiceManager', u'Playing time:'), - unicode(datetime.timedelta(seconds=length))) + text += u'

%s %s

' % (translate( + 'OpenLP.ServiceManager', u'Playing time:'), + unicode(datetime.timedelta(seconds=item.media_length))) if self.customNoteEdit.toPlainText(): text += u'

%s

%s' % (translate('OpenLP.ServiceManager', u'Custom Service Notes:'), self.customNoteEdit.toPlainText()) diff --git a/openlp/core/ui/serviceitemeditdialog.py b/openlp/core/ui/serviceitemeditdialog.py index f22f3dcf5..ef7e99a5f 100644 --- a/openlp/core/ui/serviceitemeditdialog.py +++ b/openlp/core/ui/serviceitemeditdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import create_save_cancel_button_box, \ +from openlp.core.lib.ui import create_accept_reject_button_box, \ create_delete_push_button, create_up_down_push_button_set class Ui_ServiceItemEditDialog(object): @@ -50,7 +50,7 @@ class Ui_ServiceItemEditDialog(object): self.buttonLayout.addWidget(self.downButton) self.dialogLayout.addLayout(self.buttonLayout, 0, 1) self.dialogLayout.addWidget( - create_save_cancel_button_box(serviceItemEditDialog), 1, 0, 1, 2) + create_accept_reject_button_box(serviceItemEditDialog), 1, 0, 1, 2) self.retranslateUi(serviceItemEditDialog) QtCore.QMetaObject.connectSlotsByName(serviceItemEditDialog) diff --git a/openlp/core/ui/serviceitemeditform.py b/openlp/core/ui/serviceitemeditform.py index 70518f30c..ae09cb0b2 100644 --- a/openlp/core/ui/serviceitemeditform.py +++ b/openlp/core/ui/serviceitemeditform.py @@ -110,11 +110,12 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog): temp = self.itemList[row] self.itemList.remove(self.itemList[row]) if direction == u'up': - self.itemList.insert(row - 1, temp) + row -= 1 else: - self.itemList.insert(row + 1, temp) + row += 1 + self.itemList.insert(row, temp) self.loadData() - self.listWidget.setCurrentRow(row + 1) + self.listWidget.setCurrentRow(row) def onCurrentRowChanged(self, row): """ diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 079e2202c..623c2d641 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -35,8 +35,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import OpenLPToolbar, ServiceItem, context_menu_action, \ Receiver, build_icon, ItemCapabilities, SettingsManager, translate, \ ThemeLevel -from openlp.core.lib.ui import critical_error_message_box -from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm +from openlp.core.lib.ui import UiStrings, critical_error_message_box +from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceorderform import PrintServiceOrderForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ split_filename @@ -88,6 +88,7 @@ class ServiceManager(QtGui.QWidget): self._fileName = u'' self.serviceNoteForm = ServiceNoteForm(self.mainwindow) self.serviceItemEditForm = ServiceItemEditForm(self.mainwindow) + self.startTimeForm = StartTimeForm(self.mainwindow) # start with the layout self.layout = QtGui.QVBoxLayout(self) self.layout.setSpacing(0) @@ -95,18 +96,14 @@ class ServiceManager(QtGui.QWidget): # Create the top toolbar self.toolbar = OpenLPToolbar(self) self.toolbar.addToolbarButton( - translate('OpenLP.ServiceManager', 'New Service'), - u':/general/general_new.png', - translate('OpenLP.ServiceManager', 'Create a new service'), - self.onNewServiceClicked) + UiStrings.NewService, u':/general/general_new.png', + UiStrings.CreateService, self.onNewServiceClicked) self.toolbar.addToolbarButton( - translate('OpenLP.ServiceManager', 'Open Service'), - u':/general/general_open.png', + UiStrings.OpenService, u':/general/general_open.png', translate('OpenLP.ServiceManager', 'Load an existing service'), self.onLoadServiceClicked) self.toolbar.addToolbarButton( - translate('OpenLP.ServiceManager', 'Save Service'), - u':/general/general_save.png', + UiStrings.SaveService, u':/general/general_save.png', translate('OpenLP.ServiceManager', 'Save this service'), self.saveFile) self.toolbar.addSeparator() @@ -270,16 +267,19 @@ class ServiceManager(QtGui.QWidget): self.notesAction = self.menu.addAction( translate('OpenLP.ServiceManager', '&Notes')) self.notesAction.setIcon(build_icon(u':/services/service_notes.png')) + self.timeAction = self.menu.addAction( + translate('OpenLP.ServiceManager', '&Start Time')) + self.timeAction.setIcon(build_icon(u':/media/media_time.png')) self.deleteAction = self.menu.addAction( translate('OpenLP.ServiceManager', '&Delete From Service')) self.deleteAction.setIcon(build_icon(u':/general/general_delete.png')) self.sep1 = self.menu.addAction(u'') self.sep1.setSeparator(True) self.previewAction = self.menu.addAction( - translate('OpenLP.ServiceManager', '&Preview Verse')) + translate('OpenLP.ServiceManager', 'Show &Preview')) self.previewAction.setIcon(build_icon(u':/general/general_preview.png')) self.liveAction = self.menu.addAction( - translate('OpenLP.ServiceManager', '&Live Verse')) + translate('OpenLP.ServiceManager', 'Show &Live')) self.liveAction.setIcon(build_icon(u':/general/general_live.png')) self.sep2 = self.menu.addAction(u'') self.sep2.setSeparator(True) @@ -465,7 +465,7 @@ class ServiceManager(QtGui.QWidget): save the file. """ fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow, - translate('OpenLP.ServiceManager', 'Save Service'), + UiStrings.SaveService, SettingsManager.get_last_dir( self.mainwindow.serviceSettingsSection), translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)'))) @@ -563,6 +563,7 @@ class ServiceManager(QtGui.QWidget): self.editAction.setVisible(False) self.maintainAction.setVisible(False) self.notesAction.setVisible(False) + self.timeAction.setVisible(False) if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit)\ and serviceItem[u'service_item'].edit_id: self.editAction.setVisible(True) @@ -571,6 +572,9 @@ class ServiceManager(QtGui.QWidget): self.maintainAction.setVisible(True) if item.parent() is None: self.notesAction.setVisible(True) + if serviceItem[u'service_item']\ + .is_capable(ItemCapabilities.AllowsVarableStartTime): + self.timeAction.setVisible(True) self.themeMenu.menuAction().setVisible(False) if serviceItem[u'service_item'].is_text(): self.themeMenu.menuAction().setVisible(True) @@ -583,6 +587,8 @@ class ServiceManager(QtGui.QWidget): self.onDeleteFromService() if action == self.notesAction: self.onServiceItemNoteForm() + if action == self.timeAction: + self.onStartTimeForm() if action == self.previewAction: self.makePreview() if action == self.liveAction: @@ -597,6 +603,16 @@ class ServiceManager(QtGui.QWidget): self.serviceNoteForm.textEdit.toPlainText() self.repaintServiceList(item, -1) + def onStartTimeForm(self): + item = self.findServiceItem()[0] + self.startTimeForm.item = self.serviceItems[item] + if self.startTimeForm.exec_(): + self.serviceItems[item][u'service_item'].start_time = \ + self.startTimeForm.hourSpinBox.value() * 3600 + \ + self.startTimeForm.minuteSpinBox.value() * 60 + \ + self.startTimeForm.secondSpinBox.value() + self.repaintServiceList(item, -1) + def onServiceItemEditForm(self): item = self.findServiceItem()[0] self.serviceItemEditForm.setServiceItem( @@ -843,6 +859,11 @@ class ServiceManager(QtGui.QWidget): text = frame[u'title'].replace(u'\n', u' ') child.setText(0, text[:40]) child.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(count)) + if item[u'service_item'] \ + .is_capable(ItemCapabilities.AllowsVarableStartTime): + tip = item[u'service_item'].get_media_time() + if tip: + child.setToolTip(0, tip) if serviceItem == itemcount: if item[u'expanded'] and serviceItemChild == count: self.serviceManagerList.setCurrentItem(child) diff --git a/openlp/core/ui/servicenoteform.py b/openlp/core/ui/servicenoteform.py index 473cc1685..a12b693f8 100644 --- a/openlp/core/ui/servicenoteform.py +++ b/openlp/core/ui/servicenoteform.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box class ServiceNoteForm(QtGui.QDialog): """ @@ -48,7 +48,7 @@ class ServiceNoteForm(QtGui.QDialog): self.textEdit = QtGui.QTextEdit(self) self.textEdit.setObjectName(u'textEdit') self.dialogLayout.addWidget(self.textEdit) - self.dialogLayout.addWidget(create_save_cancel_button_box(self)) + self.dialogLayout.addWidget(create_accept_reject_button_box(self)) QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 61c73961c..99acadc14 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -27,6 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate, build_icon +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_SettingsDialog(object): def setupUi(self, settingsDialog): @@ -40,16 +41,9 @@ class Ui_SettingsDialog(object): self.settingsTabWidget = QtGui.QTabWidget(settingsDialog) self.settingsTabWidget.setObjectName(u'settingsTabWidget') self.settingsLayout.addWidget(self.settingsTabWidget) - self.buttonBox = QtGui.QDialogButtonBox(settingsDialog) - self.buttonBox.setStandardButtons( - QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(u'buttonBox') + self.buttonBox = create_accept_reject_button_box(settingsDialog, True) self.settingsLayout.addWidget(self.buttonBox) self.retranslateUi(settingsDialog) - QtCore.QObject.connect(self.buttonBox, - QtCore.SIGNAL(u'accepted()'), settingsDialog.accept) - QtCore.QObject.connect(self.buttonBox, - QtCore.SIGNAL(u'rejected()'), settingsDialog.reject) QtCore.QMetaObject.connectSlotsByName(settingsDialog) def retranslateUi(self, settingsDialog): diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index c6b58ac56..52626f24f 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -877,7 +877,7 @@ class SlideController(QtGui.QWidget): using *Blank to Theme*. """ log.debug(u'updatePreview %s ' % self.screens.current[u'primary']) - if not self.screens.current[u'primary'] and \ + if not self.screens.current[u'primary'] and self.serviceItem and \ self.serviceItem.is_capable(ItemCapabilities.ProvidesOwnDisplay): # Grab now, but try again in a couple of seconds if slide change # is slow diff --git a/openlp/core/ui/starttimedialog.py b/openlp/core/ui/starttimedialog.py new file mode 100644 index 000000000..8dcc2c9ee --- /dev/null +++ b/openlp/core/ui/starttimedialog.py @@ -0,0 +1,70 @@ +# -*- 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, 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 +from openlp.core.lib.ui import create_accept_reject_button_box + +class Ui_StartTimeDialog(object): + def setupUi(self, StartTimeDialog): + StartTimeDialog.setObjectName(u'StartTimeDialog') + StartTimeDialog.resize(300, 10) + self.dialogLayout = QtGui.QGridLayout(StartTimeDialog) + self.dialogLayout.setObjectName(u'dialogLayout') + self.hourLabel = QtGui.QLabel(StartTimeDialog) + self.hourLabel.setObjectName("hourLabel") + self.dialogLayout.addWidget(self.hourLabel, 0, 0, 1, 1) + self.hourSpinBox = QtGui.QSpinBox(StartTimeDialog) + self.hourSpinBox.setObjectName("hourSpinBox") + self.dialogLayout.addWidget(self.hourSpinBox, 0, 1, 1, 1) + self.minuteLabel = QtGui.QLabel(StartTimeDialog) + self.minuteLabel.setObjectName("minuteLabel") + self.dialogLayout.addWidget(self.minuteLabel, 1, 0, 1, 1) + self.minuteSpinBox = QtGui.QSpinBox(StartTimeDialog) + self.minuteSpinBox.setObjectName("minuteSpinBox") + self.dialogLayout.addWidget(self.minuteSpinBox, 1, 1, 1, 1) + self.secondLabel = QtGui.QLabel(StartTimeDialog) + self.secondLabel.setObjectName("secondLabel") + self.dialogLayout.addWidget(self.secondLabel, 2, 0, 1, 1) + self.secondSpinBox = QtGui.QSpinBox(StartTimeDialog) + self.secondSpinBox.setObjectName("secondSpinBox") + self.dialogLayout.addWidget(self.secondSpinBox, 2, 1, 1, 1) + self.buttonBox = create_accept_reject_button_box(StartTimeDialog, True) + self.dialogLayout.addWidget(self.buttonBox, 4, 0, 1, 2) + self.retranslateUi(StartTimeDialog) + self.setMaximumHeight(self.sizeHint().height()) + QtCore.QMetaObject.connectSlotsByName(StartTimeDialog) + + def retranslateUi(self, StartTimeDialog): + self.setWindowTitle(translate('OpenLP.StartTimeForm', + 'Item Start Time')) + self.hourLabel.setText(translate('OpenLP.StartTimeForm', 'Hours:')) + self.hourSpinBox.setSuffix(translate('OpenLP.StartTimeForm', 'h')) + self.minuteSpinBox.setSuffix(translate('OpenLP.StartTimeForm', 'm')) + self.secondSpinBox.setSuffix(translate('OpenLP.StartTimeForm', 's')) + self.minuteLabel.setText(translate('OpenLP.StartTimeForm', 'Minutes:')) + self.secondLabel.setText(translate('OpenLP.StartTimeForm', 'Seconds:')) diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py new file mode 100644 index 000000000..1280931d5 --- /dev/null +++ b/openlp/core/ui/starttimeform.py @@ -0,0 +1,52 @@ +# -*- 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, 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 QtGui + +from starttimedialog import Ui_StartTimeDialog + +class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog): + """ + The exception dialog + """ + def __init__(self, parent): + QtGui.QDialog.__init__(self, parent) + self.setupUi(self) + + def exec_(self): + """ + Run the Dialog with correct heading. + """ + seconds = self.item[u'service_item'].start_time + hours = seconds / 3600 + seconds -= 3600 * hours + minutes = seconds / 60 + seconds -= 60 * minutes + self.hourSpinBox.setValue(hours) + self.minuteSpinBox.setValue(minutes) + self.secondSpinBox.setValue(seconds) + return QtGui.QDialog.exec_(self) + diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index f86fa0143..ad9e80d66 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -483,7 +483,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): Background Image button pushed. """ images_filter = get_images_filter() - images_filter = '%s;;%s (*.*) (*)' % (images_filter, UiStrings.AllFiles) + images_filter = u'%s;;%s (*.*) (*)' % ( + images_filter, UiStrings.AllFiles) filename = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.ThemeForm', 'Select Image'), u'', images_filter) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 93a5ef187..69028ad76 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -34,7 +34,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import OpenLPToolbar, ThemeXML, get_text_file_string, \ build_icon, Receiver, SettingsManager, translate, check_item_selected, \ - BackgroundType, BackgroundGradientType, check_directory_exists + BackgroundType, BackgroundGradientType, check_directory_exists, \ + VerticalType from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.theme import Theme from openlp.core.ui import FileRenameForm, ThemeForm @@ -62,28 +63,28 @@ class ThemeManager(QtGui.QWidget): self.layout.setObjectName(u'layout') self.toolbar = OpenLPToolbar(self) self.toolbar.addToolbarButton( - UiStrings.NewType % UiStrings.Theme, + translate('OpenLP.ThemeManager', 'New Theme'), u':/themes/theme_new.png', translate('OpenLP.ThemeManager', 'Create a new theme.'), self.onAddTheme) self.toolbar.addToolbarButton( - UiStrings.EditType % UiStrings.Theme, + translate('OpenLP.ThemeManager', 'Edit Theme'), u':/themes/theme_edit.png', translate('OpenLP.ThemeManager', 'Edit a theme.'), self.onEditTheme) self.deleteToolbarAction = self.toolbar.addToolbarButton( - UiStrings.DeleteType % UiStrings.Theme, + translate('OpenLP.ThemeManager', 'Delete Theme'), u':/general/general_delete.png', translate('OpenLP.ThemeManager', 'Delete a theme.'), self.onDeleteTheme) self.toolbar.addSeparator() self.toolbar.addToolbarButton( - UiStrings.ImportType % UiStrings.Theme, + translate('OpenLP.ThemeManager', 'Import Theme'), u':/general/general_import.png', translate('OpenLP.ThemeManager', 'Import a theme.'), self.onImportTheme) self.toolbar.addToolbarButton( - UiStrings.ExportType % UiStrings.Theme, + translate('OpenLP.ThemeManager', 'Export Theme'), u':/general/general_export.png', translate('OpenLP.ThemeManager', 'Export a theme.'), self.onExportTheme) @@ -405,8 +406,8 @@ class ThemeManager(QtGui.QWidget): files = QtGui.QFileDialog.getOpenFileNames(self, translate('OpenLP.ThemeManager', 'Select Theme Import File'), SettingsManager.get_last_dir(self.settingsSection), - translate('OpenLP.ThemeManager', 'Theme v1 (*.theme);;' - 'Theme v2 (*.otz);;All Files (*.*)')) + unicode(translate('OpenLP.ThemeManager', 'Theme v1 (*.theme);;' + 'Theme v2 (*.otz);;%s (*.*)')) % UiStrings.AllFiles) log.info(u'New Themes %s', unicode(files)) if files: for file in files: @@ -762,11 +763,11 @@ class ThemeManager(QtGui.QWidget): newtheme.font_main_outline = True newtheme.font_main_outline_color = \ unicode(theme.OutlineColor.name()) - vAlignCorrection = 0 + vAlignCorrection = VerticalType.Top if theme.VerticalAlign == 2: - vAlignCorrection = 1 + vAlignCorrection = VerticalType.Middle elif theme.VerticalAlign == 1: - vAlignCorrection = 2 + vAlignCorrection = VerticalType.Bottom newtheme.display_horizontal_align = theme.HorizontalAlign newtheme.display_vertical_align = vAlignCorrection return newtheme.extract_xml() diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 2fa448db8..d3410ded9 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -27,11 +27,12 @@ The :mod:``wizard`` module provides generic wizard tools for OpenLP. """ import logging +import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import build_icon, Receiver -from openlp.core.lib.ui import add_welcome_page +from openlp.core.lib import build_icon, Receiver, SettingsManager +from openlp.core.lib.ui import UiStrings, add_welcome_page log = logging.getLogger(__name__) @@ -70,6 +71,12 @@ class OpenLPWizard(QtGui.QWizard): self.retranslateUi() QtCore.QMetaObject.connectSlotsByName(self) + def registerFields(self): + """ + Hook method for wizards to register any fields they need. + """ + pass + def addProgressPage(self): """ Add the progress page for the wizard. This page informs the user how @@ -146,3 +153,30 @@ class OpenLPWizard(QtGui.QWizard): self.finishButton.setVisible(True) self.cancelButton.setVisible(False) Receiver.send_message(u'openlp_process_events') + + def getFileName(self, title, editbox, filters=u''): + """ + Opens a QFileDialog and saves the filename to the given editbox. + + ``title`` + The title of the dialog (unicode). + + ``editbox`` + A editbox (QLineEdit). + + ``filters`` + The file extension filters. It should contain the file description + as well as the file extension. For example:: + + u'OpenLP 2.0 Databases (*.sqlite)' + """ + if filters: + filters += u';;' + filters += u'%s (*)' % UiStrings.AllFiles + filename = QtGui.QFileDialog.getOpenFileName(self, title, + os.path.dirname(SettingsManager.get_last_dir( + self.plugin.settingsSection, 1)), filters) + if filename: + editbox.setText(filename) + SettingsManager.set_last_dir(self.plugin.settingsSection, + filename, 1) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 2862afef4..9db744460 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -165,6 +165,8 @@ def _get_os_dir_path(dir_type): Return a path based on which OS and environment we are running in. """ if sys.platform == u'win32': + if dir_type == AppLocation.DataDir: + return os.path.join(os.getenv(u'APPDATA'), u'openlp', u'data') return os.path.join(os.getenv(u'APPDATA'), u'openlp') elif sys.platform == u'darwin': if dir_type == AppLocation.DataDir: @@ -180,8 +182,9 @@ def _get_os_dir_path(dir_type): return os.path.join(BaseDirectory.xdg_data_home, u'openlp') elif dir_type == AppLocation.CacheDir: return os.path.join(BaseDirectory.xdg_cache_home, u'openlp') - else: - return os.path.join(os.getenv(u'HOME'), u'.openlp') + if dir_type == AppLocation.DataDir: + return os.path.join(os.getenv(u'HOME'), u'.openlp', u'data') + return os.path.join(os.getenv(u'HOME'), u'.openlp') def _get_frozen_path(frozen_option, non_frozen_option): """ @@ -189,8 +192,7 @@ def _get_frozen_path(frozen_option, non_frozen_option): """ if hasattr(sys, u'frozen') and sys.frozen == 1: return frozen_option - else: - return non_frozen_option + return non_frozen_option def check_latest_version(current_version): """ diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index 443ec1e84..136d775a5 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -40,21 +40,14 @@ class AlertsPlugin(Plugin): log.info(u'Alerts Plugin loaded') def __init__(self, plugin_helpers): - Plugin.__init__(self, u'Alerts', u'1.9.4', plugin_helpers) + Plugin.__init__(self, u'Alerts', u'1.9.4', plugin_helpers, + settingsTabClass=AlertsTab) self.weight = -3 self.icon = build_icon(u':/plugins/plugin_alerts.png') self.alertsmanager = AlertsManager(self) self.manager = Manager(u'alerts', init_schema) - self.visible_name = self.getString(StringContent.VisibleName) self.alertForm = AlertForm(self) - def getSettingsTab(self): - """ - Return the settings tab for the Alerts plugin - """ - self.alertsTab = AlertsTab(self, self.visible_name[u'title']) - return self.alertsTab - def addToolsMenuItem(self, tools_menu): """ Give the alerts plugin the opportunity to add items to the @@ -81,7 +74,7 @@ class AlertsPlugin(Plugin): log.info(u'Alerts Initialising') Plugin.initialise(self) self.toolsAlertItem.setVisible(True) - self.liveController.alertTab = self.alertsTab + self.liveController.alertTab = self.settings_tab def finalise(self): """ diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index 6fe0ae132..f69099bf1 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -84,7 +84,7 @@ class AlertsManager(QtCore.QObject): if len(self.alertList) == 0: return text = self.alertList.pop(0) - alertTab = self.parent.alertsTab + 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: @@ -103,4 +103,4 @@ class AlertsManager(QtCore.QObject): self.parent.liveController.display.alert(u'') self.killTimer(self.timer_id) self.timer_id = 0 - self.generateAlert() \ No newline at end of file + self.generateAlert() diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index 4090503db..48a4527ed 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -33,10 +33,8 @@ class AlertsTab(SettingsTab): """ AlertsTab is the alerts settings tab in the settings dialog. """ - def __init__(self, parent, visible_title): - self.parent = parent - self.manager = parent.manager - SettingsTab.__init__(self, parent.name, visible_title) + def __init__(self, name, visible_title): + SettingsTab.__init__(self, name, visible_title) def setupUi(self): self.setObjectName(u'AlertsTab') diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 89102a8eb..de0ea11f1 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -38,7 +38,8 @@ class BiblePlugin(Plugin): log.info(u'Bible Plugin loaded') def __init__(self, plugin_helpers): - Plugin.__init__(self, u'Bibles', u'1.9.4', plugin_helpers) + Plugin.__init__(self, u'Bibles', u'1.9.4', plugin_helpers, + BibleMediaItem, BiblesTab) self.weight = -9 self.icon_path = u':/plugins/plugin_bibles.png' self.icon = build_icon(self.icon_path) @@ -62,14 +63,6 @@ class BiblePlugin(Plugin): self.importBibleItem.setVisible(False) self.exportBibleItem.setVisible(False) - def getSettingsTab(self): - visible_name = self.getString(StringContent.VisibleName) - return BiblesTab(self.name, visible_name[u'title']) - - def getMediaManagerItem(self): - # Create the BibleManagerItem object. - return BibleMediaItem(self, self, self.icon) - def addImportMenuItem(self, import_menu): self.importBibleItem = QtGui.QAction(import_menu) self.importBibleItem.setObjectName(u'importBibleItem') @@ -136,9 +129,15 @@ class BiblePlugin(Plugin): u'title': translate('BiblesPlugin', 'Bibles', 'container title') } # Middle Header Bar - ## Import Action ## - self.textStrings[StringContent.Import] = { - u'title': UiStrings.Import, - u'tooltip': translate('BiblesPlugin', 'Import a Bible') + tooltips = { + u'load': u'', + u'import': translate('BiblesPlugin', 'Import a Bible'), + u'new': translate('BiblesPlugin', 'Add a new Bible'), + u'edit': translate('BiblesPlugin', 'Edit the selected Bible'), + u'delete': translate('BiblesPlugin', 'Delete the selected Bible'), + u'preview': translate('BiblesPlugin', 'Preview the selected Bible'), + u'live': translate('BiblesPlugin', 'Send the selected Bible live'), + u'service': translate('BiblesPlugin', + 'Add the selected Bible to the service') } - Plugin.setPluginTextStrings(self) + self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index f528a4b1b..463c838c9 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -33,9 +33,9 @@ import os.path from PyQt4 import QtCore, QtGui -from openlp.core.lib import Receiver, SettingsManager, translate +from openlp.core.lib import Receiver, translate from openlp.core.lib.db import delete_database -from openlp.core.lib.ui import UiStrings, critical_error_message_box +from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard from openlp.core.utils import AppLocation, string_is_unicode from openlp.plugins.bibles.lib.manager import BibleFormat @@ -727,33 +727,6 @@ class BibleImportForm(OpenLPWizard): if books_file: books_file.close() - def getFileName(self, title, editbox, filters=u''): - """ - Opens a QFileDialog and saves the filename to the given editbox. - - ``title`` - The title of the dialog (unicode). - - ``editbox`` - A editbox (QLineEdit). - - ``filters`` - The file extension filters. It should contain the file description - as well as the file extension. For example:: - - u'openlp.org 1.x bible (*.bible)' - """ - if filters: - filters += u';;' - filters += u'%s (*)' % UiStrings.AllFiles - filename = QtGui.QFileDialog.getOpenFileName(self, title, - os.path.dirname(SettingsManager.get_last_dir( - self.plugin.settingsSection, 1)), filters) - if filename: - editbox.setText(filename) - SettingsManager.set_last_dir( - self.plugin.settingsSection, filename, 1) - def preWizard(self): """ Prepare the UI for the import. diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index c162447b2..a6ceba6ac 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -28,22 +28,22 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import MediaManagerItem, Receiver, BaseListWithDnD, \ - ItemCapabilities, translate -from openlp.core.lib.ui import add_widget_completer, media_item_combo_box, \ - critical_error_message_box +from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ + translate +from openlp.core.lib.searchedit import SearchEdit +from openlp.core.lib.ui import UiStrings, add_widget_completer, \ + media_item_combo_box, critical_error_message_box from openlp.plugins.bibles.forms import BibleImportForm from openlp.plugins.bibles.lib import get_reference_match log = logging.getLogger(__name__) -class BibleListView(BaseListWithDnD): +class BibleSearch(object): """ - Custom list view descendant, required for drag and drop. + Enumeration class for the different search methods for the "quick search". """ - def __init__(self, parent=None): - self.PluginName = u'Bibles' - BaseListWithDnD.__init__(self, parent) + Reference = 1 + Text = 2 class BibleMediaItem(MediaManagerItem): @@ -54,7 +54,6 @@ class BibleMediaItem(MediaManagerItem): def __init__(self, parent, plugin, icon): self.IconPath = u'songs/song' - self.ListViewWithDnD_class = BibleListView MediaManagerItem.__init__(self, parent, plugin, icon) # Place to store the search results for both bibles. self.search_results = {} @@ -93,18 +92,17 @@ class BibleMediaItem(MediaManagerItem): u'quickSecondComboBox') self.quickSecondLabel.setBuddy(self.quickSecondComboBox) self.quickLayout.addRow(self.quickSecondLabel, self.quickSecondComboBox) - self.quickSearchTypeLabel = QtGui.QLabel(self.quickTab) - self.quickSearchTypeLabel.setObjectName(u'quickSearchTypeLabel') - self.quickSearchComboBox = media_item_combo_box(self.quickTab, - u'quickSearchComboBox') - self.quickSearchTypeLabel.setBuddy(self.quickSearchComboBox) - self.quickLayout.addRow(self.quickSearchTypeLabel, - self.quickSearchComboBox) self.quickSearchLabel = QtGui.QLabel(self.quickTab) self.quickSearchLabel.setObjectName(u'quickSearchLabel') - self.quickSearchEdit = QtGui.QLineEdit(self.quickTab) + self.quickSearchEdit = SearchEdit(self.quickTab) self.quickSearchEdit.setObjectName(u'quickSearchEdit') self.quickSearchLabel.setBuddy(self.quickSearchEdit) + self.quickSearchEdit.setSearchTypes([ + (BibleSearch.Reference, u':/bibles/bibles_search_reference.png', + translate('BiblesPlugin.MediaItem', 'Scripture Reference')), + (BibleSearch.Text, u':/bibles/bibles_search_text.png', + translate('BiblesPlugin.MediaItem', 'Text Search')) + ]) self.quickLayout.addRow(self.quickSearchLabel, self.quickSearchEdit) self.quickClearLabel = QtGui.QLabel(self.quickTab) self.quickClearLabel.setObjectName(u'quickClearLabel') @@ -192,8 +190,7 @@ class BibleMediaItem(MediaManagerItem): self.advancedSearchButtonLayout.addWidget(self.advancedSearchButton) self.advancedLayout.addLayout( self.advancedSearchButtonLayout, 7, 0, 1, 3) - self.searchTabWidget.addTab(self.advancedTab, - translate('BiblesPlugin.MediaItem', 'Advanced')) + self.searchTabWidget.addTab(self.advancedTab, UiStrings.Advanced) # Add the search tab widget to the page layout. self.pageLayout.addWidget(self.searchTabWidget) # Combo Boxes @@ -207,8 +204,8 @@ class BibleMediaItem(MediaManagerItem): QtCore.SIGNAL(u'activated(int)'), self.onAdvancedFromVerse) QtCore.QObject.connect(self.advancedToChapter, QtCore.SIGNAL(u'activated(int)'), self.onAdvancedToChapter) - QtCore.QObject.connect(self.quickSearchComboBox, - QtCore.SIGNAL(u'activated(int)'), self.updateAutoCompleter) + QtCore.QObject.connect(self.quickSearchEdit, + QtCore.SIGNAL(u'searchTypeChanged(int)'), self.updateAutoCompleter) QtCore.QObject.connect(self.quickVersionComboBox, QtCore.SIGNAL(u'activated(int)'), self.updateAutoCompleter) # Buttons @@ -242,8 +239,6 @@ class BibleMediaItem(MediaManagerItem): translate('BiblesPlugin.MediaItem', 'Version:')) self.quickSecondLabel.setText( translate('BiblesPlugin.MediaItem', 'Second:')) - self.quickSearchTypeLabel.setText( - translate('BiblesPlugin.MediaItem', 'Search type:')) self.quickSearchLabel.setText( translate('BiblesPlugin.MediaItem', 'Find:')) self.quickSearchButton.setText( @@ -268,10 +263,6 @@ class BibleMediaItem(MediaManagerItem): translate('BiblesPlugin.MediaItem', 'Results:')) self.advancedSearchButton.setText( translate('BiblesPlugin.MediaItem', 'Search')) - self.quickSearchComboBox.addItem( - translate('BiblesPlugin.MediaItem', 'Verse Search')) - self.quickSearchComboBox.addItem( - translate('BiblesPlugin.MediaItem', 'Text Search')) self.quickClearComboBox.addItem( translate('BiblesPlugin.MediaItem', 'Clear')) self.quickClearComboBox.addItem( @@ -369,11 +360,11 @@ class BibleMediaItem(MediaManagerItem): """ This updates the bible book completion list for the search field. The completion depends on the bible. It is only updated when we are doing a - verse search, otherwise the auto completion list is removed. + reference search, otherwise the auto completion list is removed. """ books = [] - # We have to do a 'Verse Search'. - if self.quickSearchComboBox.currentIndex() == 0: + # We have to do a 'Reference Search'. + if self.quickSearchEdit.currentSearchType() == BibleSearch.Reference: bibles = self.parent.manager.get_bibles() bible = unicode(self.quickVersionComboBox.currentText()) if bible: @@ -502,7 +493,7 @@ class BibleMediaItem(MediaManagerItem): def onQuickSearchButton(self): """ Does a quick search and saves the search results. Quick search can - either be "Verse Search" or "Text Search". + either be "Reference Search" or "Text Search". """ log.debug(u'Quick Search Button pressed') self.quickSearchButton.setEnabled(False) @@ -510,8 +501,8 @@ class BibleMediaItem(MediaManagerItem): bible = unicode(self.quickVersionComboBox.currentText()) second_bible = unicode(self.quickSecondComboBox.currentText()) text = unicode(self.quickSearchEdit.text()) - if self.quickSearchComboBox.currentIndex() == 0: - # We are doing a 'Verse Search'. + if self.quickSearchEdit.currentSearchType() == BibleSearch.Reference: + # We are doing a 'Reference Search'. self.search_results = self.parent.manager.get_verses(bible, text) if second_bible and self.search_results: self.second_search_results = self.parent.manager.get_verses( diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index f20fbc9cd..5a32d4657 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -48,21 +48,14 @@ class CustomPlugin(Plugin): log.info(u'Custom Plugin loaded') def __init__(self, plugin_helpers): - Plugin.__init__(self, u'Custom', u'1.9.4', plugin_helpers) + Plugin.__init__(self, u'Custom', u'1.9.4', plugin_helpers, + CustomMediaItem, CustomTab) self.weight = -5 self.manager = Manager(u'custom', init_schema) self.edit_custom_form = EditCustomForm(self.manager) self.icon_path = u':/plugins/plugin_custom.png' self.icon = build_icon(self.icon_path) - def getSettingsTab(self): - visible_name = self.getString(StringContent.VisibleName) - return CustomTab(self.name, visible_name[u'title']) - - def getMediaManagerItem(self): - # Create the ManagerItem object - return CustomMediaItem(self, self, self.icon) - def about(self): about_text = translate('CustomPlugin', 'Custom Plugin' '
The custom plugin provides the ability to set up custom ' @@ -113,13 +106,20 @@ class CustomPlugin(Plugin): u'title': translate('CustomsPlugin', 'Custom', 'container title') } # Middle Header Bar - ## Import Action ## - self.textStrings[StringContent.Import] = { - u'title': UiStrings.Import, - u'tooltip': translate('CustomsPlugin', - 'Import a Custom') + tooltips = { + u'load': translate('CustomsPlugin', 'Load a new Custom'), + u'import': translate('CustomsPlugin', 'Import a Custom'), + u'new': translate('CustomsPlugin', 'Add a new Custom'), + u'edit': translate('CustomsPlugin', 'Edit the selected Custom'), + u'delete': translate('CustomsPlugin', 'Delete the selected Custom'), + u'preview': translate('CustomsPlugin', + 'Preview the selected Custom'), + u'live': translate('CustomsPlugin', + 'Send the selected Custom live'), + u'service': translate('CustomsPlugin', + 'Add the selected Custom to the service') } - Plugin.setPluginTextStrings(self) + self.setPluginUiTextStrings(tooltips) def finalise(self): """ diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index 1ca29732b..2e8a64a9d 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate -from openlp.core.lib.ui import UiStrings, create_save_cancel_button_box, \ +from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box, \ create_delete_push_button, create_up_down_push_button_set class Ui_CustomEditDialog(object): @@ -94,7 +94,7 @@ class Ui_CustomEditDialog(object): self.creditLabel.setBuddy(self.creditEdit) self.bottomFormLayout.addRow(self.creditLabel, self.creditEdit) self.dialogLayout.addLayout(self.bottomFormLayout) - self.buttonBox = create_save_cancel_button_box(customEditDialog) + self.buttonBox = create_accept_reject_button_box(customEditDialog) self.previewButton = QtGui.QPushButton() self.buttonBox.addButton( self.previewButton, QtGui.QDialogButtonBox.ActionRole) diff --git a/openlp/plugins/custom/forms/editcustomslidedialog.py b/openlp/plugins/custom/forms/editcustomslidedialog.py index 24c40d6e6..b70e2bf5e 100644 --- a/openlp/plugins/custom/forms/editcustomslidedialog.py +++ b/openlp/plugins/custom/forms/editcustomslidedialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate, SpellTextEdit -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_CustomSlideEditDialog(object): def setupUi(self, customSlideEditDialog): @@ -37,7 +37,7 @@ class Ui_CustomSlideEditDialog(object): self.slideTextEdit = SpellTextEdit(self) self.slideTextEdit.setObjectName(u'slideTextEdit') self.dialogLayout.addWidget(self.slideTextEdit) - self.buttonBox = create_save_cancel_button_box(customSlideEditDialog) + self.buttonBox = create_accept_reject_button_box(customSlideEditDialog) self.splitButton = QtGui.QPushButton(customSlideEditDialog) self.splitButton.setObjectName(u'splitButton') self.buttonBox.addButton(self.splitButton, diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 908c4e18d..c1da5bdfa 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -28,18 +28,13 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import MediaManagerItem, BaseListWithDnD, \ - Receiver, ItemCapabilities, translate, check_item_selected +from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ + translate, check_item_selected from openlp.plugins.custom.lib import CustomXMLParser from openlp.plugins.custom.lib.db import CustomSlide log = logging.getLogger(__name__) -class CustomListView(BaseListWithDnD): - def __init__(self, parent=None): - self.PluginName = u'Custom' - BaseListWithDnD.__init__(self, parent) - class CustomMediaItem(MediaManagerItem): """ This is the custom media manager item for Custom Slides. @@ -48,9 +43,6 @@ class CustomMediaItem(MediaManagerItem): def __init__(self, parent, plugin, icon): self.IconPath = u'custom/custom' - # this next is a class, not an instance of a class - it will - # be instanced by the base MediaManagerItem - self.ListViewWithDnD_class = CustomListView MediaManagerItem.__init__(self, parent, self, icon) self.singleServiceItem = False # Holds information about whether the edit is remotly triggered and diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 51ef20960..2cc0f1d93 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -27,7 +27,6 @@ import logging from openlp.core.lib import Plugin, StringContent, build_icon, translate -from openlp.core.lib.ui import UiStrings from openlp.plugins.images.lib import ImageMediaItem log = logging.getLogger(__name__) @@ -36,15 +35,12 @@ class ImagePlugin(Plugin): log.info(u'Image Plugin loaded') def __init__(self, plugin_helpers): - Plugin.__init__(self, u'Images', u'1.9.4', plugin_helpers) + Plugin.__init__(self, u'Images', u'1.9.4', plugin_helpers, + ImageMediaItem) self.weight = -7 self.icon_path = u':/plugins/plugin_images.png' self.icon = build_icon(self.icon_path) - def getMediaManagerItem(self): - # Create the MediaManagerItem object. - return ImageMediaItem(self, self, self.icon) - def about(self): about_text = translate('ImagePlugin', 'Image Plugin' '
The image plugin provides displaying of images.
One ' @@ -73,4 +69,15 @@ class ImagePlugin(Plugin): u'title': translate('ImagePlugin', 'Images', 'container title') } # Middle Header Bar - Plugin.setPluginTextStrings(self) + tooltips = { + u'load': translate('ImagePlugin', 'Load a new Image'), + u'import': u'', + u'new': translate('ImagePlugin', 'Add a new Image'), + u'edit': translate('ImagePlugin', 'Edit the selected Image'), + u'delete': translate('ImagePlugin', 'Delete the selected Image'), + u'preview': translate('ImagePlugin', 'Preview the selected Image'), + u'live': translate('ImagePlugin', 'Send the selected Image live'), + u'service': translate('ImagePlugin', + 'Add the selected Image to the service') + } + self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 6ec4cf264..5f95a239c 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -29,21 +29,14 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ - ItemCapabilities, SettingsManager, translate, check_item_selected, \ - check_directory_exists, Receiver +from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \ + SettingsManager, translate, check_item_selected, check_directory_exists, \ + Receiver from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.utils import AppLocation, delete_file, get_images_filter log = logging.getLogger(__name__) -# We have to explicitly create separate classes for each plugin -# in order for DnD to the Service manager to work correctly. -class ImageListView(BaseListWithDnD): - def __init__(self, parent=None): - self.PluginName = u'Images' - BaseListWithDnD.__init__(self, parent) - class ImageMediaItem(MediaManagerItem): """ This is the custom media manager item for images. @@ -52,9 +45,6 @@ class ImageMediaItem(MediaManagerItem): def __init__(self, parent, plugin, icon): self.IconPath = u'images/image' - # This next is a class, not an instance of a class - it will - # be instanced by the base MediaManagerItem. - self.ListViewWithDnD_class = ImageListView MediaManagerItem.__init__(self, parent, self, icon) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged) @@ -64,15 +54,11 @@ class ImageMediaItem(MediaManagerItem): 'Select Image(s)') file_formats = get_images_filter() self.OnNewFileMasks = u'%s;;%s (*.*) (*)' % (file_formats, - unicode(UiStrings.AllFiles)) - self.replaceAction.setText( - translate('ImagePlugin.MediaItem', 'Replace Background')) - self.replaceAction.setToolTip( - translate('ImagePlugin.MediaItem', 'Replace Live Background')) - self.resetAction.setText( - translate('ImagePlugin.MediaItem', 'Reset Background')) - self.resetAction.setToolTip( - translate('ImagePlugin.MediaItem', 'Reset Live Background')) + UiStrings.AllFiles) + self.replaceAction.setText(UiStrings.ReplaceBG) + self.replaceAction.setToolTip(UiStrings.ReplaceLiveBG) + self.resetAction.setText(UiStrings.ResetBG) + self.resetAction.setToolTip(UiStrings.ResetLiveBG) def requiredIcons(self): MediaManagerItem.requiredIcons(self) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index f0549a960..cc126bbef 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -29,18 +29,13 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ - ItemCapabilities, SettingsManager, translate, check_item_selected, Receiver -from openlp.core.lib.ui import critical_error_message_box +from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \ + SettingsManager, translate, check_item_selected, Receiver +from openlp.core.lib.ui import UiStrings, critical_error_message_box +from PyQt4.phonon import Phonon log = logging.getLogger(__name__) -class MediaListView(BaseListWithDnD): - def __init__(self, parent=None): - self.PluginName = u'Media' - BaseListWithDnD.__init__(self, parent) - - class MediaMediaItem(MediaManagerItem): """ This is the custom media manager item for Media Slides. @@ -50,30 +45,27 @@ class MediaMediaItem(MediaManagerItem): def __init__(self, parent, plugin, icon): self.IconPath = u'images/image' self.background = False - # this next is a class, not an instance of a class - it will - # be instanced by the base MediaManagerItem - self.ListViewWithDnD_class = MediaListView self.PreviewFunction = QtGui.QPixmap( u':/media/media_video.png').toImage() MediaManagerItem.__init__(self, parent, self, icon) self.singleServiceItem = False + self.mediaObject = Phonon.MediaObject(self) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'video_background_replaced'), self.videobackgroundReplaced) + QtCore.QObject.connect(self.mediaObject, + QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'), + self.videoStart) def retranslateUi(self): self.OnNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media') self.OnNewFileMasks = unicode(translate('MediaPlugin.MediaItem', - 'Videos (%s);;Audio (%s);;All files (*)')) % \ - (self.parent.video_list, self.parent.audio_list) - self.replaceAction.setText( - translate('MediaPlugin.MediaItem', 'Replace Background')) - self.replaceAction.setToolTip( - translate('MediaPlugin.MediaItem', 'Replace Live Background')) - self.resetAction.setText( - translate('MediaPlugin.MediaItem', 'Reset Background')) - self.resetAction.setToolTip( - translate('ImagePlugin.MediaItem', 'Reset Live Background')) + 'Videos (%s);;Audio (%s);;%s (*)')) % (self.parent.video_list, + self.parent.audio_list, UiStrings.AllFiles) + self.replaceAction.setText(UiStrings.ReplaceBG) + self.replaceAction.setToolTip(UiStrings.ReplaceLiveBG) + self.resetAction.setText(UiStrings.ResetBG) + self.resetAction.setToolTip(UiStrings.ResetLiveBG) def requiredIcons(self): MediaManagerItem.requiredIcons(self) @@ -133,13 +125,22 @@ class MediaMediaItem(MediaManagerItem): return False filename = unicode(item.data(QtCore.Qt.UserRole).toString()) if os.path.exists(filename): + self.MediaState = None + self.mediaObject.stop() + self.mediaObject.clearQueue() + self.mediaObject.setCurrentSource(Phonon.MediaSource(filename)) + self.mediaObject.play() service_item.title = unicode( translate('MediaPlugin.MediaItem', 'Media')) service_item.add_capability(ItemCapabilities.RequiresMedia) + service_item.add_capability(ItemCapabilities.AllowsVarableStartTime) # force a nonexistent theme service_item.theme = -1 frame = u':/media/image_clapperboard.png' (path, name) = os.path.split(filename) + while not self.MediaState: + Receiver.send_message(u'openlp_process_events') + service_item.media_length = self.mediaLength service_item.add_from_command(path, name, frame) return True else: @@ -151,6 +152,7 @@ class MediaMediaItem(MediaManagerItem): return False def initialise(self): + self.listView.clear() self.listView.setIconSize(QtCore.QSize(88, 50)) self.loadList(SettingsManager.load_list(self.settingsSection, self.settingsSection)) @@ -176,3 +178,12 @@ class MediaMediaItem(MediaManagerItem): item_name.setIcon(build_icon(img)) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) self.listView.addItem(item_name) + + def videoStart(self, newState, oldState): + """ + Start the video at a predetermined point. + """ + if newState == Phonon.PlayingState: + self.MediaState = newState + self.mediaLength = self.mediaObject.totalTime()/1000 + self.mediaObject.stop() diff --git a/openlp/plugins/media/lib/mediatab.py b/openlp/plugins/media/lib/mediatab.py index 461fbf4ae..c51b53a9a 100644 --- a/openlp/plugins/media/lib/mediatab.py +++ b/openlp/plugins/media/lib/mediatab.py @@ -32,8 +32,8 @@ class MediaTab(SettingsTab): """ MediaTab is the Media settings tab in the settings dialog. """ - def __init__(self, title): - SettingsTab.__init__(self, title) + def __init__(self, title, visible_title): + SettingsTab.__init__(self, title, visible_title) def setupUi(self): self.setObjectName(u'MediaTab') @@ -53,9 +53,8 @@ class MediaTab(SettingsTab): self.onUsePhononCheckBoxChanged) def retranslateUi(self): - self.tabTitleVisible = translate('MediaPlugin.MediaTab', 'Media') - self.mediaModeGroupBox.setTitle(translate('MediaPlugin.MediaTab', - 'Media Display')) + self.mediaModeGroupBox.setTitle( + translate('MediaPlugin.MediaTab', 'Media Display')) self.usePhononCheckBox.setText( translate('MediaPlugin.MediaTab', 'Use Phonon for video playback')) diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 1619a07fe..ee413aa8c 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -30,7 +30,6 @@ import mimetypes from PyQt4.phonon import Phonon from openlp.core.lib import Plugin, StringContent, build_icon, translate -from openlp.core.lib.ui import UiStrings from openlp.plugins.media.lib import MediaMediaItem, MediaTab log = logging.getLogger(__name__) @@ -39,7 +38,8 @@ class MediaPlugin(Plugin): log.info(u'%s MediaPlugin loaded', __name__) def __init__(self, plugin_helpers): - Plugin.__init__(self, u'Media', u'1.9.4', plugin_helpers) + Plugin.__init__(self, u'Media', u'1.9.4', plugin_helpers, + MediaMediaItem, MediaTab) self.weight = -6 self.icon_path = u':/plugins/plugin_media.png' self.icon = build_icon(self.icon_path) @@ -76,13 +76,6 @@ class MediaPlugin(Plugin): mimetype = u'' return list, mimetype - def getSettingsTab(self): - return MediaTab(self.name) - - def getMediaManagerItem(self): - # Create the MediaManagerItem object. - return MediaMediaItem(self, self, self.icon) - def about(self): about_text = translate('MediaPlugin', 'Media Plugin' '
The media plugin provides playback of audio and video.') @@ -102,4 +95,15 @@ class MediaPlugin(Plugin): u'title': translate('MediaPlugin', 'Media', 'container title') } # Middle Header Bar - Plugin.setPluginTextStrings(self) + tooltips = { + u'load': translate('MediaPlugin', 'Load a new Media'), + u'import': u'', + u'new': translate('MediaPlugin', 'Add a new Media'), + u'edit': translate('MediaPlugin', 'Edit the selected Media'), + u'delete': translate('MediaPlugin', 'Delete the selected Media'), + u'preview': translate('MediaPlugin', 'Preview the selected Media'), + u'live': translate('MediaPlugin', 'Send the selected Media live'), + u'service': translate('MediaPlugin', + 'Add the selected Media to the service') + } + self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 7de095d54..ad5faa2dd 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -145,7 +145,12 @@ class ImpressController(PresentationController): log.debug(u'get COM Desktop OpenOffice') if not self.manager: return None - return self.manager.createInstance(u'com.sun.star.frame.Desktop') + desktop = None + try: + desktop = self.manager.createInstance(u'com.sun.star.frame.Desktop') + except AttributeError: + log.exception(u'Failure to find desktop - Impress may have closed') + return desktop if desktop else None def get_com_servicemanager(self): """ @@ -166,13 +171,15 @@ class ImpressController(PresentationController): log.debug(u'Kill OpenOffice') while self.docs: self.docs[0].close_presentation() - if os.name != u'nt': - desktop = self.get_uno_desktop() - else: - desktop = self.get_com_desktop() - #Sometimes we get a failure and desktop is None - if not desktop: + desktop = None + try: + if os.name != u'nt': + desktop = self.get_uno_desktop() + else: + desktop = self.get_com_desktop() + except: log.exception(u'Failed to find an OpenOffice desktop to terminate') + if not desktop: return docs = desktop.getComponents() if docs.hasElements(): diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 09020692a..0b6ab39cd 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -29,24 +29,13 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ - SettingsManager, translate, check_item_selected, Receiver, ItemCapabilities +from openlp.core.lib import MediaManagerItem, build_icon, SettingsManager, \ + translate, check_item_selected, Receiver, ItemCapabilities from openlp.core.lib.ui import critical_error_message_box, media_item_combo_box from openlp.plugins.presentations.lib import MessageListener log = logging.getLogger(__name__) -class PresentationListView(BaseListWithDnD): - """ - Class for the list of Presentations - - We have to explicitly create separate classes for each plugin - in order for DnD to the Service manager to work correctly. - """ - def __init__(self, parent=None): - self.PluginName = u'Presentations' - BaseListWithDnD.__init__(self, parent) - class PresentationMediaItem(MediaManagerItem): """ This is the Presentation media manager item for Presentation Items. @@ -61,9 +50,6 @@ class PresentationMediaItem(MediaManagerItem): self.controllers = controllers self.IconPath = u'presentations/presentation' self.Automatic = u'' - # this next is a class, not an instance of a class - it will - # be instanced by the base MediaManagerItem - self.ListViewWithDnD_class = PresentationListView MediaManagerItem.__init__(self, parent, self, icon) self.message_listener = MessageListener(self) QtCore.QObject.connect(Receiver.get_receiver(), diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index fc82600df..fdb66c511 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -27,6 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, SettingsTab, translate +from openlp.core.lib.ui import UiStrings class PresentationTab(SettingsTab): """ @@ -85,9 +86,7 @@ class PresentationTab(SettingsTab): checkbox.setText( unicode(translate('PresentationPlugin.PresentationTab', '%s (unvailable)')) % controller.name) - self.AdvancedGroupBox.setTitle( - translate('PresentationPlugin.PresentationTab', - 'Advanced')) + self.AdvancedGroupBox.setTitle(UiStrings.Advanced) self.OverrideAppCheckBox.setText( translate('PresentationPlugin.PresentationTab', 'Allow presentation application to be overriden')) diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index c4cf29aca..ece25e363 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -31,7 +31,6 @@ import os import logging from openlp.core.lib import Plugin, StringContent, build_icon, translate -from openlp.core.lib.ui import UiStrings from openlp.core.utils import AppLocation from openlp.plugins.presentations.lib import PresentationController, \ PresentationMediaItem, PresentationTab @@ -168,4 +167,18 @@ class PresentationPlugin(Plugin): 'container title') } # Middle Header Bar - Plugin.setPluginTextStrings(self) + tooltips = { + u'load': translate('PresentationPlugin', 'Load a new Presentation'), + u'import': u'', + u'new': u'', + u'edit': u'', + u'delete': translate('PresentationPlugin', + 'Delete the selected Presentation'), + u'preview': translate('PresentationPlugin', + 'Preview the selected Presentation'), + u'live': translate('PresentationPlugin', + 'Send the selected Presentation live'), + u'service': translate('PresentationPlugin', + 'Add the selected Presentation to the service') + } + self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index dbc56a61c..b513d4ff7 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -38,7 +38,8 @@ class RemotesPlugin(Plugin): """ remotes constructor """ - Plugin.__init__(self, u'Remotes', u'1.9.4', plugin_helpers) + Plugin.__init__(self, u'Remotes', u'1.9.4', plugin_helpers, + settingsTabClass=RemoteTab) self.icon = build_icon(u':/plugins/plugin_remote.png') self.weight = -1 self.server = None @@ -61,13 +62,6 @@ class RemotesPlugin(Plugin): if self.server: self.server.close() - def getSettingsTab(self): - """ - Create the settings Tab - """ - visible_name = self.getString(StringContent.VisibleName) - return RemoteTab(self.name, visible_name[u'title']) - def about(self): """ Information about this plugin diff --git a/openlp/plugins/songs/forms/__init__.py b/openlp/plugins/songs/forms/__init__.py index 160a014ac..e75f9fe04 100644 --- a/openlp/plugins/songs/forms/__init__.py +++ b/openlp/plugins/songs/forms/__init__.py @@ -57,4 +57,6 @@ from songbookform import SongBookForm from editverseform import EditVerseForm from editsongform import EditSongForm from songmaintenanceform import SongMaintenanceForm -from songimportform import SongImportForm \ No newline at end of file +from songimportform import SongImportForm +from songexportform import SongExportForm + diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py index 0b3f791d1..09c723364 100644 --- a/openlp/plugins/songs/forms/authorsdialog.py +++ b/openlp/plugins/songs/forms/authorsdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_AuthorsDialog(object): def setupUi(self, authorsDialog): @@ -57,7 +57,7 @@ class Ui_AuthorsDialog(object): self.authorLayout.addRow(self.displayLabel, self.displayEdit) self.dialogLayout.addLayout(self.authorLayout) self.dialogLayout.addWidget( - create_save_cancel_button_box(authorsDialog)) + create_accept_reject_button_box(authorsDialog)) self.retranslateUi(authorsDialog) authorsDialog.setMaximumHeight(authorsDialog.sizeHint().height()) QtCore.QMetaObject.connectSlotsByName(authorsDialog) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 6854a17ae..234d92283 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate -from openlp.core.lib.ui import UiStrings, create_save_cancel_button_box +from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box class Ui_EditSongDialog(object): def setupUi(self, editSongDialog): @@ -241,7 +241,7 @@ class Ui_EditSongDialog(object): self.themeTabLayout.addWidget(self.commentsGroupBox) self.songTabWidget.addTab(self.themeTab, u'') self.dialogLayout.addWidget(self.songTabWidget) - self.buttonBox = create_save_cancel_button_box(editSongDialog) + self.buttonBox = create_accept_reject_button_box(editSongDialog) self.dialogLayout.addWidget(self.buttonBox) self.retranslateUi(editSongDialog) QtCore.QMetaObject.connectSlotsByName(editSongDialog) diff --git a/openlp/plugins/songs/forms/editversedialog.py b/openlp/plugins/songs/forms/editversedialog.py index b4bc4551f..7caf782e6 100644 --- a/openlp/plugins/songs/forms/editversedialog.py +++ b/openlp/plugins/songs/forms/editversedialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate, SpellTextEdit -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box from openlp.plugins.songs.lib import VerseType class Ui_EditVerseDialog(object): @@ -61,7 +61,7 @@ class Ui_EditVerseDialog(object): self.verseTypeLayout.addStretch() self.dialogLayout.addLayout(self.verseTypeLayout) self.dialogLayout.addWidget( - create_save_cancel_button_box(editVerseDialog)) + create_accept_reject_button_box(editVerseDialog)) self.retranslateUi(editVerseDialog) QtCore.QMetaObject.connectSlotsByName(editVerseDialog) diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py index 89b8941b5..f6dd3930c 100644 --- a/openlp/plugins/songs/forms/songbookdialog.py +++ b/openlp/plugins/songs/forms/songbookdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_SongBookDialog(object): def setupUi(self, songBookDialog): @@ -51,7 +51,7 @@ class Ui_SongBookDialog(object): self.bookLayout.addRow(self.publisherLabel, self.publisherEdit) self.dialogLayout.addLayout(self.bookLayout) self.dialogLayout.addWidget( - create_save_cancel_button_box(songBookDialog)) + create_accept_reject_button_box(songBookDialog)) self.retranslateUi(songBookDialog) songBookDialog.setMaximumHeight(songBookDialog.sizeHint().height()) QtCore.QMetaObject.connectSlotsByName(songBookDialog) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py new file mode 100644 index 000000000..849a1ad1e --- /dev/null +++ b/openlp/plugins/songs/forms/songexportform.py @@ -0,0 +1,367 @@ +# -*- 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, 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 # +############################################################################### +""" +The :mod:`songexportform` module provides the wizard for exporting songs to the +OpenLyrics format. +""" +import logging + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import build_icon, Receiver, SettingsManager, translate +from openlp.core.lib.ui import critical_error_message_box +from openlp.core.ui.wizard import OpenLPWizard +from openlp.plugins.songs.lib.db import Song +from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport + +log = logging.getLogger(__name__) + +class SongExportForm(OpenLPWizard): + """ + This is the Song Export Wizard, which allows easy exporting of Songs to the + OpenLyrics format. + """ + log.info(u'SongExportForm loaded') + + def __init__(self, parent, plugin): + """ + Instantiate the wizard, and run any extra setup we need to. + + ``parent`` + The QWidget-derived parent of the wizard. + + ``plugin`` + The songs plugin. + """ + self.plugin = plugin + OpenLPWizard.__init__(self, parent, plugin, u'songExportWizard', + u':/wizards/wizard_exportsong.bmp') + self.stop_export_flag = False + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'openlp_stop_wizard'), self.stop_export) + + def stop_export(self): + """ + Sets the flag for the exporter to stop the export. + """ + log.debug(u'Stopping songs export') + self.stop_export_flag = True + + def setupUi(self, image): + """ + Set up the song wizard UI. + """ + OpenLPWizard.setupUi(self, image) + + def customInit(self): + """ + Song wizard specific initialisation. + """ + pass + + def customSignals(self): + """ + Song wizard specific signals. + """ + QtCore.QObject.connect(self.availableListWidget, + QtCore.SIGNAL(u'itemActivated(QListWidgetItem*)'), + self.onItemPressed) + QtCore.QObject.connect(self.searchLineEdit, + QtCore.SIGNAL(u'textEdited(const QString&)'), + self.onSearchLineEditChanged) + QtCore.QObject.connect(self.uncheckButton, + QtCore.SIGNAL(u'clicked()'), self.onUncheckButtonClicked) + QtCore.QObject.connect(self.checkButton, + QtCore.SIGNAL(u'clicked()'), self.onCheckButtonClicked) + QtCore.QObject.connect(self.directoryButton, + QtCore.SIGNAL(u'clicked()'), self.onDirectoryButtonClicked) + + def addCustomPages(self): + """ + Add song wizard specific pages. + """ + # The page with all available songs. + self.availableSongsPage = QtGui.QWizardPage() + self.availableSongsPage.setObjectName(u'availableSongsPage') + self.availableSongsLayout = QtGui.QHBoxLayout(self.availableSongsPage) + self.availableSongsLayout.setObjectName(u'availableSongsLayout') + self.verticalLayout = QtGui.QVBoxLayout() + self.verticalLayout.setObjectName(u'verticalLayout') + self.availableListWidget = QtGui.QListWidget(self.availableSongsPage) + self.availableListWidget.setObjectName(u'availableListWidget') + self.verticalLayout.addWidget(self.availableListWidget) + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName(u'horizontalLayout') + self.searchLabel = QtGui.QLabel(self.availableSongsPage) + self.searchLabel.setObjectName(u'searchLabel') + self.horizontalLayout.addWidget(self.searchLabel) + self.searchLineEdit = QtGui.QLineEdit(self.availableSongsPage) + self.searchLineEdit.setObjectName(u'searchLineEdit') + self.horizontalLayout.addWidget(self.searchLineEdit) + spacerItem = QtGui.QSpacerItem(40, 20, + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.uncheckButton = QtGui.QPushButton(self.availableSongsPage) + self.uncheckButton.setObjectName(u'uncheckButton') + self.horizontalLayout.addWidget(self.uncheckButton) + self.checkButton = QtGui.QPushButton(self.availableSongsPage) + self.checkButton.setObjectName(u'selectButton') + self.horizontalLayout.addWidget(self.checkButton) + self.verticalLayout.addLayout(self.horizontalLayout) + self.availableSongsLayout.addLayout(self.verticalLayout) + self.addPage(self.availableSongsPage) + # The page with the selected songs. + self.exportSongPage = QtGui.QWizardPage() + self.exportSongPage.setObjectName(u'availableSongsPage') + self.exportSongLayout = QtGui.QHBoxLayout(self.exportSongPage) + self.exportSongLayout.setObjectName(u'exportSongLayout') + self.gridLayout = QtGui.QGridLayout() + self.gridLayout.setObjectName(u'gridLayout') + self.selectedListWidget = QtGui.QListWidget(self.exportSongPage) + self.selectedListWidget.setObjectName(u'selectedListWidget') + self.gridLayout.addWidget(self.selectedListWidget, 1, 0, 1, 1) + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName(u'horizontalLayout') + self.directoryLabel = QtGui.QLabel(self.exportSongPage) + self.directoryLabel.setObjectName(u'directoryLabel') + self.horizontalLayout.addWidget(self.directoryLabel) + self.directoryLineEdit = QtGui.QLineEdit(self.exportSongPage) + self.directoryLineEdit.setObjectName(u'directoryLineEdit') + self.horizontalLayout.addWidget(self.directoryLineEdit) + self.directoryButton = QtGui.QToolButton(self.exportSongPage) + self.directoryButton.setIcon(build_icon(u':/exports/export_load.png')) + self.directoryButton.setObjectName(u'directoryButton') + self.horizontalLayout.addWidget(self.directoryButton) + self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) + self.exportSongLayout.addLayout(self.gridLayout) + self.addPage(self.exportSongPage) + + def retranslateUi(self): + """ + Song wizard localisation. + """ + self.setWindowTitle( + translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard')) + self.titleLabel.setText( + u'%s' % + translate('SongsPlugin.ExportWizardForm', + 'Welcome to the Song Export Wizard')) + self.informationLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'This wizard will help to' + ' export your songs to the open and free OpenLyrics worship song ' + 'format.')) + self.availableSongsPage.setTitle( + translate('SongsPlugin.ExportWizardForm', 'Select Songs')) + self.availableSongsPage.setSubTitle( + translate('SongsPlugin.ExportWizardForm', + 'Check the songs, you want to export.')) + self.searchLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'Search:')) + self.uncheckButton.setText( + translate('SongsPlugin.ExportWizardForm', 'Uncheck All')) + self.checkButton.setText( + translate('SongsPlugin.ExportWizardForm', 'Check All')) + self.exportSongPage.setTitle( + translate('SongsPlugin.ExportWizardForm', 'Select Directory')) + self.exportSongPage.setSubTitle( + translate('SongsPlugin.ExportWizardForm', + 'Select the directory you want the songs to be saved.')) + self.directoryLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'Directory:')) + self.progressPage.setTitle( + translate('SongsPlugin.ExportWizardForm', 'Exporting')) + self.progressPage.setSubTitle( + translate('SongsPlugin.ExportWizardForm', + 'Please wait while your songs are exported.')) + self.progressLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'Ready.')) + self.progressBar.setFormat( + translate('SongsPlugin.ExportWizardForm', '%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.availableSongsPage: + items = [ + item for item in self._findListWidgetItems( + self.availableListWidget) if item.checkState() + ] + if not items: + critical_error_message_box( + translate('SongsPlugin.ExportWizardForm', + 'No Song Selected'), + translate('SongsPlugin.ExportWizardForm', + 'You need to add at least one Song to export.')) + return False + self.selectedListWidget.clear() + # Add the songs to the list of selected songs. + for item in items: + song = QtGui.QListWidgetItem(item.text()) + song.setData(QtCore.Qt.UserRole, + QtCore.QVariant(item.data(QtCore.Qt.UserRole).toPyObject())) + song.setFlags(QtCore.Qt.ItemIsEnabled) + self.selectedListWidget.addItem(song) + return True + elif self.currentPage() == self.exportSongPage: + if not self.directoryLineEdit.text(): + critical_error_message_box( + translate('SongsPlugin.ExportWizardForm', + 'No Save Location specified'), + translate('SongsPlugin.ExportWizardForm', + 'You need to specified a directory to save the songs in.')) + return False + return True + elif self.currentPage() == self.progressPage: + self.availableListWidget.clear() + self.selectedListWidget.clear() + return True + + def setDefaults(self): + """ + Set default form values for the song export wizard. + """ + self.restart() + self.finishButton.setVisible(False) + self.cancelButton.setVisible(True) + self.availableListWidget.clear() + self.selectedListWidget.clear() + self.directoryLineEdit.clear() + # Load the list of songs. + Receiver.send_message(u'cursor_busy') + songs = self.plugin.manager.get_all_objects(Song) + for song in songs: + authors = u', '.join([author.display_name + for author in song.authors]) + title = u'%s (%s)' % (unicode(song.title), authors) + item = QtGui.QListWidgetItem(title) + item.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) + item.setFlags(QtCore.Qt.ItemIsSelectable| + QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled) + item.setCheckState(QtCore.Qt.Unchecked) + self.availableListWidget.addItem(item) + Receiver.send_message(u'cursor_normal') + + def preWizard(self): + """ + Perform pre export tasks. + """ + OpenLPWizard.preWizard(self) + self.progressLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'Starting export...')) + Receiver.send_message(u'openlp_process_events') + + def performWizard(self): + """ + Perform the actual export. This creates an *openlyricsexport* instance + and calls the *do_export* method. + """ + songs = [ + song.data(QtCore.Qt.UserRole).toPyObject() + for song in self._findListWidgetItems(self.selectedListWidget) + ] + exporter = OpenLyricsExport( + self, songs, unicode(self.directoryLineEdit.text())) + if exporter.do_export(): + self.progressLabel.setText( + translate('SongsPlugin.SongExportForm', 'Finished export.')) + else: + self.progressLabel.setText( + translate('SongsPlugin.SongExportForm', + 'Your song export failed.')) + + def _findListWidgetItems(self, listWidget, text=u''): + """ + Returns a list of *QListWidgetItem*s of the ``listWidget``. Note, that + hidden items are included. + + ``listWidget`` + The widget to get all items from. (QListWidget) + + ``text`` + The text to search for. (unicode string) + """ + return [item for item in listWidget.findItems( + QtCore.QString(unicode(text)), QtCore.Qt.MatchContains) + ] + + def onItemPressed(self, item): + """ + Called, when an item in the *availableListWidget* has been pressed. Thes + item is check if it was not checked, whereas it is unchecked when it was + checked. + + ``item`` + The *QListWidgetItem* which was pressed. + """ + item.setCheckState( + QtCore.Qt.Unchecked if item.checkState() else QtCore.Qt.Checked) + + def onSearchLineEditChanged(self, text): + """ + The *searchLineEdit*'s text has been changed. Update the list of + available songs. Note that any song, which does not match the ``text`` + will be hidden, but not unchecked! + + ``text`` + The text of the *searchLineEdit*. (QString) + """ + search_result = [ + song for song in self._findListWidgetItems( + self.availableListWidget, unicode(text)) + ] + for item in self._findListWidgetItems(self.availableListWidget): + item.setHidden(False if item in search_result else True) + + def onUncheckButtonClicked(self): + """ + The *uncheckButton* has been clicked. Set all songs unchecked. + """ + for row in range(self.availableListWidget.count()): + item = self.availableListWidget.item(row) + item.setCheckState(QtCore.Qt.Unchecked) + + def onCheckButtonClicked(self): + """ + The *checkButton* has been clicked. Set all songs checked. + """ + for row in range(self.availableListWidget.count()): + item = self.availableListWidget.item(row) + item.setCheckState(QtCore.Qt.Checked) + + def onDirectoryButtonClicked(self): + """ + Called when the *directoryButton* was clicked. Opens a dialog and writes + the path to *directoryLineEdit*. + """ + path = unicode(QtGui.QFileDialog.getExistingDirectory(self, + translate('SongsPlugin.ExportWizardForm', 'Selecte to Folder'), + SettingsManager.get_last_dir(self.plugin.settingsSection, 1), + options=QtGui.QFileDialog.ShowDirsOnly)) + SettingsManager.set_last_dir(self.plugin.settingsSection, path, 1) + self.directoryLineEdit.setText(path) diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 8cd8c70fa..6e2a45b45 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -140,6 +140,12 @@ class SongImportForm(OpenLPWizard): QtCore.QObject.connect(self.songBeamerRemoveButton, QtCore.SIGNAL(u'clicked()'), self.onSongBeamerRemoveButtonClicked) + QtCore.QObject.connect(self.songShowPlusAddButton, + QtCore.SIGNAL(u'clicked()'), + self.onSongShowPlusAddButtonClicked) + QtCore.QObject.connect(self.songShowPlusRemoveButton, + QtCore.SIGNAL(u'clicked()'), + self.onSongShowPlusRemoveButtonClicked) def addCustomPages(self): """ @@ -188,6 +194,8 @@ class SongImportForm(OpenLPWizard): self.addFileSelectItem(u'ew', single_select=True) # Words of Worship self.addFileSelectItem(u'songBeamer') + # Song Show Plus + self.addFileSelectItem(u'songShowPlus') # Commented out for future use. # self.addFileSelectItem(u'csv', u'CSV', single_select=True) self.sourceLayout.addLayout(self.formatStack) @@ -237,6 +245,8 @@ class SongImportForm(OpenLPWizard): translate('SongsPlugin.ImportWizardForm', 'EasyWorship')) self.formatComboBox.setItemText(10, translate('SongsPlugin.ImportWizardForm', 'SongBeamer')) + self.formatComboBox.setItemText(11, + translate('SongsPlugin.ImportWizardForm', 'SongShow Plus')) # self.formatComboBox.setItemText(11, # translate('SongsPlugin.ImportWizardForm', 'CSV')) self.openLP2FilenameLabel.setText( @@ -301,6 +311,10 @@ class SongImportForm(OpenLPWizard): translate('SongsPlugin.ImportWizardForm', 'Add Files...')) self.songBeamerRemoveButton.setText( translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) + self.songShowPlusAddButton.setText( + translate('SongsPlugin.ImportWizardForm', 'Add Files...')) + self.songShowPlusRemoveButton.setText( + translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) # self.csvFilenameLabel.setText( # translate('SongsPlugin.ImportWizardForm', 'Filename:')) # self.csvBrowseButton.setText( @@ -438,38 +452,20 @@ class SongImportForm(OpenLPWizard): 'file to import from.')) self.songBeamerAddButton.setFocus() return False + elif source_format == SongFormat.SongShowPlus: + if self.songShowPlusFileListWidget.count() == 0: + critical_error_message_box( + translate('SongsPlugin.ImportWizardForm', + 'No SongShow Plus Files Selected'), + translate('SongsPlugin.ImportWizardForm', + 'You need to add at least one SongShow Plus ' + 'file to import from.')) + self.wordsOfWorshipAddButton.setFocus() + return False return True elif self.currentPage() == self.progressPage: return True - def getFileName(self, title, editbox, filters=u''): - """ - Opens a QFileDialog and writes the filename to the given editbox. - - ``title`` - The title of the dialog (unicode). - - ``editbox`` - A editbox (QLineEdit). - - ``filters`` - The file extension filters. It should contain the file descriptions - as well as the file extensions. For example:: - - u'OpenLP 2.0 Databases (*.sqlite)' - """ - if filters: - filters += u';;' - filters += u'%s (*)' % translate('SongsPlugin.ImportWizardForm', - 'All Files') - filename = QtGui.QFileDialog.getOpenFileName(self, title, - SettingsManager.get_last_dir(self.plugin.settingsSection, 1), - filters) - if filename: - editbox.setText(filename) - SettingsManager.set_last_dir(self.plugin.settingsSection, - os.path.split(unicode(filename))[0], 1) - def getFiles(self, title, listbox, filters=u''): """ Opens a QFileDialog and writes the filenames to the given listbox. @@ -671,12 +667,24 @@ class SongImportForm(OpenLPWizard): Remove selected SongBeamer files from the import list """ self.removeSelectedItems(self.songBeamerFileListWidget) + + def onSongShowPlusAddButtonClicked(self): + """ + Get SongShow Plus song database files + """ + self.getFiles( + translate('SongsPlugin.ImportWizardForm', + 'Select SongShow Plus Files'), + self.songShowPlusFileListWidget, u'%s (*.sbsong)' + % translate('SongsPlugin.ImportWizardForm', + 'SongShow Plus Song Files') + ) - def registerFields(self): + def onSongShowPlusRemoveButtonClicked(self): """ - Register song import wizard fields. + Remove selected SongShow Plus files from the import list """ - pass + self.removeSelectedItems(self.songShowPlusFileListWidget) def setDefaults(self): """ @@ -697,6 +705,7 @@ class SongImportForm(OpenLPWizard): self.easiSlidesFilenameEdit.setText(u'') self.ewFilenameEdit.setText(u'') self.songBeamerFileListWidget.clear() + self.songShowPlusFileListWidget.clear() #self.csvFilenameEdit.setText(u'') def preWizard(self): @@ -773,6 +782,12 @@ class SongImportForm(OpenLPWizard): importer = self.plugin.importSongs(SongFormat.SongBeamer, filenames=self.getListOfFiles(self.songBeamerFileListWidget) ) + elif source_format == SongFormat.SongShowPlus: + # Import ShongShow Plus songs + importer = self.plugin.importSongs(SongFormat.SongShowPlus, + filenames=self.getListOfFiles( + self.songShowPlusFileListWidget) + ) if importer.do_import(): self.progressLabel.setText( translate('SongsPlugin.SongImportForm', 'Finished import.')) diff --git a/openlp/plugins/songs/forms/topicsdialog.py b/openlp/plugins/songs/forms/topicsdialog.py index 596597034..1e7bdb6a0 100644 --- a/openlp/plugins/songs/forms/topicsdialog.py +++ b/openlp/plugins/songs/forms/topicsdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_TopicsDialog(object): def setupUi(self, topicsDialog): @@ -45,7 +45,7 @@ class Ui_TopicsDialog(object): self.nameLayout.addRow(self.nameLabel, self.nameEdit) self.dialogLayout.addLayout(self.nameLayout) self.dialogLayout.addWidget( - create_save_cancel_button_box(topicsDialog)) + create_accept_reject_button_box(topicsDialog)) self.retranslateUi(topicsDialog) topicsDialog.setMaximumHeight(topicsDialog.sizeHint().height()) QtCore.QMetaObject.connectSlotsByName(topicsDialog) diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 6f566ff4f..cbe5c6922 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -34,6 +34,7 @@ from wowimport import WowImport from cclifileimport import CCLIFileImport from ewimport import EasyWorshipSongImport from songbeamerimport import SongBeamerImport +from songshowplusimport import SongShowPlusImport # Imports that might fail try: from olp1import import OpenLP1SongImport @@ -71,6 +72,7 @@ class SongFormat(object): EasiSlides = 8 EasyWorship = 9 SongBeamer = 10 + SongShowPlus = 11 @staticmethod def get_class(format): @@ -102,6 +104,8 @@ class SongFormat(object): return EasyWorshipSongImport elif format == SongFormat.SongBeamer: return SongBeamerImport + elif format == SongFormat.SongShowPlus: + return SongShowPlusImport return None @staticmethod @@ -120,7 +124,8 @@ class SongFormat(object): SongFormat.Generic, SongFormat.EasiSlides, SongFormat.EasyWorship, - SongFormat.SongBeamer + SongFormat.SongBeamer, + SongFormat.SongShowPlus ] @staticmethod diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 312d0d6e7..283aa6c03 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -31,23 +31,17 @@ import re from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_ -from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \ - ItemCapabilities, translate, check_item_selected, PluginStatus +from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ + translate, check_item_selected, PluginStatus from openlp.core.lib.ui import UiStrings from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \ - SongImportForm + SongImportForm, SongExportForm from openlp.plugins.songs.lib import OpenLyrics, SongXML from openlp.plugins.songs.lib.db import Author, Song from openlp.core.lib.searchedit import SearchEdit log = logging.getLogger(__name__) -class SongListView(BaseListWithDnD): - def __init__(self, parent=None): - self.PluginName = u'Songs' - BaseListWithDnD.__init__(self, parent) - - class SongMediaItem(MediaManagerItem): """ This is the custom media manager item for Songs. @@ -56,7 +50,6 @@ class SongMediaItem(MediaManagerItem): def __init__(self, parent, plugin, icon): self.IconPath = u'songs/song' - self.ListViewWithDnD_class = SongListView MediaManagerItem.__init__(self, parent, self, icon) self.edit_song_form = EditSongForm(self, self.parent.manager) self.openLyrics = OpenLyrics(self.parent.manager) @@ -265,6 +258,11 @@ class SongMediaItem(MediaManagerItem): if self.import_wizard.exec_() == QtGui.QDialog.Accepted: Receiver.send_message(u'songs_load_list') + def onExportClick(self): + if not hasattr(self, u'export_wizard'): + self.export_wizard = SongExportForm(self, self.parent) + self.export_wizard.exec_() + def onNewClick(self): log.debug(u'onNewClick') self.edit_song_form.newSong() diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py new file mode 100755 index 000000000..ffb1a2d6f --- /dev/null +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -0,0 +1,74 @@ +# -*- 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, 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 # +############################################################################### +""" +The :mod:`openlyricsexport` module provides the functionality for exporting +songs from the database to the OpenLyrics format. +""" +import logging +import os + +from lxml import etree + +from openlp.core.lib import Receiver, translate +from openlp.plugins.songs.lib import OpenLyrics + +log = logging.getLogger(__name__) + +class OpenLyricsExport(object): + """ + This provides the Openlyrics export. + """ + def __init__(self, parent, songs, save_path): + """ + Initialise the export. + """ + log.debug(u'initialise OpenLyricsExport') + self.parent = parent + self.manager = parent.plugin.manager + self.songs = songs + self.save_path = save_path + if not os.path.exists(self.save_path): + os.mkdir(self.save_path) + + def do_export(self): + """ + Export the songs. + """ + log.debug(u'started OpenLyricsExport') + openLyrics = OpenLyrics(self.manager) + self.parent.progressBar.setMaximum(len(self.songs)) + for song in self.songs: + Receiver.send_message(u'openlp_process_events') + if self.parent.stop_export_flag: + return False + self.parent.incrementProgressBar(unicode(translate( + 'SongsPlugin.OpenLyricsExport', 'Exporting "%s"...')) % + song.title) + xml = openLyrics.song_to_xml(song) + tree = etree.ElementTree(etree.fromstring(xml)) + tree.write(os.path.join(self.save_path, song.title + u'.xml'), + encoding=u'utf-8', xml_declaration=True, pretty_print=True) + return True diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index e7557f952..8c6d283e3 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -36,6 +36,7 @@ from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) +#TODO: Use lxml for parsing and make sure we use methods of "SongImport" . class OpenSongImport(SongImport): """ Import songs exported from OpenSong @@ -146,7 +147,7 @@ class OpenSongImport(SongImport): log.info(u'Zip importing %s', parts[-1]) self.import_wizard.incrementProgressBar( unicode(translate('SongsPlugin.ImportWizardForm', - 'Importing %s...')) % parts[-1]) + 'Importing %s...')) % parts[-1]) songfile = z.open(song) self.do_import_file(songfile) if self.commit: @@ -276,7 +277,7 @@ class OpenSongImport(SongImport): for num in versenums: versetag = u'%s%s' % (our_verse_type, num) lines = u'\n'.join(verses[versetype][num]) - self.verses.append([versetag, lines]) + self.add_verse(lines, versetag) # Keep track of what we have for error checking later versetags[versetag] = 1 # now figure out the presentation order @@ -292,6 +293,8 @@ class OpenSongImport(SongImport): else: log.warn(u'No verse order available for %s, skipping.', self.title) + # TODO: make sure that the default order list will be overwritten, if + # the songs provides its own order list. for tag in order: if tag[0].isdigit(): # Assume it's a verse if it has no prefix diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py new file mode 100644 index 000000000..78c2e838d --- /dev/null +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -0,0 +1,212 @@ +# -*- 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, 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 # +############################################################################### +""" +The :mod:`wowimport` module provides the functionality for importing Words of +Worship songs into the OpenLP database. +""" +import os +import logging +import struct + +from openlp.plugins.songs.lib.songimport import SongImport + +TITLE = 1 +AUTHOR = 2 +COPYRIGHT = 3 +CCLI_NO = 5 +VERSE = 12 +CHORUS = 20 +TOPIC = 29 +COMMENTS = 30 +VERSE_ORDER = 31 +SONG_BOOK = 35 +SONG_NUMBER = 36 +CUSTOM_VERSE = 37 + +log = logging.getLogger(__name__) + +class SongShowPlusImport(SongImport): + """ + The :class:`SongShowPlusImport` class provides the ability to import song + files from SongShow Plus. + + **SongShow Plus Song File Format:** + + The SongShow Plus song file format is as follows: + + * Each piece of data in the song file has some information that precedes + it. + * The general format of this data is as follows: + 4 Bytes, forming a 32 bit number, a key if you will, this describes what + the data is (see blockKey below) + 4 Bytes, forming a 32 bit number, which is the number of bytes until the + next block starts + 1 Byte, which tells how namy bytes follows + 1 or 4 Bytes, describes how long the string is, if its 1 byte, the string + is less than 255 + The next bytes are the actuall data. + The next block of data follows on. + + This description does differ for verses. Which includes extra bytes + stating the verse type or number. In some cases a "custom" verse is used, + in that case, this block will in include 2 strings, with the associated + string length descriptors. The first string is the name of the verse, the + second is the verse content. + + The file is ended with four null bytes. + + Valid extensions for a SongShow Plus song file are: + + * .sbsong + """ + otherList = {} + otherCount = 0 + + def __init__(self, master_manager, **kwargs): + """ + Initialise the import. + + ``master_manager`` + The song manager for the running OpenLP installation. + """ + SongImport.__init__(self, master_manager) + if kwargs.has_key(u'filename'): + self.import_source = kwargs[u'filename'] + if kwargs.has_key(u'filenames'): + self.import_source = kwargs[u'filenames'] + log.debug(self.import_source) + + def do_import(self): + """ + Receive a single file or a list of files to import. + """ + if isinstance(self.import_source, list): + self.import_wizard.progressBar.setMaximum(len(self.import_source)) + for file in self.import_source: + author = u'' + copyright = u'' + self.sspVerseOrderList = [] + otherCount = 0 + otherList = {} + file_name = os.path.split(file)[1] + self.import_wizard.incrementProgressBar( + u'Importing %s' % (file_name), 0) + songData = open(file, 'rb') + while (1): + blockKey, = struct.unpack("I",songData.read(4)) + # The file ends with 4 NUL's + if blockKey == 0: + break + nextBlockStarts, = struct.unpack("I",songData.read(4)) + if blockKey == VERSE or blockKey == CHORUS: + null, verseNo, = struct.unpack("BB",songData.read(2)) + elif blockKey == CUSTOM_VERSE: + null, verseNameLength, = struct.unpack("BB", + songData.read(2)) + verseName = songData.read(verseNameLength) + lengthDescriptorSize, = struct.unpack("B",songData.read(1)) + # Detect if/how long the length descriptor is + if lengthDescriptorSize == 12: + lengthDescriptor, = struct.unpack("I",songData.read(4)) + elif lengthDescriptorSize == 2: + lengthDescriptor = 1 + elif lengthDescriptorSize == 9: + lengthDescriptor = 0 + else: + lengthDescriptor, = struct.unpack("B",songData.read(1)) + data = songData.read(lengthDescriptor) + + if blockKey == TITLE: + self.title = unicode(data, u'cp1252') + elif blockKey == AUTHOR: + authors = data.split(" / ") + for author in authors: + if author.find(",") !=-1: + authorParts = author.split(", ") + author = authorParts[1] + " " + authorParts[0] + self.parse_author(unicode(author, u'cp1252')) + elif blockKey == COPYRIGHT: + self.add_copyright(unicode(data, u'cp1252')) + elif blockKey == CCLI_NO: + self.ccli_number = int(data) + elif blockKey == VERSE: + self.add_verse(unicode(data, u'cp1252'), + "V%s" % verseNo) + elif blockKey == CHORUS: + self.add_verse(unicode(data, u'cp1252'), + "C%s" % verseNo) + elif blockKey == TOPIC: + self.topics.append(unicode(data, u'cp1252')) + elif blockKey == COMMENTS: + self.comments = unicode(data, u'cp1252') + elif blockKey == VERSE_ORDER: + verseTag = self.toOpenLPVerseTag(data) + self.sspVerseOrderList.append(unicode(verseTag, + u'cp1252')) + elif blockKey == SONG_BOOK: + self.song_book_name = unicode(data, u'cp1252') + elif blockKey == SONG_NUMBER: + self.song_number = ord(data) + elif blockKey == CUSTOM_VERSE: + verseTag = self.toOpenLPVerseTag(verseName) + self.add_verse(unicode(data, u'cp1252'), verseTag) + else: + log.debug("Unrecognised blockKey: %s, data: %s" + %(blockKey, data)) + self.verse_order_list = self.sspVerseOrderList + songData.close() + self.finish() + self.import_wizard.incrementProgressBar( + u'Importing %s' % (file_name)) + return True + + def toOpenLPVerseTag(self, verseName): + if verseName.find(" ")!=-1: + verseParts = verseName.split(" ") + verseType = verseParts[0] + verseNumber = verseParts[1] + else: + verseType = verseName + verseNumber = "1" + verseType = verseType.lower() + if verseType == "verse": + verseTag = "V" + elif verseType == "chorus": + verseTag = "C" + elif verseType == "bridge": + verseTag = "B" + elif verseType == "pre-chorus": + verseTag = "P" + elif verseType == "bridge": + verseTag = "B" + else: + if not self.otherList.has_key(verseName): + self.otherCount = self.otherCount + 1 + self.otherList[verseName] = str(self.otherCount) + verseTag = "O" + verseNumber = self.otherList[verseName] + verseTag = verseTag + verseNumber + return verseTag diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 4bf11887a..b96e79961 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -60,6 +60,7 @@ The XML of an `OpenLyrics `_ song looks like this:: """ +import datetime import logging import re @@ -89,8 +90,10 @@ class SongXML(object): Add a verse to the ```` tag. ``type`` - A string denoting the type of verse. Possible values are "V", - "C", "B", "P", "I", "E" and "O". + A string denoting the type of verse. Possible values are *Verse*, + *Chorus*, *Bridge*, *Pre-Chorus*, *Intro*, *Ending* and *Other*. + Any other type is **not** allowed, this also includes translated + types. ``number`` An integer denoting the number of the item, for example: verse 1. @@ -126,8 +129,8 @@ class SongXML(object): The returned list has the following format:: - [[{'lang': 'en', 'type': 'V', 'label': '1'}, u"The English verse."], - [{'lang': 'en', 'type': 'C', 'label': '1'}, u"The English chorus."]] + [[{'lang': 'en', 'type': 'Verse', 'label': '1'}, u"English verse"], + [{'lang': 'en', 'type': 'Chorus', 'label': '1'}, u"English chorus"]] """ self.song_xml = None if xml[:5] == u'`` - The attribute *translit* is not supported. + The attribute *translit* is not supported. Note, the attribute *lang* is + considered, but there is not further functionality implemented yet. ```` OpenLP supports this property. """ + IMPLEMENTED_VERSION = u'0.7' def __init__(self, manager): self.manager = manager @@ -222,8 +227,14 @@ class OpenLyrics(object): """ sxml = SongXML() verse_list = sxml.get_verses(song.lyrics) - song_xml = objectify.fromstring( - u'') + song_xml = objectify.fromstring(u'') + # Append the necessary meta data to the song. + song_xml.set(u'xmlns', u'http://openlyrics.info/namespace/2009/song') + song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION) + song_xml.set(u'createdIn', u'OpenLP 1.9.4') # Use variable + song_xml.set(u'modifiedIn', u'OpenLP 1.9.4') # Use variable + song_xml.set(u'modifiedDate', + datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S')) properties = etree.SubElement(song_xml, u'properties') titles = etree.SubElement(properties, u'titles') self._add_text_to_element(u'title', titles, song.title.strip()) @@ -237,7 +248,7 @@ class OpenLyrics(object): self._add_text_to_element(u'copyright', properties, song.copyright) if song.verse_order: self._add_text_to_element( - u'verseOrder', properties, song.verse_order) + u'verseOrder', properties, song.verse_order.lower()) if song.ccli_number: self._add_text_to_element(u'ccliNo', properties, song.ccli_number) if song.authors: @@ -252,7 +263,8 @@ class OpenLyrics(object): songbooks = etree.SubElement(properties, u'songbooks') element = self._add_text_to_element( u'songbook', songbooks, None, book) - element.set(u'entry', song.song_number) + if song.song_number: + element.set(u'entry', song.song_number) if song.topics: themes = etree.SubElement(properties, u'themes') for topic in song.topics: @@ -263,6 +275,8 @@ class OpenLyrics(object): verse[0][u'type'][0].lower(), verse[0][u'label']) element = \ self._add_text_to_element(u'verse', lyrics, None, verse_tag) + if verse[0].has_key(u'lang'): + element.set(u'lang', verse[0][u'lang']) element = self._add_text_to_element(u'lines', element) for line in unicode(verse[1]).split(u'\n'): self._add_text_to_element(u'line', element, line) @@ -450,7 +464,7 @@ class OpenLyrics(object): text += u'\n' text += u'\n'.join([unicode(line) for line in lines.line]) verse_name = self._get(verse, u'name') - verse_type = unicode(VerseType.to_string(verse_name[0]))[0] + verse_type = unicode(VerseType.to_string(verse_name[0])) verse_number = re.compile(u'[a-zA-Z]*').sub(u'', verse_name) verse_part = re.compile(u'[0-9]*').sub(u'', verse_name[1:]) # OpenLyrics allows e. g. "c", but we need "c1". @@ -478,9 +492,9 @@ class OpenLyrics(object): for name in temp_verse_order: if name[0] == previous_type: if name[1] != previous_number: - verse_order.append(u''.join((name[0], name[1]))) + verse_order.append(u''.join((name[0][0], name[1]))) else: - verse_order.append(u''.join((name[0], name[1]))) + verse_order.append(u''.join((name[0][0], name[1]))) previous_type = name[0] previous_number = name[1] previous_part = name[2] diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 2bd636097..ac41458c2 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -31,7 +31,6 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.db import Manager -from openlp.core.lib.ui import UiStrings from openlp.plugins.songs.lib import SongMediaItem, SongsTab, SongXML from openlp.plugins.songs.lib.db import Author, init_schema, Song from openlp.plugins.songs.lib.importer import SongFormat @@ -52,17 +51,14 @@ class SongsPlugin(Plugin): """ Create and set up the Songs plugin. """ - Plugin.__init__(self, u'Songs', u'1.9.4', plugin_helpers) + Plugin.__init__(self, u'Songs', u'1.9.4', plugin_helpers, + SongMediaItem, SongsTab) self.weight = -10 self.manager = Manager(u'songs', init_schema) self.icon_path = u':/plugins/plugin_songs.png' self.icon = build_icon(self.icon_path) self.whitespace = re.compile(r'\W+', re.UNICODE) - def getSettingsTab(self): - visible_name = self.getString(StringContent.VisibleName) - return SongsTab(self.name, visible_name[u'title']) - def initialise(self): log.info(u'Songs Initialising') Plugin.initialise(self) @@ -70,13 +66,6 @@ class SongsPlugin(Plugin): self.mediaItem.displayResultsSong( self.manager.get_all_objects(Song, order_by_ref=Song.search_title)) - def getMediaManagerItem(self): - """ - Create the MediaManagerItem object, which is displaed in the - Media Manager. - """ - return SongMediaItem(self, self, self.icon) - def addImportMenuItem(self, import_menu): """ Give the Songs plugin the opportunity to add items to the @@ -107,8 +96,17 @@ class SongsPlugin(Plugin): The actual **Export** menu item, so that your actions can use it as their parent. """ - # No menu items for now. - pass + # Main song import menu item - will eventually be the only one + self.SongExportItem = QtGui.QAction(export_menu) + self.SongExportItem.setObjectName(u'SongExportItem') + self.SongExportItem.setText(translate( + 'SongsPlugin', '&Song')) + self.SongExportItem.setToolTip(translate('SongsPlugin', + 'Exports songs using the export wizard.')) + export_menu.addAction(self.SongExportItem) + # Signals and slots + QtCore.QObject.connect(self.SongExportItem, + QtCore.SIGNAL(u'triggered()'), self.onSongExportItemClicked) def addToolsMenuItem(self, tools_menu): """ @@ -184,6 +182,10 @@ class SongsPlugin(Plugin): if self.mediaItem: self.mediaItem.onImportClick() + def onSongExportItemClicked(self): + if self.mediaItem: + self.mediaItem.onExportClick() + def about(self): about_text = translate('SongsPlugin', 'Songs Plugin' '
The songs plugin provides the ability to display and ' @@ -237,7 +239,18 @@ class SongsPlugin(Plugin): u'title': translate('SongsPlugin', 'Songs', 'container title') } # Middle Header Bar - Plugin.setPluginTextStrings(self) + tooltips = { + u'load': u'', + u'import': u'', + u'new': translate('SongsPlugin', 'Add a new Song'), + u'edit': translate('SongsPlugin', 'Edit the selected Song'), + u'delete': translate('SongsPlugin', 'Delete the selected Song'), + u'preview': translate('SongsPlugin', 'Preview the selected Song'), + u'live': translate('SongsPlugin', 'Send the selected Song live'), + u'service': translate('SongsPlugin', + 'Add the selected Song to the service') + } + self.setPluginUiTextStrings(tooltips) def finalise(self): """ diff --git a/openlp/plugins/songusage/forms/songusagedeletedialog.py b/openlp/plugins/songusage/forms/songusagedeletedialog.py index af85ad5a9..9dc4219fc 100644 --- a/openlp/plugins/songusage/forms/songusagedeletedialog.py +++ b/openlp/plugins/songusage/forms/songusagedeletedialog.py @@ -25,7 +25,9 @@ ############################################################################### from PyQt4 import QtCore, QtGui + from openlp.core.lib import translate +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_SongUsageDeleteDialog(object): def setupUi(self, songUsageDeleteDialog): @@ -43,22 +45,14 @@ class Ui_SongUsageDeleteDialog(object): QtGui.QCalendarWidget.NoVerticalHeader) self.deleteCalendar.setObjectName(u'deleteCalendar') self.verticalLayout.addWidget(self.deleteCalendar) - self.buttonBox = QtGui.QDialogButtonBox(songUsageDeleteDialog) + self.buttonBox = create_accept_reject_button_box( + songUsageDeleteDialog, True) self.buttonBox.setGeometry(QtCore.QRect(30, 210, 245, 25)) - self.buttonBox.setStandardButtons( - QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName(u'buttonBox') - self.retranslateUi(songUsageDeleteDialog) - QtCore.QObject.connect( - self.buttonBox, QtCore.SIGNAL(u'accepted()'), - songUsageDeleteDialog.accept) - QtCore.QObject.connect( - self.buttonBox, QtCore.SIGNAL(u'rejected()'), - songUsageDeleteDialog.close) QtCore.QMetaObject.connectSlotsByName(songUsageDeleteDialog) def retranslateUi(self, songUsageDeleteDialog): songUsageDeleteDialog.setWindowTitle( translate('SongUsagePlugin.SongUsageDeleteForm', - 'Delete Song Usage Data')) \ No newline at end of file + 'Delete Song Usage Data')) diff --git a/openlp/plugins/songusage/forms/songusagedetaildialog.py b/openlp/plugins/songusage/forms/songusagedetaildialog.py index ec1f69d7e..322e3eb4b 100644 --- a/openlp/plugins/songusage/forms/songusagedetaildialog.py +++ b/openlp/plugins/songusage/forms/songusagedetaildialog.py @@ -27,6 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_SongUsageDetailDialog(object): def setupUi(self, songUsageDetailDialog): @@ -71,17 +72,10 @@ class Ui_SongUsageDetailDialog(object): self.verticalLayout4.addLayout(self.horizontalLayout) self.verticalLayout2.addWidget(self.fileGroupBox) self.verticalLayout.addWidget(self.dateRangeGroupBox) - self.buttonBox = QtGui.QDialogButtonBox(songUsageDetailDialog) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | - QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(u'buttonBox') + self.buttonBox = create_accept_reject_button_box( + songUsageDetailDialog, True) self.verticalLayout.addWidget(self.buttonBox) - self.retranslateUi(songUsageDetailDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'accepted()'), - songUsageDetailDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'), - songUsageDetailDialog.close) QtCore.QObject.connect(self.saveFilePushButton, QtCore.SIGNAL(u'pressed()'), songUsageDetailDialog.defineOutputLocation) diff --git a/resources/forms/songexport.ui b/resources/forms/songexport.ui deleted file mode 100644 index 9830db3ef..000000000 --- a/resources/forms/songexport.ui +++ /dev/null @@ -1,241 +0,0 @@ - - SongExportDialog - - - - 0 - 0 - 641 - 607 - - - - Dialog - - - - 8 - - - 8 - - - - - - 8 - - - 0 - - - - - Available Songs - - - - - - - - - Select All - - - - - - - - - - - 0 - 0 - - - - - 30 - 16777215 - - - - - 8 - - - QLayout::SetMinimumSize - - - 0 - - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 20 - 132 - - - - - - - - Select Songs - - - - :/exports/export_move_to_list.png:/exports/export_move_to_list.png - - - - 20 - 20 - - - - - - - - Deselect Songs - - - - :/exports/export_remove.png:/exports/export_remove.png - - - - 20 - 20 - - - - - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 20 - 131 - - - - - - - - - - - Selected Songs - - - - - - - - - Select All - - - - - - - - - - - - - 0 - - - - OpenLyric Format - - - - - Text File - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - SongExportButtonBox - AvailableListWidget - AvailableAllToolButton - SelectToolButton - DeselectToolButton - SelectedListWidget - SelectedAllToolButton - ExportTabWidget - - - - - - - SongExportButtonBox - accepted() - SongExportDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - SongExportButtonBox - rejected() - SongExportDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/resources/images/bibles_search_reference.png b/resources/images/bibles_search_reference.png new file mode 100644 index 000000000..f64c0ad78 Binary files /dev/null and b/resources/images/bibles_search_reference.png differ diff --git a/resources/images/bibles_search_text.png b/resources/images/bibles_search_text.png new file mode 100644 index 000000000..1ab7145c6 Binary files /dev/null and b/resources/images/bibles_search_text.png differ diff --git a/resources/images/export_move_to_list.png b/resources/images/export_move_to_list.png deleted file mode 100644 index 5c0005856..000000000 Binary files a/resources/images/export_move_to_list.png and /dev/null differ diff --git a/resources/images/export_remove.png b/resources/images/export_remove.png deleted file mode 100644 index ef8e685e2..000000000 Binary files a/resources/images/export_remove.png and /dev/null differ diff --git a/resources/images/export_selectall.png b/resources/images/export_selectall.png deleted file mode 100644 index 0f0d9f152..000000000 Binary files a/resources/images/export_selectall.png and /dev/null differ diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 2db924363..9e6ff6543 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -21,6 +21,10 @@ song_topic_edit.png song_book_edit.png + + bibles_search_text.png + bibles_search_reference.png + plugin_alerts.png plugin_bibles.png @@ -80,12 +84,10 @@ import_load.png - export_selectall.png - export_remove.png export_load.png - export_move_to_list.png + wizard_exportsong.bmp wizard_importsong.bmp wizard_importbible.bmp wizard_createtheme.bmp diff --git a/resources/images/wizard_exportsong.bmp b/resources/images/wizard_exportsong.bmp new file mode 100644 index 000000000..948422dcc Binary files /dev/null and b/resources/images/wizard_exportsong.bmp differ