diff --git a/openlp.pyw b/openlp.pyw
index 85ba81fba..ad06cfe0f 100755
--- a/openlp.pyw
+++ b/openlp.pyw
@@ -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)
diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py
index b68237b4a..eaf628a34 100644
--- a/openlp/core/lib/rendermanager.py
+++ b/openlp/core/lib/rendermanager.py
@@ -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)
diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py
index 4ec1f91cc..b9de8a12e 100644
--- a/openlp/core/ui/mainwindow.py
+++ b/openlp/core/ui/mainwindow.py
@@ -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))
diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py
index e13985e4b..e2dde59fd 100644
--- a/openlp/plugins/bibles/lib/http.py
+++ b/openlp/plugins/bibles/lib/http.py
@@ -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,21 +221,14 @@ 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
@@ -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)
diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py
index 6794ec1ba..344d11584 100644
--- a/openlp/plugins/bibles/lib/manager.py
+++ b/openlp/plugins/bibles/lib/manager.py
@@ -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,
diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py
index e7cf273c1..ab7897828 100644
--- a/openlp/plugins/bibles/lib/mediaitem.py
+++ b/openlp/plugins/bibles/lib/mediaitem.py
@@ -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
diff --git a/openlp/plugins/songs/lib/foilpresenterimport.py b/openlp/plugins/songs/lib/foilpresenterimport.py
index c0f9f093d..0d81e6f41 100644
--- a/openlp/plugins/songs/lib/foilpresenterimport.py
+++ b/openlp/plugins/songs/lib/foilpresenterimport.py
@@ -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'',
diff --git a/resources/osx/Info.plist.master b/resources/osx/Info.plist.master
new file mode 100755
index 000000000..e97e1faf5
--- /dev/null
+++ b/resources/osx/Info.plist.master
@@ -0,0 +1,32 @@
+
+
+
+
+CFBundleIdentifier
+org.openlp
+CFBundleShortVersionString
+%(openlp_version)s
+CFBundleVersion
+%(openlp_version)s
+CFBundleInfoDictionaryVersion
+6.0
+CFBundleDisplayName
+%(openlp_appname)s
+CFBundleIconFile
+openlp-logo-with-text.icns
+CFBundleExecutable
+MacOS/openlp
+CFBundleName
+%(openlp_appname)s
+CFBundleGetInfoString
+%(openlp_appname)s %(openlp_version)s
+LSHasLocalizedDisplayName
+
+NSAppleScriptEnabled
+
+CFBundlePackageType
+APPL
+LSBackgroundOnly
+
+
+
diff --git a/resources/osx/Makefile b/resources/osx/Makefile
new file mode 100644
index 000000000..422749da5
--- /dev/null
+++ b/resources/osx/Makefile
@@ -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
+
diff --git a/resources/osx/applescript-adjustview-10-5.master b/resources/osx/applescript-adjustview-10-5.master
new file mode 100755
index 000000000..abc0e5a8d
--- /dev/null
+++ b/resources/osx/applescript-adjustview-10-5.master
@@ -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
diff --git a/resources/osx/applescript-adjustview-10-6.master b/resources/osx/applescript-adjustview-10-6.master
new file mode 100755
index 000000000..2b5a0c000
--- /dev/null
+++ b/resources/osx/applescript-adjustview-10-6.master
@@ -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
diff --git a/resources/osx/applescript-seticon-10-5.master b/resources/osx/applescript-seticon-10-5.master
new file mode 100755
index 000000000..738c4b883
--- /dev/null
+++ b/resources/osx/applescript-seticon-10-5.master
@@ -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
diff --git a/resources/osx/applescript-seticon-10-6.master b/resources/osx/applescript-seticon-10-6.master
new file mode 100755
index 000000000..849f02344
--- /dev/null
+++ b/resources/osx/applescript-seticon-10-6.master
@@ -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
diff --git a/resources/osx/build.py b/resources/osx/build.py
new file mode 100644
index 000000000..dfe0b897a
--- /dev/null
+++ b/resources/osx/build.py
@@ -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)
+
diff --git a/resources/osx/expander.py b/resources/osx/expander.py
new file mode 100755
index 000000000..77b0d1441
--- /dev/null
+++ b/resources/osx/expander.py
@@ -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'(?[^\(]+?)\)s')
+re_conf = re.compile(r'(?P%?)%\((?P[^+=:&\)]+?)'
+ + '(?:(?P[+=:&])(?P[^\)]+))?\)(?Ps|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
+
diff --git a/resources/osx/installation-background.png b/resources/osx/installation-background.png
new file mode 100755
index 000000000..8a41d3b68
Binary files /dev/null and b/resources/osx/installation-background.png differ
diff --git a/resources/osx/openlp-logo-420x420-background.png b/resources/osx/openlp-logo-420x420-background.png
new file mode 100755
index 000000000..1008621d6
Binary files /dev/null and b/resources/osx/openlp-logo-420x420-background.png differ
diff --git a/resources/osx/openlp-logo-420x420.png b/resources/osx/openlp-logo-420x420.png
new file mode 100644
index 000000000..85457d8fb
Binary files /dev/null and b/resources/osx/openlp-logo-420x420.png differ
diff --git a/resources/osx/openlp-logo-with-text.icns b/resources/osx/openlp-logo-with-text.icns
new file mode 100755
index 000000000..6066b111e
Binary files /dev/null and b/resources/osx/openlp-logo-with-text.icns differ
diff --git a/resources/osx/openlp-splash-screen.png b/resources/osx/openlp-splash-screen.png
new file mode 100755
index 000000000..618e47c6e
Binary files /dev/null and b/resources/osx/openlp-splash-screen.png differ
diff --git a/resources/osx/openlp.cfg b/resources/osx/openlp.cfg
new file mode 100755
index 000000000..1f04861b5
--- /dev/null
+++ b/resources/osx/openlp.cfg
@@ -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
diff --git a/resources/osx/openlp.spec.master b/resources/osx/openlp.spec.master
new file mode 100755
index 000000000..94aec2a60
--- /dev/null
+++ b/resources/osx/openlp.spec.master
@@ -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')
diff --git a/resources/osx/version.master b/resources/osx/version.master
new file mode 100755
index 000000000..d517a51bc
--- /dev/null
+++ b/resources/osx/version.master
@@ -0,0 +1 @@
+%(openlp_full_version)s