diff --git a/documentation/manual/source/glossary.rst b/documentation/manual/source/glossary.rst index 41a8f4ac6..6f4ebcdd6 100644 --- a/documentation/manual/source/glossary.rst +++ b/documentation/manual/source/glossary.rst @@ -48,7 +48,7 @@ Service Manger -------------- The service manager contains the media items in your service file. This is the -area from wich your media items go live, and you can also save, open, and edit +area from which your media items go live, and you can also save, open, and edit services files. .. image:: pics/servicemanager.png diff --git a/documentation/manual/source/index.rst b/documentation/manual/source/index.rst index ac29c4360..5786af1ae 100644 --- a/documentation/manual/source/index.rst +++ b/documentation/manual/source/index.rst @@ -14,6 +14,8 @@ Contents: introduction glossary dualmonitors + mediamanager + songs Indices and tables ================== diff --git a/documentation/manual/source/mediamanager.rst b/documentation/manual/source/mediamanager.rst new file mode 100644 index 000000000..2cf1d2c57 --- /dev/null +++ b/documentation/manual/source/mediamanager.rst @@ -0,0 +1,26 @@ +============= +Media Manager +============= + +Once you get your system set up for OpenLP you will be ready to add content to +your setup. This will all happen through the **Media Manager**. The +`Media Manager` contains all the bibles, songs, presentations, media, and +everything else that you will project through OpenLP. + +Enabling the Plugins +-------------------- + +You may need to enable the plugins that came with OpenLP. As you can see below +this is what the `Media Manager` looks like with all the plugins enabled. + +.. image:: pics/mediamanager.png + +To enable the plugins navigate to :menuselection:`Settings --> Plugins` or +press `F7`. You will then want to click on the plugin to the left that you want +to enable and select **active** from the drop down box to the right. + +.. image:: pics/plugins.png + + +Now you should be ready to add content to OpenLP check out the section of this +guide on the individual plugins. diff --git a/documentation/manual/source/pics/finishedimport.png b/documentation/manual/source/pics/finishedimport.png new file mode 100644 index 000000000..d49876ec9 Binary files /dev/null and b/documentation/manual/source/pics/finishedimport.png differ diff --git a/documentation/manual/source/pics/plugins.png b/documentation/manual/source/pics/plugins.png new file mode 100644 index 000000000..b6810242b Binary files /dev/null and b/documentation/manual/source/pics/plugins.png differ diff --git a/documentation/manual/source/pics/selectsongs.png b/documentation/manual/source/pics/selectsongs.png new file mode 100644 index 000000000..20df3ba2a Binary files /dev/null and b/documentation/manual/source/pics/selectsongs.png differ diff --git a/documentation/manual/source/pics/songimporter.png b/documentation/manual/source/pics/songimporter.png new file mode 100644 index 000000000..96c39ea38 Binary files /dev/null and b/documentation/manual/source/pics/songimporter.png differ diff --git a/documentation/manual/source/pics/songimporterchoices.png b/documentation/manual/source/pics/songimporterchoices.png new file mode 100644 index 000000000..5c458838e Binary files /dev/null and b/documentation/manual/source/pics/songimporterchoices.png differ diff --git a/documentation/manual/source/pics/songselectlyrics.png b/documentation/manual/source/pics/songselectlyrics.png new file mode 100644 index 000000000..2e3d92f0d Binary files /dev/null and b/documentation/manual/source/pics/songselectlyrics.png differ diff --git a/documentation/manual/source/pics/songselectsongsearch.png b/documentation/manual/source/pics/songselectsongsearch.png new file mode 100644 index 000000000..de0ea12ca Binary files /dev/null and b/documentation/manual/source/pics/songselectsongsearch.png differ diff --git a/documentation/manual/source/pics/songusage.png b/documentation/manual/source/pics/songusage.png new file mode 100644 index 000000000..10f29f2a9 Binary files /dev/null and b/documentation/manual/source/pics/songusage.png differ diff --git a/documentation/manual/source/pics/songusagedelete.png b/documentation/manual/source/pics/songusagedelete.png new file mode 100644 index 000000000..fec1b5e5d Binary files /dev/null and b/documentation/manual/source/pics/songusagedelete.png differ diff --git a/documentation/manual/source/pics/songusagereport.png b/documentation/manual/source/pics/songusagereport.png new file mode 100644 index 000000000..c0d9df3dd Binary files /dev/null and b/documentation/manual/source/pics/songusagereport.png differ diff --git a/documentation/manual/source/pics/vistadisplaysettings.png b/documentation/manual/source/pics/vistadisplaysettings.png old mode 100755 new mode 100644 diff --git a/documentation/manual/source/pics/vistapersonalize.png b/documentation/manual/source/pics/vistapersonalize.png old mode 100755 new mode 100644 diff --git a/documentation/manual/source/pics/winsevendisplay.png b/documentation/manual/source/pics/winsevendisplay.png old mode 100755 new mode 100644 diff --git a/documentation/manual/source/pics/winsevenresolution.png b/documentation/manual/source/pics/winsevenresolution.png old mode 100755 new mode 100644 diff --git a/documentation/manual/source/pics/xpdisplaysettings.png b/documentation/manual/source/pics/xpdisplaysettings.png index eb7a8921c..e1ec66c6f 100644 Binary files a/documentation/manual/source/pics/xpdisplaysettings.png and b/documentation/manual/source/pics/xpdisplaysettings.png differ diff --git a/documentation/manual/source/songs.rst b/documentation/manual/source/songs.rst new file mode 100644 index 000000000..678a6206c --- /dev/null +++ b/documentation/manual/source/songs.rst @@ -0,0 +1,100 @@ +===== +Songs +===== + +Managing your songs in OpenLP is a relatively simple process. There are also +converters provided to get data from other formats into OpenLP. + +Song Importer +============= + +If you are using an earlier version of OpenLP or come from another software +package, you may be able to convert your existing database to work in OpenLP +2.0. To access the Song Importer :menuselection:`File --> Import --> Song`. +You will then see the Song Importer window, then click :guilabel:`Next`. + +.. image:: pics/songimporter.png + +After choosing :guilabel:`Next` you can then select from the various types of +software that OpenLP will convert songs from. + +.. image:: pics/songimporterchoices.png + +Then click on the file folder icon to choose the file of the song database you +want to import. See the following sections for information on the different +formats that OpenLP will import. + +Importing from OpenLP Version 1 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Converting from OpenLP Version 1 is a pretty simple process. You will first +need to locate your version 1 database file. + +Windows XP:: + + C:\Documents and Settings\All Users\Application Data\openlp.org\Data\songs.olp + +Windows Vista / Windows 7:: + + C:\ProgramData\openlp.org\Data\songs.olp + +After clicking :guilabel:`Next` your conversion should be complete. + +.. image:: pics/finishedimport.png + +Then press :guilabel:`Finish` and you should now be ready to use your OpenLP +version one songs. + +Importing from OpenSong +^^^^^^^^^^^^^^^^^^^^^^^ + +Converting from OpenSong you will need to locate your songs database. In the +later versions of OpenSong you are asked to define the location of this. The +songs will be located in a folder named :guilabel:`Songs`. This folder should +contain files with all your songs in them without a file extension. (file.xxx). +When you have located this folder you will then need to select the songs from +the folder. + +.. image:: pics/selectsongs.png + +On most operating systems to select all the songs, first select the first song +in the lest then press shift and select the last song in the list. After this +press :guilabel:`Next` and you should see that your import has been successful. + +.. image:: pics/finishedimport.png + +Press :guilabel:`Finish` and you will now be ready to use your songs imported +from OpenSong. + +Importing from CCLI Song Select +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To import from CCLI Song Select you must be a CCLI Subscriber and also a +subscriber of the Song Select service. For info on that go to: +http://www.ccli.com + +The first step for importing from CCLI Song Select is to log into your account. +Then search for your desired song. For this example we will be adding the song +"Amazing Grace". + +.. image:: pics/songselectsongsearch.png + +For the song you are searching for select `lyrics` This should take you to a +page displaying the lyrics and copyright info for your song. + +.. image:: pics/songselectlyrics.png + +Next, hover over the :guilabel:`Lyrics` menu from the upper right corner. Then +choose either the .txt or .usr file. You will then be asked to chose a download +location if your browser does not automatically select that for you. Select +this file from the OpenLP import window and then click :guilabel:`Next` You can +also select multiple songs for import at once on most operating systems by +selecting the first item in the list then holding shift select the last item in +the list. When finished you should see that your import has completed. + +.. image:: pics/finishedimport.png + +Press :guilabel:`Finish` and you will now be ready to use your songs imported +from CCLI SongSelect. + + diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 0dc138abc..8afa02111 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -294,4 +294,5 @@ class Manager(object): """ if self.is_dirty: engine = create_engine(self.db_url) - engine.execute("vacuum") + if self.db_url.startswith(u'sqlite'): + engine.execute("vacuum") diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 2d563962e..130da0c0e 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -320,15 +320,9 @@ class MediaManagerItem(QtGui.QWidget): translate('OpenLP.MediaManagerItem', '&Add to selected Service Item'), self.onAddEditClick)) - if QtCore.QSettings().value(u'advanced/double click live', - QtCore.QVariant(False)).toBool(): - QtCore.QObject.connect(self.listView, - QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), - self.onLiveClick) - else: - QtCore.QObject.connect(self.listView, - QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), - self.onPreviewClick) + QtCore.QObject.connect(self.listView, + QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), + self.onClickPressed) def initialise(self): """ @@ -426,10 +420,20 @@ class MediaManagerItem(QtGui.QWidget): raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to ' u'be defined by the plugin') - def generateSlideData(self, service_item, item=None): + def generateSlideData(self, serviceItem, item=None, xmlVersion=False): raise NotImplementedError(u'MediaManagerItem.generateSlideData needs ' u'to be defined by the plugin') + def onClickPressed(self): + """ + Allows the list click action to be determined dynamically + """ + if QtCore.QSettings().value(u'advanced/double click live', + QtCore.QVariant(False)).toBool(): + self.onLiveClick() + else: + self.onPreviewClick() + def onPreviewClick(self): """ Preview an item by building a service item then adding that service @@ -442,10 +446,10 @@ class MediaManagerItem(QtGui.QWidget): 'You must select one or more items to preview.')) else: log.debug(self.plugin.name + u' Preview requested') - service_item = self.buildServiceItem() - if service_item: - service_item.from_plugin = True - self.parent.previewController.addServiceItem(service_item) + serviceItem = self.buildServiceItem() + if serviceItem: + serviceItem.from_plugin = True + self.parent.previewController.addServiceItem(serviceItem) def onLiveClick(self): """ @@ -459,10 +463,10 @@ class MediaManagerItem(QtGui.QWidget): 'You must select one or more items to send live.')) else: log.debug(self.plugin.name + u' Live requested') - service_item = self.buildServiceItem() - if service_item: - service_item.from_plugin = True - self.parent.liveController.addServiceItem(service_item) + serviceItem = self.buildServiceItem() + if serviceItem: + serviceItem.from_plugin = True + self.parent.liveController.addServiceItem(serviceItem) def onAddClick(self): """ @@ -474,22 +478,22 @@ class MediaManagerItem(QtGui.QWidget): translate('OpenLP.MediaManagerItem', 'You must select one or more items.')) else: - # Is it posssible to process multiple list items to generate multiple - # service items? + # Is it posssible to process multiple list items to generate + # multiple service items? if self.singleServiceItem or self.remoteTriggered: log.debug(self.plugin.name + u' Add requested') - service_item = self.buildServiceItem() - if service_item: - service_item.from_plugin = False - self.parent.serviceManager.addServiceItem(service_item, + serviceItem = self.buildServiceItem(None, True) + if serviceItem: + serviceItem.from_plugin = False + self.parent.serviceManager.addServiceItem(serviceItem, replace=self.remoteTriggered) else: items = self.listView.selectedIndexes() for item in items: - service_item = self.buildServiceItem(item) - if service_item: - service_item.from_plugin = False - self.parent.serviceManager.addServiceItem(service_item) + serviceItem = self.buildServiceItem(item, True) + if serviceItem: + serviceItem.from_plugin = False + self.parent.serviceManager.addServiceItem(serviceItem) def onAddEditClick(self): """ @@ -502,36 +506,36 @@ class MediaManagerItem(QtGui.QWidget): 'You must select one or more items')) else: log.debug(self.plugin.name + u' Add requested') - service_item = self.parent.serviceManager.getServiceItem() - if not service_item: + serviceItem = self.parent.serviceManager.getServiceItem() + if not serviceItem: QtGui.QMessageBox.information(self, translate('OpenLP.MediaManagerItem', 'No Service Item Selected'), translate('OpenLP.MediaManagerItem', 'You must select an existing service item to add to.')) - elif self.title.lower() == service_item.name.lower(): - self.generateSlideData(service_item) - self.parent.serviceManager.addServiceItem(service_item, + elif self.title.lower() == serviceItem.name.lower(): + self.generateSlideData(serviceItem) + self.parent.serviceManager.addServiceItem(serviceItem, replace=True) else: - #Turn off the remote edit update message indicator + # Turn off the remote edit update message indicator QtGui.QMessageBox.information(self, translate('OpenLP.MediaManagerItem', 'Invalid Service Item'), unicode(translate('OpenLP.MediaManagerItem', 'You must select a %s service item.')) % self.title) - def buildServiceItem(self, item=None): + def buildServiceItem(self, item=None, xmlVersion=False): """ Common method for generating a service item """ - service_item = ServiceItem(self.parent) + serviceItem = ServiceItem(self.parent) if self.serviceItemIconName: - service_item.add_icon(self.serviceItemIconName) + serviceItem.add_icon(self.serviceItemIconName) else: - service_item.add_icon(self.parent.icon_path) - if self.generateSlideData(service_item, item): - return service_item + serviceItem.add_icon(self.parent.icon_path) + if self.generateSlideData(serviceItem, item, xmlVersion): + return serviceItem else: return None diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 9b8b78818..68839f16d 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -32,7 +32,7 @@ import logging from PyQt4 import QtWebKit from openlp.core.lib import expand_tags, build_lyrics_format_css, \ - build_lyrics_outline_css + build_lyrics_outline_css, Receiver log = logging.getLogger(__name__) @@ -92,13 +92,20 @@ class Renderer(object): (build_lyrics_format_css(self._theme, self.page_width, self.page_height), build_lyrics_outline_css(self._theme)) - def format_slide(self, words, line_break): + def format_slide(self, words, line_break, force_page=False): """ Figure out how much text can appear on a slide, using the current theme settings. ``words`` The words to be fitted on the slide. + + ``line_break`` + Add line endings after each line of text used for bibles. + + ``force_page`` + Flag to tell message lines in page. + """ log.debug(u'format_slide - Start') line_end = u'' @@ -114,13 +121,19 @@ class Renderer(object): formatted = [] html_text = u'' styled_text = u'' + line_count = 0 for line in text: + if line_count != -1: + line_count += 1 styled_line = expand_tags(line) + line_end styled_text += styled_line html = self.page_shell + styled_text + u'' self.web.setHtml(html) # Text too long so go to next page if self.web_frame.contentsSize().height() > self.page_height: + if force_page and line_count > 0: + Receiver.send_message(u'theme_line_count', line_count) + line_count = -1 if html_text.endswith(u'
'): html_text = html_text[:len(html_text)-4] formatted.append(html_text) diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py index bf561b4b3..81cde12a0 100644 --- a/openlp/core/lib/rendermanager.py +++ b/openlp/core/lib/rendermanager.py @@ -67,8 +67,9 @@ class RenderManager(object): self.service_theme = u'' self.theme_level = u'' self.override_background = None - self.themedata = None + self.theme_data = None self.alertTab = None + self.force_page = False def update_display(self): """ @@ -80,7 +81,7 @@ class RenderManager(object): self.display.imageManager = self.image_manager self.display.setup() self.renderer.bg_frame = None - self.themedata = None + self.theme_data = None self.image_manager.update_display(self.width, self.height) def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global): @@ -99,7 +100,7 @@ class RenderManager(object): self.theme_level = theme_level self.global_theme_data = \ self.theme_manager.getThemeData(self.global_theme) - self.themedata = None + self.theme_data = None def set_service_theme(self, service_theme): """ @@ -109,7 +110,7 @@ class RenderManager(object): The service-level theme to be set. """ self.service_theme = service_theme - self.themedata = None + self.theme_data = None def set_override_theme(self, theme, overrideLevels=False): """ @@ -146,19 +147,19 @@ class RenderManager(object): self.theme = self.service_theme else: self.theme = self.global_theme - if self.theme != self.renderer.theme_name or self.themedata is None \ + if self.theme != self.renderer.theme_name or self.theme_data is None \ or overrideLevels: log.debug(u'theme is now %s', self.theme) # Force the theme to be the one passed in. if overrideLevels: - self.themedata = theme + self.theme_data = theme else: - self.themedata = self.theme_manager.getThemeData(self.theme) + self.theme_data = self.theme_manager.getThemeData(self.theme) self.calculate_default(self.screens.current[u'size']) - self.renderer.set_theme(self.themedata) - self.build_text_rectangle(self.themedata) - self.image_manager.add_image(self.themedata.theme_name, - self.themedata.background_filename) + self.renderer.set_theme(self.theme_data) + self.build_text_rectangle(self.theme_data) + self.image_manager.add_image(self.theme_data.theme_name, + self.theme_data.background_filename) return self.renderer._rect, self.renderer._rect_footer def build_text_rectangle(self, theme): @@ -187,14 +188,19 @@ class RenderManager(object): theme.font_footer_height - 1) self.renderer.set_text_rectangle(main_rect, footer_rect) - def generate_preview(self, themedata): + def generate_preview(self, theme_data, force_page=False): """ Generate a preview of a theme. - ``themedata`` + ``theme_data`` The theme to generated a preview for. + + ``force_page`` + Flag to tell message lines per page need to be generated. """ log.debug(u'generate preview') + # save value for use in format_slide + self.force_page = force_page # set the default image size for previews self.calculate_default(self.screens.preview[u'size']) verse = u'The Lord said to {r}Noah{/r}: \n' \ @@ -204,23 +210,27 @@ class RenderManager(object): 'Get those children out of the muddy, muddy \n' \ '{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \ 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n' + # make big page for theme edit dialog to get line count + if self.force_page: + verse = verse + verse + verse footer = [] footer.append(u'Arky Arky (Unknown)' ) footer.append(u'Public Domain') footer.append(u'CCLI 123456') # build a service item to generate preview serviceItem = ServiceItem() - serviceItem.theme = themedata + serviceItem.theme = theme_data serviceItem.add_from_text(u'', verse, footer) serviceItem.render_manager = self serviceItem.raw_footer = footer serviceItem.render(True) - self.display.buildHtml(serviceItem) - raw_html = serviceItem.get_rendered_frame(0) - preview = self.display.text(raw_html) - # Reset the real screen size for subsequent render requests - self.calculate_default(self.screens.current[u'size']) - return preview + if not self.force_page: + self.display.buildHtml(serviceItem) + raw_html = serviceItem.get_rendered_frame(0) + preview = self.display.text(raw_html) + # Reset the real screen size for subsequent render requests + self.calculate_default(self.screens.current[u'size']) + return preview def format_slide(self, words, line_break): """ @@ -228,9 +238,12 @@ class RenderManager(object): ``words`` The words to go on the slides. + + ``line_break`` + Add line endings after each line of text used for bibles. """ log.debug(u'format slide') - return self.renderer.format_slide(words, line_break) + return self.renderer.format_slide(words, line_break, self.force_page) def calculate_default(self, screen): """ diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index ab57e4328..b9394030a 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -100,6 +100,8 @@ class ServiceItem(object): self.bg_image_bytes = None self.search_string = u'' self.data_string = u'' + self.edit_id = None + self.xml_version = None self._new_item() def _new_item(self): @@ -251,7 +253,8 @@ class ServiceItem(object): u'from_plugin': self.from_plugin, u'capabilities': self.capabilities, u'search': self.search_string, - u'data': self.data_string + u'data': self.data_string, + u'xml_version': self.xml_version } service_data = [] if self.service_item_type == ServiceItemType.Text: @@ -293,6 +296,8 @@ class ServiceItem(object): if u'search' in header: self.search_string = header[u'search'] self.data_string = header[u'data'] + if u'xml_version' in header: + self.xml_version = header[u'xml_version'] if self.service_item_type == ServiceItemType.Text: for slide in serviceitem[u'serviceitem'][u'data']: self._raw_frames.append(slide) diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index dc126d863..a390780ca 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -146,7 +146,7 @@ class AdvancedTab(SettingsTab): self.mediaPluginCheckBox.setText(translate('OpenLP.AdvancedTab', 'Remember active media manager tab on startup')) self.doubleClickLiveCheckBox.setText(translate('OpenLP.AdvancedTab', - 'Double-click to send items straight to live (requires restart)')) + 'Double-click to send items straight to live')) self.expandServiceItemCheckBox.setText(translate('OpenLP.AdvancedTab', 'Expand new service items on creation')) # self.sharedDirGroupBox.setTitle( diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 5a53a84fe..90f920a50 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -100,7 +100,7 @@ class MainDisplay(DisplayWidget): self.screens = screens self.isLive = live self.alertTab = None - self.hide_mode = None + self.hideMode = None self.setWindowTitle(u'OpenLP Display') self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;') self.setWindowFlags(QtCore.Qt.FramelessWindowHint | @@ -381,8 +381,8 @@ class MainDisplay(DisplayWidget): if self.isLive: self.setVisible(True) # if was hidden keep it hidden - if self.hide_mode and self.isLive: - self.hideDisplay(self.hide_mode) + if self.hideMode and self.isLive: + self.hideDisplay(self.hideMode) preview = QtGui.QImage(self.screen[u'size'].width(), self.screen[u'size'].height(), QtGui.QImage.Format_ARGB32_Premultiplied) @@ -412,8 +412,8 @@ class MainDisplay(DisplayWidget): if serviceItem.foot_text and serviceItem.foot_text: self.footer(serviceItem.foot_text) # if was hidden keep it hidden - if self.hide_mode and self.isLive: - self.hideDisplay(self.hide_mode) + if self.hideMode and self.isLive: + self.hideDisplay(self.hideMode) def footer(self, text): """ @@ -444,7 +444,7 @@ class MainDisplay(DisplayWidget): self.setVisible(True) if self.phononActive: self.webView.setVisible(True) - self.hide_mode = mode + self.hideMode = mode def showDisplay(self): """ @@ -459,9 +459,9 @@ class MainDisplay(DisplayWidget): if self.phononActive: self.webView.setVisible(False) self.videoPlay() + self.hideMode = None # Trigger actions when display is active again Receiver.send_message(u'maindisplay_active') - self.hide_mode = None class AudioPlayer(QtCore.QObject): """ diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 255859abe..6d721823a 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -308,7 +308,7 @@ class ServiceManager(QtGui.QWidget): self.maintainAction.setVisible(False) self.notesAction.setVisible(False) if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit)\ - and hasattr(serviceItem[u'service_item'], u'editId'): + and serviceItem[u'service_item'].edit_id: self.editAction.setVisible(True) if serviceItem[u'service_item']\ .is_capable(ItemCapabilities.AllowsMaintain): @@ -789,6 +789,8 @@ class ServiceManager(QtGui.QWidget): self.serviceName = name[len(name) - 1] self.parent.addRecentFile(filename) self.parent.serviceChanged(True, self.serviceName) + # Refresh Plugin lists + Receiver.send_message(u'plugin_list_refresh') def validateItem(self, serviceItem): """ @@ -864,7 +866,7 @@ class ServiceManager(QtGui.QWidget): editId, uuid = message.split(u':') for item in self.serviceItems: if item[u'service_item']._uuid == uuid: - item[u'service_item'].editId = editId + item[u'service_item'].edit_id = editId def replaceServiceItem(self, newItem): """ @@ -873,7 +875,7 @@ class ServiceManager(QtGui.QWidget): """ newItem.render() for itemcount, item in enumerate(self.serviceItems): - if item[u'service_item'].editId == newItem.editId and \ + if item[u'service_item'].edit_id == newItem.edit_id and \ item[u'service_item'].name == newItem.name: newItem.merge(item[u'service_item']) item[u'service_item'] = newItem @@ -983,7 +985,7 @@ class ServiceManager(QtGui.QWidget): .is_capable(ItemCapabilities.AllowsEdit): Receiver.send_message(u'%s_edit' % self.serviceItems[item][u'service_item'].name.lower(), u'L:%s' % - self.serviceItems[item][u'service_item'].editId ) + self.serviceItems[item][u'service_item'].edit_id ) def findServiceItem(self): """ @@ -1028,6 +1030,9 @@ class ServiceManager(QtGui.QWidget): # ServiceManager started the drag and drop if plugin == u'ServiceManager': startpos, startCount = self.findServiceItem() + # If no items selected + if startpos == -1: + return if item is None: endpos = len(self.serviceItems) else: diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 61fc78cd3..48810990a 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -331,10 +331,8 @@ class SlideController(QtGui.QWidget): QtCore.QObject.connect(self.PreviewListWidget, QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected) if not self.isLive: - if QtCore.QSettings().value(u'advanced/double click live', - QtCore.QVariant(False)).toBool(): - QtCore.QObject.connect(self.PreviewListWidget, - QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLive) + QtCore.QObject.connect(self.PreviewListWidget, + QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLiveClick) if isLive: QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'slidecontroller_live_spin_delay'), @@ -391,6 +389,8 @@ class SlideController(QtGui.QWidget): if self.isLive: QtCore.QObject.connect(self.volumeSlider, QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'maindisplay_active'), self.updatePreview) def screenSizeChanged(self): """ @@ -823,16 +823,15 @@ class SlideController(QtGui.QWidget): row) def updatePreview(self): + log.debug(u'updatePreview %s ' %self.screens.current[u'primary']) if not self.screens.current[u'primary']: # Grab now, but try again in a couple of seconds if slide change # is slow QtCore.QTimer.singleShot(0.5, self.grabMainDisplay) QtCore.QTimer.singleShot(2.5, self.grabMainDisplay) else: - label = self.PreviewListWidget.cellWidget( - self.PreviewListWidget.currentRow(), 1) - if label: - self.SlidePreview.setPixmap(label.pixmap()) + self.SlidePreview.setPixmap( + QtGui.QPixmap.fromImage(self.display.preview())) def grabMainDisplay(self): winid = QtGui.QApplication.desktop().winId() @@ -942,7 +941,15 @@ class SlideController(QtGui.QWidget): """ self.songEdit = True Receiver.send_message(u'%s_edit' % self.serviceItem.name.lower(), - u'P:%s' % self.serviceItem.editId) + u'P:%s' % self.serviceItem.edit_id) + + def onGoLiveClick(self): + """ + triggered by clicking the Preview slide items + """ + if QtCore.QSettings().value(u'advanced/double click live', + QtCore.QVariant(False)).toBool(): + self.onGoLive() def onGoLive(self): """ diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index ff57ecd56..cc52abafc 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -29,7 +29,8 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate, BackgroundType, BackgroundGradientType +from openlp.core.lib import translate, BackgroundType, BackgroundGradientType, \ + Receiver from openlp.core.utils import get_images_filter from themewizard import Ui_ThemeWizard @@ -96,10 +97,40 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): QtCore.QObject.connect(self, QtCore.SIGNAL(u'currentIdChanged(int)'), self.pageChanged) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'theme_line_count'), + self.updateLinesText) + QtCore.QObject.connect(self.mainSizeSpinBox, + QtCore.SIGNAL(u'valueChanged(int)'), + self.calculateLines) + QtCore.QObject.connect(self.mainSizeSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.calculateLines) + QtCore.QObject.connect(self.lineSpacingSpinBox, + QtCore.SIGNAL(u'valueChanged(int)'), + self.calculateLines) + QtCore.QObject.connect(self.lineSpacingSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.calculateLines) + QtCore.QObject.connect(self.outlineSizeSpinBox, + QtCore.SIGNAL(u'valueChanged(int)'), + self.calculateLines) + QtCore.QObject.connect(self.outlineSizeSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.calculateLines) + QtCore.QObject.connect(self.shadowSizeSpinBox, + QtCore.SIGNAL(u'valueChanged(int)'), + self.calculateLines) + QtCore.QObject.connect(self.shadowSizeSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.calculateLines) + QtCore.QObject.connect(self.mainFontComboBox, + QtCore.SIGNAL(u'activated(int)'), + self.calculateLines) def pageChanged(self, pageId): """ - Detects Page changes and updates. + Detects Page changes and updates as approprate. """ if pageId == 6: self.updateTheme() @@ -184,6 +215,22 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): self.backgroundPage.registerField( u'name', self.themeNameEdit) + def calculateLines(self): + """ + Calculate the number of lines on a page by rendering text + """ + # Do not trigger on start up + if self.page != 0: + self.updateTheme() + frame = self.thememanager.generateImage(self.theme, True) + + def updateLinesText(self, lines): + """ + Updates the lines on a page on the wizard + """ + self.mainLineCountLabel.setText(unicode(translate('OpenLP.ThemeForm', \ + '(%d lines per slide)' % int(lines)))) + def onOutlineCheckCheckBoxChanged(self, state): """ Change state as Outline check box changed @@ -194,6 +241,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): self.theme.font_main_outline = False self.outlineColorPushButton.setEnabled(self.theme.font_main_outline) self.outlineSizeSpinBox.setEnabled(self.theme.font_main_outline) + self.calculateLines() def onShadowCheckCheckBoxChanged(self, state): """ @@ -205,6 +253,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): self.theme.font_main_shadow = False self.shadowColorPushButton.setEnabled(self.theme.font_main_shadow) self.shadowSizeSpinBox.setEnabled(self.theme.font_main_shadow) + self.calculateLines() def onMainDefaultPositionCheckBox(self, value): """ @@ -244,6 +293,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): Set up the pages for Initial run through dialog """ log.debug(u'initializePage %s' % id) + self.page = id if id == 1: self.setBackgroundTabValues() elif id == 2: @@ -569,15 +619,24 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): (QtGui.QMessageBox.Ok), QtGui.QMessageBox.Ok) return - save_from = None - save_to = None + if self.theme.theme_name == u'-1' or self.theme.theme_name == u'None': + QtGui.QMessageBox.critical(self, + translate('OpenLP.ThemeForm', 'Theme Name Invalid'), + translate('OpenLP.ThemeForm', + 'Invalid theme name. ' + 'Please enter one.'), + (QtGui.QMessageBox.Ok), + QtGui.QMessageBox.Ok) + return + saveFrom = None + saveTo = None if self.theme.background_type == \ BackgroundType.to_string(BackgroundType.Image): filename = \ os.path.split(unicode(self.theme.background_filename))[1] - save_to = os.path.join(self.path, self.theme.theme_name, filename) - save_from = self.theme.background_filename - if self.thememanager.saveTheme(self.theme, save_from, save_to): + saveTo = os.path.join(self.path, self.theme.theme_name, filename) + saveFrom = self.theme.background_filename + if self.thememanager.saveTheme(self.theme, saveFrom, saveTo): return QtGui.QDialog.accept(self) def _colorButton(self, field): diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 0f0802bf2..65913a9f6 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -181,7 +181,7 @@ class ThemeManager(QtGui.QWidget): '%s (default)')) % newName self.themeListWidget.item(count).setText(name) - def changeGlobalFromScreen(self, index = -1): + def changeGlobalFromScreen(self, index=-1): """ Change the global theme when a theme is double clicked upon in the Theme Manager list @@ -252,17 +252,14 @@ class ThemeManager(QtGui.QWidget): Takes a theme and makes a new copy of it as well as saving it. """ log.debug(u'cloneThemeData') - themeData.new_document(newThemeName) - themeData.build_xml_from_attrs() - save_to = None - save_from = None + saveTo = None + saveFrom = None if themeData.background_type == u'image': - save_to = os.path.join(self.path, newThemeName, + saveTo = os.path.join(self.path, newThemeName, os.path.split(unicode(themeData.background_filename))[1]) - save_from = themeData.background_filename - theme = themeData.extract_xml() - pretty_theme = themeData.extract_formatted_xml() - self.saveTheme(newThemeName, theme, pretty_theme, save_from, save_to) + saveFrom = themeData.background_filename + themeData.theme_name = newThemeName + self.saveTheme(themeData, saveFrom, saveTo) def onEditTheme(self): """ @@ -462,17 +459,17 @@ class ThemeManager(QtGui.QWidget): """ return self.themelist - def getThemeData(self, themename): + def getThemeData(self, themeName): """ Returns a theme object from an XML file - ``themename`` + ``themeName`` Name of the theme to load from file """ - log.debug(u'getthemedata for theme %s', themename) - xml_file = os.path.join(self.path, unicode(themename), - unicode(themename) + u'.xml') - xml = get_text_file_string(xml_file) + log.debug(u'getthemedata for theme %s', themeName) + xmlFile = os.path.join(self.path, unicode(themeName), + unicode(themeName) + u'.xml') + xml = get_text_file_string(xmlFile) if not xml: return self.baseTheme() else: @@ -640,7 +637,7 @@ class ThemeManager(QtGui.QWidget): newtheme.display_vertical_align = vAlignCorrection return newtheme.extract_xml() - def saveTheme(self, theme, image_from, image_to): + def saveTheme(self, theme, imageFrom, imageTo): """ Called by thememaintenance Dialog to save the theme and to trigger the reload of the theme list @@ -673,8 +670,8 @@ class ThemeManager(QtGui.QWidget): self.deleteTheme(self.saveThemeName) if result == QtGui.QMessageBox.Yes: # Save the theme, overwriting the existing theme if necessary. - if image_to and self.oldBackgroundImage and \ - image_to != self.oldBackgroundImage: + if imageTo and self.oldBackgroundImage and \ + imageTo != self.oldBackgroundImage: try: os.remove(self.oldBackgroundImage) except OSError: @@ -688,12 +685,12 @@ class ThemeManager(QtGui.QWidget): finally: if outfile: outfile.close() - if image_from and image_from != image_to: + if imageFrom and imageFrom != imageTo: try: encoding = get_filesystem_encoding() shutil.copyfile( - unicode(image_from).encode(encoding), - unicode(image_to).encode(encoding)) + unicode(imageFrom).encode(encoding), + unicode(imageTo).encode(encoding)) except IOError: log.exception(u'Failed to save theme image') self.generateAndSaveImage(self.path, name, theme) @@ -729,7 +726,6 @@ class ThemeManager(QtGui.QWidget): def generateAndSaveImage(self, dir, name, theme): log.debug(u'generateAndSaveImage %s %s', dir, name) - #theme = self.createThemeFromXml(theme_xml, dir) theme_xml = theme.extract_xml() frame = self.generateImage(theme) samplepathname = os.path.join(self.path, name + u'.png') @@ -742,12 +738,18 @@ class ThemeManager(QtGui.QWidget): pixmap.save(thumb, u'png') log.debug(u'Theme image written to %s', samplepathname) - def generateImage(self, themedata): + def generateImage(self, themeData, forcePage=False): """ Call the RenderManager to build a Sample Image + + ``themeData`` + The theme to generated a preview for. + + ``forcePage`` + Flag to tell message lines per page need to be generated. """ - log.debug(u'generateImage \n%s ', themedata) - return self.parent.RenderManager.generate_preview(themedata) + log.debug(u'generateImage \n%s ', themeData) + return self.parent.RenderManager.generate_preview(themeData, forcePage) def getPreviewImage(self, theme): """ @@ -768,14 +770,14 @@ class ThemeManager(QtGui.QWidget): newtheme = ThemeXML() return newtheme - def createThemeFromXml(self, theme_xml, path): + def createThemeFromXml(self, themeXml, path): """ Return a theme object using information parsed from XML - ``theme_xml`` + ``themeXml`` The XML data to load into the theme """ theme = ThemeXML() - theme.parse(theme_xml) + theme.parse(themeXml) theme.extend_image_filename(path) return theme diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 361525563..fa47dd7f5 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -213,7 +213,7 @@ class BGExtract(object): finally: if not page: return None - cleaner = [(re.compile(' |
'), lambda match: '')] + cleaner = [(re.compile(' |
|\'\+\''), lambda match: '')] soup = None try: soup = BeautifulSoup(page, markupMassage=cleaner) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 6ea18dfe2..a3b95d3f3 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -697,7 +697,7 @@ class BibleMediaItem(MediaManagerItem): obj = obj.toPyObject() return unicode(obj) - def generateSlideData(self, service_item, item=None): + def generateSlideData(self, service_item, item=None, xmlVersion=False): """ Generates and formats the slides for the service item as well as the service item's title. diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index 65a73337a..c5281574b 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -46,7 +46,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): Constructor """ QtGui.QDialog.__init__(self, parent) - #self.parent = parent self.setupUi(self) # Connecting signals and slots self.previewButton = QtGui.QPushButton() @@ -124,8 +123,9 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): self.slideListView.addItem(slide[1]) theme = self.customSlide.theme_name id = self.themeComboBox.findText(theme, QtCore.Qt.MatchExactly) + # No theme match if id == -1: - id = 0 # Not Found + id = 0 self.themeComboBox.setCurrentIndex(id) else: self.themeComboBox.setCurrentIndex(0) @@ -264,7 +264,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): self.titleEdit.setFocus() return False, translate('CustomPlugin.EditCustomForm', 'You need to type in a title.') - # We must have one slide. + # We must have at least one slide. if self.slideListView.count() == 0: return False, translate('CustomPlugin.EditCustomForm', 'You need to add at least one slide') diff --git a/openlp/plugins/custom/forms/editcustomslideform.py b/openlp/plugins/custom/forms/editcustomslideform.py index 5f535c8bc..72c7dbb4a 100644 --- a/openlp/plugins/custom/forms/editcustomslideform.py +++ b/openlp/plugins/custom/forms/editcustomslideform.py @@ -50,7 +50,7 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog): def setText(self, text): """ Set the text for slideTextEdit. - + ``text`` The text (unicode). """ @@ -67,7 +67,7 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog): def onSplitButtonPressed(self): """ - Splits a slide in two slides. + Adds a slide split at the cursor. """ if self.slideTextEdit.textCursor().columnNumber() != 0: self.slideTextEdit.insertPlainText(u'\n') diff --git a/openlp/plugins/custom/lib/customxmlhandler.py b/openlp/plugins/custom/lib/customxmlhandler.py index b554f9657..907c3470d 100644 --- a/openlp/plugins/custom/lib/customxmlhandler.py +++ b/openlp/plugins/custom/lib/customxmlhandler.py @@ -43,6 +43,7 @@ import logging from xml.dom.minidom import Document from xml.etree.ElementTree import ElementTree, XML, dump +from lxml import etree, objectify from xml.parsers.expat import ExpatError log = logging.getLogger(__name__) @@ -55,14 +56,14 @@ class CustomXMLBuilder(object): def __init__(self): """ - Set up the song builder. + Set up the custom builder. """ # Create the minidom document self.custom_xml = Document() def new_document(self): """ - Create a new song XML document. + Create a new custom XML document. """ # Create the base element self.song = self.custom_xml.createElement(u'song') @@ -72,7 +73,7 @@ class CustomXMLBuilder(object): def add_lyrics_to_song(self): """ Set up and add a ```` tag which contains the lyrics of the - song. + custom item. """ # Create the main element self.lyrics = self.custom_xml.createElement(u'lyrics') @@ -93,7 +94,6 @@ class CustomXMLBuilder(object): ``content`` The actual text of the verse to be stored. """ - #log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content)) verse = self.custom_xml.createElement(u'verse') verse.setAttribute(u'type', type) verse.setAttribute(u'label', number) @@ -102,7 +102,7 @@ class CustomXMLBuilder(object): cds = self.custom_xml.createCDATASection(content) verse.appendChild(cds) - def dump_xml(self): + def _dump_xml(self): """ Debugging aid to dump XML so that we can see what we have. """ @@ -110,29 +110,30 @@ class CustomXMLBuilder(object): def extract_xml(self): """ - Extract our newly created XML song. + Extract our newly created XML custom. """ return self.custom_xml.toxml(u'utf-8') class CustomXMLParser(object): """ - A class to read in and parse a song's XML. + A class to read in and parse a custom's XML. """ log.info(u'CustomXMLParser Loaded') def __init__(self, xml): """ - Set up our song XML parser. + Set up our custom XML parser. ``xml`` - The XML of the song to be parsed. + The XML of the custom to be parsed. """ self.custom_xml = None + if xml[:5] == u' 1: self.DisplayTypeComboBox.insertItem(0, self.Automatic) self.DisplayTypeComboBox.setCurrentIndex(0) - if QtCore.QSettings().value(self.settingsSection + u'/override app', + if QtCore.QSettings().value(self.settingsSection + u'/override app', QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked: self.PresentationWidget.show() else: @@ -238,7 +238,7 @@ class PresentationMediaItem(MediaManagerItem): SettingsManager.set_list(self.settingsSection, self.settingsSection, self.getFileList()) - def generateSlideData(self, service_item, item=None): + def generateSlideData(self, service_item, item=None, xmlVersion=False): """ Load the relevant information for displaying the presentation in the slidecontroller. In the case of powerpoints, an image @@ -277,7 +277,7 @@ class PresentationMediaItem(MediaManagerItem): def findControllerByType(self, filename): """ Determine the default application controller to use for the selected - file type. This is used if "Automatic" is set as the preferred + file type. This is used if "Automatic" is set as the preferred controller. Find the first (alphabetic) enabled controller which "supports" the extension. If none found, then look for a controller which "alsosupports" it instead. diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 241c6fd95..e421e63a0 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -152,6 +152,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): def newSong(self): log.debug(u'New Song') + self.initialise() self.SongTabWidget.setCurrentIndex(0) self.TitleEditItem.setText(u'') self.AlternativeEdit.setText(u'') @@ -170,8 +171,18 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): # it's a new song to preview is not possible self.previewButton.setVisible(False) - def loadSong(self, id, preview): + def loadSong(self, id, preview=False): + """ + Loads a song. + + ``id`` + The song id (int). + + ``preview`` + Should be ``True`` if the song is also previewed (boolean). + """ log.debug(u'Load Song') + self.initialise() self.SongTabWidget.setCurrentIndex(0) self.loadAuthors() self.loadTopics() @@ -594,6 +605,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): """ Save and Preview button pressed. The Song is valid so as the plugin to add it to preview to see. + + ``button`` + A button (QPushButton). """ log.debug(u'onPreview') if unicode(button.objectName()) == u'previewButton' and \ @@ -631,13 +645,16 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): database. ``preview`` - Should be True if song is also previewed. + Should be ``True`` if the song is also previewed (boolean). """ self.song.title = unicode(self.TitleEditItem.text()) self.song.alternate_title = unicode(self.AlternativeEdit.text()) self.song.copyright = unicode(self.CopyrightEditItem.text()) - self.song.search_title = self.song.title + u'@' + \ - self.song.alternate_title + if self.song.alternate_title: + self.song.search_title = self.song.title + u'@' + \ + self.song.alternate_title + else: + self.song.search_title = self.song.title self.song.comments = unicode(self.CommentsEdit.toPlainText()) self.song.verse_order = unicode(self.VerseOrderEdit.text()) self.song.ccli_number = unicode(self.CCLNumberEdit.text()) @@ -648,6 +665,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): Book.name == book_name) else: self.song.book = None + theme_name = unicode(self.ThemeSelectionComboItem.currentText()) + if theme_name: + self.song.theme_name = theme_name + else: + self.song.theme_name = None if self._validate_song(): self.processLyrics() self.processTitle() diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 351d50071..795116b4e 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -62,6 +62,36 @@ class VerseType(object): elif verse_type == VerseType.Other: return translate('SongsPlugin.VerseType', 'Other') + @staticmethod + def expand_string(verse_type): + """ + Return the VerseType for a given string + + ``verse_type`` + The string to return a VerseType for + """ + verse_type = verse_type.lower() + if verse_type == unicode(VerseType.to_string(VerseType.Verse)).lower()[0]: + return translate('SongsPlugin.VerseType', 'Verse') + elif verse_type == \ + unicode(VerseType.to_string(VerseType.Chorus)).lower()[0]: + return translate('SongsPlugin.VerseType', 'Chorus') + elif verse_type == \ + unicode(VerseType.to_string(VerseType.Bridge)).lower()[0]: + return translate('SongsPlugin.VerseType', 'Bridge') + elif verse_type == \ + unicode(VerseType.to_string(VerseType.PreChorus)).lower()[0]: + return translate('SongsPlugin.VerseType', 'PreChorus') + elif verse_type == \ + unicode(VerseType.to_string(VerseType.Intro)).lower()[0]: + return translate('SongsPlugin.VerseType', 'Intro') + elif verse_type == \ + unicode(VerseType.to_string(VerseType.Ending)).lower()[0]: + return translate('SongsPlugin.VerseType', 'Ending') + elif verse_type == \ + unicode(VerseType.to_string(VerseType.Other)).lower()[0]: + return translate('SongsPlugin.VerseType', 'Other') + @staticmethod def from_string(verse_type): """ @@ -92,7 +122,6 @@ class VerseType(object): unicode(VerseType.to_string(VerseType.Other)).lower(): return VerseType.Other - -from xml import LyricsXML, SongXMLBuilder, SongXMLParser +from xml import LyricsXML, SongXMLBuilder, SongXMLParser, OpenLyricsParser from songstab import SongsTab from mediaitem import SongMediaItem diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 63496affd..432eee744 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -32,7 +32,7 @@ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \ ItemCapabilities, translate, check_item_selected from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \ SongImportForm -from openlp.plugins.songs.lib import SongXMLParser +from openlp.plugins.songs.lib import SongXMLParser, OpenLyricsParser from openlp.plugins.songs.lib.db import Author, Song log = logging.getLogger(__name__) @@ -53,8 +53,8 @@ class SongMediaItem(MediaManagerItem): self.ListViewWithDnD_class = SongListView MediaManagerItem.__init__(self, parent, self, icon) self.edit_song_form = EditSongForm(self, self.parent.manager) + self.openLyrics = OpenLyricsParser(self.parent.manager) self.singleServiceItem = False - #self.edit_song_form = EditSongForm(self.parent.manager, self) self.song_maintenance_form = SongMaintenanceForm( self.parent.manager, self) # Holds information about whether the edit is remotly triggered and @@ -114,6 +114,8 @@ class SongMediaItem(MediaManagerItem): self.SearchButtonLayout.addWidget(self.ClearTextButton) self.pageLayout.addLayout(self.SearchButtonLayout) # Signals and slots + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'plugin_list_refresh'), self.onSearchTextButtonClick) QtCore.QObject.connect(self.SearchTextEdit, QtCore.SIGNAL(u'returnPressed()'), self.onSearchTextButtonClick) QtCore.QObject.connect(self.SearchTextButton, @@ -141,7 +143,7 @@ class SongMediaItem(MediaManagerItem): self.updateServiceOnEdit = QtCore.QSettings().value( self.settingsSection + u'/update service on edit', QtCore.QVariant(u'False')).toBool() - self.AddSongFromServide = QtCore.QSettings().value( + self.addSongFromService = QtCore.QSettings().value( self.settingsSection + u'/add song from service', QtCore.QVariant(u'True')).toBool() @@ -192,6 +194,7 @@ class SongMediaItem(MediaManagerItem): Handle the exit from the edit dialog and trigger remote updates of songs """ + log.debug(u'onSongListLoad') # Called to redisplay the song list screen edit from a search # or from the exit of the Song edit dialog. If remote editing is active # Trigger it and clean up so it will not update again. @@ -259,6 +262,7 @@ class SongMediaItem(MediaManagerItem): Receiver.send_message(u'songs_load_list') def onNewClick(self): + log.debug(u'onNewClick') self.edit_song_form.newSong() self.edit_song_form.exec_() @@ -266,6 +270,7 @@ class SongMediaItem(MediaManagerItem): self.song_maintenance_form.exec_() def onRemoteEditClear(self): + log.debug(u'onRemoteEditClear') self.remoteTriggered = None self.remoteSong = -1 @@ -275,6 +280,7 @@ class SongMediaItem(MediaManagerItem): the Song Id in the payload along with an indicator to say which type of display is required. """ + log.debug(u'onRemoteEdit %s' % songid) fields = songid.split(u':') valid = self.parent.manager.get_object(Song, fields[1]) if valid: @@ -287,6 +293,7 @@ class SongMediaItem(MediaManagerItem): """ Edit a song """ + log.debug(u'onEditClick') if check_item_selected(self.listView, translate('SongsPlugin.MediaItem', 'You must select an item to edit.')): @@ -323,7 +330,8 @@ class SongMediaItem(MediaManagerItem): self.parent.manager.delete_object(Song, item_id) self.onSearchTextButtonClick() - def generateSlideData(self, service_item, item=None): + def generateSlideData(self, service_item, item=None, xmlVersion=False): + log.debug(u'generateSlideData (%s:%s)' % (service_item, item)) raw_footer = [] author_list = u'' author_audit = [] @@ -345,11 +353,11 @@ class SongMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.AddIfNewItem) song = self.parent.manager.get_object(Song, item_id) service_item.theme = song.theme_name - service_item.editId = item_id + service_item.edit_id = item_id if song.lyrics.startswith(u'' + \ u'%s' % lyrics_output return song_output + + +class OpenLyricsParser(object): + """ + This class represents the converter for Song to/from OpenLyrics XML. + """ + def __init__(self, manager): + self.manager = manager + + def song_to_xml(self, song): + """ + Convert the song to OpenLyrics Format + """ + song_xml_parser = SongXMLParser(song.lyrics) + verse_list = song_xml_parser.get_verses() + song_xml = objectify.fromstring( + u'') + properties = etree.SubElement(song_xml, u'properties') + titles = etree.SubElement(properties, u'titles') + self._add_text_to_element(u'title', titles, song.title) + if song.alternate_title: + self._add_text_to_element(u'title', titles, song.alternate_title) + if song.theme_name: + themes = etree.SubElement(properties, u'themes') + self._add_text_to_element(u'theme', themes, song.theme_name) + self._add_text_to_element(u'copyright', properties, song.copyright) + self._add_text_to_element(u'verseOrder', properties, song.verse_order) + if song.ccli_number: + self._add_text_to_element(u'ccliNo', properties, song.ccli_number) + authors = etree.SubElement(properties, u'authors') + for author in song.authors: + self._add_text_to_element(u'author', authors, author.display_name) + lyrics = etree.SubElement(song_xml, u'lyrics') + for verse in verse_list: + verse_tag = u'%s%s' % ( + verse[0][u'type'][0].lower(), verse[0][u'label']) + element = \ + self._add_text_to_element(u'verse', lyrics, None, verse_tag) + 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) + return self._extract_xml(song_xml) + + def xml_to_song(self, xml): + """ + Create a Song from OpenLyrics format xml + """ + # No xml get out of here + if not xml: + return 0 + song = Song() + if xml[:5] == u'