This commit is contained in:
Andreas Preikschat 2011-03-02 15:41:28 +01:00
commit 6c310efd52
45 changed files with 1053 additions and 126 deletions

View File

@ -273,6 +273,9 @@ def main():
qInitResources()
# Now create and actually run the application.
app = OpenLP(qt_args)
if sys.platform == 'darwin':
OpenLP.addLibraryPath(QtGui.QApplication.applicationDirPath()
+ "/qt4_plugins")
#i18n Set Language
language = LanguageManager.get_language()
appTranslator = LanguageManager.get_translator(language)

View File

@ -106,8 +106,8 @@ def translate(context, text, comment=None,
def get_text_file_string(text_file):
"""
Open a file and return its content as unicode string. If the supplied file
name is not a file then the function returns False. If there is an error
Open a file and return its content as unicode string. If the supplied file
name is not a file then the function returns False. If there is an error
loading the file or the content can't be decoded then the function will
return None.

View File

@ -65,7 +65,7 @@ def delete_database(plugin_name, db_file_name=None):
The name of the plugin to remove the database for
``db_file_name``
The database file name. Defaults to None resulting in the
The database file name. Defaults to None resulting in the
plugin_name being used.
"""
db_file_path = None
@ -91,6 +91,7 @@ class BaseModel(object):
instance.__setattr__(key, kwargs[key])
return instance
class Manager(object):
"""
Provide generic object persistence management
@ -107,7 +108,7 @@ class Manager(object):
The init_schema function for this database
``db_file_name``
The file name to use for this database. Defaults to None resulting
The file name to use for this database. Defaults to None resulting
in the plugin_name being used.
"""
settings = QtCore.QSettings()
@ -211,11 +212,11 @@ class Manager(object):
The type of objects to return
``filter_clause``
The filter governing selection of objects to return. Defaults to
The filter governing selection of objects to return. Defaults to
None.
``order_by_ref``
Any parameters to order the returned objects by. Defaults to None.
Any parameters to order the returned objects by. Defaults to None.
"""
query = self.session.query(object_class)
if filter_clause is not None:
@ -232,7 +233,7 @@ class Manager(object):
The type of objects to return.
``filter_clause``
The filter governing selection of objects to return. Defaults to
The filter governing selection of objects to return. Defaults to
None.
"""
query = self.session.query(object_class)

View File

@ -34,6 +34,15 @@ from openlp.core.ui import MainDisplay
log = logging.getLogger(__name__)
VERSE = u'The Lord said to {r}Noah{/r}: \n' \
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \
'The Lord said to {g}Noah{/g}:\n' \
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \
'Get those children out of the muddy, muddy \n' \
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
class RenderManager(object):
"""
Class to pull all Renderer interactions into one place. The plugins will
@ -202,28 +211,17 @@ class RenderManager(object):
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' \
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \
'The Lord said to {g}Noah{/g}:\n' \
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \
'Get those children out of the muddy, muddy \n' \
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
# make big page for theme edit dialog to get line count
if self.force_page:
verse = verse + verse + verse
else:
self.image_manager.del_image(theme_data.theme_name)
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 = theme_data
serviceItem.add_from_text(u'', verse, footer)
if self.force_page:
# make big page for theme edit dialog to get line count
serviceItem.add_from_text(u'', VERSE + VERSE + VERSE, FOOTER)
else:
self.image_manager.del_image(theme_data.theme_name)
serviceItem.add_from_text(u'', VERSE, FOOTER)
serviceItem.render_manager = self
serviceItem.raw_footer = footer
serviceItem.raw_footer = FOOTER
serviceItem.render(True)
if not self.force_page:
self.display.buildHtml(serviceItem)

View File

@ -64,7 +64,7 @@ class SettingsManager(object):
Read the last directory used for plugin.
``section``
The section of code calling the method. This is used in the
The section of code calling the method. This is used in the
settings key.
``num``
@ -84,7 +84,7 @@ class SettingsManager(object):
Save the last directory used for plugin.
``section``
The section of code calling the method. This is used in the
The section of code calling the method. This is used in the
settings key.
``directory``
@ -160,11 +160,11 @@ class SettingsManager(object):
Get a list of files from the data files path.
``section``
Defaults to *None*. The section of code getting the files - used
Defaults to *None*. The section of code getting the files - used
to load from a section's data subdirectory.
``extension``
Defaults to *None*. The extension to search for.
Defaults to *None*. The extension to search for.
"""
path = AppLocation.get_data_path()
if section:

View File

@ -91,6 +91,7 @@ class ThemeLevel(object):
Service = 2
Song = 3
class BackgroundType(object):
"""
Type enumeration for backgrounds.
@ -123,6 +124,7 @@ class BackgroundType(object):
elif type_string == u'image':
return BackgroundType.Image
class BackgroundGradientType(object):
"""
Type enumeration for background gradients.
@ -200,6 +202,7 @@ 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']
class ThemeXML(object):
"""
A class to encapsulate the Theme XML.

View File

@ -130,7 +130,7 @@ def create_accept_reject_button_box(parent, okay=False):
methods to handle the default ``accepted()`` and ``rejected()`` signals.
``parent``
The parent object. This should be a ``QWidget`` descendant.
The parent object. This should be a ``QWidget`` descendant.
``okay``
If true creates an okay/cancel combination instead of save/cancel.
@ -185,15 +185,15 @@ def media_item_combo_box(parent, name):
def create_delete_push_button(parent, icon=None):
"""
Creates a standard push button with a delete label and optional icon. The
Creates a standard push button with a delete label and optional icon. The
button is connected to the parent's ``onDeleteButtonClicked()`` method to
handle the ``clicked()`` signal.
``parent``
The parent object. This should be a ``QWidget`` descendant.
The parent object. This should be a ``QWidget`` descendant.
``icon``
An icon to display on the button. This can be either a ``QIcon``, a
An icon to display on the button. This can be either a ``QIcon``, a
resource path or a file name.
"""
delete_button = QtGui.QPushButton(parent)
@ -210,12 +210,12 @@ def create_delete_push_button(parent, icon=None):
def create_up_down_push_button_set(parent):
"""
Creates a standard set of two push buttons, one for up and the other for
down, for use with lists. The buttons use arrow icons and no text and are
down, for use with lists. The buttons use arrow icons and no text and are
connected to the parent's ``onUpButtonClicked()`` and
``onDownButtonClicked()`` to handle their respective ``clicked()`` signals.
``parent``
The parent object. This should be a ``QWidget`` descendant.
The parent object. This should be a ``QWidget`` descendant.
"""
up_button = QtGui.QPushButton(parent)
up_button.setIcon(build_icon(u':/services/service_up.png'))
@ -295,7 +295,7 @@ def create_valign_combo(form, parent, layout):
The UI screen that the label and combo will appear on.
``parent``
The parent object. This should be a ``QWidget`` descendant.
The parent object. This should be a ``QWidget`` descendant.
``layout``
A layout object to add the label and combo widgets to.

View File

@ -92,7 +92,7 @@ class Theme(object):
* ``solid`` - color
``BackgroundParameter2``
Extra information about the background. The contents of this attribute
Extra information about the background. The contents of this attribute
depend on the BackgroundType:
* ``image`` - border color
@ -100,7 +100,7 @@ class Theme(object):
* ``solid`` - N/A
``BackgroundParameter3``
Extra information about the background. The contents of this attribute
Extra information about the background. The contents of this attribute
depend on the BackgroundType:
* ``image`` - N/A
@ -142,7 +142,7 @@ class Theme(object):
Color for the outline (or None if Outline is 0)
``HorizontalAlign``
The horizontal alignment to apply to text. Valid alignments are:
The horizontal alignment to apply to text. Valid alignments are:
* ``0`` - left align
* ``1`` - right align
@ -156,7 +156,7 @@ class Theme(object):
* ``2`` - centre align
``WrapStyle``
The wrap style to apply to the text. Valid styles are:
The wrap style to apply to the text. Valid styles are:
* ``0`` - normal
* ``1`` - lyrics

View File

@ -72,7 +72,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
def preLoad(self):
"""
Load the Tags from store so can be used in the system or used to
update the display. If Cancel was selected this is needed to reset the
update the display. If Cancel was selected this is needed to reset the
dsiplay to the correct version.
"""
# Initial Load of the Tags

View File

@ -45,7 +45,7 @@ class ValidEdit(QtGui.QLineEdit):
def validText(self):
"""
Only return Integers. Space is 0
Only return Integers. Space is 0
"""
if self.text().isEmpty():
return QtCore.QString(u'0')

View File

@ -279,7 +279,7 @@ class Ui_MainWindow(object):
add_actions(self.SettingsLanguageMenu, self.LanguageGroup.actions())
add_actions(self.SettingsMenu, (self.settingsPluginListItem,
self.SettingsLanguageMenu.menuAction(), None,
self.SettingsShortcutsItem, self.DisplayTagItem,
self.DisplayTagItem, self.SettingsShortcutsItem,
self.SettingsConfigureItem))
add_actions(self.ToolsMenu, (self.ToolsAddToolItem, None))
add_actions(self.ToolsMenu, (self.ToolsOpenDataFolder, None))

View File

@ -266,7 +266,7 @@ class ThemeManager(QtGui.QWidget):
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
self.fileRenameForm.fileNameEdit.setText(oldThemeName)
if self.fileRenameForm.exec_():
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
if self.checkIfThemeExists(newThemeName):
oldThemeData = self.getThemeData(oldThemeName)
self.cloneThemeData(oldThemeData, newThemeName)
@ -284,7 +284,7 @@ class ThemeManager(QtGui.QWidget):
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
self.fileRenameForm.fileNameEdit.setText(oldThemeName)
if self.fileRenameForm.exec_(True):
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
if self.checkIfThemeExists(newThemeName):
themeData = self.getThemeData(oldThemeName)
self.cloneThemeData(themeData, newThemeName)
@ -399,7 +399,7 @@ class ThemeManager(QtGui.QWidget):
def onImportTheme(self):
"""
Opens a file dialog to select the theme file(s) to import before
attempting to extract OpenLP themes from those files. This process
attempting to extract OpenLP themes from those files. This process
will load both OpenLP version 1 and version 2 themes.
"""
files = QtGui.QFileDialog.getOpenFileNames(self,

View File

@ -242,7 +242,7 @@ def add_actions(target, actions):
The menu or toolbar to add actions to.
``actions``
The actions to be added. An action consisting of the keyword 'None'
The actions to be added. An action consisting of the keyword 'None'
will result in a separator being inserted into the target.
"""
for action in actions:
@ -318,7 +318,7 @@ def get_web_page(url, header=None, update_openlp=False):
Tells OpenLP to update itself if the page is successfully downloaded.
Defaults to False.
"""
# TODO: Add proxy usage. Get proxy info from OpenLP settings, add to a
# TODO: Add proxy usage. Get proxy info from OpenLP settings, add to a
# proxy_handler, build into an opener and install the opener into urllib2.
# http://docs.python.org/library/urllib2.html
if not url:

View File

@ -322,7 +322,7 @@ class BibleDB(QtCore.QObject, Manager):
def get_books(self):
"""
A wrapper so both local and web bibles have a get_books() method that
manager can call. Used in the media manager advanced search tab.
manager can call. Used in the media manager advanced search tab.
"""
return self.get_all_objects(Book, order_by_ref=Book.id)

View File

@ -35,7 +35,7 @@ import socket
import urllib
from HTMLParser import HTMLParseError
from BeautifulSoup import BeautifulSoup, NavigableString
from BeautifulSoup import BeautifulSoup, NavigableString, Tag
from openlp.core.lib import Receiver, translate
from openlp.core.lib.ui import critical_error_message_box
@ -221,25 +221,18 @@ class BGExtract(object):
crossrefs = soup.findAll(u'sup', u'xref')
if crossrefs:
[crossref.extract() for crossref in crossrefs]
headings = soup.findAll(u'h5')
if headings:
[heading.extract() for heading in headings]
cleanup = [(re.compile('\s+'), lambda match: ' ')]
verses = BeautifulSoup(str(soup), markupMassage=cleanup)
content = verses.find(u'div', u'result-text-style-normal')
if not content:
content = verses.find(u'div', u'result-text-style-rtl-serif')
if not content:
log.debug(u'No content found in the BibleGateway response.')
send_error_message(u'parse')
return None
verse_count = len(verses.findAll(u'sup', u'versenum'))
found_count = 0
verse_list = {}
while found_count < verse_count:
content = content.findNext(u'sup', u'versenum')
raw_verse_num = content.next
for verse in verses(u'sup', u'versenum'):
raw_verse_num = verse.next
clean_verse_num = 0
# Not all verses exist in all translations and may or may not be
# represented by a verse number. If they are not fine, if they are
# it will probably be in a format that breaks int(). We will then
# represented by a verse number. If they are not fine, if they are
# it will probably be in a format that breaks int(). We will then
# have no idea what garbage may be sucked in to the verse text so
# if we do not get a clean int() then ignore the verse completely.
try:
@ -248,9 +241,22 @@ class BGExtract(object):
log.exception(u'Illegal verse number in %s %s %s:%s',
version, bookname, chapter, unicode(raw_verse_num))
if clean_verse_num:
raw_verse_text = raw_verse_num.next
verse_list[clean_verse_num] = unicode(raw_verse_text)
found_count += 1
verse_text = raw_verse_num.next
part = raw_verse_num.next.next
while not (isinstance(part, Tag) and part.attrMap and
part.attrMap[u'class'] == u'versenum'):
# While we are still in the same verse grab all the text.
if isinstance(part, NavigableString):
verse_text = verse_text + part
if isinstance(part.next, Tag) and part.next.name == u'div':
# Run out of verses so stop.
break
part = part.next
verse_list[clean_verse_num] = unicode(verse_text)
if not verse_list:
log.debug(u'No content found in the BibleGateway response.')
send_error_message(u'parse')
return None
return SearchResults(bookname, chapter, verse_list)
@ -384,7 +390,7 @@ class HTTPBible(BibleDB):
BibleDB.__init__(self, parent, **kwargs)
self.download_source = kwargs[u'download_source']
self.download_name = kwargs[u'download_name']
# TODO: Clean up proxy stuff. We probably want one global proxy per
# TODO: Clean up proxy stuff. We probably want one global proxy per
# connection type (HTTP and HTTPS) at most.
self.proxy_server = None
self.proxy_username = None
@ -458,8 +464,8 @@ class HTTPBible(BibleDB):
search_results = self.get_chapter(book, reference[1])
if search_results and search_results.has_verselist():
## We have found a book of the bible lets check to see
## if it was there. By reusing the returned book name
## we get a correct book. For example it is possible
## if it was there. By reusing the returned book name
## we get a correct book. For example it is possible
## to request ac and get Acts back.
bookname = search_results.book
Receiver.send_message(u'openlp_process_events')

View File

@ -46,16 +46,6 @@ except ImportError:
log = logging.getLogger(__name__)
class BibleMode(object):
"""
This is basically an enumeration class which specifies the mode of a Bible.
Mode refers to whether or not a Bible in OpenLP is a full Bible or needs to
be downloaded from the Internet on an as-needed basis.
"""
Full = 1
Partial = 2
class BibleFormat(object):
"""
This is a special enumeration class that holds the various types of Bibles,
@ -275,7 +265,7 @@ class BibleManager(object):
'Scripture Reference Error'),
u'message': translate('BiblesPlugin.BibleManager',
'Your scripture reference is either not supported by OpenLP '
'or is invalid. Please make sure your reference conforms to '
'or is invalid. Please make sure your reference conforms to '
'one of the following patterns:\n\n'
'Book Chapter\n'
'Book Chapter-Chapter\n'

View File

@ -482,7 +482,7 @@ class BibleMediaItem(MediaManagerItem):
self.listView.clear()
if self.listView.count() != 0:
self.__checkSecondBible(bible, second_bible)
else:
elif self.search_results:
self.displayResults(bible, second_bible)
Receiver.send_message(u'cursor_normal')
self.advancedSearchButton.setEnabled(True)
@ -698,11 +698,7 @@ class BibleMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.AllowsPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop)
# Service Item: Title
for title in raw_title:
if not service_item.title:
service_item.title = title
else:
service_item.title += u', ' + title
service_item.title = u', '.join(raw_title)
# Service Item: Theme
if len(self.settings.bible_theme) == 0:
service_item.theme = None

View File

@ -63,7 +63,7 @@ class MediaPlugin(Plugin):
if ext not in list:
list.append(ext)
self.serviceManager.supportedSuffixes(extension[1:])
log.info(u'MediaPlugin: %s extensions: %s' % (mimetype,
log.info(u'MediaPlugin: %s extensions: %s' % (mimetype,
u' '.join(extensions)))
def about(self):

View File

@ -211,8 +211,8 @@ class ImpressDocument(PresentationDocument):
"""
Called when a presentation is added to the SlideController.
It builds the environment, starts communcations with the background
OpenOffice task started earlier. If OpenOffice is not present is is
started. Once the environment is available the presentation is loaded
OpenOffice task started earlier. If OpenOffice is not present is is
started. Once the environment is available the presentation is loaded
and started.
``presentation``

View File

@ -213,6 +213,7 @@ class Controller(object):
def poll(self):
self.doc.poll_slidenumber(self.is_live)
class MessageListener(object):
"""
This is the Presentation listener who acts on events from the slide

View File

@ -357,7 +357,7 @@ class PresentationController(object):
def __init__(self, plugin=None, name=u'PresentationController',
document_class=PresentationDocument):
"""
This is the constructor for the presentationcontroller object. This
This is the constructor for the presentationcontroller object. This
provides an easy way for descendent plugins to populate common data.
This method *must* be overridden, like so::

View File

@ -43,7 +43,7 @@ log = logging.getLogger(__name__)
class HttpServer(object):
"""
Ability to control OpenLP via a webbrowser
e.g. http://localhost:4316/send/slidecontroller_live_next
e.g. http://localhost:4316/send/slidecontroller_live_next
http://localhost:4316/send/alerts_text?q=your%20alert%20text
"""
def __init__(self, parent):

View File

@ -557,7 +557,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
"""
Check the validity of the song.
"""
# This checks data in the form *not* self.song. self.song is still
# This checks data in the form *not* self.song. self.song is still
# None at this point.
log.debug(u'Validate Song')
# Lets be nice and assume the data is correct.
@ -714,14 +714,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
def saveSong(self, preview=False):
"""
Get all the data from the widgets on the form, and then save it to the
database. The form has been validated and all reference items
database. The form has been validated and all reference items
(Authors, Books and Topics) have been saved before this function is
called.
``preview``
Should be ``True`` if the song is also previewed (boolean).
"""
# The Song() assignment. No database calls should be made while a
# The Song() assignment. No database calls should be made while a
# Song() is in a partially complete state.
if not self.song:
self.song = Song()

View File

@ -96,7 +96,7 @@ class EasiSlidesImport(SongImport):
mandatory=False):
"""
Add imported values to the song model converting them to unicode at the
same time. If the unicode decode fails or a mandatory attribute is not
same time. If the unicode decode fails or a mandatory attribute is not
present _success is set to False so the importer can react
appropriately.

View File

@ -72,7 +72,7 @@ def strip_rtf(blob, encoding):
elif control_str == 'tab':
clear_text.append(u'\t')
# Prefer the encoding specified by the RTF data to that
# specified by the Paradox table header
# specified by the Paradox table header
# West European encoding
elif control_str == 'fcharset0':
encoding = u'cp1252'
@ -129,6 +129,7 @@ class FieldDescEntry:
self.type = type
self.size = size
class EasyWorshipSongImport(SongImport):
"""
The :class:`EasyWorshipSongImport` class provides OpenLP with the
@ -163,7 +164,7 @@ class EasyWorshipSongImport(SongImport):
if code_page == 852:
self.encoding = u'cp1250'
# The following codepage to actual encoding mappings have not been
# observed, but merely guessed. Actual example files are needed.
# observed, but merely guessed. Actual example files are needed.
elif code_page == 737:
self.encoding = u'cp1253'
elif code_page == 775:

View File

@ -133,6 +133,7 @@ class FoilPresenterImport(SongImport):
log.exception(u'XML syntax error in file %s' % file_path)
return True
class FoilPresenter(object):
"""
This class represents the converter for Foilpresenter XML from a song.
@ -259,7 +260,6 @@ class FoilPresenter(object):
copyright = None
if copyright:
strings = []
author_temp = []
if copyright.find(u'Copyright') != -1:
temp = copyright.partition(u'Copyright')
copyright = temp[0]
@ -296,7 +296,7 @@ class FoilPresenter(object):
u'Text +u\.?n?d? +Musik', u'T & M', u'Melodie und Satz',
u'Text[\w\,\. ]*:', u'Melodie', u'Musik', u'Satz',
u'Weise', u'[dD]eutsch', u'[dD]t[\.\:]', u'Englisch',
u'[oO]riginal', u'Bearbeitung', u'[R|r]efrain']
u'[oO]riginal', u'Bearbeitung', u'[R|r]efrain']
for marker in markers:
copyright = re.compile(marker).sub(u'<marker>', copyright, re.U)
copyright = re.compile(u'(?<=<marker>) *:').sub(u'', copyright)
@ -305,7 +305,7 @@ class FoilPresenter(object):
while i != 1:
if copyright.find(u'<marker>') != -1:
temp = copyright.partition(u'<marker>')
if (temp[0].strip() != u'') & (x > 0):
if temp[0].strip() and x > 0:
strings.append(temp[0])
copyright = temp[2]
x += 1
@ -314,14 +314,15 @@ class FoilPresenter(object):
i = 1
else:
i = 1
author_temp = []
for author in strings:
temp = re.split(u',(?=\D{2})|(?<=\D),|\/(?=\D{3,})|(?<=\D);',
author)
for tempx in temp:
author_temp.append(tempx)
for author in author_temp:
regex = u'^[\/,;\-\s]+|[\/,;\-\s]+$|'\
'\s*[0-9]{4}\s*[\-\/]?\s*([0-9]{4})?[\/,;\-\s]*$'
regex = u'^[\/,;\-\s\.]+|[\/,;\-\s\.]+$|'\
'\s*[0-9]{4}\s*[\-\/]?\s*([0-9]{4})?[\/,;\-\s\.]*$'
author = re.compile(regex).sub(u'', author)
author = re.compile(
u'[0-9]{1,2}\.\s?J(ahr)?h\.|um\s*$|vor\s*$').sub(u'',
@ -330,12 +331,12 @@ class FoilPresenter(object):
author = author.strip()
if re.search(
u'\w+\.?\s+\w{3,}\s+[a|u]nd\s|\w+\.?\s+\w{3,}\s+&\s',
author, re.U) != None:
author, re.U):
temp = re.split(u'\s[a|u]nd\s|\s&\s', author)
for tempx in temp:
tempx = tempx.strip()
authors.append(tempx)
elif (len(author) > 2):
elif len(author) > 2:
authors.append(author)
for display_name in authors:
author = self.manager.get_object_filtered(Author,
@ -411,7 +412,7 @@ class FoilPresenter(object):
temp_verse_order_backup = []
temp_sortnr_backup = 1
temp_sortnr_liste = []
versenumber = {u'V': 1, u'C': 1, u'B': 1, u'E': 1, u'O': 1, u'I': 1,
versenumber = {u'V': 1, u'C': 1, u'B': 1, u'E': 1, u'O': 1, u'I': 1,
u'P': 1}
for strophe in foilpresenterfolie.strophen.strophe:
text = self._child(strophe.text_)

View File

@ -53,6 +53,7 @@ class SongSearch(object):
Authors = 4
Themes = 5
class SongMediaItem(MediaManagerItem):
"""
This is the custom media manager item for Songs.
@ -199,7 +200,7 @@ class SongMediaItem(MediaManagerItem):
"""
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
# 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.
if self.remoteTriggered == u'L':
self.onAddClick()
@ -218,13 +219,9 @@ class SongMediaItem(MediaManagerItem):
self.listView.clear()
searchresults.sort(cmp=self.collateSongTitles)
for song in searchresults:
author_list = u''
for author in song.authors:
if author_list != u'':
author_list = author_list + u', '
author_list = author_list + author.display_name
author_list = [author.display_name for author in song.authors]
song_title = unicode(song.title)
song_detail = u'%s (%s)' % (song_title, author_list)
song_detail = u'%s (%s)' % (song_title, u', '.join(author_list))
song_name = QtGui.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song.id))
self.listView.addItem(song_name)
@ -334,9 +331,6 @@ class SongMediaItem(MediaManagerItem):
def generateSlideData(self, service_item, item=None, xmlVersion=False):
log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
raw_footer = []
author_list = u''
author_audit = []
ccli = u''
item_id = self._getIdOfItemToGenerate(item, self.remoteSong)
service_item.add_capability(ItemCapabilities.AllowsEdit)
service_item.add_capability(ItemCapabilities.AllowsPreview)
@ -397,13 +391,9 @@ class SongMediaItem(MediaManagerItem):
for slide in verses:
service_item.add_from_text(slide[:30], unicode(slide))
service_item.title = song.title
for author in song.authors:
if len(author_list) > 1:
author_list = author_list + u', '
author_list = author_list + unicode(author.display_name)
author_audit.append(unicode(author.display_name))
author_list = [unicode(author.display_name) for author in song.authors]
raw_footer.append(song.title)
raw_footer.append(author_list)
raw_footer.append(u', '.join(author_list))
raw_footer.append(song.copyright)
if QtCore.QSettings().value(u'general/ccli number',
QtCore.QVariant(u'')).toString():
@ -413,10 +403,10 @@ class SongMediaItem(MediaManagerItem):
QtCore.QVariant(u'')).toString()))
service_item.raw_footer = raw_footer
service_item.audit = [
song.title, author_audit, song.copyright, unicode(song.ccli_number)
song.title, author_list, song.copyright, unicode(song.ccli_number)
]
service_item.data_string = {u'title': song.search_title,
u'authors': author_list}
u'authors': u', '.join(author_list)}
service_item.xml_version = self.openLyrics.song_to_xml(song)
return True

View File

@ -36,7 +36,6 @@ from openlp.plugins.songs.lib.songimport import SongImport
log = logging.getLogger(__name__)
#TODO: Use lxml for parsing and make sure we use methods of "SongImport" .
class OpenSongImport(SongImport):
"""
Import songs exported from OpenSong

View File

@ -347,7 +347,7 @@ class SongImport(QtCore.QObject):
"""
For debugging
"""
print u'========================================' \
print u'========================================' \
+ u'========================================'
print u'TITLE: ' + self.title
print u'ALT TITLE: ' + self.alternate_title

32
resources/osx/Info.plist.master Executable file
View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>org.openlp</string>
<key>CFBundleShortVersionString</key>
<string>%(openlp_version)s</string>
<key>CFBundleVersion</key>
<string>%(openlp_version)s</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleDisplayName</key>
<string>%(openlp_appname)s</string>
<key>CFBundleIconFile</key>
<string>openlp-logo-with-text.icns</string>
<key>CFBundleExecutable</key>
<string>MacOS/openlp</string>
<key>CFBundleName</key>
<string>%(openlp_appname)s</string>
<key>CFBundleGetInfoString</string>
<string>%(openlp_appname)s %(openlp_version)s</string>
<key>LSHasLocalizedDisplayName</string>
<false/>
<key>NSAppleScriptEnabled</key>
<false/>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSBackgroundOnly</key>
<false/>
</dict>
</plist>

28
resources/osx/Makefile Normal file
View File

@ -0,0 +1,28 @@
all:
python build.py -c openlp.cfg
view:
python build.py -c openlp.cfg --package-view --compress-view
package:
python build.py -c openlp.cfg --package --package-view
bundle:
python build.py -c openlp.cfg --compress --compress-view
clean:
# remove old configuration files
rm -f openlp.spec
rm -f Info.plist
rm -f .version
# remove old build artifacts
rm -rf build
rm -rf dist
rm -rf Macopenlp.app
rm -rf OpenLP.app
rm -f warnopenlp.txt
rm -f *dmg

View File

@ -0,0 +1,74 @@
on saveImageWithItselfAsIcon(icon_image_file)
-- save icon_image_file with itself as icon
set icon_image_file_string to icon_image_file as string
tell application "Image Events"
launch
set icon_image to open file icon_image_file_string
save icon_image with icon
close icon_image
end tell
end saveImageWithItselfAsIcon
on copyIconOfTo(aFileOrFolderWithIcon, aFileOrFolder)
tell application "Finder" to set f to aFileOrFolderWithIcon as alias
-- grab the file's icon
my CopyOrPaste(f, "c")
-- now the icon is in the clipboard
tell application "Finder" to set c to aFileOrFolder as alias
my CopyOrPaste(result, "v")
end copyIconOfTo
on CopyOrPaste(i, cv)
tell application "Finder"
activate
open information window of i
end tell
tell application "System Events" to tell process "Finder" to tell window 1
keystroke tab -- select icon button
keystroke (cv & "w") using command down (* (copy or paste) + close window *)
end tell -- window 1 then process Finder then System Events
end CopyOrPaste
on run
set icon_image_file to POSIX file "%s" as alias
set dmg_file to POSIX file "/Volumes/%s" as alias
my saveImageWithItselfAsIcon(icon_image_file)
-- wait for virus scanner
delay 2
my copyIconOfTo(icon_image_file, dmg_file)
tell application "Finder"
tell disk "%s"
open
set current view of container window to icon view
set toolbar visible of container window to false
set statusbar visible of container window to false
set the bounds of container window to {400, 100, 1100, 500}
set theViewOptions to the icon view options of container window
set arrangement of theViewOptions to not arranged
set icon size of theViewOptions to 128
set background picture of theViewOptions to file ".installer-background.png"
if not exists file "Applications" then
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
end if
delay 5
set position of item "%s" of container window to {160, 200}
set position of item ".Trashes" of container window to {100, 500}
set position of item ".installer-background.png" of container window to {200, 500}
set position of item ".DS_Store" of container window to {400, 500}
set position of item "Applications" of container window to {550, 200}
set position of item ".VolumeIcon.icns" of container window to {500, 500}
set position of item ".fseventsd" of container window to {300, 500}
if exists POSIX file ".SymAVx86QSFile" then
set position of item ".SymAVx86QSFile" of container window to {600, 500}
end if
open
close
update without registering applications
-- wait until the virus scan completes
delay 5
-- eject
end tell
end tell
end run

View File

@ -0,0 +1,73 @@
on saveImageWithItselfAsIcon(icon_image_file)
-- save icon_image_file with itself as icon
set icon_image_file_string to icon_image_file as string
tell application "Image Events"
launch
set icon_image to open file icon_image_file_string
save icon_image with icon
close icon_image
end tell
end saveImageWithItselfAsIcon
on copyIconOfTo(aFileOrFolderWithIcon, aFileOrFolder)
tell application "Finder" to set f to aFileOrFolderWithIcon as alias
-- grab the file's icon
my CopyOrPaste(f, "c")
-- now the icon is in the clipboard
tell application "Finder" to set c to aFileOrFolder as alias
my CopyOrPaste(result, "v")
end copyIconOfTo
on CopyOrPaste(i, cv)
tell application "Finder"
activate
set infoWindow to open information window of i
set infoWindowName to name of infoWindow
end tell
tell application "System Events" to tell process "Finder" to tell window infoWindowName
keystroke tab -- select icon button
keystroke (cv & "w") using command down (* (copy or paste) + close window *)
end tell -- window 1 then process Finder then System Events
end CopyOrPaste
on run
set icon_image_file to POSIX file "%s" as alias
set dmg_file to POSIX file "/Volumes/%s" as alias
my saveImageWithItselfAsIcon(icon_image_file)
-- wait for virus scanner
delay 2
my copyIconOfTo(icon_image_file, dmg_file)
tell application "Finder"
tell disk "%s"
open
set current view of container window to icon view
set toolbar visible of container window to false
set statusbar visible of container window to false
set the bounds of container window to {400, 100, 1100, 500}
set theViewOptions to the icon view options of container window
set arrangement of theViewOptions to not arranged
set icon size of theViewOptions to 128
set background picture of theViewOptions to file ".installer-background.png"
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
delay 5
set position of item "%s" of container window to {160, 200}
set position of item ".Trashes" of container window to {100, 500}
set position of item ".installer-background.png" of container window to {200, 500}
set position of item ".DS_Store" of container window to {400, 500}
set position of item "Applications" of container window to {550, 200}
set position of item ".VolumeIcon.icns" of container window to {500, 500}
set position of item ".fseventsd" of container window to {300, 500}
if exists POSIX file ".SymAVx86QSFile" then
set position of item ".SymAVx86QSFile" of container window to {600, 500}
end if
open
close
update without registering applications
-- wait until the virus scan completes
delay 5
-- eject
end tell
end tell
end run

View File

@ -0,0 +1,40 @@
on saveImageWithItselfAsIcon(icon_image_file)
-- save icon_image_file with itself as icon
set icon_image_file_string to icon_image_file as string
tell application "Image Events"
launch
set icon_image to open file icon_image_file_string
save icon_image with icon
close icon_image
end tell
end saveImageWithItselfAsIcon
on copyIconOfTo(aFileOrFolderWithIcon, aFileOrFolder)
tell application "Finder" to set f to aFileOrFolderWithIcon as alias
-- grab the file's icon
my CopyOrPaste(f, "c")
-- now the icon is in the clipboard
tell application "Finder" to set c to aFileOrFolder as alias
my CopyOrPaste(result, "v")
end copyIconOfTo
on CopyOrPaste(i, cv)
tell application "Finder"
activate
open information window of i
end tell
tell application "System Events" to tell process "Finder" to tell window 1
keystroke tab -- select icon button
keystroke (cv & "w") using command down (* (copy or paste) + close window *)
end tell -- window 1 then process Finder then System Events
end CopyOrPaste
on run
set icon_image_file to POSIX file "%s" as alias
set dmg_file to POSIX file "%s" as alias
my saveImageWithItselfAsIcon(icon_image_file)
-- wait for virus scanner
delay 2
my copyIconOfTo(icon_image_file, dmg_file)
end run

View File

@ -0,0 +1,41 @@
on saveImageWithItselfAsIcon(icon_image_file)
-- save icon_image_file with itself as icon
set icon_image_file_string to icon_image_file as string
tell application "Image Events"
launch
set icon_image to open file icon_image_file_string
save icon_image with icon
close icon_image
end tell
end saveImageWithItselfAsIcon
on copyIconOfTo(aFileOrFolderWithIcon, aFileOrFolder)
tell application "Finder" to set f to aFileOrFolderWithIcon as alias
-- grab the file's icon
my CopyOrPaste(f, "c")
-- now the icon is in the clipboard
tell application "Finder" to set c to aFileOrFolder as alias
my CopyOrPaste(result, "v")
end copyIconOfTo
on CopyOrPaste(i, cv)
tell application "Finder"
activate
set infoWindow to open information window of i
set infoWindowName to name of infoWindow
end tell
tell application "System Events" to tell process "Finder" to tell window infoWindowName
keystroke tab -- select icon button
keystroke (cv & "w") using command down (* (copy or paste) + close window *)
end tell -- window 1 then process Finder then System Events
end CopyOrPaste
on run
set icon_image_file to POSIX file "%s" as alias
set dmg_file to POSIX file "%s" as alias
my saveImageWithItselfAsIcon(icon_image_file)
-- wait for virus scanner
delay 2
my copyIconOfTo(icon_image_file, dmg_file)
end run

412
resources/osx/build.py Normal file
View File

@ -0,0 +1,412 @@
#!/usr/bin/python
# -*- encoding: utf-8 -*-
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Armin Köhler, 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 #
###############################################################################
"""
Mac OS X Build Script
---------------------
This script is used to build the OS X binary and the accompanying installer.
For this script to work out of the box, it depends on a number of things:
Python 2.6
This build script only works with Python 2.6.
PyQt4
You should already have this installed, OpenLP doesn't work without it.
The version the script expects is the packaged one available from River
Bank Computing.
PyInstaller
PyInstaller should be a checkout of revision 1355 of trunk, and in a
directory which is configured in the openlp.cfg. The revision is very
important as there is just included a fix for builds on OS X.
To install PyInstaller, first checkout trunk from Subversion. The
easiest way is to do a
svn co http://svn.pyinstaller.org/trunk
Then you need to copy the two hook-*.py files from the "pyinstaller"
subdirectory in OpenLP's "resources" directory into PyInstaller's
"hooks" directory.
openlp.cfg
The configuration file contains settings of the version string to include
in the bundle as well as directory and file settings for different
purposes (e.g. PyInstaller location or installer background image)
To start the build process do a
make
inside the resources/osx directory. The result should be a {openlp_dmgname}.dmg
file in the same directory. If something went wrong - this sometimes happen
with the graphical commands in the Apple script - do a
make clean
and start the build process again. If you want to execute only parts of the
build process you can specify different make targets
make view -- runs the Apple scripts to set the icons
make package -- creates the dmg file and copies the application files
make bundle -- compresses the dmg file and sets the dmg file icon
"""
import time
import os
import ConfigParser
import logging
import optparse
import sys
import platform
import re
import subprocess as subp
# set the script name
script_name = "build"
def build_application(settings, app_name_lower, app_dir):
logging.info('[%s] now building the app with pyinstaller at "%s"...',
script_name, settings['pyinstaller_basedir'])
result = os.system('python %s/pyinstaller.py openlp.spec' \
% settings['pyinstaller_basedir'])
if (result != 0):
logging.error('[%s] The pyinstaller build reported an error, cannot \
continue!', script_name)
sys.exit(1)
logging.info('[%s] copying the qt_menu files...', script_name)
# see http://www.pyinstaller.org/ticket/157
result = os.system('cp -R %(qt_menu_directory)s \
%(application_directory)s/Contents/Resources' \
% { 'qt_menu_directory' : settings['qt_menu_basedir'],
'application_directory' : app_dir })
if (result != 0):
logging.error('[%s] could not copy the qt_menu files, cannot \
continue!', script_name)
sys.exit(1)
dist_folder = os.getcwd() + '/dist/' + app_name_lower
logging.info('[%s] copying the new plugins...', script_name)
result = os.system('cp -R %(openlp_directory)s/openlp/plugins \
%(application_directory)s/Contents/MacOS' \
% { 'openlp_directory' : settings['openlp_basedir'],
'application_directory' : app_dir })
if (result != 0):
logging.error('[%s] could not copy plugins, dmg creation failed!',
script_name)
sys.exit(1)
logging.info('[%s] copying the icons to the resource directory...',
script_name)
result = os.system('cp %(icon_file)s \
%(application_directory)s/Contents/Resources' \
% { 'icon_file' : settings['openlp_icon_file'],
'application_directory' : app_dir })
if (result != 0):
logging.error('[%s] could not copy the icon, dmg creation failed!',
script_name)
sys.exit(1)
logging.info('[%s] copying the version file...', script_name)
result = os.system('CpMac %s/.version %s/Contents/MacOS' % (os.getcwd(),
app_dir))
if (result != 0):
logging.error('[%s] could not copy the version file, dmg creation \
failed!', script_name)
sys.exit(1)
logging.info('[%s] copying the new Info.plist...', script_name)
result = os.system('cp %(target_directory)s/Info.plist \
%(application_directory)s/Contents' \
% { 'target_directory' : os.getcwd(),
'application_directory' : app_dir })
if (result != 0):
logging.error('[%s] could not copy the info file, dmg creation \
failed!', script_name)
sys.exit(1)
def deploy_qt(settings):
logging.info('[%s] running mac deploy qt on %s.app...', script_name,
settings['openlp_appname']);
result = os.system('macdeployqt %s.app' % settings['openlp_appname']);
if (result != 0):
logging.error('[%s] could not create dmg file!', script_name)
sys.exit(1)
def create_dmg(settings):
logging.info('[%s] creating the dmg...', script_name)
dmg_file = os.getcwd() + '/' + settings['openlp_dmgname'] + '.dmg'
result = os.system('hdiutil create %(dmg_file)s~ -ov -megabytes \
%(vol_size)s -fs HFS+ -volname %(vol_name)s' \
% { 'dmg_file' : dmg_file,
'vol_size' : '250',
'vol_name' : settings['openlp_appname'] })
if (result != 0):
logging.error('[%s] could not create dmg file!', script_name)
sys.exit(1)
logging.info('[%s] mounting the dmg file...', script_name)
output = subp.Popen(["hdiutil", "attach", dmg_file + "~.dmg"],
stdout=subp.PIPE).communicate()[0]
logging.debug(output)
p = re.compile('Apple_HFS\s+(.+?)\s*$')
result = p.search(output, re.M)
volume_basedir = ''
if result:
volume_basedir = result.group(1)
else:
logging.error('could not mount dmg file, cannot continue!')
sys.exit(1)
logging.info('[%s] copying the app (from %s) to the dmg (at %s)...',
script_name, app_dir, volume_basedir)
result = os.system('CpMac -r %s %s' \
% ( app_dir, volume_basedir ))
if (result != 0):
logging.error('[%s] could not copy application, dmg creation failed!',
script_name)
sys.exit(1)
logging.info('[%s] copying the background image...', script_name)
# os.mkdir(volume_basedir + '/.background')
result = os.system('CpMac %s %s'
% (settings['installer_backgroundimage_file'],
volume_basedir + '/.installer-background.png'))
if (result != 0):
logging.error('[%s] could not copy the background image, dmg creation\
failed!', script_name)
sys.exit(1)
return (volume_basedir, dmg_file)
def unmount_dmg(settings, volume_basedir):
logging.info('[%s] unmounting the dmg...', script_name)
result = os.system('hdiutil detach %s' % volume_basedir)
if (result != 0):
logging.error('[%s] could not unmount the dmg file, dmg creation \
failed!', script_name)
sys.exit(1)
def compress_view(settings, seticon_scriptname, dmg_file):
logging.info('[%s] setting icon of the dmg file...', script_name)
try:
f = open(seticon_scriptname)
p = subp.Popen(["osascript"], stdin=subp.PIPE)
p.communicate(f.read() % ((os.getcwd() + '/' +
settings['openlp_dmg_icon_file']), dmg_file))
f.close()
result = p.returncode
if (result != 0):
logging.error('[%s] could not set the icon to the dmg file, \
dmg creation failed!', script_name)
sys.exit(1)
except IOError, e:
logging.error('[%s] could not adjust the view (%s), dmg creation \
failed!', script_name, e)
sys.exit(1)
except OSError, e:
logging.error('[%s] could not set the icon to the dmg file(%s), \
dmg creation failed!', script_name, e)
sys.exit(1)
def adjust_package_view(settings, adjustview_scriptname):
logging.info('[%s] making adjustments to the view...', script_name)
try:
f = open(adjustview_scriptname)
p = subp.Popen(["osascript"], stdin=subp.PIPE)
p.communicate(f.read() % ((os.getcwd() + '/' + \
settings['openlp_dmg_icon_file']),
settings['openlp_appname'],
settings['openlp_appname'],
settings['openlp_appname']))
f.close()
result = p.returncode
if (result != 0):
logging.error('[%s] could not adjust the view, dmg creation \
failed!', script_name)
sys.exit(1)
except IOError, e:
logging.error('[%s] could not adjust the view (%s), dmg creation \
failed!', script_name, e)
sys.exit(1)
except OSError, e:
logging.error('[%s] could not adjust the view (%s), dmg creation \
failed!', script_name, e)
sys.exit(1)
def compress_dmg(settings):
logging.info('[%s] compress the dmg file...', script_name)
result = os.system('hdiutil convert %s~.dmg -format UDZO \
-imagekey zlib-level=9 -o %s' \
% (dmg_file, dmg_file))
if (result != 0):
logging.error('[%s] could not compress the dmg file, dmg creation \
failed!', script_name)
sys.exit(1)
if __name__ == '__main__':
# set default actions
do_build = True
do_compress_view = True
do_package_view = True
do_create_dmg = True
do_compress_dmg = True
do_deploy_qt = True
parser = optparse.OptionParser()
parser.add_option('-c', '--config', dest='config', help='config file',
metavar='CONFIG')
parser.add_option('-v', '--package-view', dest='package_view',
help='triggers view adjustment scripts for package',
metavar='PACKAGEVIEWONLY', action='store_true', default=False)
parser.add_option('-y', '--compress-view', dest='compress_view',
help='triggers view adjustment scripts for dmg',
metavar='COMPRESSVIEWONLY', action='store_true', default=False)
parser.add_option('-p', '--package', dest='package',
help='package application folder to dmg', metavar='PACKAGE',
action='store_true', default=False)
parser.add_option('-z', '--compress', dest='compress',
help='compresses the existing dmg', metavar='COMPRESS',
action='store_true', default=False)
parser.add_option('-b', '--basedir', dest='basedir',
help='volume basedir like /Volumes/OpenLP', metavar='BASEDIR',
default='/Volumes/OpenLP')
(options, args) = parser.parse_args()
# if an option is set, false all
if (options.package_view is True or options.compress_view is True
or options.package is True or options.compress is True):
do_build = False
do_deploy_qt = False
do_package_view = options.package_view
do_compress_view = options.compress_view
do_create_dmg = options.package
do_compress_dmg = options.compress
if not options.config:
parser.error('option --config|-c is required')
logHandler = logging.StreamHandler()
logHandler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)-8s %(message)s',
'%a, %d %b %Y %H:%M:%S'))
logging.getLogger().addHandler(logHandler)
logging.getLogger().setLevel(logging.DEBUG)
config = ConfigParser.RawConfigParser()
config.readfp(open(options.config, 'r'))
if not config.has_section('openlp'):
logging.error('[%s] config file "%s" lacks an [openlp] section',
script_name, options.config)
sys.exit(1)
if not sys.platform == "darwin":
logging.error('[%s] this script only works on Macintosh OS X systems,'
+ 'not on %s', script_name, sys.platform)
sys.exit(1)
version = platform.mac_ver()[0]
# we only need the differenciation between leopard and snow leopard
if version.startswith("10.6"):
SNOWLEOPARD = True
logging.info('[%s] using snow leopard scripts (version = %s)',
script_name, version)
adjustview_scriptname = "applescript-adjustview-10-6.master"
seticon_scriptname = "applescript-seticon-10-6.master"
else:
SNOWLEOPARD = False
logging.info('[%s] using leopard scripts (version = %s)', script_name,
version)
adjustview_scriptname = "applescript-adjustview-10-5.master"
seticon_scriptname = "applescript-seticon-10-5.master"
if not os.path.isfile(adjustview_scriptname) \
or not os.path.isfile(seticon_scriptname):
logging.error('[%s] could not find apple scripts for given OS X '
+ 'version %s', script_name, version)
sys.exit(1)
settings = dict()
for k in config.options('openlp'):
settings[k] = config.get('openlp', k)
# prepare the configuration files
os.system('python expander.py --config %(config_file)s \
--template openlp.spec.master \
--expandto %(target_directory)s/openlp.spec' \
% { 'config_file' : options.config, 'target_directory' : os.getcwd() })
os.system('python expander.py --config %(config_file)s \
--template Info.plist.master \
--expandto %(target_directory)s/Info.plist' \
% { 'config_file' : options.config, 'target_directory' : os.getcwd() })
os.system('python expander.py --config %(config_file)s \
--template version.master \
--expandto %(target_directory)s/.version' \
% { 'config_file' : options.config, 'target_directory' : os.getcwd() })
# prepare variables
app_name_lower = settings['openlp_appname'].lower()
app_dir = os.getcwd() + '/' + settings['openlp_appname'] + '.app'
# if the view option is set, skip the building steps
if (do_build is True):
build_application(settings, app_name_lower, app_dir)
if (do_deploy_qt is True):
deploy_qt(settings)
if (do_create_dmg is True):
(volume_basedir, dmg_file) = create_dmg(settings)
else:
# setting base dir
volume_basedir = options.basedir
dmg_file = os.getcwd() + '/' + settings['openlp_dmgname'] + '.dmg'
if (do_package_view is True):
adjust_package_view(settings, adjustview_scriptname)
if (do_create_dmg is True):
unmount_dmg(settings, volume_basedir)
if (do_compress_dmg is True):
compress_dmg(settings)
if (do_compress_view is True):
compress_view(settings, seticon_scriptname, dmg_file)
if (do_compress_dmg is True):
logging.info('[%s] finished creating dmg file, resulting file is "%s"',
script_name, dmg_file)

202
resources/osx/expander.py Executable file
View File

@ -0,0 +1,202 @@
#!/usr/bin/python
# -*- encoding: utf-8 -*-
# TODOs:
# - defaults for non-supplied expansions:
# template contains
import ConfigParser
import logging
import optparse
import os
import re
import sys
# variable expansion:
# - %(dog)s --- normal python expansion
# - %(dog%)s --- no python expansion, leave as is (stripping the trailing %)
# - %(dog:cat) --- if there is an expansion for dog, dog will be used;
# otherwise if cat exists cat will be used
# - %(dog=cat) --- if there is an expansion for dog, dog will be used;
# otherwise "cat" will be used
# re_conf = re.compile(r'(?<!%)%\((?P<key>[^\(]+?)\)s')
re_conf = re.compile(r'(?P<verbatim>%?)%\((?P<key>[^+=:&\)]+?)'
+ '(?:(?P<kind>[+=:&])(?P<default>[^\)]+))?\)(?P<type>s|d)')
def expand_variable(match, expansions, errors):
key = match.group('key')
kind = match.group('kind')
default = match.group('default')
typ = match.group('type')
verbatim = match.group('verbatim')
if verbatim:
return match.group(0)[1:]
# literal default
if kind == '=':
if key in expansions:
return expansions[key]
return default
# variable default
if kind == ':' and default in expansions:
return expansions[default]
if kind == '+' and default in expansions:
if key in expansions:
key = expansions[key]
if typ == 's':
return '%s%s' % (key, expansions[default])
if typ == 'd':
try:
return str(int(key) + int(expansions[default]))
except:
pass
if kind == '&' and default in expansions:
if typ == 's':
return '%s%s' % (key, expansions[default])
if typ == 'd':
try:
return str(int(key) + int(expansions[default]))
except:
pass
if key in expansions:
return expansions[key]
if not match.group(0) in errors:
errors.append(match.group(0))
return None
options = None
if __name__ == '__main__':
# get config file
parser = optparse.OptionParser()
parser.add_option('-c', '--config', dest='config',
help='config file', metavar='CONFIG')
parser.add_option('-t', '--template', dest='template',
help='template file', metavar='TEMPLATE')
parser.add_option('-x', '--expandto', dest='expanded',
help='expanded file', metavar='EXPANDED')
parser.add_option('-e', '--echo', dest='echo',
help='echo variable', metavar='ECHOVAR')
(options, args) = parser.parse_args()
if not options.config:
parser.error('option --config|-c is required')
if not os.path.exists(options.config):
parser.error('config file "%s" does not exist' % options.config)
if not options.echo:
if not options.template:
parser.error('option --template|-t is required')
if not os.path.exists(options.template):
parser.error('template file "%s" does not exist' \
% options.template)
if not options.expanded:
parser.error('option --expandto|-e is required')
logHandler = logging.StreamHandler()
logHandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)-8s '
+ ' %(message)s', '%a, %d %b %Y %H:%M:%S'))
logging.getLogger().addHandler(logHandler)
logging.getLogger().setLevel(logging.DEBUG)
config = ConfigParser.RawConfigParser()
config.readfp(open(options.config, 'r'))
if not config.has_section('openlp'):
logging.error('[expander] %s: config file "%s" lacks an [openlp] '
+ 'section', options.template, options.config)
expansions = dict()
for k in config.options('openlp'):
expansions[k] = config.get('openlp', k)
# commandline overrides?
for override in args:
if not '=' in override:
continue
(k, v) = override.split('=', 2)
expansions[k] = v
if options.echo:
if options.echo in expansions:
print expansions[options.echo]
sys.exit(0)
else:
sys.exit(1)
# closure to capture expansions and errors variable
errors = []
expanded = []
try:
# try to expand the template
line = 0
faulty = False
template = open(options.template, 'r')
raw = template.readlines()
template.close()
def _expand(m):
return expand_variable(m, expansions = expansions, errors = errors)
for l in raw:
line += 1
exp = re_conf.sub(_expand, l)
if errors:
for key in errors:
logging.error('[expander] %s: line %d: could not expand '
+ 'key "%s"', options.template, line, key)
faulty = True
errors = []
else:
expanded.append(exp)
if faulty:
sys.exit(1)
# successfully expanded template, now backup potentially existing
# target file
targetFile = options.expanded % expansions
if os.path.exists(targetFile):
if os.path.exists('%s~' % targetFile):
os.unlink('%s~' % targetFile)
os.rename(options.expanded, '%s~' % targetFile)
logging.info('[expander] %s: backed up existing target file "%s" '
+ 'to "%s"', options.template, targetFile,
'%s~' % options.expanded)
# make sure that target directory exists
targetDir = os.path.dirname(targetFile)
if not os.path.exists(targetDir):
os.makedirs(targetDir)
# write target file
try:
target = open(targetFile, 'w')
for exp in expanded:
target.write(exp)
target.close()
except Exception, e:
logging.error('[expander] %s: could not expand to "%s"',
options.template, options.expaned, e)
# copy over file access mode from template
mode = os.stat(options.template)
os.chmod(options.expanded, mode.st_mode)
logging.info('[expander] expanded "%s" to "%s"',
options.template, options.expanded)
except:
pass

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

11
resources/osx/openlp.cfg Executable file
View File

@ -0,0 +1,11 @@
[openlp]
openlp_appname = OpenLP
openlp_dmgname = OpenLP-1.9.4-bzrXXXX
openlp_version = XXXX
openlp_full_version = 1.9.4-latest
openlp_basedir = /Users/openlp/trunk
openlp_icon_file = openlp-logo-with-text.icns
openlp_dmg_icon_file = openlp-logo-420x420.png
installer_backgroundimage_file = installation-background.png
pyinstaller_basedir = /Users/openlp/pyinstaller/trunk
qt_menu_basedir = /Library/Frameworks/QtGui.framework/Versions/4/Resources/qt_menu.nib

View File

@ -0,0 +1,24 @@
# -*- mode: python -*-
a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), '%(openlp_basedir)s/openlp.pyw'],
pathex=['%(pyinstaller_basedir)s'], hookspath=['%(openlp_basedir)s/resources/pyinstaller'])
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
exclude_binaries=1,
name=os.path.join('build/pyi.darwin/openlp', 'openlp'),
debug=False,
strip=False,
upx=True,
console=1 )
coll = COLLECT( exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name=os.path.join('dist', 'openlp'))
import sys
if sys.platform.startswith("darwin"):
app = BUNDLE(coll,
name='%(openlp_appname)s.app',
version='%(openlp_version)s')

1
resources/osx/version.master Executable file
View File

@ -0,0 +1 @@
%(openlp_full_version)s