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/documentation/api/source/core/theme.rst b/documentation/api/source/core/theme.rst index 3dbc7a6ec..3621c6581 100644 --- a/documentation/api/source/core/theme.rst +++ b/documentation/api/source/core/theme.rst @@ -1,8 +1,10 @@ .. _core-theme: -:mod:`theme` Module -=================== +Theme Function Library +====================== .. automodule:: openlp.core.theme :members: +.. autoclass:: openlp.core.theme.theme.Theme + :members: diff --git a/documentation/api/source/openlp.rst b/documentation/api/source/openlp.rst deleted file mode 100644 index 76a1a2098..000000000 --- a/documentation/api/source/openlp.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _openlp: - -:mod:`openlp` Module -==================== - -.. automodule:: openlp - :members: diff --git a/documentation/api/source/plugins/bibles.rst b/documentation/api/source/plugins/bibles.rst index 67162d414..c89f9c6ae 100644 --- a/documentation/api/source/plugins/bibles.rst +++ b/documentation/api/source/plugins/bibles.rst @@ -18,7 +18,7 @@ Forms .. automodule:: openlp.plugins.bibles.forms :members: -.. autoclass:: openlp.plugins.bibles.forms.importwizardform.ImportWizardForm +.. autoclass:: openlp.plugins.bibles.forms.bibleimportform.BibleImportForm :members: Helper Classes & Functions diff --git a/documentation/manual/source/conf.py b/documentation/manual/source/conf.py index 517fc2f44..f0d918c11 100644 --- a/documentation/manual/source/conf.py +++ b/documentation/manual/source/conf.py @@ -48,7 +48,7 @@ copyright = u'2004-2010 Raoul Snyman' # The short X.Y version. version = '2.0' # The full version, including alpha/beta/rc tags. -release = '1.9.3' +release = '1.9.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -92,22 +92,24 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. +#html_theme = 'openlp_qthelp' html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -html_theme_options = { - 'sidebarbgcolor': '#3a60a9', - 'relbarbgcolor': '#203b6f', - 'footerbgcolor': '#26437c', - 'headtextcolor': '#203b6f', - 'linkcolor': '#26437c', - 'sidebarlinkcolor': '#ceceff' -} +if html_theme == 'default': + html_theme_options = { + 'sidebarbgcolor': '#3a60a9', + 'relbarbgcolor': '#203b6f', + 'footerbgcolor': '#26437c', + 'headtextcolor': '#203b6f', + 'linkcolor': '#26437c', + 'sidebarlinkcolor': '#ceceff' + } # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +html_theme_path = [u'../themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". diff --git a/documentation/manual/source/dualmonitors.rst b/documentation/manual/source/dualmonitors.rst index 7e5fdc19b..ee4bc90a2 100644 --- a/documentation/manual/source/dualmonitors.rst +++ b/documentation/manual/source/dualmonitors.rst @@ -149,15 +149,15 @@ Or, as root:: root@linux: # nividia-settings If you do not want to write the changes to your ``xorg.conf`` file simply run -the nVidia Settings program (``nvidia-settings``) from your desktop's menu, -usually in an administration or system menu, or from the terminal as a normal -user run:: +the nVidia Settings program (:command:`nvidia-settings`) from your desktop's +menu, usually in an administration or system menu, or from the terminal as a +normal user run:: user@linux:~ $ nvidia-settings -Once you have opened nVidia Settings, click on -:guilabel:`X Server Display Configuration`. Then select the monitor you are -wanting to use as your second monitor and click :guilabel:`Configure`. +Once you have opened nVidia Settings, click on :guilabel:`X Server Display +Configuration`. Then select the monitor you are wanting to use as your second +monitor and click :guilabel:`Configure`. .. image:: pics/nvlinux1.png diff --git a/documentation/manual/source/index.rst b/documentation/manual/source/index.rst index 5786af1ae..378cfe2a0 100644 --- a/documentation/manual/source/index.rst +++ b/documentation/manual/source/index.rst @@ -17,10 +17,3 @@ Contents: mediamanager songs -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/documentation/manual/themes/openlp_qthelp/layout.html b/documentation/manual/themes/openlp_qthelp/layout.html new file mode 100644 index 000000000..d16116773 --- /dev/null +++ b/documentation/manual/themes/openlp_qthelp/layout.html @@ -0,0 +1,68 @@ +{# + openlp_qthelp/layout.html + ~~~~~~~~~~~~~~~~~ + + Sphinx layout template for the openlp_qthelp theme. + + :copyright: Copyright 2004-2010 Raoul Snyman. + :license: GPL +#} +{% extends "basic/layout.html" %} +{% set script_files = script_files + ['_static/theme_extras.js'] %} +{% set css_files = css_files + ['_static/print.css'] %} + +{# do not display relbars #} +{% block relbar1 %}{% endblock %} +{% block relbar2 %}{% endblock %} + +{% macro nav() %} +

+ {%- block openlp_qthelprel1 %} + {%- endblock %} + {%- if prev %} + «  {{ prev.title }} +   ::   + {%- endif %} + {{ _('Contents') }} + {%- if next %} +   ::   + {{ next.title }}  » + {%- endif %} + {%- block openlp_qthelprel2 %} + {%- endblock %} +

+{% endmacro %} + +{% block content %} + +
+ {{ nav() }} +
+
+ {#{%- if display_toc %} +
+

Table Of Contents

+ {{ toc }} +
+ {%- endif %}#} + {% block body %}{% endblock %} +
+
+ {{ nav() }} +
+{% endblock %} diff --git a/documentation/manual/themes/openlp_qthelp/static/alert_info_32.png b/documentation/manual/themes/openlp_qthelp/static/alert_info_32.png new file mode 100644 index 000000000..05b4fe898 Binary files /dev/null and b/documentation/manual/themes/openlp_qthelp/static/alert_info_32.png differ diff --git a/documentation/manual/themes/openlp_qthelp/static/alert_warning_32.png b/documentation/manual/themes/openlp_qthelp/static/alert_warning_32.png new file mode 100644 index 000000000..f13611cde Binary files /dev/null and b/documentation/manual/themes/openlp_qthelp/static/alert_warning_32.png differ diff --git a/documentation/manual/themes/openlp_qthelp/static/bg-page.png b/documentation/manual/themes/openlp_qthelp/static/bg-page.png new file mode 100644 index 000000000..c6f3bc477 Binary files /dev/null and b/documentation/manual/themes/openlp_qthelp/static/bg-page.png differ diff --git a/documentation/manual/themes/openlp_qthelp/static/bullet_orange.png b/documentation/manual/themes/openlp_qthelp/static/bullet_orange.png new file mode 100644 index 000000000..ad5d02f34 Binary files /dev/null and b/documentation/manual/themes/openlp_qthelp/static/bullet_orange.png differ diff --git a/documentation/manual/themes/openlp_qthelp/static/openlp_qthelp.css_t b/documentation/manual/themes/openlp_qthelp/static/openlp_qthelp.css_t new file mode 100644 index 000000000..918ed661b --- /dev/null +++ b/documentation/manual/themes/openlp_qthelp/static/openlp_qthelp.css_t @@ -0,0 +1,372 @@ +/* + * openlp_qthelp.css_t + * ~~~~~~~~~~~ + * + * Sphinx stylesheet -- openlp_qthelp theme. + * + * Adapted from http://openlp_qthelp-os.org/docs/Haiku-doc.css. + * Original copyright message: + * + * Copyright 2008-2009, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Francois Revol + * Stephan Assmus + * Braden Ewing + * Humdinger + * + * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +@import url("basic.css"); + +html { + margin: 0px; + padding: 0px; + background-color: #fff; + background-image: none; +} + +body { + line-height: 1.5; + margin: auto; + padding: 0px; + font-family: "DejaVu Sans", Arial, Helvetica, sans-serif; + min-width: 59em; + max-width: 70em; + color: {{ theme_textcolor }}; +} + +div.footer { + padding: 8px; + font-size: 11px; + text-align: center; + letter-spacing: 0.5px; +} + +/* link colors and text decoration */ + +a:link { + font-weight: bold; + text-decoration: none; + color: {{ theme_linkcolor }}; +} + +a:visited { + font-weight: bold; + text-decoration: none; + color: {{ theme_visitedlinkcolor }}; +} + +a:hover, a:active { + text-decoration: underline; + color: {{ theme_hoverlinkcolor }}; +} + +/* Some headers act as anchors, don't give them a hover effect */ + +h1 a:hover, a:active { + text-decoration: none; + color: {{ theme_headingcolor }}; +} + +h2 a:hover, a:active { + text-decoration: none; + color: {{ theme_headingcolor }}; +} + +h3 a:hover, a:active { + text-decoration: none; + color: {{ theme_headingcolor }}; +} + +h4 a:hover, a:active { + text-decoration: none; + color: {{ theme_headingcolor }}; +} + +a.headerlink { + color: #a7ce38; + padding-left: 5px; +} + +a.headerlink:hover { + color: #a7ce38; +} + +/* basic text elements */ + +div.content { + margin-top: 20px; + margin-left: 40px; + margin-right: 40px; + margin-bottom: 50px; + font-size: 0.9em; +} + +/* heading and navigation */ + +div.header { + position: relative; + left: 0px; + top: 0px; + height: 85px; + /* background: #eeeeee; */ + padding: 0 40px; +} +div.header h1 { + font-size: 1.6em; + font-weight: normal; + letter-spacing: 1px; + color: {{ theme_headingcolor }}; + border: 0; + margin: 0; + padding-top: 15px; +} +div.header h1 a { + font-weight: normal; + color: {{ theme_headingcolor }}; +} +div.header h2 { + font-size: 1.3em; + font-weight: normal; + letter-spacing: 1px; + text-transform: uppercase; + color: #aaa; + border: 0; + margin-top: -3px; + padding: 0; +} + +div.header img.rightlogo { + float: right; +} + + +div.title { + font-size: 1.3em; + font-weight: bold; + color: {{ theme_headingcolor }}; + border-bottom: dotted thin #e0e0e0; + margin-bottom: 25px; +} +div.topnav { + /* background: #e0e0e0; */ +} +div.topnav p { + margin-top: 0; + margin-left: 40px; + margin-right: 40px; + margin-bottom: 0px; + text-align: right; + font-size: 0.8em; +} +div.bottomnav { + background: #eeeeee; +} +div.bottomnav p { + margin-right: 40px; + text-align: right; + font-size: 0.8em; +} + +a.uplink { + font-weight: normal; +} + + +/* contents box */ + +table.index { + margin: 0px 0px 30px 30px; + padding: 1px; + border-width: 1px; + border-style: dotted; + border-color: #e0e0e0; +} +table.index tr.heading { + background-color: #e0e0e0; + text-align: center; + font-weight: bold; + font-size: 1.1em; +} +table.index tr.index { + background-color: #eeeeee; +} +table.index td { + padding: 5px 20px; +} + +table.index a:link, table.index a:visited { + font-weight: normal; + text-decoration: none; + color: {{ theme_linkcolor }}; +} +table.index a:hover, table.index a:active { + text-decoration: underline; + color: {{ theme_hoverlinkcolor }}; +} + + +/* Haiku User Guide styles and layout */ + +/* Rounded corner boxes */ +/* Common declarations */ +div.admonition { + -webkit-border-radius: 10px; + -khtml-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + border-style: dotted; + border-width: thin; + border-color: #dcdcdc; + padding: 10px 15px 10px 15px; + margin-bottom: 15px; + margin-top: 15px; +} +div.note { + padding: 10px 15px 10px 80px; + background: #e4ffde url(alert_info_32.png) 15px 15px no-repeat; + min-height: 42px; +} +div.warning { + padding: 10px 15px 10px 80px; + background: #fffbc6 url(alert_warning_32.png) 15px 15px no-repeat; + min-height: 42px; +} +div.seealso { + background: #e4ffde; +} + +/* More layout and styles */ +h1 { + font-size: 1.3em; + font-weight: bold; + color: {{ theme_headingcolor }}; + border-bottom: dotted thin #e0e0e0; + margin-top: 30px; +} + +h2 { + font-size: 1.2em; + font-weight: normal; + color: {{ theme_headingcolor }}; + border-bottom: dotted thin #e0e0e0; + margin-top: 30px; +} + +h3 { + font-size: 1.1em; + font-weight: normal; + color: {{ theme_headingcolor }}; + margin-top: 30px; +} + +h4 { + font-size: 1.0em; + font-weight: normal; + color: {{ theme_headingcolor }}; + margin-top: 30px; +} + +p { + text-align: justify; +} + +p.last { + margin-bottom: 0; +} + +ol { + padding-left: 20px; +} + +ul { + padding-left: 5px; + margin-top: 3px; +} + +li { + line-height: 1.3; +} + +div.content ul > li { + -moz-background-clip:border; + -moz-background-inline-policy:continuous; + -moz-background-origin:padding; + background: transparent url(bullet_orange.png) no-repeat scroll left 0.45em; + list-style-image: none; + list-style-type: none; + padding: 0 0 0 1.666em; + margin-bottom: 3px; +} + +td { + vertical-align: top; +} + +tt { + background-color: #e2e2e2; + font-size: 1.0em; + font-family: monospace; +} + +pre { + border-color: #0c3762; + border-style: dotted; + border-width: thin; + margin: 0 0 12px 0; + padding: 0.8em; + background-color: #f0f0f0; +} + +hr { + border-top: 1px solid #ccc; + border-bottom: 0; + border-right: 0; + border-left: 0; + margin-bottom: 10px; + margin-top: 20px; +} + +/* printer only pretty stuff */ +@media print { + .noprint { + display: none; + } + /* for acronyms we want their definitions inlined at print time */ + acronym[title]:after { + font-size: small; + content: " (" attr(title) ")"; + font-style: italic; + } + /* and not have mozilla dotted underline */ + acronym { + border: none; + } + div.topnav, div.bottomnav, div.header, table.index { + display: none; + } + div.content { + margin: 0px; + padding: 0px; + } + html { + background: #FFF; + } +} + +.viewcode-back { + font-family: "DejaVu Sans", Arial, Helvetica, sans-serif; +} + +div.viewcode-block:target { + background-color: #f4debf; + border-top: 1px solid #ac9; + border-bottom: 1px solid #ac9; + margin: -1px -12px; + padding: 0 12px; +} diff --git a/documentation/manual/themes/openlp_qthelp/theme.conf b/documentation/manual/themes/openlp_qthelp/theme.conf new file mode 100644 index 000000000..0d44b0faa --- /dev/null +++ b/documentation/manual/themes/openlp_qthelp/theme.conf @@ -0,0 +1,12 @@ +[theme] +inherit = basic +stylesheet = openlp_qthelp.css +pygments_style = autumn + +[options] +full_logo = false +textcolor = #333333 +headingcolor = #203b6f +linkcolor = #26437c +visitedlinkcolor = #26437c +hoverlinkcolor = #26437c diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 33280f83b..80bf4a67b 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -239,7 +239,8 @@ def resize_image(image, width, height, background=QtCore.Qt.black): Resize an image to fit on the current screen. ``image`` - The image to resize. + The image to resize. It has to be either a ``QImage`` instance or the + path to the image. ``width`` The new image width. @@ -319,6 +320,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 +341,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/db.py b/openlp/core/lib/db.py index 3171730ea..d9d094949 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -86,10 +86,10 @@ class BaseModel(object): """ Creates an instance of a class and populates it, returning the instance """ - me = cls() + instance = cls() for key in kwargs: - me.__setattr__(key, kwargs[key]) - return me + instance.__setattr__(key, kwargs[key]) + return instance class Manager(object): """ diff --git a/openlp/core/lib/displaytags.py b/openlp/core/lib/displaytags.py index dd7276a7d..d414f80bb 100644 --- a/openlp/core/lib/displaytags.py +++ b/openlp/core/lib/displaytags.py @@ -60,8 +60,8 @@ class DisplayTags(object): DisplayTags.html_expands.append(tag) @staticmethod - def remove_html_tag(id): + def remove_html_tag(tag_id): """ - Removes amd individual html_expands list. + Removes an individual html_expands tag. """ - DisplayTags.html_expands.pop(id) + DisplayTags.html_expands.pop(tag_id) 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/imagemanager.py b/openlp/core/lib/imagemanager.py index 02d7010be..0a76ce834 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -61,10 +61,10 @@ class Image(object): image = None image_bytes = None + class ImageManager(QtCore.QObject): """ Image Manager handles the conversion and sizing of images. - """ log.info(u'Image Manager loaded') @@ -85,8 +85,7 @@ class ImageManager(QtCore.QObject): for key in self._cache.keys(): image = self._cache[key] image.dirty = True - image.image = resize_image(image.path, - self.width, self.height) + image.image = resize_image(image.path, self.width, self.height) self._cache_dirty = True # only one thread please if not self._thread_running: @@ -128,8 +127,7 @@ class ImageManager(QtCore.QObject): image = Image() image.name = name image.path = path - image.image = resize_image(path, - self.width, self.height) + image.image = resize_image(path, self.width, self.height) self._cache[name] = image else: log.debug(u'Image in cache %s:%s' % (name, path)) 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..d4fdfff17 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( @@ -275,7 +252,6 @@ class MediaManagerItem(QtGui.QWidget): self.pageLayout.addWidget(self.listView) # define and add the context menu self.listView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) - name_string = self.plugin.getString(StringContent.Name) if self.hasEditIcon: self.listView.addAction( context_menu_action( @@ -500,7 +476,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 fb31006b5..730bb1a36 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -31,6 +31,7 @@ import logging from PyQt4 import QtCore from openlp.core.lib import Receiver +from openlp.core.lib.ui import UiStrings log = logging.getLogger(__name__) @@ -44,6 +45,9 @@ class PluginStatus(object): class StringContent(object): + """ + Provide standard strings for objects to use. + """ Name = u'name' Import = u'import' Load = u'load' @@ -110,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* @@ -128,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 @@ -137,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 @@ -195,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): """ @@ -226,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): """ @@ -316,8 +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 """ - pass + ## Load Action ## + self.__setNameTextString(StringContent.Load, + UiStrings.Load, tooltips[u'load']) + ## Import Action ## + self.__setNameTextString(StringContent.Import, + UiStrings.Import, tooltips[u'import']) + ## New Action ## + self.__setNameTextString(StringContent.New, + UiStrings.Add, tooltips[u'new']) + ## Edit Action ## + self.__setNameTextString(StringContent.Edit, + UiStrings.Edit, tooltips[u'edit']) + ## Delete Action ## + self.__setNameTextString(StringContent.Delete, + UiStrings.Delete, tooltips[u'delete']) + ## Preview Action ## + self.__setNameTextString(StringContent.Preview, + UiStrings.Preview, tooltips[u'preview']) + ## Send Live Action ## + self.__setNameTextString(StringContent.Live, + UiStrings.Live, tooltips[u'live']) + ## Add to Service Action ## + self.__setNameTextString(StringContent.Service, + UiStrings.Service, tooltips[u'service']) + + 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} diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py index 5896ca4e6..860a52b60 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): @@ -204,12 +203,12 @@ class RenderManager(object): # 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' \ - 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \ - 'The Lord said to {g}Noah{/g}:\n' \ - 'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \ - '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' + 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \ + 'The Lord said to {g}Noah{/g}:\n' \ + 'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \ + '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 @@ -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..f9d690ba2 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,24 @@ 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 + """ + 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 d119f19ff..225e1335c 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 == HorizontalType.Right: + return u'right' + elif horizontal_type == HorizontalType.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'] @@ -583,8 +610,7 @@ class ThemeXML(object): self.background_end_color, self.background_direction) else: - filename = \ - os.path.split(self.background_filename)[1] + filename = os.path.split(self.background_filename)[1] self.add_background_image(filename) self.add_font(self.font_main_name, self.font_main_color, diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 4a346b3e1..a98e2fb7f 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -34,6 +34,39 @@ from openlp.core.lib import build_icon, Receiver, translate log = logging.getLogger(__name__) +class UiStrings(object): + """ + Provide standard strings for objects to use. + """ + # 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') + 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') + Edit = translate('OpenLP.Ui', '&Edit') + Error = translate('OpenLP.Ui', 'Error') + Import = translate('OpenLP.Ui', 'Import') + LengthTime = unicode(translate('OpenLP.Ui', 'Length %s')) + Live = translate('OpenLP.Ui', 'Live') + Load = translate('OpenLP.Ui', 'Load') + New = translate('OpenLP.Ui', 'New') + NewService = translate('OpenLP.Ui', 'New Service') + OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') + OpenService = translate('OpenLP.Ui', 'Open Service') + Preview = translate('OpenLP.Ui', 'Preview') + 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. @@ -61,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) @@ -98,13 +138,12 @@ def critical_error_message_box(title=None, message=None, parent=None, ``question`` Should this message box question the user. """ - error = translate('OpenLP.Ui', 'Error') if question: - return QtGui.QMessageBox.critical(parent, error, message, + return QtGui.QMessageBox.critical(parent, UiStrings.Error, message, QtGui.QMessageBox.StandardButtons( QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) data = {u'message': message} - data[u'title'] = title if title else error + data[u'title'] = title if title else UiStrings.Error return Receiver.send_message(u'openlp_error_message', data) def media_item_combo_box(parent, name): @@ -134,7 +173,7 @@ def create_delete_push_button(parent, icon=None): delete_button.setObjectName(u'deleteButton') delete_icon = icon if icon else u':/general/general_delete.png' delete_button.setIcon(build_icon(delete_icon)) - delete_button.setText(translate('OpenLP.Ui', '&Delete')) + delete_button.setText(UiStrings.Delete) delete_button.setToolTip( translate('OpenLP.Ui', 'Delete the selected item.')) QtCore.QObject.connect(delete_button, @@ -219,3 +258,28 @@ def add_widget_completer(cache, widget): completer = QtGui.QCompleter(cache) completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) widget.setCompleter(completer) + +def create_valign_combo(form, parent, layout): + """ + Creates a standard label and combo box for asking users to select a + vertical alignment. + + ``form`` + The UI screen that the label and combo will appear on. + + ``parent`` + The parent object. This should be a ``QWidget`` descendant. + + ``layout`` + A layout object to add the label and combo widgets to. + """ + verticalLabel = QtGui.QLabel(parent) + verticalLabel.setObjectName(u'VerticalLabel') + verticalLabel.setText(translate('OpenLP.Ui', '&Vertical Align:')) + form.verticalComboBox = QtGui.QComboBox(parent) + form.verticalComboBox.setObjectName(u'VerticalComboBox') + form.verticalComboBox.addItem(translate('OpenLP.Ui', 'Top')) + form.verticalComboBox.addItem(translate('OpenLP.Ui', 'Middle')) + form.verticalComboBox.addItem(translate('OpenLP.Ui', 'Bottom')) + verticalLabel.setBuddy(form.verticalComboBox) + layout.addRow(verticalLabel, form.verticalComboBox) diff --git a/openlp/core/theme/theme.py b/openlp/core/theme/theme.py index e506fc2c2..c5d60e299 100644 --- a/openlp/core/theme/theme.py +++ b/openlp/core/theme/theme.py @@ -33,11 +33,14 @@ processing version 1 themes in OpenLP version 2. from xml.etree.ElementTree import ElementTree, XML from PyQt4 import QtGui -DELPHI_COLORS = {u'clRed': 0xFF0000, - u'clBlue': 0x0000FF, - u'clYellow': 0xFFFF00, - u'clBlack': 0x000000, - u'clWhite': 0xFFFFFF} +DELPHI_COLORS = { + u'clAqua': 0x00FFFF, u'clBlack': 0x000000, u'clBlue': 0x0000FF, + u'clFuchsia': 0xFF00FF, u'clGray': 0x808080, u'clGreen': 0x008000, + u'clLime': 0x00FF00, u'clMaroon': 0x800000, u'clNavy': 0x000080, + u'clOlive': 0x808000, u'clPurple': 0x800080, u'clRed': 0xFF0000, + u'clSilver': 0xC0C0C0, u'clTeal': 0x008080, u'clWhite': 0xFFFFFF, + u'clYellow': 0xFFFF00 +} BLANK_STYLE_XML = \ ''' @@ -68,38 +71,45 @@ class Theme(object): Theme name ``BackgroundMode`` - The behaviour of the background. Valid modes are: - - 0 - Transparent - - 1 - Opaque + The behaviour of the background. Valid modes are: + + * ``0`` - Transparent + * ``1`` - Opaque ``BackgroundType`` - The content of the background. Valid types are: - - 0 - solid color - - 1 - gradient color - - 2 - image + The content of the background. Valid types are: + + * ``0`` - solid color + * ``1`` - gradient color + * ``2`` - image ``BackgroundParameter1`` - Extra information about the background. The contents of this attribute + Extra information about the background. The contents of this attribute depend on the BackgroundType: - - image: image filename - - gradient: start color - - solid: color + + * ``image`` - image filename + * ``gradient`` - start color + * ``solid`` - color ``BackgroundParameter2`` Extra information about the background. The contents of this attribute depend on the BackgroundType: - - image: border color - - gradient: end color - - solid: N/A + + * ``image`` - border color + * ``gradient`` - end color + * ``solid`` - N/A ``BackgroundParameter3`` Extra information about the background. The contents of this attribute depend on the BackgroundType: - - image: N/A - - gradient: The direction of the gradient. Valid entries are: - - 0 -> vertical - - 1 -> horizontal - - solid: N/A + + * ``image`` - N/A + * ``gradient`` - The direction of the gradient. Valid entries are: + + * ``0`` - vertical + * ``1`` - horizontal + + * ``solid`` - N/A ``FontName`` Name of the font to use for the main font. @@ -115,36 +125,41 @@ class Theme(object): ``Shadow`` The shadow type to apply to the main font. - - 0 - no shadow - - non-zero - use shadow + + * ``0`` - no shadow + * non-zero - use shadow ``ShadowColor`` Color for the shadow ``Outline`` The outline to apply to the main font - - 0 - no outline - - non-zero - use outline + + * ``0`` - no outline + * non-zero - use outline ``OutlineColor`` Color for the outline (or None if Outline is 0) ``HorizontalAlign`` The horizontal alignment to apply to text. Valid alignments are: - - 0 - left align - - 1 - right align - - 2 - centre align + + * ``0`` - left align + * ``1`` - right align + * ``2`` - centre align ``VerticalAlign`` The vertical alignment to apply to the text. Valid alignments are: - - 0 - top align - - 1 - bottom align - - 2 - centre align + + * ``0`` - top align + * ``1`` - bottom align + * ``2`` - centre align ``WrapStyle`` The wrap style to apply to the text. Valid styles are: - - 0 - normal - - 1 - lyrics + + * ``0`` - normal + * ``1`` - lyrics """ def __init__(self, xml): """ @@ -184,7 +199,6 @@ class Theme(object): if element.tag != u'Theme': element_text = element.text val = 0 - # easy! if element_text is None: val = element_text # strings need special handling to sort the colours out 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 f68131894..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): """ @@ -80,16 +81,6 @@ class AdvancedTab(SettingsTab): self.hideMouseCheckBox.setObjectName(u'hideMouseCheckBox') self.hideMouseLayout.addWidget(self.hideMouseCheckBox) self.leftLayout.addWidget(self.hideMouseGroupBox) - self.serviceOrderGroupBox = QtGui.QGroupBox(self.leftColumn) - self.serviceOrderGroupBox.setObjectName(u'serviceOrderGroupBox') - self.serviceOrderLayout = QtGui.QVBoxLayout(self.serviceOrderGroupBox) - self.serviceOrderLayout.setObjectName(u'serviceOrderLayout') - self.detailedServicePrintCheckBox = QtGui.QCheckBox( - self.serviceOrderGroupBox) - self.detailedServicePrintCheckBox.setObjectName( - u'detailedServicePrintCheckBox') - self.serviceOrderLayout.addWidget(self.detailedServicePrintCheckBox) - self.leftLayout.addWidget(self.serviceOrderGroupBox) # self.sharedDirGroupBox = QtGui.QGroupBox(self.leftColumn) # self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox') # self.sharedDirLayout = QtGui.QFormLayout(self.sharedDirGroupBox) @@ -122,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', @@ -139,11 +130,6 @@ class AdvancedTab(SettingsTab): 'Mouse Cursor')) self.hideMouseCheckBox.setText(translate('OpenLP.AdvancedTab', 'Hide the mouse cursor when moved over the display window')) - self.serviceOrderGroupBox.setTitle(translate('OpenLP.AdvancedTab', - 'Service Order Print')) - self.detailedServicePrintCheckBox.setText( - translate('OpenLP.AdvancedTab', - 'Print slide texts and service item notes as well')) # self.sharedDirGroupBox.setTitle( # translate('AdvancedTab', 'Central Data Store')) # self.sharedCheckBox.setText( @@ -179,8 +165,6 @@ class AdvancedTab(SettingsTab): QtCore.QVariant(True)).toBool()) self.hideMouseCheckBox.setChecked( settings.value(u'hide mouse', QtCore.QVariant(False)).toBool()) - self.detailedServicePrintCheckBox.setChecked(settings.value( - u'detailed service print', QtCore.QVariant(False)).toBool()) settings.endGroup() def save(self): @@ -201,8 +185,6 @@ class AdvancedTab(SettingsTab): QtCore.QVariant(self.enableAutoCloseCheckBox.isChecked())) settings.setValue(u'hide mouse', QtCore.QVariant(self.hideMouseCheckBox.isChecked())) - settings.setValue(u'detailed service print', - QtCore.QVariant(self.detailedServicePrintCheckBox.isChecked())) settings.endGroup() # def onSharedCheckBoxChanged(self, checked): diff --git a/openlp/core/ui/displaytagtab.py b/openlp/core/ui/displaytagtab.py index 983bc17a8..abf0ca44f 100644 --- a/openlp/core/ui/displaytagtab.py +++ b/openlp/core/ui/displaytagtab.py @@ -23,27 +23,27 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -''' +""" The :mod:`DisplayTagTab` provides an Tag Edit facility. The Base set are protected and included each time loaded. Custom tags can be defined and saved. The Custom Tag arrays are saved in a pickle so QSettings works on them. Base Tags cannot be changed. - -''' +""" import cPickle + from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, translate, DisplayTags -from openlp.core.lib.ui import critical_error_message_box +from openlp.core.lib.ui import UiStrings, critical_error_message_box class DisplayTagTab(SettingsTab): - ''' + """ The :class:`DisplayTagTab` manages the settings tab . - ''' + """ def __init__(self): - ''' + """ Initialise the settings tab - ''' + """ SettingsTab.__init__(self, u'Display Tags') def resizeEvent(self, event=None): @@ -67,9 +67,9 @@ class DisplayTagTab(SettingsTab): self.selected = -1 def setupUi(self): - ''' + """ Configure the UI elements for the tab. - ''' + """ self.setObjectName(u'DisplayTagTab') self.tabTitleVisible = \ translate(u'OpenLP.DisplayTagTab', 'Display Tags') @@ -164,8 +164,7 @@ class DisplayTagTab(SettingsTab): self.startTagLabel.setText( translate('OpenLP.DisplayTagTab', 'Start tag')) self.endTagLabel.setText(translate('OpenLP.DisplayTagTab', 'End tag')) - self.deletePushButton.setText( - translate('OpenLP.DisplayTagTab', 'Delete')) + self.deletePushButton.setText(UiStrings.Delete) self.defaultPushButton.setText( translate('OpenLP.DisplayTagTab', 'Default')) self.newPushButton.setText(translate('OpenLP.DisplayTagTab', 'New')) 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 3dfde8640..90042f80b 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -72,7 +72,7 @@ class MainDisplay(DisplayWidget): self.setWindowIcon(mainIcon) self.retranslateUi() self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;') - self.setWindowFlags(QtCore.Qt.FramelessWindowHint | + self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint) if self.isLive: QtCore.QObject.connect(Receiver.get_receiver(), @@ -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']: @@ -326,8 +329,7 @@ class MainDisplay(DisplayWidget): vol = float(volume)/float(10) if isBackground or not self.usePhonon: js = u'show_video("init", "%s", %s, true); show_video("play");' % \ - (videoPath.replace(u'\\', u'\\\\'), \ - str(vol)) + (videoPath.replace(u'\\', u'\\\\'), str(vol)) self.frame.evaluateJavaScript(js) else: self.phononActive = True @@ -342,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 @@ -398,8 +407,7 @@ class MainDisplay(DisplayWidget): if u'video' in self.override: Receiver.send_message(u'video_background_replaced') self.override = {} - elif self.override[u'theme'] != \ - serviceItem.themedata.theme_name: + elif self.override[u'theme'] != serviceItem.themedata.theme_name: Receiver.send_message(u'live_theme_changed') self.override = {} else: @@ -408,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 9af4931fb..c691c006e 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -30,7 +30,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \ SettingsManager, PluginManager, Receiver, translate -from openlp.core.lib.ui import base_action, checkable_action, icon_action +from openlp.core.lib.ui import UiStrings, base_action, checkable_action, \ + icon_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ ThemeManager, SlideController, PluginForm, MediaDockManager, \ ShortcutListForm @@ -77,10 +78,10 @@ class Ui_MainWindow(object): self.MainContentLayout.setMargin(0) self.MainContentLayout.setObjectName(u'MainContentLayout') mainWindow.setCentralWidget(self.MainContent) - self.ControlSplitter = QtGui.QSplitter(self.MainContent) - self.ControlSplitter.setOrientation(QtCore.Qt.Horizontal) - self.ControlSplitter.setObjectName(u'ControlSplitter') - self.MainContentLayout.addWidget(self.ControlSplitter) + self.controlSplitter = QtGui.QSplitter(self.MainContent) + self.controlSplitter.setOrientation(QtCore.Qt.Horizontal) + self.controlSplitter.setObjectName(u'controlSplitter') + self.MainContentLayout.addWidget(self.controlSplitter) # Create slide controllers self.previewController = SlideController(self, self.settingsmanager, self.screens) @@ -102,9 +103,9 @@ class Ui_MainWindow(object): self.FileExportMenu = QtGui.QMenu(self.FileMenu) self.FileExportMenu.setObjectName(u'FileExportMenu') # View Menu - self.ViewMenu = QtGui.QMenu(self.MenuBar) - self.ViewMenu.setObjectName(u'ViewMenu') - self.ViewModeMenu = QtGui.QMenu(self.ViewMenu) + self.viewMenu = QtGui.QMenu(self.MenuBar) + self.viewMenu.setObjectName(u'viewMenu') + self.ViewModeMenu = QtGui.QMenu(self.viewMenu) self.ViewModeMenu.setObjectName(u'ViewModeMenu') # Tools Menu self.ToolsMenu = QtGui.QMenu(self.MenuBar) @@ -125,38 +126,38 @@ class Ui_MainWindow(object): self.DefaultThemeLabel.setObjectName(u'DefaultThemeLabel') self.StatusBar.addPermanentWidget(self.DefaultThemeLabel) # Create the MediaManager - self.MediaManagerDock = OpenLPDockWidget(mainWindow, - u'MediaManagerDock', u':/system/system_mediamanager.png') - self.MediaManagerDock.setStyleSheet(MEDIA_MANAGER_STYLE) - self.MediaManagerDock.setMinimumWidth( + self.mediaManagerDock = OpenLPDockWidget(mainWindow, + u'mediaManagerDock', u':/system/system_mediamanager.png') + self.mediaManagerDock.setStyleSheet(MEDIA_MANAGER_STYLE) + self.mediaManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_left) # Create the media toolbox - self.MediaToolBox = QtGui.QToolBox(self.MediaManagerDock) + self.MediaToolBox = QtGui.QToolBox(self.mediaManagerDock) self.MediaToolBox.setObjectName(u'MediaToolBox') - self.MediaManagerDock.setWidget(self.MediaToolBox) + self.mediaManagerDock.setWidget(self.MediaToolBox) mainWindow.addDockWidget(QtCore.Qt.LeftDockWidgetArea, - self.MediaManagerDock) + self.mediaManagerDock) # Create the service manager - self.ServiceManagerDock = OpenLPDockWidget(mainWindow, - u'ServiceManagerDock', u':/system/system_servicemanager.png') - self.ServiceManagerDock.setMinimumWidth( + self.serviceManagerDock = OpenLPDockWidget(mainWindow, + u'serviceManagerDock', u':/system/system_servicemanager.png') + self.serviceManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_right) self.ServiceManagerContents = ServiceManager(mainWindow, - self.ServiceManagerDock) - self.ServiceManagerDock.setWidget(self.ServiceManagerContents) + self.serviceManagerDock) + self.serviceManagerDock.setWidget(self.ServiceManagerContents) mainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, - self.ServiceManagerDock) + self.serviceManagerDock) # Create the theme manager - self.ThemeManagerDock = OpenLPDockWidget(mainWindow, - u'ThemeManagerDock', u':/system/system_thememanager.png') - self.ThemeManagerDock.setMinimumWidth( + self.themeManagerDock = OpenLPDockWidget(mainWindow, + u'themeManagerDock', u':/system/system_thememanager.png') + self.themeManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_right) self.ThemeManagerContents = ThemeManager(mainWindow, - self.ThemeManagerDock) + self.themeManagerDock) self.ThemeManagerContents.setObjectName(u'ThemeManagerContents') - self.ThemeManagerDock.setWidget(self.ThemeManagerContents) + self.themeManagerDock.setWidget(self.ThemeManagerContents) mainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, - self.ThemeManagerDock) + self.themeManagerDock) # Create the menu items self.FileNewItem = icon_action(mainWindow, u'FileNewItem', u':/general/general_new.png') @@ -186,14 +187,14 @@ class Ui_MainWindow(object): mainWindow.actionList.add_action(self.ExportLanguageItem, u'Export') self.ViewMediaManagerItem = icon_action(mainWindow, u'ViewMediaManagerItem', u':/system/system_mediamanager.png', - self.MediaManagerDock.isVisible()) + self.mediaManagerDock.isVisible()) self.ViewThemeManagerItem = icon_action(mainWindow, u'ViewThemeManagerItem', u':/system/system_thememanager.png', - self.ThemeManagerDock.isVisible()) + self.themeManagerDock.isVisible()) mainWindow.actionList.add_action(self.ViewMediaManagerItem, u'View') self.ViewServiceManagerItem = icon_action(mainWindow, u'ViewServiceManagerItem', u':/system/system_servicemanager.png', - self.ServiceManagerDock.isVisible()) + self.serviceManagerDock.isVisible()) mainWindow.actionList.add_action(self.ViewServiceManagerItem, u'View') self.ViewPreviewPanel = checkable_action(mainWindow, u'ViewPreviewPanel', previewVisible) @@ -215,9 +216,12 @@ 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.SettingsPluginListItem = icon_action(mainWindow, - u'SettingsPluginListItem', u':/system/settings_plugin_list.png') - mainWindow.actionList.add_action(self.SettingsPluginListItem, + 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, u'Settings') # i18n Language Items self.AutoLanguageItem = checkable_action(mainWindow, @@ -264,22 +268,23 @@ class Ui_MainWindow(object): self.FileExportMenu.menuAction(), self.FileExitItem) add_actions(self.ViewModeMenu, (self.ModeDefaultItem, self.ModeSetupItem, self.ModeLiveItem)) - add_actions(self.ViewMenu, (self.ViewModeMenu.menuAction(), + add_actions(self.viewMenu, (self.ViewModeMenu.menuAction(), None, self.ViewMediaManagerItem, self.ViewServiceManagerItem, self.ViewThemeManagerItem, None, self.ViewPreviewPanel, self.ViewLivePanel)) # i18n add Language Actions add_actions(self.SettingsLanguageMenu, (self.AutoLanguageItem, None)) add_actions(self.SettingsLanguageMenu, self.LanguageGroup.actions()) - add_actions(self.SettingsMenu, (self.SettingsPluginListItem, + add_actions(self.SettingsMenu, (self.settingsPluginListItem, 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)) add_actions(self.MenuBar, (self.FileMenu.menuAction(), - self.ViewMenu.menuAction(), self.ToolsMenu.menuAction(), + self.viewMenu.menuAction(), self.ToolsMenu.menuAction(), self.SettingsMenu.menuAction(), self.HelpMenu.menuAction())) # Initialise the translation self.retranslateUi(mainWindow) @@ -295,39 +300,35 @@ class Ui_MainWindow(object): """ Set up the translation system """ - mainWindow.mainTitle = translate('OpenLP.MainWindow', 'OpenLP 2.0') + mainWindow.mainTitle = UiStrings.OLPV2 mainWindow.setWindowTitle(mainWindow.mainTitle) self.FileMenu.setTitle(translate('OpenLP.MainWindow', '&File')) self.FileImportMenu.setTitle(translate('OpenLP.MainWindow', '&Import')) self.FileExportMenu.setTitle(translate('OpenLP.MainWindow', '&Export')) - self.ViewMenu.setTitle(translate('OpenLP.MainWindow', '&View')) + self.viewMenu.setTitle(translate('OpenLP.MainWindow', '&View')) self.ViewModeMenu.setTitle(translate('OpenLP.MainWindow', 'M&ode')) self.ToolsMenu.setTitle(translate('OpenLP.MainWindow', '&Tools')) self.SettingsMenu.setTitle(translate('OpenLP.MainWindow', '&Settings')) self.SettingsLanguageMenu.setTitle(translate('OpenLP.MainWindow', '&Language')) self.HelpMenu.setTitle(translate('OpenLP.MainWindow', '&Help')) - self.MediaManagerDock.setWindowTitle( + self.mediaManagerDock.setWindowTitle( translate('OpenLP.MainWindow', 'Media Manager')) - self.ServiceManagerDock.setWindowTitle( + self.serviceManagerDock.setWindowTitle( translate('OpenLP.MainWindow', 'Service Manager')) - self.ThemeManagerDock.setWindowTitle( + 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')) @@ -403,11 +404,11 @@ class Ui_MainWindow(object): 'Toggle the visibility of the live panel.')) self.ViewLivePanel.setShortcut( translate('OpenLP.MainWindow', 'F12')) - self.SettingsPluginListItem.setText(translate('OpenLP.MainWindow', + self.settingsPluginListItem.setText(translate('OpenLP.MainWindow', '&Plugin List')) - self.SettingsPluginListItem.setStatusTip( + self.settingsPluginListItem.setStatusTip( translate('OpenLP.MainWindow', 'List the Plugins')) - self.SettingsPluginListItem.setShortcut( + self.settingsPluginListItem.setShortcut( translate('OpenLP.MainWindow', 'Alt+F7')) self.HelpDocumentationItem.setText( translate('OpenLP.MainWindow', '&User Guide')) @@ -432,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', @@ -501,20 +506,22 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): QtCore.SIGNAL(u'toggled(bool)'), self.setPreviewPanelVisibility) QtCore.QObject.connect(self.ViewLivePanel, QtCore.SIGNAL(u'toggled(bool)'), self.setLivePanelVisibility) - QtCore.QObject.connect(self.MediaManagerDock, + QtCore.QObject.connect(self.mediaManagerDock, QtCore.SIGNAL(u'visibilityChanged(bool)'), self.ViewMediaManagerItem.setChecked) - QtCore.QObject.connect(self.ServiceManagerDock, + QtCore.QObject.connect(self.serviceManagerDock, QtCore.SIGNAL(u'visibilityChanged(bool)'), self.ViewServiceManagerItem.setChecked) - QtCore.QObject.connect(self.ThemeManagerDock, + QtCore.QObject.connect(self.themeManagerDock, QtCore.SIGNAL(u'visibilityChanged(bool)'), self.ViewThemeManagerItem.setChecked) QtCore.QObject.connect(self.HelpWebSiteItem, QtCore.SIGNAL(u'triggered()'), self.onHelpWebSiteClicked) QtCore.QObject.connect(self.HelpAboutItem, QtCore.SIGNAL(u'triggered()'), self.onHelpAboutItemClicked) - QtCore.QObject.connect(self.SettingsPluginListItem, + 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, QtCore.SIGNAL(u'triggered()'), self.onSettingsConfigureItemClicked) @@ -702,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 @@ -748,9 +762,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): settings = QtCore.QSettings() settings.setValue(u'%s/view mode' % self.generalSettingsSection, mode) - self.MediaManagerDock.setVisible(media) - self.ServiceManagerDock.setVisible(service) - self.ThemeManagerDock.setVisible(theme) + self.mediaManagerDock.setVisible(media) + self.serviceManagerDock.setVisible(service) + self.themeManagerDock.setVisible(theme) self.setPreviewPanelVisibility(preview) self.setLivePanelVisibility(live) @@ -867,16 +881,16 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): theme) def toggleMediaManager(self, visible): - if self.MediaManagerDock.isVisible() != visible: - self.MediaManagerDock.setVisible(visible) + if self.mediaManagerDock.isVisible() != visible: + self.mediaManagerDock.setVisible(visible) def toggleServiceManager(self, visible): - if self.ServiceManagerDock.isVisible() != visible: - self.ServiceManagerDock.setVisible(visible) + if self.serviceManagerDock.isVisible() != visible: + self.serviceManagerDock.setVisible(visible) def toggleThemeManager(self, visible): - if self.ThemeManagerDock.isVisible() != visible: - self.ThemeManagerDock.setVisible(visible) + if self.themeManagerDock.isVisible() != visible: + self.themeManagerDock.setVisible(visible) def setPreviewPanelVisibility(self, visible): """ diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 3b41d76a3..6318a44a5 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -34,7 +34,9 @@ from plugindialog import Ui_PluginViewDialog log = logging.getLogger(__name__) class PluginForm(QtGui.QDialog, Ui_PluginViewDialog): - + """ + The plugin form provides user control over the plugins OpenLP uses. + """ def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.parent = parent diff --git a/openlp/core/ui/printserviceorderdialog.py b/openlp/core/ui/printserviceorderdialog.py new file mode 100644 index 000000000..f8db4d1c0 --- /dev/null +++ b/openlp/core/ui/printserviceorderdialog.py @@ -0,0 +1,137 @@ +# -*- 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 build_icon, translate, SpellTextEdit + +class Ui_PrintServiceOrderDialog(object): + def setupUi(self, printServiceOrderDialog): + printServiceOrderDialog.setObjectName(u'printServiceOrderDialog') + self.dialogLayout = QtGui.QGridLayout(printServiceOrderDialog) + self.dialogLayout.setObjectName(u'dialogLayout') + self.perviewLayout = QtGui.QVBoxLayout() + self.perviewLayout.setObjectName(u'perviewLayout') + self.previewLabel = QtGui.QLabel(printServiceOrderDialog) + self.previewLabel.setSizePolicy( + QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) + self.previewLabel.setObjectName(u'previewLabel') + self.perviewLayout.addWidget(self.previewLabel) + self.previewWidget = QtGui.QPrintPreviewWidget( + self.printer, self, QtCore.Qt.Widget) + self.previewWidget.setEnabled(True) + self.previewWidget.setSizePolicy( + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Expanding) + self.previewWidget.setObjectName(u'previewWidget') + # Give the previewWidget a fixed size, to prevent resizing when clicking + # the zoom buttons. + self.previewWidget.setFixedWidth(350) + self.perviewLayout.addWidget(self.previewWidget) + self.dialogLayout.addLayout(self.perviewLayout, 0, 0, 1, 1) + self.settingsLayout = QtGui.QVBoxLayout() + self.settingsLayout.setObjectName(u'settingsLayout') + self.serviceTitleLayout = QtGui.QGridLayout() + self.serviceTitleLayout.setObjectName(u'serviceTitleLayout') + self.serviceTitleLineEdit = QtGui.QLineEdit(printServiceOrderDialog) + self.serviceTitleLineEdit.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) + self.serviceTitleLineEdit.setObjectName(u'serviceTitleLineEdit') + self.serviceTitleLayout.addWidget(self.serviceTitleLineEdit, 1, 1, 1, 1) + self.serviceTitleLabel = QtGui.QLabel(printServiceOrderDialog) + self.serviceTitleLabel.setSizePolicy( + QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) + self.serviceTitleLabel.setObjectName(u'serviceTitleLabel') + self.serviceTitleLayout.addWidget(self.serviceTitleLabel, 1, 0, 1, 1) + self.settingsLayout.addLayout(self.serviceTitleLayout) + self.printSlideTextCheckBox = QtGui.QCheckBox(printServiceOrderDialog) + self.printSlideTextCheckBox.setObjectName(u'printSlideTextCheckBox') + self.settingsLayout.addWidget(self.printSlideTextCheckBox) + self.printNotesCheckBox = QtGui.QCheckBox(printServiceOrderDialog) + self.printNotesCheckBox.setObjectName(u'printNotesCheckBox') + self.settingsLayout.addWidget(self.printNotesCheckBox) + self.printMetaDataCheckBox = QtGui.QCheckBox(printServiceOrderDialog) + self.printMetaDataCheckBox.setObjectName(u'printMetaDataCheckBox') + self.settingsLayout.addWidget(self.printMetaDataCheckBox) + spacerItem = QtGui.QSpacerItem(20, 40, + QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.settingsLayout.addItem(spacerItem) + self.customNotesLabel = QtGui.QLabel(self) + self.customNotesLabel.setObjectName(u'customNotesLabel') + self.settingsLayout.addWidget(self.customNotesLabel) + self.customNoteEdit = SpellTextEdit(self) + self.customNoteEdit.setObjectName(u'customNoteEdit') + self.settingsLayout.addWidget(self.customNoteEdit) + self.dialogLayout.addLayout(self.settingsLayout, 0, 3, 1, 1) + self.buttonLayout = QtGui.QHBoxLayout() + self.buttonLayout.setObjectName(u'buttonLayout') + spacerItem = QtGui.QSpacerItem(40, 20, + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.buttonLayout.addItem(spacerItem) + self.cancelButton = QtGui.QPushButton(printServiceOrderDialog) + self.cancelButton.setObjectName(u'cancelButton') + self.buttonLayout.addWidget(self.cancelButton) + self.printButton = QtGui.QPushButton(printServiceOrderDialog) + self.printButton.setObjectName(u'printButton') + self.buttonLayout.addWidget(self.printButton) + self.dialogLayout.addLayout(self.buttonLayout, 1, 3, 1, 1) + self.zoomButtonLayout = QtGui.QHBoxLayout() + self.zoomButtonLayout.setObjectName(u'zoomButtonLayout') + spacerItem = QtGui.QSpacerItem(40, 20, + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.zoomButtonLayout.addItem(spacerItem) + self.zoomOutButton = QtGui.QToolButton(printServiceOrderDialog) + self.zoomOutButton.setIcon( + build_icon(u':/general/general_zoom_out.png')) + self.zoomOutButton.setObjectName(u'zoomOutButton') + self.zoomButtonLayout.addWidget(self.zoomOutButton) + self.zoomInButton = QtGui.QToolButton(printServiceOrderDialog) + self.zoomInButton.setIcon(build_icon(u':/general/general_zoom_in.png')) + self.zoomInButton.setObjectName(u'zoomInButton') + self.zoomButtonLayout.addWidget(self.zoomInButton) + self.dialogLayout.addLayout(self.zoomButtonLayout, 1, 0, 1, 1) + self.retranslateUi(printServiceOrderDialog) + QtCore.QMetaObject.connectSlotsByName(printServiceOrderDialog) + + def retranslateUi(self, printServiceOrderDialog): + printServiceOrderDialog.setWindowTitle( + translate('OpenLP.PrintServiceOrderForm', 'Print Service Order')) + self.previewLabel.setText( + translate('OpenLP.ServiceManager', 'Preview:')) + self.printSlideTextCheckBox.setText(translate( + 'OpenLP.PrintServiceOrderForm', 'Include slide text if available')) + self.printNotesCheckBox.setText(translate( + 'OpenLP.PrintServiceOrderForm', 'Include service item notes')) + self.printMetaDataCheckBox.setText( + translate('OpenLP.PrintServiceOrderForm', + 'Include play length of media items')) + self.serviceTitleLabel.setText(translate( + 'OpenLP.PrintServiceOrderForm', 'Title:')) + self.serviceTitleLineEdit.setText(translate('OpenLP.ServiceManager', + 'Service Order Sheet')) + self.printButton.setText(translate('OpenLP.ServiceManager', 'Print')) + self.cancelButton.setText(translate('OpenLP.ServiceManager', 'Cancel')) + self.customNotesLabel.setText( + translate('OpenLP.ServiceManager', 'Custom Service Notes:')) diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py new file mode 100644 index 000000000..b5160e61c --- /dev/null +++ b/openlp/core/ui/printserviceorderform.py @@ -0,0 +1,175 @@ +# -*- 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 # +############################################################################### +import datetime + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import translate +from openlp.core.ui.printserviceorderdialog import Ui_PrintServiceOrderDialog + +class PrintServiceOrderForm(QtGui.QDialog, Ui_PrintServiceOrderDialog): + def __init__(self, parent, serviceManager): + """ + Constructor + """ + QtGui.QDialog.__init__(self, parent) + self.serviceManager = serviceManager + self.printer = QtGui.QPrinter() + self.printDialog = QtGui.QPrintDialog(self.printer, self) + self.document = QtGui.QTextDocument() + self.setupUi(self) + # Load the settings for the dialog. + settings = QtCore.QSettings() + settings.beginGroup(u'advanced') + self.printSlideTextCheckBox.setChecked(settings.value( + u'print slide text', QtCore.QVariant(False)).toBool()) + self.printMetaDataCheckBox.setChecked(settings.value( + u'print file meta data', QtCore.QVariant(False)).toBool()) + self.printNotesCheckBox.setChecked(settings.value( + u'print notes', QtCore.QVariant(False)).toBool()) + settings.endGroup() + # Signals + QtCore.QObject.connect(self.printButton, + QtCore.SIGNAL(u'clicked()'), self.printServiceOrder) + QtCore.QObject.connect(self.zoomOutButton, + QtCore.SIGNAL(u'clicked()'), self.zoomOut) + QtCore.QObject.connect(self.zoomInButton, + QtCore.SIGNAL(u'clicked()'), self.zoomIn) + QtCore.QObject.connect(self.previewWidget, + QtCore.SIGNAL(u'paintRequested(QPrinter *)'), self.paintRequested) + QtCore.QObject.connect(self.serviceTitleLineEdit, + QtCore.SIGNAL(u'textChanged(const QString)'), + self.updatePreviewText) + QtCore.QObject.connect(self.printSlideTextCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), self.updatePreviewText) + QtCore.QObject.connect(self.printNotesCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), self.updatePreviewText) + QtCore.QObject.connect(self.printMetaDataCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), self.updatePreviewText) + QtCore.QObject.connect(self.customNoteEdit, + QtCore.SIGNAL(u'textChanged()'), self.updatePreviewText) + QtCore.QObject.connect(self.cancelButton, + QtCore.SIGNAL(u'clicked()'), self.reject) + self.updatePreviewText() + + def updatePreviewText(self): + """ + Creates the html text and updates the html of *self.document*. + """ + text = u'' + if self.serviceTitleLineEdit.text(): + text += u'

%s

' % unicode(self.serviceTitleLineEdit.text()) + for item in self.serviceManager.serviceItems: + item = item[u'service_item'] + # Add the title of the service item. + text += u'

%s

' % (item.icon, + item.get_display_title()) + # Add slide text of the service item. + if self.printSlideTextCheckBox.isChecked(): + if item.is_text(): + # Add the text of the service item. + for slide in item.get_frames(): + text += u'

' + slide[u'text'] + u'

' + elif item.is_image(): + # Add the image names of the service item. + text += u'
    ' + for slide in range(len(item.get_frames())): + text += u'
  1. %s

  2. ' % \ + item.get_frame_title(slide) + text += u'
' + if item.foot_text: + # add footer + text += u'

%s

' % item.foot_text + # Add service items' notes. + if self.printNotesCheckBox.isChecked(): + if item.notes: + text += u'

%s

%s' % (translate( + 'OpenLP.ServiceManager', 'Notes:'), + item.notes.replace(u'\n', u'
')) + # Add play length of media files. + if item.is_media() and self.printMetaDataCheckBox.isChecked(): + 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()) + self.document.setHtml(text) + self.previewWidget.updatePreview() + + def paintRequested(self, printer): + """ + Paint the preview of the *self.document*. + + ``printer`` + A *QPrinter* object. + """ + self.document.print_(printer) + + def printServiceOrder(self): + """ + Called, when the *printButton* is clicked. Opens the *printDialog*. + """ + if not self.printDialog.exec_(): + return + # Print the document. + self.document.print_(self.printer) + self.accept() + + def zoomIn(self): + """ + Called when *zoomInButton* is clicked. + """ + self.previewWidget.zoomIn() + + def zoomOut(self): + """ + Called when *zoomOutButton* is clicked. + """ + self.previewWidget.zoomOut() + + def accept(self): + """ + Save the settings and close the dialog. + """ + # Save the settings for this dialog. + settings = QtCore.QSettings() + settings.beginGroup(u'advanced') + settings.setValue(u'print slide text', + QtCore.QVariant(self.printSlideTextCheckBox.isChecked())) + settings.setValue(u'print file meta data', + QtCore.QVariant(self.printMetaDataCheckBox.isChecked())) + settings.setValue(u'print notes', + QtCore.QVariant(self.printNotesCheckBox.isChecked())) + settings.endGroup() + # Close the dialog. + return QtGui.QDialog.accept(self) + + def reject(self): + """ + Close the dialog, do not print the service and do not save the settings. + """ + return QtGui.QDialog.reject(self) 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 bddd150b8..623c2d641 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -23,10 +23,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - -import os -import logging import cPickle +import logging +import os import zipfile log = logging.getLogger(__name__) @@ -36,8 +35,9 @@ 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 @@ -67,8 +67,8 @@ class ServiceManagerList(QtGui.QTreeWidget): class ServiceManager(QtGui.QWidget): """ - Manages the services. This involves taking text strings from plugins and - adding them to the service. This service can then be zipped up with all + Manages the services. This involves taking text strings from plugins and + adding them to the service. This service can then be zipped up with all the resources used into one OSZ file for use on any OpenLP v2 installation. Also handles the UI tasks of moving things up and down etc. """ @@ -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) @@ -391,7 +391,7 @@ class ServiceManager(QtGui.QWidget): return QtGui.QMessageBox.question(self.mainwindow, translate('OpenLP.ServiceManager', 'Modified Service'), translate('OpenLP.ServiceManager', 'The current service has ' - 'been modified. Would you like to save this service?'), + 'been modified. Would you like to save this service?'), QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Save) @@ -461,11 +461,11 @@ class ServiceManager(QtGui.QWidget): def saveFileAs(self): """ - Get a file name and then call :function:`ServiceManager.saveFile` to + Get a file name and then call :func:`ServiceManager.saveFile` to 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) @@ -1187,41 +1208,5 @@ class ServiceManager(QtGui.QWidget): """ Print a Service Order Sheet. """ - if not self.serviceItems: - critical_error_message_box( - message=translate('OpenLP.ServiceManager', - 'There is no service item in this service.')) - return - printDialog = QtGui.QPrintDialog() - if not printDialog.exec_(): - return - text = u'

%s

' % translate('OpenLP.ServiceManager', - 'Service Order Sheet') - for item in self.serviceItems: - item = item[u'service_item'] - # add the title - text += u'

%s

' % (item.icon, - item.get_display_title()) - if not QtCore.QSettings().value(u'advanced' + - u'/detailed service print', QtCore.QVariant(True)).toBool(): - continue - if item.is_text(): - # Add the text of the service item. - for slide in item.get_frames(): - text += u'

' + slide[u'text'] + u'

' - elif item.is_image(): - # Add the image names of the service item. - text += u'
    ' - for slide in range(len(item.get_frames())): - text += u'
  1. %s

  2. ' % item.get_frame_title(slide) - text += u'
' - if item.foot_text: - # add footer - text += u'

%s

' % item.foot_text - if item.notes: - # add notes - text += u'

%s %s

' % (translate( - 'OpenLP.ServiceManager', 'Notes:'), item.notes) - serviceDocument = QtGui.QTextDocument() - serviceDocument.setHtml(text) - serviceDocument.print_(printDialog.printer()) + settingDialog = PrintServiceOrderForm(self.mainwindow, self) + settingDialog.exec_() 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..41b6baccb 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): @@ -35,21 +36,13 @@ class Ui_SettingsDialog(object): settingsDialog.setWindowIcon( build_icon(u':/system/system_settings.png')) self.settingsLayout = QtGui.QVBoxLayout(settingsDialog) - margins = self.settingsLayout.contentsMargins() self.settingsLayout.setObjectName(u'settingsLayout') 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/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 3f41d377a..4e20671c5 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -36,7 +36,7 @@ class Ui_ShortcutListDialog(object): self.treeWidget = QtGui.QTreeWidget(shortcutListDialog) self.treeWidget.setAlternatingRowColors(True) self.treeWidget.setObjectName(u'treeWidget') - self.treeWidget.setColumnCount(2) + self.treeWidget.setColumnCount(3) self.dialogLayout.addWidget(self.treeWidget) self.defaultButton = QtGui.QRadioButton(shortcutListDialog) self.defaultButton.setChecked(True) @@ -78,7 +78,8 @@ class Ui_ShortcutListDialog(object): translate('OpenLP.ShortcutListDialog', 'Customize Shortcuts')) self.treeWidget.setHeaderLabels([ translate('OpenLP.ShortcutListDialog', 'Action'), - translate('OpenLP.ShortcutListDialog', 'Shortcut')]) + translate('OpenLP.ShortcutListDialog', 'Shortcut'), + translate('OpenLP.ShortcutListDialog', 'Alternate')]) self.defaultButton.setText( translate('OpenLP.ShortcutListDialog', 'Default: %s')) self.customButton.setText( diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 9d2b31853..0de4bea7f 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -95,8 +95,13 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): item = QtGui.QTreeWidgetItem([category.name]) for action in category.actions: actionText = REMOVE_AMPERSAND.sub('', unicode(action.text())) - shortcutText = action.shortcut().toString() - actionItem = QtGui.QTreeWidgetItem([actionText, shortcutText]) + if (len(action.shortcuts()) == 2): + shortcutText = action.shortcuts()[0].toString() + alternateText = action.shortcuts()[1].toString() + else: + shortcutText = action.shortcut().toString() + alternateText = u'' + actionItem = QtGui.QTreeWidgetItem([actionText, shortcutText, alternateText]) actionItem.setIcon(0, action.icon()) item.addChild(actionItem) item.setExpanded(True) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index bf0453e05..073263616 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -32,7 +32,7 @@ from PyQt4.phonon import Phonon from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \ ItemCapabilities, translate -from openlp.core.lib.ui import shortcut_action +from openlp.core.lib.ui import UiStrings, shortcut_action from openlp.core.ui import HideMode, MainDisplay log = logging.getLogger(__name__) @@ -78,7 +78,7 @@ class SlideController(QtGui.QWidget): self.selectedRow = 0 self.serviceItem = None self.alertTab = None - self.panel = QtGui.QWidget(parent.ControlSplitter) + self.panel = QtGui.QWidget(parent.controlSplitter) self.slideList = {} # Layout for holding panel self.panelLayout = QtGui.QVBoxLayout(self.panel) @@ -87,12 +87,11 @@ class SlideController(QtGui.QWidget): # Type label for the top of the slide controller self.typeLabel = QtGui.QLabel(self.panel) if self.isLive: - self.typeLabel.setText(translate('OpenLP.SlideController', 'Live')) + self.typeLabel.setText(UiStrings.Live) self.split = 1 self.typePrefix = u'live' else: - self.typeLabel.setText(translate('OpenLP.SlideController', - 'Preview')) + self.typeLabel.setText(UiStrings.Preview) self.split = 0 self.typePrefix = u'preview' self.typeLabel.setStyleSheet(u'font-weight: bold; font-size: 12pt;') @@ -179,10 +178,12 @@ class SlideController(QtGui.QWidget): QtCore.SIGNAL(u'triggered(bool)'), self.onHideDisplay) self.toolbar.addToolbarSeparator(u'Loop Separator') self.toolbar.addToolbarButton( + # Does not need translating - control string. u'Start Loop', u':/media/media_time.png', translate('OpenLP.SlideController', 'Start continuous loop'), self.onStartLoop) self.toolbar.addToolbarButton( + # Does not need translating - control string. u'Stop Loop', u':/media/media_stop.png', translate('OpenLP.SlideController', 'Stop continuous loop'), self.onStopLoop) @@ -197,11 +198,13 @@ class SlideController(QtGui.QWidget): else: self.toolbar.addToolbarSeparator(u'Close Separator') self.toolbar.addToolbarButton( + # Does not need translating - control string. u'Go Live', u':/general/general_live.png', translate('OpenLP.SlideController', 'Move to live'), self.onGoLive) self.toolbar.addToolbarSeparator(u'Close Separator') self.toolbar.addToolbarButton( + # Does not need translating - control string. u'Edit Song', u':/general/general_edit.png', translate('OpenLP.SlideController', 'Edit and reload song preview'), @@ -377,7 +380,7 @@ class SlideController(QtGui.QWidget): self.previousItem.setShortcuts([QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp]) self.previousItem.setShortcutContext( QtCore.Qt.WidgetWithChildrenShortcut) - actionList.add_action(self.nextItem, u'Live') + actionList.add_action(self.previousItem, u'Live') self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown]) self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) actionList.add_action(self.nextItem, u'Live') @@ -455,7 +458,7 @@ class SlideController(QtGui.QWidget): self.previewListWidget.resizeRowsToContents() else: # Sort out image heights. - width = self.parent.ControlSplitter.sizes()[self.split] + width = self.parent.controlSplitter.sizes()[self.split] for framenumber in range(len(self.serviceItem.get_frames())): self.previewListWidget.setRowHeight( framenumber, width / self.ratio) @@ -584,7 +587,7 @@ class SlideController(QtGui.QWidget): Receiver.send_message(u'%s_start' % serviceItem.name.lower(), [serviceItem, self.isLive, blanked, slideno]) self.slideList = {} - width = self.parent.ControlSplitter.sizes()[self.split] + width = self.parent.controlSplitter.sizes()[self.split] self.serviceItem = serviceItem self.previewListWidget.clear() self.previewListWidget.setRowCount(0) @@ -874,7 +877,8 @@ 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']: + 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 QtCore.QTimer.singleShot(0.5, self.grabMainDisplay) 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 018df7597..ad9e80d66 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -31,7 +31,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate, BackgroundType, BackgroundGradientType, \ Receiver -from openlp.core.lib.ui import critical_error_message_box +from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.utils import get_images_filter from themewizard import Ui_ThemeWizard @@ -483,8 +483,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): Background Image button pushed. """ images_filter = get_images_filter() - images_filter = '%s;;%s (*.*) (*)' % (images_filter, - translate('OpenLP.ThemeForm', 'All Files')) + 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 317065c6e..78c4596e3 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -34,8 +34,9 @@ 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 -from openlp.core.lib.ui import critical_error_message_box + 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 from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ @@ -313,7 +314,6 @@ class ThemeManager(QtGui.QWidget): translate('OpenLP.ThemeManager', 'You must select a theme to edit.')): item = self.themeListWidget.currentItem() - themeName = unicode(item.text()) theme = self.getThemeData( unicode(item.data(QtCore.Qt.UserRole).toString())) if theme.background_type == u'image': @@ -405,8 +405,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', + 'OpenLP Themes (*.theme *.otz)'))) log.info(u'New Themes %s', unicode(files)) if files: for file in files: @@ -529,6 +529,18 @@ class ThemeManager(QtGui.QWidget): else: outfile = open(fullpath, u'wb') outfile.write(zip.read(file)) + except (IOError, NameError): + critical_error_message_box( + translate('OpenLP.ThemeManager', 'Validation Error'), + translate('OpenLP.ThemeManager', 'File is not a valid theme.')) + log.exception(u'Importing theme from zip failed %s' % filename) + finally: + # Close the files, to be able to continue creating the theme. + if zip: + zip.close() + if outfile: + outfile.close() + # As all files are closed, we can create the Theme. if filexml: theme = self._createThemeFromXml(filexml, self.path) self.generateAndSaveImage(dir, themename, theme) @@ -539,17 +551,6 @@ class ThemeManager(QtGui.QWidget): 'File is not a valid theme.')) log.exception(u'Theme file does not contain XML data %s' % filename) - except (IOError, NameError): - critical_error_message_box( - translate('OpenLP.ThemeManager', 'Validation Error'), - translate('OpenLP.ThemeManager', - 'File is not a valid theme.')) - log.exception(u'Importing theme from zip failed %s' % filename) - finally: - if zip: - zip.close() - if outfile: - outfile.close() def checkIfThemeExists(self, themeName): """ @@ -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/themestab.py b/openlp/core/ui/themestab.py index 441b95155..ba4ce5acb 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -27,6 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, Receiver, ThemeLevel, translate +from openlp.core.lib.ui import UiStrings class ThemesTab(SettingsTab): """ @@ -98,7 +99,7 @@ class ThemesTab(SettingsTab): QtCore.SIGNAL(u'theme_update_list'), self.updateThemeList) def retranslateUi(self): - self.tabTitleVisible = translate('OpenLP.ThemesTab', 'Themes') + self.tabTitleVisible = UiStrings.Themes self.GlobalGroupBox.setTitle( translate('OpenLP.ThemesTab', 'Global Theme')) self.LevelGroupBox.setTitle( diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index 49522df70..38dd9f1dc 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate, build_icon -from openlp.core.lib.ui import add_welcome_page +from openlp.core.lib.ui import add_welcome_page, create_valign_combo class Ui_ThemeWizard(object): def setupUi(self, themeWizard): @@ -242,12 +242,8 @@ class Ui_ThemeWizard(object): self.horizontalComboBox.setObjectName(u'HorizontalComboBox') self.alignmentLayout.addRow(self.horizontalLabel, self.horizontalComboBox) - self.verticalLabel = QtGui.QLabel(self.alignmentPage) - self.verticalLabel.setObjectName(u'VerticalLabel') - self.verticalComboBox = QtGui.QComboBox(self.alignmentPage) - self.verticalComboBox.addItems([u'', u'', u'']) - self.verticalComboBox.setObjectName(u'VerticalComboBox') - self.alignmentLayout.addRow(self.verticalLabel, self.verticalComboBox) + create_valign_combo(themeWizard, self.alignmentPage, + self.alignmentLayout) self.transitionsLabel = QtGui.QLabel(self.alignmentPage) self.transitionsLabel.setObjectName(u'TransitionsLabel') self.transitionsCheckBox = QtGui.QCheckBox(self.alignmentPage) @@ -450,8 +446,7 @@ class Ui_ThemeWizard(object): self.mainAreaPage.setSubTitle( translate('OpenLP.ThemeWizard', 'Define the font and display ' 'characteristics for the Display text')) - self.mainFontLabel.setText( - translate('OpenLP.ThemeWizard', 'Font:')) + self.mainFontLabel.setText(translate('OpenLP.ThemeWizard', 'Font:')) self.mainColorLabel.setText(translate('OpenLP.ThemeWizard', 'Color:')) self.mainSizeLabel.setText(translate('OpenLP.ThemeWizard', 'Size:')) self.mainSizeSpinBox.setSuffix(translate('OpenLP.ThemeWizard', 'pt')) @@ -465,8 +460,7 @@ class Ui_ThemeWizard(object): self.shadowCheckBox.setText(translate('OpenLP.ThemeWizard', '&Shadow:')) self.shadowSizeLabel.setText(translate('OpenLP.ThemeWizard', 'Size:')) self.shadowSizeSpinBox.setSuffix(translate('OpenLP.ThemeWizard', 'pt')) - self.mainBoldCheckBox.setText( - translate('OpenLP.ThemeWizard', 'Bold')) + self.mainBoldCheckBox.setText(translate('OpenLP.ThemeWizard', 'Bold')) self.mainItalicsCheckBox.setText( translate('OpenLP.ThemeWizard', 'Italic')) self.footerAreaPage.setTitle( @@ -491,14 +485,6 @@ class Ui_ThemeWizard(object): translate('OpenLP.ThemeWizard', 'Right')) self.horizontalComboBox.setItemText(2, translate('OpenLP.ThemeWizard', 'Center')) - self.verticalLabel.setText( - translate('OpenLP.ThemeWizard', 'Vertical Align:')) - self.verticalComboBox.setItemText(0, - translate('OpenLP.ThemeWizard', 'Top')) - self.verticalComboBox.setItemText(1, - translate('OpenLP.ThemeWizard', 'Middle')) - self.verticalComboBox.setItemText(2, - translate('OpenLP.ThemeWizard', 'Bottom')) self.transitionsLabel.setText( translate('OpenLP.ThemeWizard', 'Transitions:')) self.areaPositionPage.setTitle( 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 37cbd7a63..3f5ee90c2 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -24,7 +24,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`utils` module provides the utility libraries for OpenLP +The :mod:`openlp.core.utils` module provides the utility libraries for OpenLP. """ import logging import os @@ -164,24 +164,34 @@ def _get_os_dir_path(dir_type): """ Return a path based on which OS and environment we are running in. """ + encoding = sys.getfilesystemencoding() if sys.platform == u'win32': - return os.path.join(os.getenv(u'APPDATA'), u'openlp') + if dir_type == AppLocation.DataDir: + return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), + u'openlp', u'data') + return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), + u'openlp') elif sys.platform == u'darwin': if dir_type == AppLocation.DataDir: - return os.path.join(os.getenv(u'HOME'), u'Library', - u'Application Support', u'openlp', u'Data') - return os.path.join(os.getenv(u'HOME'), u'Library', - u'Application Support', u'openlp') + return os.path.join(unicode(os.getenv(u'HOME'), encoding), + u'Library', u'Application Support', u'openlp', u'Data') + return os.path.join(unicode(os.getenv(u'HOME'), encoding), + u'Library', u'Application Support', u'openlp') else: if XDG_BASE_AVAILABLE: if dir_type == AppLocation.ConfigDir: - return os.path.join(BaseDirectory.xdg_config_home, u'openlp') + return os.path.join(unicode(BaseDirectory.xdg_config_home, + encoding), u'openlp') elif dir_type == AppLocation.DataDir: - return os.path.join(BaseDirectory.xdg_data_home, u'openlp') + return os.path.join( + unicode(BaseDirectory.xdg_data_home, encoding), 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') + return os.path.join(unicode(BaseDirectory.xdg_cache_home, + encoding), u'openlp') + if dir_type == AppLocation.DataDir: + return os.path.join(unicode(os.getenv(u'HOME'), encoding), + u'.openlp', u'data') + return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'.openlp') def _get_frozen_path(frozen_option, non_frozen_option): """ @@ -189,8 +199,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): """ @@ -373,13 +382,13 @@ def get_uno_command(): """ Returns the UNO command to launch an openoffice.org instance. """ + COMMAND = u'soffice' + OPTIONS = u'-nologo -norestore -minimized -invisible -nofirststartwizard' if UNO_CONNECTION_TYPE == u'pipe': - return u'openoffice.org -nologo -norestore -minimized -invisible ' \ - + u'-nofirststartwizard -accept=pipe,name=openlp_pipe;urp;' + CONNECTION = u'"-accept=pipe,name=openlp_pipe;urp;"' else: - return u'openoffice.org -nologo -norestore -minimized ' \ - + u'-invisible -nofirststartwizard ' \ - + u'-accept=socket,host=localhost,port=2002;urp;' + CONNECTION = u'"-accept=socket,host=localhost,port=2002;urp;"' + return u'%s %s %s' % (COMMAND, OPTIONS, CONNECTION) def get_uno_instance(resolver): """ 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 96870d94c..48a4527ed 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -27,61 +27,55 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, translate +from openlp.core.lib.ui import UiStrings, create_valign_combo 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') SettingsTab.setupUi(self) - self.FontGroupBox = QtGui.QGroupBox(self.leftColumn) - self.FontGroupBox.setObjectName(u'FontGroupBox') - self.FontLayout = QtGui.QFormLayout(self.FontGroupBox) - self.FontLayout.setObjectName(u'FontLayout') - self.FontLabel = QtGui.QLabel(self.FontGroupBox) + self.fontGroupBox = QtGui.QGroupBox(self.leftColumn) + self.fontGroupBox.setObjectName(u'fontGroupBox') + self.fontLayout = QtGui.QFormLayout(self.fontGroupBox) + self.fontLayout.setObjectName(u'fontLayout') + self.FontLabel = QtGui.QLabel(self.fontGroupBox) self.FontLabel.setObjectName(u'FontLabel') - self.FontComboBox = QtGui.QFontComboBox(self.FontGroupBox) + self.FontComboBox = QtGui.QFontComboBox(self.fontGroupBox) self.FontComboBox.setObjectName(u'FontComboBox') - self.FontLayout.addRow(self.FontLabel, self.FontComboBox) - self.FontColorLabel = QtGui.QLabel(self.FontGroupBox) + self.fontLayout.addRow(self.FontLabel, self.FontComboBox) + self.FontColorLabel = QtGui.QLabel(self.fontGroupBox) self.FontColorLabel.setObjectName(u'FontColorLabel') self.ColorLayout = QtGui.QHBoxLayout() self.ColorLayout.setObjectName(u'ColorLayout') - self.FontColorButton = QtGui.QPushButton(self.FontGroupBox) + self.FontColorButton = QtGui.QPushButton(self.fontGroupBox) self.FontColorButton.setObjectName(u'FontColorButton') self.ColorLayout.addWidget(self.FontColorButton) self.ColorLayout.addSpacing(20) - self.BackgroundColorLabel = QtGui.QLabel(self.FontGroupBox) + self.BackgroundColorLabel = QtGui.QLabel(self.fontGroupBox) self.BackgroundColorLabel.setObjectName(u'BackgroundColorLabel') self.ColorLayout.addWidget(self.BackgroundColorLabel) - self.BackgroundColorButton = QtGui.QPushButton(self.FontGroupBox) + self.BackgroundColorButton = QtGui.QPushButton(self.fontGroupBox) self.BackgroundColorButton.setObjectName(u'BackgroundColorButton') self.ColorLayout.addWidget(self.BackgroundColorButton) - self.FontLayout.addRow(self.FontColorLabel, self.ColorLayout) - self.FontSizeLabel = QtGui.QLabel(self.FontGroupBox) + self.fontLayout.addRow(self.FontColorLabel, self.ColorLayout) + self.FontSizeLabel = QtGui.QLabel(self.fontGroupBox) self.FontSizeLabel.setObjectName(u'FontSizeLabel') - self.FontSizeSpinBox = QtGui.QSpinBox(self.FontGroupBox) + self.FontSizeSpinBox = QtGui.QSpinBox(self.fontGroupBox) self.FontSizeSpinBox.setObjectName(u'FontSizeSpinBox') - self.FontLayout.addRow(self.FontSizeLabel, self.FontSizeSpinBox) - self.TimeoutLabel = QtGui.QLabel(self.FontGroupBox) + self.fontLayout.addRow(self.FontSizeLabel, self.FontSizeSpinBox) + self.TimeoutLabel = QtGui.QLabel(self.fontGroupBox) self.TimeoutLabel.setObjectName(u'TimeoutLabel') - self.TimeoutSpinBox = QtGui.QSpinBox(self.FontGroupBox) + self.TimeoutSpinBox = QtGui.QSpinBox(self.fontGroupBox) self.TimeoutSpinBox.setMaximum(180) self.TimeoutSpinBox.setObjectName(u'TimeoutSpinBox') - self.FontLayout.addRow(self.TimeoutLabel, self.TimeoutSpinBox) - self.LocationLabel = QtGui.QLabel(self.FontGroupBox) - self.LocationLabel.setObjectName(u'LocationLabel') - self.LocationComboBox = QtGui.QComboBox(self.FontGroupBox) - self.LocationComboBox.addItems([u'', u'', u'']) - self.LocationComboBox.setObjectName(u'LocationComboBox') - self.FontLayout.addRow(self.LocationLabel, self.LocationComboBox) - self.leftLayout.addWidget(self.FontGroupBox) + self.fontLayout.addRow(self.TimeoutLabel, self.TimeoutSpinBox) + create_valign_combo(self, self.fontGroupBox, self.fontLayout) + self.leftLayout.addWidget(self.fontGroupBox) self.leftLayout.addStretch() self.PreviewGroupBox = QtGui.QGroupBox(self.rightColumn) self.PreviewGroupBox.setObjectName(u'PreviewGroupBox') @@ -99,15 +93,13 @@ class AlertsTab(SettingsTab): QtCore.SIGNAL(u'pressed()'), self.onFontColorButtonClicked) QtCore.QObject.connect(self.FontComboBox, QtCore.SIGNAL(u'activated(int)'), self.onFontComboBoxClicked) - QtCore.QObject.connect(self.LocationComboBox, - QtCore.SIGNAL(u'activated(int)'), self.onLocationComboBoxClicked) QtCore.QObject.connect(self.TimeoutSpinBox, QtCore.SIGNAL(u'valueChanged(int)'), self.onTimeoutSpinBoxChanged) QtCore.QObject.connect(self.FontSizeSpinBox, QtCore.SIGNAL(u'valueChanged(int)'), self.onFontSizeSpinBoxChanged) def retranslateUi(self): - self.FontGroupBox.setTitle( + self.fontGroupBox.setTitle( translate('AlertsPlugin.AlertsTab', 'Font')) self.FontLabel.setText( translate('AlertsPlugin.AlertsTab', 'Font name:')) @@ -123,18 +115,8 @@ class AlertsTab(SettingsTab): translate('AlertsPlugin.AlertsTab', 'Alert timeout:')) self.TimeoutSpinBox.setSuffix( translate('AlertsPlugin.AlertsTab', 's')) - self.LocationLabel.setText( - translate('AlertsPlugin.AlertsTab', 'Location:')) - self.PreviewGroupBox.setTitle( - translate('AlertsPlugin.AlertsTab', 'Preview')) - self.FontPreview.setText( - translate('AlertsPlugin.AlertsTab', 'OpenLP 2.0')) - self.LocationComboBox.setItemText(0, - translate('AlertsPlugin.AlertsTab', 'Top')) - self.LocationComboBox.setItemText(1, - translate('AlertsPlugin.AlertsTab', 'Middle')) - self.LocationComboBox.setItemText(2, - translate('AlertsPlugin.AlertsTab', 'Bottom')) + self.PreviewGroupBox.setTitle(UiStrings.Preview) + self.FontPreview.setText(UiStrings.OLPV2) def onBackgroundColorButtonClicked(self): new_color = QtGui.QColorDialog.getColor( @@ -148,9 +130,6 @@ class AlertsTab(SettingsTab): def onFontComboBoxClicked(self): self.updateDisplay() - def onLocationComboBoxClicked(self, location): - self.location = location - def onFontColorButtonClicked(self): new_color = QtGui.QColorDialog.getColor( QtGui.QColor(self.font_color), self) @@ -188,7 +167,7 @@ class AlertsTab(SettingsTab): u'background-color: %s' % self.font_color) self.BackgroundColorButton.setStyleSheet( u'background-color: %s' % self.bg_color) - self.LocationComboBox.setCurrentIndex(self.location) + self.verticalComboBox.setCurrentIndex(self.location) font = QtGui.QFont() font.setFamily(self.font_face) self.FontComboBox.setCurrentFont(font) @@ -197,14 +176,14 @@ class AlertsTab(SettingsTab): def save(self): settings = QtCore.QSettings() settings.beginGroup(self.settingsSection) - self.font_face = self.FontComboBox.currentFont().family() settings.setValue(u'background color', QtCore.QVariant(self.bg_color)) settings.setValue(u'font color', QtCore.QVariant(self.font_color)) settings.setValue(u'font size', QtCore.QVariant(self.font_size)) + self.font_face = self.FontComboBox.currentFont().family() settings.setValue(u'font face', QtCore.QVariant(self.font_face)) settings.setValue(u'timeout', QtCore.QVariant(self.timeout)) - settings.setValue(u'location', - QtCore.QVariant(self.LocationComboBox.currentIndex())) + self.location = self.verticalComboBox.currentIndex() + settings.setValue(u'location', QtCore.QVariant(self.location)) settings.endGroup() def updateDisplay(self): diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index e3b2ad4aa..b992552f1 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -37,7 +37,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) @@ -61,14 +62,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') @@ -135,40 +128,15 @@ class BiblePlugin(Plugin): u'title': translate('BiblesPlugin', 'Bibles', 'container title') } # Middle Header Bar - ## Import Action ## - self.textStrings[StringContent.Import] = { - u'title': translate('BiblesPlugin', '&Import'), - u'tooltip': translate('BiblesPlugin', 'Import a Bible') - } - ## New Action ## - self.textStrings[StringContent.New] = { - u'title': translate('BiblesPlugin', '&Add'), - u'tooltip': translate('BiblesPlugin', 'Add a new Bible') - } - ## Edit Action ## - self.textStrings[StringContent.Edit] = { - u'title': translate('BiblesPlugin', '&Edit'), - u'tooltip': translate('BiblesPlugin', 'Edit the selected Bible') - } - ## Delete Action ## - self.textStrings[StringContent.Delete] = { - u'title': translate('BiblesPlugin', '&Delete'), - u'tooltip': translate('BiblesPlugin', 'Delete the selected Bible') - } - ## Preview Action ## - self.textStrings[StringContent.Preview] = { - u'title': translate('BiblesPlugin', 'Preview'), - u'tooltip': translate('BiblesPlugin', 'Preview the selected Bible') - } - ## Send Live Action ## - self.textStrings[StringContent.Live] = { - u'title': translate('BiblesPlugin', 'Live'), - u'tooltip': translate('BiblesPlugin', - 'Send the selected Bible live') - } - ## Add to Service Action ## - self.textStrings[StringContent.Service] = { - u'title': translate('BiblesPlugin', 'Service'), - u'tooltip': translate('BiblesPlugin', + 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') } + self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index f21dd0e07..463c838c9 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -33,7 +33,7 @@ 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 critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard @@ -727,34 +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 (*)' % translate('BiblesPlugin.ImportWizardForm', - 'All Files') - 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/__init__.py b/openlp/plugins/bibles/lib/__init__.py index e6fff1cc8..314651ced 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -33,6 +33,13 @@ import re log = logging.getLogger(__name__) def get_reference_match(match_type): + """ + Provides the regexes and matches to use while parsing strings for bible + references. + + ``match_type`` + The type of reference information trying to be extracted in this call. + """ local_separator = unicode(u':;;\s*[:vV]\s*;;-;;\s*-\s*;;,;;\s*,\s*;;end' ).split(u';;') # English # local_separator = unicode(u',;;\s*,\s*;;-;;\s*-\s*;;.;;\.;;[Ee]nde' @@ -62,69 +69,90 @@ def get_reference_match(match_type): def parse_reference(reference): """ This is the next generation über-awesome function that takes a person's - typed in string and converts it to a reference list, a list of references to - be queried from the Bible database files. + typed in string and converts it to a list of references to be queried from + the Bible database files. - This is a user manual like description, how the references are working. + ``reference`` + A string. The Bible reference to parse. - - Each reference starts with the book name. A chapter name is manditory. - ``John 3`` refers to Gospel of John chapter 3 - - A reference range can be given after a range separator. - ``John 3-5`` refers to John chapters 3 to 5 - - Single verses can be addressed after a verse separator - ``John 3:16`` refers to John chapter 3 verse 16 - ``John 3:16-4:3`` refers to John chapter 3 verse 16 to chapter 4 verse 3 - - After a verse reference all further single values are treat as verse in - the last selected chapter. - ``John 3:16-18`` refers to John chapter 3 verses 16 to 18 - - After a list separator it is possible to refer to additional verses. They - are build analog to the first ones. This way it is possible to define each - number of verse references. It is not possible to refer to verses in - additional books. - ``John 3:16,18`` refers to John chapter 3 verses 16 and 18 - ``John 3:16-18,20`` refers to John chapter 3 verses 16 to 18 and 20 - ``John 3:16-18,4:1`` refers to John chapter 3 verses 16 to 18 and - chapter 3 verse 1 - - If there is a range separator without further verse declaration the last - refered chapter is addressed until the end. - - ``range_string`` is a regular expression which matches for verse range - declarations: - - 1. ``(?:(?P[0-9]+)%(sep_v)s)?`` - It starts with a optional chapter reference ``from_chapter`` followed by - a verse separator. - 2. ``(?P[0-9]+)`` - The verse reference ``from_verse`` is manditory - 3. ``(?P%(sep_r)s(?:`` ... ``|%(sep_e)s)?)?`` - A ``range_to`` declaration is optional. It starts with a range separator - and contains optional a chapter and verse declaration or a end - separator. - 4. ``(?:(?P[0-9]+)%(sep_v)s)?`` - The ``to_chapter`` reference with separator is equivalent to group 1. - 5. ``(?P[0-9]+)`` - The ``to_verse`` reference is equivalent to group 2. - - The full reference is matched against get_reference_match(u'full'). This - regular expression looks like this: - - 1. ``^\s*(?!\s)(?P[\d]*[^\d]+)(?(?:`` + range_string + ``(?:%(sep_l)s|(?=\s*$)))+)\s*$`` - The second group contains all ``ranges``. This can be multiple - declarations of a range_string separated by a list separator. + Returns ``None`` or a reference list. The reference list is a list of tuples, with each tuple structured like this:: (book, chapter, from_verse, to_verse) + + For example:: + + [(u'John', 3, 16, 18), (u'John', 4, 1, 1)] - ``reference`` - The bible reference to parse. + **Reference string details:** + + Each reference starts with the book name and a chapter number. These are + both mandatory. + + * ``John 3`` refers to Gospel of John chapter 3 + + A reference range can be given after a range separator. + + * ``John 3-5`` refers to John chapters 3 to 5 + + Single verses can be addressed after a verse separator. + + * ``John 3:16`` refers to John chapter 3 verse 16 + * ``John 3:16-4:3`` refers to John chapter 3 verse 16 to chapter 4 verse 3 + + After a verse reference all further single values are treat as verse in + the last selected chapter. + + * ``John 3:16-18`` refers to John chapter 3 verses 16 to 18 + + After a list separator it is possible to refer to additional verses. They + are build analog to the first ones. This way it is possible to define each + number of verse references. It is not possible to refer to verses in + additional books. + + * ``John 3:16,18`` refers to John chapter 3 verses 16 and 18 + * ``John 3:16-18,20`` refers to John chapter 3 verses 16 to 18 and 20 + * ``John 3:16-18,4:1`` refers to John chapter 3 verses 16 to 18 and + chapter 4 verse 1 + + If there is a range separator without further verse declaration the last + refered chapter is addressed until the end. + + ``range_string`` is a regular expression which matches for verse range + declarations: + + ``(?:(?P[0-9]+)%(sep_v)s)?`` + It starts with a optional chapter reference ``from_chapter`` followed by + a verse separator. + + ``(?P[0-9]+)`` + The verse reference ``from_verse`` is manditory + + ``(?P%(sep_r)s(?:`` ... ``|%(sep_e)s)?)?`` + A ``range_to`` declaration is optional. It starts with a range separator + and contains optional a chapter and verse declaration or a end + separator. + + ``(?:(?P[0-9]+)%(sep_v)s)?`` + The ``to_chapter`` reference with separator is equivalent to group 1. + + ``(?P[0-9]+)`` + The ``to_verse`` reference is equivalent to group 2. + + The full reference is matched against get_reference_match(u'full'). This + regular expression looks like this: + + ``^\s*(?!\s)(?P[\d]*[^\d]+)(?(?:`` + range_string + ``(?:%(sep_l)s|(?=\s*$)))+)\s*$`` + The second group contains all ``ranges``. This can be multiple + declarations of a range_string separated by a list separator. - Returns None or a reference list. """ log.debug(u'parse_reference("%s")', reference) match = get_reference_match(u'full').match(reference) @@ -194,7 +222,7 @@ def parse_reference(reference): class SearchResults(object): """ - Encapsulate a set of search results. This is Bible-type independent. + Encapsulate a set of search results. This is Bible-type independent. """ def __init__(self, book, chapter, verselist): """ @@ -207,7 +235,8 @@ class SearchResults(object): The chapter of the book. ``verselist`` - The list of verses for this reading + The list of verses for this reading. + """ self.book = book self.chapter = chapter 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 54cb38501..65245fc8a 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -47,21 +47,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 ' @@ -112,54 +105,20 @@ class CustomPlugin(Plugin): u'title': translate('CustomsPlugin', 'Custom', 'container title') } # Middle Header Bar - ## Import Action ## - self.textStrings[StringContent.Import] = { - u'title': translate('CustomsPlugin', 'Import'), - u'tooltip': translate('CustomsPlugin', - 'Import a Custom') - } - ## Load Action ## - self.textStrings[StringContent.Load] = { - u'title': translate('CustomsPlugin', 'Load'), - u'tooltip': translate('CustomsPlugin', - 'Load a new Custom') - } - ## New Action ## - self.textStrings[StringContent.New] = { - u'title': translate('CustomsPlugin', 'Add'), - u'tooltip': translate('CustomsPlugin', - 'Add a new Custom') - } - ## Edit Action ## - self.textStrings[StringContent.Edit] = { - u'title': translate('CustomsPlugin', 'Edit'), - u'tooltip': translate('CustomsPlugin', - 'Edit the selected Custom') - } - ## Delete Action ## - self.textStrings[StringContent.Delete] = { - u'title': translate('CustomsPlugin', 'Delete'), - u'tooltip': translate('CustomsPlugin', - 'Delete the selected Custom') - } - ## Preview Action ## - self.textStrings[StringContent.Preview] = { - u'title': translate('CustomsPlugin', 'Preview'), - u'tooltip': translate('CustomsPlugin', - 'Preview the selected Custom') - } - ## Send Live Action ## - self.textStrings[StringContent.Live] = { - u'title': translate('CustomsPlugin', 'Live'), - u'tooltip': translate('CustomsPlugin', - 'Send the selected Custom live') - } - ## Add to Service Action ## - self.textStrings[StringContent.Service] = { - u'title': translate('CustomsPlugin', 'Service'), - u'tooltip': translate('CustomsPlugin', + 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') } + self.setPluginUiTextStrings(tooltips) def finalise(self): """ diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index b7887aa90..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 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) @@ -107,13 +107,11 @@ class Ui_CustomEditDialog(object): translate('CustomPlugin.EditCustomForm', 'Edit Custom Slides')) self.titleLabel.setText( translate('CustomPlugin.EditCustomForm', '&Title:')) - self.addButton.setText( - translate('CustomPlugin.EditCustomForm', '&Add')) + self.addButton.setText(UiStrings.Add) self.addButton.setToolTip( translate('CustomPlugin.EditCustomForm', 'Add a new slide at ' 'bottom.')) - self.editButton.setText( - translate('CustomPlugin.EditCustomForm', '&Edit')) + self.editButton.setText(UiStrings.Edit) self.editButton.setToolTip( translate('CustomPlugin.EditCustomForm', 'Edit the selected ' 'slide.')) 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 ea118d3ec..2cc0f1d93 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -35,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 ' @@ -72,45 +69,15 @@ class ImagePlugin(Plugin): u'title': translate('ImagePlugin', 'Images', 'container title') } # Middle Header Bar - ## Load Button ## - self.textStrings[StringContent.Load] = { - u'title': translate('ImagePlugin', 'Load'), - u'tooltip': translate('ImagePlugin', - 'Load a new Image') - } - ## New Button ## - self.textStrings[StringContent.New] = { - u'title': translate('ImagePlugin', 'Add'), - u'tooltip': translate('ImagePlugin', - 'Add a new Image') - } - ## Edit Button ## - self.textStrings[StringContent.Edit] = { - u'title': translate('ImagePlugin', 'Edit'), - u'tooltip': translate('ImagePlugin', - 'Edit the selected Image') - } - ## Delete Button ## - self.textStrings[StringContent.Delete] = { - u'title': translate('ImagePlugin', 'Delete'), - u'tooltip': translate('ImagePlugin', - 'Delete the selected Image') - } - ## Preview ## - self.textStrings[StringContent.Preview] = { - u'title': translate('ImagePlugin', 'Preview'), - u'tooltip': translate('ImagePlugin', - 'Preview the selected Image') - } - ## Live Button ## - self.textStrings[StringContent.Live] = { - u'title': translate('ImagePlugin', 'Live'), - u'tooltip': translate('ImagePlugin', - 'Send the selected Image live') - } - ## Add to service Button ## - self.textStrings[StringContent.Service] = { - u'title': translate('ImagePlugin', 'Service'), - u'tooltip': translate('ImagePlugin', + 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 71027881c..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.ui import critical_error_message_box +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(translate('ImagePlugin.MediaItem', 'All Files'))) - 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) @@ -84,8 +70,6 @@ class ImageMediaItem(MediaManagerItem): def initialise(self): log.debug(u'initialise') self.listView.clear() - self.listView.setSelectionMode( - QtGui.QAbstractItemView.ExtendedSelection) self.listView.setIconSize(QtCore.QSize(88, 50)) self.servicePath = os.path.join( AppLocation.get_section_data_path(self.settingsSection), diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index ea1b679b4..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,8 +152,7 @@ class MediaMediaItem(MediaManagerItem): return False def initialise(self): - self.listView.setSelectionMode( - QtGui.QAbstractItemView.ExtendedSelection) + self.listView.clear() self.listView.setIconSize(QtCore.QSize(88, 50)) self.loadList(SettingsManager.load_list(self.settingsSection, self.settingsSection)) @@ -178,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 ad6087daf..ee413aa8c 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -38,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) @@ -75,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.') @@ -101,45 +95,15 @@ class MediaPlugin(Plugin): u'title': translate('MediaPlugin', 'Media', 'container title') } # Middle Header Bar - ## Load Action ## - self.textStrings[StringContent.Load] = { - u'title': translate('MediaPlugin', 'Load'), - u'tooltip': translate('MediaPlugin', - 'Load a new Media') - } - ## New Action ## - self.textStrings[StringContent.New] = { - u'title': translate('MediaPlugin', 'Add'), - u'tooltip': translate('MediaPlugin', - 'Add a new Media') - } - ## Edit Action ## - self.textStrings[StringContent.Edit] = { - u'title': translate('MediaPlugin', 'Edit'), - u'tooltip': translate('MediaPlugin', - 'Edit the selected Media') - } - ## Delete Action ## - self.textStrings[StringContent.Delete] = { - u'title': translate('MediaPlugin', 'Delete'), - u'tooltip': translate('MediaPlugin', - 'Delete the selected Media') - } - ## Preview Action ## - self.textStrings[StringContent.Preview] = { - u'title': translate('MediaPlugin', 'Preview'), - u'tooltip': translate('MediaPlugin', - 'Preview the selected Media') - } - ## Send Live Action ## - self.textStrings[StringContent.Live] = { - u'title': translate('MediaPlugin', 'Live'), - u'tooltip': translate('MediaPlugin', - 'Send the selected Media live') - } - ## Add to Service Action ## - self.textStrings[StringContent.Service] = { - u'title': translate('MediaPlugin', 'Service'), - u'tooltip': translate('MediaPlugin', + 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 f12d36dc8..ad5faa2dd 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -69,7 +69,8 @@ class ImpressController(PresentationController): Initialise the class """ log.debug(u'Initialising') - PresentationController.__init__(self, plugin, u'Impress') + PresentationController.__init__(self, plugin, u'Impress', + ImpressDocument) self.supports = [u'odp'] self.alsosupports = [u'ppt', u'pps', u'pptx', u'ppsx'] self.process = None @@ -144,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): """ @@ -165,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(): @@ -183,14 +191,6 @@ class ImpressController(PresentationController): except: log.exception(u'Failed to terminate OpenOffice') - def add_doc(self, name): - """ - Called when a new Impress document is opened - """ - log.debug(u'Add Doc OpenOffice') - doc = ImpressDocument(self, name) - self.docs.append(doc) - return doc class ImpressDocument(PresentationDocument): """ @@ -431,35 +431,36 @@ class ImpressDocument(PresentationDocument): def get_slide_text(self, slide_no): """ - Returns the text on the slide + Returns the text on the slide. ``slide_no`` - The slide the text is required for, starting at 1 + The slide the text is required for, starting at 1 + """ + return self.__get_text_from_page(slide_no) + + def get_slide_notes(self, slide_no): + """ + Returns the text in the slide notes. + + ``slide_no`` + The slide the notes are required for, starting at 1 + """ + return self.__get_text_from_page(slide_no, True) + + def __get_text_from_page(self, slide_no, notes=False): + """ + Return any text extracted from the presentation page. + + ``notes`` + A boolean. If set the method searches the notes of the slide. """ - doc = self.document - pages = doc.getDrawPages() text = '' + pages = self.document.getDrawPages() page = pages.getByIndex(slide_no - 1) + if notes: + page = page.getNotesPage() for idx in range(page.getCount()): shape = page.getByIndex(idx) if shape.supportsService("com.sun.star.drawing.Text"): text += shape.getString() + '\n' return text - - def get_slide_notes(self, slide_no): - """ - Returns the text on the slide - - ``slide_no`` - The slide the notes are required for, starting at 1 - """ - doc = self.document - pages = doc.getDrawPages() - text = '' - page = pages.getByIndex(slide_no - 1) - notes = page.getNotesPage() - for idx in range(notes.getCount()): - shape = notes.getByIndex(idx) - if shape.supportsService("com.sun.star.drawing.Text"): - text += shape.getString() + '\n' - return text diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index a0173cb27..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(), @@ -186,7 +172,7 @@ class PresentationMediaItem(MediaManagerItem): controller_name = self.findControllerByType(filename) if controller_name: controller = self.controllers[controller_name] - doc = controller.add_doc(unicode(file)) + doc = controller.add_document(unicode(file)) thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png') preview = doc.get_thumbnail_path(1, True) if not preview and not initialLoad: @@ -226,7 +212,7 @@ class PresentationMediaItem(MediaManagerItem): filepath = unicode(item.data( QtCore.Qt.UserRole).toString()) for cidx in self.controllers: - doc = self.controllers[cidx].add_doc(filepath) + doc = self.controllers[cidx].add_document(filepath) doc.presentation_deleted() doc.close_presentation() for row in row_list: @@ -260,7 +246,7 @@ class PresentationMediaItem(MediaManagerItem): return False controller = self.controllers[service_item.shortname] (path, name) = os.path.split(filename) - doc = controller.add_doc(filename) + doc = controller.add_document(filename) if doc.get_thumbnail_path(1, True) is None: doc.load_presentation() i = 1 diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 4d926ad3d..b7c64ccee 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -59,7 +59,7 @@ class Controller(object): self.controller = controller if self.doc is not None: self.shutdown() - self.doc = self.controller.add_doc(file) + self.doc = self.controller.add_document(file) if not self.doc.load_presentation(): # Display error message to user # Inform slidecontroller that the action failed? diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 012f8534a..eb00da255 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -53,7 +53,8 @@ class PowerpointController(PresentationController): Initialise the class """ log.debug(u'Initialising') - PresentationController.__init__(self, plugin, u'Powerpoint') + PresentationController.__init__(self, plugin, u'Powerpoint', + PowerpointDocument) self.supports = [u'ppt', u'pps', u'pptx', u'ppsx'] self.process = None @@ -97,14 +98,6 @@ class PowerpointController(PresentationController): pass self.process = None - def add_doc(self, name): - """ - Called when a new powerpoint document is opened - """ - log.debug(u'Add Doc PowerPoint') - doc = PowerpointDocument(self, name) - self.docs.append(doc) - return doc class PowerpointDocument(PresentationDocument): """ @@ -154,8 +147,10 @@ class PowerpointDocument(PresentationDocument): """ if self.check_thumbnails(): return - self.presentation.Export(os.path.join(self.get_thumbnail_folder(), ''), - 'png', 320, 240) + for num in range(0, self.presentation.Slides.Count): + self.presentation.Slides(num + 1).Export(os.path.join( + self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), + 'png', 320, 240) def close_presentation(self): """ @@ -291,13 +286,7 @@ class PowerpointDocument(PresentationDocument): ``slide_no`` The slide the text is required for, starting at 1. """ - text = '' - shapes = self.presentation.Slides(slide_no).Shapes - for idx in range(shapes.Count): - shape = shapes(idx + 1) - if shape.HasTextFrame: - text += shape.TextFrame.TextRange.Text + '\n' - return text + return _get_text_from_shapes(self.presentation.Slides(slide_no).Shapes) def get_slide_notes(self, slide_no): """ @@ -306,10 +295,19 @@ class PowerpointDocument(PresentationDocument): ``slide_no`` The slide the notes are required for, starting at 1. """ - text = '' - shapes = self.presentation.Slides(slide_no).NotesPage.Shapes - for idx in range(shapes.Count): - shape = shapes(idx + 1) - if shape.HasTextFrame: - text += shape.TextFrame.TextRange.Text + '\n' - return text \ No newline at end of file + return _get_text_from_shapes( + self.presentation.Slides(slide_no).NotesPage.Shapes) + +def _get_text_from_shapes(shapes): + """ + Returns any text extracted from the shapes on a presentation slide. + + ``shapes`` + A set of shapes to search for text. + """ + text = '' + for idx in range(shapes.Count): + shape = shapes(idx + 1) + if shape.HasTextFrame: + text += shape.TextFrame.TextRange.Text + '\n' + return text diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 4c8e7bf95..fc839195c 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -49,7 +49,8 @@ class PptviewController(PresentationController): """ log.debug(u'Initialising') self.process = None - PresentationController.__init__(self, plugin, u'Powerpoint Viewer') + PresentationController.__init__(self, plugin, u'Powerpoint Viewer', + PptviewDocument) self.supports = [u'ppt', u'pps', u'pptx', u'ppsx'] def check_available(self): @@ -93,14 +94,6 @@ class PptviewController(PresentationController): while self.docs: self.docs[0].close_presentation() - def add_doc(self, name): - """ - Called when a new powerpoint document is opened - """ - log.debug(u'Add Doc PPTView') - doc = PptviewDocument(self, name) - self.docs.append(doc) - return doc class PptviewDocument(PresentationDocument): """ @@ -161,8 +154,9 @@ class PptviewDocument(PresentationDocument): being shut down """ log.debug(u'ClosePresentation') - self.controller.process.ClosePPT(self.pptid) - self.pptid = -1 + if self.controller.process: + self.controller.process.ClosePPT(self.pptid) + self.pptid = -1 self.controller.remove_doc(self) def is_loaded(self): @@ -247,4 +241,4 @@ class PptviewDocument(PresentationDocument): """ Triggers the previous slide on the running presentation """ - self.controller.process.PrevStep(self.pptid) \ No newline at end of file + self.controller.process.PrevStep(self.pptid) diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 42f7b654a..8b282e0f4 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -35,151 +35,6 @@ from openlp.core.utils import AppLocation log = logging.getLogger(__name__) -class PresentationController(object): - """ - This class is used to control interactions with presentation applications - by creating a runtime environment. This is a base class for presentation - controllers to inherit from. - - To create a new controller, take a copy of this file and name it so it ends - with ``controller.py``, i.e. ``foobarcontroller.py``. Make sure it inherits - :class:`~openlp.plugins.presentations.lib.presentationcontroller.PresentationController`, - and then fill in the blanks. If possible try to make sure it loads on all - platforms, usually by using :mod:``os.name`` checks, although - ``__init__``, ``check_available`` and ``presentation_deleted`` should - always be implemented. - - See :class:`~openlp.plugins.presentations.lib.impresscontroller.ImpressController`, - :class:`~openlp.plugins.presentations.lib.powerpointcontroller.PowerpointController` or - :class:`~openlp.plugins.presentations.lib.pptviewcontroller.PptviewController` - for examples. - - **Basic Attributes** - - ``name`` - The name that appears in the options and the media manager - - ``enabled`` - The controller is enabled - - ``available`` - The controller is available on this machine. Set by init via - call to check_available - - ``plugin`` - The presentationplugin object - - ``supports`` - The primary native file types this application supports - - ``alsosupports`` - Other file types the application can import, although not necessarily - the first choice due to potential incompatibilities - - **Hook Functions** - - ``kill()`` - Called at system exit to clean up any running presentations - - ``check_available()`` - Returns True if presentation application is installed/can run on this - machine - - ``presentation_deleted()`` - Deletes presentation specific files, e.g. thumbnails - - """ - log.info(u'PresentationController loaded') - - def __init__(self, plugin=None, name=u'PresentationController'): - """ - This is the constructor for the presentationcontroller object. This - provides an easy way for descendent plugins to populate common data. - This method *must* be overridden, like so:: - - class MyPresentationController(PresentationController): - def __init__(self, plugin): - PresentationController.__init( - self, plugin, u'My Presenter App') - - ``plugin`` - Defaults to *None*. The presentationplugin object - - ``name`` - Name of the application, to appear in the application - """ - self.supports = [] - self.alsosupports = [] - self.docs = [] - self.plugin = plugin - self.name = name - self.settings_section = self.plugin.settingsSection - self.available = self.check_available() - self.temp_folder = os.path.join( - AppLocation.get_section_data_path(self.settings_section), name) - self.thumbnail_folder = os.path.join( - AppLocation.get_section_data_path(self.settings_section), - u'thumbnails') - self.thumbnail_prefix = u'slide' - if not os.path.isdir(self.thumbnail_folder): - os.makedirs(self.thumbnail_folder) - if not os.path.isdir(self.temp_folder): - os.makedirs(self.temp_folder) - - def enabled(self): - """ - Return whether the controller is currently enabled - """ - if self.available: - return QtCore.QSettings().value( - self.settings_section + u'/' + self.name, - QtCore.QVariant(QtCore.Qt.Checked)).toInt()[0] == \ - QtCore.Qt.Checked - else: - return False - - def check_available(self): - """ - Presentation app is able to run on this machine - """ - return False - - def start_process(self): - """ - Loads a running version of the presentation application in the - background. - """ - pass - - def kill(self): - """ - Called at system exit to clean up any running presentations and - close the application - """ - log.debug(u'Kill') - self.close_presentation() - - def add_doc(self, name): - """ - Called when a new presentation document is opened - """ - doc = PresentationDocument(self, name) - self.docs.append(doc) - return doc - - def remove_doc(self, doc=None): - """ - Called to remove an open document from the collection - """ - log.debug(u'remove_doc Presentation') - if doc is None: - return - if doc in self.docs: - self.docs.remove(doc) - - def close_presentation(self): - pass - class PresentationDocument(object): """ Base class for presentation documents to inherit from. @@ -440,4 +295,152 @@ class PresentationDocument(object): ``slide_no`` The slide the notes are required for, starting at 1 """ - return '' \ No newline at end of file + return '' + + +class PresentationController(object): + """ + This class is used to control interactions with presentation applications + by creating a runtime environment. This is a base class for presentation + controllers to inherit from. + + To create a new controller, take a copy of this file and name it so it ends + with ``controller.py``, i.e. ``foobarcontroller.py``. Make sure it inherits + :class:`~openlp.plugins.presentations.lib.presentationcontroller.PresentationController`, + and then fill in the blanks. If possible try to make sure it loads on all + platforms, usually by using :mod:``os.name`` checks, although + ``__init__``, ``check_available`` and ``presentation_deleted`` should + always be implemented. + + See :class:`~openlp.plugins.presentations.lib.impresscontroller.ImpressController`, + :class:`~openlp.plugins.presentations.lib.powerpointcontroller.PowerpointController` or + :class:`~openlp.plugins.presentations.lib.pptviewcontroller.PptviewController` + for examples. + + **Basic Attributes** + + ``name`` + The name that appears in the options and the media manager + + ``enabled`` + The controller is enabled + + ``available`` + The controller is available on this machine. Set by init via + call to check_available + + ``plugin`` + The presentationplugin object + + ``supports`` + The primary native file types this application supports + + ``alsosupports`` + Other file types the application can import, although not necessarily + the first choice due to potential incompatibilities + + **Hook Functions** + + ``kill()`` + Called at system exit to clean up any running presentations + + ``check_available()`` + Returns True if presentation application is installed/can run on this + machine + + ``presentation_deleted()`` + Deletes presentation specific files, e.g. thumbnails + + """ + log.info(u'PresentationController loaded') + + def __init__(self, plugin=None, name=u'PresentationController', + document_class=PresentationDocument): + """ + This is the constructor for the presentationcontroller object. This + provides an easy way for descendent plugins to populate common data. + This method *must* be overridden, like so:: + + class MyPresentationController(PresentationController): + def __init__(self, plugin): + PresentationController.__init( + self, plugin, u'My Presenter App') + + ``plugin`` + Defaults to *None*. The presentationplugin object + + ``name`` + Name of the application, to appear in the application + """ + self.supports = [] + self.alsosupports = [] + self.docs = [] + self.plugin = plugin + self.name = name + self.document_class = document_class + self.settings_section = self.plugin.settingsSection + self.available = self.check_available() + self.temp_folder = os.path.join( + AppLocation.get_section_data_path(self.settings_section), name) + self.thumbnail_folder = os.path.join( + AppLocation.get_section_data_path(self.settings_section), + u'thumbnails') + self.thumbnail_prefix = u'slide' + if not os.path.isdir(self.thumbnail_folder): + os.makedirs(self.thumbnail_folder) + if not os.path.isdir(self.temp_folder): + os.makedirs(self.temp_folder) + + def enabled(self): + """ + Return whether the controller is currently enabled + """ + if self.available: + return QtCore.QSettings().value( + self.settings_section + u'/' + self.name, + QtCore.QVariant(QtCore.Qt.Checked)).toInt()[0] == \ + QtCore.Qt.Checked + else: + return False + + def check_available(self): + """ + Presentation app is able to run on this machine + """ + return False + + def start_process(self): + """ + Loads a running version of the presentation application in the + background. + """ + pass + + def kill(self): + """ + Called at system exit to clean up any running presentations and + close the application + """ + log.debug(u'Kill') + self.close_presentation() + + def add_document(self, name): + """ + Called when a new presentation document is opened + """ + document = self.document_class(self, name) + self.docs.append(document) + return document + + def remove_doc(self, doc=None): + """ + Called to remove an open document from the collection + """ + log.debug(u'remove_doc Presentation') + if doc is None: + return + if doc in self.docs: + self.docs.remove(doc) + + def close_presentation(self): + pass 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 17417df58..ece25e363 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -167,33 +167,18 @@ class PresentationPlugin(Plugin): 'container title') } # Middle Header Bar - ## Load Action ## - self.textStrings[StringContent.Load] = { - u'title': translate('PresentationPlugin', 'Load'), - u'tooltip': translate('PresentationPlugin', - 'Load a new Presentation') - } - ## Delete Action ## - self.textStrings[StringContent.Delete] = { - u'title': translate('PresentationPlugin', 'Delete'), - u'tooltip': translate('PresentationPlugin', - 'Delete the selected Presentation') - } - ## Preview Action ## - self.textStrings[StringContent.Preview] = { - u'title': translate('PresentationPlugin', 'Preview'), - u'tooltip': translate('PresentationPlugin', - 'Preview the selected Presentation') - } - ## Send Live Action ## - self.textStrings[StringContent.Live] = { - u'title': translate('PresentationPlugin', 'Live'), - u'tooltip': translate('PresentationPlugin', - 'Send the selected Presentation live') - } - ## Add to Service Action ## - self.textStrings[StringContent.Service] = { - u'title': translate('PresentationPlugin', 'Service'), - u'tooltip': translate('PresentationPlugin', + 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 e9be62830..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 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) @@ -257,19 +257,15 @@ class Ui_EditSongDialog(object): translate('SongsPlugin.EditSongForm', '&Lyrics:')) self.verseOrderLabel.setText( translate('SongsPlugin.EditSongForm', '&Verse order:')) - self.verseAddButton.setText( - translate('SongsPlugin.EditSongForm', '&Add')) - self.verseEditButton.setText( - translate('SongsPlugin.EditSongForm', '&Edit')) + self.verseAddButton.setText(UiStrings.Add) + self.verseEditButton.setText(UiStrings.Edit) self.verseEditAllButton.setText( translate('SongsPlugin.EditSongForm', 'Ed&it All')) - self.verseDeleteButton.setText( - translate('SongsPlugin.EditSongForm', '&Delete')) + self.verseDeleteButton.setText(UiStrings.Delete) self.songTabWidget.setTabText( self.songTabWidget.indexOf(self.lyricsTab), translate('SongsPlugin.EditSongForm', 'Title && Lyrics')) - self.authorsGroupBox.setTitle( - translate('SongsPlugin.EditSongForm', 'Authors')) + self.authorsGroupBox.setTitle(UiStrings.Authors) self.authorAddButton.setText( translate('SongsPlugin.EditSongForm', '&Add to Song')) self.authorRemoveButton.setText( @@ -292,8 +288,7 @@ class Ui_EditSongDialog(object): self.songTabWidget.indexOf(self.authorsTab), translate('SongsPlugin.EditSongForm', 'Authors, Topics && Song Book')) - self.themeGroupBox.setTitle( - translate('SongsPlugin.EditSongForm', 'Theme')) + self.themeGroupBox.setTitle(UiStrings.Theme) self.themeAddButton.setText( translate('SongsPlugin.EditSongForm', 'New &Theme')) self.rightsGroupBox.setTitle( diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 8536d38b8..39f1ba256 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -161,6 +161,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): def newSong(self): log.debug(u'New Song') + self.song = None self.initialise() self.songTabWidget.setCurrentIndex(0) self.titleEdit.setText(u'') 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..f331fbcb9 --- /dev/null +++ b/openlp/plugins/songs/forms/songexportform.py @@ -0,0 +1,370 @@ +# -*- 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() + self.searchLineEdit.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 visible songs unchecked. + """ + for row in range(self.availableListWidget.count()): + item = self.availableListWidget.item(row) + if not item.isHidden(): + item.setCheckState(QtCore.Qt.Unchecked) + + def onCheckButtonClicked(self): + """ + The *checkButton* has been clicked. Set all visible songs checked. + """ + for row in range(self.availableListWidget.count()): + item = self.availableListWidget.item(row) + if not item.isHidden(): + 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 a9af7f6b2..eda3d6750 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -32,7 +32,7 @@ import os from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, SettingsManager, translate -from openlp.core.lib.ui import critical_error_message_box +from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard from openlp.plugins.songs.lib.importer import SongFormat @@ -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) @@ -215,8 +223,7 @@ class SongImportForm(OpenLPWizard): 'Select the import format, and where to import from.')) self.formatLabel.setText( translate('SongsPlugin.ImportWizardForm', 'Format:')) - self.formatComboBox.setItemText(0, - translate('SongsPlugin.ImportWizardForm', 'OpenLP 2.0')) + self.formatComboBox.setItemText(0, UiStrings.OLPV2) self.formatComboBox.setItemText(1, translate('SongsPlugin.ImportWizardForm', 'openlp.org 1.x')) self.formatComboBox.setItemText(2, @@ -238,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( @@ -302,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( @@ -439,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. @@ -485,12 +480,11 @@ class SongImportForm(OpenLPWizard): The file extension filters. It should contain the file descriptions as well as the file extensions. For example:: - u'SongBeamer files (*.sng)' + u'SongBeamer Files (*.sng)' """ if filters: filters += u';;' - filters += u'%s (*)' % translate('SongsPlugin.ImportWizardForm', - 'All Files') + filters += u'%s (*)' % UiStrings.AllFiles filenames = QtGui.QFileDialog.getOpenFileNames(self, title, SettingsManager.get_last_dir(self.plugin.settingsSection, 1), filters) @@ -615,7 +609,7 @@ class SongImportForm(OpenLPWizard): 'Select Songs of Fellowship Files'), self.songsOfFellowshipFileListWidget, u'%s (*.rtf)' % translate('SongsPlugin.ImportWizardForm', - 'Songs Of Felloship Song Files') + 'Songs Of Fellowship Song Files') ) def onSongsOfFellowshipRemoveButtonClicked(self): @@ -665,7 +659,7 @@ class SongImportForm(OpenLPWizard): translate('SongsPlugin.ImportWizardForm', 'Select SongBeamer Files'), self.songBeamerFileListWidget, u'%s (*.sng)' % - translate('SongsPlugin.ImportWizardForm', 'SongBeamer files') + translate('SongsPlugin.ImportWizardForm', 'SongBeamer Files') ) def onSongBeamerRemoveButtonClicked(self): @@ -673,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): """ @@ -699,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): @@ -775,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/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py index 51fafcc7a..0fa3335dc 100644 --- a/openlp/plugins/songs/forms/songmaintenancedialog.py +++ b/openlp/plugins/songs/forms/songmaintenancedialog.py @@ -27,6 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate +from openlp.core.lib.ui import UiStrings class Ui_SongMaintenanceDialog(object): def setupUi(self, songMaintenanceDialog): @@ -145,30 +146,21 @@ class Ui_SongMaintenanceDialog(object): def retranslateUi(self, songMaintenanceDialog): songMaintenanceDialog.setWindowTitle( translate('SongsPlugin.SongMaintenanceForm', 'Song Maintenance')) - authorsString = translate('SongsPlugin.SongMaintenanceForm', 'Authors') + authorsString = UiStrings.Authors topicsString = translate('SongsPlugin.SongMaintenanceForm', 'Topics') booksString = translate('SongsPlugin.SongMaintenanceForm', 'Song Books') self.listItemAuthors.setText(authorsString) self.listItemTopics.setText(topicsString) self.listItemBooks.setText(booksString) - self.authorsAddButton.setText( - translate('SongsPlugin.SongMaintenanceForm', '&Add')) - self.authorsEditButton.setText( - translate('SongsPlugin.SongMaintenanceForm', '&Edit')) - self.authorsDeleteButton.setText( - translate('SongsPlugin.SongMaintenanceForm', '&Delete')) - self.topicsAddButton.setText( - translate('SongsPlugin.SongMaintenanceForm', '&Add')) - self.topicsEditButton.setText( - translate('SongsPlugin.SongMaintenanceForm', '&Edit')) - self.topicsDeleteButton.setText( - translate('SongsPlugin.SongMaintenanceForm', '&Delete')) - self.booksAddButton.setText( - translate('SongsPlugin.SongMaintenanceForm', '&Add')) - self.booksEditButton.setText( - translate('SongsPlugin.SongMaintenanceForm', '&Edit')) - self.booksDeleteButton.setText( - translate('SongsPlugin.SongMaintenanceForm', '&Delete')) + self.authorsAddButton.setText(UiStrings.Add) + self.authorsEditButton.setText(UiStrings.Edit) + self.authorsDeleteButton.setText(UiStrings.Delete) + self.topicsAddButton.setText(UiStrings.Add) + self.topicsEditButton.setText(UiStrings.Edit) + self.topicsDeleteButton.setText(UiStrings.Delete) + self.booksAddButton.setText(UiStrings.Add) + self.booksEditButton.setText(UiStrings.Edit) + self.booksDeleteButton.setText(UiStrings.Delete) typeListWidth = max(self.fontMetrics().width(authorsString), self.fontMetrics().width(topicsString), self.fontMetrics().width(booksString)) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 1eb63fbf4..1f693223c 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -457,7 +457,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onTopicDeleteButtonClick(self): """ - Delete the Book is the Book is not attached to any songs. + Delete the Book if the Book is not attached to any songs. """ self._deleteItem(Topic, self.topicsListWidget, self.resetTopics, translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'), @@ -470,7 +470,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onBookDeleteButtonClick(self): """ - Delete the Book is the Book is not attached to any songs. + Delete the Book if the Book is not attached to any songs. """ self._deleteItem(Book, self.booksListWidget, self.resetBooks, translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'), 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/__init__.py b/openlp/plugins/songs/lib/__init__.py index af8b71795..c763d70b9 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -104,6 +104,15 @@ class VerseType(object): def retrieve_windows_encoding(recommendation=None): + """ + Determines which encoding to use on an information source. The process uses + both automated detection, which is passed to this method as a + recommendation, and user confirmation to return an encoding. + + ``recommendation`` + A recommended encoding discovered programmatically for the user to + confirm. + """ # map chardet result to compatible windows standard code page codepage_mapping = {'IBM866': u'cp866', 'TIS-620': u'cp874', 'SHIFT_JIS': u'cp932', 'GB2312': u'cp936', 'HZ-GB-2312': u'cp936', diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 2bbf818b3..d9a3202b5 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -71,7 +71,6 @@ class Topic(BaseModel): def init_schema(url): - """ Setup the songs database connection and initialise the database schema. diff --git a/openlp/plugins/songs/lib/easislidesimport.py b/openlp/plugins/songs/lib/easislidesimport.py index 0b10ce428..b31e50862 100644 --- a/openlp/plugins/songs/lib/easislidesimport.py +++ b/openlp/plugins/songs/lib/easislidesimport.py @@ -81,14 +81,16 @@ class EasiSlidesImport(SongImport): def _parse_song(self, song): self._success = True - self._add_title(self.title, song.Title1, True) - self._add_alttitle(self.alternate_title, song.Title2) - self._add_number(self.song_number, song.SongNumber) + self._add_unicode_attribute(u'title', song.Title1, True) + self._add_unicode_attribute(u'alternate_title', song.Title2) + self._add_unicode_attribute(u'song_number', song.SongNumber) if self.song_number == u'0': self.song_number = u'' self._add_authors(song) - self._add_copyright(song) - self._add_book(self.song_book_name, song.BookReference) + self._add_copyright(song.Copyright) + self._add_copyright(song.LicenceAdmin1) + self._add_copyright(song.LicenceAdmin2) + self._add_unicode_attribute(u'song_book_name', song.BookReference) self._parse_and_add_lyrics(song) return self._success @@ -110,7 +112,7 @@ class EasiSlidesImport(SongImport): Signals that this attribute must exist in a valid song. """ try: - self_attribute = unicode(import_attribute).strip() + setattr(self, self_attribute, unicode(import_attribute).strip()) except UnicodeDecodeError: log.exception(u'UnicodeDecodeError decoding %s' % import_attribute) self._success = False @@ -124,7 +126,7 @@ class EasiSlidesImport(SongImport): authors = unicode(song.Writer).split(u',') for author in authors: author = author.strip() - if len(author) > 0: + if len(author): self.authors.append(author) except UnicodeDecodeError: log.exception(u'Unicode decode error while decoding Writer') @@ -132,35 +134,18 @@ class EasiSlidesImport(SongImport): except AttributeError: pass - def _add_copyright(self, song): - """ - Assign the copyright information from the import to the song being - created. - - ``song`` - The current song being imported. - """ - copyright_list = [] - self.__add_copyright_element(copyright_list, song.Copyright) - self.__add_copyright_element(copyright_list, song.LicenceAdmin1) - self.__add_copyright_element(copyright_list, song.LicenceAdmin2) - self.add_copyright(u' '.join(copyright_list)) - - def __add_copyright_element(self, copyright_list, element): + def _add_copyright(self, element): """ Add a piece of copyright to the total copyright information for the song. - ``copyright_list`` - The array to add the information to. - ``element`` The imported variable to get the data from. """ try: - copyright_list.append(unicode(element).strip()) + self.add_copyright(unicode(element).strip()) except UnicodeDecodeError: - log.exception(u'Unicode error decoding %s' % element) + log.exception(u'Unicode error on decoding copyright: %s' % element) self._success = False except AttributeError: pass @@ -285,10 +270,12 @@ class EasiSlidesImport(SongImport): # as these appeared originally in the file for [reg, vt, vn, inst] in our_verse_order: if self._listHas(verses, [reg, vt, vn, inst]): + # this is false, but needs user input + lang = None versetag = u'%s%s' % (vt, vn) versetags.append(versetag) lines = u'\n'.join(verses[reg][vt][vn][inst]) - self.verses.append([versetag, lines]) + self.verses.append([versetag, lines, lang]) SeqTypes = { u'p': u'P1', diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 6f566ff4f..230dcd8d0 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 @@ -67,10 +68,11 @@ class SongFormat(object): CCLI = 5 SongsOfFellowship = 6 Generic = 7 - #CSV = 8 EasiSlides = 8 EasyWorship = 9 SongBeamer = 10 + SongShowPlus = 11 + #CSV = 12 @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 66cff169f..283aa6c03 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -31,22 +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. @@ -55,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) @@ -147,10 +141,8 @@ class SongMediaItem(MediaManagerItem): translate('SongsPlugin.MediaItem', 'Titles')), (3, u':/songs/song_search_lyrics.png', translate('SongsPlugin.MediaItem', 'Lyrics')), - (4, u':/songs/song_search_author.png', - translate('SongsPlugin.MediaItem', 'Authors')), - (5, u':/slides/slide_theme.png', - translate('SongsPlugin.MediaItem', 'Themes')) + (4, u':/songs/song_search_author.png', UiStrings.Authors), + (5, u':/slides/slide_theme.png', UiStrings.Themes) ]) self.configUpdated() @@ -266,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 eb16f4ba4..8c6d283e3 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -36,9 +36,7 @@ from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) -class OpenSongImportError(Exception): - pass - +#TODO: Use lxml for parsing and make sure we use methods of "SongImport" . class OpenSongImport(SongImport): """ Import songs exported from OpenSong @@ -149,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: @@ -279,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 @@ -295,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/songimport.py b/openlp/plugins/songs/lib/songimport.py index 592aa7ac1..da017d4f5 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -50,6 +50,7 @@ class SongImport(QtCore.QObject): ``manager`` An instance of a SongManager, through which all database access is performed. + """ self.manager = manager self.stop_import_flag = False @@ -199,7 +200,7 @@ class SongImport(QtCore.QObject): def add_verse(self, versetext, versetag=u'V', lang=None): """ - Add a verse. This is the whole verse, lines split by \n. It will also + Add a verse. This is the whole verse, lines split by \\n. It will also attempt to detect duplicates. In this case it will just add to the verse order. @@ -212,6 +213,7 @@ class SongImport(QtCore.QObject): ``lang`` The language code (ISO-639) of the verse, for example *en* or *de*. + """ for (oldversetag, oldverse, oldlang) in self.verses: if oldverse.strip() == versetext.strip(): diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py new file mode 100644 index 000000000..5df36a5b1 --- /dev/null +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -0,0 +1,210 @@ +# -*- 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'' + 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 91bfdb025..b96e79961 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -26,7 +26,7 @@ """ The :mod:`xml` module provides the XML functionality. -The basic XML for storing the lyrics in the song database is of the format:: +The basic XML for storing the lyrics in the song database looks like this:: @@ -38,7 +38,7 @@ The basic XML for storing the lyrics in the song database is of the format:: -The XML of `OpenLyrics `_ songs is of the format:: +The XML of an `OpenLyrics `_ song looks like this:: `_ songs is of the format:: """ +import datetime import logging import re @@ -86,11 +87,13 @@ class SongXML(object): def add_verse_to_lyrics(self, type, number, content, lang=None): """ - Add a verse to the ** tag. + 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'* + ```` OpenLP does not support the attribute *type* and *lang*. - ** + ```` This property is not supported. - ** - The ** property is fully supported. But comments in lyrics + ```` + The ```` property is fully supported. But comments in lyrics are not supported. - ** + ```` This property is fully supported. - ** + ```` This property is not supported. - ** + ```` This property is not supported. - ** + ```` This property is not supported. - ** + ```` The attribute *part* is not supported. - ** + ```` This property is not supported. - ** + ```` As OpenLP does only support one songbook, we cannot consider more than one songbook. - ** + ```` This property is not supported. - ** + ```` Topics, as they are called in OpenLP, are fully supported, whereby only the topic text (e. g. Grace) is considered, but neither the *id* nor *lang*. - ** + ```` This property is not supported. - ** + ```` This property is not supported. - ** - 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 @@ -221,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()) @@ -236,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: @@ -251,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: @@ -262,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) @@ -449,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". @@ -477,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 7efe73db2..887ddb7b2 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -51,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) @@ -69,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 @@ -106,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): """ @@ -172,6 +171,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 ' @@ -225,42 +228,18 @@ class SongsPlugin(Plugin): u'title': translate('SongsPlugin', 'Songs', 'container title') } # Middle Header Bar - ## New Action ## - self.textStrings[StringContent.New] = { - u'title': translate('SongsPlugin', 'Add'), - u'tooltip': translate('SongsPlugin', - 'Add a new Song') - } - ## Edit Action ## - self.textStrings[StringContent.Edit] = { - u'title': translate('SongsPlugin', 'Edit'), - u'tooltip': translate('SongsPlugin', - 'Edit the selected Song') - } - ## Delete Action ## - self.textStrings[StringContent.Delete] = { - u'title': translate('SongsPlugin', 'Delete'), - u'tooltip': translate('SongsPlugin', - 'Delete the selected Song') - } - ## Preview Action ## - self.textStrings[StringContent.Preview] = { - u'title': translate('SongsPlugin', 'Preview'), - u'tooltip': translate('SongsPlugin', - 'Preview the selected Song') - } - ## Send Live Action ## - self.textStrings[StringContent.Live] = { - u'title': translate('SongsPlugin', 'Live'), - u'tooltip': translate('SongsPlugin', - 'Send the selected Song live') - } - ## Add to Service Action ## - self.textStrings[StringContent.Service] = { - u'title': translate('SongsPlugin', 'Service'), - u'tooltip': translate('SongsPlugin', + 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/printserviceorderdialog.ui b/resources/forms/printserviceorderdialog.ui new file mode 100644 index 000000000..131979b65 --- /dev/null +++ b/resources/forms/printserviceorderdialog.ui @@ -0,0 +1,206 @@ + + + Dialog + + + + 0 + 0 + 494 + 426 + + + + Dialog + + + + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Service Title: + + + + + + + + + Include slide text if avaialbe + + + + + + + Include service item notes + + + + + + + Include play lenght of media items + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <b>Custom Notes:</b> + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Print + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + ... + + + + :/general/general_zoom_out.png:/general/general_zoom_out.png + + + + + + + + :/general/general_zoom_in.png:/general/general_zoom_in.png + + + + + + + + + + + + 0 + 0 + + + + Preview: + + + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + + + + + + + + + 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/general_zoom_in.png b/resources/images/general_zoom_in.png new file mode 100644 index 000000000..2016b2a1b Binary files /dev/null and b/resources/images/general_zoom_in.png differ diff --git a/resources/images/general_zoom_out.png b/resources/images/general_zoom_out.png new file mode 100644 index 000000000..ffa8cfdb5 Binary files /dev/null and b/resources/images/general_zoom_out.png differ diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 6b9d6dd54..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 @@ -41,6 +45,8 @@ general_export.png general_import.png general_new.png + general_zoom_out.png + general_zoom_in.png general_open.png general_save.png general_email.png @@ -78,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