diff --git a/documentation/manual/source/glossary.rst b/documentation/manual/source/glossary.rst index 41a8f4ac6..6f4ebcdd6 100644 --- a/documentation/manual/source/glossary.rst +++ b/documentation/manual/source/glossary.rst @@ -48,7 +48,7 @@ Service Manger -------------- The service manager contains the media items in your service file. This is the -area from wich your media items go live, and you can also save, open, and edit +area from which your media items go live, and you can also save, open, and edit services files. .. image:: pics/servicemanager.png diff --git a/documentation/manual/source/index.rst b/documentation/manual/source/index.rst index ac29c4360..5786af1ae 100644 --- a/documentation/manual/source/index.rst +++ b/documentation/manual/source/index.rst @@ -14,6 +14,8 @@ Contents: introduction glossary dualmonitors + mediamanager + songs Indices and tables ================== diff --git a/documentation/manual/source/mediamanager.rst b/documentation/manual/source/mediamanager.rst new file mode 100644 index 000000000..2cf1d2c57 --- /dev/null +++ b/documentation/manual/source/mediamanager.rst @@ -0,0 +1,26 @@ +============= +Media Manager +============= + +Once you get your system set up for OpenLP you will be ready to add content to +your setup. This will all happen through the **Media Manager**. The +`Media Manager` contains all the bibles, songs, presentations, media, and +everything else that you will project through OpenLP. + +Enabling the Plugins +-------------------- + +You may need to enable the plugins that came with OpenLP. As you can see below +this is what the `Media Manager` looks like with all the plugins enabled. + +.. image:: pics/mediamanager.png + +To enable the plugins navigate to :menuselection:`Settings --> Plugins` or +press `F7`. You will then want to click on the plugin to the left that you want +to enable and select **active** from the drop down box to the right. + +.. image:: pics/plugins.png + + +Now you should be ready to add content to OpenLP check out the section of this +guide on the individual plugins. diff --git a/documentation/manual/source/pics/finishedimport.png b/documentation/manual/source/pics/finishedimport.png new file mode 100644 index 000000000..d49876ec9 Binary files /dev/null and b/documentation/manual/source/pics/finishedimport.png differ diff --git a/documentation/manual/source/pics/plugins.png b/documentation/manual/source/pics/plugins.png new file mode 100644 index 000000000..b6810242b Binary files /dev/null and b/documentation/manual/source/pics/plugins.png differ diff --git a/documentation/manual/source/pics/selectsongs.png b/documentation/manual/source/pics/selectsongs.png new file mode 100644 index 000000000..20df3ba2a Binary files /dev/null and b/documentation/manual/source/pics/selectsongs.png differ diff --git a/documentation/manual/source/pics/songimporter.png b/documentation/manual/source/pics/songimporter.png new file mode 100644 index 000000000..96c39ea38 Binary files /dev/null and b/documentation/manual/source/pics/songimporter.png differ diff --git a/documentation/manual/source/pics/songimporterchoices.png b/documentation/manual/source/pics/songimporterchoices.png new file mode 100644 index 000000000..5c458838e Binary files /dev/null and b/documentation/manual/source/pics/songimporterchoices.png differ diff --git a/documentation/manual/source/pics/songselectlyrics.png b/documentation/manual/source/pics/songselectlyrics.png new file mode 100644 index 000000000..2e3d92f0d Binary files /dev/null and b/documentation/manual/source/pics/songselectlyrics.png differ diff --git a/documentation/manual/source/pics/songselectsongsearch.png b/documentation/manual/source/pics/songselectsongsearch.png new file mode 100644 index 000000000..de0ea12ca Binary files /dev/null and b/documentation/manual/source/pics/songselectsongsearch.png differ diff --git a/documentation/manual/source/pics/vistadisplaysettings.png b/documentation/manual/source/pics/vistadisplaysettings.png old mode 100755 new mode 100644 diff --git a/documentation/manual/source/pics/vistapersonalize.png b/documentation/manual/source/pics/vistapersonalize.png old mode 100755 new mode 100644 diff --git a/documentation/manual/source/pics/winsevendisplay.png b/documentation/manual/source/pics/winsevendisplay.png old mode 100755 new mode 100644 diff --git a/documentation/manual/source/pics/winsevenresolution.png b/documentation/manual/source/pics/winsevenresolution.png old mode 100755 new mode 100644 diff --git a/documentation/manual/source/pics/xpdisplaysettings.png b/documentation/manual/source/pics/xpdisplaysettings.png index eb7a8921c..e1ec66c6f 100644 Binary files a/documentation/manual/source/pics/xpdisplaysettings.png and b/documentation/manual/source/pics/xpdisplaysettings.png differ diff --git a/documentation/manual/source/songs.rst b/documentation/manual/source/songs.rst new file mode 100644 index 000000000..678a6206c --- /dev/null +++ b/documentation/manual/source/songs.rst @@ -0,0 +1,100 @@ +===== +Songs +===== + +Managing your songs in OpenLP is a relatively simple process. There are also +converters provided to get data from other formats into OpenLP. + +Song Importer +============= + +If you are using an earlier version of OpenLP or come from another software +package, you may be able to convert your existing database to work in OpenLP +2.0. To access the Song Importer :menuselection:`File --> Import --> Song`. +You will then see the Song Importer window, then click :guilabel:`Next`. + +.. image:: pics/songimporter.png + +After choosing :guilabel:`Next` you can then select from the various types of +software that OpenLP will convert songs from. + +.. image:: pics/songimporterchoices.png + +Then click on the file folder icon to choose the file of the song database you +want to import. See the following sections for information on the different +formats that OpenLP will import. + +Importing from OpenLP Version 1 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Converting from OpenLP Version 1 is a pretty simple process. You will first +need to locate your version 1 database file. + +Windows XP:: + + C:\Documents and Settings\All Users\Application Data\openlp.org\Data\songs.olp + +Windows Vista / Windows 7:: + + C:\ProgramData\openlp.org\Data\songs.olp + +After clicking :guilabel:`Next` your conversion should be complete. + +.. image:: pics/finishedimport.png + +Then press :guilabel:`Finish` and you should now be ready to use your OpenLP +version one songs. + +Importing from OpenSong +^^^^^^^^^^^^^^^^^^^^^^^ + +Converting from OpenSong you will need to locate your songs database. In the +later versions of OpenSong you are asked to define the location of this. The +songs will be located in a folder named :guilabel:`Songs`. This folder should +contain files with all your songs in them without a file extension. (file.xxx). +When you have located this folder you will then need to select the songs from +the folder. + +.. image:: pics/selectsongs.png + +On most operating systems to select all the songs, first select the first song +in the lest then press shift and select the last song in the list. After this +press :guilabel:`Next` and you should see that your import has been successful. + +.. image:: pics/finishedimport.png + +Press :guilabel:`Finish` and you will now be ready to use your songs imported +from OpenSong. + +Importing from CCLI Song Select +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To import from CCLI Song Select you must be a CCLI Subscriber and also a +subscriber of the Song Select service. For info on that go to: +http://www.ccli.com + +The first step for importing from CCLI Song Select is to log into your account. +Then search for your desired song. For this example we will be adding the song +"Amazing Grace". + +.. image:: pics/songselectsongsearch.png + +For the song you are searching for select `lyrics` This should take you to a +page displaying the lyrics and copyright info for your song. + +.. image:: pics/songselectlyrics.png + +Next, hover over the :guilabel:`Lyrics` menu from the upper right corner. Then +choose either the .txt or .usr file. You will then be asked to chose a download +location if your browser does not automatically select that for you. Select +this file from the OpenLP import window and then click :guilabel:`Next` You can +also select multiple songs for import at once on most operating systems by +selecting the first item in the list then holding shift select the last item in +the list. When finished you should see that your import has completed. + +.. image:: pics/finishedimport.png + +Press :guilabel:`Finish` and you will now be ready to use your songs imported +from CCLI SongSelect. + + diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 1ad17a039..b6946a628 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -303,6 +303,8 @@ def expand_tags(text): text = text.replace(tag[u'end tag'], tag[u'end html']) return text +from theme import ThemeLevel, ThemeXML, BackgroundGradientType, BackgroundType, \ + HorizontalType, VerticalType from spelltextedit import SpellTextEdit from eventreceiver import Receiver from imagemanager import ImageManager @@ -317,7 +319,6 @@ from htmlbuilder import build_html, build_lyrics_format_css, \ build_lyrics_outline_css from toolbar import OpenLPToolbar from dockwidget import OpenLPDockWidget -from theme import ThemeLevel, ThemeXML from renderer import Renderer from rendermanager import RenderManager from mediamanageritem import MediaManagerItem diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index e59a9737b..0dc138abc 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -158,6 +158,27 @@ class Manager(object): log.exception(u'Object save failed') return False + def save_objects(self, object_list, commit=True): + """ + Save a list of objects to the database + + ``object_list`` + The list of objects to save + + ``commit`` + Commit the session with this object + """ + try: + self.session.add_all(object_list) + if commit: + self.session.commit() + self.is_dirty = True + return True + except InvalidRequestError: + self.session.rollback() + log.exception(u'Object list save failed') + return False + def get_object(self, object_class, key=None): """ Return the details of an object @@ -207,6 +228,22 @@ class Manager(object): return query.order_by(order_by_ref).all() return query.all() + def get_object_count(self, object_class, filter_clause=None): + """ + Returns a count of the number of objects in the database. + + ``object_class`` + The type of objects to return. + + ``filter_clause`` + The filter governing selection of objects to return. Defaults to + None. + """ + query = self.session.query(object_class) + if filter_clause is not None: + query = query.filter(filter_clause) + return query.count() + def delete_object(self, object_class, key): """ Delete an object from the database diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index dc3a48565..b2f46e655 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -25,8 +25,11 @@ ############################################################################### import logging + from PyQt4 import QtWebKit +from openlp.core.lib import BackgroundType, BackgroundGradientType + log = logging.getLogger(__name__) HTMLSRC = u""" @@ -368,16 +371,32 @@ def build_background_css(item, width, height): theme = item.themedata background = u'background-color: black' if theme: - if theme.background_type == u'solid': + if theme.background_type == BackgroundType.to_string(BackgroundType.Solid): background = u'background-color: %s' % theme.background_color else: - if theme.background_direction == u'horizontal': + if theme.background_direction == BackgroundGradientType.to_string \ + (BackgroundGradientType.Horizontal): background = \ u'background: ' \ u'-webkit-gradient(linear, left top, left bottom, ' \ 'from(%s), to(%s))' % (theme.background_start_color, theme.background_end_color) - elif theme.background_direction == u'vertical': + elif theme.background_direction == BackgroundGradientType.to_string \ + (BackgroundGradientType.LeftTop): + background = \ + u'background: ' \ + u'-webkit-gradient(linear, left top, right bottom, ' \ + 'from(%s), to(%s))' % (theme.background_start_color, + theme.background_end_color) + elif theme.background_direction == BackgroundGradientType.to_string \ + (BackgroundGradientType.LeftBottom): + background = \ + u'background: ' \ + u'-webkit-gradient(linear, left bottom, right top, ' \ + 'from(%s), to(%s))' % (theme.background_start_color, + theme.background_end_color) + elif theme.background_direction == BackgroundGradientType.to_string \ + (BackgroundGradientType.Vertical): background = \ u'background: -webkit-gradient(linear, left top, ' \ u'right top, from(%s), to(%s))' % \ @@ -452,17 +471,17 @@ def build_lyrics_css(item, webkitvers): lyricsmain += build_lyrics_outline_css(theme) else: outline = build_lyrics_outline_css(theme) - if theme.display_shadow: - if theme.display_outline and webkitvers < 534.3: + if theme.font_main_shadow: + if theme.font_main_outline and webkitvers < 534.3: shadow = u'padding-left: %spx; padding-top: %spx;' % \ - (int(theme.display_shadow_size) + - (int(theme.display_outline_size) * 2), - theme.display_shadow_size) + (int(theme.font_main_shadow_size) + + (int(theme.font_main_outline_size) * 2), + theme.font_main_shadow_size) shadow += build_lyrics_outline_css(theme, True) else: lyricsmain += u' text-shadow: %s %spx %spx;' % \ - (theme.display_shadow_color, theme.display_shadow_size, - theme.display_shadow_size) + (theme.font_main_shadow_color, theme.font_main_shadow_size, + theme.font_main_shadow_size) lyrics_css = style % (lyricstable, lyrics, lyricsmain, outline, shadow) return lyrics_css @@ -477,14 +496,14 @@ def build_lyrics_outline_css(theme, is_shadow=False): `is_shadow` If true, use the shadow colors instead """ - if theme.display_outline: - size = float(theme.display_outline_size) / 16 + if theme.font_main_outline: + size = float(theme.font_main_outline_size) / 16 if is_shadow: - fill_color = theme.display_shadow_color - outline_color = theme.display_shadow_color + fill_color = theme.font_main_shadow_color + outline_color = theme.font_main_shadow_color else: fill_color = theme.font_main_color - outline_color = theme.display_outline_color + outline_color = theme.font_main_outline_color return u' -webkit-text-stroke: %sem %s; ' \ u'-webkit-text-fill-color: %s; ' % (size, outline_color, fill_color) else: @@ -517,23 +536,23 @@ def build_lyrics_format_css(theme, width, height): valign = u'middle' else: valign = u'top' - if theme.display_outline: - left_margin = int(theme.display_outline_size) * 2 + if theme.font_main_outline: + left_margin = int(theme.font_main_outline_size) * 2 else: left_margin = 0 lyrics = u'white-space:pre-wrap; word-wrap: break-word; ' \ 'text-align: %s; vertical-align: %s; font-family: %s; ' \ 'font-size: %spt; color: %s; line-height: %d%%; margin:0;' \ 'padding:0; padding-left:%spx; width: %spx; height: %spx; ' % \ - (align, valign, theme.font_main_name, theme.font_main_proportion, + (align, valign, theme.font_main_name, theme.font_main_size, theme.font_main_color, 100 + int(theme.font_main_line_adjustment), left_margin, width, height) - if theme.display_outline: + if theme.font_main_outline: if webkit_version() < 534.3: lyrics += u' letter-spacing: 1px;' if theme.font_main_italics: lyrics += u' font-style:italic; ' - if theme.font_main_weight == u'Bold': + if theme.font_main_bold: lyrics += u' font-weight:bold; ' return lyrics @@ -553,7 +572,7 @@ def build_lyrics_html(item, webkitvers): # display:table/display:table-cell are required for each lyric block. lyrics = u'' theme = item.themedata - if webkitvers < 534.4 and theme and theme.display_outline: + if webkitvers < 534.4 and theme and theme.font_main_outline: lyrics += u'
' \ u'
' @@ -589,7 +608,7 @@ def build_footer_css(item, height): bottom = height - int(item.footer.y()) - int(item.footer.height()) lyrics_html = style % (item.footer.x(), bottom, item.footer.width(), theme.font_footer_name, - theme.font_footer_proportion, theme.font_footer_color) + theme.font_footer_size, theme.font_footer_color) return lyrics_html def build_alert_css(alertTab, width): diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 2d563962e..3eae97238 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -514,7 +514,7 @@ class MediaManagerItem(QtGui.QWidget): self.parent.serviceManager.addServiceItem(service_item, replace=True) else: - #Turn off the remote edit update message indicator + # Turn off the remote edit update message indicator QtGui.QMessageBox.information(self, translate('OpenLP.MediaManagerItem', 'Invalid Service Item'), diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index f9a768205..68839f16d 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -32,7 +32,7 @@ import logging from PyQt4 import QtWebKit from openlp.core.lib import expand_tags, build_lyrics_format_css, \ - build_lyrics_outline_css + build_lyrics_outline_css, Receiver log = logging.getLogger(__name__) @@ -77,9 +77,9 @@ class Renderer(object): self._rect_footer = rect_footer self.page_width = self._rect.width() self.page_height = self._rect.height() - if self._theme.display_shadow: - self.page_width -= int(self._theme.display_shadow_size) - self.page_height -= int(self._theme.display_shadow_size) + if self._theme.font_main_shadow: + self.page_width -= int(self._theme.font_main_shadow_size) + self.page_height -= int(self._theme.font_main_shadow_size) self.web = QtWebKit.QWebView() self.web.setVisible(False) self.web.resize(self.page_width, self.page_height) @@ -92,13 +92,20 @@ class Renderer(object): (build_lyrics_format_css(self._theme, self.page_width, self.page_height), build_lyrics_outline_css(self._theme)) - def format_slide(self, words, line_break): + def format_slide(self, words, line_break, force_page=False): """ Figure out how much text can appear on a slide, using the current theme settings. ``words`` The words to be fitted on the slide. + + ``line_break`` + Add line endings after each line of text used for bibles. + + ``force_page`` + Flag to tell message lines in page. + """ log.debug(u'format_slide - Start') line_end = u'' @@ -114,13 +121,19 @@ class Renderer(object): formatted = [] html_text = u'' styled_text = u'' + line_count = 0 for line in text: + if line_count != -1: + line_count += 1 styled_line = expand_tags(line) + line_end styled_text += styled_line html = self.page_shell + styled_text + u'' self.web.setHtml(html) # Text too long so go to next page if self.web_frame.contentsSize().height() > self.page_height: + if force_page and line_count > 0: + Receiver.send_message(u'theme_line_count', line_count) + line_count = -1 if html_text.endswith(u'
'): html_text = html_text[:len(html_text)-4] formatted.append(html_text) diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py index bf561b4b3..81cde12a0 100644 --- a/openlp/core/lib/rendermanager.py +++ b/openlp/core/lib/rendermanager.py @@ -67,8 +67,9 @@ class RenderManager(object): self.service_theme = u'' self.theme_level = u'' self.override_background = None - self.themedata = None + self.theme_data = None self.alertTab = None + self.force_page = False def update_display(self): """ @@ -80,7 +81,7 @@ class RenderManager(object): self.display.imageManager = self.image_manager self.display.setup() self.renderer.bg_frame = None - self.themedata = None + self.theme_data = None self.image_manager.update_display(self.width, self.height) def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global): @@ -99,7 +100,7 @@ class RenderManager(object): self.theme_level = theme_level self.global_theme_data = \ self.theme_manager.getThemeData(self.global_theme) - self.themedata = None + self.theme_data = None def set_service_theme(self, service_theme): """ @@ -109,7 +110,7 @@ class RenderManager(object): The service-level theme to be set. """ self.service_theme = service_theme - self.themedata = None + self.theme_data = None def set_override_theme(self, theme, overrideLevels=False): """ @@ -146,19 +147,19 @@ class RenderManager(object): self.theme = self.service_theme else: self.theme = self.global_theme - if self.theme != self.renderer.theme_name or self.themedata is None \ + if self.theme != self.renderer.theme_name or self.theme_data is None \ or overrideLevels: log.debug(u'theme is now %s', self.theme) # Force the theme to be the one passed in. if overrideLevels: - self.themedata = theme + self.theme_data = theme else: - self.themedata = self.theme_manager.getThemeData(self.theme) + self.theme_data = self.theme_manager.getThemeData(self.theme) self.calculate_default(self.screens.current[u'size']) - self.renderer.set_theme(self.themedata) - self.build_text_rectangle(self.themedata) - self.image_manager.add_image(self.themedata.theme_name, - self.themedata.background_filename) + self.renderer.set_theme(self.theme_data) + self.build_text_rectangle(self.theme_data) + self.image_manager.add_image(self.theme_data.theme_name, + self.theme_data.background_filename) return self.renderer._rect, self.renderer._rect_footer def build_text_rectangle(self, theme): @@ -187,14 +188,19 @@ class RenderManager(object): theme.font_footer_height - 1) self.renderer.set_text_rectangle(main_rect, footer_rect) - def generate_preview(self, themedata): + def generate_preview(self, theme_data, force_page=False): """ Generate a preview of a theme. - ``themedata`` + ``theme_data`` The theme to generated a preview for. + + ``force_page`` + Flag to tell message lines per page need to be generated. """ log.debug(u'generate preview') + # save value for use in format_slide + self.force_page = force_page # set the default image size for previews self.calculate_default(self.screens.preview[u'size']) verse = u'The Lord said to {r}Noah{/r}: \n' \ @@ -204,23 +210,27 @@ class RenderManager(object): 'Get those children out of the muddy, muddy \n' \ '{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \ 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n' + # make big page for theme edit dialog to get line count + if self.force_page: + verse = verse + verse + verse footer = [] footer.append(u'Arky Arky (Unknown)' ) footer.append(u'Public Domain') footer.append(u'CCLI 123456') # build a service item to generate preview serviceItem = ServiceItem() - serviceItem.theme = themedata + serviceItem.theme = theme_data serviceItem.add_from_text(u'', verse, footer) serviceItem.render_manager = self serviceItem.raw_footer = footer serviceItem.render(True) - self.display.buildHtml(serviceItem) - raw_html = serviceItem.get_rendered_frame(0) - preview = self.display.text(raw_html) - # Reset the real screen size for subsequent render requests - self.calculate_default(self.screens.current[u'size']) - return preview + if not self.force_page: + self.display.buildHtml(serviceItem) + raw_html = serviceItem.get_rendered_frame(0) + preview = self.display.text(raw_html) + # Reset the real screen size for subsequent render requests + self.calculate_default(self.screens.current[u'size']) + return preview def format_slide(self, words, line_break): """ @@ -228,9 +238,12 @@ class RenderManager(object): ``words`` The words to go on the slides. + + ``line_break`` + Add line endings after each line of text used for bibles. """ log.debug(u'format slide') - return self.renderer.format_slide(words, line_break) + return self.renderer.format_slide(words, line_break, self.force_page) def calculate_default(self, screen): """ diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index ab57e4328..0cbc34de5 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -100,6 +100,7 @@ class ServiceItem(object): self.bg_image_bytes = None self.search_string = u'' self.data_string = u'' + self.edit_id = None self._new_item() def _new_item(self): diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index fcd3c60ab..f211048e3 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -28,52 +28,56 @@ Provide the theme XML and handling functions for OpenLP v2 themes. """ import os import re +import logging from xml.dom.minidom import Document from xml.etree.ElementTree import ElementTree, XML +from lxml import etree, objectify from openlp.core.lib import str_to_bool +log = logging.getLogger(__name__) + BLANK_THEME_XML = \ ''' - BlankStyle - - - #000000 + + + - + #000000 #000000 vertical - - + + #000000 Arial - #000000 - 30 - Normal + #FFFFFF + 30 + False False 0 + True + False Arial - #000000 - 12 - Normal + #FFFFFF + 12 + False False 0 + True + False - True - False 0 0 - 0 False @@ -87,10 +91,76 @@ class ThemeLevel(object): Service = 2 Song = 3 +class BackgroundType(object): + Solid = 0 + Gradient = 1 + Image = 2 + + @staticmethod + def to_string(type): + if type == BackgroundType.Solid: + return u'solid' + elif type == BackgroundType.Gradient: + return u'gradient' + elif type == BackgroundType.Image: + return u'image' + + @staticmethod + def from_string(type_string): + if type_string == u'solid': + return BackgroundType.Solid + elif type_string == u'gradient': + return BackgroundType.Gradient + elif type_string == u'image': + return BackgroundType.Image + +class BackgroundGradientType(object): + Horizontal = 0 + Vertical = 1 + Circular = 2 + LeftTop = 3 + LeftBottom = 4 + + @staticmethod + def to_string(type): + if type == BackgroundGradientType.Horizontal: + return u'horizontal' + elif type == BackgroundGradientType.Vertical: + return u'vertical' + elif type == BackgroundGradientType.Circular: + return u'circular' + elif type == BackgroundGradientType.LeftTop: + return u'leftTop' + elif type == BackgroundGradientType.LeftBottom: + return u'leftBottom' + + @staticmethod + def from_string(type_string): + if type_string == u'horizontal': + return BackgroundGradientType.Horizontal + elif type_string == u'vertical': + return BackgroundGradientType.Vertical + elif type_string == u'circular': + return BackgroundGradientType.Circular + elif type_string == u'leftTop': + return BackgroundGradientType.LeftTop + elif type_string == u'leftBottom': + return BackgroundGradientType.LeftBottom + +class HorizontalType(object): + Left = 0 + Center = 1 + Right = 2 + +class VerticalType(object): + Top = 0 + Middle = 1 + Bottom = 2 + boolean_list = [u'italics', u'override', u'outline', u'shadow', u'slide_transition'] -integer_list = [u'proportion', u'line_adjustment', u'x', u'height', u'y', +integer_list = [u'size', u'line_adjustment', u'x', u'height', u'y', u'width', u'shadow_size', u'outline_size', u'horizontal_align', u'vertical_align', u'wrap_style'] @@ -104,6 +174,7 @@ class ThemeXML(object): """ # Create the minidom document self.theme_xml = Document() + self.parse_xml(BLANK_THEME_XML) def extend_image_filename(self, path): """ @@ -112,19 +183,21 @@ class ThemeXML(object): ``path`` The path name to be added. """ - if self.background_filename and path: - self.theme_name = self.theme_name.strip() - self.background_filename = self.background_filename.strip() - self.background_filename = os.path.join(path, self.theme_name, - self.background_filename) + if self.background_type == u'image': + if self.background_filename and path: + self.theme_name = self.theme_name.strip() + self.background_filename = self.background_filename.strip() + self.background_filename = os.path.join(path, self.theme_name, + self.background_filename) - def new_document(self, name): + def _new_document(self, name): """ Create a new theme XML document. """ + self.theme_xml = Document() self.theme = self.theme_xml.createElement(u'theme') self.theme_xml.appendChild(self.theme) - self.theme.setAttribute(u'version', u'1.0') + self.theme.setAttribute(u'version', u'2.0') self.name = self.theme_xml.createElement(u'name') text_node = self.theme_xml.createTextNode(name) self.name.appendChild(text_node) @@ -146,10 +219,9 @@ class ThemeXML(object): The color of the background. """ background = self.theme_xml.createElement(u'background') - background.setAttribute(u'mode', u'opaque') background.setAttribute(u'type', u'solid') self.theme.appendChild(background) - self.child_element(background, u'color', bkcolor) + self.child_element(background, u'color', unicode(bkcolor)) def add_background_gradient(self, startcolor, endcolor, direction): """ @@ -165,15 +237,14 @@ class ThemeXML(object): The direction of the gradient. """ background = self.theme_xml.createElement(u'background') - background.setAttribute(u'mode', u'opaque') background.setAttribute(u'type', u'gradient') self.theme.appendChild(background) # Create startColor element - self.child_element(background, u'startColor', startcolor) + self.child_element(background, u'startColor', unicode(startcolor)) # Create endColor element - self.child_element(background, u'endColor', endcolor) + self.child_element(background, u'endColor', unicode(endcolor)) # Create direction element - self.child_element(background, u'direction', direction) + self.child_element(background, u'direction', unicode(direction)) def add_background_image(self, filename): """ @@ -183,15 +254,15 @@ class ThemeXML(object): The file name of the image. """ background = self.theme_xml.createElement(u'background') - background.setAttribute(u'mode', u'opaque') background.setAttribute(u'type', u'image') self.theme.appendChild(background) - #Create Filename element + # Create Filename element self.child_element(background, u'filename', filename) - def add_font(self, name, color, proportion, override, fonttype=u'main', - weight=u'Normal', italics=u'False', line_adjustment=0, - xpos=0, ypos=0, width=0, height=0): + def add_font(self, name, color, size, override, fonttype=u'main', + bold=u'False', italics=u'False', line_adjustment=0, + xpos=0, ypos=0, width=0, height=0 , outline=u'False', outline_color=u'#ffffff', + outline_pixel=2, shadow=u'False', shadow_color=u'#ffffff', shadow_pixel=5): """ Add a Font. @@ -201,7 +272,7 @@ class ThemeXML(object): ``color`` The colour of the font. - ``proportion`` + ``size`` The size of the font. ``override`` @@ -227,45 +298,6 @@ class ThemeXML(object): ``height`` The height of the text block. - """ - background = self.theme_xml.createElement(u'font') - background.setAttribute(u'type', fonttype) - self.theme.appendChild(background) - #Create Font name element - self.child_element(background, u'name', name) - #Create Font color element - self.child_element(background, u'color', color) - #Create Proportion name element - self.child_element(background, u'proportion', proportion) - #Create weight name element - self.child_element(background, u'weight', weight) - #Create italics name element - self.child_element(background, u'italics', italics) - #Create indentation name element - self.child_element( - background, u'line_adjustment', unicode(line_adjustment)) - - #Create Location element - element = self.theme_xml.createElement(u'location') - element.setAttribute(u'override', override) - if override == u'True': - element.setAttribute(u'x', xpos) - element.setAttribute(u'y', ypos) - element.setAttribute(u'width', width) - element.setAttribute(u'height', height) - background.appendChild(element) - - def add_display(self, shadow, shadow_color, outline, outline_color, - horizontal, vertical, wrap, transition, shadow_pixel=5, - outline_pixel=2): - """ - Add a Display options. - - ``shadow`` - Whether or not to show a shadow. - - ``shadow_color`` - The colour of the shadow. ``outline`` Whether or not to show an outline. @@ -273,53 +305,88 @@ class ThemeXML(object): ``outline_color`` The colour of the outline. + ``outline_size`` + How big the Shadow is + + ``shadow`` + Whether or not to show a shadow. + + ``shadow_color`` + The colour of the shadow. + + ``shadow_size`` + How big the Shadow is + + + """ + background = self.theme_xml.createElement(u'font') + background.setAttribute(u'type', fonttype) + self.theme.appendChild(background) + # Create Font name element + self.child_element(background, u'name', name) + # Create Font color element + self.child_element(background, u'color', color) + # Create Proportion name element + self.child_element(background, u'size', unicode(size)) + # Create weight name element + self.child_element(background, u'bold', unicode(bold)) + # Create italics name element + self.child_element(background, u'italics', unicode(italics)) + # Create indentation name element + self.child_element( + background, u'line_adjustment', unicode(line_adjustment)) + # Create Location element + element = self.theme_xml.createElement(u'location') + element.setAttribute(u'override', unicode(override)) + element.setAttribute(u'x', unicode(xpos)) + element.setAttribute(u'y', unicode(ypos)) + element.setAttribute(u'width', unicode(width)) + element.setAttribute(u'height', unicode(height)) + background.appendChild(element) + # Shadow + element = self.theme_xml.createElement(u'shadow') + element.setAttribute(u'shadowColor', unicode(shadow_color)) + element.setAttribute(u'shadowSize', unicode(shadow_pixel)) + value = self.theme_xml.createTextNode(unicode(shadow)) + element.appendChild(value) + background.appendChild(element) + # Outline + element = self.theme_xml.createElement(u'outline') + element.setAttribute(u'outlineColor', unicode(outline_color)) + element.setAttribute(u'outlineSize', unicode(outline_pixel)) + value = self.theme_xml.createTextNode(unicode(outline)) + element.appendChild(value) + background.appendChild(element) + + def add_display(self, horizontal, vertical, transition): + """ + Add a Display options. + ``horizontal`` The horizontal alignment of the text. ``vertical`` The vertical alignment of the text. - ``wrap`` - Wrap style. - ``transition`` Whether the slide transition is active. """ background = self.theme_xml.createElement(u'display') self.theme.appendChild(background) - # Shadow - element = self.theme_xml.createElement(u'shadow') - element.setAttribute(u'color', shadow_color) - element.setAttribute(u'size', unicode(shadow_pixel)) - value = self.theme_xml.createTextNode(shadow) - element.appendChild(value) - background.appendChild(element) - # Outline - element = self.theme_xml.createElement(u'outline') - element.setAttribute(u'color', outline_color) - element.setAttribute(u'size', unicode(outline_pixel)) - value = self.theme_xml.createTextNode(outline) - element.appendChild(value) - background.appendChild(element) # Horizontal alignment element = self.theme_xml.createElement(u'horizontalAlign') - value = self.theme_xml.createTextNode(horizontal) + value = self.theme_xml.createTextNode(unicode(horizontal)) element.appendChild(value) background.appendChild(element) # Vertical alignment element = self.theme_xml.createElement(u'verticalAlign') - value = self.theme_xml.createTextNode(vertical) - element.appendChild(value) - background.appendChild(element) - # Wrap style - element = self.theme_xml.createElement(u'wrapStyle') - value = self.theme_xml.createTextNode(wrap) + value = self.theme_xml.createTextNode(unicode(vertical)) element.appendChild(value) background.appendChild(element) # Slide Transition element = self.theme_xml.createElement(u'slideTransition') - value = self.theme_xml.createTextNode(transition) + value = self.theme_xml.createTextNode(unicode(transition)) element.appendChild(value) background.appendChild(element) @@ -342,12 +409,14 @@ class ThemeXML(object): """ Print out the XML string. """ + self._build_xml_from_attrs() return self.theme_xml.toxml(u'utf-8').decode(u'utf-8') def extract_formatted_xml(self): """ Pull out the XML string formatted for human consumption """ + self._build_xml_from_attrs() return self.theme_xml.toprettyxml(indent=u' ', newl=u'\n', encoding=u'utf-8') @@ -358,8 +427,7 @@ class ThemeXML(object): ``xml`` The XML string to parse. """ - self.parse_xml(BLANK_THEME_XML) - self.parse_xml(xml) + self.parse_xml(unicode(xml)) def parse_xml(self, xml): """ @@ -368,51 +436,95 @@ class ThemeXML(object): ``xml`` The XML string to parse. """ - theme_xml = ElementTree(element=XML(xml.encode(u'ascii', - u'xmlcharrefreplace'))) + # remove encoding string + line = xml.find(u'?>') + if line: + xml = xml[line + 2:] + try: + theme_xml = objectify.fromstring(xml) + except etree.XMLSyntaxError: + log.exception(u'Invalid xml %s', xml) + return xml_iter = theme_xml.getiterator() - master = u'' for element in xml_iter: - if not isinstance(element.text, unicode): - element.text = unicode(str(element.text), u'utf-8') - if element.getchildren(): - master = element.tag + u'_' + parent = element.getparent() + master = u'' + if parent is not None: + if element.getparent().tag == u'font': + master = element.getparent().tag + u'_' + \ + element.getparent().attrib[u'type'] + # set up Outline and Shadow Tags and move to font_main + if element.getparent().tag == u'display': + if element.tag.startswith(u'shadow') or \ + element.tag.startswith(u'outline'): + self._create_attr(u'font_main', element.tag, element.text) + master = element.getparent().tag + if element.getparent().tag == u'background': + master = element.getparent().tag + if element.getparent().attrib: + for attr in element.getparent().attrib: + self._create_attr(master, attr, \ + element.getparent().attrib[attr]) + if master: + self._create_attr(master, element.tag, element.text) + if element.attrib: + for attr in element.attrib: + base_element = attr + # correction for the shadow and outline tags + if element.tag == u'shadow' or element.tag == u'outline': + if not attr.startswith(element.tag): + base_element = element.tag + u'_' + attr + self._create_attr(master, base_element, + element.attrib[attr]) else: - # background transparent tags have no children so special case - if element.tag == u'background': - for e in element.attrib.iteritems(): - self._create_attr(element.tag , e[0], e[1]) - if element.attrib: - for e in element.attrib.iteritems(): - if master == u'font_' and e[0] == u'type': - master += e[1] + u'_' - elif master == u'display_' and (element.tag == u'shadow' - or element.tag == u'outline'): - self._create_attr(master, element.tag, element.text) - self._create_attr(master, element.tag + u'_'+ e[0], - e[1]) - else: - field = master + e[0] - self._create_attr(master, e[0], e[1]) + if element.tag == u'name': + self._create_attr(u'theme', element.tag, element.text) + + def _translate_tags(self, master, element, value): + """ + Clean up XML removing and redefining tags + """ + master = master.strip().lstrip() + element = element.strip().lstrip() + value = unicode(value).strip().lstrip() + if master == u'display': + if element == u'wrapStyle': + return True, None, None, None + if element.startswith(u'shadow') or element.startswith(u'outline'): + master = u'font_main' + # fix bold font + if element == u'weight': + element = u'bold' + if value == u'Normal': + value = False else: - if element.tag: - element.text = element.text.strip().lstrip() - self._create_attr(master , element.tag, element.text) + value = True + if element == u'proportion': + element = u'size' + return False, master, element, value def _create_attr(self, master , element, value): """ Create the attributes with the correct data types and name format """ + reject, master, element, value = \ + self._translate_tags(master, element, value) + if reject: + return field = self._de_hump(element) + tag = master + u'_' + field if field in boolean_list: - setattr(self, master + field, str_to_bool(value)) + setattr(self, tag, str_to_bool(value)) elif field in integer_list: - setattr(self, master + field, int(value)) + setattr(self, tag, int(value)) else: + # make string value unicode + if not isinstance(value, unicode): + value = unicode(str(value), u'utf-8') # None means an empty string so lets have one. if value == u'None': value = u'' - setattr(self, master + field, unicode(value)) + setattr(self, tag, unicode(value).strip().lstrip()) def __str__(self): """ @@ -431,3 +543,58 @@ class ThemeXML(object): s1 = re.sub(u'(.)([A-Z][a-z]+)', r'\1_\2', name) return re.sub(u'([a-z0-9])([A-Z])', r'\1_\2', s1).lower() + def _build_xml_from_attrs(self): + """ + Build the XML from the varables in the object + """ + self._new_document(self.theme_name) + if self.background_type == \ + BackgroundType.to_string(BackgroundType.Solid): + self.add_background_solid(self.background_color) + elif self.background_type == \ + BackgroundType.to_string(BackgroundType.Gradient): + self.add_background_gradient( + self.background_start_color, + self.background_end_color, + self.background_direction) + else: + filename = \ + os.path.split(self.background_filename)[1] + self.add_background_image(filename) + self.add_font(self.font_main_name, + self.font_main_color, + self.font_main_size, + self.font_main_override, u'main', + self.font_main_bold, + self.font_main_italics, + self.font_main_line_adjustment, + self.font_main_x, + self.font_main_y, + self.font_main_width, + self.font_main_height, + self.font_main_outline, + self.font_main_outline_color, + self.font_main_outline_size, + self.font_main_shadow, + self.font_main_shadow_color, + self.font_main_shadow_size) + self.add_font(self.font_footer_name, + self.font_footer_color, + self.font_footer_size, + self.font_footer_override, u'footer', + self.font_footer_bold, + self.font_footer_italics, + 0, # line adjustment + self.font_footer_x, + self.font_footer_y, + self.font_footer_width, + self.font_footer_height, + self.font_footer_outline, + self.font_footer_outline_color, + self.font_footer_outline_size, + self.font_footer_shadow, + self.font_footer_shadow_color, + self.font_footer_shadow_size) + self.add_display(self.display_horizontal_align, + self.display_vertical_align, + self.display_slide_transition) diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 37265f3d1..377999553 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -37,12 +37,12 @@ class HideMode(object): Theme = 2 Screen = 3 +from themeform import ThemeForm from filerenameform import FileRenameForm from maindisplay import MainDisplay from servicenoteform import ServiceNoteForm from serviceitemeditform import ServiceItemEditForm from screen import ScreenList -from amendthemeform import AmendThemeForm from slidecontroller import SlideController from splashscreen import SplashScreen from generaltab import GeneralTab @@ -58,4 +58,4 @@ from thememanager import ThemeManager __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager', - 'AmendThemeForm', 'MediaDockManager', 'ServiceItemEditForm'] + 'MediaDockManager', 'ServiceItemEditForm'] diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index ffcade344..dc126d863 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -81,6 +81,9 @@ class AdvancedTab(SettingsTab): self.doubleClickLiveCheckBox = QtGui.QCheckBox(self.uiGroupBox) self.doubleClickLiveCheckBox.setObjectName(u'doubleClickLiveCheckBox') self.uiLayout.addWidget(self.doubleClickLiveCheckBox) +# self.expandServiceItemCheckBox = QtGui.QCheckBox(self.uiGroupBox) +# self.expandServiceItemCheckBox.setObjectName(u'expandServiceItemCheckBox') +# self.uiLayout.addWidget(self.expandServiceItemCheckBox) self.leftLayout.addWidget(self.uiGroupBox) self.expandServiceItemCheckBox = QtGui.QCheckBox(self.uiGroupBox) self.expandServiceItemCheckBox.setObjectName( diff --git a/openlp/core/ui/amendthemeform.py b/openlp/core/ui/amendthemeform.py index 1e0404f10..1a7871f22 100644 --- a/openlp/core/ui/amendthemeform.py +++ b/openlp/core/ui/amendthemeform.py @@ -90,6 +90,16 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): QtCore.QObject.connect(self.fontMainSizeSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onFontMainSizeSpinBoxChanged) + QtCore.QObject.connect(self.fontMainLineAdjustmentSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.onFontMainLineAdjustmentSpinBoxChanged) + QtCore.QObject.connect(self.shadowSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.onShadowSpinBoxChanged) + QtCore.QObject.connect(self.outlineSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.onOutlineSpinBoxChanged) + QtCore.QObject.connect(self.fontFooterSizeSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onFontFooterSizeSpinBoxChanged) @@ -118,12 +128,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): QtCore.QObject.connect(self.fontFooterHeightSpinBox, QtCore.SIGNAL(u'editingFinished()'), self.onFontFooterHeightSpinBoxChanged) - QtCore.QObject.connect(self.shadowSpinBox, - QtCore.SIGNAL(u'editingFinished()'), - self.onShadowSpinBoxChanged) - QtCore.QObject.connect(self.outlineSpinBox, - QtCore.SIGNAL(u'editingFinished()'), - self.onOutlineSpinBoxChanged) + # CheckBoxes QtCore.QObject.connect(self.fontMainDefaultCheckBox, QtCore.SIGNAL(u'stateChanged(int)'), @@ -525,7 +530,7 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): else: self.gradientComboBox.setCurrentIndex(2) # Font Main Tab - self.fontMainComboBox.setCurrentFont( + self.mainFontComboBox.setCurrentFont( QtGui.QFont(self.theme.font_main_name)) self.fontMainSizeSpinBox.setValue(self.theme.font_main_proportion) if not self.theme.font_main_italics and \ diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 84b484878..5a53a84fe 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -36,6 +36,7 @@ from openlp.core.ui import HideMode log = logging.getLogger(__name__) #http://www.steveheffernan.com/html5-video-player/demo-video-player.html +#http://html5demos.com/two-videos class DisplayWidget(QtGui.QGraphicsView): """ diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index c63f3a7fc..a513cc99e 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -855,7 +855,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.plugin_manager.finalise_plugins() # Save settings self.saveSettings() - #Close down the display + # Close down the display self.LiveController.display.close() def serviceChanged(self, reset=False, serviceName=None): diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 3e7feafd3..f294cedd4 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -116,6 +116,7 @@ class ServiceManager(QtGui.QWidget): self.layout = QtGui.QVBoxLayout(self) self.layout.setSpacing(0) self.layout.setMargin(0) + self.expandTabs = False # Create the top toolbar self.toolbar = OpenLPToolbar(self) self.toolbar.addToolbarButton( @@ -203,13 +204,13 @@ class ServiceManager(QtGui.QWidget): self.orderToolbar.addSeparator() self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', '&Expand all'), - u':/services/service_top.png', + u':/services/service_expand_all.png', translate('OpenLP.ServiceManager', 'Expand all the service items.'), self.onExpandAll) self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', '&Collapse all'), - u':/services/service_bottom.png', + u':/services/service_collapse_all.png', translate('OpenLP.ServiceManager', 'Collapse all the service items.'), self.onCollapseAll) @@ -307,7 +308,7 @@ class ServiceManager(QtGui.QWidget): self.maintainAction.setVisible(False) self.notesAction.setVisible(False) if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit)\ - and hasattr(serviceItem[u'service_item'], u'editId'): + and serviceItem[u'service_item'].edit_id: self.editAction.setVisible(True) if serviceItem[u'service_item']\ .is_capable(ItemCapabilities.AllowsMaintain): @@ -863,7 +864,7 @@ class ServiceManager(QtGui.QWidget): editId, uuid = message.split(u':') for item in self.serviceItems: if item[u'service_item']._uuid == uuid: - item[u'service_item'].editId = editId + item[u'service_item'].edit_id = editId def replaceServiceItem(self, newItem): """ @@ -872,7 +873,7 @@ class ServiceManager(QtGui.QWidget): """ newItem.render() for itemcount, item in enumerate(self.serviceItems): - if item[u'service_item'].editId == newItem.editId and \ + if item[u'service_item'].edit_id == newItem.edit_id and \ item[u'service_item'].name == newItem.name: newItem.merge(item[u'service_item']) item[u'service_item'] = newItem @@ -890,8 +891,8 @@ class ServiceManager(QtGui.QWidget): ``expand`` Override the default expand settings. (Tristate) """ - log.debug(u'addServiceItem') - if expand == None: + # if not passed set to config value + if expand is None: expand = self.expandTabs sitem = self.findServiceItem()[0] item.render() @@ -982,7 +983,7 @@ class ServiceManager(QtGui.QWidget): .is_capable(ItemCapabilities.AllowsEdit): Receiver.send_message(u'%s_edit' % self.serviceItems[item][u'service_item'].name.lower(), u'L:%s' % - self.serviceItems[item][u'service_item'].editId ) + self.serviceItems[item][u'service_item'].edit_id ) def findServiceItem(self): """ diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 61fc78cd3..0afb0efaa 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -942,7 +942,7 @@ class SlideController(QtGui.QWidget): """ self.songEdit = True Receiver.send_message(u'%s_edit' % self.serviceItem.name.lower(), - u'P:%s' % self.serviceItem.editId) + u'P:%s' % self.serviceItem.edit_id) def onGoLive(self): """ diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py new file mode 100644 index 000000000..cc52abafc --- /dev/null +++ b/openlp/core/ui/themeform.py @@ -0,0 +1,650 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian # +# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # +# Carsten Tinggaard, Frode Woldsund # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### + +import logging +import os + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import translate, BackgroundType, BackgroundGradientType, \ + Receiver +from openlp.core.utils import get_images_filter +from themewizard import Ui_ThemeWizard + +log = logging.getLogger(__name__) + +class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): + """ + This is the Bible Import Wizard, which allows easy importing of Bibles + into OpenLP from other formats like OSIS, CSV and OpenSong. + """ + log.info(u'ThemeWizardForm loaded') + + def __init__(self, parent): + """ + Instantiate the wizard, and run any extra setup we need to. + + ``parent`` + The QWidget-derived parent of the wizard. + """ + QtGui.QWizard.__init__(self, parent) + self.thememanager = parent + self.setupUi(self) + self.registerFields() + self.accepted = False + QtCore.QObject.connect(self.backgroundTypeComboBox, + QtCore.SIGNAL(u'currentIndexChanged(int)'), + self.onBackgroundComboBox) + QtCore.QObject.connect(self.gradientComboBox, + QtCore.SIGNAL(u'currentIndexChanged(int)'), + self.onGradientComboBox) + QtCore.QObject.connect(self.color1PushButton, + QtCore.SIGNAL(u'pressed()'), + self.onColor1PushButtonClicked) + QtCore.QObject.connect(self.color2PushButton, + QtCore.SIGNAL(u'pressed()'), + self.onColor2PushButtonClicked) + QtCore.QObject.connect(self.imageBrowseButton, + QtCore.SIGNAL(u'pressed()'), + self.onImageBrowseButtonClicked) + QtCore.QObject.connect(self.mainColorPushButton, + QtCore.SIGNAL(u'pressed()'), + self.onMainColourPushButtonClicked) + QtCore.QObject.connect(self.outlineColorPushButton, + QtCore.SIGNAL(u'pressed()'), + self.onOutlineColourPushButtonClicked) + QtCore.QObject.connect(self.shadowColorPushButton, + QtCore.SIGNAL(u'pressed()'), + self.onShadowColourPushButtonClicked) + QtCore.QObject.connect(self.outlineCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), + self.onOutlineCheckCheckBoxChanged) + QtCore.QObject.connect(self.shadowCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), + self.onShadowCheckCheckBoxChanged) + QtCore.QObject.connect(self.footerColorPushButton, + QtCore.SIGNAL(u'pressed()'), + self.onFooterColourPushButtonClicked) + QtCore.QObject.connect(self.mainDefaultPositionCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), + self.onMainDefaultPositionCheckBox) + QtCore.QObject.connect(self.footerDefaultPositionCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), + self.onFooterDefaultPositionCheckBox) + QtCore.QObject.connect(self, + QtCore.SIGNAL(u'currentIdChanged(int)'), + self.pageChanged) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'theme_line_count'), + self.updateLinesText) + QtCore.QObject.connect(self.mainSizeSpinBox, + QtCore.SIGNAL(u'valueChanged(int)'), + self.calculateLines) + QtCore.QObject.connect(self.mainSizeSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.calculateLines) + QtCore.QObject.connect(self.lineSpacingSpinBox, + QtCore.SIGNAL(u'valueChanged(int)'), + self.calculateLines) + QtCore.QObject.connect(self.lineSpacingSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.calculateLines) + QtCore.QObject.connect(self.outlineSizeSpinBox, + QtCore.SIGNAL(u'valueChanged(int)'), + self.calculateLines) + QtCore.QObject.connect(self.outlineSizeSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.calculateLines) + QtCore.QObject.connect(self.shadowSizeSpinBox, + QtCore.SIGNAL(u'valueChanged(int)'), + self.calculateLines) + QtCore.QObject.connect(self.shadowSizeSpinBox, + QtCore.SIGNAL(u'editingFinished()'), + self.calculateLines) + QtCore.QObject.connect(self.mainFontComboBox, + QtCore.SIGNAL(u'activated(int)'), + self.calculateLines) + + def pageChanged(self, pageId): + """ + Detects Page changes and updates as approprate. + """ + if pageId == 6: + self.updateTheme() + frame = self.thememanager.generateImage(self.theme) + self.previewBoxLabel.setPixmap(QtGui.QPixmap.fromImage(frame)) + + def setDefaults(self): + """ + Set up display at start of theme edit. + """ + self.restart() + self.accepted = False + self.setBackgroundTabValues() + self.setMainAreaTabValues() + self.setFooterAreaTabValues() + self.setAlignmentTabValues() + self.setPositionTabValues() + self.setPreviewTabValues() + + def registerFields(self): + """ + Map field names to screen names, + """ + self.backgroundPage.registerField( + u'background_type', self.backgroundTypeComboBox) + self.backgroundPage.registerField( + u'color_1', self.color1PushButton) + self.backgroundPage.registerField( + u'color_2', self.color2PushButton) + self.backgroundPage.registerField( + u'background_image', self.imageLineEdit) + self.backgroundPage.registerField( + u'gradient', self.gradientComboBox) + self.mainAreaPage.registerField( + u'mainFontComboBox', self.mainFontComboBox) + self.mainAreaPage.registerField( + u'mainColorPushButton', self.mainColorPushButton) + self.mainAreaPage.registerField( + u'mainSizeSpinBox', self.mainSizeSpinBox) + self.mainAreaPage.registerField( + u'lineSpacingSpinBox', self.lineSpacingSpinBox) + self.mainAreaPage.registerField( + u'outlineCheckBox', self.outlineCheckBox) + self.mainAreaPage.registerField( + u'outlineColorPushButton', self.outlineColorPushButton) + self.mainAreaPage.registerField( + u'outlineSizeSpinBox', self.outlineSizeSpinBox) + self.mainAreaPage.registerField( + u'shadowCheckBox', self.shadowCheckBox) + self.mainAreaPage.registerField( + u'boldCheckBox', self.boldCheckBox) + self.mainAreaPage.registerField( + u'italicsCheckBox', self.italicsCheckBox) + self.mainAreaPage.registerField( + u'shadowColorPushButton', self.shadowColorPushButton) + self.mainAreaPage.registerField( + u'shadowSizeSpinBox', self.shadowSizeSpinBox) + self.mainAreaPage.registerField( + u'footerSizeSpinBox', self.footerSizeSpinBox) + self.areaPositionPage.registerField( + u'mainPositionX', self.mainXSpinBox) + self.areaPositionPage.registerField( + u'mainPositionY', self.mainYSpinBox) + self.areaPositionPage.registerField( + u'mainPositionWidth', self.mainWidthSpinBox) + self.areaPositionPage.registerField( + u'mainPositionHeight', self.mainHeightSpinBox) + self.areaPositionPage.registerField( + u'footerPositionX', self.footerXSpinBox) + self.areaPositionPage.registerField( + u'footerPositionY', self.footerYSpinBox) + self.areaPositionPage.registerField( + u'footerPositionWidth', self.footerWidthSpinBox) + self.areaPositionPage.registerField( + u'footerPositionHeight', self.footerHeightSpinBox) + self.backgroundPage.registerField( + u'horizontal', self.horizontalComboBox) + self.backgroundPage.registerField( + u'vertical', self.verticalComboBox) + self.backgroundPage.registerField( + u'slideTransition', self.transitionsCheckBox) + self.backgroundPage.registerField( + u'name', self.themeNameEdit) + + def calculateLines(self): + """ + Calculate the number of lines on a page by rendering text + """ + # Do not trigger on start up + if self.page != 0: + self.updateTheme() + frame = self.thememanager.generateImage(self.theme, True) + + def updateLinesText(self, lines): + """ + Updates the lines on a page on the wizard + """ + self.mainLineCountLabel.setText(unicode(translate('OpenLP.ThemeForm', \ + '(%d lines per slide)' % int(lines)))) + + def onOutlineCheckCheckBoxChanged(self, state): + """ + Change state as Outline check box changed + """ + if state == QtCore.Qt.Checked: + self.theme.font_main_outline = True + else: + self.theme.font_main_outline = False + self.outlineColorPushButton.setEnabled(self.theme.font_main_outline) + self.outlineSizeSpinBox.setEnabled(self.theme.font_main_outline) + self.calculateLines() + + def onShadowCheckCheckBoxChanged(self, state): + """ + Change state as Shadow check box changed + """ + if state == QtCore.Qt.Checked: + self.theme.font_main_shadow = True + else: + self.theme.font_main_shadow = False + self.shadowColorPushButton.setEnabled(self.theme.font_main_shadow) + self.shadowSizeSpinBox.setEnabled(self.theme.font_main_shadow) + self.calculateLines() + + def onMainDefaultPositionCheckBox(self, value): + """ + Change state as Main Area Position check box changed + """ + if value == QtCore.Qt.Checked: + self.theme.font_main_override = False + else: + self.theme.font_main_override = True + self.mainXSpinBox.setEnabled(self.theme.font_main_override) + self.mainYSpinBox.setEnabled(self.theme.font_main_override) + self.mainHeightSpinBox.setEnabled(self.theme.font_main_override) + self.mainWidthSpinBox.setEnabled(self.theme.font_main_override) + + def onFooterDefaultPositionCheckBox(self, value): + """ + Change state as Footer Area Position check box changed + """ + if value == QtCore.Qt.Checked: + self.theme.font_footer_override = False + else: + self.theme.font_footer_override = True + self.footerXSpinBox.setEnabled(self.theme.font_footer_override) + self.footerYSpinBox.setEnabled(self.theme.font_footer_override) + self.footerHeightSpinBox.setEnabled(self.theme.font_footer_override) + self.footerWidthSpinBox.setEnabled(self.theme.font_footer_override) + + def exec_(self): + """ + Run the wizard. + """ + self.setDefaults() + return QtGui.QWizard.exec_(self) + + def initializePage(self, id): + """ + Set up the pages for Initial run through dialog + """ + log.debug(u'initializePage %s' % id) + self.page = id + if id == 1: + self.setBackgroundTabValues() + elif id == 2: + self.setMainAreaTabValues() + elif id == 3: + self.setFooterAreaTabValues() + elif id == 4: + self.setAlignmentTabValues() + elif id == 5: + self.setPositionTabValues() + + def setBackgroundTabValues(self): + """ + Handle the display and State of the background display tab. + """ + if self.theme.background_type == \ + BackgroundType.to_string(BackgroundType.Solid): + self.setField(u'background_type', QtCore.QVariant(0)) + self.color1PushButton.setVisible(True) + self.color1Label.setVisible(True) + self.color1PushButton.setStyleSheet(u'background-color: %s' % + self.theme.background_color) + self.color1Label.setText( + translate('OpenLP.ThemeForm', 'Color:')) + self.color2PushButton.setVisible(False) + self.color2Label.setVisible(False) + self.gradientLabel.setVisible(False) + self.gradientComboBox.setVisible(False) + self.imageLabel.setVisible(False) + self.imageLineEdit.setVisible(False) + self.imageBrowseButton.setVisible(False) + self.imageLineEdit.setText(u'') + elif self.theme.background_type == \ + BackgroundType.to_string(BackgroundType.Gradient): + self.setField(u'background_type', QtCore.QVariant(1)) + self.color1PushButton.setVisible(True) + self.color1Label.setVisible(True) + self.color1PushButton.setStyleSheet(u'background-color: %s' % + self.theme.background_start_color) + self.color1Label.setText( + translate('OpenLP.ThemeForm', 'First color:')) + self.color2PushButton.setVisible(True) + self.color2Label.setVisible(True) + self.color2PushButton.setStyleSheet(u'background-color: %s' % + self.theme.background_end_color) + self.color2Label.setText( + translate('OpenLP.ThemeForm', 'Second color:')) + self.gradientLabel.setVisible(True) + self.gradientComboBox.setVisible(True) + self.imageLabel.setVisible(False) + self.imageLineEdit.setVisible(False) + self.imageBrowseButton.setVisible(False) + self.imageLineEdit.setText(u'') + else: + self.setField(u'background_type', QtCore.QVariant(2)) + self.color1PushButton.setVisible(False) + self.color1Label.setVisible(False) + self.color2PushButton.setVisible(False) + self.color2Label.setVisible(False) + self.gradientLabel.setVisible(False) + self.gradientComboBox.setVisible(False) + self.imageLineEdit.setVisible(True) + self.imageLabel.setVisible(True) + self.imageBrowseButton.setVisible(True) + self.imageLineEdit.setText(self.theme.background_filename) + if self.theme.background_direction == \ + BackgroundGradientType.to_string(BackgroundGradientType.Horizontal): + self.setField(u'gradient', QtCore.QVariant(0)) + elif self.theme.background_direction == \ + BackgroundGradientType.to_string(BackgroundGradientType.Vertical): + self.setField(u'gradient', QtCore.QVariant(1)) + elif self.theme.background_direction == \ + BackgroundGradientType.to_string(BackgroundGradientType.Circular): + self.setField(u'gradient', QtCore.QVariant(2)) + elif self.theme.background_direction == \ + BackgroundGradientType.to_string(BackgroundGradientType.LeftTop): + self.setField(u'gradient', QtCore.QVariant(3)) + else: + self.setField(u'gradient', QtCore.QVariant(4)) + + def setMainAreaTabValues(self): + """ + Handle the display and State of the Main Area tab. + """ + self.mainFontComboBox.setCurrentFont( + QtGui.QFont(self.theme.font_main_name)) + self.mainColorPushButton.setStyleSheet(u'background-color: %s' % + self.theme.font_main_color) + self.setField(u'mainSizeSpinBox', \ + QtCore.QVariant(self.theme.font_main_size)) + self.setField(u'lineSpacingSpinBox', \ + QtCore.QVariant(self.theme.font_main_line_adjustment)) + self.setField(u'outlineCheckBox', \ + QtCore.QVariant(self.theme.font_main_outline)) + self.outlineColorPushButton.setStyleSheet(u'background-color: %s' % + self.theme.font_main_outline_color) + self.setField(u'outlineSizeSpinBox', \ + QtCore.QVariant(self.theme.font_main_outline_size)) + self.setField(u'shadowCheckBox', \ + QtCore.QVariant(self.theme.font_main_shadow)) + self.shadowColorPushButton.setStyleSheet(u'background-color: %s' % + self.theme.font_main_shadow_color) + self.setField(u'shadowSizeSpinBox', \ + QtCore.QVariant(self.theme.font_main_shadow_size)) + self.setField(u'boldCheckBox', \ + QtCore.QVariant(self.theme.font_main_bold)) + self.setField(u'italicsCheckBox', \ + QtCore.QVariant(self.theme.font_main_italics)) + # Set up field states + if self.theme.font_main_outline: + self.setField(u'outlineCheckBox', QtCore.QVariant(False)) + else: + self.setField(u'outlineCheckBox', QtCore.QVariant(True)) + self.outlineColorPushButton.setEnabled(self.theme.font_main_outline) + self.outlineSizeSpinBox.setEnabled(self.theme.font_main_outline) + if self.theme.font_main_shadow: + self.setField(u'shadowCheckBox', QtCore.QVariant(False)) + else: + self.setField(u'shadowCheckBox', QtCore.QVariant(True)) + self.shadowColorPushButton.setEnabled(self.theme.font_main_shadow) + self.shadowSizeSpinBox.setEnabled(self.theme.font_main_shadow) + + def setFooterAreaTabValues(self): + """ + Handle the display and State of the Footer Area tab. + """ + self.footerFontComboBox.setCurrentFont( + QtGui.QFont(self.theme.font_main_name)) + self.footerColorPushButton.setStyleSheet(u'background-color: %s' % + self.theme.font_footer_color) + self.setField(u'footerSizeSpinBox', \ + QtCore.QVariant(self.theme.font_footer_size)) + + def setPositionTabValues(self): + """ + Handle the display and State of the Position tab. + """ + # Main Area + if self.theme.font_main_override: + self.mainDefaultPositionCheckBox.setChecked(False) + else: + self.mainDefaultPositionCheckBox.setChecked(True) + self.setField(u'mainPositionX', \ + QtCore.QVariant(self.theme.font_main_x)) + self.setField(u'mainPositionY', \ + QtCore.QVariant(self.theme.font_main_y)) + self.setField(u'mainPositionHeight', \ + QtCore.QVariant(self.theme.font_main_height)) + self.setField(u'mainPositionWidth', \ + QtCore.QVariant(self.theme.font_main_width)) + # Footer + if self.theme.font_footer_override: + self.footerDefaultPositionCheckBox.setChecked(False) + else: + self.footerDefaultPositionCheckBox.setChecked(True) + self.setField(u'footerPositionX', \ + QtCore.QVariant(self.theme.font_footer_x)) + self.setField(u'footerPositionY', \ + QtCore.QVariant(self.theme.font_footer_y)) + self.setField(u'footerPositionHeight', \ + QtCore.QVariant(self.theme.font_footer_height)) + self.setField(u'footerPositionWidth', \ + QtCore.QVariant(self.theme.font_footer_width)) + + def setAlignmentTabValues(self): + """ + Define the Tab Alignments Page + """ + self.setField(u'horizontal', \ + QtCore.QVariant(self.theme.display_horizontal_align)) + self.setField(u'vertical', \ + QtCore.QVariant(self.theme.display_vertical_align)) + self.setField(u'slideTransition', \ + QtCore.QVariant(self.theme.display_slide_transition)) + + def setPreviewTabValues(self): + self.setField(u'name', QtCore.QVariant(self.theme.theme_name)) + if len(self.theme.theme_name) > 1: + self.themeNameEdit.setEnabled(False) + else: + self.themeNameEdit.setEnabled(True) + + def onBackgroundComboBox(self, index): + """ + Background style Combo box has changed. + """ + self.theme.background_type = BackgroundType.to_string(index) + self.setBackgroundTabValues() + + def onGradientComboBox(self, index): + """ + Background gradient Combo box has changed. + """ + self.theme.background_direction = \ + BackgroundGradientType.to_string(index) + self.setBackgroundTabValues() + + def onColor1PushButtonClicked(self): + """ + Background / Gradient 1 Color button pushed. + """ + if self.theme.background_type == \ + BackgroundType.to_string(BackgroundType.Solid): + self.theme.background_color = \ + self._colorButton(self.theme.background_color) + else: + self.theme.background_start_color = \ + self._colorButton(self.theme.background_start_color) + self.setBackgroundTabValues() + + def onColor2PushButtonClicked(self): + """ + Gradient 2 Color button pushed. + """ + self.theme.background_end_color = \ + self._colorButton(self.theme.background_end_color) + self.setBackgroundTabValues() + + def onImageBrowseButtonClicked(self): + """ + Background Image button pushed. + """ + images_filter = get_images_filter() + images_filter = '%s;;%s (*.*) (*)' % (images_filter, + translate('OpenLP.ThemeForm', 'All Files')) + filename = QtGui.QFileDialog.getOpenFileName(self, + translate('OpenLP.ThemeForm', 'Select Image'), u'', + images_filter) + if filename: + self.theme.background_filename = unicode(filename) + self.setBackgroundTabValues() + + def onMainFontComboBox(self): + """ + Main Font Combo box changed + """ + self.theme.font_main_name = self.mainFontComboBox.currentFont().family() + + def onMainColourPushButtonClicked(self): + self.theme.font_main_color = \ + self._colorButton(self.theme.font_main_color) + self.setMainAreaTabValues() + + def onOutlineColourPushButtonClicked(self): + self.theme.font_main_outline_color = \ + self._colorButton(self.theme.font_main_outline_color) + self.setMainAreaTabValues() + + def onShadowColourPushButtonClicked(self): + self.theme.font_main_shadow_color = \ + self._colorButton(self.theme.font_main_shadow_color) + self.setMainAreaTabValues() + + def onFooterColourPushButtonClicked(self): + self.theme.font_footer_color = \ + self._colorButton(self.theme.font_footer_color) + self.setFooterAreaTabValues() + + def updateTheme(self): + """ + Update the theme object from the UI for fields not already updated + when the are changed. + """ + log.debug(u'updateTheme') + # main page + self.theme.font_main_name = \ + unicode(self.mainFontComboBox.currentFont().family()) + self.theme.font_main_size = \ + self.field(u'mainSizeSpinBox').toInt()[0] + self.theme.font_main_line_adjustment = \ + self.field(u'lineSpacingSpinBox').toInt()[0] + self.theme.font_main_outline_size = \ + self.field(u'outlineSizeSpinBox').toInt()[0] + self.theme.font_main_shadow_size = \ + self.field(u'shadowSizeSpinBox').toInt()[0] + self.theme.font_main_bold = \ + self.field(u'boldCheckBox').toBool() + self.theme.font_main_italics = \ + self.field(u'italicsCheckBox').toBool() + # footer page + self.theme.font_footer_name = \ + unicode(self.footerFontComboBox.currentFont().family()) + self.theme.font_footer_size = \ + self.field(u'footerSizeSpinBox').toInt()[0] + # position page + self.theme.font_main_x = self.field(u'mainPositionX').toInt()[0] + self.theme.font_main_y = self.field(u'mainPositionY').toInt()[0] + self.theme.font_main_height = \ + self.field(u'mainPositionHeight').toInt()[0] + self.theme.font_main_width = self.field(u'mainPositionWidth').toInt()[0] + self.theme.font_footer_x = self.field(u'footerPositionX').toInt()[0] + self.theme.font_footer_y = self.field(u'footerPositionY').toInt()[0] + self.theme.font_footer_height = \ + self.field(u'footerPositionHeight').toInt()[0] + self.theme.font_footer_width = \ + self.field(u'footerPositionWidth').toInt()[0] + # position page + self.theme.display_horizontal_align = \ + self.horizontalComboBox.currentIndex() + self.theme.display_vertical_align = \ + self.verticalComboBox.currentIndex() + self.theme.display_slide_transition = \ + self.field(u'slideTransition').toBool() + + def accept(self): + """ + Lets save the them as Finish has been pressed + """ + # Some reason getting double submission. + # Hack to stop it for now. + if self.accepted: + return + self.accepted = True + # Save the theme name + self.theme.theme_name = \ + unicode(self.field(u'name').toString()) + if not self.theme.theme_name: + QtGui.QMessageBox.critical(self, + translate('OpenLP.ThemeForm', 'Theme Name Missing'), + translate('OpenLP.ThemeForm', + 'There is no name for this theme. ' + 'Please enter one.'), + (QtGui.QMessageBox.Ok), + QtGui.QMessageBox.Ok) + return + if self.theme.theme_name == u'-1' or self.theme.theme_name == u'None': + QtGui.QMessageBox.critical(self, + translate('OpenLP.ThemeForm', 'Theme Name Invalid'), + translate('OpenLP.ThemeForm', + 'Invalid theme name. ' + 'Please enter one.'), + (QtGui.QMessageBox.Ok), + QtGui.QMessageBox.Ok) + return + saveFrom = None + saveTo = None + if self.theme.background_type == \ + BackgroundType.to_string(BackgroundType.Image): + filename = \ + os.path.split(unicode(self.theme.background_filename))[1] + saveTo = os.path.join(self.path, self.theme.theme_name, filename) + saveFrom = self.theme.background_filename + if self.thememanager.saveTheme(self.theme, saveFrom, saveTo): + return QtGui.QDialog.accept(self) + + def _colorButton(self, field): + """ + Handle Color buttons + """ + new_color = QtGui.QColorDialog.getColor( + QtGui.QColor(field), self) + if new_color.isValid(): + field = new_color.name() + return field diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 2f060b12f..65913a9f6 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -32,10 +32,11 @@ import logging from xml.etree.ElementTree import ElementTree, XML from PyQt4 import QtCore, QtGui -from openlp.core.ui import AmendThemeForm, FileRenameForm +from openlp.core.ui import FileRenameForm, ThemeForm from openlp.core.theme import Theme from openlp.core.lib import OpenLPToolbar, ThemeXML, get_text_file_string, \ - build_icon, Receiver, SettingsManager, translate, check_item_selected + build_icon, Receiver, SettingsManager, translate, check_item_selected, \ + BackgroundType, BackgroundGradientType from openlp.core.utils import AppLocation, get_filesystem_encoding log = logging.getLogger(__name__) @@ -52,7 +53,7 @@ class ThemeManager(QtGui.QWidget): self.layout = QtGui.QVBoxLayout(self) self.layout.setSpacing(0) self.layout.setMargin(0) - self.amendThemeForm = AmendThemeForm(self) + self.themeForm = ThemeForm(self) self.fileRenameForm = FileRenameForm(self) self.toolbar = OpenLPToolbar(self) self.toolbar.addToolbarButton( @@ -125,7 +126,7 @@ class ThemeManager(QtGui.QWidget): self.checkThemesExists(self.path) self.thumbPath = os.path.join(self.path, u'thumbnails') self.checkThemesExists(self.thumbPath) - self.amendThemeForm.path = self.path + self.themeForm.path = self.path self.oldBackgroundImage = None self.editingDefault = False # Last little bits of setting up @@ -180,7 +181,7 @@ class ThemeManager(QtGui.QWidget): '%s (default)')) % newName self.themeListWidget.item(count).setText(name) - def changeGlobalFromScreen(self, index = -1): + def changeGlobalFromScreen(self, index=-1): """ Change the global theme when a theme is double clicked upon in the Theme Manager list @@ -213,10 +214,10 @@ class ThemeManager(QtGui.QWidget): Loads a new theme with the default settings and then launches the theme editing form for the user to make their customisations. """ - theme = self.createThemeFromXml(self.baseTheme(), self.path) - self.amendThemeForm.loadTheme(theme) + theme = ThemeXML() self.saveThemeName = u'' - self.amendThemeForm.exec_() + self.themeForm.theme = theme + self.themeForm.exec_() def onRenameTheme(self): """ @@ -242,67 +243,23 @@ class ThemeManager(QtGui.QWidget): self.saveThemeName = u'' if self.fileRenameForm.exec_(): newThemeName = unicode(self.fileRenameForm.FileNameEdit.text()) - oldThemeData = self.getThemeData(oldThemeName) - self.cloneThemeData(oldThemeData, newThemeName) + themeData = self.getThemeData(oldThemeName) + self.cloneThemeData(themeData, newThemeName) self.loadThemes() - def cloneThemeData(self, oldThemeData, newThemeName): + def cloneThemeData(self, themeData, newThemeName): """ + Takes a theme and makes a new copy of it as well as saving it. """ log.debug(u'cloneThemeData') - new_theme = ThemeXML() - new_theme.new_document(newThemeName) - save_from = None - save_to = None - if oldThemeData.background_type == u'solid': - new_theme.add_background_solid( - unicode(oldThemeData.background_color)) - elif oldThemeData.background_type == u'gradient': - new_theme.add_background_gradient( - unicode(oldThemeData.background_start_color), - unicode(oldThemeData.background_end_color), - oldThemeData.background_direction) - else: - filename = \ - os.path.split(unicode(oldThemeData.background_filename))[1] - new_theme.add_background_image(filename) - save_to = os.path.join(self.path, newThemeName, filename) - save_from = oldThemeData.background_filename - new_theme.add_font(unicode(oldThemeData.font_main_name), - unicode(oldThemeData.font_main_color), - unicode(oldThemeData.font_main_proportion), - unicode(oldThemeData.font_main_override), u'main', - unicode(oldThemeData.font_main_weight), - unicode(oldThemeData.font_main_italics), - unicode(oldThemeData.font_main_line_adjustment), - unicode(oldThemeData.font_main_x), - unicode(oldThemeData.font_main_y), - unicode(oldThemeData.font_main_width), - unicode(oldThemeData.font_main_height)) - new_theme.add_font(unicode(oldThemeData.font_footer_name), - unicode(oldThemeData.font_footer_color), - unicode(oldThemeData.font_footer_proportion), - unicode(oldThemeData.font_footer_override), u'footer', - unicode(oldThemeData.font_footer_weight), - unicode(oldThemeData.font_footer_italics), - 0, # line adjustment - unicode(oldThemeData.font_footer_x), - unicode(oldThemeData.font_footer_y), - unicode(oldThemeData.font_footer_width), - unicode(oldThemeData.font_footer_height)) - new_theme.add_display(unicode(oldThemeData.display_shadow), - unicode(oldThemeData.display_shadow_color), - unicode(oldThemeData.display_outline), - unicode(oldThemeData.display_outline_color), - unicode(oldThemeData.display_horizontal_align), - unicode(oldThemeData.display_vertical_align), - unicode(oldThemeData.display_wrap_style), - unicode(oldThemeData.display_slide_transition), - unicode(oldThemeData.display_shadow_size), - unicode(oldThemeData.display_outline_size)) - theme = new_theme.extract_xml() - pretty_theme = new_theme.extract_formatted_xml() - self.saveTheme(newThemeName, theme, pretty_theme, save_from, save_to) + saveTo = None + saveFrom = None + if themeData.background_type == u'image': + saveTo = os.path.join(self.path, newThemeName, + os.path.split(unicode(themeData.background_filename))[1]) + saveFrom = themeData.background_filename + themeData.theme_name = newThemeName + self.saveTheme(themeData, saveFrom, saveTo) def onEditTheme(self): """ @@ -320,10 +277,10 @@ class ThemeManager(QtGui.QWidget): unicode(item.data(QtCore.Qt.UserRole).toString())) if theme.background_type == u'image': self.oldBackgroundImage = theme.background_filename - self.amendThemeForm.loadTheme(theme) self.saveThemeName = unicode( item.data(QtCore.Qt.UserRole).toString()) - self.amendThemeForm.exec_() + self.themeForm.theme = theme + self.themeForm.exec_() def onDeleteTheme(self): """ @@ -340,7 +297,8 @@ class ThemeManager(QtGui.QWidget): # confirm deletion answer = QtGui.QMessageBox.question(self, translate('OpenLP.ThemeManager', 'Delete Confirmation'), - translate('OpenLP.ThemeManager', 'Delete theme?'), + unicode(translate('OpenLP.ThemeManager', 'Delete %s theme?')) + % theme, QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.No) if answer == QtGui.QMessageBox.No: @@ -462,6 +420,7 @@ class ThemeManager(QtGui.QWidget): self.themelist = [] self.themeListWidget.clear() dirList = os.listdir(self.path) + dirList.sort() for name in dirList: if name.endswith(u'.png'): # check to see file is in theme root directory @@ -500,20 +459,21 @@ class ThemeManager(QtGui.QWidget): """ return self.themelist - def getThemeData(self, themename): + def getThemeData(self, themeName): """ Returns a theme object from an XML file - ``themename`` + ``themeName`` Name of the theme to load from file """ - log.debug(u'getthemedata for theme %s', themename) - xml_file = os.path.join(self.path, unicode(themename), - unicode(themename) + u'.xml') - xml = get_text_file_string(xml_file) + log.debug(u'getthemedata for theme %s', themeName) + xmlFile = os.path.join(self.path, unicode(themeName), + unicode(themeName) + u'.xml') + xml = get_text_file_string(xmlFile) if not xml: - xml = self.baseTheme() - return self.createThemeFromXml(xml, self.path) + return self.baseTheme() + else: + return self.createThemeFromXml(xml, self.path) def checkThemesExists(self, dir): """ @@ -584,7 +544,8 @@ class ThemeManager(QtGui.QWidget): outfile = open(fullpath, u'wb') outfile.write(zip.read(file)) if filexml: - self.generateAndSaveImage(dir, themename, filexml) + theme = self.createThemeFromXml(filexml, self.path) + self.generateAndSaveImage(dir, themename, theme) else: QtGui.QMessageBox.critical(self, translate('OpenLP.ThemeManager', 'Error'), @@ -631,50 +592,59 @@ class ThemeManager(QtGui.QWidget): """ theme = Theme(xml_data) newtheme = ThemeXML() - newtheme.new_document(theme.Name) + newtheme.theme_name = theme.Name if theme.BackgroundType == 0: - newtheme.add_background_solid(unicode( - theme.BackgroundParameter1.name())) + newtheme.background_type = \ + BackgroundType.to_string(BackgroundType.Solid) + newtheme.background_color = \ + unicode(theme.BackgroundParameter1.name()) elif theme.BackgroundType == 1: - direction = u'vertical' + newtheme.background_type = \ + BackgroundType.to_string(BackgroundType.Gradient) + newtheme.background_direction = \ + BackgroundGradientType. \ + to_string(BackgroundGradientType.Horizontal) if theme.BackgroundParameter3.name() == 1: - direction = u'horizontal' - newtheme.add_background_gradient( - unicode(theme.BackgroundParameter1.name()), - unicode(theme.BackgroundParameter2.name()), direction) + newtheme.background_direction = \ + BackgroundGradientType. \ + to_string(BackgroundGradientType.Horizontal) + newtheme.background_start_color = \ + unicode(theme.BackgroundParameter1.name()) + newtheme.background_end_color = \ + unicode(theme.BackgroundParameter2.name()) else: - newtheme.add_background_image(unicode(theme.BackgroundParameter1)) - newtheme.add_font(unicode(theme.FontName), - unicode(theme.FontColor.name()), - unicode(theme.FontProportion * 3), u'False') - newtheme.add_font(unicode(theme.FontName), - unicode(theme.FontColor.name()), - unicode(12), u'False', u'footer') - outline = False - shadow = False + newtheme.background_type = \ + BackgroundType.to_string(BackgroundType.Image) + newtheme.background_filename = unicode(theme.BackgroundParameter1) + newtheme.font_main_name = theme.FontName + newtheme.font_main_color = unicode(theme.FontColor.name()) + newtheme.font_main_size = theme.FontProportion * 3 + newtheme.font_footer_name = theme.FontName + newtheme.font_footer_color = unicode(theme.FontColor.name()) + newtheme.font_main_shadow = False if theme.Shadow == 1: - shadow = True + newtheme.font_main_shadow = True + newtheme.font_main_shadow_color = unicode(theme.ShadowColor.name()) if theme.Outline == 1: - outline = True + newtheme.font_main_outline = True + newtheme.font_main_outline_color = unicode(theme.OutlineColor.name()) vAlignCorrection = 0 if theme.VerticalAlign == 2: vAlignCorrection = 1 elif theme.VerticalAlign == 1: vAlignCorrection = 2 - newtheme.add_display(unicode(shadow), - unicode(theme.ShadowColor.name()), - unicode(outline), unicode(theme.OutlineColor.name()), - unicode(theme.HorizontalAlign), unicode(vAlignCorrection), - unicode(theme.WrapStyle), unicode(0)) + newtheme.display_horizontal_align = theme.HorizontalAlign + newtheme.display_vertical_align = vAlignCorrection return newtheme.extract_xml() - def saveTheme(self, name, theme_xml, theme_pretty_xml, image_from, - image_to): + def saveTheme(self, theme, imageFrom, imageTo): """ Called by thememaintenance Dialog to save the theme and to trigger the reload of the theme list """ - log.debug(u'saveTheme %s %s', name, theme_xml) + name = theme.theme_name + theme_pretty_xml = theme.extract_formatted_xml() + log.debug(u'saveTheme %s %s', name, theme_pretty_xml) theme_dir = os.path.join(self.path, name) if not os.path.exists(theme_dir): os.mkdir(os.path.join(self.path, name)) @@ -700,8 +670,8 @@ class ThemeManager(QtGui.QWidget): self.deleteTheme(self.saveThemeName) if result == QtGui.QMessageBox.Yes: # Save the theme, overwriting the existing theme if necessary. - if image_to and self.oldBackgroundImage and \ - image_to != self.oldBackgroundImage: + if imageTo and self.oldBackgroundImage and \ + imageTo != self.oldBackgroundImage: try: os.remove(self.oldBackgroundImage) except OSError: @@ -715,15 +685,15 @@ class ThemeManager(QtGui.QWidget): finally: if outfile: outfile.close() - if image_from and image_from != image_to: + if imageFrom and imageFrom != imageTo: try: encoding = get_filesystem_encoding() shutil.copyfile( - unicode(image_from).encode(encoding), - unicode(image_to).encode(encoding)) + unicode(imageFrom).encode(encoding), + unicode(imageTo).encode(encoding)) except IOError: log.exception(u'Failed to save theme image') - self.generateAndSaveImage(self.path, name, theme_xml) + self.generateAndSaveImage(self.path, name, theme) self.loadThemes() # Check if we need to set a new service theme if editedServiceTheme: @@ -748,14 +718,15 @@ class ThemeManager(QtGui.QWidget): self.global_theme) self.editingDefault = False self.pushThemes() + return True else: # Don't close the dialog - allow the user to change the name of # the theme or to cancel the theme dialog completely. return False - def generateAndSaveImage(self, dir, name, theme_xml): - log.debug(u'generateAndSaveImage %s %s %s', dir, name, theme_xml) - theme = self.createThemeFromXml(theme_xml, dir) + def generateAndSaveImage(self, dir, name, theme): + log.debug(u'generateAndSaveImage %s %s', dir, name) + theme_xml = theme.extract_xml() frame = self.generateImage(theme) samplepathname = os.path.join(self.path, name + u'.png') if os.path.exists(samplepathname): @@ -767,12 +738,18 @@ class ThemeManager(QtGui.QWidget): pixmap.save(thumb, u'png') log.debug(u'Theme image written to %s', samplepathname) - def generateImage(self, themedata): + def generateImage(self, themeData, forcePage=False): """ Call the RenderManager to build a Sample Image + + ``themeData`` + The theme to generated a preview for. + + ``forcePage`` + Flag to tell message lines per page need to be generated. """ - log.debug(u'generateImage \n%s ', themedata) - return self.parent.RenderManager.generate_preview(themedata) + log.debug(u'generateImage \n%s ', themeData) + return self.parent.RenderManager.generate_preview(themeData, forcePage) def getPreviewImage(self, theme): """ @@ -791,25 +768,16 @@ class ThemeManager(QtGui.QWidget): """ log.debug(u'base theme created') newtheme = ThemeXML() - newtheme.new_document( - unicode(translate('OpenLP.ThemeManager', 'New Theme'))) - newtheme.add_background_solid(u'#000000') - newtheme.add_font(unicode(QtGui.QFont().family()), u'#FFFFFF', - u'30', u'False') - newtheme.add_font(unicode(QtGui.QFont().family()), u'#FFFFFF', - u'12', u'False', u'footer') - newtheme.add_display(u'False', u'#FFFFFF', u'False', - unicode(u'#FFFFFF'), u'0', u'0', u'0', u'False') - return newtheme.extract_xml() + return newtheme - def createThemeFromXml(self, theme_xml, path): + def createThemeFromXml(self, themeXml, path): """ Return a theme object using information parsed from XML - ``theme_xml`` + ``themeXml`` The XML data to load into the theme """ theme = ThemeXML() - theme.parse(theme_xml) + theme.parse(themeXml) theme.extend_image_filename(path) return theme diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py new file mode 100644 index 000000000..b3af9df98 --- /dev/null +++ b/openlp/core/ui/themewizard.py @@ -0,0 +1,547 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian # +# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # +# Carsten Tinggaard, Frode Woldsund # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import translate, build_icon + +class Ui_ThemeWizard(object): + def setupUi(self, ThemeWizard): + ThemeWizard.setObjectName(u'ThemeWizard') + ThemeWizard.resize(550, 386) + ThemeWizard.setModal(True) + ThemeWizard.setWizardStyle(QtGui.QWizard.ModernStyle) + ThemeWizard.setOptions(QtGui.QWizard.IndependentPages|QtGui.QWizard.NoBackButtonOnStartPage) + self.welcomePage = QtGui.QWizardPage() + self.welcomePage.setTitle(u'') + self.welcomePage.setSubTitle(u'') + self.welcomePage.setObjectName(u'welcomePage') + self.welcomeLayout = QtGui.QHBoxLayout(self.welcomePage) + self.welcomeLayout.setSpacing(8) + self.welcomeLayout.setMargin(0) + self.welcomeLayout.setObjectName(u'welcomeLayout') + self.importBibleImage = QtGui.QLabel(self.welcomePage) + self.importBibleImage.setMinimumSize(QtCore.QSize(163, 0)) + self.importBibleImage.setMaximumSize(QtCore.QSize(163, 16777215)) + self.importBibleImage.setLineWidth(0) + self.importBibleImage.setText(u'') + self.importBibleImage.setPixmap(QtGui.QPixmap(u':/wizards/wizard_importbible.bmp')) + self.importBibleImage.setIndent(0) + self.importBibleImage.setObjectName(u'importBibleImage') + self.welcomeLayout.addWidget(self.importBibleImage) + self.welcomePageLayout = QtGui.QVBoxLayout() + self.welcomePageLayout.setSpacing(8) + self.welcomePageLayout.setObjectName(u'welcomePageLayout') + self.titleLabel = QtGui.QLabel(self.welcomePage) + self.titleLabel.setObjectName(u'titleLabel') + self.welcomePageLayout.addWidget(self.titleLabel) + spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + self.welcomePageLayout.addItem(spacerItem) + self.informationLabel = QtGui.QLabel(self.welcomePage) + self.informationLabel.setWordWrap(True) + self.informationLabel.setMargin(10) + self.informationLabel.setObjectName(u'informationLabel') + self.welcomePageLayout.addWidget(self.informationLabel) + spacerItem1 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.welcomePageLayout.addItem(spacerItem1) + self.welcomeLayout.addLayout(self.welcomePageLayout) + ThemeWizard.addPage(self.welcomePage) + self.backgroundPage = QtGui.QWizardPage() + self.backgroundPage.setObjectName(u'backgroundPage') + self.backgroundLayout = QtGui.QFormLayout(self.backgroundPage) + self.backgroundLayout.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow) + self.backgroundLayout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.backgroundLayout.setMargin(20) + self.backgroundLayout.setSpacing(8) + self.backgroundLayout.setObjectName(u'backgroundLayout') + self.backgroundTypeLabel = QtGui.QLabel(self.backgroundPage) + self.backgroundTypeLabel.setObjectName(u'backgroundTypeLabel') + self.backgroundLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.backgroundTypeLabel) + self.backgroundTypeComboBox = QtGui.QComboBox(self.backgroundPage) + self.backgroundTypeComboBox.setObjectName(u'backgroundTypeComboBox') + self.backgroundTypeComboBox.addItem(u'') + self.backgroundTypeComboBox.addItem(u'') + self.backgroundTypeComboBox.addItem(u'') + self.backgroundLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.backgroundTypeComboBox) + self.color1Label = QtGui.QLabel(self.backgroundPage) + self.color1Label.setObjectName(u'color1Label') + self.backgroundLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.color1Label) + self.color1PushButton = QtGui.QPushButton(self.backgroundPage) + self.color1PushButton.setText(u'') + self.color1PushButton.setObjectName(u'color1PushButton') + self.backgroundLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.color1PushButton) + self.color2Label = QtGui.QLabel(self.backgroundPage) + self.color2Label.setObjectName(u'color2Label') + self.backgroundLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.color2Label) + self.color2PushButton = QtGui.QPushButton(self.backgroundPage) + self.color2PushButton.setText(u'') + self.color2PushButton.setObjectName(u'color2PushButton') + self.backgroundLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.color2PushButton) + self.imageLabel = QtGui.QLabel(self.backgroundPage) + self.imageLabel.setObjectName(u'imageLabel') + self.backgroundLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.imageLabel) + self.imageLayout = QtGui.QHBoxLayout() + self.imageLayout.setSpacing(8) + self.imageLayout.setObjectName(u'imageLayout') + self.imageLineEdit = QtGui.QLineEdit(self.backgroundPage) + self.imageLineEdit.setObjectName(u'imageLineEdit') + self.imageLayout.addWidget(self.imageLineEdit) + self.imageBrowseButton = QtGui.QToolButton(self.backgroundPage) + self.imageBrowseButton.setText(u'') + self.imageBrowseButton.setIcon(build_icon(u':/general/general_open.png')) + self.imageBrowseButton.setObjectName(u'imageBrowseButton') + self.imageLayout.addWidget(self.imageBrowseButton) + self.backgroundLayout.setLayout(3, QtGui.QFormLayout.FieldRole, self.imageLayout) + self.gradientLabel = QtGui.QLabel(self.backgroundPage) + self.gradientLabel.setObjectName(u'gradientLabel') + self.backgroundLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.gradientLabel) + self.gradientComboBox = QtGui.QComboBox(self.backgroundPage) + self.gradientComboBox.setObjectName(u'gradientComboBox') + self.gradientComboBox.addItem(u'') + self.gradientComboBox.addItem(u'') + self.gradientComboBox.addItem(u'') + self.gradientComboBox.addItem(u'') + self.gradientComboBox.addItem(u'') + self.backgroundLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.gradientComboBox) + ThemeWizard.addPage(self.backgroundPage) + self.mainAreaPage = QtGui.QWizardPage() + self.mainAreaPage.setObjectName(u'mainAreaPage') + self.formLayout = QtGui.QFormLayout(self.mainAreaPage) + self.formLayout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) + self.formLayout.setContentsMargins(-1, 20, 20, 20) + self.formLayout.setSpacing(8) + self.formLayout.setObjectName(u'formLayout') + self.mainFontLabel = QtGui.QLabel(self.mainAreaPage) + self.mainFontLabel.setObjectName(u'mainFontLabel') + self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.mainFontLabel) + self.mainFontComboBox = QtGui.QFontComboBox(self.mainAreaPage) + self.mainFontComboBox.setObjectName(u'mainFontComboBox') + self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.mainFontComboBox) + self.mainColorLabel = QtGui.QLabel(self.mainAreaPage) + self.mainColorLabel.setObjectName(u'mainColorLabel') + self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.mainColorLabel) + self.mainColorPushButton = QtGui.QPushButton(self.mainAreaPage) + self.mainColorPushButton.setText(u'') + self.mainColorPushButton.setObjectName(u'mainColorPushButton') + self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.mainColorPushButton) + self.mainSizeLabel = QtGui.QLabel(self.mainAreaPage) + self.mainSizeLabel.setObjectName(u'mainSizeLabel') + self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.mainSizeLabel) + self.mainSizeLayout = QtGui.QHBoxLayout() + self.mainSizeLayout.setSpacing(8) + self.mainSizeLayout.setMargin(0) + self.mainSizeLayout.setObjectName(u'mainSizeLayout') + self.mainSizeSpinBox = QtGui.QSpinBox(self.mainAreaPage) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.mainSizeSpinBox.sizePolicy().hasHeightForWidth()) + self.mainSizeSpinBox.setSizePolicy(sizePolicy) + self.mainSizeSpinBox.setMinimumSize(QtCore.QSize(70, 0)) + self.mainSizeSpinBox.setMaximum(999) + self.mainSizeSpinBox.setProperty(u'value', 16) + self.mainSizeSpinBox.setObjectName(u'mainSizeSpinBox') + self.mainSizeLayout.addWidget(self.mainSizeSpinBox) + self.mainLineCountLabel = QtGui.QLabel(self.mainAreaPage) + self.mainLineCountLabel.setObjectName(u'mainLineCountLabel') + self.mainSizeLayout.addWidget(self.mainLineCountLabel) + self.formLayout.setLayout(2, QtGui.QFormLayout.FieldRole, self.mainSizeLayout) + self.lineSpacingLabel = QtGui.QLabel(self.mainAreaPage) + self.lineSpacingLabel.setObjectName(u'lineSpacingLabel') + self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.lineSpacingLabel) + self.lineSpacingSpinBox = QtGui.QSpinBox(self.mainAreaPage) + self.lineSpacingSpinBox.setMinimum(-50) + self.lineSpacingSpinBox.setMaximum(50) + self.lineSpacingSpinBox.setObjectName(u'lineSpacingSpinBox') + self.formLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.lineSpacingSpinBox) + self.outlineCheckBox = QtGui.QCheckBox(self.mainAreaPage) + self.outlineCheckBox.setObjectName(u'outlineCheckBox') + self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.outlineCheckBox) + self.outlineLayout = QtGui.QHBoxLayout() + self.outlineLayout.setObjectName(u'outlineLayout') + self.outlineColorPushButton = QtGui.QPushButton(self.mainAreaPage) + self.outlineColorPushButton.setEnabled(True) + self.outlineColorPushButton.setText(u'') + self.outlineColorPushButton.setObjectName(u'outlineColorPushButton') + self.outlineLayout.addWidget(self.outlineColorPushButton) + self.outlineSizeLabel = QtGui.QLabel(self.mainAreaPage) + self.outlineSizeLabel.setObjectName(u'outlineSizeLabel') + self.outlineLayout.addWidget(self.outlineSizeLabel) + self.outlineSizeSpinBox = QtGui.QSpinBox(self.mainAreaPage) + self.outlineSizeSpinBox.setObjectName(u'outlineSizeSpinBox') + self.outlineLayout.addWidget(self.outlineSizeSpinBox) + self.formLayout.setLayout(4, QtGui.QFormLayout.FieldRole, self.outlineLayout) + self.shadowCheckBox = QtGui.QCheckBox(self.mainAreaPage) + self.shadowCheckBox.setObjectName(u'shadowCheckBox') + self.formLayout.setWidget(5, QtGui.QFormLayout.LabelRole, self.shadowCheckBox) + self.shadowLayout = QtGui.QHBoxLayout() + self.shadowLayout.setObjectName(u'shadowLayout') + self.shadowColorPushButton = QtGui.QPushButton(self.mainAreaPage) + self.shadowColorPushButton.setEnabled(True) + self.shadowColorPushButton.setText(u'') + self.shadowColorPushButton.setObjectName(u'shadowColorPushButton') + self.shadowLayout.addWidget(self.shadowColorPushButton) + self.shadowSizeLabel = QtGui.QLabel(self.mainAreaPage) + self.shadowSizeLabel.setObjectName(u'shadowSizeLabel') + self.shadowLayout.addWidget(self.shadowSizeLabel) + self.shadowSizeSpinBox = QtGui.QSpinBox(self.mainAreaPage) + self.shadowSizeSpinBox.setObjectName(u'shadowSizeSpinBox') + self.shadowLayout.addWidget(self.shadowSizeSpinBox) + self.formLayout.setLayout(5, QtGui.QFormLayout.FieldRole, self.shadowLayout) + self.boldCheckBox = QtGui.QCheckBox(self.mainAreaPage) + self.boldCheckBox.setObjectName(u'boldCheckBox') + self.formLayout.setWidget(6, QtGui.QFormLayout.FieldRole, self.boldCheckBox) + self.italicsCheckBox = QtGui.QCheckBox(self.mainAreaPage) + self.italicsCheckBox.setObjectName(u'italicsCheckBox') + self.formLayout.setWidget(7, QtGui.QFormLayout.FieldRole, self.italicsCheckBox) + ThemeWizard.addPage(self.mainAreaPage) + self.footerAreaPage = QtGui.QWizardPage() + self.footerAreaPage.setObjectName(u'footerAreaPage') + self.footerLayout = QtGui.QFormLayout(self.footerAreaPage) + self.footerLayout.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow) + self.footerLayout.setContentsMargins(50, 20, 20, 20) + self.footerLayout.setSpacing(8) + self.footerLayout.setObjectName(u'footerLayout') + self.footerFontLabel = QtGui.QLabel(self.footerAreaPage) + self.footerFontLabel.setObjectName(u'footerFontLabel') + self.footerLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.footerFontLabel) + self.footerFontComboBox = QtGui.QFontComboBox(self.footerAreaPage) + self.footerFontComboBox.setObjectName(u'footerFontComboBox') + self.footerLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.footerFontComboBox) + self.footerColorLabel = QtGui.QLabel(self.footerAreaPage) + self.footerColorLabel.setObjectName(u'footerColorLabel') + self.footerLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.footerColorLabel) + self.footerColorPushButton = QtGui.QPushButton(self.footerAreaPage) + self.footerColorPushButton.setText(u'') + self.footerColorPushButton.setObjectName(u'footerColorPushButton') + self.footerLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.footerColorPushButton) + self.footerSizeLabel = QtGui.QLabel(self.footerAreaPage) + self.footerSizeLabel.setObjectName(u'footerSizeLabel') + self.footerLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.footerSizeLabel) + self.footerSizeSpinBox = QtGui.QSpinBox(self.footerAreaPage) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.footerSizeSpinBox.sizePolicy().hasHeightForWidth()) + self.footerSizeSpinBox.setSizePolicy(sizePolicy) + self.footerSizeSpinBox.setMinimumSize(QtCore.QSize(70, 0)) + self.footerSizeSpinBox.setMaximum(999) + self.footerSizeSpinBox.setProperty(u'value', 10) + self.footerSizeSpinBox.setObjectName(u'footerSizeSpinBox') + self.footerLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.footerSizeSpinBox) + ThemeWizard.addPage(self.footerAreaPage) + self.alignmentPage = QtGui.QWizardPage() + self.alignmentPage.setObjectName(u'alignmentPage') + self.formLayout_2 = QtGui.QFormLayout(self.alignmentPage) + self.formLayout_2.setMargin(20) + self.formLayout_2.setObjectName(u'formLayout_2') + self.horizontalLabel = QtGui.QLabel(self.alignmentPage) + self.horizontalLabel.setObjectName(u'horizontalLabel') + self.formLayout_2.setWidget(0, QtGui.QFormLayout.LabelRole, self.horizontalLabel) + self.horizontalComboBox = QtGui.QComboBox(self.alignmentPage) + self.horizontalComboBox.setEditable(False) + self.horizontalComboBox.setObjectName(u'horizontalComboBox') + self.horizontalComboBox.addItem(u'') + self.horizontalComboBox.addItem(u'') + self.horizontalComboBox.addItem(u'') + self.formLayout_2.setWidget(0, QtGui.QFormLayout.FieldRole, self.horizontalComboBox) + self.verticalLabel = QtGui.QLabel(self.alignmentPage) + self.verticalLabel.setObjectName(u'verticalLabel') + self.formLayout_2.setWidget(1, QtGui.QFormLayout.LabelRole, self.verticalLabel) + self.verticalComboBox = QtGui.QComboBox(self.alignmentPage) + self.verticalComboBox.setObjectName(u'verticalComboBox') + self.verticalComboBox.addItem(u'') + self.verticalComboBox.addItem(u'') + self.verticalComboBox.addItem(u'') + self.formLayout_2.setWidget(1, QtGui.QFormLayout.FieldRole, self.verticalComboBox) + self.transitionsCheckBox = QtGui.QCheckBox(self.alignmentPage) + self.transitionsCheckBox.setObjectName(u'transitionsCheckBox') + self.formLayout_2.setWidget(2, QtGui.QFormLayout.FieldRole, self.transitionsCheckBox) + ThemeWizard.addPage(self.alignmentPage) + self.areaPositionPage = QtGui.QWizardPage() + self.areaPositionPage.setObjectName(u'areaPositionPage') + self.gridLayout_2 = QtGui.QGridLayout(self.areaPositionPage) + self.gridLayout_2.setMargin(20) + self.gridLayout_2.setSpacing(8) + self.gridLayout_2.setObjectName(u'gridLayout_2') + self.mainPositionGroupBox = QtGui.QGroupBox(self.areaPositionPage) + self.mainPositionGroupBox.setMinimumSize(QtCore.QSize(248, 0)) + self.mainPositionGroupBox.setObjectName(u'mainPositionGroupBox') + self.mainPositionLayout = QtGui.QFormLayout(self.mainPositionGroupBox) + self.mainPositionLayout.setMargin(8) + self.mainPositionLayout.setSpacing(8) + self.mainPositionLayout.setObjectName(u'mainPositionLayout') + self.mainDefaultPositionCheckBox = QtGui.QCheckBox(self.mainPositionGroupBox) + self.mainDefaultPositionCheckBox.setChecked(True) + self.mainDefaultPositionCheckBox.setTristate(False) + self.mainDefaultPositionCheckBox.setObjectName(u'mainDefaultPositionCheckBox') + self.mainPositionLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.mainDefaultPositionCheckBox) + self.nainXLabel = QtGui.QLabel(self.mainPositionGroupBox) + self.nainXLabel.setObjectName(u'nainXLabel') + self.mainPositionLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.nainXLabel) + self.mainXSpinBox = QtGui.QSpinBox(self.mainPositionGroupBox) + self.mainXSpinBox.setEnabled(False) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.mainXSpinBox.sizePolicy().hasHeightForWidth()) + self.mainXSpinBox.setSizePolicy(sizePolicy) + self.mainXSpinBox.setMinimumSize(QtCore.QSize(78, 0)) + self.mainXSpinBox.setMaximum(9999) + self.mainXSpinBox.setProperty(u'value', 0) + self.mainXSpinBox.setObjectName(u'mainXSpinBox') + self.mainPositionLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.mainXSpinBox) + self.mainYSpinBox = QtGui.QSpinBox(self.mainPositionGroupBox) + self.mainYSpinBox.setEnabled(False) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.mainYSpinBox.sizePolicy().hasHeightForWidth()) + self.mainYSpinBox.setSizePolicy(sizePolicy) + self.mainYSpinBox.setMinimumSize(QtCore.QSize(78, 0)) + self.mainYSpinBox.setMaximum(9999) + self.mainYSpinBox.setObjectName(u'mainYSpinBox') + self.mainPositionLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.mainYSpinBox) + self.mainYLabel = QtGui.QLabel(self.mainPositionGroupBox) + self.mainYLabel.setObjectName(u'mainYLabel') + self.mainPositionLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.mainYLabel) + self.mainWidthSpinBox = QtGui.QSpinBox(self.mainPositionGroupBox) + self.mainWidthSpinBox.setEnabled(False) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.mainWidthSpinBox.sizePolicy().hasHeightForWidth()) + self.mainWidthSpinBox.setSizePolicy(sizePolicy) + self.mainWidthSpinBox.setMinimumSize(QtCore.QSize(78, 0)) + self.mainWidthSpinBox.setMaximum(9999) + self.mainWidthSpinBox.setObjectName(u'mainWidthSpinBox') + self.mainPositionLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.mainWidthSpinBox) + self.mainWidthLabel = QtGui.QLabel(self.mainPositionGroupBox) + self.mainWidthLabel.setObjectName(u'mainWidthLabel') + self.mainPositionLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.mainWidthLabel) + self.mainHeightSpinBox = QtGui.QSpinBox(self.mainPositionGroupBox) + self.mainHeightSpinBox.setEnabled(False) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.mainHeightSpinBox.sizePolicy().hasHeightForWidth()) + self.mainHeightSpinBox.setSizePolicy(sizePolicy) + self.mainHeightSpinBox.setMinimumSize(QtCore.QSize(78, 0)) + self.mainHeightSpinBox.setMaximum(9999) + self.mainHeightSpinBox.setObjectName(u'mainHeightSpinBox') + self.mainPositionLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.mainHeightSpinBox) + self.mainHeightLabel = QtGui.QLabel(self.mainPositionGroupBox) + self.mainHeightLabel.setObjectName(u'mainHeightLabel') + self.mainPositionLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.mainHeightLabel) + self.gridLayout_2.addWidget(self.mainPositionGroupBox, 1, 0, 1, 1) + self.footerPositionGroupBox = QtGui.QGroupBox(self.areaPositionPage) + self.footerPositionGroupBox.setMinimumSize(QtCore.QSize(248, 0)) + self.footerPositionGroupBox.setObjectName(u'footerPositionGroupBox') + self.footerPositionLayout = QtGui.QFormLayout(self.footerPositionGroupBox) + self.footerPositionLayout.setMargin(8) + self.footerPositionLayout.setSpacing(8) + self.footerPositionLayout.setObjectName(u'footerPositionLayout') + self.footerXLabel = QtGui.QLabel(self.footerPositionGroupBox) + self.footerXLabel.setObjectName(u'footerXLabel') + self.footerPositionLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.footerXLabel) + self.footerXSpinBox = QtGui.QSpinBox(self.footerPositionGroupBox) + self.footerXSpinBox.setEnabled(False) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.footerXSpinBox.sizePolicy().hasHeightForWidth()) + self.footerXSpinBox.setSizePolicy(sizePolicy) + self.footerXSpinBox.setMinimumSize(QtCore.QSize(78, 0)) + self.footerXSpinBox.setMaximum(9999) + self.footerXSpinBox.setProperty(u'value', 0) + self.footerXSpinBox.setObjectName(u'footerXSpinBox') + self.footerPositionLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.footerXSpinBox) + self.footerYLabel = QtGui.QLabel(self.footerPositionGroupBox) + self.footerYLabel.setObjectName(u'footerYLabel') + self.footerPositionLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.footerYLabel) + self.footerYSpinBox = QtGui.QSpinBox(self.footerPositionGroupBox) + self.footerYSpinBox.setEnabled(False) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.footerYSpinBox.sizePolicy().hasHeightForWidth()) + self.footerYSpinBox.setSizePolicy(sizePolicy) + self.footerYSpinBox.setMinimumSize(QtCore.QSize(78, 0)) + self.footerYSpinBox.setMaximum(9999) + self.footerYSpinBox.setProperty(u'value', 0) + self.footerYSpinBox.setObjectName(u'footerYSpinBox') + self.footerPositionLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.footerYSpinBox) + self.footerWidthLabel = QtGui.QLabel(self.footerPositionGroupBox) + self.footerWidthLabel.setObjectName(u'footerWidthLabel') + self.footerPositionLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.footerWidthLabel) + self.footerWidthSpinBox = QtGui.QSpinBox(self.footerPositionGroupBox) + self.footerWidthSpinBox.setEnabled(False) + self.footerWidthSpinBox.setMinimumSize(QtCore.QSize(78, 0)) + self.footerWidthSpinBox.setMaximum(9999) + self.footerWidthSpinBox.setObjectName(u'footerWidthSpinBox') + self.footerPositionLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.footerWidthSpinBox) + self.footerHeightLabel = QtGui.QLabel(self.footerPositionGroupBox) + self.footerHeightLabel.setObjectName(u'footerHeightLabel') + self.footerPositionLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.footerHeightLabel) + self.footerHeightSpinBox = QtGui.QSpinBox(self.footerPositionGroupBox) + self.footerHeightSpinBox.setEnabled(False) + self.footerHeightSpinBox.setMinimumSize(QtCore.QSize(78, 0)) + self.footerHeightSpinBox.setMaximum(9999) + self.footerHeightSpinBox.setObjectName(u'footerHeightSpinBox') + self.footerPositionLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.footerHeightSpinBox) + self.footerDefaultPositionCheckBox = QtGui.QCheckBox(self.footerPositionGroupBox) + self.footerDefaultPositionCheckBox.setChecked(True) + self.footerDefaultPositionCheckBox.setObjectName(u'footerDefaultPositionCheckBox') + self.footerPositionLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.footerDefaultPositionCheckBox) + self.gridLayout_2.addWidget(self.footerPositionGroupBox, 1, 1, 1, 1) + ThemeWizard.addPage(self.areaPositionPage) + self.previewPage = QtGui.QWizardPage() + self.previewPage.setObjectName(u'previewPage') + self.themeNameLabel = QtGui.QLabel(self.previewPage) + self.themeNameLabel.setGeometry(QtCore.QRect(20, 10, 82, 16)) + self.themeNameLabel.setTextFormat(QtCore.Qt.PlainText) + self.themeNameLabel.setObjectName(u'themeNameLabel') + self.previewLabel = QtGui.QLabel(self.previewPage) + self.previewLabel.setGeometry(QtCore.QRect(250, 60, 48, 16)) + self.previewLabel.setAlignment(QtCore.Qt.AlignCenter) + self.previewLabel.setObjectName(u'previewLabel') + self.themeNameEdit = QtGui.QLineEdit(self.previewPage) + self.themeNameEdit.setGeometry(QtCore.QRect(117, 4, 351, 23)) + self.themeNameEdit.setObjectName(u'themeNameEdit') + self.groupBox = QtGui.QGroupBox(self.previewPage) + self.groupBox.setGeometry(QtCore.QRect(40, 80, 464, 214)) + self.groupBox.setTitle(u'') + self.groupBox.setObjectName(u'groupBox') + self.horizontalLayout = QtGui.QHBoxLayout(self.groupBox) + self.horizontalLayout.setObjectName(u'horizontalLayout') + spacerItem2 = QtGui.QSpacerItem(58, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem2) + self.previewBoxLabel = QtGui.QLabel(self.groupBox) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.previewBoxLabel.sizePolicy().hasHeightForWidth()) + self.previewBoxLabel.setSizePolicy(sizePolicy) + self.previewBoxLabel.setMinimumSize(QtCore.QSize(300, 200)) + self.previewBoxLabel.setFrameShape(QtGui.QFrame.WinPanel) + self.previewBoxLabel.setFrameShadow(QtGui.QFrame.Sunken) + self.previewBoxLabel.setLineWidth(1) + self.previewBoxLabel.setText(u'') + self.previewBoxLabel.setScaledContents(True) + self.previewBoxLabel.setObjectName(u'previewBoxLabel') + self.horizontalLayout.addWidget(self.previewBoxLabel) + spacerItem3 = QtGui.QSpacerItem(78, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem3) + ThemeWizard.addPage(self.previewPage) + self.themeNameLabel.setBuddy(self.themeNameEdit) + + self.retranslateUi(ThemeWizard) + QtCore.QObject.connect(ThemeWizard, QtCore.SIGNAL(u'accepted()'), ThemeWizard.accept) + QtCore.QMetaObject.connectSlotsByName(ThemeWizard) + + def retranslateUi(self, ThemeWizard): + ThemeWizard.setWindowTitle(translate('OpenLP.ThemeForm', 'Theme Wizard')) + self.titleLabel.setText(translate('OpenLP.ThemeForm', '\n' +'\n' +'

Welcome to the Theme Wizard

')) + self.informationLabel.setText(translate('OpenLP.ThemeForm', 'This wizard will help you to maintain Themes . Click the next button below to start the process by setting up your background.')) + self.backgroundPage.setTitle(translate('OpenLP.ThemeForm', 'Set Up Background')) + self.backgroundPage.setSubTitle(translate('OpenLP.ThemeForm', 'Set up your theme\'s background according to the parameters below.')) + self.backgroundTypeLabel.setText(translate('OpenLP.ThemeForm', 'Background type:')) + self.backgroundTypeComboBox.setItemText(0, translate('OpenLP.ThemeForm', 'Solid Color')) + self.backgroundTypeComboBox.setItemText(1, translate('OpenLP.ThemeForm', 'Gradient')) + self.backgroundTypeComboBox.setItemText(2, translate('OpenLP.ThemeForm', 'Image')) + self.color1Label.setText(translate('OpenLP.ThemeForm', '')) + self.color2Label.setText(translate('OpenLP.ThemeForm', '')) + self.imageLabel.setText(translate('OpenLP.ThemeForm', 'Image:')) + self.gradientLabel.setText(translate('OpenLP.ThemeForm', 'Gradient:')) + self.gradientComboBox.setItemText(0, translate('OpenLP.ThemeForm', 'Horizontal')) + self.gradientComboBox.setItemText(1, translate('OpenLP.ThemeForm', 'Vertical')) + self.gradientComboBox.setItemText(2, translate('OpenLP.ThemeForm', 'Circular')) + self.gradientComboBox.setItemText(3, translate('OpenLP.ThemeForm', 'Top Left - Bottom Right')) + self.gradientComboBox.setItemText(4, translate('OpenLP.ThemeForm', 'Bottom Left - Top Right')) + self.mainAreaPage.setTitle(translate('OpenLP.ThemeForm', 'Main Area Font Details')) + self.mainAreaPage.setSubTitle(translate('OpenLP.ThemeForm', 'Define the font and display characteristics for the Display text')) + self.mainFontLabel.setText(translate('OpenLP.ThemeForm', 'Font:')) + self.mainColorLabel.setText(translate('OpenLP.ThemeForm', 'Color:')) + self.mainSizeLabel.setText(translate('OpenLP.ThemeForm', 'Size:')) + self.mainSizeSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'pt')) + self.mainLineCountLabel.setText(translate('OpenLP.ThemeForm', '(%d lines per slide)')) + self.lineSpacingLabel.setText(translate('OpenLP.ThemeForm', 'Line Spacing:')) + self.lineSpacingSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'pt')) + self.outlineCheckBox.setText(translate('OpenLP.ThemeForm', '&Outline:')) + self.outlineSizeLabel.setText(translate('OpenLP.ThemeForm', 'Size:')) + self.outlineSizeSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'pt')) + self.shadowCheckBox.setText(translate('OpenLP.ThemeForm', '&Shadow:')) + self.shadowSizeLabel.setText(translate('OpenLP.ThemeForm', 'Size:')) + self.shadowSizeSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'pt')) + self.boldCheckBox.setText(translate('OpenLP.ThemeForm', 'Bold Display')) + self.italicsCheckBox.setText(translate('OpenLP.ThemeForm', 'Italic Display')) + self.footerAreaPage.setTitle(translate('OpenLP.ThemeForm', 'Footer Area Font Details')) + self.footerAreaPage.setSubTitle(translate('OpenLP.ThemeForm', 'Define the font and display characteristics for the Footer text')) + self.footerFontLabel.setText(translate('OpenLP.ThemeForm', 'Font:')) + self.footerColorLabel.setText(translate('OpenLP.ThemeForm', 'Color:')) + self.footerSizeLabel.setText(translate('OpenLP.ThemeForm', 'Size:')) + self.footerSizeSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'pt')) + self.alignmentPage.setTitle(translate('OpenLP.ThemeForm', 'Text Formatting Details')) + self.alignmentPage.setSubTitle(translate('OpenLP.ThemeForm', 'Allows additional display formatting information to be defined')) + self.horizontalLabel.setText(translate('OpenLP.ThemeForm', 'Horizontal Align:')) + self.horizontalComboBox.setItemText(0, translate('OpenLP.ThemeForm', 'Left')) + self.horizontalComboBox.setItemText(1, translate('OpenLP.ThemeForm', 'Right')) + self.horizontalComboBox.setItemText(2, translate('OpenLP.ThemeForm', 'Center')) + self.verticalLabel.setText(translate('OpenLP.ThemeForm', 'Vertcal Align:')) + self.verticalComboBox.setItemText(0, translate('OpenLP.ThemeForm', 'Top')) + self.verticalComboBox.setItemText(1, translate('OpenLP.ThemeForm', 'Middle')) + self.verticalComboBox.setItemText(2, translate('OpenLP.ThemeForm', 'Bottom')) + self.transitionsCheckBox.setText(translate('OpenLP.ThemeForm', 'Transitions')) + self.areaPositionPage.setTitle(translate('OpenLP.ThemeForm', 'Output Area Locations')) + self.areaPositionPage.setSubTitle(translate('OpenLP.ThemeForm', 'Allows you to change and move the Main and Footer areas.')) + self.mainPositionGroupBox.setTitle(translate('OpenLP.ThemeForm', '&Main Area')) + self.mainDefaultPositionCheckBox.setText(translate('OpenLP.ThemeForm', '&Use default location')) + self.nainXLabel.setText(translate('OpenLP.ThemeForm', 'X position:')) + self.mainXSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'px')) + self.mainYSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'px')) + self.mainYLabel.setText(translate('OpenLP.ThemeForm', 'Y position:')) + self.mainWidthSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'px')) + self.mainWidthLabel.setText(translate('OpenLP.ThemeForm', 'Width:')) + self.mainHeightSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'px')) + self.mainHeightLabel.setText(translate('OpenLP.ThemeForm', 'Height:')) + self.footerPositionGroupBox.setTitle(translate('OpenLP.ThemeForm', 'Footer Area')) + self.footerXLabel.setText(translate('OpenLP.ThemeForm', 'X position:')) + self.footerXSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'px')) + self.footerYLabel.setText(translate('OpenLP.ThemeForm', 'Y position:')) + self.footerYSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'px')) + self.footerWidthLabel.setText(translate('OpenLP.ThemeForm', 'Width:')) + self.footerWidthSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'px')) + self.footerHeightLabel.setText(translate('OpenLP.ThemeForm', 'Height:')) + self.footerHeightSpinBox.setSuffix(translate('OpenLP.ThemeForm', 'px')) + self.footerDefaultPositionCheckBox.setText(translate('OpenLP.ThemeForm', 'Use default location')) + self.previewPage.setTitle(translate('OpenLP.ThemeForm', 'Save and Preview')) + self.previewPage.setSubTitle(translate('OpenLP.ThemeForm', 'View the theme and save it replacing the current one or change the name to create a new theme')) + self.themeNameLabel.setText(translate('OpenLP.ThemeForm', 'Theme name:')) + self.previewLabel.setText(translate('OpenLP.ThemeForm', 'Preview')) diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index e6a474f5c..235862ae8 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -213,7 +213,7 @@ class BGExtract(object): finally: if not page: return None - cleaner = [(re.compile(' |
'), lambda match: '')] + cleaner = [(re.compile(' |
|\'\+\''), lambda match: '')] soup = None try: soup = BeautifulSoup(page, markupMassage=cleaner) diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 9cdcb9ed7..8549e6449 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -165,7 +165,7 @@ class CustomMediaItem(MediaManagerItem): customSlide = self.parent.manager.get_object(CustomSlide, item_id) title = customSlide.title credit = customSlide.credits - service_item.editId = item_id + service_item.edit_id = item_id theme = customSlide.theme_name if theme: service_item.theme = theme diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 6e3695149..030f67153 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -125,6 +125,8 @@ class MediaMediaItem(MediaManagerItem): service_item.title = unicode( translate('MediaPlugin.MediaItem', 'Media')) service_item.add_capability(ItemCapabilities.RequiresMedia) + # force a nonexistent theme + service_item.theme = -1 frame = u':/media/image_clapperboard.png' (path, name) = os.path.split(filename) service_item.add_from_command(path, name, frame) diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py index b32712d8d..d954bfb1f 100644 --- a/openlp/plugins/songs/forms/editverseform.py +++ b/openlp/plugins/songs/forms/editverseform.py @@ -60,7 +60,6 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog): def contextMenu(self, point): item = self.serviceManagerList.itemAt(point) - print item def insertVerse(self, title, num=1): if self.verseTextEdit.textCursor().columnNumber() != 0: diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 63496affd..cb2d6a6e0 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -192,6 +192,7 @@ class SongMediaItem(MediaManagerItem): Handle the exit from the edit dialog and trigger remote updates of songs """ + log.debug(u'onSongListLoad') # Called to redisplay the song list screen edit from a search # or from the exit of the Song edit dialog. If remote editing is active # Trigger it and clean up so it will not update again. @@ -259,6 +260,7 @@ class SongMediaItem(MediaManagerItem): Receiver.send_message(u'songs_load_list') def onNewClick(self): + log.debug(u'onNewClick') self.edit_song_form.newSong() self.edit_song_form.exec_() @@ -266,6 +268,7 @@ class SongMediaItem(MediaManagerItem): self.song_maintenance_form.exec_() def onRemoteEditClear(self): + log.debug(u'onRemoteEditClear') self.remoteTriggered = None self.remoteSong = -1 @@ -275,6 +278,7 @@ class SongMediaItem(MediaManagerItem): the Song Id in the payload along with an indicator to say which type of display is required. """ + log.debug(u'onRemoteEdit %s' % songid) fields = songid.split(u':') valid = self.parent.manager.get_object(Song, fields[1]) if valid: @@ -287,6 +291,7 @@ class SongMediaItem(MediaManagerItem): """ Edit a song """ + log.debug(u'onEditClick') if check_item_selected(self.listView, translate('SongsPlugin.MediaItem', 'You must select an item to edit.')): @@ -324,6 +329,7 @@ class SongMediaItem(MediaManagerItem): self.onSearchTextButtonClick() def generateSlideData(self, service_item, item=None): + log.debug(u'generateSlideData (%s:%s)' % (service_item, item)) raw_footer = [] author_list = u'' author_audit = [] @@ -345,7 +351,7 @@ class SongMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.AddIfNewItem) song = self.parent.manager.get_object(Song, item_id) service_item.theme = song.theme_name - service_item.editId = item_id + service_item.edit_id = item_id if song.lyrics.startswith(u' - Wizard - + ThemeWizard + 0 0 - 559 - 487 + 550 + 386 @@ -20,16 +20,16 @@ QWizard::ModernStyle - QWizard::HaveCustomButton1|QWizard::NoBackButtonOnStartPage + QWizard::IndependentPages|QWizard::NoBackButtonOnStartPage - + - + 8 @@ -37,7 +37,7 @@ 0 - + 163 @@ -65,12 +65,12 @@ - + 8 - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> @@ -81,7 +81,7 @@ p, li { white-space: pre-wrap; } - + Qt::Vertical @@ -97,9 +97,9 @@ p, li { white-space: pre-wrap; } - + - This wizard will help you to maintain Themes . Click the next button below to start the process.. + This wizard will help you to maintain Themes . Click the next button below to start the process by setting up your background. true @@ -110,7 +110,7 @@ p, li { white-space: pre-wrap; } - + Qt::Vertical @@ -126,341 +126,416 @@ p, li { white-space: pre-wrap; } - + - Select Import Source + Set Up Background - Select the import format, and where to import from. + Set up your theme's background according to the parameters below. - + + + QFormLayout::ExpandingFieldsGrow + - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 8 + + + 8 + + + 20 - + + + Background type: + + + + + - - - Background: - - + + Solid Color + - - - - Opaque - - - - - Transparent - - - + + Gradient + - + + + Image + + + - - - - - Background Type: - - - - - - - - Solid Color - - - - - Gradient - - - - - Image - - - - - - - - - - - - <Color1> - - - - - - - - - - - - - - - - - - <Color2> - - - - - - - - - - - - - - - - - - Gradient : - - - - - - - - Horizontal - - - - - Vertical - - - - - Circular - - - - - - - - - - - - Image: - - - - - - - - - - ... - - - - - - - - - - Main Area Font Details - - - Define the font and display charaistics for the Display text - - - - + - Font: + <Color1> - + + + + + - + - Font Color: + <Color2> + + + + + + + + + + + + + + Image: - + + + 8 + + + + + + + + + + + + :/general/general_open.png:/general/general_open.png + + + + + + + - + Gradient: - - - Size: - - - - - - - - 0 - 0 - - - - - 70 - 0 - - - - pt - - - 999 - - - 16 - - - - - - - Wrap Indentation - - - - - - - - - - TextLabel - - - - - - - Show Outline: - - - - - - - - - - - - - - Outline Color: - - - - - - - - - - - - - - Show Shadow: - - - - - - - - - - - - - - Shadow Color: - - - - - - - - + + + + Horizontal + + + + + Vertical + + + + + Circular + + + + + Top Left - Bottom Right + + + + + Bottom Left - Top Right + + - + - Footer Area Font Details + Main Area Font Details - Define the font and display charaistics for the Footer text + Define the font and display characteristics for the Display text - + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 8 + + + 8 + + + 20 + + + 20 + + + 20 + - + Font: - + - + - Font Color: + Color: - + - + Size: - + + + 8 + + + 0 + + + + + + 0 + 0 + + + + + 70 + 0 + + + + pt + + + 999 + + + 16 + + + + + + + (%d lines per slide) + + + + + + + + + Line Spacing: + + + + + + + pt + + + -50 + + + 50 + + + + + + + &Outline: + + + + + + + + + true + + + + + + + + + + Size: + + + + + + + pt + + + + + + + + + &Shadow: + + + + + + + + + true + + + + + + + + + + Size: + + + + + + + pt + + + + + + + + + Bold Display + + + + + + + Italic Display + + + + + + + + Footer Area Font Details + + + Define the font and display characteristics for the Footer text + + + + QFormLayout::ExpandingFieldsGrow + + + 8 + + + 8 + + + 50 + + + 20 + + + 20 + + + 20 + + + + + Font: + + + + + + + + + + Color: + + + + + + + + + + + + + + Size: + + + + + 0 @@ -484,410 +559,570 @@ p, li { white-space: pre-wrap; } - - + + + + + Text Formatting Details + + + Allows additional display formatting information to be defined + + + + 20 + + + - Show Outline: + Horizontal Align: - - + + + + false + + + + Left + + + + + Right + + + + + Center + + + + + + - + Vertcal Align: - - - - Shadow Color: - + + + + + Top + + + + + Middle + + + + + Bottom + + - - + + - - - - - - - - Show Shadow: - - - - - - - - - - - - - - Outline Color: - - - - - - - + Transitions - + - Text Display Layout + Output Area Locations Allows you to change and move the Main and Footer areas. - - - - + + 20 + + + 8 + + + + + + 248 + 0 + - - false + + &Main Area + + + 8 + + + 8 + + + 8 + + + + + &Use default location + + + true + + + false + + + + + + + X position: + + + + + + + false + + + + 0 + 0 + + + + + 78 + 0 + + + + px + + + 9999 + + + 0 + + + + + + + false + + + + 0 + 0 + + + + + 78 + 0 + + + + px + + + 9999 + + + + + + + Y position: + + + + + + + false + + + + 0 + 0 + + + + + 78 + 0 + + + + px + + + 9999 + + + + + + + Width: + + + + + + + false + + + + 0 + 0 + + + + + 78 + 0 + + + + px + + + 9999 + + + + + + + Height: + + + + - - - - - - Main Area - - - - - - - Footer Area - - - - - - - X Position: - - - - - - - - 0 - 0 - - - - - 78 - 0 - - - - px - - - 9999 - - - 0 - - - - - - - X Position: - - - - - - - - 0 - 0 - - - - - 78 - 0 - - - - px - - - 9999 - - - 0 - - - - - - - Y Position: - - - - - - - - 0 - 0 - - - - - 78 - 0 - - - - px - - - 9999 - - - - - - - Y Position: - - - - - - - - 0 - 0 - - - - - 78 - 0 - - - - px - - - 9999 - - - 0 - - - - - - - Width: - - - - - - - - 0 - 0 - - - - - 78 - 0 - - - - px - - - 9999 - - - - - - - Width: - - - - - - - - 78 - 0 - - - - px - - - 9999 - - - - - - - Height: - - - - - - - - 0 - 0 - - - - - 78 - 0 - - - - px - - - 9999 - - - - - - - Height: - - - - - - - - 78 - 0 - - - - px - - - 9999 - - - - - - - - - Use Default Location: + + + + + 248 + 0 + + + Footer Area + + + + 8 + + + 8 + + + 8 + + + + + X position: + + + + + + + false + + + + 0 + 0 + + + + + 78 + 0 + + + + px + + + 9999 + + + 0 + + + + + + + Y position: + + + + + + + false + + + + 0 + 0 + + + + + 78 + 0 + + + + px + + + 9999 + + + 0 + + + + + + + Width: + + + + + + + false + + + + 78 + 0 + + + + px + + + 9999 + + + + + + + Height: + + + + + + + false + + + + 78 + 0 + + + + px + + + 9999 + + + + + + + Use default location + + + true + + + + - + Save and Preview View the theme and save it replacing the current one or change the name to create a new theme - - - - - - - Theme Name: - - - - - - - - - - - - - - Preview - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 300 - 225 - - - - QFrame::WinPanel - - - QFrame::Sunken - - - 1 - - - - - - true - - - - - - + + + + 20 + 10 + 82 + 16 + + + + Theme name: + + + Qt::PlainText + + + themeNameEdit + + + + + + 250 + 60 + 48 + 16 + + + + Preview + + + Qt::AlignCenter + + + + + + 117 + 4 + 351 + 23 + + + + + + + 40 + 80 + 464 + 214 + + + + + + + + + + Qt::Horizontal + + + + 58 + 20 + + + + + + + + + 0 + 0 + + + + + 300 + 200 + + + + QFrame::WinPanel + + + QFrame::Sunken + + + 1 + + + + + + true + + + + + + + Qt::Horizontal + + + + 78 + 20 + + + + + + + + - + + + ThemeWizard + accepted() + ThemeWizard + accept() + + + 455 + 368 + + + 483 + 401 + + + + diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 7e8645441..0ecb87bca 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -83,6 +83,8 @@ wizard_importbible.bmp + service_collapse_all.png + service_expand_all.png service_notes.png service_item_notes.png service_bottom.png diff --git a/resources/images/service_collapse_all.png b/resources/images/service_collapse_all.png new file mode 100644 index 000000000..6e682fc4d Binary files /dev/null and b/resources/images/service_collapse_all.png differ diff --git a/resources/images/service_expand_all.png b/resources/images/service_expand_all.png new file mode 100644 index 000000000..09b4ba394 Binary files /dev/null and b/resources/images/service_expand_all.png differ