From c2b822872993a6af3e652063c2b3ef26141eda3c Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Fri, 10 Dec 2010 07:09:03 +0200 Subject: [PATCH 001/108] Updates to documentation. --- documentation/api/source/core/theme.rst | 6 +- documentation/api/source/plugins/bibles.rst | 2 +- documentation/manual/source/dualmonitors.rst | 12 ++-- openlp/core/theme/theme.py | 74 ++++++++++++-------- openlp/core/utils/__init__.py | 2 +- 5 files changed, 55 insertions(+), 41 deletions(-) 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/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/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/openlp/core/theme/theme.py b/openlp/core/theme/theme.py index 52dda1631..6b6740b07 100644 --- a/openlp/core/theme/theme.py +++ b/openlp/core/theme/theme.py @@ -68,38 +68,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 +122,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): """ diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 119bf6b55..f33d7ca04 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 From 40ddcd1010e21638a7b057476bf514251bec6e3c Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Fri, 10 Dec 2010 14:52:43 +0200 Subject: [PATCH 002/108] Added a qthelp theme, updated the manual to work better as Qt help. --- documentation/manual/source/conf.py | 20 +- documentation/manual/source/index.rst | 7 - .../manual/themes/openlp_qthelp/layout.html | 68 ++++ .../openlp_qthelp/static/alert_info_32.png | Bin 0 -> 1168 bytes .../openlp_qthelp/static/alert_warning_32.png | Bin 0 -> 1060 bytes .../themes/openlp_qthelp/static/bg-page.png | Bin 0 -> 164 bytes .../openlp_qthelp/static/bullet_orange.png | Bin 0 -> 365 bytes .../openlp_qthelp/static/openlp_qthelp.css_t | 372 ++++++++++++++++++ .../manual/themes/openlp_qthelp/theme.conf | 12 + 9 files changed, 462 insertions(+), 17 deletions(-) create mode 100644 documentation/manual/themes/openlp_qthelp/layout.html create mode 100644 documentation/manual/themes/openlp_qthelp/static/alert_info_32.png create mode 100644 documentation/manual/themes/openlp_qthelp/static/alert_warning_32.png create mode 100644 documentation/manual/themes/openlp_qthelp/static/bg-page.png create mode 100644 documentation/manual/themes/openlp_qthelp/static/bullet_orange.png create mode 100644 documentation/manual/themes/openlp_qthelp/static/openlp_qthelp.css_t create mode 100644 documentation/manual/themes/openlp_qthelp/theme.conf diff --git a/documentation/manual/source/conf.py b/documentation/manual/source/conf.py index 517fc2f44..70b4fb2f6 100644 --- a/documentation/manual/source/conf.py +++ b/documentation/manual/source/conf.py @@ -92,22 +92,22 @@ 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 = 'default' +html_theme = 'openlp_qthelp' # 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' -} +#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/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 0000000000000000000000000000000000000000..05b4fe898c39c93ca6041650c49dcb67e6595432 GIT binary patch literal 1168 zcmV;B1aJF^P)KB1(GEo2OU5dU1MjixU?b4HlPyU1Z#btg*bwF1uutH8k9WJ2h0&wD~{( zq-o+GmX3s=tgtkx6sd&#N1|j3;?zy1&gP$OUftcd7w1f`OJdNyJ@D}3at=J7^L(D) z@0<(&9%FzO;23Zx%%eaiyjIkB8aP38t}OABtn!#B-zOp)1W5rW!h4DmV}O&y+vS@2 z<;@zBYwDM!&dcJz3U7yC(lA6(I*}ndPi}fv)oZ0Cd!*`hlAE5D)tnL~-wInJs8Xn- z;BnNdevvmjC_bcM^=3JVi=2-_f-A>c?c0J1jWPq`Vvli%yW49O;*6{X(`TPLhl zK(;sl6^CREJ#sUp$Q;Sdl&qmgmN+K-F9pkmEh-wo5u)2R>B4+r5#coof14;hDk8hWt`r!+Yq}!Wl9NC4uHs9xs$6|j@dZ69X}Y9HxwF+k)?t)1qT3l0i4`B zS-FO+EZr|l-ja>KE_go}L|TKhfRBm-03bItE7x#aR@X0^a8o3DLx6>XMS>NAkBbN} zB`d4HEvxO5Ro@U5f8_xz5v&w!C?Y`PZP}y&*~DA2s_SChwGd#~|7yX;q5wPs1no}; z?YGKa5x_)xH3&{i;jQ?V)QRoC9D^p2cr+XiLnf&1Gpeoe%#hW>{S zpiczbhXK++azwrcyb2_M*TYNzF9A>fW9VXwb4q@i)0>~?lxiQmxBc$E&ShZI7@+!u>A1Q^G$hqUVNi2L-!F3-B`V z_``@V^jc7ez_8@Gr@$>Q-6v4VBO7S6NH1T<#9jNX7HxMPN6U7T%8{a#2J&1z&|{Lvf4eM;!PKn6vuz>4R0vJ5GZ9JCSY|{n-F$v?83&>Rb%7EO*d_5V&cw3mu}oBb)lFvibm5k zsqv%1bYY?~*iemz27y8oC@oZ47>5C-;LLQs-n;j>n0Yg=wt2%$J14n|$$fMGzjN;Y z-gBXPSimV@8HfYl0dE3_0do)kuRC1f+rz^j$m;5{?C!*5X8Jd|c;TF!8G2oS4dB~G z0o@)R9+uVBRS|JSbWTL4MAVjub5F!xl-6+k&44rO(tZvAQiZUvlT_R22#!7l#xhSA&zfKv9# z1E8JCz%75IQCpyj*^A?W$d>?g`7;AB8UmoTC-c(&yJi&aQKwNAa6IlWl!m(n>Y+~? zW5S1yUcMskn$xRp1RDY1alZ1J&@TF;ByJcD^MISZ?b&+=gfc)`w{ZYY63n1V#_vyssn*G@~3ork@o?(9& zdfH7a!@V1X>~;*pgd6Lvyzym}^#nt}^``}J`p97K$k^x!UOm#y)L$FCeeN1<`$F_| zG!Y6yDjs8O8ou}`!^9E^U>umO1;F<~vbpdMUES@d>;qJChuvtLKYm~2{Lf1q8|-E2 z<~oPMaQp>`<|7>aWUJbmwE@V&X%L09Mo__4bS8zflOVehV{YaSDwX1J`xd4N`=1{m zbaK9C0RQ)Za-fjLNX2OqjkF)c?CQqc*TP_1m_j^?y5CGF1g8H=bpmKl_fvU{&3T$M znC%A%bRB?n2Ay2RFx%==uL__%jmLBR%-vg1BV{7VjP$U7J+FdgVlS5p9OGr@rrw~xc*we)^#N+te3mbVE6nI!RHr%Mc z8g7=rvO+oj?W?d!Ra;*xb2KS{k=laGckLV}Uu4bBVJnhcB7PmHox#)9&t;ucLK6V3 Ci7X5N literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ad5d02f341dfcd8fec482e500170bb711257f0b9 GIT binary patch literal 365 zcmeAS@N?(olHy`uVBq!ia0vp^Y#_|R3?#1$$yWiXgaDrq*9Uc4|IbYRzdQHA%KGnb zp8lU>{{P{#za{Vg>DGTApT26<`oGZT|IxPp5BL1v zU-JLUi^r?#|Ns9FG!HA7@@RewP=YbZ+ucQ;l~-Udki%Kv5m^kRJ;2!QWVRhhu&lr_ z9Y}+n$uRZe&q+XzzNd?02*-8SgPcqZ0vs#{O4}y1{I|c#qqlle!_6Z7fZ4}QmsI^> zTd~_#Zi!CI4@OlM##wA?*5`m)RZCnWN>UO_QmvAUQh^kMk%6Isu7RPhfq96bk(G(1 rm4TVIfuWUwfz`yy6DS&T^HVa@DsgM@K69`HsDZ)L)z4*}Q$iB}`ILMr literal 0 HcmV?d00001 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 From b5982dec403d87c8efc7775813a08246966ee4ce Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 23 Jan 2011 17:05:46 +0100 Subject: [PATCH 003/108] --- openlp/plugins/songs/forms/__init__.py | 4 +- openlp/plugins/songs/forms/songexportform.py | 229 +++++++++++++++++++ openlp/plugins/songs/lib/mediaitem.py | 8 +- openlp/plugins/songs/lib/openlyricsexport.py | 77 +++++++ openlp/plugins/songs/songsplugin.py | 17 +- 5 files changed, 331 insertions(+), 4 deletions(-) create mode 100644 openlp/plugins/songs/forms/songexportform.py create mode 100755 openlp/plugins/songs/lib/openlyricsexport.py 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/songexportform.py b/openlp/plugins/songs/forms/songexportform.py new file mode 100644 index 000000000..6868da4aa --- /dev/null +++ b/openlp/plugins/songs/forms/songexportform.py @@ -0,0 +1,229 @@ +# -*- 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 song export function for OpenLP. +""" +import logging +import os + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import Receiver, SettingsManager, translate +from openlp.core.ui import criticalErrorMessageBox +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 + OpenLyrics. + """ + 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_importsong.bmp') + + def setupUi(self, image): + """ + Set up the song wizard UI. + """ + OpenLPWizard.setupUi(self, image) + + def customInit(self): + """ + Song wizard specific initialisation. + """ + songs = self.plugin.manager.get_all_objects(Song) + for song in songs: + self.availableListWidget.addItem(unicode(song.id)) + + def customSignals(self): + """ + Song wizard specific signals. + """ + pass + + def addCustomPages(self): + """ + Add song wizard specific pages. + """ + # Source Page + self.sourcePage = QtGui.QWizardPage() + self.sourcePage.setObjectName(u'SourcePage') + self.sourceLayout = QtGui.QVBoxLayout(self.sourcePage) + self.sourceLayout.setObjectName(u'SourceLayout') + + self.songListsWidget = QtGui.QWidget(self.sourcePage) + self.songListsWidget.setGeometry(QtCore.QRect(8, 10, 541, 291)) + self.songListsWidget.setObjectName("songListsWidget") + self.SongListsLayout = QtGui.QHBoxLayout(self.songListsWidget) + self.SongListsLayout.setSpacing(0) + self.SongListsLayout.setMargin(0) + self.SongListsLayout.setObjectName("SongListsLayout") + self.availableGroupBox = QtGui.QGroupBox(self.songListsWidget) + self.availableGroupBox.setObjectName("availableGroupBox") + self.verticalLayout_5 = QtGui.QVBoxLayout(self.availableGroupBox) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.availableListWidget = QtGui.QListWidget(self.availableGroupBox) + self.availableListWidget.setObjectName("availableListWidget") + self.verticalLayout_5.addWidget(self.availableListWidget) + self.SongListsLayout.addWidget(self.availableGroupBox) + self.selectionWidget = QtGui.QWidget(self.songListsWidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.selectionWidget.sizePolicy().hasHeightForWidth()) + self.selectionWidget.setSizePolicy(sizePolicy) + self.selectionWidget.setMaximumSize(QtCore.QSize(30, 16777215)) + self.selectionWidget.setObjectName("selectionWidget") + self.SelectionLayout = QtGui.QVBoxLayout(self.selectionWidget) + self.SelectionLayout.setSpacing(0) + self.SelectionLayout.setSizeConstraint(QtGui.QLayout.SetMinimumSize) + self.SelectionLayout.setMargin(0) + self.SelectionLayout.setObjectName("SelectionLayout") + self.addSelected = QtGui.QToolButton(self.selectionWidget) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/exports/export_move_to_list.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.addSelected.setIcon(icon) + self.addSelected.setIconSize(QtCore.QSize(20, 20)) + self.addSelected.setObjectName("addSelected") + self.SelectionLayout.addWidget(self.addSelected) + self.removeSelected = QtGui.QToolButton(self.selectionWidget) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/imports/import_remove.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.removeSelected.setIcon(icon1) + self.removeSelected.setIconSize(QtCore.QSize(20, 20)) + self.removeSelected.setObjectName("removeSelected") + self.SelectionLayout.addWidget(self.removeSelected) + self.SongListsLayout.addWidget(self.selectionWidget) + self.selectedGroupBox = QtGui.QGroupBox(self.songListsWidget) + self.selectedGroupBox.setObjectName("selectedGroupBox") + self.verticalLayout_6 = QtGui.QVBoxLayout(self.selectedGroupBox) + self.verticalLayout_6.setObjectName("verticalLayout_6") + self.selectedListWidget = QtGui.QListWidget(self.selectedGroupBox) + self.selectedListWidget.setObjectName("selectedListWidget") + self.verticalLayout_6.addWidget(self.selectedListWidget) + self.SongListsLayout.addWidget(self.selectedGroupBox) + + + self.formatStack = QtGui.QStackedLayout() + self.formatStack.setObjectName(u'FormatStack') + self.sourceLayout.addLayout(self.formatStack) + self.addPage(self.sourcePage) + + def retranslateUi(self): + """ + Song wizard localisation. + """ + self.setWindowTitle( + translate('SongsPlugin.ImportWizardForm', 'Song Import Wizard')) + self.titleLabel.setText( + u'%s' % \ + translate('SongsPlugin.ImportWizardForm', + 'Welcome to the Song Import Wizard')) + self.informationLabel.setText( + translate('SongsPlugin.ImportWizardForm', + 'This wizard will help you to export songs from a variety of ' + 'formats. Click the next button below to start the process by ' + 'selecting a format to export from.')) + self.sourcePage.setTitle( + translate('SongsPlugin.ImportWizardForm', 'Select Import Source')) + self.sourcePage.setSubTitle( + translate('SongsPlugin.ImportWizardForm', + 'Select the export format, and where to export from.')) + + self.progressPage.setTitle( + translate('SongsPlugin.ImportWizardForm', 'Importing')) + self.progressPage.setSubTitle( + translate('SongsPlugin.ImportWizardForm', + 'Please wait while your songs are exported.')) + self.progressLabel.setText( + translate('SongsPlugin.ImportWizardForm', 'Ready.')) + self.progressBar.setFormat( + translate('SongsPlugin.ImportWizardForm', '%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.sourcePage: + return True + elif self.currentPage() == self.progressPage: + return True + + def registerFields(self): + """ + Register song export wizard fields. + """ + pass + + def setDefaults(self): + """ + Set default form values for the song export wizard. + """ + self.restart() + self.finishButton.setVisible(False) + self.cancelButton.setVisible(True) + + def preWizard(self): + """ + Perform pre export tasks + """ + OpenLPWizard.preWizard(self) + self.progressLabel.setText( + translate('SongsPlugin.ImportWizardForm', 'Starting export...')) + Receiver.send_message(u'openlp_process_events') + + def performWizard(self): + """ + Perform the actual export. This method pulls in the correct exporter + class, and then runs the ``do_export`` method of the exporter to do + the actual exporting. + """ + exporter = OpenLyricsExport() + if exporter.do_export(): + self.progressLabel.setText( + translate('SongsPlugin.SongImportForm', 'Finished export.')) + else: + self.progressLabel.setText( + translate('SongsPlugin.SongImportForm', + 'Your song export failed.')) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 625f99f18..0dd0df882 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -34,7 +34,7 @@ from sqlalchemy.sql import or_ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \ ItemCapabilities, translate, check_item_selected 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 @@ -271,6 +271,12 @@ 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) + if self.export_wizard.exec_() == QtGui.QDialog.Accepted: + Receiver.send_message(u'songs_load_list') + 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..2615a7448 --- /dev/null +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -0,0 +1,77 @@ +# -*- 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-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian # +# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # +# Carsten Tinggaard, Frode Woldsund # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The :mod:`openlyricsexport` module provides the functionality for exporting +songs from the database. +""" +import datetime +import logging +import os + +from lxml import etree + +from openlp.core.lib import translate +from openlp.plugins.songs.lib import OpenLyrics + +log = logging.getLogger(__name__) + +class OpenLyricsExport(object): + """ + This provides the Openlyrics export. + """ + def __init__(self, master_manager, song_ids, save_path): + """ + Initialise the export. + """ + log.debug(u'initialise OpenLyricsExport') + self.master_manager = master_manager + self.song_ids = song_ids + self.save_path = save_path + + def do_export(self): + """ + Export the songs. + """ + openLyrics = OpenLyrics(self.master_manager) + self.export_wizard.exportProgressBar.setMaximum(len(songs)) + for song in self.songs: + if self.stop_export_flag: + return False + self.export_wizard.incrementProgressBar(unicode(translate( + 'SongsPlugin.OpenLyricsExport', 'Exporting %s...')) % + song.title) + path = os.path.join(self.save_path, song.title + u'.xml') + xml = openLyrics.song_to_xml(song) + # Add "IMPLEMENTED_VERSION = u'0.7" to xml.py/OpenLyrics class! + xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION) + xml.set(u'createdIn', u'OpenLP 1.9.4') # Use variable + xml.set(u'modifiedIn', u'OpenLP 1.9.4') # Use variable + xml.set(u'modifiedDate', + datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S')) + tree = etree.ElementTree(etree.fromstring(xml)) + tree.write(path, encoding=u'utf-8', xml_declaration=True, + pretty_print=True) + return True diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 7efe73db2..02ae6f2e7 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -106,8 +106,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 +181,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 ' From 6d1b2973108ca8d603ce972f414f62440b714f3b Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 23 Jan 2011 17:15:05 +0100 Subject: [PATCH 004/108] --- openlp/plugins/songs/forms/songexportform.py | 75 +++++++++----------- 1 file changed, 33 insertions(+), 42 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 6868da4aa..9058b6a2c 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -87,65 +87,56 @@ class SongExportForm(OpenLPWizard): # Source Page self.sourcePage = QtGui.QWizardPage() self.sourcePage.setObjectName(u'SourcePage') - self.sourceLayout = QtGui.QVBoxLayout(self.sourcePage) + self.sourceLayout = QtGui.QHBoxLayout(self.sourcePage) self.sourceLayout.setObjectName(u'SourceLayout') - self.songListsWidget = QtGui.QWidget(self.sourcePage) - self.songListsWidget.setGeometry(QtCore.QRect(8, 10, 541, 291)) - self.songListsWidget.setObjectName("songListsWidget") - self.SongListsLayout = QtGui.QHBoxLayout(self.songListsWidget) - self.SongListsLayout.setSpacing(0) - self.SongListsLayout.setMargin(0) - self.SongListsLayout.setObjectName("SongListsLayout") - self.availableGroupBox = QtGui.QGroupBox(self.songListsWidget) - self.availableGroupBox.setObjectName("availableGroupBox") + self.availableGroupBox = QtGui.QGroupBox(self.sourcePage) + self.availableGroupBox.setObjectName(u'availableGroupBox') self.verticalLayout_5 = QtGui.QVBoxLayout(self.availableGroupBox) - self.verticalLayout_5.setObjectName("verticalLayout_5") + self.verticalLayout_5.setObjectName(u'verticalLayout_5') self.availableListWidget = QtGui.QListWidget(self.availableGroupBox) - self.availableListWidget.setObjectName("availableListWidget") + self.availableListWidget.setObjectName(u'availableListWidget') self.verticalLayout_5.addWidget(self.availableListWidget) - self.SongListsLayout.addWidget(self.availableGroupBox) - self.selectionWidget = QtGui.QWidget(self.songListsWidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.selectionWidget.sizePolicy().hasHeightForWidth()) - self.selectionWidget.setSizePolicy(sizePolicy) + self.sourceLayout.addWidget(self.availableGroupBox) + self.selectionWidget = QtGui.QWidget(self.sourcePage) self.selectionWidget.setMaximumSize(QtCore.QSize(30, 16777215)) - self.selectionWidget.setObjectName("selectionWidget") - self.SelectionLayout = QtGui.QVBoxLayout(self.selectionWidget) - self.SelectionLayout.setSpacing(0) - self.SelectionLayout.setSizeConstraint(QtGui.QLayout.SetMinimumSize) - self.SelectionLayout.setMargin(0) - self.SelectionLayout.setObjectName("SelectionLayout") + self.selectionWidget.setObjectName(u'selectionWidget') + self.selectionLayout = QtGui.QVBoxLayout(self.selectionWidget) + self.selectionLayout.setSpacing(0) + self.selectionLayout.setSizeConstraint(QtGui.QLayout.SetMinimumSize) + self.selectionLayout.setMargin(0) + self.selectionLayout.setObjectName(u'SelectionLayout') self.addSelected = QtGui.QToolButton(self.selectionWidget) icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(":/exports/export_move_to_list.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon.addPixmap(QtGui.QPixmap( + u':/exports/export_move_to_list.png'), + QtGui.QIcon.Normal, QtGui.QIcon.Off) self.addSelected.setIcon(icon) self.addSelected.setIconSize(QtCore.QSize(20, 20)) - self.addSelected.setObjectName("addSelected") - self.SelectionLayout.addWidget(self.addSelected) + self.addSelected.setObjectName(u'addSelected') + self.selectionLayout.addWidget(self.addSelected) self.removeSelected = QtGui.QToolButton(self.selectionWidget) - icon1 = QtGui.QIcon() - icon1.addPixmap(QtGui.QPixmap(":/imports/import_remove.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.removeSelected.setIcon(icon1) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap( + u':/imports/import_remove.png'), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.removeSelected.setIcon(icon) self.removeSelected.setIconSize(QtCore.QSize(20, 20)) - self.removeSelected.setObjectName("removeSelected") - self.SelectionLayout.addWidget(self.removeSelected) - self.SongListsLayout.addWidget(self.selectionWidget) - self.selectedGroupBox = QtGui.QGroupBox(self.songListsWidget) - self.selectedGroupBox.setObjectName("selectedGroupBox") + self.removeSelected.setObjectName(u'removeSelected') + self.selectionLayout.addWidget(self.removeSelected) + self.sourceLayout.addWidget(self.selectionWidget) + self.selectedGroupBox = QtGui.QGroupBox(self.sourcePage) + self.selectedGroupBox.setObjectName(u'selectedGroupBox') self.verticalLayout_6 = QtGui.QVBoxLayout(self.selectedGroupBox) - self.verticalLayout_6.setObjectName("verticalLayout_6") + self.verticalLayout_6.setObjectName(u'verticalLayout_6') self.selectedListWidget = QtGui.QListWidget(self.selectedGroupBox) - self.selectedListWidget.setObjectName("selectedListWidget") + self.selectedListWidget.setObjectName(u'selectedListWidget') self.verticalLayout_6.addWidget(self.selectedListWidget) - self.SongListsLayout.addWidget(self.selectedGroupBox) + self.sourceLayout.addWidget(self.selectedGroupBox) - self.formatStack = QtGui.QStackedLayout() - self.formatStack.setObjectName(u'FormatStack') - self.sourceLayout.addLayout(self.formatStack) + #self.formatStack = QtGui.QStackedLayout() + #self.formatStack.setObjectName(u'FormatStack') + #self.sourceLayout.addLayout(self.formatStack) self.addPage(self.sourcePage) def retranslateUi(self): From 1e0b9cbdd4f517e32296f67b06362c01feb109e9 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 24 Jan 2011 20:27:59 +0100 Subject: [PATCH 005/108] --- openlp/plugins/songs/forms/songexportform.py | 131 +++++++++++++++---- openlp/plugins/songs/lib/openlyricsexport.py | 35 ++--- openlp/plugins/songs/lib/opensongimport.py | 7 +- openlp/plugins/songs/lib/xml.py | 18 ++- 4 files changed, 140 insertions(+), 51 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 18d1c85ba..6cf172087 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -27,11 +27,11 @@ The song export function for OpenLP. """ import logging -import os +#import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import Receiver, SettingsManager, translate +from openlp.core.lib import Receiver, translate from openlp.core.ui import criticalErrorMessageBox from openlp.core.ui.wizard import OpenLPWizard from openlp.plugins.songs.lib.db import Song @@ -59,6 +59,16 @@ class SongExportForm(OpenLPWizard): self.plugin = plugin OpenLPWizard.__init__(self, parent, plugin, u'songExportWizard', u':/wizards/wizard_importsong.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 exporters to stop their export + """ + log.debug(u'Stopping songs export') + self.stop_export_flag = True def setupUi(self, image): """ @@ -70,25 +80,22 @@ class SongExportForm(OpenLPWizard): """ Song wizard specific initialisation. """ - songs = self.plugin.manager.get_all_objects(Song) - for song in songs: - author_list = u'' - for author in song.authors: - if author_list != u'': - author_list = author_list + u', ' - author_list = author_list + author.display_name - song_title = unicode(song.title) - song_detail = u'%s (%s)' % (song_title, author_list) - song_name = QtGui.QListWidgetItem(song_detail) - song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) - self.availableListWidget.addItem(song_name) - self.availableListWidget.selectAll() + pass def customSignals(self): """ Song wizard specific signals. """ - pass + QtCore.QObject.connect(self.addSelected, + QtCore.SIGNAL(u'clicked()'), self.onAddSelectedClicked) + QtCore.QObject.connect(self.removeSelected, + QtCore.SIGNAL(u'clicked()'), self.onRemoveSelectedClicked) + QtCore.QObject.connect(self.availableListWidget, + QtCore.SIGNAL(u'itemDoubleClicked(QListWidgetItem *)'), + self.onAvailableListItemDoubleClicked) + QtCore.QObject.connect(self.selectedListWidget, + QtCore.SIGNAL(u'itemDoubleClicked(QListWidgetItem *)'), + self.onSelectedListItemDoubleClicked) def addCustomPages(self): """ @@ -105,6 +112,11 @@ class SongExportForm(OpenLPWizard): self.verticalLayout.setObjectName(u'verticalLayout') self.availableListWidget = QtGui.QListWidget(self.availableGroupBox) self.availableListWidget.setObjectName(u'availableListWidget') + self.availableListWidget.setSelectionMode( + QtGui.QAbstractItemView.ExtendedSelection) + self.availableListWidget.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) + self.availableListWidget.setSortingEnabled(True) self.verticalLayout.addWidget(self.availableListWidget) self.sourceLayout.addWidget(self.availableGroupBox) self.selectionWidget = QtGui.QWidget(self.sourcePage) @@ -138,6 +150,11 @@ class SongExportForm(OpenLPWizard): self.verticalLayout.setObjectName(u'verticalLayout') self.selectedListWidget = QtGui.QListWidget(self.selectedGroupBox) self.selectedListWidget.setObjectName(u'selectedListWidget') + self.selectedListWidget.setSelectionMode( + QtGui.QAbstractItemView.ExtendedSelection) + self.selectedListWidget.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) + self.selectedListWidget.setSortingEnabled(True) self.verticalLayout.addWidget(self.selectedListWidget) self.sourceLayout.addWidget(self.selectedGroupBox) self.addPage(self.sourcePage) @@ -151,23 +168,23 @@ class SongExportForm(OpenLPWizard): self.titleLabel.setText( u'%s' % \ translate('SongsPlugin.ExportWizardForm', - 'Welcome to the Song Export Wizard')) + 'Welcome to the Song Export Wizard')) self.informationLabel.setText( translate('SongsPlugin.ExportWizardForm', 'This wizard will help to ' 'export your songs to the free and open OpenLyrics worship song ' 'format. You can import these songs in all lyrics projection ' 'software, which supports OpenLyrics.')) self.sourcePage.setTitle( - translate('SongsPlugin.ExportWizardForm', 'Select Emport Source')) + translate('SongsPlugin.ExportWizardForm', 'Select Songs')) self.sourcePage.setSubTitle( translate('SongsPlugin.ExportWizardForm', - 'Select the export format, and where to export from.')) + 'Select the songs, you want to export.')) self.progressPage.setTitle( translate('SongsPlugin.ExportWizardForm', 'Exporting')) self.progressPage.setSubTitle( translate('SongsPlugin.ExportWizardForm', - 'Please wait while your songs are exported.')) + 'Please wait while your songs are exported.')) self.progressLabel.setText( translate('SongsPlugin.ExportWizardForm', 'Ready.')) self.progressBar.setFormat( @@ -187,10 +204,35 @@ class SongExportForm(OpenLPWizard): Validate the current page before moving on to the next page. """ if self.currentPage() == self.welcomePage: + Receiver.send_message(u'cursor_busy') + songs = self.plugin.manager.get_all_objects(Song) + for song in songs: + author_list = u'' + for author in song.authors: + if author_list != u'': + author_list = author_list + u', ' + author_list = author_list + author.display_name + song_title = unicode(song.title) + song_detail = u'%s (%s)' % (song_title, author_list) + song_name = QtGui.QListWidgetItem(song_detail) + song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) + self.availableListWidget.addItem(song_name) + self.availableListWidget.selectAll() + Receiver.send_message(u'cursor_normal') return True elif self.currentPage() == self.sourcePage: + self.selectedListWidget.selectAll() + if not self.selectedListWidget.selectedItems(): + criticalErrorMessageBox( + translate('SongsPlugin.ExportWizardForm', + 'No Song Selected'), + translate('SongsPlugin.ImportWizardForm', + 'You need to add at least one Song to export.')) + return False return True elif self.currentPage() == self.progressPage: + self.availableListWidget.clear() + self.selectedListWidget.clear() return True def registerFields(self): @@ -222,8 +264,9 @@ class SongExportForm(OpenLPWizard): class, and then runs the ``do_export`` method of the exporter to do the actual exporting. """ - exporter = OpenLyricsExport(self.plugin.manager, - self.plugin.manager.get_all_objects(Song), u'/tmp/') + songs = [item.data(QtCore.Qt.UserRole).toPyObject() + for item in self.selectedListWidget.selectedItems()] + exporter = OpenLyricsExport(self, songs, u'/tmp/') if exporter.do_export(): self.progressLabel.setText( translate('SongsPlugin.SongExportForm', 'Finished export.')) @@ -231,3 +274,47 @@ class SongExportForm(OpenLPWizard): self.progressLabel.setText( translate('SongsPlugin.SongExportForm', 'Your song export failed.')) + + def onAddSelectedClicked(self): + """ + Removes the selected items from the list of available songs and add them + to the list of selected songs. + """ + items = self.availableListWidget.selectedItems() + # Save a list with tuples which consist of the item row, and the item. + items = [(self.availableListWidget.row(item), item) for item in items] + items.sort(reverse=True) + for item in items: + self.availableListWidget.takeItem(item[0]) + self.selectedListWidget.addItem(item[1]) + + def onRemoveSelectedClicked(self): + """ + Removes the selected items from the list of selected songs and add them + back to the list of available songs. + """ + items = self.selectedListWidget.selectedItems() + # Save a list with tuples which consist of the item row, and the item. + items = [(self.selectedListWidget.row(item), item) for item in items] + items.sort(reverse=True) + for item in items: + self.selectedListWidget.takeItem(item[0]) + self.availableListWidget.addItem(item[1]) + + def onAvailableListItemDoubleClicked(self, item): + """ + Adds the double clicked item to the list of selected songs and removes + it from the list of availables songs. + """ + row = self.availableListWidget.row(item) + self.availableListWidget.takeItem(row) + self.selectedListWidget.addItem(item) + + def onSelectedListItemDoubleClicked(self, item): + """ + Adds the double clicked item back to the list of available songs and + removes it from the list of selected songs. + """ + row = self.selectedListWidget.row(item) + self.selectedListWidget.takeItem(row) + self.availableListWidget.addItem(item) diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index 3a27b2e8a..0f8f679f5 100755 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -27,13 +27,12 @@ The :mod:`openlyricsexport` module provides the functionality for exporting songs from the database. """ -import datetime import logging import os from lxml import etree, objectify -from openlp.core.lib import translate +from openlp.core.lib import Receiver, translate from openlp.plugins.songs.lib import OpenLyrics log = logging.getLogger(__name__) @@ -42,42 +41,34 @@ class OpenLyricsExport(object): """ This provides the Openlyrics export. """ - def __init__(self, master_manager, song_ids, save_path): + def __init__(self, parent, songs, save_path): """ Initialise the export. """ log.debug(u'initialise OpenLyricsExport') - self.master_manager = master_manager - self.songs = song_ids + self.parent = parent + self.manager = parent.plugin.manager + self.songs = songs self.save_path = save_path def do_export(self): """ Export the songs. """ - openLyrics = OpenLyrics(self.master_manager) -# self.export_wizard.exportProgressBar.setMaximum(len(songs)) + openLyrics = OpenLyrics(self.manager) + self.parent.progressBar.setMaximum(len(self.songs)) for song in self.songs: -# if self.stop_export_flag: -# return False -# self.export_wizard.incrementProgressBar(unicode(translate( -# 'SongsPlugin.OpenLyricsExport', 'Exporting %s...')) % -# song.title) + 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) # Check if path exists. If not, create the directories! # What do we do with songs with the same title? I do not want to # overwrite them! path = os.path.join(self.save_path, song.title + u'.xml') - # Convert the song object to an unicode string. xml = openLyrics.song_to_xml(song) - song_xml = objectify.fromstring(xml) - # Append the necessary meta data to the song. - # (Maybe move this to the xml module? - 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')) - xml = etree.tostring(song_xml) tree = etree.ElementTree(etree.fromstring(xml)) tree.write(path, encoding=u'utf-8', xml_declaration=True, pretty_print=True) diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index eb16f4ba4..b995b5def 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -39,6 +39,7 @@ 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 +150,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 +280,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 +296,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/xml.py b/openlp/plugins/songs/lib/xml.py index 9694e7ce7..78028f637 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -60,6 +60,7 @@ The XML of `OpenLyrics `_ songs is of the format:: """ +import datetime import logging import re @@ -207,7 +208,8 @@ class OpenLyrics(object): 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. @@ -222,8 +224,14 @@ class OpenLyrics(object): """ sxml = SongXML() verse_list = sxml.get_verses(song.lyrics) - song_xml = objectify.fromstring( - u'') + song_xml = objectify.fromstring(u'') + # Append the necessary meta data to the song. + song_xml.set(u'xmlns', u'http://openlyrics.info/namespace/2009/song') + song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION) + song_xml.set(u'createdIn', u'OpenLP 1.9.4') # Use variable + song_xml.set(u'modifiedIn', u'OpenLP 1.9.4') # Use variable + song_xml.set(u'modifiedDate', + datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S')) properties = etree.SubElement(song_xml, u'properties') titles = etree.SubElement(properties, u'titles') self._add_text_to_element(u'title', titles, song.title.strip()) @@ -237,7 +245,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: @@ -450,7 +458,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". From c6505d7e042f4a3ba3ca1cffb2ede441af5041ae Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 24 Jan 2011 20:31:31 +0100 Subject: [PATCH 006/108] --- openlp/plugins/songs/forms/songexportform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 6cf172087..e8e11d8b7 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -95,7 +95,7 @@ class SongExportForm(OpenLPWizard): self.onAvailableListItemDoubleClicked) QtCore.QObject.connect(self.selectedListWidget, QtCore.SIGNAL(u'itemDoubleClicked(QListWidgetItem *)'), - self.onSelectedListItemDoubleClicked) + self.onSelectedListItemDoubleClicked) def addCustomPages(self): """ @@ -189,7 +189,7 @@ class SongExportForm(OpenLPWizard): translate('SongsPlugin.ExportWizardForm', 'Ready.')) self.progressBar.setFormat( translate('SongsPlugin.ExportWizardForm', '%p%')) - + self.availableGroupBox.setTitle( translate('SongsPlugin.ExportWizardForm', 'Available Songs')) self.addSelected.setText( From d087bbb53ad4827be33c2927ad05993b5dcc0ea3 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 24 Jan 2011 21:01:09 +0100 Subject: [PATCH 007/108] verseOrder fix --- openlp/plugins/songs/forms/songexportform.py | 12 ++++++++---- openlp/plugins/songs/lib/openlyricsexport.py | 3 ++- openlp/plugins/songs/lib/xml.py | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index f7cc0e616..b4bc2de42 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -305,16 +305,20 @@ class SongExportForm(OpenLPWizard): """ Adds the double clicked item to the list of selected songs and removes it from the list of availables songs. + + ``item`` + The *QListWidgetItem* which was double clicked. """ - row = self.availableListWidget.row(item) - self.availableListWidget.takeItem(row) + self.availableListWidget.takeItem(self.availableListWidget.row(item)) self.selectedListWidget.addItem(item) def onSelectedListItemDoubleClicked(self, item): """ Adds the double clicked item back to the list of available songs and removes it from the list of selected songs. + + ``ìtem`` + The *QListWidgetItem* which was double clicked. """ - row = self.selectedListWidget.row(item) - self.selectedListWidget.takeItem(row) + self.selectedListWidget.takeItem(self.selectedListWidget.row(item)) self.availableListWidget.addItem(item) diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index 0f8f679f5..b657f891f 100755 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -30,7 +30,7 @@ songs from the database. import logging import os -from lxml import etree, objectify +from lxml import etree from openlp.core.lib import Receiver, translate from openlp.plugins.songs.lib import OpenLyrics @@ -55,6 +55,7 @@ class OpenLyricsExport(object): """ Export the songs. """ + log.debug(u'started OpenLyricsExport') openLyrics = OpenLyrics(self.manager) self.parent.progressBar.setMaximum(len(self.songs)) for song in self.songs: diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 78028f637..cc2660874 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -486,9 +486,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] From 5b680b32f652825f00279cf005bb050634001a28 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 24 Jan 2011 22:00:36 +0100 Subject: [PATCH 008/108] --- openlp/plugins/songs/forms/songexportform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index b4bc2de42..4ec6acc7c 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -157,7 +157,7 @@ class SongExportForm(OpenLPWizard): self.verticalLayout.addWidget(self.selectedListWidget) self.sourceLayout.addWidget(self.selectedGroupBox) self.addPage(self.sourcePage) - #TODO: Add save dialog + #TODO: Add save dialog and search box. def retranslateUi(self): """ From ea83bf26d7f6347352b061261be3d7b985dd4054 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 25 Jan 2011 19:34:17 +0100 Subject: [PATCH 009/108] --- openlp/plugins/songs/forms/songexportform.py | 45 +++++++++++++++++++- openlp/plugins/songs/lib/xml.py | 2 + 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 3b9bf3ffd..e9904e0bb 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -30,7 +30,7 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Receiver, translate +from openlp.core.lib import Receiver, SettingsManager, translate from openlp.core.ui import criticalErrorMessageBox from openlp.core.ui.wizard import OpenLPWizard from openlp.plugins.songs.lib.db import Song @@ -158,7 +158,43 @@ class SongExportForm(OpenLPWizard): self.selectedListWidget.setSortingEnabled(True) self.verticalLayout.addWidget(self.selectedListWidget) self.sourceLayout.addWidget(self.selectedGroupBox) + + """ + importLayout.setObjectName(obj_prefix + u'Layout') + filenameLabel = QtGui.QLabel(importWidget) + filenameLabel.setObjectName(obj_prefix + u'FilenameLabel') + fileLayout = QtGui.QHBoxLayout() + fileLayout.setObjectName(obj_prefix + u'FileLayout') + filenameEdit = QtGui.QLineEdit(importWidget) + filenameEdit.setObjectName(obj_prefix + u'FilenameEdit') + fileLayout.addWidget(filenameEdit) + browseButton = QtGui.QToolButton(importWidget) + browseButton.setIcon(self.openIcon) + browseButton.setObjectName(obj_prefix + u'BrowseButton') + fileLayout.addWidget(browseButton) + """ + + + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName(u'horizontalLayout') + + self.pathLabel = QtGui.QLabel() + self.pathLabel.setObjectName(u'pathLabel') + self.horizontalLayout.addWidget(self.pathLabel) + + self.pathEdit = QtGui.QLineEdit(self.sourcePage) + self.pathEdit.setObjectName(u'pathEdit') + + self.horizontalLayout.addWidget(self.pathEdit) + + self.browseButton = QtGui.QPushButton(self.sourcePage) + self.browseButton.setObjectName(u'browseButton') + self.horizontalLayout.addWidget(self.browseButton) + + self.sourceLayout.addLayout(self.horizontalLayout) self.addPage(self.sourcePage) + + #TODO: Add save dialog and maybe a search box. def retranslateUi(self): @@ -266,9 +302,14 @@ class SongExportForm(OpenLPWizard): class, and then runs the ``do_export`` method of the exporter to do the actual exporting. """ + 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) songs = [item.data(QtCore.Qt.UserRole).toPyObject() for item in self.selectedListWidget.selectedItems()] - exporter = OpenLyricsExport(self, songs, u'/tmp/') + exporter = OpenLyricsExport(self, songs, path) if exporter.do_export(): self.progressLabel.setText( translate('SongsPlugin.SongExportForm', 'Finished export.')) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index cc2660874..d9446ec12 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -271,6 +271,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) From 98084cbe2f012f992ba519eb5601ab5a0e7ce77a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 25 Jan 2011 19:35:00 +0100 Subject: [PATCH 010/108] --- openlp/plugins/songs/forms/songexportform.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index e9904e0bb..cbf84115b 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -173,12 +173,11 @@ class SongExportForm(OpenLPWizard): browseButton.setObjectName(obj_prefix + u'BrowseButton') fileLayout.addWidget(browseButton) """ - self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.setObjectName(u'horizontalLayout') - self.pathLabel = QtGui.QLabel() + self.pathLabel = QtGui.QLabel(self.sourcePage) self.pathLabel.setObjectName(u'pathLabel') self.horizontalLayout.addWidget(self.pathLabel) From 54e1b8d9b73efba228eff39602ea71125c918db1 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 26 Jan 2011 17:54:10 +0100 Subject: [PATCH 011/108] clean ups --- openlp/plugins/songs/forms/songexportform.py | 6 ++++++ openlp/plugins/songs/lib/mediaitem.py | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 71ba83c73..512d7ec90 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -125,6 +125,9 @@ class SongExportForm(OpenLPWizard): self.selectionLayout.setSpacing(0) self.selectionLayout.setMargin(0) self.selectionLayout.setObjectName(u'selectionLayout') + spacerItem = QtGui.QSpacerItem(20, 0, + QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.MinimumExpanding) + self.selectionLayout.addItem(spacerItem) self.addSelected = QtGui.QToolButton(self.selectionWidget) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap( @@ -143,6 +146,9 @@ class SongExportForm(OpenLPWizard): self.removeSelected.setIconSize(QtCore.QSize(20, 20)) self.removeSelected.setObjectName(u'removeSelected') self.selectionLayout.addWidget(self.removeSelected) + spacerItem = QtGui.QSpacerItem(20, 0, + QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.MinimumExpanding) + self.selectionLayout.addItem(spacerItem) self.sourceLayout.addWidget(self.selectionWidget) self.selectedGroupBox = QtGui.QGroupBox(self.sourcePage) self.selectedGroupBox.setObjectName(u'selectedGroupBox') diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 0dd0df882..6067442c0 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -274,8 +274,7 @@ class SongMediaItem(MediaManagerItem): def onExportClick(self): if not hasattr(self, u'export_wizard'): self.export_wizard = SongExportForm(self, self.parent) - if self.export_wizard.exec_() == QtGui.QDialog.Accepted: - Receiver.send_message(u'songs_load_list') + self.export_wizard.exec_() def onNewClick(self): log.debug(u'onNewClick') From e0b6107dd5c2dd09e57d20813403b811b3cf8dc3 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 26 Jan 2011 20:12:14 +0100 Subject: [PATCH 012/108] clean up --- openlp/plugins/songs/forms/songexportform.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 512d7ec90..21adcc93f 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -64,7 +64,7 @@ class SongExportForm(OpenLPWizard): def stop_export(self): """ - Sets the flag for exporters to stop their export + Sets the flag for the exporter to stop the export. """ log.debug(u'Stopping songs export') self.stop_export_flag = True @@ -140,7 +140,7 @@ class SongExportForm(OpenLPWizard): self.removeSelected = QtGui.QToolButton(self.selectionWidget) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap( - u':/imports/import_remove.png'), + u':/exports/export_remove.png'), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.removeSelected.setIcon(icon) self.removeSelected.setIconSize(QtCore.QSize(20, 20)) @@ -192,7 +192,7 @@ class SongExportForm(OpenLPWizard): 'Welcome to the Song Export Wizard')) self.informationLabel.setText( translate('SongsPlugin.ExportWizardForm', 'This wizard will help to ' - 'export your songs to the free and open OpenLyrics worship song ' + 'export your songs to the open and free OpenLyrics worship song ' 'format. You can import these songs in all lyrics projection ' 'software, which supports OpenLyrics.')) self.sourcePage.setTitle( @@ -213,10 +213,6 @@ class SongExportForm(OpenLPWizard): self.availableGroupBox.setTitle( translate('SongsPlugin.ExportWizardForm', 'Available Songs')) - self.addSelected.setText( - translate('SongsPlugin.ExportWizardForm', 'Select Songs')) - self.removeSelected.setText( - translate('SongsPlugin.ExportWizardForm', 'Select Songs')) self.selectedGroupBox.setTitle( translate('SongsPlugin.ExportWizardForm', 'Selected Songs')) From aa494c9ea32ded5b414cb5ceb4abd9cafc0b0210 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 27 Jan 2011 16:13:11 +0100 Subject: [PATCH 013/108] --- openlp/plugins/songs/forms/songexportform.py | 225 +++++++++-------- openlp/plugins/songs/lib/openlyricsexport.py | 12 +- resources/forms/songexport.ui | 241 ------------------- resources/forms/songexportform.ui | 128 ++++++++++ resources/images/openlp-2.qrc | 1 + resources/images/wizard_exportsong.bmp | Bin 0 -> 172254 bytes 6 files changed, 252 insertions(+), 355 deletions(-) delete mode 100644 resources/forms/songexport.ui create mode 100644 resources/forms/songexportform.ui create mode 100644 resources/images/wizard_exportsong.bmp diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 21adcc93f..f3311596e 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -24,7 +24,8 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The song export function for OpenLP. +The :mod:`songexportform` module provides the wizard for exporting songs to the +OpenLyrics format. """ import logging @@ -40,8 +41,8 @@ log = logging.getLogger(__name__) class SongExportForm(OpenLPWizard): """ - This is the Song Export Wizard, which allows easy exporting of Songs to - OpenLyrics. + This is the Song Export Wizard, which allows easy exporting of Songs to the + OpenLyrics format. """ log.info(u'SongExportForm loaded') @@ -57,7 +58,7 @@ class SongExportForm(OpenLPWizard): """ self.plugin = plugin OpenLPWizard.__init__(self, parent, plugin, u'songExportWizard', - u':/wizards/wizard_importsong.bmp') + 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) @@ -85,9 +86,9 @@ class SongExportForm(OpenLPWizard): """ Song wizard specific signals. """ - QtCore.QObject.connect(self.addSelected, + QtCore.QObject.connect(self.addButton, QtCore.SIGNAL(u'clicked()'), self.onAddSelectedClicked) - QtCore.QObject.connect(self.removeSelected, + QtCore.QObject.connect(self.removeButton, QtCore.SIGNAL(u'clicked()'), self.onRemoveSelectedClicked) QtCore.QObject.connect(self.availableListWidget, QtCore.SIGNAL(u'itemDoubleClicked(QListWidgetItem *)'), @@ -95,6 +96,8 @@ class SongExportForm(OpenLPWizard): QtCore.QObject.connect(self.selectedListWidget, QtCore.SIGNAL(u'itemDoubleClicked(QListWidgetItem *)'), self.onSelectedListItemDoubleClicked) + QtCore.QObject.connect(self.directoryButton, + QtCore.SIGNAL(u'clicked()'), self.onDirectoryButtonClicked) def addCustomPages(self): """ @@ -103,82 +106,70 @@ class SongExportForm(OpenLPWizard): # Source Page self.sourcePage = QtGui.QWizardPage() self.sourcePage.setObjectName(u'sourcePage') - self.sourceLayout = QtGui.QHBoxLayout(self.sourcePage) - self.sourceLayout.setObjectName(u'sourceLayout') - self.availableGroupBox = QtGui.QGroupBox(self.sourcePage) - self.availableGroupBox.setObjectName(u'availableGroupBox') - self.verticalLayout = QtGui.QVBoxLayout(self.availableGroupBox) + self.horizontalLayout = QtGui.QHBoxLayout(self.sourcePage) + self.horizontalLayout.setObjectName(u'horizontalLayout') + self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setObjectName(u'verticalLayout') - self.verticalLayout.setContentsMargins(0, -1, 0, 0) - self.availableListWidget = QtGui.QListWidget(self.availableGroupBox) - self.availableListWidget.setObjectName(u'availableListWidget') - self.availableListWidget.setSelectionMode( - QtGui.QAbstractItemView.ExtendedSelection) - self.availableListWidget.setSizePolicy( - QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) - self.availableListWidget.setSortingEnabled(True) - self.verticalLayout.addWidget(self.availableListWidget) - self.sourceLayout.addWidget(self.availableGroupBox) - self.selectionWidget = QtGui.QWidget(self.sourcePage) - self.selectionWidget.setObjectName(u'selectionWidget') - self.selectionLayout = QtGui.QVBoxLayout(self.selectionWidget) - self.selectionLayout.setSpacing(0) - self.selectionLayout.setMargin(0) - self.selectionLayout.setObjectName(u'selectionLayout') - spacerItem = QtGui.QSpacerItem(20, 0, - QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.MinimumExpanding) - self.selectionLayout.addItem(spacerItem) - self.addSelected = QtGui.QToolButton(self.selectionWidget) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap( - u':/exports/export_move_to_list.png'), - QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.addSelected.setIcon(icon) - self.addSelected.setIconSize(QtCore.QSize(20, 20)) - self.addSelected.setObjectName(u'addSelected') - self.selectionLayout.addWidget(self.addSelected) - self.removeSelected = QtGui.QToolButton(self.selectionWidget) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap( - u':/exports/export_remove.png'), - QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.removeSelected.setIcon(icon) - self.removeSelected.setIconSize(QtCore.QSize(20, 20)) - self.removeSelected.setObjectName(u'removeSelected') - self.selectionLayout.addWidget(self.removeSelected) - spacerItem = QtGui.QSpacerItem(20, 0, - QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.MinimumExpanding) - self.selectionLayout.addItem(spacerItem) - self.sourceLayout.addWidget(self.selectionWidget) - self.selectedGroupBox = QtGui.QGroupBox(self.sourcePage) - self.selectedGroupBox.setObjectName(u'selectedGroupBox') - self.verticalLayout = QtGui.QVBoxLayout(self.selectedGroupBox) - self.verticalLayout.setObjectName(u'verticalLayout') - self.verticalLayout.setContentsMargins(0, -1, 0, 0) - self.selectedListWidget = QtGui.QListWidget(self.selectedGroupBox) + self.gridLayout = QtGui.QGridLayout() + self.gridLayout.setObjectName(u'gridLayout') + self.selectedListWidget = QtGui.QListWidget(self.sourcePage) self.selectedListWidget.setObjectName(u'selectedListWidget') self.selectedListWidget.setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection) - self.selectedListWidget.setSizePolicy( - QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) self.selectedListWidget.setSortingEnabled(True) - self.verticalLayout.addWidget(self.selectedListWidget) - self.sourceLayout.addWidget(self.selectedGroupBox) - # - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName(u'horizontalLayout') - self.pathLabel = QtGui.QLabel() - self.pathLabel.setObjectName(u'pathLabel') - self.horizontalLayout.addWidget(self.pathLabel) - self.pathEdit = QtGui.QLineEdit() - self.pathEdit.setObjectName(u'pathEdit') - self.horizontalLayout.addWidget(self.pathEdit) - self.browseButton = QtGui.QToolButton() - self.browseButton.setObjectName(u'browseButton') - self.horizontalLayout.addWidget(self.browseButton) - # + self.gridLayout.addWidget(self.selectedListWidget, 1, 2, 1, 1) + self.gridLayout2 = QtGui.QGridLayout() + self.gridLayout2.setObjectName(u'gridLayout2') + self.addButton = QtGui.QToolButton(self.sourcePage) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(u':/exports/export_move_to_list.png'), + QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.addButton.setIcon(icon) + self.addButton.setObjectName(u'addButton') + self.gridLayout2.addWidget(self.addButton, 1, 0, 1, 1) + self.removeButton = QtGui.QToolButton(self.sourcePage) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(u':/exports/export_remove.png'), + QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.removeButton.setIcon(icon) + self.removeButton.setObjectName(u'removeButton') + self.gridLayout2.addWidget(self.removeButton, 2, 0, 1, 1) + spacerItem = QtGui.QSpacerItem(20, 40, + QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.gridLayout2.addItem(spacerItem, 0, 0, 1, 1) + self.gridLayout2.addItem(spacerItem, 3, 0, 1, 1) + self.gridLayout.addLayout(self.gridLayout2, 1, 1, 1, 1) + self.availableLabel = QtGui.QLabel(self.sourcePage) + self.availableLabel.setObjectName(u'availableLabel') + self.gridLayout.addWidget(self.availableLabel, 0, 0, 1, 1) + self.selectedLabel = QtGui.QLabel(self.sourcePage) + self.selectedLabel.setObjectName(u'selectedLabel') + self.gridLayout.addWidget(self.selectedLabel, 0, 2, 1, 1) + self.availableListWidget = QtGui.QListWidget(self.sourcePage) + self.availableListWidget.setObjectName(u'availableListWidget') + self.availableListWidget.setSelectionMode( + QtGui.QAbstractItemView.ExtendedSelection) + self.availableListWidget.setSortingEnabled(True) + self.gridLayout.addWidget(self.availableListWidget, 1, 0, 1, 1) + self.verticalLayout.addLayout(self.gridLayout) + self.gridLayout5 = QtGui.QGridLayout() + self.gridLayout5.setObjectName(u'gridLayout5') + self.directoryButton = QtGui.QToolButton(self.sourcePage) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(u':/exports/export_load.png'), + QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.directoryButton.setIcon(icon) + self.directoryButton.setObjectName(u'directoryButton') + self.gridLayout5.addWidget(self.directoryButton, 0, 2, 1, 1) + self.directoryLineEdit = QtGui.QLineEdit(self.sourcePage) + self.directoryLineEdit.setObjectName(u'directoryLineEdit') + self.gridLayout5.addWidget(self.directoryLineEdit, 0, 1, 1, 1) + self.directoryLabel = QtGui.QLabel(self.sourcePage) + self.directoryLabel.setObjectName(u'directoryLabel') + self.gridLayout5.addWidget(self.directoryLabel, 0, 0, 1, 1) + self.verticalLayout.addLayout(self.gridLayout5) + self.horizontalLayout.addLayout(self.verticalLayout) self.addPage(self.sourcePage) - #TODO: Add save dialog and maybe a search box. def retranslateUi(self): """ @@ -193,13 +184,13 @@ class SongExportForm(OpenLPWizard): self.informationLabel.setText( translate('SongsPlugin.ExportWizardForm', 'This wizard will help to ' 'export your songs to the open and free OpenLyrics worship song ' - 'format. You can import these songs in all lyrics projection ' - 'software, which supports OpenLyrics.')) + 'format.')) self.sourcePage.setTitle( translate('SongsPlugin.ExportWizardForm', 'Select Songs')) self.sourcePage.setSubTitle( translate('SongsPlugin.ExportWizardForm', - 'Select the songs, you want to export.')) + 'Add the songs, you want to export to the list on the right hand ' + 'side. You can use the buttons below or double click them.')) self.progressPage.setTitle( translate('SongsPlugin.ExportWizardForm', 'Exporting')) @@ -211,37 +202,34 @@ class SongExportForm(OpenLPWizard): self.progressBar.setFormat( translate('SongsPlugin.ExportWizardForm', '%p%')) - self.availableGroupBox.setTitle( - translate('SongsPlugin.ExportWizardForm', 'Available Songs')) - self.selectedGroupBox.setTitle( - translate('SongsPlugin.ExportWizardForm', 'Selected Songs')) + self.directoryLabel.setText(translate('SongsPlugin.ExportWizardForm', + 'Directory:')) + self.availableLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'Available Songs')) + self.selectedLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'Selected Songs')) def validateCurrentPage(self): """ Validate the current page before moving on to the next page. """ if self.currentPage() == self.welcomePage: - 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]) - song_detail = u'%s (%s)' % (unicode(song.title), authors) - song_name = QtGui.QListWidgetItem(song_detail) - song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) - self.availableListWidget.addItem(song_name) - self.availableListWidget.selectAll() - Receiver.send_message(u'cursor_normal') return True elif self.currentPage() == self.sourcePage: - self.selectedListWidget.selectAll() - if not self.selectedListWidget.selectedItems(): + if not self.selectedListWidget.count(): criticalErrorMessageBox( translate('SongsPlugin.ExportWizardForm', 'No Song Selected'), - translate('SongsPlugin.ImportWizardForm', + translate('SongsPlugin.ExportWizardForm', 'You need to add at least one Song to export.')) return False + elif not self.directoryLineEdit.text(): + criticalErrorMessageBox( + 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() @@ -261,10 +249,25 @@ class SongExportForm(OpenLPWizard): self.restart() self.finishButton.setVisible(False) self.cancelButton.setVisible(True) + self.availableListWidget.clear() + self.selectedListWidget.clear() + self.directoryLineEdit.clear() + # Load the list of songs. + Receiver.send_message(u'cursor_busy') + songs = self.plugin.manager.get_all_objects(Song) + for song in songs: + authors = u', '.join([author.display_name + for author in song.authors]) + song_detail = u'%s (%s)' % (unicode(song.title), authors) + song_name = QtGui.QListWidgetItem(song_detail) + song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) + self.availableListWidget.addItem(song_name) + self.availableListWidget.selectAll() + Receiver.send_message(u'cursor_normal') def preWizard(self): """ - Perform pre export tasks + Perform pre export tasks. """ OpenLPWizard.preWizard(self) self.progressLabel.setText( @@ -273,18 +276,14 @@ class SongExportForm(OpenLPWizard): def performWizard(self): """ - Perform the actual export. This method pulls in the correct exporter - class, and then runs the ``do_export`` method of the exporter to do - the actual exporting. + Perform the actual export. This creates an *openlyricsexport* instance + and calls the *do_export* method. """ - 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.selectedListWidget.selectAll() songs = [item.data(QtCore.Qt.UserRole).toPyObject() for item in self.selectedListWidget.selectedItems()] - exporter = OpenLyricsExport(self, songs, path) + exporter = OpenLyricsExport( + self, songs, unicode(self.directoryLineEdit.text())) if exporter.do_export(): self.progressLabel.setText( translate('SongsPlugin.SongExportForm', 'Finished export.')) @@ -340,3 +339,15 @@ class SongExportForm(OpenLPWizard): """ self.selectedListWidget.takeItem(self.selectedListWidget.row(item)) self.availableListWidget.addItem(item) + + def onDirectoryButtonClicked(self): + """ + Called when click on the *directoryButton*. 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/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index 68dfde93f..380db1932 100755 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -25,7 +25,7 @@ ############################################################################### """ The :mod:`openlyricsexport` module provides the functionality for exporting -songs from the database. +songs from the database to the OpenLyrics format. """ import logging import os @@ -50,6 +50,8 @@ class OpenLyricsExport(object): 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): """ @@ -65,12 +67,8 @@ class OpenLyricsExport(object): self.parent.incrementProgressBar(unicode(translate( 'SongsPlugin.OpenLyricsExport', 'Exporting %s...')) % song.title) - # Check if path exists. If not, create the directories! - # What do we do with songs with the same title? I do not want to - # overwrite them! - path = os.path.join(self.save_path, song.title + u'.xml') xml = openLyrics.song_to_xml(song) tree = etree.ElementTree(etree.fromstring(xml)) - tree.write(path, encoding=u'utf-8', xml_declaration=True, - pretty_print=True) + 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/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/forms/songexportform.ui b/resources/forms/songexportform.ui new file mode 100644 index 000000000..f3d775a3a --- /dev/null +++ b/resources/forms/songexportform.ui @@ -0,0 +1,128 @@ + + + WizardPage + + + + 0 + 0 + 576 + 334 + + + + WizardPage + + + + + + + + + + + + + + + ... + + + + :/exports/export_move_to_list.png:/exports/export_move_to_list.png + + + + + + + ... + + + + :/exports/export_remove.png:/exports/export_remove.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Available Songs + + + + + + + Selected Songs + + + + + + + + + + + + + + ... + + + + :/exports/export_load.png:/exports/export_load.png + + + + + + + + + + Directory: + + + + + + + + + + + + + + diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 6b9d6dd54..41424cf53 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -84,6 +84,7 @@ 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 0000000000000000000000000000000000000000..948422dcc4d845b05342bcd6dcf732a286aba486 GIT binary patch literal 172254 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%0D7|v^>o33f~x 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 literal 0 HcmV?d00001 From 3362fa9a84d32fd2443ac016b944e37059559924 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 27 Jan 2011 16:42:11 +0100 Subject: [PATCH 014/108] removed not needed code --- openlp/plugins/songs/forms/songexportform.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 94abf54c8..908392b94 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -157,11 +157,6 @@ class SongExportForm(OpenLPWizard): self.gridLayout.addWidget(self.availableListWidget, 1, 0, 1, 1) # Button to select all songs in the "selectedListWidget". self.allSelectedButton = QtGui.QToolButton(self.sourcePage) -# sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) -# sizePolicy.setHorizontalStretch(0) -# sizePolicy.setVerticalStretch(0) -# sizePolicy.setHeightForWidth(self.allSelectedButton.sizePolicy().hasHeightForWidth()) -# self.allSelectedButton.setSizePolicy(sizePolicy) self.allSelectedButton.setObjectName(u'allSelectedButton') self.gridLayout.addWidget(self.allSelectedButton, 3, 2, 1, 1) # Button to select all songs in the "availableListWidget". From 74d20ae81b73c97718e4b49d769a561a03d1ffab Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 27 Jan 2011 19:14:55 +0100 Subject: [PATCH 015/108] removed blank lines --- openlp/plugins/songs/forms/songexportform.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 908392b94..7e2ce7660 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -203,7 +203,6 @@ class SongExportForm(OpenLPWizard): translate('SongsPlugin.ExportWizardForm', 'Add the songs, you want to export to the list on the right hand ' 'side. You can use the buttons below or double click them.')) - self.progressPage.setTitle( translate('SongsPlugin.ExportWizardForm', 'Exporting')) self.progressPage.setSubTitle( @@ -213,7 +212,6 @@ class SongExportForm(OpenLPWizard): translate('SongsPlugin.ExportWizardForm', 'Ready.')) self.progressBar.setFormat( translate('SongsPlugin.ExportWizardForm', '%p%')) - self.directoryLabel.setText(translate('SongsPlugin.ExportWizardForm', 'Directory:')) self.availableLabel.setText( From eff7708e00dc805389014bb2154fde08a312a9d9 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 3 Feb 2011 14:41:42 +0100 Subject: [PATCH 016/108] fixed doc --- openlp/plugins/songs/forms/songexportform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 7e2ce7660..004fbdcf8 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -362,7 +362,7 @@ class SongExportForm(OpenLPWizard): def onDirectoryButtonClicked(self): """ - Called when click on the *directoryButton*. Opens a dialog and writes + Called when the *directoryButton* was clicked. Opens a dialog and writes the path to *directoryLineEdit*. """ path = unicode(QtGui.QFileDialog.getExistingDirectory(self, From aaba4c1b5e6c408a1e6a80efede63dbad9632de2 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 3 Feb 2011 16:57:36 +0100 Subject: [PATCH 017/108] fixed Receiver, fixed errorBox --- openlp/core/lib/ui.py | 2 +- openlp/core/ui/__init__.py | 2 +- openlp/plugins/songs/forms/songexportform.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 8cfffef45..bb8238bf0 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -30,7 +30,7 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate +from openlp.core.lib import Receiver, translate log = logging.getLogger(__name__) diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index eb0e89775..d820c9a5b 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): """ diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 004fbdcf8..307d91570 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -32,7 +32,7 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, SettingsManager, translate -from openlp.core.ui import criticalErrorMessageBox +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 @@ -231,14 +231,14 @@ class SongExportForm(OpenLPWizard): return True elif self.currentPage() == self.sourcePage: if not self.selectedListWidget.count(): - criticalErrorMessageBox( + 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 elif not self.directoryLineEdit.text(): - criticalErrorMessageBox( + critical_error_message_box( translate('SongsPlugin.ExportWizardForm', 'No Save Location specified'), translate('SongsPlugin.ExportWizardForm', From 5d73211a9ff268f021bb012e91c2014928f147c4 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 3 Feb 2011 18:37:49 +0100 Subject: [PATCH 018/108] fixed doc --- openlp/plugins/songs/lib/xml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index d9446ec12..da2a7a08d 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -159,7 +159,7 @@ class OpenLyrics(object): to/from a song. As OpenLyrics has a rich set of different features, we cannot support them - all. The following features are supported by the :class:`OpenLyrics`:: + all. The following features are supported by the :class:`OpenLyrics`: ** OpenLP does not support the attribute *type* and *lang*. From 0047b8d1c105f969293f2fe8e7cd64ea7da1d4df Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 4 Feb 2011 13:57:48 +0100 Subject: [PATCH 019/108] --- openlp/core/ui/advancedtab.py | 39 ++++++++++++------ openlp/core/ui/servicemanager.py | 68 +++++++++++++++++++------------- 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index f68131894..630cbc18c 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -84,11 +84,15 @@ class AdvancedTab(SettingsTab): 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.printSlideTextCheckBox = QtGui.QCheckBox(self.serviceOrderGroupBox) + self.printSlideTextCheckBox.setObjectName(u'printSlideTextCheckBox') + self.serviceOrderLayout.addWidget(self.printSlideTextCheckBox) + self.printMetaDataCheckBox = QtGui.QCheckBox(self.serviceOrderGroupBox) + self.printMetaDataCheckBox.setObjectName(u'printMetaDataCheckBox') + self.serviceOrderLayout.addWidget(self.printMetaDataCheckBox) + self.printNotesCheckBox = QtGui.QCheckBox(self.serviceOrderGroupBox) + self.printNotesCheckBox.setObjectName(u'printNotesCheckBox') + self.serviceOrderLayout.addWidget(self.printNotesCheckBox) self.leftLayout.addWidget(self.serviceOrderGroupBox) # self.sharedDirGroupBox = QtGui.QGroupBox(self.leftColumn) # self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox') @@ -141,9 +145,12 @@ class AdvancedTab(SettingsTab): '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.printSlideTextCheckBox.setText( + translate('OpenLP.AdvancedTab', 'Include text slides if available')) + self.printMetaDataCheckBox.setText(translate( + 'OpenLP.AdvancedTab', 'Include playing time of media files')) + self.printNotesCheckBox.setText( + translate('OpenLP.AdvancedTab', 'Include service item notes')) # self.sharedDirGroupBox.setTitle( # translate('AdvancedTab', 'Central Data Store')) # self.sharedCheckBox.setText( @@ -179,8 +186,12 @@ 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()) + 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() def save(self): @@ -201,8 +212,12 @@ 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.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() # def onSharedCheckBoxChanged(self, checked): diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 4d36f4aec..3f38afe58 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -24,9 +24,11 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -import os -import logging import cPickle +import datetime +import logging +import mutagen +import os import zipfile log = logging.getLogger(__name__) @@ -1188,11 +1190,6 @@ 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 @@ -1200,29 +1197,44 @@ class ServiceManager(QtGui.QWidget): 'Service Order Sheet') for item in self.serviceItems: item = item[u'service_item'] - # add the title + # Add the title of the service item. 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) + # Add slide text of the service item. + if QtCore.QSettings().value(u'advanced' + + u'/print slide text', QtCore.QVariant(False)).toBool(): + 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 QtCore.QSettings().value(u'advanced' + + u'/print notes', QtCore.QVariant(False)).toBool(): + if item.notes: + text += u'

%s %s

' % (translate( + 'OpenLP.ServiceManager', 'Notes:'), item.notes) + # Add play length of media files. + if item.is_media() and QtCore.QSettings().value(u'advanced' + + u'/print file meta data', QtCore.QVariant(False)).toBool(): + path = os.path.join(item.get_frames()[0][u'path'], + item.get_frames()[0][u'title']) + if not os.path.isfile(path): + continue + file = mutagen.File(path) + if file is not None: + length = int(file.info.length) + text += u'

%s %s

' % (translate( + 'OpenLP.ServiceManager', u'Playing time:'), + unicode(datetime.timedelta(seconds=length))) serviceDocument = QtGui.QTextDocument() serviceDocument.setHtml(text) serviceDocument.print_(printDialog.printer()) From 7c69bad1120fef22071f5c42c86598d374fc949b Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 4 Feb 2011 14:35:55 +0100 Subject: [PATCH 020/108] fixed long line --- openlp/core/ui/servicemanager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 3f38afe58..ec1025d45 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1211,7 +1211,8 @@ class ServiceManager(QtGui.QWidget): # 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'
  3. %s

  4. ' % \ + item.get_frame_title(slide) text += u'
' if item.foot_text: # add footer From ce2640c80e8324580ea5379fa2f9a2532443801e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 4 Feb 2011 16:46:16 +0100 Subject: [PATCH 021/108] --- openlp/core/ui/__init__.py | 1 + openlp/core/ui/servicemanager.py | 59 ++++---------------------------- 2 files changed, 8 insertions(+), 52 deletions(-) diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index eb0e89775..9de96e9b5 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -70,6 +70,7 @@ from shortcutlistform import ShortcutListForm from mediadockmanager import MediaDockManager from servicemanager import ServiceManager from thememanager import ThemeManager +from printserviceorderform import PrintServiceOrderForm __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager', 'MediaDockManager', diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index ec1025d45..05171daf7 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -25,15 +25,13 @@ ############################################################################### import cPickle -import datetime import logging -import mutagen import os import zipfile log = logging.getLogger(__name__) -from PyQt4 import QtCore, QtGui +from PyQt4 import QtCore, QtGui, QtWebKit from openlp.core.lib import OpenLPToolbar, ServiceItem, context_menu_action, \ Receiver, build_icon, ItemCapabilities, SettingsManager, translate, \ @@ -1190,52 +1188,9 @@ class ServiceManager(QtGui.QWidget): """ Print a Service Order Sheet. """ - 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 of the service item. - text += u'

%s

' % (item.icon, - item.get_display_title()) - # Add slide text of the service item. - if QtCore.QSettings().value(u'advanced' + - u'/print slide text', QtCore.QVariant(False)).toBool(): - 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 QtCore.QSettings().value(u'advanced' + - u'/print notes', QtCore.QVariant(False)).toBool(): - if item.notes: - text += u'

%s %s

' % (translate( - 'OpenLP.ServiceManager', 'Notes:'), item.notes) - # Add play length of media files. - if item.is_media() and QtCore.QSettings().value(u'advanced' + - u'/print file meta data', QtCore.QVariant(False)).toBool(): - path = os.path.join(item.get_frames()[0][u'path'], - item.get_frames()[0][u'title']) - if not os.path.isfile(path): - continue - file = mutagen.File(path) - if file is not None: - length = int(file.info.length) - text += u'

%s %s

' % (translate( - 'OpenLP.ServiceManager', u'Playing time:'), - unicode(datetime.timedelta(seconds=length))) - serviceDocument = QtGui.QTextDocument() - serviceDocument.setHtml(text) - serviceDocument.print_(printDialog.printer()) + from openlp.core.ui import PrintServiceOrderForm + dialog = PrintServiceOrderForm(self) + dialog.exec_() +# printer = QtGui.QPrinter() +# serviceDocument = QtGui.QTextDocument() +# serviceDocument.setHtml(text) From 642011b379065bb31c08db2cc84bb26bbe892230 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 4 Feb 2011 19:00:59 +0100 Subject: [PATCH 022/108] --- openlp/core/ui/__init__.py | 3 +- openlp/core/ui/advancedtab.py | 34 ------ openlp/core/ui/printserviceorderform.py | 133 ++++++++++++++++++++++++ openlp/core/ui/servicemanager.py | 11 +- 4 files changed, 140 insertions(+), 41 deletions(-) create mode 100644 openlp/core/ui/printserviceorderform.py diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 9de96e9b5..d6d1aa582 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -70,8 +70,7 @@ from shortcutlistform import ShortcutListForm from mediadockmanager import MediaDockManager from servicemanager import ServiceManager from thememanager import ThemeManager -from printserviceorderform import PrintServiceOrderForm __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager', 'MediaDockManager', - 'ServiceItemEditForm'] + 'ServiceItemEditForm',] diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 630cbc18c..58b637bc2 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -80,20 +80,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.printSlideTextCheckBox = QtGui.QCheckBox(self.serviceOrderGroupBox) - self.printSlideTextCheckBox.setObjectName(u'printSlideTextCheckBox') - self.serviceOrderLayout.addWidget(self.printSlideTextCheckBox) - self.printMetaDataCheckBox = QtGui.QCheckBox(self.serviceOrderGroupBox) - self.printMetaDataCheckBox.setObjectName(u'printMetaDataCheckBox') - self.serviceOrderLayout.addWidget(self.printMetaDataCheckBox) - self.printNotesCheckBox = QtGui.QCheckBox(self.serviceOrderGroupBox) - self.printNotesCheckBox.setObjectName(u'printNotesCheckBox') - self.serviceOrderLayout.addWidget(self.printNotesCheckBox) - self.leftLayout.addWidget(self.serviceOrderGroupBox) # self.sharedDirGroupBox = QtGui.QGroupBox(self.leftColumn) # self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox') # self.sharedDirLayout = QtGui.QFormLayout(self.sharedDirGroupBox) @@ -143,14 +129,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.printSlideTextCheckBox.setText( - translate('OpenLP.AdvancedTab', 'Include text slides if available')) - self.printMetaDataCheckBox.setText(translate( - 'OpenLP.AdvancedTab', 'Include playing time of media files')) - self.printNotesCheckBox.setText( - translate('OpenLP.AdvancedTab', 'Include service item notes')) # self.sharedDirGroupBox.setTitle( # translate('AdvancedTab', 'Central Data Store')) # self.sharedCheckBox.setText( @@ -186,12 +164,6 @@ class AdvancedTab(SettingsTab): QtCore.QVariant(True)).toBool()) self.hideMouseCheckBox.setChecked( settings.value(u'hide mouse', QtCore.QVariant(False)).toBool()) - 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() def save(self): @@ -212,12 +184,6 @@ class AdvancedTab(SettingsTab): QtCore.QVariant(self.enableAutoCloseCheckBox.isChecked())) settings.setValue(u'hide mouse', QtCore.QVariant(self.hideMouseCheckBox.isChecked())) - 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() # def onSharedCheckBoxChanged(self, checked): diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py new file mode 100644 index 000000000..127cb95a8 --- /dev/null +++ b/openlp/core/ui/printserviceorderform.py @@ -0,0 +1,133 @@ +# -*- 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 +import mutagen +import os + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import translate +from openlp.core.lib.ui import save_cancel_button_box + + +class PrintServiceOrderForm(QtGui.QDialog): + def __init__(self, parent=None): + """ + Constructor + """ + self.serviceManager = parent + QtGui.QDialog.__init__(self, parent) + self.setupUi() + self.retranslateUi() + + def setupUi(self): + self.dialogLayout = QtGui.QHBoxLayout(self) + self.dialogLayout.setObjectName(u'dialogLayout') + self.verticalLayout = QtGui.QVBoxLayout() + self.verticalLayout.setObjectName(u'verticalLayout') + self.serviceTitleLayout = QtGui.QHBoxLayout() + self.serviceTitleLayout.setObjectName(u'serviceTitleLayout') + self.serviceTitleLabel = QtGui.QLabel(self) + self.serviceTitleLabel.setObjectName(u'serviceTitleLabel') + self.serviceTitleLayout.addWidget(self.serviceTitleLabel) + self.serviceTitleLineEdit = QtGui.QLineEdit(self) + self.serviceTitleLineEdit.setObjectName(u'serviceTitleLineEdit') + self.serviceTitleLayout.addWidget(self.serviceTitleLineEdit) + self.verticalLayout.addLayout(self.serviceTitleLayout) + self.printSlideTextCheckBox = QtGui.QCheckBox(self) + self.printSlideTextCheckBox.setObjectName(u'printSlideTextCheckBox') + self.verticalLayout.addWidget(self.printSlideTextCheckBox) + self.printNotesCheckBox = QtGui.QCheckBox(self) + self.printNotesCheckBox.setObjectName(u'printNotesCheckBox') + self.verticalLayout.addWidget(self.printNotesCheckBox) + self.metaDataCheckBox = QtGui.QCheckBox(self) + self.metaDataCheckBox.setObjectName(u'metaDataCheckBox') + self.verticalLayout.addWidget(self.metaDataCheckBox) + self.verticalLayout.addWidget(save_cancel_button_box(self)) + self.dialogLayout.addLayout(self.verticalLayout) + QtCore.QMetaObject.connectSlotsByName(self) + + def retranslateUi(self): + self.setWindowTitle( + translate('OpenLP.PrintServiceOrderForm', 'Print Service Order')) + self.printSlideTextCheckBox.setText(translate( + 'OpenLP.PrintServiceOrderForm', 'Include slide text if avaialbe')) + self.printNotesCheckBox.setText(translate( + 'OpenLP.PrintServiceOrderForm', 'Include service item notes')) + self.metaDataCheckBox.setText(translate('OpenLP.PrintServiceOrderForm', + 'Include play lenght of media items')) + self.serviceTitleLabel.setText(translate( + 'OpenLP.PrintServiceOrderForm', 'Service Order Title')) + + def serviceOrderText(self): + """ + """ + if self.serviceTitleLineEdit.text(): + text = u'

%s

' % unicode(self.serviceTitleLineEdit.text()) + else: + text = u'

%s

' % translate('OpenLP.ServiceManager', + 'Service Order Sheet') + 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) + # Add play length of media files. + if item.is_media() and self.metaDataCheckBox.isChecked(): + path = os.path.join(item.get_frames()[0][u'path'], + item.get_frames()[0][u'title']) + if not os.path.isfile(path): + continue + file = mutagen.File(path) + if file is not None: + length = int(file.info.length) + text += u'

%s %s

' % (translate( + 'OpenLP.ServiceManager', u'Playing time:'), + unicode(datetime.timedelta(seconds=length))) + return text + + diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index b97171a5b..3d6c8e861 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -38,6 +38,7 @@ from openlp.core.lib import OpenLPToolbar, ServiceItem, context_menu_action, \ ThemeLevel from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm +from openlp.core.ui.printserviceorderform import PrintServiceOrderForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ split_filename @@ -1188,11 +1189,11 @@ class ServiceManager(QtGui.QWidget): """ Print a Service Order Sheet. """ - from openlp.core.ui import PrintServiceOrderForm - dialog = PrintServiceOrderForm(self) - if dialog.exec_(): - serviceDocument = QtGui.QTextDocument() - serviceDocument.setHtml(dialog.printServiceOrder()) + settingDialog = PrintServiceOrderForm(self) + if not settingDialog.exec_(): + return + serviceDocument = QtGui.QTextDocument() + serviceDocument.setHtml(settingDialog.serviceOrderText()) printDialog = QtGui.QPrintDialog() if not printDialog.exec_(): return From 9659eb2a1cfe36227b1584aa1428f495e68480b8 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 4 Feb 2011 19:34:22 +0100 Subject: [PATCH 023/108] added print dialog --- openlp/core/ui/printserviceorderform.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py index 616159da6..b130f9470 100644 --- a/openlp/core/ui/printserviceorderform.py +++ b/openlp/core/ui/printserviceorderform.py @@ -80,17 +80,17 @@ class PrintServiceOrderForm(QtGui.QDialog): self.metaDataCheckBox.setText(translate('OpenLP.PrintServiceOrderForm', 'Include play lenght of media items')) self.serviceTitleLabel.setText(translate( - 'OpenLP.PrintServiceOrderForm', 'Service Order Title')) + 'OpenLP.PrintServiceOrderForm', 'Service Order Title:')) + self.serviceTitleLineEdit.setText(translate('OpenLP.ServiceManager', + 'Service Order Sheet')) def serviceOrderText(self): """ Creates the html text, to print the service items. """ + text = u'' if self.serviceTitleLineEdit.text(): - text = u'

%s

' % unicode(self.serviceTitleLineEdit.text()) - else: - text = u'

%s

' % translate('OpenLP.ServiceManager', - 'Service Order Sheet') + 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. From 860b1d376b59cde145664e01d3f797e02620968d Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 4 Feb 2011 21:10:32 +0100 Subject: [PATCH 024/108] --- openlp/core/ui/printserviceorderform.py | 31 +++++++-- resources/forms/printServiceOrder.ui | 91 +++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 resources/forms/printServiceOrder.ui diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py index b130f9470..d24bfdf91 100644 --- a/openlp/core/ui/printserviceorderform.py +++ b/openlp/core/ui/printserviceorderform.py @@ -42,6 +42,16 @@ class PrintServiceOrderForm(QtGui.QDialog): QtGui.QDialog.__init__(self, parent) self.setupUi() self.retranslateUi() + # Load the settings for this 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() def setupUi(self): self.dialogLayout = QtGui.QHBoxLayout(self) @@ -63,9 +73,9 @@ class PrintServiceOrderForm(QtGui.QDialog): self.printNotesCheckBox = QtGui.QCheckBox(self) self.printNotesCheckBox.setObjectName(u'printNotesCheckBox') self.verticalLayout.addWidget(self.printNotesCheckBox) - self.metaDataCheckBox = QtGui.QCheckBox(self) - self.metaDataCheckBox.setObjectName(u'metaDataCheckBox') - self.verticalLayout.addWidget(self.metaDataCheckBox) + self.printMetaDataCheckBox = QtGui.QCheckBox(self) + self.printMetaDataCheckBox.setObjectName(u'printMetaDataCheckBox') + self.verticalLayout.addWidget(self.printMetaDataCheckBox) self.verticalLayout.addWidget(save_cancel_button_box(self)) self.dialogLayout.addLayout(self.verticalLayout) QtCore.QMetaObject.connectSlotsByName(self) @@ -77,7 +87,8 @@ class PrintServiceOrderForm(QtGui.QDialog): 'OpenLP.PrintServiceOrderForm', 'Include slide text if avaialbe')) self.printNotesCheckBox.setText(translate( 'OpenLP.PrintServiceOrderForm', 'Include service item notes')) - self.metaDataCheckBox.setText(translate('OpenLP.PrintServiceOrderForm', + self.printMetaDataCheckBox.setText( + translate('OpenLP.PrintServiceOrderForm', 'Include play lenght of media items')) self.serviceTitleLabel.setText(translate( 'OpenLP.PrintServiceOrderForm', 'Service Order Title:')) @@ -118,7 +129,7 @@ class PrintServiceOrderForm(QtGui.QDialog): text += u'

%s %s

' % (translate( 'OpenLP.ServiceManager', 'Notes:'), item.notes) # Add play length of media files. - if item.is_media() and self.metaDataCheckBox.isChecked(): + if item.is_media() and self.printMetaDataCheckBox.isChecked(): path = os.path.join(item.get_frames()[0][u'path'], item.get_frames()[0][u'title']) if not os.path.isfile(path): @@ -129,4 +140,14 @@ class PrintServiceOrderForm(QtGui.QDialog): text += u'

%s %s

' % (translate( 'OpenLP.ServiceManager', u'Playing time:'), unicode(datetime.timedelta(seconds=length))) + # 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() return text diff --git a/resources/forms/printServiceOrder.ui b/resources/forms/printServiceOrder.ui new file mode 100644 index 000000000..728e42d77 --- /dev/null +++ b/resources/forms/printServiceOrder.ui @@ -0,0 +1,91 @@ + + + Dialog + + + + 0 + 0 + 321 + 205 + + + + Dialog + + + + + + + + + + Service Title: + + + + + + + + + + + + Include slide text if avaialbe + + + + + + + Include service item notes + + + + + + + Include play lenght of media items + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Print + + + + + + + + + + + + From d7b9cc93f97cd3e18f5e603df43fd2296905f90a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 5 Feb 2011 10:45:08 +0100 Subject: [PATCH 025/108] fixed Bug #677282 --- openlp/core/ui/maindisplay.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 3dfde8640..bae00c2da 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(), From 668b5290247944a4c6ce14710963b7f46ca7b1e9 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 5 Feb 2011 19:29:49 +0100 Subject: [PATCH 026/108] new bible search box --- openlp/plugins/bibles/lib/mediaitem.py | 30 +++++++++++--------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index c162447b2..99b8a0eaf 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -30,6 +30,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, Receiver, BaseListWithDnD, \ ItemCapabilities, translate +from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import add_widget_completer, media_item_combo_box, \ critical_error_message_box from openlp.plugins.bibles.forms import BibleImportForm @@ -93,18 +94,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([ + (1, u':/songs/song_topic_edit.png', + translate('BiblesPlugin.MediaItem', 'Verse Search')), + (2, u':/songs/song_search_author.png', + translate('BiblesPlugin.MediaItem', 'Text Search')) + ]) self.quickLayout.addRow(self.quickSearchLabel, self.quickSearchEdit) self.quickClearLabel = QtGui.QLabel(self.quickTab) self.quickClearLabel.setObjectName(u'quickClearLabel') @@ -207,8 +207,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 +242,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 +266,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( @@ -373,7 +367,7 @@ class BibleMediaItem(MediaManagerItem): """ books = [] # We have to do a 'Verse Search'. - if self.quickSearchComboBox.currentIndex() == 0: + if self.quickSearchEdit.currentSearchType() == 1: bibles = self.parent.manager.get_bibles() bible = unicode(self.quickVersionComboBox.currentText()) if bible: @@ -510,7 +504,7 @@ class BibleMediaItem(MediaManagerItem): bible = unicode(self.quickVersionComboBox.currentText()) second_bible = unicode(self.quickSecondComboBox.currentText()) text = unicode(self.quickSearchEdit.text()) - if self.quickSearchComboBox.currentIndex() == 0: + if self.quickSearchEdit.currentSearchType() == 1: # We are doing a 'Verse Search'. self.search_results = self.parent.manager.get_verses(bible, text) if second_bible and self.search_results: From 1933144731741362bc516581469476174556f034 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 6 Feb 2011 08:16:42 +0100 Subject: [PATCH 027/108] --- openlp/core/ui/printserviceorderform.py | 133 ++++++++++++++++++---- resources/forms/printServiceOrder.ui | 145 ++++++++++++++++-------- 2 files changed, 207 insertions(+), 71 deletions(-) diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py index 7f09ceea2..34e1845d0 100644 --- a/openlp/core/ui/printserviceorderform.py +++ b/openlp/core/ui/printserviceorderform.py @@ -38,14 +38,13 @@ class PrintServiceOrderForm(QtGui.QDialog): """ Constructor """ - self.serviceManager = parent QtGui.QDialog.__init__(self, parent) - self.setupUi() - self.retranslateUi() - self.html = u'' + self.serviceManager = parent self.printer = QtGui.QPrinter() self.printDialog = QtGui.QPrintDialog(self.printer, self) self.document = QtGui.QTextDocument() + self.setupUi() + self.retranslateUi() # Load the settings for this dialog. settings = QtCore.QSettings() settings.beginGroup(u'advanced') @@ -56,37 +55,109 @@ class PrintServiceOrderForm(QtGui.QDialog): self.printNotesCheckBox.setChecked(settings.value( u'print notes', QtCore.QVariant(False)).toBool()) settings.endGroup() + # Signals + QtCore.QObject.connect(self.printButton, + QtCore.SIGNAL('clicked()'), self.printServiceOrder) + QtCore.QObject.connect(self.zoomOutButton, + QtCore.SIGNAL('clicked()'), self.zoomOut) + QtCore.QObject.connect(self.zoomInButton, + QtCore.SIGNAL('clicked()'), self.zoomIn) + QtCore.QObject.connect(self.previewWidget, + QtCore.SIGNAL('paintRequested(QPrinter *)'), self.paintRequested) + QtCore.QObject.connect(self.serviceTitleLineEdit, + QtCore.SIGNAL('textChanged(const QString)'), self.updatePreviewText) + QtCore.QObject.connect(self.printSlideTextCheckBox, + QtCore.SIGNAL('stateChanged(int)'), self.updatePreviewText) + QtCore.QObject.connect(self.printNotesCheckBox, + QtCore.SIGNAL('stateChanged(int)'), self.updatePreviewText) + QtCore.QObject.connect(self.printMetaDataCheckBox, + QtCore.SIGNAL('stateChanged(int)'), self.updatePreviewText) + self.updatePreviewText() def setupUi(self): self.dialogLayout = QtGui.QHBoxLayout(self) self.dialogLayout.setObjectName(u'dialogLayout') - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setObjectName(u'verticalLayout') + self.perviewLayout = QtGui.QVBoxLayout() + self.perviewLayout.setObjectName(u'perviewLayout') + self.previewLabel = QtGui.QLabel(self) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.previewLabel.sizePolicy().hasHeightForWidth()) + self.previewLabel.setSizePolicy(sizePolicy) + self.previewLabel.setObjectName(u'previewLabel') + self.perviewLayout.addWidget(self.previewLabel) + self.previewWidget = QtGui.QPrintPreviewWidget(self.printer, self, QtCore.Qt.Widget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.previewWidget.sizePolicy().hasHeightForWidth()) + self.previewWidget.setSizePolicy(sizePolicy) + self.previewWidget.setObjectName(u'previewWidget') + self.previewWidget.fitToWidth() + self.perviewLayout.addWidget(self.previewWidget) + self.dialogLayout.addLayout(self.perviewLayout) + self.settingsLayout = QtGui.QVBoxLayout() + self.settingsLayout.setObjectName(u'settingsLayout') self.serviceTitleLayout = QtGui.QHBoxLayout() self.serviceTitleLayout.setObjectName(u'serviceTitleLayout') self.serviceTitleLabel = QtGui.QLabel(self) self.serviceTitleLabel.setObjectName(u'serviceTitleLabel') self.serviceTitleLayout.addWidget(self.serviceTitleLabel) self.serviceTitleLineEdit = QtGui.QLineEdit(self) + self.serviceTitleLineEdit.setSizePolicy( + QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.serviceTitleLineEdit.setObjectName(u'serviceTitleLineEdit') self.serviceTitleLayout.addWidget(self.serviceTitleLineEdit) - self.verticalLayout.addLayout(self.serviceTitleLayout) + self.settingsLayout.addLayout(self.serviceTitleLayout) self.printSlideTextCheckBox = QtGui.QCheckBox(self) self.printSlideTextCheckBox.setObjectName(u'printSlideTextCheckBox') - self.verticalLayout.addWidget(self.printSlideTextCheckBox) + self.settingsLayout.addWidget(self.printSlideTextCheckBox) self.printNotesCheckBox = QtGui.QCheckBox(self) self.printNotesCheckBox.setObjectName(u'printNotesCheckBox') - self.verticalLayout.addWidget(self.printNotesCheckBox) + self.settingsLayout.addWidget(self.printNotesCheckBox) self.printMetaDataCheckBox = QtGui.QCheckBox(self) self.printMetaDataCheckBox.setObjectName(u'printMetaDataCheckBox') - self.verticalLayout.addWidget(self.printMetaDataCheckBox) - self.verticalLayout.addWidget(save_cancel_button_box(self)) - self.dialogLayout.addLayout(self.verticalLayout) + self.settingsLayout.addWidget(self.printMetaDataCheckBox) + self.buttonLayout = QtGui.QHBoxLayout() + self.zoomOutButton = QtGui.QToolButton(self) + icon = QtGui.QIcon() + #icon.addPixmap(QtGui.QPixmap(u':/exports/export_move_to_list.png'), + icon.addPixmap(QtGui.QPixmap(u'/home/andreas/zoom-out.png'), + QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.zoomOutButton.setIcon(icon) + self.zoomOutButton.setObjectName(u'zoomOutButton') + self.buttonLayout.addWidget(self.zoomOutButton) + self.zoomInButton = QtGui.QToolButton(self) + icon = QtGui.QIcon() + #icon.addPixmap(QtGui.QPixmap(u':/exports/export_remove.png'), + icon.addPixmap(QtGui.QPixmap(u'/home/andreas/zoom-in.png'), + QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.zoomInButton.setIcon(icon) + self.zoomInButton.setObjectName(u'toolButton') + self.buttonLayout.addWidget(self.zoomInButton) + spacerItem = QtGui.QSpacerItem(20, 40, + QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.settingsLayout.addItem(spacerItem) + self.buttonLayout.setObjectName(u'buttonLayout') + spacerItem = QtGui.QSpacerItem(40, 20, + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.buttonLayout.addItem(spacerItem) + self.cancelButton = QtGui.QPushButton(self) + self.cancelButton.setObjectName(u'cancelButton') + self.buttonLayout.addWidget(self.cancelButton) + self.printButton = QtGui.QPushButton(self) + self.printButton.setObjectName(u'printButton') + self.buttonLayout.addWidget(self.printButton) + self.settingsLayout.addLayout(self.buttonLayout) + self.dialogLayout.addLayout(self.settingsLayout) QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): self.setWindowTitle( translate('OpenLP.PrintServiceOrderForm', 'Print Service Order')) + self.previewLabel.setText( + translate('OpenLP.ServiceManager', 'Preview:')) self.printSlideTextCheckBox.setText(translate( 'OpenLP.PrintServiceOrderForm', 'Include slide text if avaialbe')) self.printNotesCheckBox.setText(translate( @@ -95,14 +166,17 @@ class PrintServiceOrderForm(QtGui.QDialog): translate('OpenLP.PrintServiceOrderForm', 'Include play lenght of media items')) self.serviceTitleLabel.setText(translate( - 'OpenLP.PrintServiceOrderForm', 'Service Order Title:')) + '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')) - def serviceOrderText(self): + def updatePreviewText(self): """ Creates the html text, to print the service items. """ + text = u'' if self.serviceTitleLineEdit.text(): text += u'

%s

' % unicode(self.serviceTitleLineEdit.text()) for item in self.serviceManager.serviceItems: @@ -129,8 +203,9 @@ class PrintServiceOrderForm(QtGui.QDialog): # Add service items' notes. if self.printNotesCheckBox.isChecked(): if item.notes: - text += u'

%s %s

' % (translate( - 'OpenLP.ServiceManager', 'Notes:'), 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(): path = os.path.join(item.get_frames()[0][u'path'], @@ -143,6 +218,19 @@ class PrintServiceOrderForm(QtGui.QDialog): text += u'

%s %s

' % (translate( 'OpenLP.ServiceManager', u'Playing time:'), unicode(datetime.timedelta(seconds=length))) + self.document.setHtml(text) + self.previewWidget.updatePreview() + + def paintRequested(self, printer): + """ + Paint the preview. + """ + self.document.print_(printer) + + def printServiceOrder(self): + if not self.printDialog.exec_(): + return + self.document.print_(self.printer) # Save the settings for this dialog. settings = QtCore.QSettings() settings.beginGroup(u'advanced') @@ -153,12 +241,9 @@ class PrintServiceOrderForm(QtGui.QDialog): settings.setValue(u'print notes', QtCore.QVariant(self.printNotesCheckBox.isChecked())) settings.endGroup() - self.document.setHtml(text) - def printServiceOrder(self): - serviceDocument = QtGui.QTextDocument() - serviceDocument.setHtml(settingDialog.serviceOrderText()) - printDialog = QtGui.QPrintDialog() - if not printDialog.exec_(): - return - serviceDocument.print_(printDialog.printer()) + def zoomIn(self): + self.previewWidget.zoomIn() + + def zoomOut(self): + self.previewWidget.zoomOut() diff --git a/resources/forms/printServiceOrder.ui b/resources/forms/printServiceOrder.ui index 84ec84bbe..b76bc012a 100644 --- a/resources/forms/printServiceOrder.ui +++ b/resources/forms/printServiceOrder.ui @@ -13,10 +13,10 @@ Dialog - + - - + + @@ -33,37 +33,47 @@ + + true + - + 0 0 + - + - - - - - Service Title: - - - - + + - + 0 0 + + + + + 0 + 0 + + + + Service Title: + + + @@ -81,14 +91,14 @@ - + Include play lenght of media items - + Qt::Vertical @@ -100,36 +110,75 @@ + + + + - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - Print - - - - + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Print + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + ... + + + + ../../../../../zoom-out.png../../../../../zoom-out.png + + + + + + + ... + + + + ../../../../../zoom-in.png../../../../../zoom-in.png + + @@ -137,6 +186,8 @@ - + + + From ff8fb724e1f041fc7df8fb4a0d4ca0b95102410a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 6 Feb 2011 15:26:13 +0100 Subject: [PATCH 028/108] fixed line break --- openlp/core/ui/printserviceorderform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py index 3f130365a..40a3dede9 100644 --- a/openlp/core/ui/printserviceorderform.py +++ b/openlp/core/ui/printserviceorderform.py @@ -106,7 +106,7 @@ class PrintServiceOrderForm(QtGui.QDialog, Ui_PrintServiceOrderDialog): # Add service items' notes. if self.printNotesCheckBox.isChecked(): if item.notes: - text += u'

%s


%s' % (translate( + text += u'

%s

%s' % (translate( 'OpenLP.ServiceManager', 'Notes:'), item.notes.replace(u'\n', u'
')) # Add play length of media files. From cdc188b2d5bd84d8df3948d7b7731dc97e0e48e7 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 6 Feb 2011 18:21:32 +0100 Subject: [PATCH 029/108] --- openlp/plugins/songs/forms/songexportform.py | 245 ++++++------------- resources/forms/songexportform.ui | 148 ----------- resources/images/export_move_to_list.png | Bin 862 -> 0 bytes resources/images/export_remove.png | Bin 666 -> 0 bytes resources/images/openlp-2.qrc | 2 - 5 files changed, 79 insertions(+), 316 deletions(-) delete mode 100644 resources/forms/songexportform.ui delete mode 100644 resources/images/export_move_to_list.png delete mode 100644 resources/images/export_remove.png diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 307d91570..e445db5f5 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -86,102 +86,61 @@ class SongExportForm(OpenLPWizard): """ Song wizard specific signals. """ - QtCore.QObject.connect(self.addButton, - QtCore.SIGNAL(u'clicked()'), self.onAddSelectedClicked) - QtCore.QObject.connect(self.removeButton, - QtCore.SIGNAL(u'clicked()'), self.onRemoveSelectedClicked) - QtCore.QObject.connect(self.availableListWidget, - QtCore.SIGNAL(u'itemDoubleClicked(QListWidgetItem *)'), - self.onAvailableListItemDoubleClicked) - QtCore.QObject.connect(self.selectedListWidget, - QtCore.SIGNAL(u'itemDoubleClicked(QListWidgetItem *)'), - self.onSelectedListItemDoubleClicked) QtCore.QObject.connect(self.directoryButton, QtCore.SIGNAL(u'clicked()'), self.onDirectoryButtonClicked) - QtCore.QObject.connect(self.allAvailableButton, - QtCore.SIGNAL(u'clicked()'), self.onAllAvailableButtonClicked) - QtCore.QObject.connect(self.allSelectedButton, - QtCore.SIGNAL(u'clicked()'), self.onAllSelectedButtonClicked) +# QtCore.QObject.connect(self.searchLineEdit, +# QtCore.SIGNAL(u'textEdited(const QString&)'), +# self.onSearchLineEditChanged) def addCustomPages(self): """ Add song wizard specific pages. """ - # Source Page - self.sourcePage = QtGui.QWizardPage() - self.sourcePage.setObjectName(u'sourcePage') - self.horizontalLayout = QtGui.QHBoxLayout(self.sourcePage) - self.horizontalLayout.setObjectName(u'horizontalLayout') - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setObjectName(u'verticalLayout') + # 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.gridLayout = QtGui.QGridLayout() +# self.gridLayout.setObjectName(u'gridLayout') + self.availableListWidget = QtGui.QListWidget(self.availableSongsPage) + self.availableListWidget.setObjectName(u'availableListWidget') +# self.gridLayout.addWidget(self.availableListWidget, 0, 0, 1, 1) +# self.searchLineEdit = QtGui.QLineEdit(self.availableSongsPage) +# self.searchLineEdit.setObjectName(u'searchLineEdit') +# self.gridLayout.addWidget(self.searchLineEdit, 1, 0, 1, 1) +# self.availableSongsLayout.addLayout(self.gridLayout) + self.availableSongsLayout.addWidget(self.availableListWidget) + 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.sourcePage) + self.selectedListWidget = QtGui.QListWidget(self.exportSongPage) self.selectedListWidget.setObjectName(u'selectedListWidget') - self.selectedListWidget.setSelectionMode( - QtGui.QAbstractItemView.ExtendedSelection) - self.selectedListWidget.setSortingEnabled(True) - self.gridLayout.addWidget(self.selectedListWidget, 1, 2, 1, 1) - self.gridLayout2 = QtGui.QGridLayout() - self.gridLayout2.setObjectName(u'gridLayout2') - self.addButton = QtGui.QToolButton(self.sourcePage) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(u':/exports/export_move_to_list.png'), - QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.addButton.setIcon(icon) - self.addButton.setObjectName(u'addButton') - self.gridLayout2.addWidget(self.addButton, 1, 0, 1, 1) - self.removeButton = QtGui.QToolButton(self.sourcePage) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(u':/exports/export_remove.png'), - QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.removeButton.setIcon(icon) - self.removeButton.setObjectName(u'removeButton') - self.gridLayout2.addWidget(self.removeButton, 2, 0, 1, 1) - spacerItem = QtGui.QSpacerItem(20, 40, - QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout2.addItem(spacerItem, 0, 0, 1, 1) - self.gridLayout2.addItem(spacerItem, 3, 0, 1, 1) - self.gridLayout.addLayout(self.gridLayout2, 1, 1, 1, 1) - self.availableLabel = QtGui.QLabel(self.sourcePage) - self.availableLabel.setObjectName(u'availableLabel') - self.gridLayout.addWidget(self.availableLabel, 0, 0, 1, 1) - self.selectedLabel = QtGui.QLabel(self.sourcePage) - self.selectedLabel.setObjectName(u'selectedLabel') - self.gridLayout.addWidget(self.selectedLabel, 0, 2, 1, 1) - self.availableListWidget = QtGui.QListWidget(self.sourcePage) - self.availableListWidget.setObjectName(u'availableListWidget') - self.availableListWidget.setSelectionMode( - QtGui.QAbstractItemView.ExtendedSelection) - self.availableListWidget.setSortingEnabled(True) - self.gridLayout.addWidget(self.availableListWidget, 1, 0, 1, 1) - # Button to select all songs in the "selectedListWidget". - self.allSelectedButton = QtGui.QToolButton(self.sourcePage) - self.allSelectedButton.setObjectName(u'allSelectedButton') - self.gridLayout.addWidget(self.allSelectedButton, 3, 2, 1, 1) - # Button to select all songs in the "availableListWidget". - self.allAvailableButton = QtGui.QToolButton(self.sourcePage) - self.allAvailableButton.setObjectName(u'allAvailableButton') - self.gridLayout.addWidget(self.allAvailableButton, 3, 0, 1, 1) - self.verticalLayout.addLayout(self.gridLayout) - self.gridLayout3 = QtGui.QGridLayout() - self.gridLayout3.setObjectName(u'gridLayout3') - self.directoryButton = QtGui.QToolButton(self.sourcePage) + 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) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(u':/exports/export_load.png'), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.directoryButton.setIcon(icon) self.directoryButton.setObjectName(u'directoryButton') - self.gridLayout3.addWidget(self.directoryButton, 0, 2, 1, 1) - self.directoryLineEdit = QtGui.QLineEdit(self.sourcePage) - self.directoryLineEdit.setObjectName(u'directoryLineEdit') - self.gridLayout3.addWidget(self.directoryLineEdit, 0, 1, 1, 1) - self.directoryLabel = QtGui.QLabel(self.sourcePage) - self.directoryLabel.setObjectName(u'directoryLabel') - self.gridLayout3.addWidget(self.directoryLabel, 0, 0, 1, 1) - self.verticalLayout.addLayout(self.gridLayout3) - self.horizontalLayout.addLayout(self.verticalLayout) - self.addPage(self.sourcePage) + 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): """ @@ -197,12 +156,18 @@ class SongExportForm(OpenLPWizard): translate('SongsPlugin.ExportWizardForm', 'This wizard will help to ' 'export your songs to the open and free OpenLyrics worship song ' 'format.')) - self.sourcePage.setTitle( + self.availableSongsPage.setTitle( translate('SongsPlugin.ExportWizardForm', 'Select Songs')) - self.sourcePage.setSubTitle( + self.availableSongsPage.setSubTitle( translate('SongsPlugin.ExportWizardForm', - 'Add the songs, you want to export to the list on the right hand ' - 'side. You can use the buttons below or double click them.')) + 'Check the songs, you want to export.')) + 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( @@ -212,16 +177,6 @@ class SongExportForm(OpenLPWizard): translate('SongsPlugin.ExportWizardForm', 'Ready.')) self.progressBar.setFormat( translate('SongsPlugin.ExportWizardForm', '%p%')) - self.directoryLabel.setText(translate('SongsPlugin.ExportWizardForm', - 'Directory:')) - self.availableLabel.setText( - translate('SongsPlugin.ExportWizardForm', 'Available Songs')) - self.selectedLabel.setText( - translate('SongsPlugin.ExportWizardForm', 'Selected Songs')) - self.allSelectedButton.setText( - translate('SongsPlugin.ExportWizardForm', 'Select all')) - self.allAvailableButton.setText( - translate('SongsPlugin.ExportWizardForm', 'Select all')) def validateCurrentPage(self): """ @@ -229,15 +184,28 @@ class SongExportForm(OpenLPWizard): """ if self.currentPage() == self.welcomePage: return True - elif self.currentPage() == self.sourcePage: - if not self.selectedListWidget.count(): + elif self.currentPage() == self.availableSongsPage: + songs = [song for song in self.availableListWidget.findItems( + QtCore.QString(u''), QtCore.Qt.MatchContains) + if song.checkState() == QtCore.Qt.Checked] + if not songs: 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 - elif not self.directoryLineEdit.text(): + self.selectedListWidget.clear() + # Add the songs to the list of selectd songs. + for song in songs: + title = song.text() + new_song = QtGui.QListWidgetItem(title) + new_song.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) + new_song.setFlags(QtCore.Qt.ItemIsEnabled) + self.selectedListWidget.addItem(new_song) + return True + elif self.currentPage() == self.exportSongPage: + if not self.directoryLineEdit.text(): critical_error_message_box( translate('SongsPlugin.ExportWizardForm', 'No Save Location specified'), @@ -272,11 +240,13 @@ class SongExportForm(OpenLPWizard): for song in songs: authors = u', '.join([author.display_name for author in song.authors]) - song_detail = u'%s (%s)' % (unicode(song.title), authors) - song_name = QtGui.QListWidgetItem(song_detail) - song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) - self.availableListWidget.addItem(song_name) - self.availableListWidget.selectAll() + title = u'%s (%s)' % (unicode(song.title), authors) + song = QtGui.QListWidgetItem(title) + song.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) + song.setFlags(QtCore.Qt.ItemIsSelectable| + QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled) + song.setCheckState(QtCore.Qt.Checked) + self.availableListWidget.addItem(song) Receiver.send_message(u'cursor_normal') def preWizard(self): @@ -293,9 +263,9 @@ class SongExportForm(OpenLPWizard): Perform the actual export. This creates an *openlyricsexport* instance and calls the *do_export* method. """ - self.selectedListWidget.selectAll() - songs = [item.data(QtCore.Qt.UserRole).toPyObject() - for item in self.selectedListWidget.selectedItems()] + songs = [song.data(QtCore.Qt.UserRole).toPyObject() + for song in self.selectedListWidget.findItems( + QtCore.QString(u''), QtCore.Qt.MatchContains)] exporter = OpenLyricsExport( self, songs, unicode(self.directoryLineEdit.text())) if exporter.do_export(): @@ -305,60 +275,9 @@ class SongExportForm(OpenLPWizard): self.progressLabel.setText( translate('SongsPlugin.SongExportForm', 'Your song export failed.')) - - def onAddSelectedClicked(self): - """ - Removes the selected items from the list of available songs and add them - to the list of selected songs. - """ - items = self.availableListWidget.selectedItems() - # Save a list with tuples which consist of the item row, and the item. - items = [(self.availableListWidget.row(item), item) for item in items] - items.sort(reverse=True) - for item in items: - self.availableListWidget.takeItem(item[0]) - self.selectedListWidget.addItem(item[1]) - - def onRemoveSelectedClicked(self): - """ - Removes the selected items from the list of selected songs and add them - back to the list of available songs. - """ - items = self.selectedListWidget.selectedItems() - # Save a list with tuples which consist of the item row, and the item. - items = [(self.selectedListWidget.row(item), item) for item in items] - items.sort(reverse=True) - for item in items: - self.selectedListWidget.takeItem(item[0]) - self.availableListWidget.addItem(item[1]) - - def onAvailableListItemDoubleClicked(self, item): - """ - Adds the double clicked item to the list of selected songs and removes - it from the list of availables songs. - - ``item`` - The *QListWidgetItem* which was double clicked. - """ - self.availableListWidget.takeItem(self.availableListWidget.row(item)) - self.selectedListWidget.addItem(item) - - def onSelectedListItemDoubleClicked(self, item): - """ - Adds the double clicked item back to the list of available songs and - removes it from the list of selected songs. - - ``ìtem`` - The *QListWidgetItem* which was double clicked. - """ - self.selectedListWidget.takeItem(self.selectedListWidget.row(item)) - self.availableListWidget.addItem(item) - - def onAllAvailableButtonClicked(self): - """ - Selects all songs in the *availableListWidget*. - """ - self.availableListWidget.selectAll() + +# def onSearchLineEditChanged(self, text): +# pass def onDirectoryButtonClicked(self): """ @@ -371,9 +290,3 @@ class SongExportForm(OpenLPWizard): options=QtGui.QFileDialog.ShowDirsOnly)) SettingsManager.set_last_dir(self.plugin.settingsSection, path, 1) self.directoryLineEdit.setText(path) - - def onAllSelectedButtonClicked(self): - """ - Selects all songs in the *selectedListWidget*. - """ - self.selectedListWidget.selectAll() diff --git a/resources/forms/songexportform.ui b/resources/forms/songexportform.ui deleted file mode 100644 index ad3ada947..000000000 --- a/resources/forms/songexportform.ui +++ /dev/null @@ -1,148 +0,0 @@ - - - WizardPage - - - - 0 - 0 - 576 - 334 - - - - WizardPage - - - - - - - - - - - - - - - ... - - - - :/exports/export_move_to_list.png:/exports/export_move_to_list.png - - - - - - - ... - - - - :/exports/export_remove.png:/exports/export_remove.png - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Available Songs - - - - - - - Selected Songs - - - - - - - - - - - 0 - 0 - - - - Select all - - - - - - - Select all - - - - - - - - - - - ... - - - - :/exports/export_load.png:/exports/export_load.png - - - - - - - - - - Directory: - - - - - - - - - - - - - - diff --git a/resources/images/export_move_to_list.png b/resources/images/export_move_to_list.png deleted file mode 100644 index 5c000585611b2a7eef251b16d157b7d3fbe343f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 862 zcmV-k1EKthP))=>P)60+m=mEULo}(6DE|o@Zn-E{+#%FBN6by>iN&WA))_C@EKH8?PsO`9y;M64pgk+G zo4(KY^=oW**AUeLtBE#EEYk$bC)G`2e1dcXlWsAycerd)^&Wu9d5P0&wb^L>f^vQ! zG8UuNGFlnXkR*z3tmp&*ekWwL9Qe_)0)eZnJ=$f>#Hl0v@F7v zuf#Z2=f%-o!IdkQ^!ztB5_xqAkpb_8=v?E}e|GukQ7l#SRydGZXRdfqM3|k$=$f~p oro44E|HtiCRwA;~*#1|)1JnysG2#JYp8x;=07*qoM6N<$f~BjP$^ZZW diff --git a/resources/images/export_remove.png b/resources/images/export_remove.png deleted file mode 100644 index ef8e685e2edc2bd322bc20b3930b3b8a4a956692..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 666 zcmV;L0%iS)P)EpEu1Bn~z?SOH%W3`5?j4Qac4I15gKU`L5t>eN996dfGWy$GdT78e~1IEWxDihex7vOw4eG#$fZhali~XI|dwy2)5q7?kev-7Va?i{8#`0ED~iS znPE8+jBfgBF#tb?!0r|!;|Uxhh~`JWR^bpU8u7y9}-^q*}IfB~Nq%ySBz{^kAz z3q)BlA;{zV9S&*>ZND1?U{GxVemB-UgWbmGokGcyqSwMaQBF)n)gu5F4?Uh4@^Ycw-*5-~sCaop*i?$X&F0Mt&l~Qb zfY1*WGoB%i7sW@t*o-kS#!v>NY9h9$NM~6&ozmxk>*nk(P@GSvbVZtA)kJKs3`pjz z2QVQIGtrHEzzyrW3909E+g3AU>}ZYus}ujmzk3qv7);hk;Q#;t07*qoM6N<$f=kC8 Ao&W#< diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 3d953995e..f647eb39d 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -78,9 +78,7 @@ import_load.png
- export_remove.png export_load.png - export_move_to_list.png wizard_exportsong.bmp From 57e489b75ab4651b3dd7c216b73de2c02fc2da1d Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Sun, 6 Feb 2011 18:01:47 +0000 Subject: [PATCH 030/108] Dedupe presentation controllers --- .../presentations/lib/impresscontroller.py | 45 ++++++++++--------- .../presentations/lib/powerpointcontroller.py | 31 +++++++------ 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index f12d36dc8..465f733c7 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -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 __get_text_from_slide(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 __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/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 012f8534a..fcc8bfc47 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -291,13 +291,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 +300,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 From 1824ac31c6d3d552cdf476f8dc28e2f1d1383bb6 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Sun, 6 Feb 2011 19:37:35 +0000 Subject: [PATCH 031/108] Remove mediaitem duplicated calls --- openlp/plugins/images/lib/mediaitem.py | 2 -- openlp/plugins/media/lib/mediaitem.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 71027881c..6e720fa0b 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -84,8 +84,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..f0549a960 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -151,8 +151,6 @@ class MediaMediaItem(MediaManagerItem): return False def initialise(self): - self.listView.setSelectionMode( - QtGui.QAbstractItemView.ExtendedSelection) self.listView.setIconSize(QtCore.QSize(88, 50)) self.loadList(SettingsManager.load_list(self.settingsSection, self.settingsSection)) From 5504c541ad485d0d9b04ddf9cf8999d871b36604 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 6 Feb 2011 21:21:18 +0100 Subject: [PATCH 032/108] removed not needed import, use build_icon --- openlp/core/ui/printserviceorderdialog.py | 15 ++++----------- openlp/core/ui/printserviceorderform.py | 1 - 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/openlp/core/ui/printserviceorderdialog.py b/openlp/core/ui/printserviceorderdialog.py index 5706034a7..70780291a 100644 --- a/openlp/core/ui/printserviceorderdialog.py +++ b/openlp/core/ui/printserviceorderdialog.py @@ -26,9 +26,7 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate -from openlp.core.lib.ui import save_cancel_button_box - +from openlp.core.lib import build_icon, translate class Ui_PrintServiceOrderDialog(object): def setupUi(self, printServiceOrderDialog): @@ -99,17 +97,12 @@ class Ui_PrintServiceOrderDialog(object): QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.zoomButtonLayout.addItem(spacerItem) self.zoomOutButton = QtGui.QToolButton(printServiceOrderDialog) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(u':/general/general_zoom_out.png'), - QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.zoomOutButton.setIcon(icon) + 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) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(u':/general/general_zoom_in.png'), - QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.zoomInButton.setIcon(icon) + 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) diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py index 40a3dede9..d7cf87147 100644 --- a/openlp/core/ui/printserviceorderform.py +++ b/openlp/core/ui/printserviceorderform.py @@ -30,7 +30,6 @@ import os from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import save_cancel_button_box from openlp.core.ui.printserviceorderdialog import Ui_PrintServiceOrderDialog class PrintServiceOrderForm(QtGui.QDialog, Ui_PrintServiceOrderDialog): From 1bcbb83144460e4292c96c5a3e3b8a647c09224f Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 6 Feb 2011 21:26:32 +0100 Subject: [PATCH 033/108] fixed doc --- openlp/core/ui/servicemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index ea09797ba..83d7cbc3c 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1185,7 +1185,7 @@ class ServiceManager(QtGui.QWidget): def printServiceOrder(self): """ - Open a Print a Service Order Sheet. + Print a Service Order Sheet. """ settingDialog = PrintServiceOrderForm(self.mainwindow, self) settingDialog.exec_() From c736d0f1f84019aab2a16f736d01444a380b98bd Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 7 Feb 2011 15:55:02 +0000 Subject: [PATCH 034/108] Cleanups --- openlp/core/lib/imagemanager.py | 2 +- openlp/core/lib/plugin.py | 3 +++ openlp/core/ui/displaytagtab.py | 18 +++++++++--------- openlp/core/ui/pluginform.py | 4 +++- openlp/plugins/songs/lib/db.py | 1 - openlp/plugins/songs/lib/opensongimport.py | 3 --- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index 02d7010be..fb242602a 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') diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index fb31006b5..476df79b0 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -44,6 +44,9 @@ class PluginStatus(object): class StringContent(object): + """ + Provide standard strings for objects to use. + """ Name = u'name' Import = u'import' Load = u'load' diff --git a/openlp/core/ui/displaytagtab.py b/openlp/core/ui/displaytagtab.py index 983bc17a8..54d021913 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 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') 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/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/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index eb16f4ba4..e7557f952 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -36,9 +36,6 @@ from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) -class OpenSongImportError(Exception): - pass - class OpenSongImport(SongImport): """ Import songs exported from OpenSong From 9f63c43b5b7b189012be56e3091c6282ba28e904 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 7 Feb 2011 16:29:06 +0000 Subject: [PATCH 035/108] Missing function docstrings --- openlp/plugins/bibles/lib/__init__.py | 7 +++++++ openlp/plugins/songs/lib/__init__.py | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index e6fff1cc8..7de4e82c3 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' 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', From 5410d8dfd114694ed0169e548b83b4e12990b7bf Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 7 Feb 2011 18:19:54 +0100 Subject: [PATCH 036/108] added a field to enter custom service notes --- openlp/core/ui/printserviceorderdialog.py | 10 +++++++++- openlp/core/ui/printserviceorderform.py | 22 ++++++++++++++-------- resources/forms/printserviceorderdialog.ui | 10 ++++++++++ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/openlp/core/ui/printserviceorderdialog.py b/openlp/core/ui/printserviceorderdialog.py index 70780291a..5e7b5a031 100644 --- a/openlp/core/ui/printserviceorderdialog.py +++ b/openlp/core/ui/printserviceorderdialog.py @@ -26,7 +26,7 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import build_icon, translate +from openlp.core.lib import build_icon, translate, SpellTextEdit class Ui_PrintServiceOrderDialog(object): def setupUi(self, printServiceOrderDialog): @@ -78,6 +78,12 @@ class Ui_PrintServiceOrderDialog(object): 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') @@ -127,3 +133,5 @@ class Ui_PrintServiceOrderDialog(object): '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 index d7cf87147..546d52d55 100644 --- a/openlp/core/ui/printserviceorderform.py +++ b/openlp/core/ui/printserviceorderform.py @@ -55,21 +55,24 @@ class PrintServiceOrderForm(QtGui.QDialog, Ui_PrintServiceOrderDialog): settings.endGroup() # Signals QtCore.QObject.connect(self.printButton, - QtCore.SIGNAL('clicked()'), self.printServiceOrder) + QtCore.SIGNAL(u'clicked()'), self.printServiceOrder) QtCore.QObject.connect(self.zoomOutButton, - QtCore.SIGNAL('clicked()'), self.zoomOut) + QtCore.SIGNAL(u'clicked()'), self.zoomOut) QtCore.QObject.connect(self.zoomInButton, - QtCore.SIGNAL('clicked()'), self.zoomIn) + QtCore.SIGNAL(u'clicked()'), self.zoomIn) QtCore.QObject.connect(self.previewWidget, - QtCore.SIGNAL('paintRequested(QPrinter *)'), self.paintRequested) + QtCore.SIGNAL(u'paintRequested(QPrinter *)'), self.paintRequested) QtCore.QObject.connect(self.serviceTitleLineEdit, - QtCore.SIGNAL('textChanged(const QString)'), self.updatePreviewText) + QtCore.SIGNAL(u'textChanged(const QString)'), + self.updatePreviewText) QtCore.QObject.connect(self.printSlideTextCheckBox, - QtCore.SIGNAL('stateChanged(int)'), self.updatePreviewText) + QtCore.SIGNAL(u'stateChanged(int)'), self.updatePreviewText) QtCore.QObject.connect(self.printNotesCheckBox, - QtCore.SIGNAL('stateChanged(int)'), self.updatePreviewText) + QtCore.SIGNAL(u'stateChanged(int)'), self.updatePreviewText) QtCore.QObject.connect(self.printMetaDataCheckBox, - QtCore.SIGNAL('stateChanged(int)'), self.updatePreviewText) + 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() @@ -120,6 +123,9 @@ class PrintServiceOrderForm(QtGui.QDialog, Ui_PrintServiceOrderDialog): text += u'

%s %s

' % (translate( 'OpenLP.ServiceManager', u'Playing time:'), unicode(datetime.timedelta(seconds=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() diff --git a/resources/forms/printserviceorderdialog.ui b/resources/forms/printserviceorderdialog.ui index 66f44384f..131979b65 100644 --- a/resources/forms/printserviceorderdialog.ui +++ b/resources/forms/printserviceorderdialog.ui @@ -79,6 +79,16 @@ + + + + <b>Custom Notes:</b> + + + + + + From 9dec924f9873550aaad58b8c422bb38b659e69fb Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 7 Feb 2011 18:27:38 +0100 Subject: [PATCH 037/108] added missing doc --- openlp/core/ui/printserviceorderform.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py index 546d52d55..3b01f9ac7 100644 --- a/openlp/core/ui/printserviceorderform.py +++ b/openlp/core/ui/printserviceorderform.py @@ -132,6 +132,9 @@ class PrintServiceOrderForm(QtGui.QDialog, Ui_PrintServiceOrderDialog): def paintRequested(self, printer): """ Paint the preview of the *self.document*. + + ``printer`` + A *QPrinter* object. """ self.document.print_(printer) From 91db60ad7febbfa17bf48f6d17b6bfa2e2c6a5f7 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 7 Feb 2011 19:02:13 +0100 Subject: [PATCH 038/108] fixed typos --- openlp/core/ui/printserviceorderdialog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/printserviceorderdialog.py b/openlp/core/ui/printserviceorderdialog.py index 5e7b5a031..f8db4d1c0 100644 --- a/openlp/core/ui/printserviceorderdialog.py +++ b/openlp/core/ui/printserviceorderdialog.py @@ -121,12 +121,12 @@ class Ui_PrintServiceOrderDialog(object): self.previewLabel.setText( translate('OpenLP.ServiceManager', 'Preview:')) self.printSlideTextCheckBox.setText(translate( - 'OpenLP.PrintServiceOrderForm', 'Include slide text if avaialbe')) + '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 lenght of media items')) + 'Include play length of media items')) self.serviceTitleLabel.setText(translate( 'OpenLP.PrintServiceOrderForm', 'Title:')) self.serviceTitleLineEdit.setText(translate('OpenLP.ServiceManager', From 975d9f6a1c78ca633a0cfd8b9ef67f47e9f75a58 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 7 Feb 2011 18:46:22 +0000 Subject: [PATCH 039/108] Fix Song previews when returning from blank screen --- openlp/core/ui/slidecontroller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index bf0453e05..d86dba4b0 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -874,7 +874,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.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) From b6735065c0e35e9f20a63caecc2219b10d8f5e69 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 7 Feb 2011 19:37:07 +0000 Subject: [PATCH 040/108] Presentation dedupe --- .../presentations/lib/impresscontroller.py | 11 +- openlp/plugins/presentations/lib/mediaitem.py | 6 +- .../presentations/lib/messagelistener.py | 2 +- .../presentations/lib/powerpointcontroller.py | 11 +- .../presentations/lib/pptviewcontroller.py | 13 +- .../lib/presentationcontroller.py | 295 +++++++++--------- 6 files changed, 160 insertions(+), 178 deletions(-) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 465f733c7..79e7d0ea8 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 @@ -183,14 +184,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): """ diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index a0173cb27..09020692a 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -186,7 +186,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 +226,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 +260,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 fcc8bfc47..65e9f35ff 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): """ diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 4c8e7bf95..a64cd31dd 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): """ @@ -247,4 +240,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 From 943dcf1ffc7c0b73e9adbe11b3d9a0ff3b081c46 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 7 Feb 2011 22:07:48 +0000 Subject: [PATCH 041/108] Naming cleanups --- openlp/core/lib/db.py | 6 +- openlp/core/lib/displaytags.py | 6 +- openlp/core/ui/mainwindow.py | 110 +++++++++++++++--------------- openlp/core/ui/slidecontroller.py | 6 +- 4 files changed, 64 insertions(+), 64 deletions(-) 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/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 9af4931fb..e489d7b5d 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -77,10 +77,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 +102,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 +125,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 +186,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 +215,9 @@ class Ui_MainWindow(object): self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', u':/tools/tools_add.png') mainWindow.actionList.add_action(self.ToolsAddToolItem, u'Tools') - self.SettingsPluginListItem = icon_action(mainWindow, - u'SettingsPluginListItem', u':/system/settings_plugin_list.png') - mainWindow.actionList.add_action(self.SettingsPluginListItem, + self.settingsPluginListItem = icon_action(mainWindow, + u'settingsPluginListItem', u':/system/settings_plugin_list.png') + mainWindow.actionList.add_action(self.settingsPluginListItem, u'Settings') # i18n Language Items self.AutoLanguageItem = checkable_action(mainWindow, @@ -264,14 +264,14 @@ 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)) @@ -279,7 +279,7 @@ class Ui_MainWindow(object): 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) @@ -300,18 +300,18 @@ class Ui_MainWindow(object): 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( @@ -403,11 +403,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')) @@ -501,20 +501,20 @@ 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.settingsPluginListItem, QtCore.SIGNAL(u'triggered()'), self.onPluginItemClicked) QtCore.QObject.connect(self.SettingsConfigureItem, QtCore.SIGNAL(u'triggered()'), self.onSettingsConfigureItemClicked) @@ -748,9 +748,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 +867,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/slidecontroller.py b/openlp/core/ui/slidecontroller.py index bf0453e05..3445c63a1 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -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) @@ -455,7 +455,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 +584,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) From a97cd1bb9b42ab8bd91a9db8026b6d4c90a1bf77 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 8 Feb 2011 02:45:37 +0000 Subject: [PATCH 042/108] Unnecessary line breaks --- openlp/core/lib/theme.py | 3 +-- openlp/core/ui/maindisplay.py | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index d119f19ff..dca226069 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -583,8 +583,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/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index bae00c2da..c4ab75aac 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -326,8 +326,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 @@ -398,8 +397,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: From 0d6af789a00cc2604eb78ef076a658af7cc0b3c6 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 8 Feb 2011 03:25:50 +0000 Subject: [PATCH 043/108] Fix theme importing (Support 98) --- openlp/core/theme/theme.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/openlp/core/theme/theme.py b/openlp/core/theme/theme.py index e506fc2c2..8a57c3ae7 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 = \ ''' @@ -184,7 +187,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 From dc7486c3f87d43ced34ddff2b87230467d6acbec Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 8 Feb 2011 16:22:54 +0000 Subject: [PATCH 044/108] D'oh --- openlp/plugins/songs/lib/easislidesimport.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/lib/easislidesimport.py b/openlp/plugins/songs/lib/easislidesimport.py index 0b10ce428..5d56af8ce 100644 --- a/openlp/plugins/songs/lib/easislidesimport.py +++ b/openlp/plugins/songs/lib/easislidesimport.py @@ -81,14 +81,14 @@ 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(self.title, song.Title1, True) + self._add_unicode_attribute(self.alternate_title, song.Title2) + self._add_unicode_attribute(self.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_unicode_attribute(self.song_book_name, song.BookReference) self._parse_and_add_lyrics(song) return self._success From 9b1a19086965cfbf5bbbdd34f3921699b8df4955 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 8 Feb 2011 18:05:09 +0100 Subject: [PATCH 045/108] use build_icon, bug fix --- openlp/plugins/songs/forms/songexportform.py | 38 +++++++++----------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index a24aec3e2..4c918f66b 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -31,7 +31,7 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Receiver, SettingsManager, translate +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 @@ -112,7 +112,6 @@ class SongExportForm(OpenLPWizard): # self.availableSongsLayout.addLayout(self.gridLayout) self.availableSongsLayout.addWidget(self.availableListWidget) self.addPage(self.availableSongsPage) - # The page with the selected songs. self.exportSongPage = QtGui.QWizardPage() self.exportSongPage.setObjectName(u'availableSongsPage') @@ -132,10 +131,7 @@ class SongExportForm(OpenLPWizard): self.directoryLineEdit.setObjectName(u'directoryLineEdit') self.horizontalLayout.addWidget(self.directoryLineEdit) self.directoryButton = QtGui.QToolButton(self.exportSongPage) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(u':/exports/export_load.png'), - QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.directoryButton.setIcon(icon) + 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) @@ -185,10 +181,10 @@ class SongExportForm(OpenLPWizard): if self.currentPage() == self.welcomePage: return True elif self.currentPage() == self.availableSongsPage: - songs = [song for song in self.availableListWidget.findItems( + items = [item for item in self.availableListWidget.findItems( QtCore.QString(u''), QtCore.Qt.MatchContains) - if song.checkState() == QtCore.Qt.Checked] - if not songs: + if item.checkState() == QtCore.Qt.Checked] + if not items: critical_error_message_box( translate('SongsPlugin.ExportWizardForm', 'No Song Selected'), @@ -196,13 +192,13 @@ class SongExportForm(OpenLPWizard): 'You need to add at least one Song to export.')) return False self.selectedListWidget.clear() - # Add the songs to the list of selectd songs. - for song in songs: - title = song.text() - new_song = QtGui.QListWidgetItem(title) - new_song.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) - new_song.setFlags(QtCore.Qt.ItemIsEnabled) - self.selectedListWidget.addItem(new_song) + # 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(): @@ -241,12 +237,12 @@ class SongExportForm(OpenLPWizard): authors = u', '.join([author.display_name for author in song.authors]) title = u'%s (%s)' % (unicode(song.title), authors) - song = QtGui.QListWidgetItem(title) - song.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) - song.setFlags(QtCore.Qt.ItemIsSelectable| + item = QtGui.QListWidgetItem(title) + item.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) + item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled) - song.setCheckState(QtCore.Qt.Checked) - self.availableListWidget.addItem(song) + item.setCheckState(QtCore.Qt.Checked) + self.availableListWidget.addItem(item) Receiver.send_message(u'cursor_normal') def preWizard(self): From 2c0625a3923c8d113dd128e503dc98fa858ddf8c Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 8 Feb 2011 17:29:24 +0000 Subject: [PATCH 046/108] Dedupe, fix logic, fix names --- openlp/core/lib/ui.py | 22 +++++++++ openlp/core/ui/themewizard.py | 23 ++------- openlp/plugins/alerts/lib/alertstab.py | 67 ++++++++++---------------- 3 files changed, 51 insertions(+), 61 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 4a346b3e1..30f442012 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -219,3 +219,25 @@ def add_widget_completer(cache, widget): completer = QtGui.QCompleter(cache) completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) widget.setCompleter(completer) + +def create_valign_combo(parent, layout): + """ + Creates a standard label and combo box for asking users to select a + vertical alignment. + + ``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:')) + verticalComboBox = QtGui.QComboBox(parent) + verticalComboBox.setObjectName(u'VerticalComboBox') + verticalComboBox.addItem(translate('OpenLP.Ui', 'Top')) + verticalComboBox.addItem(translate('OpenLP.Ui', 'Middle')) + verticalComboBox.addItem(translate('OpenLP.Ui', 'Bottom')) + verticalLabel.setBuddy(verticalComboBox) + layout.addRow(verticalLabel, verticalComboBox) diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index 49522df70..e50ab0d45 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,7 @@ 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(self.alignmentPage, self.alignmentLayout) self.transitionsLabel = QtGui.QLabel(self.alignmentPage) self.transitionsLabel.setObjectName(u'TransitionsLabel') self.transitionsCheckBox = QtGui.QCheckBox(self.alignmentPage) @@ -450,8 +445,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 +459,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 +484,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/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index 96870d94c..01dbc2f7e 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -27,6 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, translate +from openlp.core.lib.ui import create_valign_combo class AlertsTab(SettingsTab): """ @@ -40,48 +41,43 @@ class AlertsTab(SettingsTab): 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.fontGroupBox, self.fontLayout) + self.leftLayout.addWidget(self.fontGroupBox) self.leftLayout.addStretch() self.PreviewGroupBox = QtGui.QGroupBox(self.rightColumn) self.PreviewGroupBox.setObjectName(u'PreviewGroupBox') @@ -99,15 +95,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 +117,10 @@ 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')) def onBackgroundColorButtonClicked(self): new_color = QtGui.QColorDialog.getColor( @@ -148,9 +134,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) @@ -197,14 +180,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.LocationComboBox.currentIndex() + settings.setValue(u'location', QtCore.QVariant(self.location)) settings.endGroup() def updateDisplay(self): From 261328d952a1d7cad22bab43cbc63fb6ce48b301 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 8 Feb 2011 19:31:55 +0000 Subject: [PATCH 047/108] Fix last commit --- openlp/core/lib/ui.py | 19 +++++++++++-------- openlp/core/ui/themewizard.py | 3 ++- openlp/plugins/alerts/lib/alertstab.py | 6 +++--- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 30f442012..843f4ef92 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -220,11 +220,14 @@ def add_widget_completer(cache, widget): completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) widget.setCompleter(completer) -def create_valign_combo(parent, layout): +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. @@ -234,10 +237,10 @@ def create_valign_combo(parent, layout): verticalLabel = QtGui.QLabel(parent) verticalLabel.setObjectName(u'VerticalLabel') verticalLabel.setText(translate('OpenLP.Ui', '&Vertical Align:')) - verticalComboBox = QtGui.QComboBox(parent) - verticalComboBox.setObjectName(u'VerticalComboBox') - verticalComboBox.addItem(translate('OpenLP.Ui', 'Top')) - verticalComboBox.addItem(translate('OpenLP.Ui', 'Middle')) - verticalComboBox.addItem(translate('OpenLP.Ui', 'Bottom')) - verticalLabel.setBuddy(verticalComboBox) - layout.addRow(verticalLabel, verticalComboBox) + 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/ui/themewizard.py b/openlp/core/ui/themewizard.py index e50ab0d45..38dd9f1dc 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -242,7 +242,8 @@ class Ui_ThemeWizard(object): self.horizontalComboBox.setObjectName(u'HorizontalComboBox') self.alignmentLayout.addRow(self.horizontalLabel, self.horizontalComboBox) - create_valign_combo(self.alignmentPage, self.alignmentLayout) + 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) diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index 01dbc2f7e..a350c13cc 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -76,7 +76,7 @@ class AlertsTab(SettingsTab): self.TimeoutSpinBox.setMaximum(180) self.TimeoutSpinBox.setObjectName(u'TimeoutSpinBox') self.fontLayout.addRow(self.TimeoutLabel, self.TimeoutSpinBox) - create_valign_combo(self.fontGroupBox, self.fontLayout) + create_valign_combo(self, self.fontGroupBox, self.fontLayout) self.leftLayout.addWidget(self.fontGroupBox) self.leftLayout.addStretch() self.PreviewGroupBox = QtGui.QGroupBox(self.rightColumn) @@ -171,7 +171,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) @@ -186,7 +186,7 @@ class AlertsTab(SettingsTab): 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)) - self.location = self.LocationComboBox.currentIndex() + self.location = self.verticalComboBox.currentIndex() settings.setValue(u'location', QtCore.QVariant(self.location)) settings.endGroup() From 5607b09f0defe03b10d0e19976a8e4ba21f42cf7 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Wed, 9 Feb 2011 05:04:12 +0000 Subject: [PATCH 048/108] Dedupe translations --- openlp/core/lib/ui.py | 28 +++++++++++++++-- openlp/core/ui/displaytagtab.py | 5 ++-- openlp/core/ui/mainwindow.py | 5 ++-- openlp/core/ui/slidecontroller.py | 7 ++--- openlp/core/ui/themeform.py | 5 ++-- openlp/core/ui/themestab.py | 3 +- openlp/plugins/alerts/lib/alertstab.py | 8 ++--- openlp/plugins/bibles/bibleplugin.py | 15 +++++----- .../plugins/bibles/forms/bibleimportform.py | 5 ++-- openlp/plugins/custom/customplugin.py | 17 ++++++----- .../plugins/custom/forms/editcustomdialog.py | 8 ++--- openlp/plugins/images/imageplugin.py | 15 +++++----- openlp/plugins/images/lib/mediaitem.py | 4 +-- openlp/plugins/media/mediaplugin.py | 15 +++++----- .../presentations/presentationplugin.py | 11 +++---- openlp/plugins/songs/forms/editsongdialog.py | 17 ++++------- openlp/plugins/songs/forms/songimportform.py | 8 ++--- .../songs/forms/songmaintenancedialog.py | 30 +++++++------------ openlp/plugins/songs/lib/mediaitem.py | 7 ++--- openlp/plugins/songs/songsplugin.py | 13 ++++---- 20 files changed, 116 insertions(+), 110 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 843f4ef92..41e88b54b 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -34,6 +34,29 @@ 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') + AllFiles = translate('OpenLP.Ui', 'All Files') + Authors = translate('OpenLP.Ui', 'Authors') + Delete = translate('OpenLP.Ui', '&Delete') + Edit = translate('OpenLP.Ui', '&Edit') + Error = translate('OpenLP.Ui', 'Error') + Import = translate('OpenLP.Ui', 'Import') + Live = translate('OpenLP.Ui', 'Live') + Load = translate('OpenLP.Ui', 'Load') + New = translate('OpenLP.Ui', 'New') + OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') + Preview = translate('OpenLP.Ui', 'Preview') + Service = translate('OpenLP.Ui', 'Service') + Theme = translate('OpenLP.Ui', 'Theme') + Themes = translate('OpenLP.Ui', 'Themes') + + def add_welcome_page(parent, image): """ Generate an opening welcome page for a wizard using a provided image. @@ -98,13 +121,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, 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 +156,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, diff --git a/openlp/core/ui/displaytagtab.py b/openlp/core/ui/displaytagtab.py index 54d021913..abf0ca44f 100644 --- a/openlp/core/ui/displaytagtab.py +++ b/openlp/core/ui/displaytagtab.py @@ -34,7 +34,7 @@ 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): """ @@ -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/mainwindow.py b/openlp/core/ui/mainwindow.py index e489d7b5d..c87a9544a 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 @@ -295,7 +296,7 @@ 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')) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index cf2c0983b..1caf43dfd 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__) @@ -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;') diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index 018df7597..f86fa0143 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,7 @@ 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 = '%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/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/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index a350c13cc..4090503db 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, translate -from openlp.core.lib.ui import create_valign_combo +from openlp.core.lib.ui import UiStrings, create_valign_combo class AlertsTab(SettingsTab): """ @@ -117,10 +117,8 @@ class AlertsTab(SettingsTab): translate('AlertsPlugin.AlertsTab', 'Alert timeout:')) self.TimeoutSpinBox.setSuffix( translate('AlertsPlugin.AlertsTab', 's')) - self.PreviewGroupBox.setTitle( - translate('AlertsPlugin.AlertsTab', 'Preview')) - self.FontPreview.setText( - translate('AlertsPlugin.AlertsTab', 'OpenLP 2.0')) + self.PreviewGroupBox.setTitle(UiStrings.Preview) + self.FontPreview.setText(UiStrings.OLPV2) def onBackgroundColorButtonClicked(self): new_color = QtGui.QColorDialog.getColor( diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index e3b2ad4aa..e1dadd4bf 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -29,6 +29,7 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate +from openlp.core.lib.ui import UiStrings from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem log = logging.getLogger(__name__) @@ -137,38 +138,38 @@ class BiblePlugin(Plugin): # Middle Header Bar ## Import Action ## self.textStrings[StringContent.Import] = { - u'title': translate('BiblesPlugin', '&Import'), + u'title': UiStrings.Import, u'tooltip': translate('BiblesPlugin', 'Import a Bible') } ## New Action ## self.textStrings[StringContent.New] = { - u'title': translate('BiblesPlugin', '&Add'), + u'title': UiStrings.Add, u'tooltip': translate('BiblesPlugin', 'Add a new Bible') } ## Edit Action ## self.textStrings[StringContent.Edit] = { - u'title': translate('BiblesPlugin', '&Edit'), + u'title': UiStrings.Edit, u'tooltip': translate('BiblesPlugin', 'Edit the selected Bible') } ## Delete Action ## self.textStrings[StringContent.Delete] = { - u'title': translate('BiblesPlugin', '&Delete'), + u'title': UiStrings.Delete, u'tooltip': translate('BiblesPlugin', 'Delete the selected Bible') } ## Preview Action ## self.textStrings[StringContent.Preview] = { - u'title': translate('BiblesPlugin', 'Preview'), + u'title': UiStrings.Preview, u'tooltip': translate('BiblesPlugin', 'Preview the selected Bible') } ## Send Live Action ## self.textStrings[StringContent.Live] = { - u'title': translate('BiblesPlugin', 'Live'), + u'title': UiStrings.Live, u'tooltip': translate('BiblesPlugin', 'Send the selected Bible live') } ## Add to Service Action ## self.textStrings[StringContent.Service] = { - u'title': translate('BiblesPlugin', 'Service'), + u'title': UiStrings.Service, u'tooltip': translate('BiblesPlugin', 'Add the selected Bible to the service') } diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index f21dd0e07..f528a4b1b 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -35,7 +35,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, SettingsManager, translate from openlp.core.lib.db import delete_database -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.core.utils import AppLocation, string_is_unicode from openlp.plugins.bibles.lib.manager import BibleFormat @@ -745,8 +745,7 @@ class BibleImportForm(OpenLPWizard): """ if filters: filters += u';;' - filters += u'%s (*)' % translate('BiblesPlugin.ImportWizardForm', - 'All Files') + filters += u'%s (*)' % UiStrings.AllFiles filename = QtGui.QFileDialog.getOpenFileName(self, title, os.path.dirname(SettingsManager.get_last_dir( self.plugin.settingsSection, 1)), filters) diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index 54cb38501..210556ad8 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -30,6 +30,7 @@ from forms import EditCustomForm from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.db import Manager +from openlp.core.lib.ui import UiStrings from openlp.plugins.custom.lib import CustomMediaItem, CustomTab from openlp.plugins.custom.lib.db import CustomSlide, init_schema @@ -114,49 +115,49 @@ class CustomPlugin(Plugin): # Middle Header Bar ## Import Action ## self.textStrings[StringContent.Import] = { - u'title': translate('CustomsPlugin', 'Import'), + u'title': UiStrings.Import, u'tooltip': translate('CustomsPlugin', 'Import a Custom') } ## Load Action ## self.textStrings[StringContent.Load] = { - u'title': translate('CustomsPlugin', 'Load'), + u'title': UiStrings.Load, u'tooltip': translate('CustomsPlugin', 'Load a new Custom') } ## New Action ## self.textStrings[StringContent.New] = { - u'title': translate('CustomsPlugin', 'Add'), + u'title': UiStrings.Add, u'tooltip': translate('CustomsPlugin', 'Add a new Custom') } ## Edit Action ## self.textStrings[StringContent.Edit] = { - u'title': translate('CustomsPlugin', 'Edit'), + u'title': UiStrings.Edit, u'tooltip': translate('CustomsPlugin', 'Edit the selected Custom') } ## Delete Action ## self.textStrings[StringContent.Delete] = { - u'title': translate('CustomsPlugin', 'Delete'), + u'title': UiStrings.Delete, u'tooltip': translate('CustomsPlugin', 'Delete the selected Custom') } ## Preview Action ## self.textStrings[StringContent.Preview] = { - u'title': translate('CustomsPlugin', 'Preview'), + u'title': UiStrings.Preview, u'tooltip': translate('CustomsPlugin', 'Preview the selected Custom') } ## Send Live Action ## self.textStrings[StringContent.Live] = { - u'title': translate('CustomsPlugin', 'Live'), + u'title': UiStrings.Live, u'tooltip': translate('CustomsPlugin', 'Send the selected Custom live') } ## Add to Service Action ## self.textStrings[StringContent.Service] = { - u'title': translate('CustomsPlugin', 'Service'), + u'title': UiStrings.Service, u'tooltip': translate('CustomsPlugin', 'Add the selected Custom to the service') } diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index b7887aa90..1ca29732b 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_save_cancel_button_box, \ create_delete_push_button, create_up_down_push_button_set class Ui_CustomEditDialog(object): @@ -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/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index ea118d3ec..0af48bacd 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -27,6 +27,7 @@ import logging from openlp.core.lib import Plugin, StringContent, build_icon, translate +from openlp.core.lib.ui import UiStrings from openlp.plugins.images.lib import ImageMediaItem log = logging.getLogger(__name__) @@ -74,43 +75,43 @@ class ImagePlugin(Plugin): # Middle Header Bar ## Load Button ## self.textStrings[StringContent.Load] = { - u'title': translate('ImagePlugin', 'Load'), + u'title': UiStrings.Load, u'tooltip': translate('ImagePlugin', 'Load a new Image') } ## New Button ## self.textStrings[StringContent.New] = { - u'title': translate('ImagePlugin', 'Add'), + u'title': UiStrings.Add, u'tooltip': translate('ImagePlugin', 'Add a new Image') } ## Edit Button ## self.textStrings[StringContent.Edit] = { - u'title': translate('ImagePlugin', 'Edit'), + u'title': UiStrings.Edit, u'tooltip': translate('ImagePlugin', 'Edit the selected Image') } ## Delete Button ## self.textStrings[StringContent.Delete] = { - u'title': translate('ImagePlugin', 'Delete'), + u'title': UiStrings.Delete, u'tooltip': translate('ImagePlugin', 'Delete the selected Image') } ## Preview ## self.textStrings[StringContent.Preview] = { - u'title': translate('ImagePlugin', 'Preview'), + u'title': UiStrings.Preview, u'tooltip': translate('ImagePlugin', 'Preview the selected Image') } ## Live Button ## self.textStrings[StringContent.Live] = { - u'title': translate('ImagePlugin', 'Live'), + u'title': UiStrings.Live, u'tooltip': translate('ImagePlugin', 'Send the selected Image live') } ## Add to service Button ## self.textStrings[StringContent.Service] = { - u'title': translate('ImagePlugin', 'Service'), + u'title': UiStrings.Service, u'tooltip': translate('ImagePlugin', 'Add the selected Image to the service') } diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 6e720fa0b..6ec4cf264 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -32,7 +32,7 @@ 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.ui import UiStrings, critical_error_message_box from openlp.core.utils import AppLocation, delete_file, get_images_filter log = logging.getLogger(__name__) @@ -64,7 +64,7 @@ class ImageMediaItem(MediaManagerItem): 'Select Image(s)') file_formats = get_images_filter() self.OnNewFileMasks = u'%s;;%s (*.*) (*)' % (file_formats, - unicode(translate('ImagePlugin.MediaItem', 'All Files'))) + unicode(UiStrings.AllFiles)) self.replaceAction.setText( translate('ImagePlugin.MediaItem', 'Replace Background')) self.replaceAction.setToolTip( diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index ad6087daf..42dcc321c 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -30,6 +30,7 @@ import mimetypes from PyQt4.phonon import Phonon from openlp.core.lib import Plugin, StringContent, build_icon, translate +from openlp.core.lib.ui import UiStrings from openlp.plugins.media.lib import MediaMediaItem, MediaTab log = logging.getLogger(__name__) @@ -103,43 +104,43 @@ class MediaPlugin(Plugin): # Middle Header Bar ## Load Action ## self.textStrings[StringContent.Load] = { - u'title': translate('MediaPlugin', 'Load'), + u'title': UiStrings.Load, u'tooltip': translate('MediaPlugin', 'Load a new Media') } ## New Action ## self.textStrings[StringContent.New] = { - u'title': translate('MediaPlugin', 'Add'), + u'title': UiStrings.Add, u'tooltip': translate('MediaPlugin', 'Add a new Media') } ## Edit Action ## self.textStrings[StringContent.Edit] = { - u'title': translate('MediaPlugin', 'Edit'), + u'title': UiStrings.Edit, u'tooltip': translate('MediaPlugin', 'Edit the selected Media') } ## Delete Action ## self.textStrings[StringContent.Delete] = { - u'title': translate('MediaPlugin', 'Delete'), + u'title': UiStrings.Delete, u'tooltip': translate('MediaPlugin', 'Delete the selected Media') } ## Preview Action ## self.textStrings[StringContent.Preview] = { - u'title': translate('MediaPlugin', 'Preview'), + u'title': UiStrings.Preview, u'tooltip': translate('MediaPlugin', 'Preview the selected Media') } ## Send Live Action ## self.textStrings[StringContent.Live] = { - u'title': translate('MediaPlugin', 'Live'), + u'title': UiStrings.Live, u'tooltip': translate('MediaPlugin', 'Send the selected Media live') } ## Add to Service Action ## self.textStrings[StringContent.Service] = { - u'title': translate('MediaPlugin', 'Service'), + u'title': UiStrings.Service, u'tooltip': translate('MediaPlugin', 'Add the selected Media to the service') } diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 17417df58..213ff2927 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -31,6 +31,7 @@ import os import logging from openlp.core.lib import Plugin, StringContent, build_icon, translate +from openlp.core.lib.ui import UiStrings from openlp.core.utils import AppLocation from openlp.plugins.presentations.lib import PresentationController, \ PresentationMediaItem, PresentationTab @@ -169,31 +170,31 @@ class PresentationPlugin(Plugin): # Middle Header Bar ## Load Action ## self.textStrings[StringContent.Load] = { - u'title': translate('PresentationPlugin', 'Load'), + u'title': UiStrings.Load, u'tooltip': translate('PresentationPlugin', 'Load a new Presentation') } ## Delete Action ## self.textStrings[StringContent.Delete] = { - u'title': translate('PresentationPlugin', 'Delete'), + u'title': UiStrings.Delete, u'tooltip': translate('PresentationPlugin', 'Delete the selected Presentation') } ## Preview Action ## self.textStrings[StringContent.Preview] = { - u'title': translate('PresentationPlugin', 'Preview'), + u'title': UiStrings.Preview, u'tooltip': translate('PresentationPlugin', 'Preview the selected Presentation') } ## Send Live Action ## self.textStrings[StringContent.Live] = { - u'title': translate('PresentationPlugin', 'Live'), + u'title': UiStrings.Live, u'tooltip': translate('PresentationPlugin', 'Send the selected Presentation live') } ## Add to Service Action ## self.textStrings[StringContent.Service] = { - u'title': translate('PresentationPlugin', 'Service'), + u'title': UiStrings.Service, u'tooltip': translate('PresentationPlugin', 'Add the selected Presentation to the service') } diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index e9be62830..6854a17ae 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_save_cancel_button_box class Ui_EditSongDialog(object): def setupUi(self, 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/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index a9af7f6b2..8cd8c70fa 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 @@ -215,8 +215,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, @@ -489,8 +488,7 @@ class SongImportForm(OpenLPWizard): """ 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) 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/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 66cff169f..312d0d6e7 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -33,6 +33,7 @@ from sqlalchemy.sql import or_ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \ ItemCapabilities, translate, check_item_selected, PluginStatus +from openlp.core.lib.ui import UiStrings from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \ SongImportForm from openlp.plugins.songs.lib import OpenLyrics, SongXML @@ -147,10 +148,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() diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 7efe73db2..6b9564af1 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -31,6 +31,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.db import Manager +from openlp.core.lib.ui import UiStrings from openlp.plugins.songs.lib import SongMediaItem, SongsTab, SongXML from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.importer import SongFormat @@ -227,37 +228,37 @@ class SongsPlugin(Plugin): # Middle Header Bar ## New Action ## self.textStrings[StringContent.New] = { - u'title': translate('SongsPlugin', 'Add'), + u'title': UiStrings.Add, u'tooltip': translate('SongsPlugin', 'Add a new Song') } ## Edit Action ## self.textStrings[StringContent.Edit] = { - u'title': translate('SongsPlugin', 'Edit'), + u'title': UiStrings.Edit, u'tooltip': translate('SongsPlugin', 'Edit the selected Song') } ## Delete Action ## self.textStrings[StringContent.Delete] = { - u'title': translate('SongsPlugin', 'Delete'), + u'title': UiStrings.Delete, u'tooltip': translate('SongsPlugin', 'Delete the selected Song') } ## Preview Action ## self.textStrings[StringContent.Preview] = { - u'title': translate('SongsPlugin', 'Preview'), + u'title': UiStrings.Preview, u'tooltip': translate('SongsPlugin', 'Preview the selected Song') } ## Send Live Action ## self.textStrings[StringContent.Live] = { - u'title': translate('SongsPlugin', 'Live'), + u'title': UiStrings.Live, u'tooltip': translate('SongsPlugin', 'Send the selected Song live') } ## Add to Service Action ## self.textStrings[StringContent.Service] = { - u'title': translate('SongsPlugin', 'Service'), + u'title': UiStrings.Service, u'tooltip': translate('SongsPlugin', 'Add the selected Song to the service') } From 2ce4aeba9271085f9710b0d1350a6c0b70aff4fc Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Wed, 9 Feb 2011 14:04:50 +0000 Subject: [PATCH 049/108] Fixes --- openlp/core/lib/ui.py | 2 +- openlp/plugins/presentations/lib/impresscontroller.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 41e88b54b..5d1b722c5 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -122,7 +122,7 @@ def critical_error_message_box(title=None, message=None, parent=None, Should this message box question the user. """ 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} diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 79e7d0ea8..7de095d54 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -429,7 +429,7 @@ class ImpressDocument(PresentationDocument): ``slide_no`` The slide the text is required for, starting at 1 """ - return __get_text_from_slide(slide_no) + return self.__get_text_from_page(slide_no) def get_slide_notes(self, slide_no): """ @@ -438,7 +438,7 @@ class ImpressDocument(PresentationDocument): ``slide_no`` The slide the notes are required for, starting at 1 """ - return __get_text_from_page(slide_no, True) + return self.__get_text_from_page(slide_no, True) def __get_text_from_page(self, slide_no, notes=False): """ From 45f9fa8f2f7b582ed671de2c08b6e077b34f95da Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Wed, 9 Feb 2011 17:30:38 +0000 Subject: [PATCH 050/108] More translation refactors --- openlp/core/lib/plugin.py | 32 +++++++++++++- openlp/core/lib/ui.py | 14 ++++++ openlp/core/ui/slidecontroller.py | 4 ++ openlp/core/ui/thememanager.py | 12 +++--- openlp/plugins/bibles/bibleplugin.py | 33 +------------- openlp/plugins/custom/customplugin.py | 43 +------------------ openlp/plugins/images/imageplugin.py | 43 +------------------ openlp/plugins/media/mediaplugin.py | 43 +------------------ .../presentations/presentationplugin.py | 31 +------------ openlp/plugins/songs/songsplugin.py | 37 +--------------- 10 files changed, 61 insertions(+), 231 deletions(-) diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 476df79b0..cfd3fb4c2 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__) @@ -323,4 +324,33 @@ class Plugin(QtCore.QObject): """ Called to define all translatable texts of the plugin """ - pass + ## Load Action ## + self._setSingularTextString(StringContent.Load, + UiStrings.Load, UiStrings.LoadANew) + ## New Action ## + self._setSingularTextString(StringContent.New, + UiStrings.Add, UiStrings.AddANew) + ## Edit Action ## + self._setSingularTextString(StringContent.Edit, + UiStrings.Edit, UiStrings.EditSelect) + ## Delete Action ## + self._setSingularTextString(StringContent.Delete, + UiStrings.Delete, UiStrings.DeleteSelect) + ## Preview Action ## + self._setSingularTextString(StringContent.Preview, + UiStrings.Preview, UiStrings.PreviewSelect) + ## Send Live Action ## + self._setSingularTextString(StringContent.Live, + UiStrings.Live, UiStrings.SendSelectLive) + ## Add to Service Action ## + self._setSingularTextString(StringContent.Service, + UiStrings.Service, UiStrings.AddSelectService) + + def _setSingularTextString(self, name, title, tooltip): + """ + Utility method for creating a plugin's textStrings. This method makes + use of the singular name of the plugin object so must only be called + after this has been set. + """ + self.textStrings[name] = { u'title': title, u'tooltip': tooltip % + self.getString(StringContent.Name)[u'singular']} diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 5d1b722c5..ec1c324fe 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -41,17 +41,31 @@ class UiStrings(object): # These strings should need a good reason to be retranslated elsewhere. # Should some/more/less of these have an & attached? Add = translate('OpenLP.Ui', '&Add') + AddANew = unicode(translate('OpenLP.Ui', 'Add a new %s')) + AddSelectService = unicode(translate('OpenLP.Ui', + 'Add the selected %s to the service')) AllFiles = translate('OpenLP.Ui', 'All Files') Authors = translate('OpenLP.Ui', 'Authors') Delete = translate('OpenLP.Ui', '&Delete') + DeleteSelect = unicode(translate('OpenLP.Ui', 'Delete the selected %s')) + DeleteType = unicode(translate('OpenLP.Ui', 'Delete %s')) Edit = translate('OpenLP.Ui', '&Edit') + EditSelect = unicode(translate('OpenLP.Ui', 'Edit the selected %s')) + EditType = unicode(translate('OpenLP.Ui', 'Edit %s')) Error = translate('OpenLP.Ui', 'Error') + ExportType = unicode(translate('OpenLP.Ui', 'Export %s')) Import = translate('OpenLP.Ui', 'Import') + ImportType = unicode(translate('OpenLP.Ui', 'Import %s')) Live = translate('OpenLP.Ui', 'Live') Load = translate('OpenLP.Ui', 'Load') + LoadANew = unicode(translate('OpenLP.Ui', 'Load a new %s')) New = translate('OpenLP.Ui', 'New') + NewType = unicode(translate('OpenLP.Ui', 'New %s')) OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') Preview = translate('OpenLP.Ui', 'Preview') + PreviewSelect = unicode(translate('OpenLP.Ui', 'Preview the selected %s')) + SendSelectLive = unicode(translate('OpenLP.Ui', + 'Send the selected %s live')) Service = translate('OpenLP.Ui', 'Service') Theme = translate('OpenLP.Ui', 'Theme') Themes = translate('OpenLP.Ui', 'Themes') diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 1caf43dfd..c6b58ac56 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -178,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) @@ -196,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'), diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 317065c6e..93a5ef187 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -35,7 +35,7 @@ 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 +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, \ @@ -62,28 +62,28 @@ class ThemeManager(QtGui.QWidget): self.layout.setObjectName(u'layout') self.toolbar = OpenLPToolbar(self) self.toolbar.addToolbarButton( - translate('OpenLP.ThemeManager', 'New Theme'), + UiStrings.NewType % UiStrings.Theme, u':/themes/theme_new.png', translate('OpenLP.ThemeManager', 'Create a new theme.'), self.onAddTheme) self.toolbar.addToolbarButton( - translate('OpenLP.ThemeManager', 'Edit Theme'), + UiStrings.EditType % UiStrings.Theme, u':/themes/theme_edit.png', translate('OpenLP.ThemeManager', 'Edit a theme.'), self.onEditTheme) self.deleteToolbarAction = self.toolbar.addToolbarButton( - translate('OpenLP.ThemeManager', 'Delete Theme'), + UiStrings.DeleteType % UiStrings.Theme, u':/general/general_delete.png', translate('OpenLP.ThemeManager', 'Delete a theme.'), self.onDeleteTheme) self.toolbar.addSeparator() self.toolbar.addToolbarButton( - translate('OpenLP.ThemeManager', 'Import Theme'), + UiStrings.ImportType % UiStrings.Theme, u':/general/general_import.png', translate('OpenLP.ThemeManager', 'Import a theme.'), self.onImportTheme) self.toolbar.addToolbarButton( - translate('OpenLP.ThemeManager', 'Export Theme'), + UiStrings.ExportType % UiStrings.Theme, u':/general/general_export.png', translate('OpenLP.ThemeManager', 'Export a theme.'), self.onExportTheme) diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index e1dadd4bf..89102a8eb 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -141,35 +141,4 @@ class BiblePlugin(Plugin): u'title': UiStrings.Import, u'tooltip': translate('BiblesPlugin', 'Import a Bible') } - ## New Action ## - self.textStrings[StringContent.New] = { - u'title': UiStrings.Add, - u'tooltip': translate('BiblesPlugin', 'Add a new Bible') - } - ## Edit Action ## - self.textStrings[StringContent.Edit] = { - u'title': UiStrings.Edit, - u'tooltip': translate('BiblesPlugin', 'Edit the selected Bible') - } - ## Delete Action ## - self.textStrings[StringContent.Delete] = { - u'title': UiStrings.Delete, - u'tooltip': translate('BiblesPlugin', 'Delete the selected Bible') - } - ## Preview Action ## - self.textStrings[StringContent.Preview] = { - u'title': UiStrings.Preview, - u'tooltip': translate('BiblesPlugin', 'Preview the selected Bible') - } - ## Send Live Action ## - self.textStrings[StringContent.Live] = { - u'title': UiStrings.Live, - u'tooltip': translate('BiblesPlugin', - 'Send the selected Bible live') - } - ## Add to Service Action ## - self.textStrings[StringContent.Service] = { - u'title': UiStrings.Service, - u'tooltip': translate('BiblesPlugin', - 'Add the selected Bible to the service') - } + Plugin.setPluginTextStrings(self) diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index 210556ad8..f20fbc9cd 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -119,48 +119,7 @@ class CustomPlugin(Plugin): u'tooltip': translate('CustomsPlugin', 'Import a Custom') } - ## Load Action ## - self.textStrings[StringContent.Load] = { - u'title': UiStrings.Load, - u'tooltip': translate('CustomsPlugin', - 'Load a new Custom') - } - ## New Action ## - self.textStrings[StringContent.New] = { - u'title': UiStrings.Add, - u'tooltip': translate('CustomsPlugin', - 'Add a new Custom') - } - ## Edit Action ## - self.textStrings[StringContent.Edit] = { - u'title': UiStrings.Edit, - u'tooltip': translate('CustomsPlugin', - 'Edit the selected Custom') - } - ## Delete Action ## - self.textStrings[StringContent.Delete] = { - u'title': UiStrings.Delete, - u'tooltip': translate('CustomsPlugin', - 'Delete the selected Custom') - } - ## Preview Action ## - self.textStrings[StringContent.Preview] = { - u'title': UiStrings.Preview, - u'tooltip': translate('CustomsPlugin', - 'Preview the selected Custom') - } - ## Send Live Action ## - self.textStrings[StringContent.Live] = { - u'title': UiStrings.Live, - u'tooltip': translate('CustomsPlugin', - 'Send the selected Custom live') - } - ## Add to Service Action ## - self.textStrings[StringContent.Service] = { - u'title': UiStrings.Service, - u'tooltip': translate('CustomsPlugin', - 'Add the selected Custom to the service') - } + Plugin.setPluginTextStrings(self) def finalise(self): """ diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 0af48bacd..51ef20960 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -73,45 +73,4 @@ class ImagePlugin(Plugin): u'title': translate('ImagePlugin', 'Images', 'container title') } # Middle Header Bar - ## Load Button ## - self.textStrings[StringContent.Load] = { - u'title': UiStrings.Load, - u'tooltip': translate('ImagePlugin', - 'Load a new Image') - } - ## New Button ## - self.textStrings[StringContent.New] = { - u'title': UiStrings.Add, - u'tooltip': translate('ImagePlugin', - 'Add a new Image') - } - ## Edit Button ## - self.textStrings[StringContent.Edit] = { - u'title': UiStrings.Edit, - u'tooltip': translate('ImagePlugin', - 'Edit the selected Image') - } - ## Delete Button ## - self.textStrings[StringContent.Delete] = { - u'title': UiStrings.Delete, - u'tooltip': translate('ImagePlugin', - 'Delete the selected Image') - } - ## Preview ## - self.textStrings[StringContent.Preview] = { - u'title': UiStrings.Preview, - u'tooltip': translate('ImagePlugin', - 'Preview the selected Image') - } - ## Live Button ## - self.textStrings[StringContent.Live] = { - u'title': UiStrings.Live, - u'tooltip': translate('ImagePlugin', - 'Send the selected Image live') - } - ## Add to service Button ## - self.textStrings[StringContent.Service] = { - u'title': UiStrings.Service, - u'tooltip': translate('ImagePlugin', - 'Add the selected Image to the service') - } + Plugin.setPluginTextStrings(self) diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 42dcc321c..1619a07fe 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -102,45 +102,4 @@ class MediaPlugin(Plugin): u'title': translate('MediaPlugin', 'Media', 'container title') } # Middle Header Bar - ## Load Action ## - self.textStrings[StringContent.Load] = { - u'title': UiStrings.Load, - u'tooltip': translate('MediaPlugin', - 'Load a new Media') - } - ## New Action ## - self.textStrings[StringContent.New] = { - u'title': UiStrings.Add, - u'tooltip': translate('MediaPlugin', - 'Add a new Media') - } - ## Edit Action ## - self.textStrings[StringContent.Edit] = { - u'title': UiStrings.Edit, - u'tooltip': translate('MediaPlugin', - 'Edit the selected Media') - } - ## Delete Action ## - self.textStrings[StringContent.Delete] = { - u'title': UiStrings.Delete, - u'tooltip': translate('MediaPlugin', - 'Delete the selected Media') - } - ## Preview Action ## - self.textStrings[StringContent.Preview] = { - u'title': UiStrings.Preview, - u'tooltip': translate('MediaPlugin', - 'Preview the selected Media') - } - ## Send Live Action ## - self.textStrings[StringContent.Live] = { - u'title': UiStrings.Live, - u'tooltip': translate('MediaPlugin', - 'Send the selected Media live') - } - ## Add to Service Action ## - self.textStrings[StringContent.Service] = { - u'title': UiStrings.Service, - u'tooltip': translate('MediaPlugin', - 'Add the selected Media to the service') - } + Plugin.setPluginTextStrings(self) diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 213ff2927..c4cf29aca 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -168,33 +168,4 @@ class PresentationPlugin(Plugin): 'container title') } # Middle Header Bar - ## Load Action ## - self.textStrings[StringContent.Load] = { - u'title': UiStrings.Load, - u'tooltip': translate('PresentationPlugin', - 'Load a new Presentation') - } - ## Delete Action ## - self.textStrings[StringContent.Delete] = { - u'title': UiStrings.Delete, - u'tooltip': translate('PresentationPlugin', - 'Delete the selected Presentation') - } - ## Preview Action ## - self.textStrings[StringContent.Preview] = { - u'title': UiStrings.Preview, - u'tooltip': translate('PresentationPlugin', - 'Preview the selected Presentation') - } - ## Send Live Action ## - self.textStrings[StringContent.Live] = { - u'title': UiStrings.Live, - u'tooltip': translate('PresentationPlugin', - 'Send the selected Presentation live') - } - ## Add to Service Action ## - self.textStrings[StringContent.Service] = { - u'title': UiStrings.Service, - u'tooltip': translate('PresentationPlugin', - 'Add the selected Presentation to the service') - } + Plugin.setPluginTextStrings(self) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 6b9564af1..74991fe37 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -226,42 +226,7 @@ class SongsPlugin(Plugin): u'title': translate('SongsPlugin', 'Songs', 'container title') } # Middle Header Bar - ## New Action ## - self.textStrings[StringContent.New] = { - u'title': UiStrings.Add, - u'tooltip': translate('SongsPlugin', - 'Add a new Song') - } - ## Edit Action ## - self.textStrings[StringContent.Edit] = { - u'title': UiStrings.Edit, - u'tooltip': translate('SongsPlugin', - 'Edit the selected Song') - } - ## Delete Action ## - self.textStrings[StringContent.Delete] = { - u'title': UiStrings.Delete, - u'tooltip': translate('SongsPlugin', - 'Delete the selected Song') - } - ## Preview Action ## - self.textStrings[StringContent.Preview] = { - u'title': UiStrings.Preview, - u'tooltip': translate('SongsPlugin', - 'Preview the selected Song') - } - ## Send Live Action ## - self.textStrings[StringContent.Live] = { - u'title': UiStrings.Live, - u'tooltip': translate('SongsPlugin', - 'Send the selected Song live') - } - ## Add to Service Action ## - self.textStrings[StringContent.Service] = { - u'title': UiStrings.Service, - u'tooltip': translate('SongsPlugin', - 'Add the selected Song to the service') - } + Plugin.setPluginTextStrings(self) def finalise(self): """ From 6a409e89575efcba8d0b2c5f3a2bc3781beee9a2 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 9 Feb 2011 20:36:13 +0100 Subject: [PATCH 051/108] added search box, added buttons --- openlp/plugins/songs/forms/songexportform.py | 135 +++++++++++++++---- 1 file changed, 110 insertions(+), 25 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 4c918f66b..748bf3fdf 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -86,11 +86,17 @@ class SongExportForm(OpenLPWizard): """ Song wizard specific signals. """ + QtCore.QObject.connect(self.availableListWidget, + QtCore.SIGNAL(u'itemPressed(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) -# QtCore.QObject.connect(self.searchLineEdit, -# QtCore.SIGNAL(u'textEdited(const QString&)'), -# self.onSearchLineEditChanged) def addCustomPages(self): """ @@ -101,16 +107,30 @@ class SongExportForm(OpenLPWizard): self.availableSongsPage.setObjectName(u'availableSongsPage') self.availableSongsLayout = QtGui.QHBoxLayout(self.availableSongsPage) self.availableSongsLayout.setObjectName(u'availableSongsLayout') -# self.gridLayout = QtGui.QGridLayout() -# self.gridLayout.setObjectName(u'gridLayout') + self.verticalLayout = QtGui.QVBoxLayout() + self.verticalLayout.setObjectName(u'verticalLayout') self.availableListWidget = QtGui.QListWidget(self.availableSongsPage) self.availableListWidget.setObjectName(u'availableListWidget') -# self.gridLayout.addWidget(self.availableListWidget, 0, 0, 1, 1) -# self.searchLineEdit = QtGui.QLineEdit(self.availableSongsPage) -# self.searchLineEdit.setObjectName(u'searchLineEdit') -# self.gridLayout.addWidget(self.searchLineEdit, 1, 0, 1, 1) -# self.availableSongsLayout.addLayout(self.gridLayout) - self.availableSongsLayout.addWidget(self.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() @@ -145,23 +165,29 @@ class SongExportForm(OpenLPWizard): self.setWindowTitle( translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard')) self.titleLabel.setText( - u'%s' % \ + 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 ' + 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', 'Deselect All')) + self.checkButton.setText( + translate('SongsPlugin.ExportWizardForm', 'Select 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.')) + translate('SongsPlugin.ExportWizardForm', + 'Select the directory you want the songs to be saved.')) self.directoryLabel.setText( translate('SongsPlugin.ExportWizardForm', 'Directory:')) self.progressPage.setTitle( @@ -181,9 +207,10 @@ class SongExportForm(OpenLPWizard): if self.currentPage() == self.welcomePage: return True elif self.currentPage() == self.availableSongsPage: - items = [item for item in self.availableListWidget.findItems( - QtCore.QString(u''), QtCore.Qt.MatchContains) - if item.checkState() == QtCore.Qt.Checked] + items = [ + item for item in self._findListWidgetItems( + self.availableListWidget) if item.checkState() + ] if not items: critical_error_message_box( translate('SongsPlugin.ExportWizardForm', @@ -241,7 +268,7 @@ class SongExportForm(OpenLPWizard): item.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled) - item.setCheckState(QtCore.Qt.Checked) + item.setCheckState(QtCore.Qt.Unchecked) self.availableListWidget.addItem(item) Receiver.send_message(u'cursor_normal') @@ -259,9 +286,10 @@ class SongExportForm(OpenLPWizard): 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.selectedListWidget.findItems( - QtCore.QString(u''), QtCore.Qt.MatchContains)] + 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(): @@ -272,8 +300,65 @@ class SongExportForm(OpenLPWizard): translate('SongsPlugin.SongExportForm', 'Your song export failed.')) -# def onSearchLineEditChanged(self, text): -# pass + 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. + """ + return + item.setCheckState( + QtCore.Qt.Unchecked if item.checkState() else QtCore.Qt.Checked) + + def onSearchLineEditChanged(self, text): + """ + The *searchLineEdit*'s text has been changed. Update the list of + available songs. Note that any song, which does not match the ``text`` + will be hidden, but not unchecked! + + ``text`` + The text of the *searchLineEdit*. (QString) + """ + search_result = [ + song for song in self._findListWidgetItems( + self.availableListWidget, unicode(text)) + ] + for item in self._findListWidgetItems(self.availableListWidget): + item.setHidden(False if item in search_result else True) + + def onUncheckButtonClicked(self): + """ + The *uncheckButton* has been clicked. Set all songs unchecked. + """ + for row in range(self.availableListWidget.count()): + item = self.availableListWidget.item(row) + item.setCheckState(QtCore.Qt.Unchecked) + + def onCheckButtonClicked(self): + """ + The *checkButton* has been clicked. Set all songs checked. + """ + for row in range(self.availableListWidget.count()): + item = self.availableListWidget.item(row) + item.setCheckState(QtCore.Qt.Checked) def onDirectoryButtonClicked(self): """ From 73cf235b49063ed9426d2a7ee73cd2606c556a5c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 9 Feb 2011 20:55:53 +0100 Subject: [PATCH 052/108] doc fix --- openlp/plugins/songs/lib/xml.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index da2a7a08d..75e03c855 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -90,8 +90,10 @@ class SongXML(object): Add a verse to the ** tag. ``type`` - A string denoting the type of verse. Possible values are "V", - "C", "B", "P", "I", "E" and "O". + A string denoting the type of verse. Possible values are *Verse*, + *Chorus*, *Bridge*, *Pre-Chorus*, *Intro*, *Ending* and *Other*. + Any other type is **not** allowed, this also includes translated + types. ``number`` An integer denoting the number of the item, for example: verse 1. @@ -127,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' Date: Wed, 9 Feb 2011 21:05:44 +0100 Subject: [PATCH 053/108] enable checking the item when double clicking --- openlp/plugins/songs/forms/songexportform.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 748bf3fdf..86d1b8681 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -87,7 +87,7 @@ class SongExportForm(OpenLPWizard): Song wizard specific signals. """ QtCore.QObject.connect(self.availableListWidget, - QtCore.SIGNAL(u'itemPressed(QListWidgetItem*)'), self.onItemPressed) + QtCore.SIGNAL(u'itemActivated(QListWidgetItem*)'), self.onItemPressed) QtCore.QObject.connect(self.searchLineEdit, QtCore.SIGNAL(u'textEdited(const QString&)'), self.onSearchLineEditChanged) @@ -324,7 +324,6 @@ class SongExportForm(OpenLPWizard): ``item`` The *QListWidgetItem* which was pressed. """ - return item.setCheckState( QtCore.Qt.Unchecked if item.checkState() else QtCore.Qt.Checked) From 000732db69151b40f5c14016815cf74aa860d7ab Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 9 Feb 2011 21:11:23 +0100 Subject: [PATCH 054/108] string fix --- openlp/plugins/songs/forms/songexportform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 86d1b8681..5d5b9a8b1 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -180,9 +180,9 @@ class SongExportForm(OpenLPWizard): self.searchLabel.setText( translate('SongsPlugin.ExportWizardForm', 'Search:')) self.uncheckButton.setText( - translate('SongsPlugin.ExportWizardForm', 'Deselect All')) + translate('SongsPlugin.ExportWizardForm', 'Uncheck All')) self.checkButton.setText( - translate('SongsPlugin.ExportWizardForm', 'Select All')) + translate('SongsPlugin.ExportWizardForm', 'Check All')) self.exportSongPage.setTitle( translate('SongsPlugin.ExportWizardForm', 'Select Directory')) self.exportSongPage.setSubTitle( From fb97816711c92143a640373b2c08dbbe7061898a Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Wed, 9 Feb 2011 21:30:41 +0000 Subject: [PATCH 055/108] Unused imports --- openlp/plugins/images/imageplugin.py | 1 - openlp/plugins/media/mediaplugin.py | 1 - openlp/plugins/presentations/presentationplugin.py | 1 - openlp/plugins/songs/songsplugin.py | 1 - 4 files changed, 4 deletions(-) diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 51ef20960..2642e3055 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -27,7 +27,6 @@ import logging from openlp.core.lib import Plugin, StringContent, build_icon, translate -from openlp.core.lib.ui import UiStrings from openlp.plugins.images.lib import ImageMediaItem log = logging.getLogger(__name__) diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 1619a07fe..3ebb77171 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -30,7 +30,6 @@ import mimetypes from PyQt4.phonon import Phonon from openlp.core.lib import Plugin, StringContent, build_icon, translate -from openlp.core.lib.ui import UiStrings from openlp.plugins.media.lib import MediaMediaItem, MediaTab log = logging.getLogger(__name__) diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index c4cf29aca..c81cdc028 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -31,7 +31,6 @@ import os import logging from openlp.core.lib import Plugin, StringContent, build_icon, translate -from openlp.core.lib.ui import UiStrings from openlp.core.utils import AppLocation from openlp.plugins.presentations.lib import PresentationController, \ PresentationMediaItem, PresentationTab diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 74991fe37..b6676140d 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -31,7 +31,6 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.db import Manager -from openlp.core.lib.ui import UiStrings from openlp.plugins.songs.lib import SongMediaItem, SongsTab, SongXML from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.importer import SongFormat From 5ecb3ce8b8b592bfeadab5d2a0cd71c9849f3f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=B5ldaru?= Date: Thu, 10 Feb 2011 00:53:35 +0200 Subject: [PATCH 056/108] works in gnome, menu icon missing --- openlp/core/ui/mainwindow.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index c87a9544a..c55787907 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -216,6 +216,9 @@ class Ui_MainWindow(object): self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', u':/tools/tools_add.png') mainWindow.actionList.add_action(self.ToolsAddToolItem, u'Tools') + self.ToolsOpenDataFolder = icon_action(mainWindow, u'ToolsOpenDataFolder', + u':/tools/tools_add.png') + mainWindow.actionList.add_action(self.ToolsOpenDataFolder, u'Tools') self.settingsPluginListItem = icon_action(mainWindow, u'settingsPluginListItem', u':/system/settings_plugin_list.png') mainWindow.actionList.add_action(self.settingsPluginListItem, @@ -276,6 +279,7 @@ class Ui_MainWindow(object): self.SettingsLanguageMenu.menuAction(), None, self.SettingsShortcutsItem, self.SettingsConfigureItem)) add_actions(self.ToolsMenu, (self.ToolsAddToolItem, None)) + add_actions(self.ToolsMenu, (self.ToolsOpenDataFolder, None)) add_actions(self.HelpMenu, (self.HelpDocumentationItem, self.HelpOnlineHelpItem, None, self.HelpWebSiteItem, self.HelpAboutItem)) @@ -433,6 +437,10 @@ class Ui_MainWindow(object): translate('OpenLP.MainWindow', 'Add &Tool...')) self.ToolsAddToolItem.setStatusTip(translate('OpenLP.MainWindow', 'Add an application to the list of tools.')) + self.ToolsOpenDataFolder.setText( + translate('OpenLP.MainWindow', 'Open &Data Folder...')) + self.ToolsOpenDataFolder.setStatusTip(translate('OpenLP.MainWindow', + 'Open the folder where songs, bibles and other data resides.')) self.ModeDefaultItem.setText( translate('OpenLP.MainWindow', '&Default')) self.ModeDefaultItem.setStatusTip(translate('OpenLP.MainWindow', @@ -515,6 +523,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): QtCore.SIGNAL(u'triggered()'), self.onHelpWebSiteClicked) QtCore.QObject.connect(self.HelpAboutItem, QtCore.SIGNAL(u'triggered()'), self.onHelpAboutItemClicked) + QtCore.QObject.connect(self.ToolsOpenDataFolder, + QtCore.SIGNAL(u'triggered()'), self.onToolsOpenDataFolderClicked) QtCore.QObject.connect(self.settingsPluginListItem, QtCore.SIGNAL(u'triggered()'), self.onPluginItemClicked) QtCore.QObject.connect(self.SettingsConfigureItem, @@ -703,6 +713,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 From 2495aa82fcc9ccefde26d66345abde11f04fe71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=B5ldaru?= Date: Thu, 10 Feb 2011 02:11:05 +0200 Subject: [PATCH 057/108] better icon --- openlp/core/ui/mainwindow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index c55787907..b6584f4fc 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -217,7 +217,7 @@ class Ui_MainWindow(object): u':/tools/tools_add.png') mainWindow.actionList.add_action(self.ToolsAddToolItem, u'Tools') self.ToolsOpenDataFolder = icon_action(mainWindow, u'ToolsOpenDataFolder', - u':/tools/tools_add.png') + 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') From 660028fd1e9ab50a93b2a089d827581024153ad1 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 00:36:00 +0000 Subject: [PATCH 058/108] More button boxes and translations --- openlp/core/lib/ui.py | 21 ++++++++++++---- openlp/core/ui/exceptionform.py | 3 +-- openlp/core/ui/filerenamedialog.py | 24 +++++++++---------- openlp/core/ui/filerenameform.py | 6 +---- openlp/core/ui/serviceitemeditdialog.py | 4 ++-- openlp/core/ui/servicenoteform.py | 4 ++-- openlp/core/ui/settingsdialog.py | 10 ++------ openlp/core/ui/thememanager.py | 2 +- .../plugins/custom/forms/editcustomdialog.py | 4 ++-- .../custom/forms/editcustomslidedialog.py | 4 ++-- openlp/plugins/images/lib/mediaitem.py | 14 ++++------- openlp/plugins/media/lib/mediaitem.py | 18 ++++++-------- openlp/plugins/songs/forms/authorsdialog.py | 4 ++-- openlp/plugins/songs/forms/editsongdialog.py | 4 ++-- openlp/plugins/songs/forms/editversedialog.py | 4 ++-- openlp/plugins/songs/forms/songbookdialog.py | 4 ++-- openlp/plugins/songs/forms/songimportform.py | 3 +-- openlp/plugins/songs/forms/topicsdialog.py | 4 ++-- .../songusage/forms/songusagedeletedialog.py | 16 ++++--------- .../songusage/forms/songusagedetaildialog.py | 12 +++------- 20 files changed, 71 insertions(+), 94 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index ec1c324fe..8f6f266e3 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -64,6 +64,10 @@ class UiStrings(object): OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') Preview = translate('OpenLP.Ui', 'Preview') PreviewSelect = unicode(translate('OpenLP.Ui', 'Preview the selected %s')) + 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') SendSelectLive = unicode(translate('OpenLP.Ui', 'Send the selected %s live')) Service = translate('OpenLP.Ui', 'Service') @@ -98,18 +102,25 @@ def add_welcome_page(parent, image): parent.welcomeLayout.addStretch() parent.addPage(parent.welcomePage) -def create_save_cancel_button_box(parent): +def create_accept_reject_button_box(parent, okay=False): """ - Creates a standard dialog button box with save and cancel buttons. The - button box is connected to the parent's ``accept()`` and ``reject()`` + Creates a standard dialog button box with two buttons. The buttons default + to save and cancel but the ``okay`` parameter can be used to make the + buttons okay and cancel instead. + The button box is connected to the parent's ``accept()`` and ``reject()`` methods to handle the default ``accepted()`` and ``rejected()`` signals. ``parent`` The parent object. This should be a ``QWidget`` descendant. + + ``okay`` + If true creates an okay/cancel combination instead of save/cancel. """ button_box = QtGui.QDialogButtonBox(parent) - button_box.setStandardButtons( - QtGui.QDialogButtonBox.Save | QtGui.QDialogButtonBox.Cancel) + accept_button = QtGui.QDialogButtonBox.Save + if okay: + accept_button = QtGui.QDialogButtonBox.Ok + button_box.setStandardButtons(accept_button | QtGui.QDialogButtonBox.Cancel) button_box.setObjectName(u'%sButtonBox' % parent) QtCore.QObject.connect(button_box, QtCore.SIGNAL(u'accepted()'), parent.accept) diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 7f9e23c61..69803003b 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -176,8 +176,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..60abe77da 100644 --- a/openlp/core/ui/filerenameform.py +++ b/openlp/core/ui/filerenameform.py @@ -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/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/servicenoteform.py b/openlp/core/ui/servicenoteform.py index 473cc1685..a12b693f8 100644 --- a/openlp/core/ui/servicenoteform.py +++ b/openlp/core/ui/servicenoteform.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box class ServiceNoteForm(QtGui.QDialog): """ @@ -48,7 +48,7 @@ class ServiceNoteForm(QtGui.QDialog): self.textEdit = QtGui.QTextEdit(self) self.textEdit.setObjectName(u'textEdit') self.dialogLayout.addWidget(self.textEdit) - self.dialogLayout.addWidget(create_save_cancel_button_box(self)) + self.dialogLayout.addWidget(create_accept_reject_button_box(self)) QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 61c73961c..99acadc14 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -27,6 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate, build_icon +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_SettingsDialog(object): def setupUi(self, settingsDialog): @@ -40,16 +41,9 @@ class Ui_SettingsDialog(object): self.settingsTabWidget = QtGui.QTabWidget(settingsDialog) self.settingsTabWidget.setObjectName(u'settingsTabWidget') self.settingsLayout.addWidget(self.settingsTabWidget) - self.buttonBox = QtGui.QDialogButtonBox(settingsDialog) - self.buttonBox.setStandardButtons( - QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName(u'buttonBox') + self.buttonBox = create_accept_reject_button_box(settingsDialog, True) self.settingsLayout.addWidget(self.buttonBox) self.retranslateUi(settingsDialog) - QtCore.QObject.connect(self.buttonBox, - QtCore.SIGNAL(u'accepted()'), settingsDialog.accept) - QtCore.QObject.connect(self.buttonBox, - QtCore.SIGNAL(u'rejected()'), settingsDialog.reject) QtCore.QMetaObject.connectSlotsByName(settingsDialog) def retranslateUi(self, settingsDialog): diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 93a5ef187..67f3ea120 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -406,7 +406,7 @@ class ThemeManager(QtGui.QWidget): translate('OpenLP.ThemeManager', 'Select Theme Import File'), SettingsManager.get_last_dir(self.settingsSection), translate('OpenLP.ThemeManager', 'Theme v1 (*.theme);;' - 'Theme v2 (*.otz);;All Files (*.*)')) + 'Theme v2 (*.otz);;%s (*.*)') % UiStrings.AllFiles) log.info(u'New Themes %s', unicode(files)) if files: for file in files: diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index 1ca29732b..2e8a64a9d 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate -from openlp.core.lib.ui import UiStrings, create_save_cancel_button_box, \ +from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box, \ create_delete_push_button, create_up_down_push_button_set class Ui_CustomEditDialog(object): @@ -94,7 +94,7 @@ class Ui_CustomEditDialog(object): self.creditLabel.setBuddy(self.creditEdit) self.bottomFormLayout.addRow(self.creditLabel, self.creditEdit) self.dialogLayout.addLayout(self.bottomFormLayout) - self.buttonBox = create_save_cancel_button_box(customEditDialog) + self.buttonBox = create_accept_reject_button_box(customEditDialog) self.previewButton = QtGui.QPushButton() self.buttonBox.addButton( self.previewButton, QtGui.QDialogButtonBox.ActionRole) diff --git a/openlp/plugins/custom/forms/editcustomslidedialog.py b/openlp/plugins/custom/forms/editcustomslidedialog.py index 24c40d6e6..b70e2bf5e 100644 --- a/openlp/plugins/custom/forms/editcustomslidedialog.py +++ b/openlp/plugins/custom/forms/editcustomslidedialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate, SpellTextEdit -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_CustomSlideEditDialog(object): def setupUi(self, customSlideEditDialog): @@ -37,7 +37,7 @@ class Ui_CustomSlideEditDialog(object): self.slideTextEdit = SpellTextEdit(self) self.slideTextEdit.setObjectName(u'slideTextEdit') self.dialogLayout.addWidget(self.slideTextEdit) - self.buttonBox = create_save_cancel_button_box(customSlideEditDialog) + self.buttonBox = create_accept_reject_button_box(customSlideEditDialog) self.splitButton = QtGui.QPushButton(customSlideEditDialog) self.splitButton.setObjectName(u'splitButton') self.buttonBox.addButton(self.splitButton, diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 6ec4cf264..9c739e9c1 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -64,15 +64,11 @@ class ImageMediaItem(MediaManagerItem): 'Select Image(s)') file_formats = get_images_filter() self.OnNewFileMasks = u'%s;;%s (*.*) (*)' % (file_formats, - unicode(UiStrings.AllFiles)) - self.replaceAction.setText( - translate('ImagePlugin.MediaItem', 'Replace Background')) - self.replaceAction.setToolTip( - translate('ImagePlugin.MediaItem', 'Replace Live Background')) - self.resetAction.setText( - translate('ImagePlugin.MediaItem', 'Reset Background')) - self.resetAction.setToolTip( - translate('ImagePlugin.MediaItem', 'Reset Live Background')) + UiStrings.AllFiles) + self.replaceAction.setText(UiStrings.ReplaceBG) + self.replaceAction.setToolTip(UiStrings.ReplaceLiveBG) + self.resetAction.setText(UiStrings.ResetBG) + self.resetAction.setToolTip(UiStrings.ResetLiveBG) def requiredIcons(self): MediaManagerItem.requiredIcons(self) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index f0549a960..d88c8ff14 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -31,7 +31,7 @@ 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.ui import UiStrings, critical_error_message_box log = logging.getLogger(__name__) @@ -64,16 +64,12 @@ class MediaMediaItem(MediaManagerItem): 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) diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py index 0b3f791d1..09c723364 100644 --- a/openlp/plugins/songs/forms/authorsdialog.py +++ b/openlp/plugins/songs/forms/authorsdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_AuthorsDialog(object): def setupUi(self, authorsDialog): @@ -57,7 +57,7 @@ class Ui_AuthorsDialog(object): self.authorLayout.addRow(self.displayLabel, self.displayEdit) self.dialogLayout.addLayout(self.authorLayout) self.dialogLayout.addWidget( - create_save_cancel_button_box(authorsDialog)) + create_accept_reject_button_box(authorsDialog)) self.retranslateUi(authorsDialog) authorsDialog.setMaximumHeight(authorsDialog.sizeHint().height()) QtCore.QMetaObject.connectSlotsByName(authorsDialog) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 6854a17ae..234d92283 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate -from openlp.core.lib.ui import UiStrings, create_save_cancel_button_box +from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box class Ui_EditSongDialog(object): def setupUi(self, editSongDialog): @@ -241,7 +241,7 @@ class Ui_EditSongDialog(object): self.themeTabLayout.addWidget(self.commentsGroupBox) self.songTabWidget.addTab(self.themeTab, u'') self.dialogLayout.addWidget(self.songTabWidget) - self.buttonBox = create_save_cancel_button_box(editSongDialog) + self.buttonBox = create_accept_reject_button_box(editSongDialog) self.dialogLayout.addWidget(self.buttonBox) self.retranslateUi(editSongDialog) QtCore.QMetaObject.connectSlotsByName(editSongDialog) diff --git a/openlp/plugins/songs/forms/editversedialog.py b/openlp/plugins/songs/forms/editversedialog.py index b4bc4551f..7caf782e6 100644 --- a/openlp/plugins/songs/forms/editversedialog.py +++ b/openlp/plugins/songs/forms/editversedialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, translate, SpellTextEdit -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box from openlp.plugins.songs.lib import VerseType class Ui_EditVerseDialog(object): @@ -61,7 +61,7 @@ class Ui_EditVerseDialog(object): self.verseTypeLayout.addStretch() self.dialogLayout.addLayout(self.verseTypeLayout) self.dialogLayout.addWidget( - create_save_cancel_button_box(editVerseDialog)) + create_accept_reject_button_box(editVerseDialog)) self.retranslateUi(editVerseDialog) QtCore.QMetaObject.connectSlotsByName(editVerseDialog) diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py index 89b8941b5..f6dd3930c 100644 --- a/openlp/plugins/songs/forms/songbookdialog.py +++ b/openlp/plugins/songs/forms/songbookdialog.py @@ -27,7 +27,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate -from openlp.core.lib.ui import create_save_cancel_button_box +from openlp.core.lib.ui import create_accept_reject_button_box class Ui_SongBookDialog(object): def setupUi(self, songBookDialog): @@ -51,7 +51,7 @@ class Ui_SongBookDialog(object): self.bookLayout.addRow(self.publisherLabel, self.publisherEdit) self.dialogLayout.addLayout(self.bookLayout) self.dialogLayout.addWidget( - create_save_cancel_button_box(songBookDialog)) + create_accept_reject_button_box(songBookDialog)) self.retranslateUi(songBookDialog) songBookDialog.setMaximumHeight(songBookDialog.sizeHint().height()) QtCore.QMetaObject.connectSlotsByName(songBookDialog) diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 8cd8c70fa..e85426a8f 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -460,8 +460,7 @@ class SongImportForm(OpenLPWizard): """ if filters: filters += u';;' - filters += u'%s (*)' % translate('SongsPlugin.ImportWizardForm', - 'All Files') + filters += u'%s (*)' % UiStrings.AllFiles filename = QtGui.QFileDialog.getOpenFileName(self, title, SettingsManager.get_last_dir(self.plugin.settingsSection, 1), filters) 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/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) From ae475df74b0fbec335cf6036fa6fb99e84eceb69 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 00:37:04 +0000 Subject: [PATCH 059/108] Fix import --- openlp/core/ui/exceptionform.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 69803003b..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 From 0b83c7180f3c83d0cd393b5efc3104d6fd3fa6de Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 04:06:16 +0000 Subject: [PATCH 060/108] UiStrings updates --- openlp/core/lib/mediamanageritem.py | 2 +- openlp/core/lib/ui.py | 16 +++++++++------- openlp/core/ui/advancedtab.py | 3 ++- openlp/core/ui/mainwindow.py | 5 ++--- openlp/core/ui/servicemanager.py | 6 +++--- openlp/core/ui/thememanager.py | 2 +- openlp/plugins/bibles/lib/mediaitem.py | 7 +++---- .../plugins/presentations/lib/presentationtab.py | 5 ++--- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index cc80ad564..65f6c955b 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -500,7 +500,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/ui.py b/openlp/core/lib/ui.py index 8f6f266e3..2eb74c455 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -41,16 +41,18 @@ class UiStrings(object): # These strings should need a good reason to be retranslated elsewhere. # Should some/more/less of these have an & attached? Add = translate('OpenLP.Ui', '&Add') - AddANew = unicode(translate('OpenLP.Ui', 'Add a new %s')) + AddANew = unicode(translate('OpenLP.Ui', 'Add a new %s.')) AddSelectService = unicode(translate('OpenLP.Ui', - 'Add the selected %s to the service')) + 'Add the selected %s to the service.')) + Advanced = translate('OpenLP.Ui', 'Advanced') AllFiles = translate('OpenLP.Ui', 'All Files') Authors = translate('OpenLP.Ui', 'Authors') + CreateANew = unicode(translate('OpenLP.Ui', 'Create a new %s.')) Delete = translate('OpenLP.Ui', '&Delete') - DeleteSelect = unicode(translate('OpenLP.Ui', 'Delete the selected %s')) + DeleteSelect = unicode(translate('OpenLP.Ui', 'Delete the selected %s.')) DeleteType = unicode(translate('OpenLP.Ui', 'Delete %s')) Edit = translate('OpenLP.Ui', '&Edit') - EditSelect = unicode(translate('OpenLP.Ui', 'Edit the selected %s')) + EditSelect = unicode(translate('OpenLP.Ui', 'Edit the selected %s.')) EditType = unicode(translate('OpenLP.Ui', 'Edit %s')) Error = translate('OpenLP.Ui', 'Error') ExportType = unicode(translate('OpenLP.Ui', 'Export %s')) @@ -58,18 +60,18 @@ class UiStrings(object): ImportType = unicode(translate('OpenLP.Ui', 'Import %s')) Live = translate('OpenLP.Ui', 'Live') Load = translate('OpenLP.Ui', 'Load') - LoadANew = unicode(translate('OpenLP.Ui', 'Load a new %s')) + LoadANew = unicode(translate('OpenLP.Ui', 'Load a new %s.')) New = translate('OpenLP.Ui', 'New') NewType = unicode(translate('OpenLP.Ui', 'New %s')) OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') Preview = translate('OpenLP.Ui', 'Preview') - PreviewSelect = unicode(translate('OpenLP.Ui', 'Preview the selected %s')) + PreviewSelect = unicode(translate('OpenLP.Ui', 'Preview the selected %s.')) 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') SendSelectLive = unicode(translate('OpenLP.Ui', - 'Send the selected %s live')) + 'Send the selected %s live.')) Service = translate('OpenLP.Ui', 'Service') Theme = translate('OpenLP.Ui', 'Theme') Themes = translate('OpenLP.Ui', 'Themes') diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 58b637bc2..918335b2e 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -29,6 +29,7 @@ The :mod:`advancedtab` provides an advanced settings facility. from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, translate +from openlp.core.lib.ui import UiStrings class AdvancedTab(SettingsTab): """ @@ -112,7 +113,7 @@ class AdvancedTab(SettingsTab): """ Setup the interface translation strings. """ - self.tabTitleVisible = translate('OpenLP.AdvancedTab', 'Advanced') + self.tabTitleVisible = UiStrings.Advanced self.uiGroupBox.setTitle(translate('OpenLP.AdvancedTab', 'UI Settings')) self.recentLabel.setText( translate('OpenLP.AdvancedTab', diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index c87a9544a..c329abb7d 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -315,10 +315,9 @@ class Ui_MainWindow(object): self.themeManagerDock.setWindowTitle( translate('OpenLP.MainWindow', 'Theme Manager')) self.FileNewItem.setText(translate('OpenLP.MainWindow', '&New')) - self.FileNewItem.setToolTip( - translate('OpenLP.MainWindow', 'New Service')) + self.FileNewItem.setToolTip(UiStrings.NewType % UiStrings.Service) self.FileNewItem.setStatusTip( - translate('OpenLP.MainWindow', 'Create a new service.')) + UiStrings.CreateANew % UiStrings.Service.toLower()) self.FileNewItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+N')) self.FileOpenItem.setText(translate('OpenLP.MainWindow', '&Open')) self.FileOpenItem.setToolTip( diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 83d7cbc3c..cebb119ed 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -35,7 +35,7 @@ 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.lib.ui import UiStrings, critical_error_message_box from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm from openlp.core.ui.printserviceorderform import PrintServiceOrderForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ @@ -95,9 +95,9 @@ class ServiceManager(QtGui.QWidget): # Create the top toolbar self.toolbar = OpenLPToolbar(self) self.toolbar.addToolbarButton( - translate('OpenLP.ServiceManager', 'New Service'), + UiStrings.NewType % UiStrings.Service, u':/general/general_new.png', - translate('OpenLP.ServiceManager', 'Create a new service'), + UiStrings.CreateANew % UiStrings.Service.toLower(), self.onNewServiceClicked) self.toolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Open Service'), diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 67f3ea120..d3f8da56c 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -64,7 +64,7 @@ class ThemeManager(QtGui.QWidget): self.toolbar.addToolbarButton( UiStrings.NewType % UiStrings.Theme, u':/themes/theme_new.png', - translate('OpenLP.ThemeManager', 'Create a new theme.'), + UiStrings.CreateANew % UiStrings.Theme.toLower(), self.onAddTheme) self.toolbar.addToolbarButton( UiStrings.EditType % UiStrings.Theme, diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index c162447b2..788eaea3c 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -30,8 +30,8 @@ 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.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 @@ -192,8 +192,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 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')) From 76fd92b01a02799abc35e89dfe164c40f7520906 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 04:19:38 +0000 Subject: [PATCH 061/108] Handle Impress errors better --- .../presentations/lib/impresscontroller.py | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 7de095d54..38058f2c5 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -145,7 +145,12 @@ class ImpressController(PresentationController): log.debug(u'get COM Desktop OpenOffice') if not self.manager: return None - return self.manager.createInstance(u'com.sun.star.frame.Desktop') + desktop = None + try: + desktop = self.manager.createInstance(u'com.sun.star.frame.Desktop') + except AttributeError: + log.exception(u'Failure to find desktop - Impress may have closed') + return desktop if desktop else None def get_com_servicemanager(self): """ @@ -166,14 +171,17 @@ 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') - return + finally: + if not desktop: + return docs = desktop.getComponents() if docs.hasElements(): log.debug(u'OpenOffice not terminated as docs are still open') From 8abfd2ce14384a4f9e86112a26338f18fe897c80 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Thu, 10 Feb 2011 07:25:08 +0200 Subject: [PATCH 062/108] Fix docstrings and update documentation. --- documentation/api/source/openlp.rst | 7 - documentation/api/source/plugins/bibles.rst | 2 +- openlp/core/ui/servicemanager.py | 2 +- openlp/plugins/bibles/lib/__init__.py | 134 ++++++++++++-------- openlp/plugins/songs/lib/songimport.py | 4 +- openlp/plugins/songs/lib/xml.py | 43 ++++--- 6 files changed, 105 insertions(+), 87 deletions(-) delete mode 100644 documentation/api/source/openlp.rst 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/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 4d36f4aec..eb08be377 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -461,7 +461,7 @@ 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, diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index e6fff1cc8..496535461 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -62,69 +62,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 +215,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 +228,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/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/xml.py b/openlp/plugins/songs/lib/xml.py index 91bfdb025..4bf11887a 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:: * tag. + Add a verse to the ```` tag. ``type`` A string denoting the type of verse. Possible values are "V", @@ -158,59 +158,60 @@ class OpenLyrics(object): to/from a song. As OpenLyrics has a rich set of different features, we cannot support them - all. The following features are supported by the :class:`OpenLyrics`:: + all. The following features are supported by the :class:`OpenLyrics` class: - ** + ```` 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. - ** + ```` OpenLP supports this property. + """ def __init__(self, manager): self.manager = manager From e52cae13fd38e87a68dcfee357369a4c02761cd1 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Thu, 10 Feb 2011 08:36:41 +0200 Subject: [PATCH 063/108] Some conditional configuration. --- documentation/manual/source/conf.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/documentation/manual/source/conf.py b/documentation/manual/source/conf.py index 70b4fb2f6..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,19 +92,21 @@ 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 = '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 = [u'../themes'] From abc33c48111d9a1ab3bd49d8d1d3e5928e3d712b Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 10 Feb 2011 18:42:13 +0100 Subject: [PATCH 064/108] do not add empty 'entry' property --- openlp/plugins/songs/lib/xml.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 73ac8120c..b96e79961 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -263,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: From e4d317306cdb598f855c07422e5e65ff2040377b Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 18:38:03 +0000 Subject: [PATCH 065/108] Refactor ListViews --- openlp/core/lib/__init__.py | 2 +- openlp/core/lib/baselistwithdnd.py | 11 ++++++----- openlp/core/lib/mediamanageritem.py | 9 ++------- openlp/plugins/bibles/lib/mediaitem.py | 14 ++------------ openlp/plugins/custom/lib/mediaitem.py | 12 ++---------- openlp/plugins/images/lib/mediaitem.py | 16 +++------------- openlp/plugins/media/lib/mediaitem.py | 13 ++----------- openlp/plugins/presentations/lib/mediaitem.py | 18 ++---------------- openlp/plugins/songs/lib/mediaitem.py | 11 ++--------- 9 files changed, 22 insertions(+), 84 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 33280f83b..644f0a4c6 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -319,6 +319,7 @@ def check_directory_exists(dir): if not os.path.exists(dir): os.makedirs(dir) +from baselistwithdnd import BaseListWithDnD from theme import ThemeLevel, ThemeXML, BackgroundGradientType, \ BackgroundType, HorizontalType, VerticalType from displaytags import DisplayTags @@ -339,4 +340,3 @@ from dockwidget import OpenLPDockWidget from renderer import Renderer from rendermanager import RenderManager from mediamanageritem import MediaManagerItem -from baselistwithdnd import BaseListWithDnD diff --git a/openlp/core/lib/baselistwithdnd.py b/openlp/core/lib/baselistwithdnd.py index 86535f6e7..3bc91d577 100644 --- a/openlp/core/lib/baselistwithdnd.py +++ b/openlp/core/lib/baselistwithdnd.py @@ -32,13 +32,13 @@ class BaseListWithDnD(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 65f6c955b..26e51737d 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, BaseListWithDnD 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 @@ -263,7 +258,7 @@ class MediaManagerItem(QtGui.QWidget): Creates the main widget for listing items the media item is tracking """ # Add the List widget - self.listView = self.ListViewWithDnD_class(self) + self.listView = BaseListWithDnD(self, self.title) self.listView.uniformItemSizes = True self.listView.setSpacing(1) self.listView.setSelectionMode( diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 788eaea3c..58dc11a6a 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -28,8 +28,8 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import MediaManagerItem, Receiver, BaseListWithDnD, \ - ItemCapabilities, translate +from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ + translate 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 @@ -37,15 +37,6 @@ from openlp.plugins.bibles.lib import get_reference_match log = logging.getLogger(__name__) -class BibleListView(BaseListWithDnD): - """ - Custom list view descendant, required for drag and drop. - """ - def __init__(self, parent=None): - self.PluginName = u'Bibles' - BaseListWithDnD.__init__(self, parent) - - class BibleMediaItem(MediaManagerItem): """ This is the custom media manager item for Bibles. @@ -54,7 +45,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 = {} 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/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 9c739e9c1..5f95a239c 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -29,21 +29,14 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ - ItemCapabilities, SettingsManager, translate, check_item_selected, \ - check_directory_exists, Receiver +from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \ + SettingsManager, translate, check_item_selected, check_directory_exists, \ + Receiver from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.utils import AppLocation, delete_file, get_images_filter log = logging.getLogger(__name__) -# We have to explicitly create separate classes for each plugin -# in order for DnD to the Service manager to work correctly. -class ImageListView(BaseListWithDnD): - def __init__(self, parent=None): - self.PluginName = u'Images' - BaseListWithDnD.__init__(self, parent) - class ImageMediaItem(MediaManagerItem): """ This is the custom media manager item for images. @@ -52,9 +45,6 @@ class ImageMediaItem(MediaManagerItem): def __init__(self, parent, plugin, icon): self.IconPath = u'images/image' - # This next is a class, not an instance of a class - it will - # be instanced by the base MediaManagerItem. - self.ListViewWithDnD_class = ImageListView MediaManagerItem.__init__(self, parent, self, icon) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index d88c8ff14..800d6d051 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -29,18 +29,12 @@ 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 import MediaManagerItem, build_icon, ItemCapabilities, \ + SettingsManager, translate, check_item_selected, Receiver from openlp.core.lib.ui import UiStrings, critical_error_message_box 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,9 +44,6 @@ 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) diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 09020692a..0b6ab39cd 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -29,24 +29,13 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ - SettingsManager, translate, check_item_selected, Receiver, ItemCapabilities +from openlp.core.lib import MediaManagerItem, build_icon, SettingsManager, \ + translate, check_item_selected, Receiver, ItemCapabilities from openlp.core.lib.ui import critical_error_message_box, media_item_combo_box from openlp.plugins.presentations.lib import MessageListener log = logging.getLogger(__name__) -class PresentationListView(BaseListWithDnD): - """ - Class for the list of Presentations - - We have to explicitly create separate classes for each plugin - in order for DnD to the Service manager to work correctly. - """ - def __init__(self, parent=None): - self.PluginName = u'Presentations' - BaseListWithDnD.__init__(self, parent) - class PresentationMediaItem(MediaManagerItem): """ This is the Presentation media manager item for Presentation Items. @@ -61,9 +50,6 @@ class PresentationMediaItem(MediaManagerItem): self.controllers = controllers self.IconPath = u'presentations/presentation' self.Automatic = u'' - # this next is a class, not an instance of a class - it will - # be instanced by the base MediaManagerItem - self.ListViewWithDnD_class = PresentationListView MediaManagerItem.__init__(self, parent, self, icon) self.message_listener = MessageListener(self) QtCore.QObject.connect(Receiver.get_receiver(), diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 312d0d6e7..cabb181fb 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -31,8 +31,8 @@ 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 @@ -42,12 +42,6 @@ from openlp.core.lib.searchedit import SearchEdit log = logging.getLogger(__name__) -class SongListView(BaseListWithDnD): - def __init__(self, parent=None): - self.PluginName = u'Songs' - BaseListWithDnD.__init__(self, parent) - - class SongMediaItem(MediaManagerItem): """ This is the custom media manager item for Songs. @@ -56,7 +50,6 @@ class SongMediaItem(MediaManagerItem): def __init__(self, parent, plugin, icon): self.IconPath = u'songs/song' - self.ListViewWithDnD_class = SongListView MediaManagerItem.__init__(self, parent, self, icon) self.edit_song_form = EditSongForm(self, self.parent.manager) self.openLyrics = OpenLyrics(self.parent.manager) From 552e412769429e3c4b5c1a8ce36700a34e5a67a0 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 18:59:59 +0000 Subject: [PATCH 066/108] Cleanups --- openlp/core/ui/filerenameform.py | 2 +- openlp/core/ui/mainwindow.py | 4 ++-- openlp/plugins/presentations/lib/impresscontroller.py | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/openlp/core/ui/filerenameform.py b/openlp/core/ui/filerenameform.py index 60abe77da..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 diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 4b99cc32b..1f2a05b95 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -216,8 +216,8 @@ class Ui_MainWindow(object): self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', u':/tools/tools_add.png') mainWindow.actionList.add_action(self.ToolsAddToolItem, u'Tools') - self.ToolsOpenDataFolder = icon_action(mainWindow, u'ToolsOpenDataFolder', - u':/general/general_open.png') + 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') diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 38058f2c5..ad5faa2dd 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -179,9 +179,8 @@ class ImpressController(PresentationController): desktop = self.get_com_desktop() except: log.exception(u'Failed to find an OpenOffice desktop to terminate') - finally: - if not desktop: - return + if not desktop: + return docs = desktop.getComponents() if docs.hasElements(): log.debug(u'OpenOffice not terminated as docs are still open') From 281b24b8152cd6b33d5a6ce5ed4fb82b9110b3e9 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 19:05:35 +0000 Subject: [PATCH 067/108] Rename list widget descendant --- documentation/api/source/core/lib.rst | 12 ++++++------ openlp/core/lib/__init__.py | 2 +- .../lib/{baselistwithdnd.py => listwidgetwithdnd.py} | 2 +- openlp/core/lib/mediamanageritem.py | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) rename openlp/core/lib/{baselistwithdnd.py => listwidgetwithdnd.py} (98%) diff --git a/documentation/api/source/core/lib.rst b/documentation/api/source/core/lib.rst index 6ca952d7d..fa894875d 100644 --- a/documentation/api/source/core/lib.rst +++ b/documentation/api/source/core/lib.rst @@ -6,18 +6,18 @@ Object Library .. automodule:: openlp.core.lib :members: -:mod:`BaseListWithDnD` ----------------------- - -.. autoclass:: openlp.core.lib.baselistwithdnd.BaseListWithDnD - :members: - :mod:`EventReceiver` -------------------- .. autoclass:: openlp.core.lib.eventreceiver.EventReceiver :members: +:mod:`ListWidgetWithDnD` +---------------------- + +.. autoclass:: openlp.core.lib.listwidgetwithdnd.ListWidgetWithDnD + :members: + :mod:`MediaManagerItem` ----------------------- diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 644f0a4c6..5247ae938 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -319,7 +319,7 @@ def check_directory_exists(dir): if not os.path.exists(dir): os.makedirs(dir) -from baselistwithdnd import BaseListWithDnD +from listwidgetwithdnd import ListWidgetWithDnD from theme import ThemeLevel, ThemeXML, BackgroundGradientType, \ BackgroundType, HorizontalType, VerticalType from displaytags import DisplayTags diff --git a/openlp/core/lib/baselistwithdnd.py b/openlp/core/lib/listwidgetwithdnd.py similarity index 98% rename from openlp/core/lib/baselistwithdnd.py rename to openlp/core/lib/listwidgetwithdnd.py index 3bc91d577..a1a11217b 100644 --- a/openlp/core/lib/baselistwithdnd.py +++ b/openlp/core/lib/listwidgetwithdnd.py @@ -28,7 +28,7 @@ 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 """ diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 26e51737d..06e80b7ca 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, BaseListWithDnD + translate, Receiver, ListWidgetWithDnD log = logging.getLogger(__name__) @@ -258,7 +258,7 @@ class MediaManagerItem(QtGui.QWidget): Creates the main widget for listing items the media item is tracking """ # Add the List widget - self.listView = BaseListWithDnD(self, self.title) + self.listView = ListWidgetWithDnD(self, self.title) self.listView.uniformItemSizes = True self.listView.setSpacing(1) self.listView.setSelectionMode( From 565289cf7ea3bb337b7a130dbb27ab9ef121c970 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 19:28:17 +0000 Subject: [PATCH 068/108] Small wizard refactor --- openlp/core/ui/wizard.py | 6 ++++++ openlp/plugins/songs/forms/songexportform.py | 9 ++------- openlp/plugins/songs/forms/songimportform.py | 6 ------ 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 2fa448db8..1ec348c1d 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -70,6 +70,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 diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 5d5b9a8b1..849a1ad1e 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -87,7 +87,8 @@ class SongExportForm(OpenLPWizard): Song wizard specific signals. """ QtCore.QObject.connect(self.availableListWidget, - QtCore.SIGNAL(u'itemActivated(QListWidgetItem*)'), self.onItemPressed) + QtCore.SIGNAL(u'itemActivated(QListWidgetItem*)'), + self.onItemPressed) QtCore.QObject.connect(self.searchLineEdit, QtCore.SIGNAL(u'textEdited(const QString&)'), self.onSearchLineEditChanged) @@ -241,12 +242,6 @@ class SongExportForm(OpenLPWizard): self.selectedListWidget.clear() return True - def registerFields(self): - """ - Register song export wizard fields. - """ - pass - def setDefaults(self): """ Set default form values for the song export wizard. diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index e85426a8f..2ecf35429 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -671,12 +671,6 @@ class SongImportForm(OpenLPWizard): """ self.removeSelectedItems(self.songBeamerFileListWidget) - def registerFields(self): - """ - Register song import wizard fields. - """ - pass - def setDefaults(self): """ Set default form values for the song import wizard. From 79d93c515a48a443174f1e7ce24036568f21e83d Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 19:37:02 +0000 Subject: [PATCH 069/108] Another wizard refactor --- openlp/core/ui/wizard.py | 27 +++++++++++++++++++ .../plugins/bibles/forms/bibleimportform.py | 27 ------------------- openlp/plugins/songs/forms/songimportform.py | 27 ------------------- 3 files changed, 27 insertions(+), 54 deletions(-) diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 1ec348c1d..071972df5 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -152,3 +152,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/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index f528a4b1b..890c9a3a5 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -727,33 +727,6 @@ class BibleImportForm(OpenLPWizard): if books_file: books_file.close() - def getFileName(self, title, editbox, filters=u''): - """ - Opens a QFileDialog and saves the filename to the given editbox. - - ``title`` - The title of the dialog (unicode). - - ``editbox`` - A editbox (QLineEdit). - - ``filters`` - The file extension filters. It should contain the file description - as well as the file extension. For example:: - - u'openlp.org 1.x bible (*.bible)' - """ - if filters: - filters += u';;' - filters += u'%s (*)' % UiStrings.AllFiles - filename = QtGui.QFileDialog.getOpenFileName(self, title, - os.path.dirname(SettingsManager.get_last_dir( - self.plugin.settingsSection, 1)), filters) - if filename: - editbox.setText(filename) - SettingsManager.set_last_dir( - self.plugin.settingsSection, filename, 1) - def preWizard(self): """ Prepare the UI for the import. diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 2ecf35429..b84af8dde 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -442,33 +442,6 @@ class SongImportForm(OpenLPWizard): 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 (*)' % UiStrings.AllFiles - 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. From a7f8d5ca847c68a032d7a4c4914896be99fd0f1f Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 21:07:28 +0000 Subject: [PATCH 070/108] Fixes --- openlp/core/ui/wizard.py | 5 +++-- openlp/plugins/bibles/forms/bibleimportform.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 071972df5..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__) diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index 890c9a3a5..463c838c9 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -33,9 +33,9 @@ import os.path from PyQt4 import QtCore, QtGui -from openlp.core.lib import Receiver, SettingsManager, translate +from openlp.core.lib import Receiver, translate from openlp.core.lib.db import delete_database -from openlp.core.lib.ui import UiStrings, critical_error_message_box +from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard from openlp.core.utils import AppLocation, string_is_unicode from openlp.plugins.bibles.lib.manager import BibleFormat From 12607020be3778c26d447ae651e80f8ebd081e17 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 22:49:30 +0000 Subject: [PATCH 071/108] Cleanup magic numbers and more UiStrings --- openlp/core/lib/htmlbuilder.py | 18 ++++-------------- openlp/core/lib/theme.py | 27 +++++++++++++++++++++++++++ openlp/core/lib/ui.py | 2 ++ openlp/core/ui/mainwindow.py | 6 ++---- openlp/core/ui/servicemanager.py | 6 +++--- openlp/core/ui/thememanager.py | 9 +++++---- 6 files changed, 43 insertions(+), 25 deletions(-) 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/theme.py b/openlp/core/lib/theme.py index dca226069..1fcb623fb 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -164,6 +164,7 @@ class BackgroundGradientType(object): elif type_string == u'leftBottom': return BackgroundGradientType.LeftBottom + class HorizontalType(object): """ Type enumeration for horizontal alignment. @@ -172,6 +173,19 @@ class HorizontalType(object): Center = 1 Right = 2 + @staticmethod + def to_string(horizontal_type): + """ + Return a string representation of a horizontal type. + """ + If horizontal_type == Horizontal.Right: + return u'right' + elif horizontal_type == Horizontal.Center: + return u'center' + else + return u'left' + + class VerticalType(object): """ Type enumeration for vertical alignment. @@ -180,6 +194,19 @@ class VerticalType(object): Middle = 1 Bottom = 2 + @staticmethod + def to_string(vertical_type): + """ + Return a string representation of a vertical type. + """ + If vertical_type == VerticalType.Bottom: + return u'bottom' + elif vertical_type == VerticalType.Middle: + return u'middle' + else + return u'top' + + BOOLEAN_LIST = [u'bold', u'italics', u'override', u'outline', u'shadow', u'slide_transition'] diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 2eb74c455..a3b442801 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -64,12 +64,14 @@ class UiStrings(object): New = translate('OpenLP.Ui', 'New') NewType = unicode(translate('OpenLP.Ui', 'New %s')) OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') + OpenType = unicode(translate('OpenLP.Ui', 'Open %s')) Preview = translate('OpenLP.Ui', 'Preview') PreviewSelect = unicode(translate('OpenLP.Ui', 'Preview the selected %s.')) 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') + SaveType = unicode(translate('OpenLP.Ui', 'Save %s')) SendSelectLive = unicode(translate('OpenLP.Ui', 'Send the selected %s live.')) Service = translate('OpenLP.Ui', 'Service') diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 1f2a05b95..4840ef5da 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -324,14 +324,12 @@ class Ui_MainWindow(object): UiStrings.CreateANew % UiStrings.Service.toLower()) 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.OpenType % UiStrings.Service) 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.SaveType % UiStrings.Service) self.FileSaveItem.setStatusTip( translate('OpenLP.MainWindow', 'Save the current service to disk.')) self.FileSaveItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+S')) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 5fc80b175..744da7b46 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -100,12 +100,12 @@ class ServiceManager(QtGui.QWidget): UiStrings.CreateANew % UiStrings.Service.toLower(), self.onNewServiceClicked) self.toolbar.addToolbarButton( - translate('OpenLP.ServiceManager', 'Open Service'), + UiStrings.OpenType % UiStrings.Service, u':/general/general_open.png', translate('OpenLP.ServiceManager', 'Load an existing service'), self.onLoadServiceClicked) self.toolbar.addToolbarButton( - translate('OpenLP.ServiceManager', 'Save Service'), + UiStrings.SaveType % UiStrings.Service, u':/general/general_save.png', translate('OpenLP.ServiceManager', 'Save this service'), self.saveFile) @@ -465,7 +465,7 @@ class ServiceManager(QtGui.QWidget): save the file. """ fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow, - translate('OpenLP.ServiceManager', 'Save Service'), + UiStrings.SaveType % UiStrings.Service, SettingsManager.get_last_dir( self.mainwindow.serviceSettingsSection), translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)'))) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index d3f8da56c..36abb19c1 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -34,7 +34,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import OpenLPToolbar, ThemeXML, get_text_file_string, \ build_icon, Receiver, SettingsManager, translate, check_item_selected, \ - BackgroundType, BackgroundGradientType, check_directory_exists + BackgroundType, BackgroundGradientType, check_directory_exists, \ + VerticalType from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.theme import Theme from openlp.core.ui import FileRenameForm, ThemeForm @@ -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() From ae2b625d4f5b243a162f03b5758df72929b88bae Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Thu, 10 Feb 2011 23:00:15 +0000 Subject: [PATCH 072/108] Typos --- openlp/core/lib/theme.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 1fcb623fb..06340c2eb 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -178,11 +178,11 @@ class HorizontalType(object): """ Return a string representation of a horizontal type. """ - If horizontal_type == Horizontal.Right: + if horizontal_type == Horizontal.Right: return u'right' elif horizontal_type == Horizontal.Center: return u'center' - else + else: return u'left' @@ -199,11 +199,11 @@ class VerticalType(object): """ Return a string representation of a vertical type. """ - If vertical_type == VerticalType.Bottom: + if vertical_type == VerticalType.Bottom: return u'bottom' elif vertical_type == VerticalType.Middle: return u'middle' - else + else: return u'top' From 12d08676236ec66b5637809f2fc79317e0c26e47 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Fri, 11 Feb 2011 04:04:05 +0000 Subject: [PATCH 073/108] Plugin refactor: icons, tabs, mediaitems --- openlp/core/lib/mediamanageritem.py | 62 ++++++++-------------- openlp/core/lib/plugin.py | 23 ++++++-- openlp/core/lib/rendermanager.py | 3 +- openlp/core/ui/maindisplay.py | 1 - openlp/core/ui/slidecontroller.py | 1 - openlp/plugins/alerts/alertsplugin.py | 13 ++--- openlp/plugins/alerts/lib/alertsmanager.py | 4 +- openlp/plugins/alerts/lib/alertstab.py | 6 +-- openlp/plugins/bibles/bibleplugin.py | 11 +--- openlp/plugins/custom/customplugin.py | 11 +--- openlp/plugins/images/imageplugin.py | 7 +-- openlp/plugins/media/lib/mediaitem.py | 1 + openlp/plugins/media/lib/mediatab.py | 9 ++-- openlp/plugins/media/mediaplugin.py | 10 +--- openlp/plugins/remotes/remoteplugin.py | 10 +--- openlp/plugins/songs/songsplugin.py | 14 +---- 16 files changed, 66 insertions(+), 120 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 06e80b7ca..f74ba63a9 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -197,61 +197,43 @@ 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): """ diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index cfd3fb4c2..a073d31ea 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -114,7 +114,8 @@ class Plugin(QtCore.QObject): """ log.info(u'loaded') - def __init__(self, name, version=None, pluginHelpers=None): + def __init__(self, name, version=None, pluginHelpers=None, + mediaItemClass=None, settingsTabClass=None): """ This is the constructor for the plugin object. This provides an easy way for descendent plugins to populate common data. This method *must* @@ -132,6 +133,12 @@ class Plugin(QtCore.QObject): ``pluginHelpers`` Defaults to *None*. A list of helper objects. + + ``mediaItemClass`` + The class name of the plugin's media item. + + ``settingsTabClass`` + The class name of the plugin's settings tab. """ QtCore.QObject.__init__(self) self.name = name @@ -141,6 +148,8 @@ class Plugin(QtCore.QObject): self.version = version self.settingsSection = self.name.lower() self.icon = None + self.mediaItemClass = mediaItemClass + self.settingsTabClass = settingsTabClass self.weight = 0 self.status = PluginStatus.Inactive # Set up logging @@ -199,7 +208,9 @@ class Plugin(QtCore.QObject): Construct a MediaManagerItem object with all the buttons and things you need, and return it for integration into openlp.org. """ - pass + if self.mediaItemClass: + return self.mediaItemClass(self, self, self.icon) + return None def addImportMenuItem(self, importMenu): """ @@ -230,9 +241,13 @@ class Plugin(QtCore.QObject): def getSettingsTab(self): """ - Create a tab for the settings window. + Create a tab for the settings window to display the configurable + options for this plugin to the user. """ - pass + if self.settingsTabClass: + return self.settingsTabClass(self.name, + self.getString(StringContent.VisibleName)[u'title']) + return None def addToMenu(self, menubar): """ diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py index 5896ca4e6..32a29915f 100644 --- a/openlp/core/lib/rendermanager.py +++ b/openlp/core/lib/rendermanager.py @@ -68,7 +68,6 @@ class RenderManager(object): self.theme_level = u'' self.override_background = None self.theme_data = None - self.alertTab = None self.force_page = False def update_display(self): @@ -261,4 +260,4 @@ class RenderManager(object): log.debug(u'calculate default %d, %d, %f', self.width, self.height, self.screen_ratio ) # 90% is start of footer - self.footer_start = int(self.height * 0.90) \ No newline at end of file + self.footer_start = int(self.height * 0.90) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index c4ab75aac..05a301bd7 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -65,7 +65,6 @@ class MainDisplay(DisplayWidget): self.parent = parent self.screens = screens self.isLive = live - self.alertTab = None self.hideMode = None self.override = {} mainIcon = build_icon(u':/icon/openlp-logo-16x16.png') diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index c6b58ac56..fc6cc04fa 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -415,7 +415,6 @@ class SlideController(QtGui.QWidget): # rebuild display as screen size changed self.display = MainDisplay(self, self.screens, self.isLive) self.display.imageManager = self.parent.renderManager.image_manager - self.display.alertTab = self.alertTab self.display.setup() if self.isLive: self.__addActionsToWidget(self.display) diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index 443ec1e84..136d775a5 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -40,21 +40,14 @@ class AlertsPlugin(Plugin): log.info(u'Alerts Plugin loaded') def __init__(self, plugin_helpers): - Plugin.__init__(self, u'Alerts', u'1.9.4', plugin_helpers) + Plugin.__init__(self, u'Alerts', u'1.9.4', plugin_helpers, + settingsTabClass=AlertsTab) self.weight = -3 self.icon = build_icon(u':/plugins/plugin_alerts.png') self.alertsmanager = AlertsManager(self) self.manager = Manager(u'alerts', init_schema) - self.visible_name = self.getString(StringContent.VisibleName) self.alertForm = AlertForm(self) - def getSettingsTab(self): - """ - Return the settings tab for the Alerts plugin - """ - self.alertsTab = AlertsTab(self, self.visible_name[u'title']) - return self.alertsTab - def addToolsMenuItem(self, tools_menu): """ Give the alerts plugin the opportunity to add items to the @@ -81,7 +74,7 @@ class AlertsPlugin(Plugin): log.info(u'Alerts Initialising') Plugin.initialise(self) self.toolsAlertItem.setVisible(True) - self.liveController.alertTab = self.alertsTab + self.liveController.alertTab = self.settings_tab def finalise(self): """ diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index 6fe0ae132..f69099bf1 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -84,7 +84,7 @@ class AlertsManager(QtCore.QObject): if len(self.alertList) == 0: return text = self.alertList.pop(0) - alertTab = self.parent.alertsTab + alertTab = self.parent.settings_tab self.parent.liveController.display.alert(text) # Check to see if we have a timer running. if self.timer_id == 0: @@ -103,4 +103,4 @@ class AlertsManager(QtCore.QObject): self.parent.liveController.display.alert(u'') self.killTimer(self.timer_id) self.timer_id = 0 - self.generateAlert() \ No newline at end of file + self.generateAlert() diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index 4090503db..48a4527ed 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -33,10 +33,8 @@ class AlertsTab(SettingsTab): """ AlertsTab is the alerts settings tab in the settings dialog. """ - def __init__(self, parent, visible_title): - self.parent = parent - self.manager = parent.manager - SettingsTab.__init__(self, parent.name, visible_title) + def __init__(self, name, visible_title): + SettingsTab.__init__(self, name, visible_title) def setupUi(self): self.setObjectName(u'AlertsTab') diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 89102a8eb..e3447cfdd 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -38,7 +38,8 @@ class BiblePlugin(Plugin): log.info(u'Bible Plugin loaded') def __init__(self, plugin_helpers): - Plugin.__init__(self, u'Bibles', u'1.9.4', plugin_helpers) + Plugin.__init__(self, u'Bibles', u'1.9.4', plugin_helpers, + BibleMediaItem, BiblesTab) self.weight = -9 self.icon_path = u':/plugins/plugin_bibles.png' self.icon = build_icon(self.icon_path) @@ -62,14 +63,6 @@ class BiblePlugin(Plugin): self.importBibleItem.setVisible(False) self.exportBibleItem.setVisible(False) - def getSettingsTab(self): - visible_name = self.getString(StringContent.VisibleName) - return BiblesTab(self.name, visible_name[u'title']) - - def getMediaManagerItem(self): - # Create the BibleManagerItem object. - return BibleMediaItem(self, self, self.icon) - def addImportMenuItem(self, import_menu): self.importBibleItem = QtGui.QAction(import_menu) self.importBibleItem.setObjectName(u'importBibleItem') diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index f20fbc9cd..92546cd4f 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -48,21 +48,14 @@ class CustomPlugin(Plugin): log.info(u'Custom Plugin loaded') def __init__(self, plugin_helpers): - Plugin.__init__(self, u'Custom', u'1.9.4', plugin_helpers) + Plugin.__init__(self, u'Custom', u'1.9.4', plugin_helpers, + CustomMediaItem, CustomTab) self.weight = -5 self.manager = Manager(u'custom', init_schema) self.edit_custom_form = EditCustomForm(self.manager) self.icon_path = u':/plugins/plugin_custom.png' self.icon = build_icon(self.icon_path) - def getSettingsTab(self): - visible_name = self.getString(StringContent.VisibleName) - return CustomTab(self.name, visible_name[u'title']) - - def getMediaManagerItem(self): - # Create the ManagerItem object - return CustomMediaItem(self, self, self.icon) - def about(self): about_text = translate('CustomPlugin', 'Custom Plugin' '
The custom plugin provides the ability to set up custom ' diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 2642e3055..6b64598fc 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 ' diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 800d6d051..7fb0ed0c1 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -138,6 +138,7 @@ class MediaMediaItem(MediaManagerItem): return False def initialise(self): + self.listView.clear() self.listView.setIconSize(QtCore.QSize(88, 50)) self.loadList(SettingsManager.load_list(self.settingsSection, self.settingsSection)) 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 3ebb77171..b9db9b8c1 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.') 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/songsplugin.py b/openlp/plugins/songs/songsplugin.py index ef20d15ae..646e8e86e 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 From 4a4f7d7ef477d846012a3397fa8b992834247199 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 11 Feb 2011 17:28:14 +0000 Subject: [PATCH 074/108] Fix crash on blank of single display screen Fixes: https://launchpad.net/bugs/716843 --- openlp/core/ui/slidecontroller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index fc6cc04fa..cd43cddb4 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -876,7 +876,7 @@ class SlideController(QtGui.QWidget): using *Blank to Theme*. """ log.debug(u'updatePreview %s ' % self.screens.current[u'primary']) - if not self.screens.current[u'primary'] and \ + if not self.screens.current[u'primary'] and self.serviceItem and \ self.serviceItem.is_capable(ItemCapabilities.ProvidesOwnDisplay): # Grab now, but try again in a couple of seconds if slide change # is slow From dd620456788e7cdb25e0f24ae205e18eb8940f7e Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Fri, 11 Feb 2011 17:50:34 +0000 Subject: [PATCH 075/108] Fix Windows data dir --- openlp/core/utils/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 2862afef4..161b55897 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -165,6 +165,8 @@ def _get_os_dir_path(dir_type): Return a path based on which OS and environment we are running in. """ if sys.platform == u'win32': + if dir_type == AppLocation.DataDir: + return os.path.join(os.getenv(u'APPDATA'), u'openlp', u'data') return os.path.join(os.getenv(u'APPDATA'), u'openlp') elif sys.platform == u'darwin': if dir_type == AppLocation.DataDir: @@ -180,8 +182,7 @@ def _get_os_dir_path(dir_type): return os.path.join(BaseDirectory.xdg_data_home, u'openlp') elif dir_type == AppLocation.CacheDir: return os.path.join(BaseDirectory.xdg_cache_home, u'openlp') - else: - return os.path.join(os.getenv(u'HOME'), u'.openlp') + return os.path.join(os.getenv(u'HOME'), u'.openlp') def _get_frozen_path(frozen_option, non_frozen_option): """ @@ -189,8 +190,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): """ From 98a28d85801195d0630c05ee08a3431468579f73 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Fri, 11 Feb 2011 17:55:41 +0000 Subject: [PATCH 076/108] Fix the other data dirs --- openlp/core/utils/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 161b55897..a589c7309 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -179,9 +179,12 @@ def _get_os_dir_path(dir_type): if dir_type == AppLocation.ConfigDir: return os.path.join(BaseDirectory.xdg_config_home, u'openlp') elif dir_type == AppLocation.DataDir: - return os.path.join(BaseDirectory.xdg_data_home, u'openlp') + return os.path.join(BaseDirectory.xdg_data_home, u'openlp', + u'data') elif dir_type == AppLocation.CacheDir: return os.path.join(BaseDirectory.xdg_cache_home, u'openlp') + if dir_type == AppLocation.DataDir: + return os.path.join(os.getenv(u'HOME'), u'openlp', u'data') return os.path.join(os.getenv(u'HOME'), u'.openlp') def _get_frozen_path(frozen_option, non_frozen_option): From 42b1b72ff99fc078b5181b9cdb7152eddad77104 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 11 Feb 2011 19:35:25 +0100 Subject: [PATCH 077/108] fixed buttons --- openlp/core/ui/serviceitemeditform.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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): """ From d98ea53cc15e3b039ca043a3e9282b02acdddc24 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 12 Feb 2011 10:04:10 +0000 Subject: [PATCH 078/108] Allow media items to start in the middle --- openlp/core/lib/serviceitem.py | 7 ++- openlp/core/lib/theme.py | 4 +- openlp/core/ui/__init__.py | 1 + openlp/core/ui/maindisplay.py | 13 +++++ openlp/core/ui/servicemanager.py | 27 +++++++++-- openlp/core/ui/starttimedialog.py | 70 +++++++++++++++++++++++++++ openlp/core/ui/starttimeform.py | 48 ++++++++++++++++++ openlp/plugins/media/lib/mediaitem.py | 1 + 8 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 openlp/core/ui/starttimedialog.py create mode 100644 openlp/core/ui/starttimeform.py diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index c74b89144..4652aa503 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -60,6 +60,7 @@ class ItemCapabilities(object): AddIfNewItem = 9 ProvidesOwnDisplay = 10 AllowsDetailedTitleDisplay = 11 + AllowsVarableStartTime = 12 class ServiceItem(object): @@ -105,6 +106,7 @@ class ServiceItem(object): self.data_string = u'' self.edit_id = None self.xml_version = None + self.start_time =[0, 0, 0] self._new_item() def _new_item(self): @@ -257,7 +259,8 @@ 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 } service_data = [] if self.service_item_type == ServiceItemType.Text: @@ -301,6 +304,8 @@ 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 self.service_item_type == ServiceItemType.Text: for slide in serviceitem[u'serviceitem'][u'data']: self._raw_frames.append(slide) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 06340c2eb..4189452bc 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -170,8 +170,8 @@ class HorizontalType(object): Type enumeration for horizontal alignment. """ Left = 0 - Center = 1 - Right = 2 + Center = 2 + Right = 1 @staticmethod def to_string(horizontal_type): diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index d820c9a5b..45218802e 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -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/maindisplay.py b/openlp/core/ui/maindisplay.py index 05a301bd7..caf84855a 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -105,6 +105,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()) @@ -340,6 +343,16 @@ 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 == 2: + time = self.serviceItem.start_time[0] * 60 * 60 + \ + self.serviceItem.start_time[1] * 60 + \ + self.serviceItem.start_time[2] + self.mediaObject.seek(time * 1000) + def isWebLoaded(self): """ Called by webView event to show display is fully loaded diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 744da7b46..de7047fb6 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -36,7 +36,7 @@ from openlp.core.lib import OpenLPToolbar, ServiceItem, context_menu_action, \ Receiver, build_icon, ItemCapabilities, SettingsManager, translate, \ ThemeLevel from openlp.core.lib.ui import UiStrings, critical_error_message_box -from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm +from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceorderform import PrintServiceOrderForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ split_filename @@ -88,6 +88,7 @@ class ServiceManager(QtGui.QWidget): self._fileName = u'' self.serviceNoteForm = ServiceNoteForm(self.mainwindow) self.serviceItemEditForm = ServiceItemEditForm(self.mainwindow) + self.startTimeForm = StartTimeForm(self.mainwindow) # start with the layout self.layout = QtGui.QVBoxLayout(self) self.layout.setSpacing(0) @@ -270,16 +271,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) @@ -563,6 +567,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 +576,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 +591,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 +607,17 @@ 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[0] = \ + self.startTimeForm.hourSpinBox.value() + self.serviceItems[item][u'service_item'].start_time[1] = \ + self.startTimeForm.minuteSpinBox.value() + self.serviceItems[item][u'service_item'].start_time[2] = \ + self.startTimeForm.secondSpinBox.value() + def onServiceItemEditForm(self): item = self.findServiceItem()[0] self.serviceItemEditForm.setServiceItem( 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..8bd3cd085 --- /dev/null +++ b/openlp/core/ui/starttimeform.py @@ -0,0 +1,48 @@ +# -*- 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 + +from openlp.core.lib import translate + +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. + """ + self.hourSpinBox.setValue(self.item[u'service_item'].start_time[0]) + self.minuteSpinBox.setValue(self.item[u'service_item'].start_time[1]) + self.secondSpinBox.setValue(self.item[u'service_item'].start_time[2]) + return QtGui.QDialog.exec_(self) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 7fb0ed0c1..55b745ec5 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -123,6 +123,7 @@ class MediaMediaItem(MediaManagerItem): 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' From 9f25de02802f0fdfc3441fc5e84d32a318feee89 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Sat, 12 Feb 2011 15:38:28 +0000 Subject: [PATCH 079/108] Path typo --- openlp/core/utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index a589c7309..a23077c39 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -184,7 +184,7 @@ def _get_os_dir_path(dir_type): elif dir_type == AppLocation.CacheDir: return os.path.join(BaseDirectory.xdg_cache_home, u'openlp') if dir_type == AppLocation.DataDir: - return os.path.join(os.getenv(u'HOME'), u'openlp', u'data') + return os.path.join(os.getenv(u'HOME'), u'.openlp', u'data') return os.path.join(os.getenv(u'HOME'), u'.openlp') def _get_frozen_path(frozen_option, non_frozen_option): From e69819a3d36a21043f4396c48ac9ebfe8b9ccdd6 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Sat, 12 Feb 2011 16:07:11 +0000 Subject: [PATCH 080/108] Fix alertTabs --- openlp/core/ui/maindisplay.py | 5 +++-- openlp/core/ui/slidecontroller.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 05a301bd7..11412549c 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -65,6 +65,7 @@ class MainDisplay(DisplayWidget): self.parent = parent self.screens = screens self.isLive = live + self.alertTab = None self.hideMode = None self.override = {} mainIcon = build_icon(u':/icon/openlp-logo-16x16.png') @@ -144,7 +145,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']: @@ -405,7 +406,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/slidecontroller.py b/openlp/core/ui/slidecontroller.py index cd43cddb4..52626f24f 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -415,6 +415,7 @@ class SlideController(QtGui.QWidget): # rebuild display as screen size changed self.display = MainDisplay(self, self.screens, self.isLive) self.display.imageManager = self.parent.renderManager.image_manager + self.display.alertTab = self.alertTab self.display.setup() if self.isLive: self.__addActionsToWidget(self.display) From 57c0ab31dc7e937af5b7fe7fc5a87b83b3f6a4f6 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 13 Feb 2011 10:37:27 +0000 Subject: [PATCH 081/108] Video length tagging and display --- openlp/core/lib/serviceitem.py | 32 +++++++++++++++++++++++++++++++- openlp/core/lib/ui.py | 3 ++- openlp/core/ui/servicemanager.py | 5 +++++ openlp/core/ui/starttimeform.py | 1 + 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 4652aa503..943294f7a 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -28,11 +28,14 @@ The :mod:`serviceitem` provides the service item functionality including the type and capability of an item. """ +import datetime import logging +import mutagen 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__) @@ -106,7 +109,7 @@ class ServiceItem(object): self.data_string = u'' self.edit_id = None self.xml_version = None - self.start_time =[0, 0, 0] + self.start_time = [0, 0, 0] self._new_item() def _new_item(self): @@ -425,3 +428,30 @@ class ServiceItem(object): return self._raw_frames[row][u'path'] except IndexError: return u'' + + def get_media_time(self): + """ + Returns the start and finish time for a media item + """ + tooltip = None + start = None + end = None + if self.start_time != [0, 0, 0]: + start = UiStrings.Start % \ + (self.start_time[0], self.start_time[1], self.start_time[2]) + path = os.path.join(self.get_frames()[0][u'path'], + self.get_frames()[0][u'title']) + if os.path.isfile(path): + file = mutagen.File(path) + if file is not None: + seconds = int(file.info.length) + end = UiStrings.Length % \ + unicode(datetime.timedelta(seconds=seconds)) + 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/ui.py b/openlp/core/lib/ui.py index a3b442801..0013b7099 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -58,6 +58,7 @@ class UiStrings(object): ExportType = unicode(translate('OpenLP.Ui', 'Export %s')) Import = translate('OpenLP.Ui', 'Import') ImportType = unicode(translate('OpenLP.Ui', 'Import %s')) + Length = unicode(translate('OpenLP.Ui', 'Length %s')) Live = translate('OpenLP.Ui', 'Live') Load = translate('OpenLP.Ui', 'Load') LoadANew = unicode(translate('OpenLP.Ui', 'Load a new %s.')) @@ -75,10 +76,10 @@ class UiStrings(object): SendSelectLive = unicode(translate('OpenLP.Ui', 'Send the selected %s live.')) Service = translate('OpenLP.Ui', 'Service') + Start = unicode(translate('OpenLP.Ui', 'Start %02d:%02d:%02d')) 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. diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index de7047fb6..714c555a1 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -611,12 +611,14 @@ class ServiceManager(QtGui.QWidget): item = self.findServiceItem()[0] self.startTimeForm.item = self.serviceItems[item] if self.startTimeForm.exec_(): + self.serviceItems[item][u'service_item'].start_time = [0, 0, 0] self.serviceItems[item][u'service_item'].start_time[0] = \ self.startTimeForm.hourSpinBox.value() self.serviceItems[item][u'service_item'].start_time[1] = \ self.startTimeForm.minuteSpinBox.value() self.serviceItems[item][u'service_item'].start_time[2] = \ self.startTimeForm.secondSpinBox.value() + self.repaintServiceList(item, -1) def onServiceItemEditForm(self): item = self.findServiceItem()[0] @@ -864,6 +866,9 @@ 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)) + tip= item[u'service_item'].get_media_time() + if tip: + child.setToolTip(0, tip) if serviceItem == itemcount: if item[u'expanded'] and serviceItemChild == count: self.serviceManagerList.setCurrentItem(child) diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py index 8bd3cd085..f8b27bd8d 100644 --- a/openlp/core/ui/starttimeform.py +++ b/openlp/core/ui/starttimeform.py @@ -46,3 +46,4 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog): self.minuteSpinBox.setValue(self.item[u'service_item'].start_time[1]) self.secondSpinBox.setValue(self.item[u'service_item'].start_time[2]) return QtGui.QDialog.exec_(self) + From c75e884c7e622f1b3769dc08ecca8e9587e0b355 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 13 Feb 2011 11:05:56 +0000 Subject: [PATCH 082/108] Minor fixes --- openlp/core/ui/servicemanager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 714c555a1..444b02d7a 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -611,7 +611,6 @@ class ServiceManager(QtGui.QWidget): item = self.findServiceItem()[0] self.startTimeForm.item = self.serviceItems[item] if self.startTimeForm.exec_(): - self.serviceItems[item][u'service_item'].start_time = [0, 0, 0] self.serviceItems[item][u'service_item'].start_time[0] = \ self.startTimeForm.hourSpinBox.value() self.serviceItems[item][u'service_item'].start_time[1] = \ @@ -866,7 +865,7 @@ 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)) - tip= item[u'service_item'].get_media_time() + tip = item[u'service_item'].get_media_time() if tip: child.setToolTip(0, tip) if serviceItem == itemcount: From 720c2036e3ac378c55f14f08269672b4bdc45efa Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 13 Feb 2011 13:11:15 +0000 Subject: [PATCH 083/108] Review comments --- openlp/core/lib/serviceitem.py | 10 +++++----- openlp/core/lib/ui.py | 4 ++-- openlp/core/ui/maindisplay.py | 5 +---- openlp/core/ui/servicemanager.py | 16 ++++++++-------- openlp/core/ui/starttimeform.py | 13 ++++++++----- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 943294f7a..1b5261773 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -109,7 +109,7 @@ class ServiceItem(object): self.data_string = u'' self.edit_id = None self.xml_version = None - self.start_time = [0, 0, 0] + self.start_time = 0 self._new_item() def _new_item(self): @@ -436,16 +436,16 @@ class ServiceItem(object): tooltip = None start = None end = None - if self.start_time != [0, 0, 0]: - start = UiStrings.Start % \ - (self.start_time[0], self.start_time[1], self.start_time[2]) + if self.start_time != 0: + start = UiStrings.StartTimeCode % \ + unicode(datetime.timedelta(seconds=self.start_time)) path = os.path.join(self.get_frames()[0][u'path'], self.get_frames()[0][u'title']) if os.path.isfile(path): file = mutagen.File(path) if file is not None: seconds = int(file.info.length) - end = UiStrings.Length % \ + end = UiStrings.LengthTime % \ unicode(datetime.timedelta(seconds=seconds)) if not start and not end: return None diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 0013b7099..400381b0c 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -58,7 +58,7 @@ class UiStrings(object): ExportType = unicode(translate('OpenLP.Ui', 'Export %s')) Import = translate('OpenLP.Ui', 'Import') ImportType = unicode(translate('OpenLP.Ui', 'Import %s')) - Length = unicode(translate('OpenLP.Ui', 'Length %s')) + LengthTime = unicode(translate('OpenLP.Ui', 'Length %s')) Live = translate('OpenLP.Ui', 'Live') Load = translate('OpenLP.Ui', 'Load') LoadANew = unicode(translate('OpenLP.Ui', 'Load a new %s.')) @@ -76,7 +76,7 @@ class UiStrings(object): SendSelectLive = unicode(translate('OpenLP.Ui', 'Send the selected %s live.')) Service = translate('OpenLP.Ui', 'Service') - Start = unicode(translate('OpenLP.Ui', 'Start %02d:%02d:%02d')) + StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s')) Theme = translate('OpenLP.Ui', 'Theme') Themes = translate('OpenLP.Ui', 'Themes') diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 744908406..cc2f2f72d 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -349,10 +349,7 @@ class MainDisplay(DisplayWidget): Start the video at a predetermined point. """ if newState == 2: - time = self.serviceItem.start_time[0] * 60 * 60 + \ - self.serviceItem.start_time[1] * 60 + \ - self.serviceItem.start_time[2] - self.mediaObject.seek(time * 1000) + self.mediaObject.seek(self.serviceItem.start_time * 1000) def isWebLoaded(self): """ diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 444b02d7a..71191fdbf 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -611,11 +611,9 @@ class ServiceManager(QtGui.QWidget): item = self.findServiceItem()[0] self.startTimeForm.item = self.serviceItems[item] if self.startTimeForm.exec_(): - self.serviceItems[item][u'service_item'].start_time[0] = \ - self.startTimeForm.hourSpinBox.value() - self.serviceItems[item][u'service_item'].start_time[1] = \ - self.startTimeForm.minuteSpinBox.value() - self.serviceItems[item][u'service_item'].start_time[2] = \ + 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) @@ -865,9 +863,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)) - tip = item[u'service_item'].get_media_time() - if tip: - child.setToolTip(0, tip) + if item[u'service_item'] \ + .is_capable(ItemCapabilities.AllowsVarableStartTime): + tip = item[u'service_item'].get_media_time() + if tip: + child.setToolTip(0, tip) if serviceItem == itemcount: if item[u'expanded'] and serviceItemChild == count: self.serviceManagerList.setCurrentItem(child) diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py index f8b27bd8d..1280931d5 100644 --- a/openlp/core/ui/starttimeform.py +++ b/openlp/core/ui/starttimeform.py @@ -28,8 +28,6 @@ from PyQt4 import QtGui from starttimedialog import Ui_StartTimeDialog -from openlp.core.lib import translate - class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog): """ The exception dialog @@ -42,8 +40,13 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog): """ Run the Dialog with correct heading. """ - self.hourSpinBox.setValue(self.item[u'service_item'].start_time[0]) - self.minuteSpinBox.setValue(self.item[u'service_item'].start_time[1]) - self.secondSpinBox.setValue(self.item[u'service_item'].start_time[2]) + 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) From 1913da71cce8007ec4d75ca1a87f12218d7a1df9 Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Sun, 13 Feb 2011 15:13:52 +0000 Subject: [PATCH 084/108] Added SongShow Plus importer. --- openlp/plugins/songs/forms/songimportform.py | 49 ++++ openlp/plugins/songs/lib/importer.py | 7 +- .../plugins/songs/lib/songshowplusimport.py | 212 ++++++++++++++++++ 3 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 openlp/plugins/songs/lib/songshowplusimport.py diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 8cd8c70fa..e641ccba7 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -140,6 +140,12 @@ class SongImportForm(OpenLPWizard): QtCore.QObject.connect(self.songBeamerRemoveButton, QtCore.SIGNAL(u'clicked()'), self.onSongBeamerRemoveButtonClicked) + QtCore.QObject.connect(self.songShowPlusAddButton, + QtCore.SIGNAL(u'clicked()'), + self.onSongShowPlusAddButtonClicked) + QtCore.QObject.connect(self.songShowPlusRemoveButton, + QtCore.SIGNAL(u'clicked()'), + self.onSongShowPlusRemoveButtonClicked) def addCustomPages(self): """ @@ -188,6 +194,8 @@ class SongImportForm(OpenLPWizard): self.addFileSelectItem(u'ew', single_select=True) # Words of Worship self.addFileSelectItem(u'songBeamer') + # Song Show Plus + self.addFileSelectItem(u'songShowPlus') # Commented out for future use. # self.addFileSelectItem(u'csv', u'CSV', single_select=True) self.sourceLayout.addLayout(self.formatStack) @@ -237,6 +245,8 @@ class SongImportForm(OpenLPWizard): translate('SongsPlugin.ImportWizardForm', 'EasyWorship')) self.formatComboBox.setItemText(10, translate('SongsPlugin.ImportWizardForm', 'SongBeamer')) + self.formatComboBox.setItemText(11, + translate('SongsPlugin.ImportWizardForm', 'SongShow Plus')) # self.formatComboBox.setItemText(11, # translate('SongsPlugin.ImportWizardForm', 'CSV')) self.openLP2FilenameLabel.setText( @@ -301,6 +311,10 @@ class SongImportForm(OpenLPWizard): translate('SongsPlugin.ImportWizardForm', 'Add Files...')) self.songBeamerRemoveButton.setText( translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) + self.songShowPlusAddButton.setText( + translate('SongsPlugin.ImportWizardForm', 'Add Files...')) + self.songShowPlusRemoveButton.setText( + translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) # self.csvFilenameLabel.setText( # translate('SongsPlugin.ImportWizardForm', 'Filename:')) # self.csvBrowseButton.setText( @@ -438,6 +452,16 @@ 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 @@ -671,6 +695,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 onSongShowPlusRemoveButtonClicked(self): + """ + Remove selected SongShow Plus files from the import list + """ + self.removeSelectedItems(self.songShowPlusFileListWidget) def registerFields(self): """ @@ -697,6 +739,7 @@ class SongImportForm(OpenLPWizard): self.easiSlidesFilenameEdit.setText(u'') self.ewFilenameEdit.setText(u'') self.songBeamerFileListWidget.clear() + self.songShowPlusFileListWidget.clear() #self.csvFilenameEdit.setText(u'') def preWizard(self): @@ -773,6 +816,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/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 6f566ff4f..cbe5c6922 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -34,6 +34,7 @@ from wowimport import WowImport from cclifileimport import CCLIFileImport from ewimport import EasyWorshipSongImport from songbeamerimport import SongBeamerImport +from songshowplusimport import SongShowPlusImport # Imports that might fail try: from olp1import import OpenLP1SongImport @@ -71,6 +72,7 @@ class SongFormat(object): EasiSlides = 8 EasyWorship = 9 SongBeamer = 10 + SongShowPlus = 11 @staticmethod def get_class(format): @@ -102,6 +104,8 @@ class SongFormat(object): return EasyWorshipSongImport elif format == SongFormat.SongBeamer: return SongBeamerImport + elif format == SongFormat.SongShowPlus: + return SongShowPlusImport return None @staticmethod @@ -120,7 +124,8 @@ class SongFormat(object): SongFormat.Generic, SongFormat.EasiSlides, SongFormat.EasyWorship, - SongFormat.SongBeamer + SongFormat.SongBeamer, + SongFormat.SongShowPlus ] @staticmethod diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py new file mode 100644 index 000000000..78c2e838d --- /dev/null +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2011 Raoul Snyman # +# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian # +# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # +# Carsten Tinggaard, Frode Woldsund # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The :mod:`wowimport` module provides the functionality for importing Words of +Worship songs into the OpenLP database. +""" +import os +import logging +import struct + +from openlp.plugins.songs.lib.songimport import SongImport + +TITLE = 1 +AUTHOR = 2 +COPYRIGHT = 3 +CCLI_NO = 5 +VERSE = 12 +CHORUS = 20 +TOPIC = 29 +COMMENTS = 30 +VERSE_ORDER = 31 +SONG_BOOK = 35 +SONG_NUMBER = 36 +CUSTOM_VERSE = 37 + +log = logging.getLogger(__name__) + +class SongShowPlusImport(SongImport): + """ + The :class:`SongShowPlusImport` class provides the ability to import song + files from SongShow Plus. + + **SongShow Plus Song File Format:** + + The SongShow Plus song file format is as follows: + + * Each piece of data in the song file has some information that precedes + it. + * The general format of this data is as follows: + 4 Bytes, forming a 32 bit number, a key if you will, this describes what + the data is (see blockKey below) + 4 Bytes, forming a 32 bit number, which is the number of bytes until the + next block starts + 1 Byte, which tells how namy bytes follows + 1 or 4 Bytes, describes how long the string is, if its 1 byte, the string + is less than 255 + The next bytes are the actuall data. + The next block of data follows on. + + This description does differ for verses. Which includes extra bytes + stating the verse type or number. In some cases a "custom" verse is used, + in that case, this block will in include 2 strings, with the associated + string length descriptors. The first string is the name of the verse, the + second is the verse content. + + The file is ended with four null bytes. + + Valid extensions for a SongShow Plus song file are: + + * .sbsong + """ + otherList = {} + otherCount = 0 + + def __init__(self, master_manager, **kwargs): + """ + Initialise the import. + + ``master_manager`` + The song manager for the running OpenLP installation. + """ + SongImport.__init__(self, master_manager) + if kwargs.has_key(u'filename'): + self.import_source = kwargs[u'filename'] + if kwargs.has_key(u'filenames'): + self.import_source = kwargs[u'filenames'] + log.debug(self.import_source) + + def do_import(self): + """ + Receive a single file or a list of files to import. + """ + if isinstance(self.import_source, list): + self.import_wizard.progressBar.setMaximum(len(self.import_source)) + for file in self.import_source: + author = u'' + copyright = u'' + self.sspVerseOrderList = [] + otherCount = 0 + otherList = {} + file_name = os.path.split(file)[1] + self.import_wizard.incrementProgressBar( + u'Importing %s' % (file_name), 0) + songData = open(file, 'rb') + while (1): + blockKey, = struct.unpack("I",songData.read(4)) + # The file ends with 4 NUL's + if blockKey == 0: + break + nextBlockStarts, = struct.unpack("I",songData.read(4)) + if blockKey == VERSE or blockKey == CHORUS: + null, verseNo, = struct.unpack("BB",songData.read(2)) + elif blockKey == CUSTOM_VERSE: + null, verseNameLength, = struct.unpack("BB", + songData.read(2)) + verseName = songData.read(verseNameLength) + lengthDescriptorSize, = struct.unpack("B",songData.read(1)) + # Detect if/how long the length descriptor is + if lengthDescriptorSize == 12: + lengthDescriptor, = struct.unpack("I",songData.read(4)) + elif lengthDescriptorSize == 2: + lengthDescriptor = 1 + elif lengthDescriptorSize == 9: + lengthDescriptor = 0 + else: + lengthDescriptor, = struct.unpack("B",songData.read(1)) + data = songData.read(lengthDescriptor) + + if blockKey == TITLE: + self.title = unicode(data, u'cp1252') + elif blockKey == AUTHOR: + authors = data.split(" / ") + for author in authors: + if author.find(",") !=-1: + authorParts = author.split(", ") + author = authorParts[1] + " " + authorParts[0] + self.parse_author(unicode(author, u'cp1252')) + elif blockKey == COPYRIGHT: + self.add_copyright(unicode(data, u'cp1252')) + elif blockKey == CCLI_NO: + self.ccli_number = int(data) + elif blockKey == VERSE: + self.add_verse(unicode(data, u'cp1252'), + "V%s" % verseNo) + elif blockKey == CHORUS: + self.add_verse(unicode(data, u'cp1252'), + "C%s" % verseNo) + elif blockKey == TOPIC: + self.topics.append(unicode(data, u'cp1252')) + elif blockKey == COMMENTS: + self.comments = unicode(data, u'cp1252') + elif blockKey == VERSE_ORDER: + verseTag = self.toOpenLPVerseTag(data) + self.sspVerseOrderList.append(unicode(verseTag, + u'cp1252')) + elif blockKey == SONG_BOOK: + self.song_book_name = unicode(data, u'cp1252') + elif blockKey == SONG_NUMBER: + self.song_number = ord(data) + elif blockKey == CUSTOM_VERSE: + verseTag = self.toOpenLPVerseTag(verseName) + self.add_verse(unicode(data, u'cp1252'), verseTag) + else: + log.debug("Unrecognised blockKey: %s, data: %s" + %(blockKey, data)) + self.verse_order_list = self.sspVerseOrderList + songData.close() + self.finish() + self.import_wizard.incrementProgressBar( + u'Importing %s' % (file_name)) + return True + + def toOpenLPVerseTag(self, verseName): + if verseName.find(" ")!=-1: + verseParts = verseName.split(" ") + verseType = verseParts[0] + verseNumber = verseParts[1] + else: + verseType = verseName + verseNumber = "1" + verseType = verseType.lower() + if verseType == "verse": + verseTag = "V" + elif verseType == "chorus": + verseTag = "C" + elif verseType == "bridge": + verseTag = "B" + elif verseType == "pre-chorus": + verseTag = "P" + elif verseType == "bridge": + verseTag = "B" + else: + if not self.otherList.has_key(verseName): + self.otherCount = self.otherCount + 1 + self.otherList[verseName] = str(self.otherCount) + verseTag = "O" + verseNumber = self.otherList[verseName] + verseTag = verseTag + verseNumber + return verseTag From b10f59c870a424cca49ff44247b12814ee07a090 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 13 Feb 2011 16:51:19 +0100 Subject: [PATCH 085/108] added icons --- openlp/plugins/bibles/lib/mediaitem.py | 14 +++++++------- resources/images/openlp-2.qrc | 4 ++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index b83e62c57..d4e2d837e 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -90,9 +90,9 @@ class BibleMediaItem(MediaManagerItem): self.quickSearchEdit.setObjectName(u'quickSearchEdit') self.quickSearchLabel.setBuddy(self.quickSearchEdit) self.quickSearchEdit.setSearchTypes([ - (1, u':/songs/song_topic_edit.png', - translate('BiblesPlugin.MediaItem', 'Verse Search')), - (2, u':/songs/song_search_author.png', + (1, u':/bibles/bibles_serach_reference.png', + translate('BiblesPlugin.MediaItem', 'Scripture Reference')), + (2, u':/bibles/bibles_serach_text.png', translate('BiblesPlugin.MediaItem', 'Text Search')) ]) self.quickLayout.addRow(self.quickSearchLabel, self.quickSearchEdit) @@ -352,10 +352,10 @@ 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'. + # We have to do a 'Reference Search'. if self.quickSearchEdit.currentSearchType() == 1: bibles = self.parent.manager.get_bibles() bible = unicode(self.quickVersionComboBox.currentText()) @@ -485,7 +485,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) @@ -494,7 +494,7 @@ class BibleMediaItem(MediaManagerItem): second_bible = unicode(self.quickSecondComboBox.currentText()) text = unicode(self.quickSearchEdit.text()) if self.quickSearchEdit.currentSearchType() == 1: - # We are doing a 'Verse Search'. + # 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/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index dd5abd861..929775f78 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_serach_text.png + bibles_serach_reference.png + plugin_alerts.png plugin_bibles.png From 4d310f17df0d4c32fd288d9eb42b3af8bea82e9c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 13 Feb 2011 17:31:44 +0100 Subject: [PATCH 086/108] added files, spelling --- openlp/plugins/bibles/lib/mediaitem.py | 4 ++-- resources/images/bibles_search_reference.png | Bin 0 -> 2031 bytes resources/images/bibles_search_text.png | Bin 0 -> 335 bytes resources/images/openlp-2.qrc | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 resources/images/bibles_search_reference.png create mode 100644 resources/images/bibles_search_text.png diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index d4e2d837e..5f18beca4 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -90,9 +90,9 @@ class BibleMediaItem(MediaManagerItem): self.quickSearchEdit.setObjectName(u'quickSearchEdit') self.quickSearchLabel.setBuddy(self.quickSearchEdit) self.quickSearchEdit.setSearchTypes([ - (1, u':/bibles/bibles_serach_reference.png', + (1, u':/bibles/bibles_search_reference.png', translate('BiblesPlugin.MediaItem', 'Scripture Reference')), - (2, u':/bibles/bibles_serach_text.png', + (2, u':/bibles/bibles_search_text.png', translate('BiblesPlugin.MediaItem', 'Text Search')) ]) self.quickLayout.addRow(self.quickSearchLabel, self.quickSearchEdit) diff --git a/resources/images/bibles_search_reference.png b/resources/images/bibles_search_reference.png new file mode 100644 index 0000000000000000000000000000000000000000..f64c0ad78ea8c37e0982cc56a93991d776d56faf GIT binary patch literal 2031 zcmV{l`45fF)Sd}nJobU8nr>0%Xr&{P;r+8i|24V%^3U#TJS7W63x!hkkSNJn zIpze}GN{xf>L$>l%JH}C4`Tq{oI9~OQ4aym%;nMmPXD;5TlKaJH--7m)nf3sPdgG= zGDSM=GXJpjp7c!I7Tz9v+Q{{}m0{WPpx$YbJT{Ceph2F$u;1A{=qzr%SixIs<1wv4 z^#MK}s`>l<>%5#5)#@zbNxD(Xlp7A2##z%HXiDqoe9;dOWuH=zcn%mEdDk#>FyY(Y z;g|KV*JkBxv*vF8ENYhL(g0;_xN7n@M!o)TT5qz-KGi6HmyBL(ukrBva(H7m!butm zxCB~6oo(rCC9{wzb&6=uS8o}(S?S1#F7V9Wl)?i5h=~>eq7P6PnauT?NDnZI+48fB zRDiy~8U(F)qYx;{LsuM^MmWvvfp{*|`N`C72XX%0`u7sud|qLz zs;#*5Eo!H{_aKmCzA4x|(^dn9)R1`c48RN;M5h6mL=KP?un0pG7H~!MIQ8LLSxhWZ z#E26U-Q!Y1w|VobfX76JWytW39PcJwk6eP^l=I0zpJ_*@R<))Ww^^ogoBia8QzPp= zPt#pXw#MdpoF_YU)h2JN1z>KIx!4ZSU^){~09(fXz*GT_C60C60UF4(5I@LC$D0<- z_lD%w09En6b2sMBRE_#L3O*R-lXz8pc6>IoUl1m!<9l;kJ8rgL>$q_4xpSzv&*k{% zzHS}PEA4%pXWr*aE2i?8e#a??kh>1hA8?ZH0Z21xCcgl@W9&uV9l)xwKDV~xJN9c< z%B;m2Lz9WM?d4Wn&%N*VtUEMM7$QhwceI3#Q3=jEwJ~DiqIc1|=-C9UL@MGOckN+z zcl+C{TCYN{2VT*qq76oaQXj#kY0WN5hx)0$VoQq!wHCbl+=He zfdxKM-uJu(O}lk-j1879zA&{oDpO{{&gk$zM2+piPW@HW9D6z-Hi>VD0s-@g>8|NG z;3#qIa%2Pi9Rjr=joWN5vjy8uFvYLG(#$X|te&yU>8r}G8%AODwu%^z4i z3U-w|T6WD%{p%6xV7BuA93q*rV7xQ4hx#rLnwLM}7U{P3+2=q5h;qewqGf>Yk?xkR z7GQ;}jP(En7XBsv7|0EB-N^7AyfY43lI_qeukfpj9Il$an*Wkl$ixpUV8n;Shs1|a z>~huRs>@a8f$P_5LW2tsJ4I}rs2&>G|4Oh~|$hcSq+nTE;Q)4mTZ z{-z9_8$i&59`pbti6oIE=01HvpV2lh7Fw8qr9NVb=&7jaa<^V*2rvpAgR1I3#ZLMM zxtl1NqY0h}GE}V($705dUn0_Q&Ce+ zi_f9#f2xjY*9CG606Y=#un`-v5uk0fjkeLlzkSO(HK*r{oJkkxljHlbua~4l^33Dvzm3P1q`6#sWTvdD9h=c0-1n-5vAgzOF39kR3LepX3VSys8B zN+npYRXJ5zm;Ji5E$!27n2<4Uo9tA~(NVp$^0x~hWKuL!1^_>9EpIq>iIDx1h^7~g&L@k=I#SQ%`fYc+ zAj^=i^J{lM<({nQ_tJ~ci(WcEbT^Qn$UIa?{yM66lCXbadKnKaW(>?e@)Gzj2$Uw1 z1OLb*0000YdQ@0+Q*UN;cVTj6004N}D=#nC%goCzPEIUH)ypqR2LLwM23QbN%3J^d N002ovPDHLkV1nG};fw$P literal 0 HcmV?d00001 diff --git a/resources/images/bibles_search_text.png b/resources/images/bibles_search_text.png new file mode 100644 index 0000000000000000000000000000000000000000..1ab7145c6520bc770aedc33259b26d1df59f1361 GIT binary patch literal 335 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJbFq_W2nPqp?T7vkfLzW3kH}&M z2FBeW%xLxI@gtz1WQl7;NpOBzNqJ&XDnogBxn5>oc5!lIL8@MUQTpt6Hc~)E9sxcf zuK)l42QsW(4WB}%e<@${og@7nN9H?@3?TWQBjY_sI*9D< z?gpxa10dJu!nw~tzF0|+UoeBTk&%ge1qf7DRoB$k)i*S??EDrrc?M8LxTlL_h=gS8 z!AQO)2L{#+TAvsMkL2$7@2jeJ^3TOh>n+2jc(j*irOC*oWysc)|Il#i*CwBG^$WflIelF{r5}E*( C9&~j8 literal 0 HcmV?d00001 diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 929775f78..9e6ff6543 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -22,8 +22,8 @@ song_book_edit.png - bibles_serach_text.png - bibles_serach_reference.png + bibles_search_text.png + bibles_search_reference.png plugin_alerts.png From 572cbd95f0708c5f77c5a8fd83ac54b2e5ef42cf Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sun, 13 Feb 2011 22:03:55 +0200 Subject: [PATCH 087/108] Re-fixed the data directory on Linux with XDG installed. --- openlp/core/utils/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index a23077c39..9db744460 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -179,8 +179,7 @@ def _get_os_dir_path(dir_type): if dir_type == AppLocation.ConfigDir: return os.path.join(BaseDirectory.xdg_config_home, u'openlp') elif dir_type == AppLocation.DataDir: - return os.path.join(BaseDirectory.xdg_data_home, u'openlp', - u'data') + return os.path.join(BaseDirectory.xdg_data_home, u'openlp') elif dir_type == AppLocation.CacheDir: return os.path.join(BaseDirectory.xdg_cache_home, u'openlp') if dir_type == AppLocation.DataDir: From e6ed43ae02210b432e068efe2453e7c649011cd7 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 14 Feb 2011 15:23:56 +0100 Subject: [PATCH 088/108] added enumeration class --- openlp/plugins/bibles/lib/mediaitem.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 5f18beca4..a6ceba6ac 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -38,6 +38,14 @@ from openlp.plugins.bibles.lib import get_reference_match log = logging.getLogger(__name__) +class BibleSearch(object): + """ + Enumeration class for the different search methods for the "quick search". + """ + Reference = 1 + Text = 2 + + class BibleMediaItem(MediaManagerItem): """ This is the custom media manager item for Bibles. @@ -90,9 +98,9 @@ class BibleMediaItem(MediaManagerItem): self.quickSearchEdit.setObjectName(u'quickSearchEdit') self.quickSearchLabel.setBuddy(self.quickSearchEdit) self.quickSearchEdit.setSearchTypes([ - (1, u':/bibles/bibles_search_reference.png', + (BibleSearch.Reference, u':/bibles/bibles_search_reference.png', translate('BiblesPlugin.MediaItem', 'Scripture Reference')), - (2, u':/bibles/bibles_search_text.png', + (BibleSearch.Text, u':/bibles/bibles_search_text.png', translate('BiblesPlugin.MediaItem', 'Text Search')) ]) self.quickLayout.addRow(self.quickSearchLabel, self.quickSearchEdit) @@ -356,7 +364,7 @@ class BibleMediaItem(MediaManagerItem): """ books = [] # We have to do a 'Reference Search'. - if self.quickSearchEdit.currentSearchType() == 1: + if self.quickSearchEdit.currentSearchType() == BibleSearch.Reference: bibles = self.parent.manager.get_bibles() bible = unicode(self.quickVersionComboBox.currentText()) if bible: @@ -493,7 +501,7 @@ class BibleMediaItem(MediaManagerItem): bible = unicode(self.quickVersionComboBox.currentText()) second_bible = unicode(self.quickSecondComboBox.currentText()) text = unicode(self.quickSearchEdit.text()) - if self.quickSearchEdit.currentSearchType() == 1: + 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: From 531e50a4174af16d25cfb82ecf6b63c8b12c2a16 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 14 Feb 2011 17:25:51 +0000 Subject: [PATCH 089/108] Fix translation breaking strings --- openlp/core/lib/plugin.py | 38 ++++++++++--------- openlp/core/lib/ui.py | 22 ++--------- openlp/core/ui/mainwindow.py | 9 ++--- openlp/core/ui/servicemanager.py | 14 +++---- openlp/core/ui/thememanager.py | 12 +++--- openlp/plugins/bibles/bibleplugin.py | 16 +++++--- openlp/plugins/custom/customplugin.py | 17 ++++++--- openlp/plugins/images/imageplugin.py | 13 ++++++- openlp/plugins/media/mediaplugin.py | 13 ++++++- .../presentations/presentationplugin.py | 16 +++++++- openlp/plugins/songs/songsplugin.py | 13 ++++++- 11 files changed, 112 insertions(+), 71 deletions(-) diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index a073d31ea..a525e70cc 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -335,37 +335,39 @@ class Plugin(QtCore.QObject): """ return self.textStrings[name] - def setPluginTextStrings(self): + def setPluginUiTextStrings(self, tooltips): """ Called to define all translatable texts of the plugin """ ## Load Action ## - self._setSingularTextString(StringContent.Load, - UiStrings.Load, UiStrings.LoadANew) + self.__setNameTextString(StringContent.Load, + UiStrings.Load, tooltips[load]) + ## Import Action ## + self.__setNameTextString(StringContent.Import, + UiStrings.Import, tooltips[import]) ## New Action ## - self._setSingularTextString(StringContent.New, - UiStrings.Add, UiStrings.AddANew) + self.__setNameTextString(StringContent.New, + UiStrings.Add, tooltips[new]) ## Edit Action ## - self._setSingularTextString(StringContent.Edit, - UiStrings.Edit, UiStrings.EditSelect) + self.__setNameTextString(StringContent.Edit, + UiStrings.Edit, tooltips[edit]) ## Delete Action ## - self._setSingularTextString(StringContent.Delete, - UiStrings.Delete, UiStrings.DeleteSelect) + self.__setNameTextString(StringContent.Delete, + UiStrings.Delete, tooltips[delete]) ## Preview Action ## - self._setSingularTextString(StringContent.Preview, - UiStrings.Preview, UiStrings.PreviewSelect) + self.__setNameTextString(StringContent.Preview, + UiStrings.Preview, tooltips[preview]) ## Send Live Action ## - self._setSingularTextString(StringContent.Live, - UiStrings.Live, UiStrings.SendSelectLive) + self.__setNameTextString(StringContent.Live, + UiStrings.Live, tooltips[live]) ## Add to Service Action ## - self._setSingularTextString(StringContent.Service, - UiStrings.Service, UiStrings.AddSelectService) + self.__setNameTextString(StringContent.Service, + UiStrings.Service, tooltips[service]) - def _setSingularTextString(self, name, title, tooltip): + def __setNameTextString(self, name, title, tooltip): """ Utility method for creating a plugin's textStrings. This method makes use of the singular name of the plugin object so must only be called after this has been set. """ - self.textStrings[name] = { u'title': title, u'tooltip': tooltip % - self.getString(StringContent.Name)[u'singular']} + self.textStrings[name] = {u'title': title, u'tooltip': tooltip} diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 400381b0c..48a932ab1 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -41,40 +41,26 @@ class UiStrings(object): # These strings should need a good reason to be retranslated elsewhere. # Should some/more/less of these have an & attached? Add = translate('OpenLP.Ui', '&Add') - AddANew = unicode(translate('OpenLP.Ui', 'Add a new %s.')) - AddSelectService = unicode(translate('OpenLP.Ui', - 'Add the selected %s to the service.')) Advanced = translate('OpenLP.Ui', 'Advanced') AllFiles = translate('OpenLP.Ui', 'All Files') Authors = translate('OpenLP.Ui', 'Authors') - CreateANew = unicode(translate('OpenLP.Ui', 'Create a new %s.')) + CreateService = translate('OpenLP.Ui', 'Create a new service.') Delete = translate('OpenLP.Ui', '&Delete') - DeleteSelect = unicode(translate('OpenLP.Ui', 'Delete the selected %s.')) - DeleteType = unicode(translate('OpenLP.Ui', 'Delete %s')) Edit = translate('OpenLP.Ui', '&Edit') - EditSelect = unicode(translate('OpenLP.Ui', 'Edit the selected %s.')) - EditType = unicode(translate('OpenLP.Ui', 'Edit %s')) Error = translate('OpenLP.Ui', 'Error') - ExportType = unicode(translate('OpenLP.Ui', 'Export %s')) Import = translate('OpenLP.Ui', 'Import') - ImportType = unicode(translate('OpenLP.Ui', 'Import %s')) - LengthTime = unicode(translate('OpenLP.Ui', 'Length %s')) Live = translate('OpenLP.Ui', 'Live') Load = translate('OpenLP.Ui', 'Load') - LoadANew = unicode(translate('OpenLP.Ui', 'Load a new %s.')) New = translate('OpenLP.Ui', 'New') - NewType = unicode(translate('OpenLP.Ui', 'New %s')) + NewService = translate('OpenLP.Ui', 'New Service') OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') - OpenType = unicode(translate('OpenLP.Ui', 'Open %s')) + OpenService = translate('OpenLP.Ui', 'Open Service') Preview = translate('OpenLP.Ui', 'Preview') - PreviewSelect = unicode(translate('OpenLP.Ui', 'Preview the selected %s.')) 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') - SaveType = unicode(translate('OpenLP.Ui', 'Save %s')) - SendSelectLive = unicode(translate('OpenLP.Ui', - 'Send the selected %s live.')) + SaveService = translate('OpenLP.Ui', 'Save Service') Service = translate('OpenLP.Ui', 'Service') StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s')) Theme = translate('OpenLP.Ui', 'Theme') diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 4840ef5da..c691c006e 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -319,17 +319,16 @@ class Ui_MainWindow(object): self.themeManagerDock.setWindowTitle( translate('OpenLP.MainWindow', 'Theme Manager')) self.FileNewItem.setText(translate('OpenLP.MainWindow', '&New')) - self.FileNewItem.setToolTip(UiStrings.NewType % UiStrings.Service) - self.FileNewItem.setStatusTip( - UiStrings.CreateANew % UiStrings.Service.toLower()) + 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(UiStrings.OpenType % UiStrings.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(UiStrings.SaveType % UiStrings.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')) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 71191fdbf..04a4753e8 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -96,18 +96,14 @@ class ServiceManager(QtGui.QWidget): # Create the top toolbar self.toolbar = OpenLPToolbar(self) self.toolbar.addToolbarButton( - UiStrings.NewType % UiStrings.Service, - u':/general/general_new.png', - UiStrings.CreateANew % UiStrings.Service.toLower(), - self.onNewServiceClicked) + UiStrings.NewService, u':/general/general_new.png', + UiStrings.Create.Service, self.onNewServiceClicked) self.toolbar.addToolbarButton( - UiStrings.OpenType % UiStrings.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( - UiStrings.SaveType % UiStrings.Service, - u':/general/general_save.png', + UiStrings.SaveService, u':/general/general_save.png', translate('OpenLP.ServiceManager', 'Save this service'), self.saveFile) self.toolbar.addSeparator() @@ -469,7 +465,7 @@ class ServiceManager(QtGui.QWidget): save the file. """ fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow, - UiStrings.SaveType % UiStrings.Service, + UiStrings.SaveService, SettingsManager.get_last_dir( self.mainwindow.serviceSettingsSection), translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)'))) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 36abb19c1..8dc895862 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -63,28 +63,28 @@ class ThemeManager(QtGui.QWidget): self.layout.setObjectName(u'layout') self.toolbar = OpenLPToolbar(self) self.toolbar.addToolbarButton( - UiStrings.NewType % UiStrings.Theme, + translate('OpenLP.ThemeManager', 'New Theme'), u':/themes/theme_new.png', - UiStrings.CreateANew % UiStrings.Theme.toLower(), + translate('OpenLP.ThemeManager', 'Create a new theme.'), self.onAddTheme) self.toolbar.addToolbarButton( - UiStrings.EditType % UiStrings.Theme, + translate('OpenLP.ThemeManager', 'Edit Theme'), u':/themes/theme_edit.png', translate('OpenLP.ThemeManager', 'Edit a theme.'), self.onEditTheme) self.deleteToolbarAction = self.toolbar.addToolbarButton( - UiStrings.DeleteType % UiStrings.Theme, + translate('OpenLP.ThemeManager', 'Delete Theme'), u':/general/general_delete.png', translate('OpenLP.ThemeManager', 'Delete a theme.'), self.onDeleteTheme) self.toolbar.addSeparator() self.toolbar.addToolbarButton( - UiStrings.ImportType % UiStrings.Theme, + translate('OpenLP.ThemeManager', 'Import Theme'), u':/general/general_import.png', translate('OpenLP.ThemeManager', 'Import a theme.'), self.onImportTheme) self.toolbar.addToolbarButton( - UiStrings.ExportType % UiStrings.Theme, + translate('OpenLP.ThemeManager', 'Export Theme'), u':/general/general_export.png', translate('OpenLP.ThemeManager', 'Export a theme.'), self.onExportTheme) diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index e3447cfdd..63fb28b67 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -129,9 +129,15 @@ class BiblePlugin(Plugin): u'title': translate('BiblesPlugin', 'Bibles', 'container title') } # Middle Header Bar - ## Import Action ## - self.textStrings[StringContent.Import] = { - u'title': UiStrings.Import, - u'tooltip': translate('BiblesPlugin', 'Import a Bible') + tooltips = { + load: u'' + import: translate('BiblesPlugin', 'Import a Bible') + new: translate('BiblesPlugin', 'Add a new Bible') + edit: translate('BiblesPlugin', 'Edit the selected Bible') + delete: translate('BiblesPlugin', 'Delete the selected Bible') + preview: translate('BiblesPlugin', 'Preview the selected Bible') + live: translate('BiblesPlugin', 'Send the selected Bible live') + service: translate('BiblesPlugin', + 'Add the selected Bible to the service') } - Plugin.setPluginTextStrings(self) + self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index 92546cd4f..86f1b20d8 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -106,13 +106,18 @@ class CustomPlugin(Plugin): u'title': translate('CustomsPlugin', 'Custom', 'container title') } # Middle Header Bar - ## Import Action ## - self.textStrings[StringContent.Import] = { - u'title': UiStrings.Import, - u'tooltip': translate('CustomsPlugin', - 'Import a Custom') + tooltips = { + load: translate('CustomsPlugin', 'Load a new Custom') + import: translate('CustomsPlugin', 'Import a Custom') + new: translate('CustomsPlugin', 'Add a new Custom') + edit: translate('CustomsPlugin', 'Edit the selected Custom') + delete: translate('CustomsPlugin', 'Delete the selected Custom') + preview: translate('CustomsPlugin', 'Preview the selected Custom') + live: translate('CustomsPlugin', 'Send the selected Custom live') + service: translate('CustomsPlugin', + 'Add the selected Custom to the service') } - Plugin.setPluginTextStrings(self) + self.setPluginUiTextStrings(tooltips) def finalise(self): """ diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 6b64598fc..59005de4f 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -69,4 +69,15 @@ class ImagePlugin(Plugin): u'title': translate('ImagePlugin', 'Images', 'container title') } # Middle Header Bar - Plugin.setPluginTextStrings(self) + tooltips = { + load: translate('ImagePlugin', 'Load a new Image') + import: u'' + new: translate('ImagePlugin', 'Add a new Image') + edit: translate('ImagePlugin', 'Edit the selected Image') + delete: translate('ImagePlugin', 'Delete the selected Image') + preview: translate('ImagePlugin', 'Preview the selected Image') + live: translate('ImagePlugin', 'Send the selected Image live') + service: translate('ImagePlugin', + 'Add the selected Image to the service') + } + self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index b9db9b8c1..6989e91aa 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -95,4 +95,15 @@ class MediaPlugin(Plugin): u'title': translate('MediaPlugin', 'Media', 'container title') } # Middle Header Bar - Plugin.setPluginTextStrings(self) + tooltips = { + load: translate('MediaPlugin', 'Load a new Media') + import: u'' + new: translate('MediaPlugin', 'Add a new Media') + edit: translate('MediaPlugin', 'Edit the selected Media') + delete: translate('MediaPlugin', 'Delete the selected Media') + preview: translate('MediaPlugin', 'Preview the selected Media') + live: translate('MediaPlugin', 'Send the selected Media live') + service: translate('MediaPlugin', + 'Add the selected Media to the service') + } + self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index c81cdc028..fba332eb0 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -167,4 +167,18 @@ class PresentationPlugin(Plugin): 'container title') } # Middle Header Bar - Plugin.setPluginTextStrings(self) + tooltips = { + load: translate('PresentationPlugin', 'Load a new Presentation') + import: u'' + new: u'' + edit: u'' + delete: translate('PresentationPlugin', + 'Delete the selected Presentation') + preview: translate('PresentationPlugin', + 'Preview the selected Presentation') + live: translate('PresentationPlugin', + 'Send the selected Presentation live') + service: translate('PresentationPlugin', + 'Add the selected Presentation to the service') + } + self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 646e8e86e..abeddfdae 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -228,7 +228,18 @@ class SongsPlugin(Plugin): u'title': translate('SongsPlugin', 'Songs', 'container title') } # Middle Header Bar - Plugin.setPluginTextStrings(self) + tooltips = { + load: u'' + import: u'' + new: translate('SongsPlugin', 'Add a new Song') + edit: translate('SongsPlugin', 'Edit the selected Song') + delete: translate('SongsPlugin', 'Delete the selected Song') + preview: translate('SongsPlugin', 'Preview the selected Song') + live: translate('SongsPlugin', 'Send the selected Song live') + service: translate('SongsPlugin', + 'Add the selected Song to the service') + } + self.setPluginUiTextStrings(tooltips) def finalise(self): """ From 5800d01fa13219c3ad28d323e24f97ec19bdf2d9 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 14 Feb 2011 17:30:41 +0000 Subject: [PATCH 090/108] Don't remove LengthTime --- openlp/core/lib/ui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 48a932ab1..a98e2fb7f 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -49,6 +49,7 @@ class UiStrings(object): 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') From fba2a245fb9e2020d2326658c0ad301912a6cb8d Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 14 Feb 2011 17:54:09 +0000 Subject: [PATCH 091/108] Fix Media length calc --- openlp/core/lib/serviceitem.py | 18 ++++++++---------- openlp/core/ui/printserviceorderform.py | 14 +++----------- openlp/plugins/media/lib/mediaitem.py | 22 ++++++++++++++++++++++ 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 1b5261773..e8e1f68f0 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -30,7 +30,6 @@ type and capability of an item. import datetime import logging -import mutagen import os import uuid @@ -110,6 +109,7 @@ class ServiceItem(object): self.edit_id = None self.xml_version = None self.start_time = 0 + self.media_length = 0 self._new_item() def _new_item(self): @@ -263,7 +263,8 @@ class ServiceItem(object): u'search': self.search_string, u'data': self.data_string, u'xml_version': self.xml_version, - u'start_time': self.start_time + u'start_time': self.start_time, + u'media_length': self.media_length } service_data = [] if self.service_item_type == ServiceItemType.Text: @@ -309,6 +310,8 @@ class ServiceItem(object): 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) @@ -439,14 +442,9 @@ class ServiceItem(object): if self.start_time != 0: start = UiStrings.StartTimeCode % \ unicode(datetime.timedelta(seconds=self.start_time)) - path = os.path.join(self.get_frames()[0][u'path'], - self.get_frames()[0][u'title']) - if os.path.isfile(path): - file = mutagen.File(path) - if file is not None: - seconds = int(file.info.length) - end = UiStrings.LengthTime % \ - unicode(datetime.timedelta(seconds=seconds)) + 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: diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py index 3b01f9ac7..70128cb89 100644 --- a/openlp/core/ui/printserviceorderform.py +++ b/openlp/core/ui/printserviceorderform.py @@ -24,7 +24,6 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### import datetime -import mutagen import os from PyQt4 import QtCore, QtGui @@ -113,16 +112,9 @@ class PrintServiceOrderForm(QtGui.QDialog, Ui_PrintServiceOrderDialog): item.notes.replace(u'\n', u'
')) # Add play length of media files. if item.is_media() and self.printMetaDataCheckBox.isChecked(): - path = os.path.join(item.get_frames()[0][u'path'], - item.get_frames()[0][u'title']) - if not os.path.isfile(path): - continue - file = mutagen.File(path) - if file is not None: - length = int(file.info.length) - text += u'

%s %s

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

%s %s

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

%s

%s' % (translate('OpenLP.ServiceManager', u'Custom Service Notes:'), self.customNoteEdit.toPlainText()) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 55b745ec5..47fe44801 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -32,6 +32,7 @@ from PyQt4 import QtCore, QtGui 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__) @@ -48,9 +49,13 @@ class MediaMediaItem(MediaManagerItem): 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') @@ -120,6 +125,11 @@ 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) @@ -128,6 +138,9 @@ class MediaMediaItem(MediaManagerItem): 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: @@ -165,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 == 2: + self.MediaState = newState + self.mediaLength = self.mediaObject.totalTime()/1000 + self.mediaObject.stop() From ba8918a1eaaf001762d26eb2ce9dd015953ef403 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 14 Feb 2011 18:20:59 +0000 Subject: [PATCH 092/108] Fixes --- openlp/core/ui/themeform.py | 3 ++- openlp/core/ui/thememanager.py | 4 ++-- openlp/plugins/bibles/bibleplugin.py | 14 +++++++------- openlp/plugins/custom/customplugin.py | 14 +++++++------- openlp/plugins/images/imageplugin.py | 14 +++++++------- openlp/plugins/media/mediaplugin.py | 14 +++++++------- .../presentations/presentationplugin.py | 18 +++++++++--------- openlp/plugins/songs/songsplugin.py | 14 +++++++------- 8 files changed, 48 insertions(+), 47 deletions(-) diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index f86fa0143..ad9e80d66 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -483,7 +483,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): Background Image button pushed. """ images_filter = get_images_filter() - images_filter = '%s;;%s (*.*) (*)' % (images_filter, UiStrings.AllFiles) + images_filter = u'%s;;%s (*.*) (*)' % ( + images_filter, UiStrings.AllFiles) filename = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.ThemeForm', 'Select Image'), u'', images_filter) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 8dc895862..69028ad76 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -406,8 +406,8 @@ class ThemeManager(QtGui.QWidget): files = QtGui.QFileDialog.getOpenFileNames(self, translate('OpenLP.ThemeManager', 'Select Theme Import File'), SettingsManager.get_last_dir(self.settingsSection), - translate('OpenLP.ThemeManager', 'Theme v1 (*.theme);;' - 'Theme v2 (*.otz);;%s (*.*)') % UiStrings.AllFiles) + unicode(translate('OpenLP.ThemeManager', 'Theme v1 (*.theme);;' + 'Theme v2 (*.otz);;%s (*.*)')) % UiStrings.AllFiles) log.info(u'New Themes %s', unicode(files)) if files: for file in files: diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 63fb28b67..06fd78b60 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -130,13 +130,13 @@ class BiblePlugin(Plugin): } # Middle Header Bar tooltips = { - load: u'' - import: translate('BiblesPlugin', 'Import a Bible') - new: translate('BiblesPlugin', 'Add a new Bible') - edit: translate('BiblesPlugin', 'Edit the selected Bible') - delete: translate('BiblesPlugin', 'Delete the selected Bible') - preview: translate('BiblesPlugin', 'Preview the selected Bible') - live: translate('BiblesPlugin', 'Send the selected Bible live') + load: u'', + import: translate('BiblesPlugin', 'Import a Bible'), + new: translate('BiblesPlugin', 'Add a new Bible'), + edit: translate('BiblesPlugin', 'Edit the selected Bible'), + delete: translate('BiblesPlugin', 'Delete the selected Bible'), + preview: translate('BiblesPlugin', 'Preview the selected Bible'), + live: translate('BiblesPlugin', 'Send the selected Bible live'), service: translate('BiblesPlugin', 'Add the selected Bible to the service') } diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index 86f1b20d8..076f618bc 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -107,13 +107,13 @@ class CustomPlugin(Plugin): } # Middle Header Bar tooltips = { - load: translate('CustomsPlugin', 'Load a new Custom') - import: translate('CustomsPlugin', 'Import a Custom') - new: translate('CustomsPlugin', 'Add a new Custom') - edit: translate('CustomsPlugin', 'Edit the selected Custom') - delete: translate('CustomsPlugin', 'Delete the selected Custom') - preview: translate('CustomsPlugin', 'Preview the selected Custom') - live: translate('CustomsPlugin', 'Send the selected Custom live') + load: translate('CustomsPlugin', 'Load a new Custom'), + import: translate('CustomsPlugin', 'Import a Custom'), + new: translate('CustomsPlugin', 'Add a new Custom'), + edit: translate('CustomsPlugin', 'Edit the selected Custom'), + delete: translate('CustomsPlugin', 'Delete the selected Custom'), + preview: translate('CustomsPlugin', 'Preview the selected Custom'), + live: translate('CustomsPlugin', 'Send the selected Custom live'), service: translate('CustomsPlugin', 'Add the selected Custom to the service') } diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 59005de4f..7c04f5c7a 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -70,13 +70,13 @@ class ImagePlugin(Plugin): } # Middle Header Bar tooltips = { - load: translate('ImagePlugin', 'Load a new Image') - import: u'' - new: translate('ImagePlugin', 'Add a new Image') - edit: translate('ImagePlugin', 'Edit the selected Image') - delete: translate('ImagePlugin', 'Delete the selected Image') - preview: translate('ImagePlugin', 'Preview the selected Image') - live: translate('ImagePlugin', 'Send the selected Image live') + load: translate('ImagePlugin', 'Load a new Image'), + import: u'', + new: translate('ImagePlugin', 'Add a new Image'), + edit: translate('ImagePlugin', 'Edit the selected Image'), + delete: translate('ImagePlugin', 'Delete the selected Image'), + preview: translate('ImagePlugin', 'Preview the selected Image'), + live: translate('ImagePlugin', 'Send the selected Image live'), service: translate('ImagePlugin', 'Add the selected Image to the service') } diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 6989e91aa..68bdbe937 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -96,13 +96,13 @@ class MediaPlugin(Plugin): } # Middle Header Bar tooltips = { - load: translate('MediaPlugin', 'Load a new Media') - import: u'' - new: translate('MediaPlugin', 'Add a new Media') - edit: translate('MediaPlugin', 'Edit the selected Media') - delete: translate('MediaPlugin', 'Delete the selected Media') - preview: translate('MediaPlugin', 'Preview the selected Media') - live: translate('MediaPlugin', 'Send the selected Media live') + load: translate('MediaPlugin', 'Load a new Media'), + import: u'', + new: translate('MediaPlugin', 'Add a new Media'), + edit: translate('MediaPlugin', 'Edit the selected Media'), + delete: translate('MediaPlugin', 'Delete the selected Media'), + preview: translate('MediaPlugin', 'Preview the selected Media'), + live: translate('MediaPlugin', 'Send the selected Media live'), service: translate('MediaPlugin', 'Add the selected Media to the service') } diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index fba332eb0..7411c2f71 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -168,17 +168,17 @@ class PresentationPlugin(Plugin): } # Middle Header Bar tooltips = { - load: translate('PresentationPlugin', 'Load a new Presentation') - import: u'' - new: u'' - edit: u'' + load: translate('PresentationPlugin', 'Load a new Presentation'), + import: u'', + new: u'', + edit: u'', delete: translate('PresentationPlugin', - 'Delete the selected Presentation') + 'Delete the selected Presentation'), preview: translate('PresentationPlugin', - 'Preview the selected Presentation') + 'Preview the selected Presentation'), live: translate('PresentationPlugin', - 'Send the selected Presentation live') + 'Send the selected Presentation live'), service: translate('PresentationPlugin', 'Add the selected Presentation to the service') - } - self.setPluginUiTextStrings(tooltips) + } + self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index abeddfdae..e8866e5b0 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -229,13 +229,13 @@ class SongsPlugin(Plugin): } # Middle Header Bar tooltips = { - load: u'' - import: u'' - new: translate('SongsPlugin', 'Add a new Song') - edit: translate('SongsPlugin', 'Edit the selected Song') - delete: translate('SongsPlugin', 'Delete the selected Song') - preview: translate('SongsPlugin', 'Preview the selected Song') - live: translate('SongsPlugin', 'Send the selected Song live') + load: u'', + import: u'', + new: translate('SongsPlugin', 'Add a new Song'), + edit: translate('SongsPlugin', 'Edit the selected Song'), + delete: translate('SongsPlugin', 'Delete the selected Song'), + preview: translate('SongsPlugin', 'Preview the selected Song'), + live: translate('SongsPlugin', 'Send the selected Song live'), service: translate('SongsPlugin', 'Add the selected Song to the service') } From 329db94fd204d0e9cc7ad2889b42253aa3d09c6f Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 14 Feb 2011 18:32:46 +0000 Subject: [PATCH 093/108] Fix indent --- openlp/core/lib/serviceitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index e8e1f68f0..31852709a 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -444,7 +444,7 @@ class ServiceItem(object): unicode(datetime.timedelta(seconds=self.start_time)) if self.media_length != 0: end = UiStrings.LengthTime % \ - unicode(datetime.timedelta(seconds=self.media_length)) + unicode(datetime.timedelta(seconds=self.media_length)) if not start and not end: return None elif start and not end: From 774f9d6cdd4ff97554560ea18a3acfe0a40a69b2 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 14 Feb 2011 18:43:37 +0000 Subject: [PATCH 094/108] Fix names --- openlp/core/ui/maindisplay.py | 2 +- openlp/plugins/media/lib/mediaitem.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index cc2f2f72d..90042f80b 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -348,7 +348,7 @@ class MainDisplay(DisplayWidget): """ Start the video at a predetermined point. """ - if newState == 2: + if newState == Phonon.PlayingState: self.mediaObject.seek(self.serviceItem.start_time * 1000) def isWebLoaded(self): diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 47fe44801..cc126bbef 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -183,7 +183,7 @@ class MediaMediaItem(MediaManagerItem): """ Start the video at a predetermined point. """ - if newState == 2: + if newState == Phonon.PlayingState: self.MediaState = newState self.mediaLength = self.mediaObject.totalTime()/1000 self.mediaObject.stop() From 5e8b8eedf3c5e3ac50da4bf36c03747925fe7b10 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 14 Feb 2011 19:08:18 +0000 Subject: [PATCH 095/108] Strings not variable names\! --- openlp/core/lib/plugin.py | 16 ++++++++-------- openlp/plugins/bibles/bibleplugin.py | 16 ++++++++-------- openlp/plugins/custom/customplugin.py | 18 ++++++++++-------- openlp/plugins/images/imageplugin.py | 16 ++++++++-------- openlp/plugins/media/mediaplugin.py | 16 ++++++++-------- .../presentations/presentationplugin.py | 16 ++++++++-------- openlp/plugins/songs/songsplugin.py | 16 ++++++++-------- 7 files changed, 58 insertions(+), 56 deletions(-) diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index a525e70cc..730bb1a36 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -341,28 +341,28 @@ class Plugin(QtCore.QObject): """ ## Load Action ## self.__setNameTextString(StringContent.Load, - UiStrings.Load, tooltips[load]) + UiStrings.Load, tooltips[u'load']) ## Import Action ## self.__setNameTextString(StringContent.Import, - UiStrings.Import, tooltips[import]) + UiStrings.Import, tooltips[u'import']) ## New Action ## self.__setNameTextString(StringContent.New, - UiStrings.Add, tooltips[new]) + UiStrings.Add, tooltips[u'new']) ## Edit Action ## self.__setNameTextString(StringContent.Edit, - UiStrings.Edit, tooltips[edit]) + UiStrings.Edit, tooltips[u'edit']) ## Delete Action ## self.__setNameTextString(StringContent.Delete, - UiStrings.Delete, tooltips[delete]) + UiStrings.Delete, tooltips[u'delete']) ## Preview Action ## self.__setNameTextString(StringContent.Preview, - UiStrings.Preview, tooltips[preview]) + UiStrings.Preview, tooltips[u'preview']) ## Send Live Action ## self.__setNameTextString(StringContent.Live, - UiStrings.Live, tooltips[live]) + UiStrings.Live, tooltips[u'live']) ## Add to Service Action ## self.__setNameTextString(StringContent.Service, - UiStrings.Service, tooltips[service]) + UiStrings.Service, tooltips[u'service']) def __setNameTextString(self, name, title, tooltip): """ diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 06fd78b60..de0ea11f1 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -130,14 +130,14 @@ class BiblePlugin(Plugin): } # Middle Header Bar tooltips = { - load: u'', - import: translate('BiblesPlugin', 'Import a Bible'), - new: translate('BiblesPlugin', 'Add a new Bible'), - edit: translate('BiblesPlugin', 'Edit the selected Bible'), - delete: translate('BiblesPlugin', 'Delete the selected Bible'), - preview: translate('BiblesPlugin', 'Preview the selected Bible'), - live: translate('BiblesPlugin', 'Send the selected Bible live'), - service: translate('BiblesPlugin', + 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/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index 076f618bc..5a32d4657 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -107,14 +107,16 @@ class CustomPlugin(Plugin): } # Middle Header Bar tooltips = { - load: translate('CustomsPlugin', 'Load a new Custom'), - import: translate('CustomsPlugin', 'Import a Custom'), - new: translate('CustomsPlugin', 'Add a new Custom'), - edit: translate('CustomsPlugin', 'Edit the selected Custom'), - delete: translate('CustomsPlugin', 'Delete the selected Custom'), - preview: translate('CustomsPlugin', 'Preview the selected Custom'), - live: translate('CustomsPlugin', 'Send the selected Custom live'), - service: translate('CustomsPlugin', + 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) diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 7c04f5c7a..2cc0f1d93 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -70,14 +70,14 @@ class ImagePlugin(Plugin): } # Middle Header Bar tooltips = { - load: translate('ImagePlugin', 'Load a new Image'), - import: u'', - new: translate('ImagePlugin', 'Add a new Image'), - edit: translate('ImagePlugin', 'Edit the selected Image'), - delete: translate('ImagePlugin', 'Delete the selected Image'), - preview: translate('ImagePlugin', 'Preview the selected Image'), - live: translate('ImagePlugin', 'Send the selected Image live'), - service: translate('ImagePlugin', + 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/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 68bdbe937..ee413aa8c 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -96,14 +96,14 @@ class MediaPlugin(Plugin): } # Middle Header Bar tooltips = { - load: translate('MediaPlugin', 'Load a new Media'), - import: u'', - new: translate('MediaPlugin', 'Add a new Media'), - edit: translate('MediaPlugin', 'Edit the selected Media'), - delete: translate('MediaPlugin', 'Delete the selected Media'), - preview: translate('MediaPlugin', 'Preview the selected Media'), - live: translate('MediaPlugin', 'Send the selected Media live'), - service: translate('MediaPlugin', + 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/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 7411c2f71..ece25e363 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -168,17 +168,17 @@ class PresentationPlugin(Plugin): } # Middle Header Bar tooltips = { - load: translate('PresentationPlugin', 'Load a new Presentation'), - import: u'', - new: u'', - edit: u'', - delete: translate('PresentationPlugin', + 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'), - preview: translate('PresentationPlugin', + u'preview': translate('PresentationPlugin', 'Preview the selected Presentation'), - live: translate('PresentationPlugin', + u'live': translate('PresentationPlugin', 'Send the selected Presentation live'), - service: translate('PresentationPlugin', + u'service': translate('PresentationPlugin', 'Add the selected Presentation to the service') } self.setPluginUiTextStrings(tooltips) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index e8866e5b0..887ddb7b2 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -229,14 +229,14 @@ class SongsPlugin(Plugin): } # Middle Header Bar tooltips = { - load: u'', - import: u'', - new: translate('SongsPlugin', 'Add a new Song'), - edit: translate('SongsPlugin', 'Edit the selected Song'), - delete: translate('SongsPlugin', 'Delete the selected Song'), - preview: translate('SongsPlugin', 'Preview the selected Song'), - live: translate('SongsPlugin', 'Send the selected Song live'), - service: translate('SongsPlugin', + 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) From 3c8c87138f873979c84d047d3f39abd75d76ed16 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 14 Feb 2011 19:15:49 +0000 Subject: [PATCH 096/108] Typo --- openlp/core/ui/servicemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 04a4753e8..623c2d641 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -97,7 +97,7 @@ class ServiceManager(QtGui.QWidget): self.toolbar = OpenLPToolbar(self) self.toolbar.addToolbarButton( UiStrings.NewService, u':/general/general_new.png', - UiStrings.Create.Service, self.onNewServiceClicked) + UiStrings.CreateService, self.onNewServiceClicked) self.toolbar.addToolbarButton( UiStrings.OpenService, u':/general/general_open.png', translate('OpenLP.ServiceManager', 'Load an existing service'), From 0920586e0d2847afd194ce053bdd8eccf729d1c9 Mon Sep 17 00:00:00 2001 From: rimach Date: Mon, 14 Feb 2011 21:06:32 +0100 Subject: [PATCH 097/108] use soffice for compatibility reasons --- openlp/core/utils/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 37cbd7a63..dfef0372c 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -373,13 +373,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): """ From d9a26ae4d92a37c7b096e18e29058c00d819c384 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 14 Feb 2011 20:32:19 +0000 Subject: [PATCH 098/108] Cleanups --- openlp/core/lib/mediamanageritem.py | 1 - openlp/core/lib/serviceitem.py | 1 - openlp/core/lib/theme.py | 4 +- openlp/core/ui/settingsdialog.py | 1 - openlp/core/ui/thememanager.py | 1 - openlp/plugins/songs/forms/songimportform.py | 6 +- .../songs/forms/songmaintenanceform.py | 4 +- openlp/plugins/songs/lib/importer.py | 2 +- .../plugins/songs/lib/songshowplusimport.py | 62 +++++++++---------- 9 files changed, 38 insertions(+), 44 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index f74ba63a9..d4fdfff17 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -252,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( diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 31852709a..f9d690ba2 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -436,7 +436,6 @@ class ServiceItem(object): """ Returns the start and finish time for a media item """ - tooltip = None start = None end = None if self.start_time != 0: diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 4189452bc..225e1335c 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -178,9 +178,9 @@ class HorizontalType(object): """ Return a string representation of a horizontal type. """ - if horizontal_type == Horizontal.Right: + if horizontal_type == HorizontalType.Right: return u'right' - elif horizontal_type == Horizontal.Center: + elif horizontal_type == HorizontalType.Center: return u'center' else: return u'left' diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 99acadc14..41b6baccb 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -36,7 +36,6 @@ 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') diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 69028ad76..739de7182 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -314,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': diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 6e2a45b45..eda3d6750 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -480,7 +480,7 @@ 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';;' @@ -609,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): @@ -659,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): 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/lib/importer.py b/openlp/plugins/songs/lib/importer.py index cbe5c6922..230dcd8d0 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -68,11 +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): diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index 78c2e838d..5df36a5b1 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -42,7 +42,7 @@ CHORUS = 20 TOPIC = 29 COMMENTS = 30 VERSE_ORDER = 31 -SONG_BOOK = 35 +SONG_BOOK = 35 SONG_NUMBER = 36 CUSTOM_VERSE = 37 @@ -50,32 +50,32 @@ log = logging.getLogger(__name__) class SongShowPlusImport(SongImport): """ - The :class:`SongShowPlusImport` class provides the ability to import song + 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 + + * 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 + 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 + 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 + 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 + + 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: @@ -98,7 +98,7 @@ class SongShowPlusImport(SongImport): 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. @@ -107,38 +107,36 @@ class SongShowPlusImport(SongImport): self.import_wizard.progressBar.setMaximum(len(self.import_source)) for file in self.import_source: author = u'' - copyright = u'' self.sspVerseOrderList = [] otherCount = 0 otherList = {} file_name = os.path.split(file)[1] self.import_wizard.incrementProgressBar( - u'Importing %s' % (file_name), 0) + u'Importing %s' % (file_name), 0) songData = open(file, 'rb') while (1): - blockKey, = struct.unpack("I",songData.read(4)) + 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)) + nextBlockStarts, = struct.unpack("I", songData.read(4)) if blockKey == VERSE or blockKey == CHORUS: - null, verseNo, = struct.unpack("BB",songData.read(2)) + null, verseNo, = struct.unpack("BB", songData.read(2)) elif blockKey == CUSTOM_VERSE: - null, verseNameLength, = struct.unpack("BB", + null, verseNameLength, = struct.unpack("BB", songData.read(2)) verseName = songData.read(verseNameLength) - lengthDescriptorSize, = struct.unpack("B",songData.read(1)) + 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)) + 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)) + else: + lengthDescriptor, = struct.unpack("B", songData.read(1)) data = songData.read(lengthDescriptor) - if blockKey == TITLE: self.title = unicode(data, u'cp1252') elif blockKey == AUTHOR: @@ -146,17 +144,17 @@ class SongShowPlusImport(SongImport): for author in authors: if author.find(",") !=-1: authorParts = author.split(", ") - author = authorParts[1] + " " + authorParts[0] - self.parse_author(unicode(author, u'cp1252')) + 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'), + self.add_verse(unicode(data, u'cp1252'), "V%s" % verseNo) elif blockKey == CHORUS: - self.add_verse(unicode(data, u'cp1252'), + self.add_verse(unicode(data, u'cp1252'), "C%s" % verseNo) elif blockKey == TOPIC: self.topics.append(unicode(data, u'cp1252')) @@ -182,9 +180,9 @@ class SongShowPlusImport(SongImport): self.import_wizard.incrementProgressBar( u'Importing %s' % (file_name)) return True - + def toOpenLPVerseTag(self, verseName): - if verseName.find(" ")!=-1: + if verseName.find(" ") !=-1: verseParts = verseName.split(" ") verseType = verseParts[0] verseNumber = verseParts[1] @@ -203,7 +201,7 @@ class SongShowPlusImport(SongImport): elif verseType == "bridge": verseTag = "B" else: - if not self.otherList.has_key(verseName): + if not self.otherList.has_key(verseName): self.otherCount = self.otherCount + 1 self.otherList[verseName] = str(self.otherCount) verseTag = "O" From 5fa9ace1ce636a837f88784e995e8c3567f83d97 Mon Sep 17 00:00:00 2001 From: rimach Date: Mon, 14 Feb 2011 22:06:48 +0100 Subject: [PATCH 099/108] at output for alternate shortcut --- openlp/core/ui/shortcutlistdialog.py | 5 +++-- openlp/core/ui/shortcutlistform.py | 9 +++++++-- openlp/core/ui/slidecontroller.py | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) 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 52626f24f..073263616 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -380,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') From 847dfdaafc685d5592443d00d1273292485b3152 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 14 Feb 2011 21:07:05 +0000 Subject: [PATCH 100/108] Unused imports --- openlp/core/ui/printserviceorderform.py | 1 - openlp/plugins/bibles/bibleplugin.py | 1 - openlp/plugins/custom/customplugin.py | 1 - 3 files changed, 3 deletions(-) diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py index 70128cb89..b5160e61c 100644 --- a/openlp/core/ui/printserviceorderform.py +++ b/openlp/core/ui/printserviceorderform.py @@ -24,7 +24,6 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### import datetime -import os from PyQt4 import QtCore, QtGui diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index de0ea11f1..b992552f1 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -29,7 +29,6 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate -from openlp.core.lib.ui import UiStrings from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem log = logging.getLogger(__name__) diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index 5a32d4657..65245fc8a 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -30,7 +30,6 @@ from forms import EditCustomForm from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.db import Manager -from openlp.core.lib.ui import UiStrings from openlp.plugins.custom.lib import CustomMediaItem, CustomTab from openlp.plugins.custom.lib.db import CustomSlide, init_schema From cc96d96a6a0d8b19fa6d77deb833bad97dd6808c Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 15 Feb 2011 01:58:18 +0000 Subject: [PATCH 101/108] Fix song overwriting --- openlp/plugins/songs/forms/editsongform.py | 1 + 1 file changed, 1 insertion(+) 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'') From 32272d659948b12669e3a36b368911ed01267ef6 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Tue, 15 Feb 2011 20:36:52 +0200 Subject: [PATCH 102/108] Amalgamated OpenLP theme filters into one. --- openlp/core/ui/thememanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 739de7182..4ec11c831 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -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), - unicode(translate('OpenLP.ThemeManager', 'Theme v1 (*.theme);;' - 'Theme v2 (*.otz);;%s (*.*)')) % UiStrings.AllFiles) + unicode(translate('OpenLP.ThemeManager', + 'OpenLP Themes (*.theme *.otz);;%s (*.*)')) % UiStrings.AllFiles) log.info(u'New Themes %s', unicode(files)) if files: for file in files: From 6028e0633339ed4577593c7de7452af52dbb2514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=B5ldaru?= Date: Tue, 15 Feb 2011 20:56:40 +0200 Subject: [PATCH 103/108] filesystem encoding fix for non-ascii home dir --- openlp/core/utils/__init__.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 9db744460..69e1288d8 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -164,27 +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': if dir_type == AppLocation.DataDir: - return os.path.join(os.getenv(u'APPDATA'), u'openlp', u'data') - return os.path.join(os.getenv(u'APPDATA'), u'openlp') + 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') + return os.path.join(unicode(BaseDirectory.xdg_cache_home, + encoding), u'openlp') if dir_type == AppLocation.DataDir: - return os.path.join(os.getenv(u'HOME'), u'.openlp', u'data') - return os.path.join(os.getenv(u'HOME'), u'.openlp') + 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): """ From 8ca735fe8e27174c7c185a8daa193604c4833104 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Tue, 15 Feb 2011 21:09:07 +0200 Subject: [PATCH 104/108] Removed "All Files" as per request. --- openlp/core/ui/thememanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 4ec11c831..015e48f23 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -406,7 +406,7 @@ class ThemeManager(QtGui.QWidget): translate('OpenLP.ThemeManager', 'Select Theme Import File'), SettingsManager.get_last_dir(self.settingsSection), unicode(translate('OpenLP.ThemeManager', - 'OpenLP Themes (*.theme *.otz);;%s (*.*)')) % UiStrings.AllFiles) + 'OpenLP Themes (*.theme *.otz)'))) log.info(u'New Themes %s', unicode(files)) if files: for file in files: From ed3d67c1f1f942a9a7b4456fe0d154e753e54b7e Mon Sep 17 00:00:00 2001 From: rimach Date: Tue, 15 Feb 2011 20:37:16 +0100 Subject: [PATCH 105/108] correction for Powerpoint and PptViewer --- openlp/core/utils/__init__.py | 4 ++-- openlp/plugins/presentations/lib/powerpointcontroller.py | 6 ++++-- openlp/plugins/presentations/lib/pptviewcontroller.py | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 9b868c6cd..bbedaaf6b 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -378,9 +378,9 @@ def get_uno_command(): COMMAND = u'soffice' OPTIONS = u'-nologo -norestore -minimized -invisible -nofirststartwizard' if UNO_CONNECTION_TYPE == u'pipe': - CONNECTION = u'"-accept=pipe,name=openlp_pipe;urp;"' + CONNECTION = u'"-accept=pipe,name=openlp_pipe;urp;"' else: - CONNECTION = 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/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 65e9f35ff..eb00da255 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -147,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): """ diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index a64cd31dd..fc839195c 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -154,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): From f27e6755cc9e040852f41e8f9354e1e3f415af81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=B5ldaru?= Date: Tue, 15 Feb 2011 23:19:45 +0200 Subject: [PATCH 106/108] easislides import to work again --- openlp/plugins/songs/lib/easislidesimport.py | 43 +++++++------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/openlp/plugins/songs/lib/easislidesimport.py b/openlp/plugins/songs/lib/easislidesimport.py index 5d56af8ce..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_unicode_attribute(self.title, song.Title1, True) - self._add_unicode_attribute(self.alternate_title, song.Title2) - self._add_unicode_attribute(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_unicode_attribute(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', From 9d88cec9d36546f30cf0aa063d1907d6511fa805 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 17 Feb 2011 13:53:07 +0100 Subject: [PATCH 107/108] fixed creating theme preview image too early, fixed indents --- openlp/core/lib/__init__.py | 3 ++- openlp/core/lib/imagemanager.py | 6 ++---- openlp/core/lib/rendermanager.py | 12 ++++++------ openlp/core/ui/thememanager.py | 23 ++++++++++++----------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 5247ae938..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. diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index fb242602a..0a76ce834 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -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/rendermanager.py b/openlp/core/lib/rendermanager.py index 32a29915f..860a52b60 100644 --- a/openlp/core/lib/rendermanager.py +++ b/openlp/core/lib/rendermanager.py @@ -203,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 diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 015e48f23..78c4596e3 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -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): """ From 9e6cfcfed4c7006db976cbe3f9ecf1b6feeb02cc Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 17 Feb 2011 17:11:32 +0100 Subject: [PATCH 108/108] clear search edit, when starting the wizard again; check/uncheck button change state of visible songs --- openlp/plugins/songs/forms/songexportform.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 849a1ad1e..f331fbcb9 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -252,6 +252,7 @@ class SongExportForm(OpenLPWizard): 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) @@ -340,19 +341,21 @@ class SongExportForm(OpenLPWizard): def onUncheckButtonClicked(self): """ - The *uncheckButton* has been clicked. Set all songs unchecked. + The *uncheckButton* has been clicked. Set all visible songs unchecked. """ for row in range(self.availableListWidget.count()): item = self.availableListWidget.item(row) - item.setCheckState(QtCore.Qt.Unchecked) + if not item.isHidden(): + item.setCheckState(QtCore.Qt.Unchecked) def onCheckButtonClicked(self): """ - The *checkButton* has been clicked. Set all songs checked. + The *checkButton* has been clicked. Set all visible songs checked. """ for row in range(self.availableListWidget.count()): item = self.availableListWidget.item(row) - item.setCheckState(QtCore.Qt.Checked) + if not item.isHidden(): + item.setCheckState(QtCore.Qt.Checked) def onDirectoryButtonClicked(self): """