r1972
47
documentation/openlp.1
Normal file
@ -0,0 +1,47 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.9.
|
||||
.TH OPENLP "1" "May 2012" "OpenLP 1.9.9" "User Commands"
|
||||
.SH NAME
|
||||
OpenLP \- Church worship presentation software
|
||||
.SH SYNOPSIS
|
||||
.B openlp
|
||||
[\fIoptions\fR] [\fIqt-options\fR]
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
show program's version number and exit
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
show this help message and exit
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-no\-error\-form\fR
|
||||
Disable the error notification form.
|
||||
.TP
|
||||
\fB\-l\fR LEVEL, \fB\-\-log\-level\fR=\fILEVEL\fR
|
||||
Set logging to LEVEL level. Valid values are "debug",
|
||||
"info", "warning".
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-portable\fR
|
||||
Specify if this should be run as a portable app, off a
|
||||
USB flash drive (not implemented).
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-dev\-version\fR
|
||||
Ignore the version file and pull the version directly
|
||||
from Bazaar
|
||||
.TP
|
||||
\fB\-s\fR STYLE, \fB\-\-style\fR=\fISTYLE\fR
|
||||
Set the Qt4 style (passed directly to Qt4).
|
||||
.TP
|
||||
\fB\-\-testing\fR
|
||||
Run by testing framework
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B OpenLP
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B OpenLP
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info OpenLP
|
||||
.PP
|
||||
should give you access to the complete manual.
|
@ -26,12 +26,6 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
# Import uuid now, to avoid the rare bug described in the support system:
|
||||
# http://support.openlp.org/issues/102
|
||||
# If https://bugs.gentoo.org/show_bug.cgi?id=317557 is fixed, the import can be
|
||||
# removed.
|
||||
import uuid
|
||||
|
||||
from openlp.core import main
|
||||
|
||||
|
||||
|
@ -53,8 +53,7 @@ class OpenLPDockWidget(QtGui.QDockWidget):
|
||||
self.setWindowIcon(build_icon(icon))
|
||||
# Sort out the minimum width.
|
||||
screens = ScreenList.get_instance()
|
||||
screen_width = screens.current[u'size'].width()
|
||||
mainwindow_docbars = screen_width / 5
|
||||
mainwindow_docbars = screens.current[u'size'].width() / 5
|
||||
if mainwindow_docbars > 300:
|
||||
self.setMinimumWidth(300)
|
||||
else:
|
||||
|
@ -46,13 +46,36 @@ class FormattingTags(object):
|
||||
"""
|
||||
Provide access to the html_expands list.
|
||||
"""
|
||||
# Load user defined tags otherwise user defined tags are not present.
|
||||
return FormattingTags.html_expands
|
||||
|
||||
@staticmethod
|
||||
def reset_html_tags():
|
||||
def save_html_tags():
|
||||
"""
|
||||
Resets the html_expands list.
|
||||
Saves all formatting tags except protected ones.
|
||||
"""
|
||||
tags = []
|
||||
for tag in FormattingTags.html_expands:
|
||||
if not tag[u'protected'] and not tag.get(u'temporary'):
|
||||
# Using dict ensures that copy is made and encoding of values
|
||||
# a little later does not affect tags in the original list
|
||||
tags.append(dict(tag))
|
||||
tag = tags[-1]
|
||||
# Remove key 'temporary' from tags.
|
||||
# It is not needed to be saved.
|
||||
if u'temporary' in tag:
|
||||
del tag[u'temporary']
|
||||
for element in tag:
|
||||
if isinstance(tag[element], unicode):
|
||||
tag[element] = tag[element].encode('utf8')
|
||||
# Formatting Tags were also known as display tags.
|
||||
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
||||
QtCore.QVariant(cPickle.dumps(tags) if tags else u''))
|
||||
|
||||
@staticmethod
|
||||
def load_tags():
|
||||
"""
|
||||
Load the Tags from store so can be used in the system or used to
|
||||
update the display.
|
||||
"""
|
||||
temporary_tags = [tag for tag in FormattingTags.html_expands
|
||||
if tag.get(u'temporary')]
|
||||
@ -140,38 +163,6 @@ class FormattingTags(object):
|
||||
FormattingTags.add_html_tags(base_tags)
|
||||
FormattingTags.add_html_tags(temporary_tags)
|
||||
|
||||
@staticmethod
|
||||
def save_html_tags():
|
||||
"""
|
||||
Saves all formatting tags except protected ones.
|
||||
"""
|
||||
tags = []
|
||||
for tag in FormattingTags.html_expands:
|
||||
if not tag[u'protected'] and not tag.get(u'temporary'):
|
||||
# Using dict ensures that copy is made and encoding of values
|
||||
# a little later does not affect tags in the original list
|
||||
tags.append(dict(tag))
|
||||
tag = tags[-1]
|
||||
# Remove key 'temporary' from tags.
|
||||
# It is not needed to be saved.
|
||||
if u'temporary' in tag:
|
||||
del tag[u'temporary']
|
||||
for element in tag:
|
||||
if isinstance(tag[element], unicode):
|
||||
tag[element] = tag[element].encode('utf8')
|
||||
# Formatting Tags were also known as display tags.
|
||||
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
||||
QtCore.QVariant(cPickle.dumps(tags) if tags else u''))
|
||||
|
||||
@staticmethod
|
||||
def load_tags():
|
||||
"""
|
||||
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
|
||||
dsiplay to the correct version.
|
||||
"""
|
||||
# Initial Load of the Tags
|
||||
FormattingTags.reset_html_tags()
|
||||
# Formatting Tags were also known as display tags.
|
||||
user_expands = QtCore.QSettings().value(u'displayTags/html_tags',
|
||||
QtCore.QVariant(u'')).toString()
|
||||
@ -187,17 +178,13 @@ class FormattingTags(object):
|
||||
FormattingTags.add_html_tags(user_tags)
|
||||
|
||||
@staticmethod
|
||||
def add_html_tags(tags, save=False):
|
||||
def add_html_tags(tags):
|
||||
"""
|
||||
Add a list of tags to the list.
|
||||
|
||||
``tags``
|
||||
The list with tags to add.
|
||||
|
||||
``save``
|
||||
Defaults to ``False``. If set to ``True`` the given ``tags`` are
|
||||
saved to the config.
|
||||
|
||||
Each **tag** has to be a ``dict`` and should have the following keys:
|
||||
|
||||
* desc
|
||||
@ -225,8 +212,6 @@ class FormattingTags(object):
|
||||
displaying text containing the tag. It has to be a ``boolean``.
|
||||
"""
|
||||
FormattingTags.html_expands.extend(tags)
|
||||
if save:
|
||||
FormattingTags.save_html_tags()
|
||||
|
||||
@staticmethod
|
||||
def remove_html_tag(tag_id):
|
||||
|
@ -100,6 +100,7 @@ class Image(object):
|
||||
variables ``image`` and ``image_bytes`` to ``None`` and add the image object
|
||||
to the queue of images to process.
|
||||
"""
|
||||
secondary_priority = 0
|
||||
def __init__(self, name, path, source, background):
|
||||
self.name = name
|
||||
self.path = path
|
||||
@ -108,25 +109,40 @@ class Image(object):
|
||||
self.priority = Priority.Normal
|
||||
self.source = source
|
||||
self.background = background
|
||||
self.secondary_priority = Image.secondary_priority
|
||||
Image.secondary_priority += 1
|
||||
|
||||
|
||||
class PriorityQueue(Queue.PriorityQueue):
|
||||
"""
|
||||
Customised ``Queue.PriorityQueue``.
|
||||
|
||||
Each item in the queue must be tuple with three values. The first value
|
||||
is the :class:`Image`'s ``priority`` attribute, the second value
|
||||
the :class:`Image`'s ``secondary_priority`` attribute. The last value the
|
||||
:class:`Image` instance itself::
|
||||
|
||||
(image.priority, image.secondary_priority, image)
|
||||
|
||||
Doing this, the :class:`Queue.PriorityQueue` will sort the images according
|
||||
to their priorities, but also according to there number. However, the number
|
||||
only has an impact on the result if there are more images with the same
|
||||
priority. In such case the image which has been added earlier is privileged.
|
||||
"""
|
||||
def modify_priority(self, image, new_priority):
|
||||
"""
|
||||
Modifies the priority of the given ``image``.
|
||||
|
||||
``image``
|
||||
The image to remove. This should be an ``Image`` instance.
|
||||
The image to remove. This should be an :class:`Image` instance.
|
||||
|
||||
``new_priority``
|
||||
The image's new priority.
|
||||
The image's new priority. See the :class:`Priority` class for
|
||||
priorities.
|
||||
"""
|
||||
self.remove(image)
|
||||
image.priority = new_priority
|
||||
self.put((image.priority, image))
|
||||
self.put((image.priority, image.secondary_priority, image))
|
||||
|
||||
def remove(self, image):
|
||||
"""
|
||||
@ -135,8 +151,8 @@ class PriorityQueue(Queue.PriorityQueue):
|
||||
``image``
|
||||
The image to remove. This should be an ``Image`` instance.
|
||||
"""
|
||||
if (image.priority, image) in self.queue:
|
||||
self.queue.remove((image.priority, image))
|
||||
if (image.priority, image.secondary_priority, image) in self.queue:
|
||||
self.queue.remove((image.priority, image.secondary_priority, image))
|
||||
|
||||
|
||||
class ImageManager(QtCore.QObject):
|
||||
@ -261,7 +277,8 @@ class ImageManager(QtCore.QObject):
|
||||
if not name in self._cache:
|
||||
image = Image(name, path, source, background)
|
||||
self._cache[name] = image
|
||||
self._conversion_queue.put((image.priority, image))
|
||||
self._conversion_queue.put(
|
||||
(image.priority, image.secondary_priority, image))
|
||||
else:
|
||||
log.debug(u'Image in cache %s:%s' % (name, path))
|
||||
# We want only one thread.
|
||||
@ -282,7 +299,7 @@ class ImageManager(QtCore.QObject):
|
||||
Actually does the work.
|
||||
"""
|
||||
log.debug(u'_process_cache')
|
||||
image = self._conversion_queue.get()[1]
|
||||
image = self._conversion_queue.get()[2]
|
||||
# Generate the QImage for the image.
|
||||
if image.image is None:
|
||||
image.image = resize_image(image.path, self.width, self.height,
|
||||
|
@ -373,12 +373,12 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
Process a list for files either from the File Dialog or from Drag and
|
||||
Drop
|
||||
|
||||
``files``
|
||||
The files to be loaded
|
||||
``files``
|
||||
The files to be loaded.
|
||||
"""
|
||||
names = []
|
||||
fullList = []
|
||||
for count in range(0, self.listView.count()):
|
||||
for count in range(self.listView.count()):
|
||||
names.append(unicode(self.listView.item(count).text()))
|
||||
fullList.append(unicode(self.listView.item(count).
|
||||
data(QtCore.Qt.UserRole).toString()))
|
||||
|
@ -125,7 +125,7 @@ class Renderer(object):
|
||||
Set the appropriate theme depending on the theme level.
|
||||
Called by the service item when building a display frame
|
||||
|
||||
``theme``
|
||||
``override_theme``
|
||||
The name of the song-level theme. None means the service
|
||||
item wants to use the given value.
|
||||
|
||||
@ -235,8 +235,8 @@ class Renderer(object):
|
||||
# the first two slides (and neglect the last for now).
|
||||
if len(slides) == 3:
|
||||
html_text = expand_tags(u'\n'.join(slides[:2]))
|
||||
# We check both slides to determine if the optional break is
|
||||
# needed (there is only one optional break).
|
||||
# We check both slides to determine if the optional split is
|
||||
# needed (there is only one optional split).
|
||||
else:
|
||||
html_text = expand_tags(u'\n'.join(slides))
|
||||
html_text = html_text.replace(u'\n', u'<br>')
|
||||
@ -247,14 +247,18 @@ class Renderer(object):
|
||||
else:
|
||||
# The first optional slide fits, which means we have to
|
||||
# render the first optional slide.
|
||||
text_contains_break = u'[---]' in text
|
||||
if text_contains_break:
|
||||
text_contains_split = u'[---]' in text
|
||||
if text_contains_split:
|
||||
try:
|
||||
text_to_render, text = \
|
||||
text.split(u'\n[---]\n', 1)
|
||||
except:
|
||||
text_to_render = text.split(u'\n[---]\n')[0]
|
||||
text = u''
|
||||
text_to_render, raw_tags, html_tags = \
|
||||
self._get_start_tags(text_to_render)
|
||||
if text:
|
||||
text = raw_tags + text
|
||||
else:
|
||||
text_to_render = text
|
||||
text = u''
|
||||
@ -263,7 +267,7 @@ class Renderer(object):
|
||||
if len(slides) > 1 and text:
|
||||
# Add all slides apart from the last one the list.
|
||||
pages.extend(slides[:-1])
|
||||
if text_contains_break:
|
||||
if text_contains_split:
|
||||
text = slides[-1] + u'\n[---]\n' + text
|
||||
else:
|
||||
text = slides[-1] + u'\n'+ text
|
||||
@ -492,7 +496,7 @@ class Renderer(object):
|
||||
(raw_text.find(tag[u'start tag']), tag[u'start tag'],
|
||||
tag[u'end tag']))
|
||||
html_tags.append(
|
||||
(raw_text.find(tag[u'start tag']), tag[u'start html']))
|
||||
(raw_text.find(tag[u'start tag']), tag[u'start html']))
|
||||
# Sort the lists, so that the tags which were opened first on the first
|
||||
# slide (the text we are checking) will be opened first on the next
|
||||
# slide as well.
|
||||
|
@ -313,17 +313,12 @@ class ServiceItem(object):
|
||||
self.from_plugin = header[u'from_plugin']
|
||||
self.capabilities = header[u'capabilities']
|
||||
# Added later so may not be present in older services.
|
||||
if u'search' in header:
|
||||
self.search_string = header[u'search']
|
||||
self.data_string = header[u'data']
|
||||
if u'xml_version' in header:
|
||||
self.xml_version = header[u'xml_version']
|
||||
if u'start_time' in header:
|
||||
self.start_time = header[u'start_time']
|
||||
if u'end_time' in header:
|
||||
self.end_time = header[u'end_time']
|
||||
if u'media_length' in header:
|
||||
self.media_length = header[u'media_length']
|
||||
self.search_string = header.get(u'search', u'')
|
||||
self.data_string = header.get(u'data', u'')
|
||||
self.xml_version = header.get(u'xml_version')
|
||||
self.start_time = header.get(u'start_time', 0)
|
||||
self.end_time = header.get(u'end_time', 0)
|
||||
self.media_length = header.get(u'media_length', 0)
|
||||
if u'background_audio' in header:
|
||||
self.background_audio = []
|
||||
for filename in header[u'background_audio']:
|
||||
|
@ -57,6 +57,14 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
QtCore.SIGNAL(u'clicked()'), self.onDeleteClicked)
|
||||
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'),
|
||||
self.close)
|
||||
QtCore.QObject.connect(self.descriptionLineEdit,
|
||||
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||
QtCore.QObject.connect(self.tagLineEdit,
|
||||
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||
QtCore.QObject.connect(self.startTagLineEdit,
|
||||
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||
QtCore.QObject.connect(self.endTagLineEdit,
|
||||
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||
# Forces reloading of tags from openlp configuration.
|
||||
FormattingTags.load_tags()
|
||||
|
||||
@ -65,7 +73,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
Load Display and set field state.
|
||||
"""
|
||||
# Create initial copy from master
|
||||
self._resetTable()
|
||||
self._reloadTable()
|
||||
self.selected = -1
|
||||
return QtGui.QDialog.exec_(self)
|
||||
|
||||
@ -73,9 +81,9 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
"""
|
||||
Table Row selected so display items and set field state.
|
||||
"""
|
||||
row = self.tagTableWidget.currentRow()
|
||||
html = FormattingTags.html_expands[row]
|
||||
self.selected = row
|
||||
self.savePushButton.setEnabled(False)
|
||||
self.selected = self.tagTableWidget.currentRow()
|
||||
html = FormattingTags.get_html_tags()[self.selected]
|
||||
self.descriptionLineEdit.setText(html[u'desc'])
|
||||
self.tagLineEdit.setText(self._strip(html[u'start tag']))
|
||||
self.startTagLineEdit.setText(html[u'start html'])
|
||||
@ -85,21 +93,26 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
self.tagLineEdit.setEnabled(False)
|
||||
self.startTagLineEdit.setEnabled(False)
|
||||
self.endTagLineEdit.setEnabled(False)
|
||||
self.savePushButton.setEnabled(False)
|
||||
self.deletePushButton.setEnabled(False)
|
||||
else:
|
||||
self.descriptionLineEdit.setEnabled(True)
|
||||
self.tagLineEdit.setEnabled(True)
|
||||
self.startTagLineEdit.setEnabled(True)
|
||||
self.endTagLineEdit.setEnabled(True)
|
||||
self.savePushButton.setEnabled(True)
|
||||
self.deletePushButton.setEnabled(True)
|
||||
|
||||
def onTextEdited(self, text):
|
||||
"""
|
||||
Enable the ``savePushButton`` when any of the selected tag's properties
|
||||
has been changed.
|
||||
"""
|
||||
self.savePushButton.setEnabled(True)
|
||||
|
||||
def onNewClicked(self):
|
||||
"""
|
||||
Add a new tag to list only if it is not a duplicate.
|
||||
"""
|
||||
for html in FormattingTags.html_expands:
|
||||
for html in FormattingTags.get_html_tags():
|
||||
if self._strip(html[u'start tag']) == u'n':
|
||||
critical_error_message_box(
|
||||
translate('OpenLP.FormattingTagForm', 'Update Error'),
|
||||
@ -117,11 +130,13 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
u'temporary': False
|
||||
}
|
||||
FormattingTags.add_html_tags([tag])
|
||||
self._resetTable()
|
||||
FormattingTags.save_html_tags()
|
||||
self._reloadTable()
|
||||
# Highlight new row
|
||||
self.tagTableWidget.selectRow(self.tagTableWidget.rowCount() - 1)
|
||||
self.onRowSelected()
|
||||
self.tagTableWidget.scrollToBottom()
|
||||
#self.savePushButton.setEnabled(False)
|
||||
|
||||
def onDeleteClicked(self):
|
||||
"""
|
||||
@ -130,14 +145,14 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
if self.selected != -1:
|
||||
FormattingTags.remove_html_tag(self.selected)
|
||||
self.selected = -1
|
||||
self._resetTable()
|
||||
FormattingTags.save_html_tags()
|
||||
FormattingTags.save_html_tags()
|
||||
self._reloadTable()
|
||||
|
||||
def onSavedClicked(self):
|
||||
"""
|
||||
Update Custom Tag details if not duplicate and save the data.
|
||||
"""
|
||||
html_expands = FormattingTags.html_expands
|
||||
html_expands = FormattingTags.get_html_tags()
|
||||
if self.selected != -1:
|
||||
html = html_expands[self.selected]
|
||||
tag = unicode(self.tagLineEdit.text())
|
||||
@ -157,14 +172,13 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
# Keep temporary tags when the user changes one.
|
||||
html[u'temporary'] = False
|
||||
self.selected = -1
|
||||
self._resetTable()
|
||||
FormattingTags.save_html_tags()
|
||||
self._reloadTable()
|
||||
|
||||
def _resetTable(self):
|
||||
def _reloadTable(self):
|
||||
"""
|
||||
Reset List for loading.
|
||||
"""
|
||||
FormattingTags.load_tags()
|
||||
self.tagTableWidget.clearContents()
|
||||
self.tagTableWidget.setRowCount(0)
|
||||
self.newPushButton.setEnabled(True)
|
||||
|
@ -64,7 +64,7 @@ class MediaDockManager(object):
|
||||
visible_title = media_item.plugin.getString(StringContent.VisibleName)
|
||||
log.debug(u'Inserting %s dock' % visible_title[u'title'])
|
||||
match = False
|
||||
for dock_index in range(0, self.media_dock.count()):
|
||||
for dock_index in range(self.media_dock.count()):
|
||||
if self.media_dock.widget(dock_index).settingsSection == \
|
||||
media_item.plugin.name:
|
||||
match = True
|
||||
@ -81,7 +81,7 @@ class MediaDockManager(object):
|
||||
"""
|
||||
visible_title = media_item.plugin.getString(StringContent.VisibleName)
|
||||
log.debug(u'remove %s dock' % visible_title[u'title'])
|
||||
for dock_index in range(0, self.media_dock.count()):
|
||||
for dock_index in range(self.media_dock.count()):
|
||||
if self.media_dock.widget(dock_index):
|
||||
if self.media_dock.widget(dock_index).settingsSection == \
|
||||
media_item.plugin.name:
|
||||
|
@ -106,13 +106,13 @@ class ScreenList(object):
|
||||
"""
|
||||
# Do not log at start up.
|
||||
if changed_screen != -1:
|
||||
log.info(u'screen_count_changed %d' % self.desktop.numScreens())
|
||||
log.info(u'screen_count_changed %d' % self.desktop.screenCount())
|
||||
# Remove unplugged screens.
|
||||
for screen in copy.deepcopy(self.screen_list):
|
||||
if screen[u'number'] == self.desktop.numScreens():
|
||||
if screen[u'number'] == self.desktop.screenCount():
|
||||
self.remove_screen(screen[u'number'])
|
||||
# Add new screens.
|
||||
for number in xrange(0, self.desktop.numScreens()):
|
||||
for number in xrange(self.desktop.screenCount()):
|
||||
if not self.screen_exists(number):
|
||||
self.add_screen({
|
||||
u'number': number,
|
||||
|
@ -51,7 +51,7 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
||||
"""
|
||||
Set up key bindings and mouse behaviour for the service list
|
||||
"""
|
||||
def __init__(self, serviceManager, parent=None, name=None):
|
||||
def __init__(self, serviceManager, parent=None):
|
||||
QtGui.QTreeWidget.__init__(self, parent)
|
||||
self.serviceManager = serviceManager
|
||||
|
||||
@ -64,6 +64,9 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
||||
elif event.key() == QtCore.Qt.Key_Down:
|
||||
self.serviceManager.onMoveSelectionDown()
|
||||
event.accept()
|
||||
elif event.key() == QtCore.Qt.Key_Delete:
|
||||
self.serviceManager.onDeleteFromService()
|
||||
event.accept()
|
||||
event.ignore()
|
||||
else:
|
||||
event.ignore()
|
||||
@ -101,7 +104,6 @@ class ServiceManager(QtGui.QWidget):
|
||||
QtGui.QWidget.__init__(self, parent)
|
||||
self.mainwindow = mainwindow
|
||||
self.serviceItems = []
|
||||
self.serviceName = u''
|
||||
self.suffixes = []
|
||||
self.dropPosition = 0
|
||||
self.expandTabs = False
|
||||
@ -219,6 +221,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
icon=u':/general/general_delete.png',
|
||||
tooltip=translate('OpenLP.ServiceManager',
|
||||
'Delete the selected item from the service.'),
|
||||
shortcuts=[QtCore.Qt.Key_Delete],
|
||||
triggers=self.onDeleteFromService)
|
||||
self.orderToolbar.addSeparator()
|
||||
self.serviceManagerList.expand = self.orderToolbar.addToolbarAction(
|
||||
@ -299,17 +302,14 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.timeAction = create_widget_action(self.menu,
|
||||
text=translate('OpenLP.ServiceManager', '&Start Time'),
|
||||
icon=u':/media/media_time.png', triggers=self.onStartTimeForm)
|
||||
self.deleteAction = create_widget_action(self.menu,
|
||||
text=translate('OpenLP.ServiceManager', '&Delete From Service'),
|
||||
icon=u':/general/general_delete.png',
|
||||
triggers=self.onDeleteFromService)
|
||||
# Add already existing delete action to the menu.
|
||||
self.menu.addAction(self.serviceManagerList.delete)
|
||||
self.menu.addSeparator()
|
||||
self.previewAction = create_widget_action(self.menu,
|
||||
text=translate('OpenLP.ServiceManager', 'Show &Preview'),
|
||||
icon=u':/general/general_preview.png', triggers=self.makePreview)
|
||||
self.liveAction = create_widget_action(self.menu,
|
||||
text=translate('OpenLP.ServiceManager', 'Show &Live'),
|
||||
icon=u':/general/general_live.png', triggers=self.makeLive)
|
||||
# Add already existing make live action to the menu.
|
||||
self.menu.addAction(self.serviceManagerList.makeLive)
|
||||
self.menu.addSeparator()
|
||||
self.themeMenu = QtGui.QMenu(
|
||||
translate('OpenLP.ServiceManager', '&Change Item Theme'))
|
||||
@ -561,14 +561,12 @@ class ServiceManager(QtGui.QWidget):
|
||||
zip.write(audio_from, audio_to.encode(u'utf-8'))
|
||||
except IOError:
|
||||
log.exception(u'Failed to save service to disk: %s', temp_file_name)
|
||||
# Add this line in after the release to notify the user that saving
|
||||
# their file failed. Commented out due to string freeze.
|
||||
#Receiver.send_message(u'openlp_error_message', {
|
||||
# u'title': translate(u'OpenLP.ServiceManager',
|
||||
# u'Error Saving File'),
|
||||
# u'message': translate(u'OpenLP.ServiceManager',
|
||||
# u'There was an error saving your file.')
|
||||
#})
|
||||
Receiver.send_message(u'openlp_error_message', {
|
||||
u'title': translate(u'OpenLP.ServiceManager',
|
||||
u'Error Saving File'),
|
||||
u'message': translate(u'OpenLP.ServiceManager',
|
||||
u'There was an error saving your file.')
|
||||
})
|
||||
success = False
|
||||
finally:
|
||||
if zip:
|
||||
@ -1318,15 +1316,15 @@ class ServiceManager(QtGui.QWidget):
|
||||
|
||||
def findServiceItem(self):
|
||||
"""
|
||||
Finds the selected ServiceItem in the list and returns the position of
|
||||
the serviceitem and its selected child item. For example, if the third
|
||||
child item (in the Slidecontroller known as slide) in the second service
|
||||
item is selected this will return::
|
||||
Finds the first selected ServiceItem in the list and returns the
|
||||
position of the serviceitem and its selected child item. For example,
|
||||
if the third child item (in the Slidecontroller known as slide) in the
|
||||
second service item is selected this will return::
|
||||
|
||||
(1, 2)
|
||||
"""
|
||||
items = self.serviceManagerList.selectedItems()
|
||||
serviceItem = 0
|
||||
serviceItem = -1
|
||||
serviceItemChild = -1
|
||||
for item in items:
|
||||
parentitem = item.parent()
|
||||
@ -1335,8 +1333,10 @@ class ServiceManager(QtGui.QWidget):
|
||||
else:
|
||||
serviceItem = parentitem.data(0, QtCore.Qt.UserRole).toInt()[0]
|
||||
serviceItemChild = item.data(0, QtCore.Qt.UserRole).toInt()[0]
|
||||
# Adjust for zero based arrays.
|
||||
serviceItem -= 1
|
||||
# Adjust for zero based arrays.
|
||||
serviceItem -= 1
|
||||
# Only process the first item on the list for this method.
|
||||
break
|
||||
return serviceItem, serviceItemChild
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
|
@ -92,7 +92,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
"""
|
||||
Process the form saving the settings
|
||||
"""
|
||||
for tabIndex in range(0, self.stackedLayout.count()):
|
||||
for tabIndex in range(self.stackedLayout.count()):
|
||||
self.stackedLayout.widget(tabIndex).save()
|
||||
# Must go after all settings are save
|
||||
Receiver.send_message(u'config_updated')
|
||||
@ -102,7 +102,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
"""
|
||||
Process the form saving the settings
|
||||
"""
|
||||
for tabIndex in range(0, self.stackedLayout.count()):
|
||||
for tabIndex in range(self.stackedLayout.count()):
|
||||
self.stackedLayout.widget(tabIndex).cancel()
|
||||
return QtGui.QDialog.reject(self)
|
||||
|
||||
|
@ -373,7 +373,7 @@ class SlideController(Controller):
|
||||
u'text': translate('OpenLP.SlideController', 'Go to "Ending"')},
|
||||
{u'key': u'O', u'configurable': True,
|
||||
u'text': translate('OpenLP.SlideController', 'Go to "Other"')}]
|
||||
shortcuts += [{u'key': unicode(number)} for number in range(0, 10)]
|
||||
shortcuts += [{u'key': unicode(number)} for number in range(10)]
|
||||
self.previewListWidget.addActions([create_action(self,
|
||||
u'shortcutAction_%s' % s[u'key'], text=s.get(u'text'),
|
||||
shortcuts=[QtGui.QKeySequence(s[u'key'])],
|
||||
|
@ -53,6 +53,7 @@ class WizardStrings(object):
|
||||
OL = u'OpenLyrics'
|
||||
OS = u'OpenSong'
|
||||
OSIS = u'OSIS'
|
||||
PS = u'PowerSong 1.0'
|
||||
SB = u'SongBeamer'
|
||||
SoF = u'Songs of Fellowship'
|
||||
SSP = u'SongShow Plus'
|
||||
|
@ -330,13 +330,7 @@ class BibleManager(object):
|
||||
'Import Wizard to install one or more Bibles.')
|
||||
})
|
||||
return None
|
||||
language_selection = self.get_meta_data(bible, u'book_name_language')
|
||||
if language_selection:
|
||||
language_selection = int(language_selection.value)
|
||||
if language_selection is None or language_selection == -1:
|
||||
language_selection = QtCore.QSettings().value(
|
||||
self.settingsSection + u'/bookname language',
|
||||
QtCore.QVariant(0)).toInt()[0]
|
||||
language_selection = self.get_language_selection(bible)
|
||||
reflist = parse_reference(versetext, self.db_cache[bible],
|
||||
language_selection, book_ref_id)
|
||||
if reflist:
|
||||
@ -378,12 +372,16 @@ class BibleManager(object):
|
||||
"""
|
||||
log.debug(u'BibleManager.get_language_selection("%s")', bible)
|
||||
language_selection = self.get_meta_data(bible, u'book_name_language')
|
||||
if language_selection and language_selection.value != u'None':
|
||||
return int(language_selection.value)
|
||||
if language_selection is None or language_selection.value == u'None':
|
||||
return QtCore.QSettings().value(
|
||||
if language_selection:
|
||||
try:
|
||||
language_selection = int(language_selection.value)
|
||||
except (ValueError, TypeError):
|
||||
language_selection = LanguageSelection.Application
|
||||
if language_selection is None or language_selection == -1:
|
||||
language_selection = QtCore.QSettings().value(
|
||||
self.settingsSection + u'/bookname language',
|
||||
QtCore.QVariant(0)).toInt()[0]
|
||||
return language_selection
|
||||
|
||||
def verse_search(self, bible, second_bible, text):
|
||||
"""
|
||||
|
@ -843,10 +843,11 @@ class BibleMediaItem(MediaManagerItem):
|
||||
items = []
|
||||
language_selection = self.plugin.manager.get_language_selection(bible)
|
||||
for count, verse in enumerate(search_results):
|
||||
book = None
|
||||
if language_selection == LanguageSelection.Bible:
|
||||
book = verse.book.name
|
||||
elif language_selection == LanguageSelection.Application:
|
||||
book_names = BibleStrings().Booknames
|
||||
book_names = BibleStrings().BookNames
|
||||
data = BiblesResourcesDB.get_book_by_id(
|
||||
verse.book.book_reference_id)
|
||||
book = unicode(book_names[data[u'abbreviation']])
|
||||
|
@ -127,7 +127,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
sxml.new_document()
|
||||
sxml.add_lyrics_to_song()
|
||||
count = 1
|
||||
for i in range(0, self.slideListView.count()):
|
||||
for i in range(self.slideListView.count()):
|
||||
sxml.add_verse_to_lyrics(u'custom', unicode(count),
|
||||
unicode(self.slideListView.item(i).text()))
|
||||
count += 1
|
||||
@ -170,7 +170,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
Edits all slides.
|
||||
"""
|
||||
slide_list = u''
|
||||
for row in range(0, self.slideListView.count()):
|
||||
for row in range(self.slideListView.count()):
|
||||
item = self.slideListView.item(row)
|
||||
slide_list += item.text()
|
||||
if row != self.slideListView.count() - 1:
|
||||
@ -206,7 +206,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
old_row = self.slideListView.currentRow()
|
||||
# Create a list with all (old/unedited) slides.
|
||||
old_slides = [self.slideListView.item(row).text() for row in \
|
||||
range(0, self.slideListView.count())]
|
||||
range(self.slideListView.count())]
|
||||
self.slideListView.clear()
|
||||
old_slides.pop(old_row)
|
||||
# Insert all slides to make the old_slides list complete.
|
||||
|
@ -152,7 +152,7 @@ class PowerpointDocument(PresentationDocument):
|
||||
log.debug(u'create_thumbnails')
|
||||
if self.check_thumbnails():
|
||||
return
|
||||
for num in range(0, self.presentation.Slides.Count):
|
||||
for num in range(self.presentation.Slides.Count):
|
||||
self.presentation.Slides(num + 1).Export(os.path.join(
|
||||
self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)),
|
||||
'png', 320, 240)
|
||||
|
9408
openlp/plugins/remotes/html/jquery.js
vendored
@ -336,7 +336,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
Tag the Song List rows based on the verse list
|
||||
"""
|
||||
row_label = []
|
||||
for row in range(0, self.verseListWidget.rowCount()):
|
||||
for row in range(self.verseListWidget.rowCount()):
|
||||
item = self.verseListWidget.item(row, 0)
|
||||
verse_def = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
verse_tag = VerseType.translated_tag(verse_def[0])
|
||||
@ -494,7 +494,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
if len(tempText.split(u'\n')) != len(after_text.split(u'\n')):
|
||||
tempList = {}
|
||||
tempId = {}
|
||||
for row in range(0, self.verseListWidget.rowCount()):
|
||||
for row in range(self.verseListWidget.rowCount()):
|
||||
tempList[row] = self.verseListWidget.item(row, 0)\
|
||||
.text()
|
||||
tempId[row] = self.verseListWidget.item(row, 0)\
|
||||
@ -511,7 +511,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
def onVerseEditAllButtonClicked(self):
|
||||
verse_list = u''
|
||||
if self.verseListWidget.rowCount() > 0:
|
||||
for row in range(0, self.verseListWidget.rowCount()):
|
||||
for row in range(self.verseListWidget.rowCount()):
|
||||
item = self.verseListWidget.item(row, 0)
|
||||
field = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
verse_tag = VerseType.translated_name(field[0])
|
||||
@ -579,7 +579,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
verses = []
|
||||
verse_names = []
|
||||
order = self.__extractVerseOrder(text)
|
||||
for index in range(0, self.verseListWidget.rowCount()):
|
||||
for index in range(self.verseListWidget.rowCount()):
|
||||
verse = self.verseListWidget.item(index, 0)
|
||||
verse = unicode(verse.data(QtCore.Qt.UserRole).toString())
|
||||
if verse not in verse_names:
|
||||
@ -620,7 +620,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
verse_names = []
|
||||
order_names = unicode(verse_order).split()
|
||||
order = self.__extractVerseOrder(verse_order)
|
||||
for index in range(0, verse_count):
|
||||
for index in range(verse_count):
|
||||
verse = self.verseListWidget.item(index, 0)
|
||||
verse = unicode(verse.data(QtCore.Qt.UserRole).toString())
|
||||
if verse not in verse_names:
|
||||
@ -920,7 +920,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
try:
|
||||
sxml = SongXML()
|
||||
multiple = []
|
||||
for i in range(0, self.verseListWidget.rowCount()):
|
||||
for i in range(self.verseListWidget.rowCount()):
|
||||
item = self.verseListWidget.item(i, 0)
|
||||
verseId = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
verse_tag = verseId[0]
|
||||
|
@ -171,6 +171,12 @@ class SongImportForm(OpenLPWizard):
|
||||
QtCore.QObject.connect(self.foilPresenterRemoveButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onFoilPresenterRemoveButtonClicked)
|
||||
QtCore.QObject.connect(self.powerSongAddButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onPowerSongAddButtonClicked)
|
||||
QtCore.QObject.connect(self.powerSongRemoveButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onPowerSongRemoveButtonClicked)
|
||||
|
||||
def addCustomPages(self):
|
||||
"""
|
||||
@ -217,6 +223,8 @@ class SongImportForm(OpenLPWizard):
|
||||
self.addFileSelectItem(u'foilPresenter')
|
||||
# Open Song
|
||||
self.addFileSelectItem(u'openSong', u'OpenSong')
|
||||
# PowerSong
|
||||
self.addFileSelectItem(u'powerSong')
|
||||
# SongBeamer
|
||||
self.addFileSelectItem(u'songBeamer')
|
||||
# Song Show Plus
|
||||
@ -264,6 +272,8 @@ class SongImportForm(OpenLPWizard):
|
||||
self.formatComboBox.setItemText(
|
||||
SongFormat.FoilPresenter, WizardStrings.FP)
|
||||
self.formatComboBox.setItemText(SongFormat.OpenSong, WizardStrings.OS)
|
||||
self.formatComboBox.setItemText(
|
||||
SongFormat.PowerSong, WizardStrings.PS)
|
||||
self.formatComboBox.setItemText(
|
||||
SongFormat.SongBeamer, WizardStrings.SB)
|
||||
self.formatComboBox.setItemText(
|
||||
@ -305,6 +315,10 @@ class SongImportForm(OpenLPWizard):
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.dreamBeamRemoveButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
|
||||
self.powerSongAddButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.powerSongRemoveButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
|
||||
self.songsOfFellowshipAddButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.songsOfFellowshipRemoveButton.setText(
|
||||
@ -417,6 +431,12 @@ class SongImportForm(OpenLPWizard):
|
||||
WizardStrings.YouSpecifyFile % WizardStrings.DB)
|
||||
self.dreamBeamAddButton.setFocus()
|
||||
return False
|
||||
elif source_format == SongFormat.PowerSong:
|
||||
if self.powerSongFileListWidget.count() == 0:
|
||||
critical_error_message_box(UiStrings().NFSp,
|
||||
WizardStrings.YouSpecifyFile % WizardStrings.PS)
|
||||
self.powerSongAddButton.setFocus()
|
||||
return False
|
||||
elif source_format == SongFormat.SongsOfFellowship:
|
||||
if self.songsOfFellowshipFileListWidget.count() == 0:
|
||||
critical_error_message_box(UiStrings().NFSp,
|
||||
@ -600,6 +620,22 @@ class SongImportForm(OpenLPWizard):
|
||||
"""
|
||||
self.removeSelectedItems(self.dreamBeamFileListWidget)
|
||||
|
||||
def onPowerSongAddButtonClicked(self):
|
||||
"""
|
||||
Get PowerSong song database files
|
||||
"""
|
||||
self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.PS,
|
||||
self.powerSongFileListWidget, u'%s (*.song)'
|
||||
% translate('SongsPlugin.ImportWizardForm',
|
||||
'PowerSong 1.0 Song Files')
|
||||
)
|
||||
|
||||
def onPowerSongRemoveButtonClicked(self):
|
||||
"""
|
||||
Remove selected PowerSong files from the import list
|
||||
"""
|
||||
self.removeSelectedItems(self.powerSongFileListWidget)
|
||||
|
||||
def onSongsOfFellowshipAddButtonClicked(self):
|
||||
"""
|
||||
Get Songs of Fellowship song database files
|
||||
@ -717,6 +753,7 @@ class SongImportForm(OpenLPWizard):
|
||||
self.wordsOfWorshipFileListWidget.clear()
|
||||
self.ccliFileListWidget.clear()
|
||||
self.dreamBeamFileListWidget.clear()
|
||||
self.powerSongFileListWidget.clear()
|
||||
self.songsOfFellowshipFileListWidget.clear()
|
||||
self.genericFileListWidget.clear()
|
||||
self.easySlidesFilenameEdit.setText(u'')
|
||||
@ -784,6 +821,12 @@ class SongImportForm(OpenLPWizard):
|
||||
filenames=self.getListOfFiles(
|
||||
self.dreamBeamFileListWidget)
|
||||
)
|
||||
elif source_format == SongFormat.PowerSong:
|
||||
# Import PowerSong songs
|
||||
importer = self.plugin.importSongs(SongFormat.PowerSong,
|
||||
filenames=self.getListOfFiles(
|
||||
self.powerSongFileListWidget)
|
||||
)
|
||||
elif source_format == SongFormat.SongsOfFellowship:
|
||||
# Import a Songs of Fellowship RTF file
|
||||
importer = self.plugin.importSongs(SongFormat.SongsOfFellowship,
|
||||
|
@ -185,7 +185,7 @@ class CCLIFileImport(SongImport):
|
||||
check_first_verse_line = False
|
||||
field_list = song_fields.split(u'/t')
|
||||
words_list = song_words.split(u'/t')
|
||||
for counter in range(0, len(field_list)):
|
||||
for counter in range(len(field_list)):
|
||||
if field_list[counter].startswith(u'Ver'):
|
||||
verse_type = VerseType.Tags[VerseType.Verse]
|
||||
elif field_list[counter].startswith(u'Ch'):
|
||||
|
@ -36,6 +36,7 @@ from openlyricsimport import OpenLyricsImport
|
||||
from wowimport import WowImport
|
||||
from cclifileimport import CCLIFileImport
|
||||
from dreambeamimport import DreamBeamImport
|
||||
from powersongimport import PowerSongImport
|
||||
from ewimport import EasyWorshipSongImport
|
||||
from songbeamerimport import SongBeamerImport
|
||||
from songshowplusimport import SongShowPlusImport
|
||||
@ -79,16 +80,17 @@ class SongFormat(object):
|
||||
EasyWorship = 7
|
||||
FoilPresenter = 8
|
||||
OpenSong = 9
|
||||
SongBeamer = 10
|
||||
SongShowPlus = 11
|
||||
SongsOfFellowship = 12
|
||||
WordsOfWorship = 13
|
||||
#CSV = 14
|
||||
PowerSong = 10
|
||||
SongBeamer = 11
|
||||
SongShowPlus = 12
|
||||
SongsOfFellowship = 13
|
||||
WordsOfWorship = 14
|
||||
#CSV = 15
|
||||
|
||||
@staticmethod
|
||||
def get_class(format):
|
||||
"""
|
||||
Return the appropriate imeplementation class.
|
||||
Return the appropriate implementation class.
|
||||
|
||||
``format``
|
||||
The song format.
|
||||
@ -111,6 +113,8 @@ class SongFormat(object):
|
||||
return CCLIFileImport
|
||||
elif format == SongFormat.DreamBeam:
|
||||
return DreamBeamImport
|
||||
elif format == SongFormat.PowerSong:
|
||||
return PowerSongImport
|
||||
elif format == SongFormat.EasySlides:
|
||||
return EasySlidesImport
|
||||
elif format == SongFormat.EasyWorship:
|
||||
@ -139,6 +143,7 @@ class SongFormat(object):
|
||||
SongFormat.EasyWorship,
|
||||
SongFormat.FoilPresenter,
|
||||
SongFormat.OpenSong,
|
||||
SongFormat.PowerSong,
|
||||
SongFormat.SongBeamer,
|
||||
SongFormat.SongShowPlus,
|
||||
SongFormat.SongsOfFellowship,
|
||||
|
195
openlp/plugins/songs/lib/powersongimport.py
Normal file
@ -0,0 +1,195 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2012 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`powersongimport` module provides the functionality for importing
|
||||
PowerSong songs into the OpenLP database.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.plugins.songs.lib.songimport import SongImport
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class PowerSongImport(SongImport):
|
||||
"""
|
||||
The :class:`PowerSongImport` class provides the ability to import song files
|
||||
from PowerSong.
|
||||
|
||||
**PowerSong 1.0 Song File Format:**
|
||||
|
||||
The file has a number of label-field (think key-value) pairs.
|
||||
|
||||
Label and Field strings:
|
||||
|
||||
* Every label and field is a variable length string preceded by an
|
||||
integer specifying it's byte length.
|
||||
* Integer is 32-bit but is encoded in 7-bit format to save space. Thus
|
||||
if length will fit in 7 bits (ie <= 127) it takes up only one byte.
|
||||
|
||||
Metadata fields:
|
||||
|
||||
* Every PowerSong file has a TITLE field.
|
||||
* There is zero or more AUTHOR fields.
|
||||
* There is always a COPYRIGHTLINE label, but its field may be empty.
|
||||
This field may also contain a CCLI number: e.g. "CCLI 176263".
|
||||
|
||||
Lyrics fields:
|
||||
|
||||
* Each verse is contained in a PART field.
|
||||
* Lines have Windows line endings ``CRLF`` (0x0d, 0x0a).
|
||||
* There is no concept of verse types.
|
||||
|
||||
Valid extensions for a PowerSong song file are:
|
||||
|
||||
* .song
|
||||
"""
|
||||
|
||||
def doImport(self):
|
||||
"""
|
||||
Receive a list of files to import.
|
||||
"""
|
||||
if not isinstance(self.importSource, list):
|
||||
self.logError(unicode(translate('SongsPlugin.PowerSongImport',
|
||||
'No files to import.')))
|
||||
return
|
||||
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||
for file in self.importSource:
|
||||
if self.stopImportFlag:
|
||||
return
|
||||
self.setDefaults()
|
||||
parse_error = False
|
||||
with open(file, 'rb') as song_data:
|
||||
while True:
|
||||
try:
|
||||
label = self._readString(song_data)
|
||||
if not label:
|
||||
break
|
||||
field = self._readString(song_data)
|
||||
except ValueError:
|
||||
parse_error = True
|
||||
self.logError(file, unicode(
|
||||
translate('SongsPlugin.PowerSongImport',
|
||||
'Invalid PowerSong file. Unexpected byte value.')))
|
||||
break
|
||||
else:
|
||||
if label == u'TITLE':
|
||||
self.title = field.replace(u'\n', u' ')
|
||||
elif label == u'AUTHOR':
|
||||
self.parseAuthor(field)
|
||||
elif label == u'COPYRIGHTLINE':
|
||||
found_copyright = True
|
||||
self._parseCopyrightCCLI(field)
|
||||
elif label == u'PART':
|
||||
self.addVerse(field)
|
||||
if parse_error:
|
||||
continue
|
||||
# Check that file had TITLE field
|
||||
if not self.title:
|
||||
self.logError(file, unicode(
|
||||
translate('SongsPlugin.PowerSongImport',
|
||||
'Invalid PowerSong file. Missing "TITLE" header.')))
|
||||
continue
|
||||
# Check that file had COPYRIGHTLINE label
|
||||
if not found_copyright:
|
||||
self.logError(file, unicode(
|
||||
translate('SongsPlugin.PowerSongImport',
|
||||
'"%s" Invalid PowerSong file. Missing "COPYRIGHTLINE" '
|
||||
'header.' % self.title)))
|
||||
continue
|
||||
# Check that file had at least one verse
|
||||
if not self.verses:
|
||||
self.logError(file, unicode(
|
||||
translate('SongsPlugin.PowerSongImport',
|
||||
'"%s" Verses not found. Missing "PART" header.'
|
||||
% self.title)))
|
||||
continue
|
||||
if not self.finish():
|
||||
self.logError(file)
|
||||
|
||||
def _readString(self, file_object):
|
||||
"""
|
||||
Reads in next variable-length string.
|
||||
"""
|
||||
string_len = self._read7BitEncodedInteger(file_object)
|
||||
return unicode(file_object.read(string_len), u'utf-8', u'ignore')
|
||||
|
||||
def _read7BitEncodedInteger(self, file_object):
|
||||
"""
|
||||
Reads in a 32-bit integer in compressed 7-bit format.
|
||||
|
||||
Accomplished by reading the integer 7 bits at a time. The high bit
|
||||
of the byte when set means to continue reading more bytes.
|
||||
If the integer will fit in 7 bits (ie <= 127), it only takes up one
|
||||
byte. Otherwise, it may take up to 5 bytes.
|
||||
|
||||
Reference: .NET method System.IO.BinaryReader.Read7BitEncodedInt
|
||||
"""
|
||||
val = 0
|
||||
shift = 0
|
||||
i = 0
|
||||
while True:
|
||||
# Check for corrupted stream (since max 5 bytes per 32-bit integer)
|
||||
if i == 5:
|
||||
raise ValueError
|
||||
byte = self._readByte(file_object)
|
||||
# Strip high bit and shift left
|
||||
val += (byte & 0x7f) << shift
|
||||
shift += 7
|
||||
high_bit_set = byte & 0x80
|
||||
if not high_bit_set:
|
||||
break
|
||||
i += 1
|
||||
return val
|
||||
|
||||
def _readByte(self, file_object):
|
||||
"""
|
||||
Reads in next byte as an unsigned integer
|
||||
|
||||
Note: returns 0 at end of file.
|
||||
"""
|
||||
byte_str = file_object.read(1)
|
||||
# If read result is empty, then reached end of file
|
||||
if not byte_str:
|
||||
return 0
|
||||
else:
|
||||
return ord(byte_str)
|
||||
|
||||
def _parseCopyrightCCLI(self, field):
|
||||
"""
|
||||
Look for CCLI song number, and get copyright
|
||||
"""
|
||||
copyright, sep, ccli_no = field.rpartition(u'CCLI')
|
||||
if not sep:
|
||||
copyright = ccli_no
|
||||
ccli_no = u''
|
||||
if copyright:
|
||||
self.addCopyright(copyright.rstrip(u'\n').replace(u'\n', u' '))
|
||||
if ccli_no:
|
||||
ccli_no = ccli_no.strip(u' :')
|
||||
if ccli_no.isdigit():
|
||||
self.ccliNumber = ccli_no
|
@ -107,11 +107,11 @@ class SongImport(QtCore.QObject):
|
||||
|
||||
``filepath``
|
||||
This should be the file path if ``self.importSource`` is a list
|
||||
with different files. If it is not a list, but a single file (for
|
||||
with different files. If it is not a list, but a single file (for
|
||||
instance a database), then this should be the song's title.
|
||||
|
||||
``reason``
|
||||
The reason, why the import failed. The string should be as
|
||||
The reason why the import failed. The string should be as
|
||||
informative as possible.
|
||||
"""
|
||||
self.setDefaults()
|
||||
|
@ -71,7 +71,7 @@ class WowImport(SongImport):
|
||||
* ``SOH`` (0x01) - Chorus
|
||||
* ``STX`` (0x02) - Bridge
|
||||
|
||||
Blocks are seperated by two bytes. The first byte is 0x01, and the
|
||||
Blocks are separated by two bytes. The first byte is 0x01, and the
|
||||
second byte is 0x80.
|
||||
|
||||
Lines:
|
||||
@ -126,7 +126,7 @@ class WowImport(SongImport):
|
||||
('Invalid Words of Worship song file. Missing '
|
||||
'"CSongDoc::CBlock" string.'))))
|
||||
continue
|
||||
# Seek to the beging of the first block
|
||||
# Seek to the beginning of the first block
|
||||
song_data.seek(82)
|
||||
for block in range(no_of_blocks):
|
||||
self.linesToRead = ord(song_data.read(4)[:1])
|
||||
@ -140,7 +140,7 @@ class WowImport(SongImport):
|
||||
block_text += self.lineText
|
||||
self.linesToRead -= 1
|
||||
block_type = BLOCK_TYPES[ord(song_data.read(4)[:1])]
|
||||
# Blocks are seperated by 2 bytes, skip them, but not if
|
||||
# Blocks are separated by 2 bytes, skip them, but not if
|
||||
# this is the last block!
|
||||
if block + 1 < no_of_blocks:
|
||||
song_data.seek(2, os.SEEK_CUR)
|
||||
|
@ -33,7 +33,7 @@ The basic XML for storing the lyrics in the song database looks like this::
|
||||
<song version="1.0">
|
||||
<lyrics>
|
||||
<verse type="c" label="1" lang="en">
|
||||
<![CDATA[Chorus virtual slide 1[---]Chorus virtual slide 2]]>
|
||||
<![CDATA[Chorus optional split 1[---]Chorus optional split 2]]>
|
||||
</verse>
|
||||
</lyrics>
|
||||
</song>
|
||||
@ -135,7 +135,7 @@ class SongXML(object):
|
||||
The returned list has the following format::
|
||||
|
||||
[[{'type': 'v', 'label': '1'},
|
||||
u"virtual slide 1[---]virtual slide 2"],
|
||||
u"optional slide split 1[---]optional slide split 2"],
|
||||
[{'lang': 'en', 'type': 'c', 'label': '1'}, u"English chorus"]]
|
||||
"""
|
||||
self.song_xml = None
|
||||
@ -317,9 +317,7 @@ class OpenLyrics(object):
|
||||
tags_element = None
|
||||
match = re.search(u'\{/?\w+\}', song.lyrics, re.UNICODE)
|
||||
if match:
|
||||
# Reset available tags.
|
||||
FormattingTags.reset_html_tags()
|
||||
# Named 'formatting' - 'format' is built-in fuction in Python.
|
||||
# Named 'format_' - 'format' is built-in fuction in Python.
|
||||
format_ = etree.SubElement(song_xml, u'format')
|
||||
tags_element = etree.SubElement(format_, u'tags')
|
||||
tags_element.set(u'application', u'OpenLP')
|
||||
@ -334,18 +332,59 @@ class OpenLyrics(object):
|
||||
self._add_text_to_element(u'verse', lyrics, None, verse_def)
|
||||
if u'lang' in verse[0]:
|
||||
verse_element.set(u'lang', verse[0][u'lang'])
|
||||
# Create a list with all "virtual" verses.
|
||||
virtual_verses = cgi.escape(verse[1])
|
||||
virtual_verses = virtual_verses.split(u'[---]')
|
||||
for index, virtual_verse in enumerate(virtual_verses):
|
||||
# Create a list with all "optional" verses.
|
||||
optional_verses = cgi.escape(verse[1])
|
||||
optional_verses = optional_verses.split(u'\n[---]\n')
|
||||
start_tags = u''
|
||||
end_tags = u''
|
||||
for index, optional_verse in enumerate(optional_verses):
|
||||
# Fix up missing end and start tags such as {r} or {/r}.
|
||||
optional_verse = start_tags + optional_verse
|
||||
start_tags, end_tags = self._get_missing_tags(optional_verse)
|
||||
optional_verse += end_tags
|
||||
# Add formatting tags to text
|
||||
lines_element = self._add_text_with_tags_to_lines(verse_element,
|
||||
virtual_verse, tags_element)
|
||||
optional_verse, tags_element)
|
||||
# Do not add the break attribute to the last lines element.
|
||||
if index < len(virtual_verses) - 1:
|
||||
if index < len(optional_verses) - 1:
|
||||
lines_element.set(u'break', u'optional')
|
||||
return self._extract_xml(song_xml)
|
||||
|
||||
def _get_missing_tags(self, text):
|
||||
"""
|
||||
Tests the given text for not closed formatting tags and returns a tuple
|
||||
consisting of two unicode strings::
|
||||
|
||||
(u'{st}{r}', u'{/r}{/st}')
|
||||
|
||||
The first unicode string are the start tags (for the next slide). The
|
||||
second unicode string are the end tags.
|
||||
|
||||
``text``
|
||||
The text to test. The text must **not** contain html tags, only
|
||||
OpenLP formatting tags are allowed::
|
||||
|
||||
{st}{r}Text text text
|
||||
"""
|
||||
tags = []
|
||||
for tag in FormattingTags.get_html_tags():
|
||||
if tag[u'start tag'] == u'{br}':
|
||||
continue
|
||||
if text.count(tag[u'start tag']) != text.count(tag[u'end tag']):
|
||||
tags.append((text.find(tag[u'start tag']),
|
||||
tag[u'start tag'], tag[u'end tag']))
|
||||
# Sort the lists, so that the tags which were opened first on the first
|
||||
# slide (the text we are checking) will be opened first on the next
|
||||
# slide as well.
|
||||
tags.sort(key=lambda tag: tag[0])
|
||||
end_tags = []
|
||||
start_tags = []
|
||||
for tag in tags:
|
||||
start_tags.append(tag[1])
|
||||
end_tags.append(tag[2])
|
||||
end_tags.reverse()
|
||||
return u''.join(start_tags), u''.join(end_tags)
|
||||
|
||||
def xml_to_song(self, xml, parse_and_temporary_save=False):
|
||||
"""
|
||||
Create and save a song from OpenLyrics format xml to the database. Since
|
||||
@ -572,7 +611,8 @@ class OpenLyrics(object):
|
||||
for tag in FormattingTags.get_html_tags()]
|
||||
new_tags = [tag for tag in found_tags
|
||||
if tag[u'start tag'] not in existing_tag_ids]
|
||||
FormattingTags.add_html_tags(new_tags, True)
|
||||
FormattingTags.add_html_tags(new_tags)
|
||||
FormattingTags.save_html_tags()
|
||||
|
||||
def _process_lines_mixed_content(self, element, newlines=True):
|
||||
"""
|
||||
|
@ -1,125 +0,0 @@
|
||||
<?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>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtension</key>
|
||||
<array>
|
||||
<string>osz</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array>
|
||||
<string>openlp-logo-with-text.icns</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>OpenLP Service</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>org.openlp.osz</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtension</key>
|
||||
<array>
|
||||
<string>otz</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array>
|
||||
<string>openlp-logo-with-text.icns</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>OpenLP Theme</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>org.openlp.otz</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.openlp.osz</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>OpenLP Service</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
<string>public.content</string>
|
||||
</array>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>osz</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>application/x-openlp-service</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.openlp.otz</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>OpenLP Theme</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
<string>public.content</string>
|
||||
</array>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>otz</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>application/x-openlp-theme</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
<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</key>
|
||||
<string>%(openlp_appname)s %(openlp_version)s</string>
|
||||
<key>LSHasLocalizedDisplayName</key>
|
||||
<false/>
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<false/>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>LSBackgroundOnly</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
@ -1,28 +0,0 @@
|
||||
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
|
||||
|
@ -1,74 +0,0 @@
|
||||
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 ".background: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
|
@ -1,77 +0,0 @@
|
||||
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 ".background: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 1
|
||||
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 ".background" 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}
|
||||
if exists file ".VolumeIcon.icns" then
|
||||
set position of item ".VolumeIcon.icns" of container window to {500, 500}
|
||||
end if
|
||||
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
|
@ -1,40 +0,0 @@
|
||||
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
|
@ -1,41 +0,0 @@
|
||||
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
|
@ -1,426 +0,0 @@
|
||||
#!/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, Matthias Hub, Meinert Jordan, Armin Köhler, #
|
||||
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, 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/2.7
|
||||
This build script only works with Python 2.6/2.7
|
||||
|
||||
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 os
|
||||
import ConfigParser
|
||||
import logging
|
||||
import optparse
|
||||
import sys
|
||||
import glob
|
||||
import platform
|
||||
import re
|
||||
import subprocess as subp
|
||||
|
||||
from shutil import copy
|
||||
|
||||
# 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('arch -i386 %s %s/pyinstaller.py openlp.spec' \
|
||||
% ( sys.executable,
|
||||
settings['pyinstaller_basedir']) )
|
||||
if (result != 0):
|
||||
logging.error('[%s] The pyinstaller build reported an error, 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] removing the presentations plugin...', script_name)
|
||||
result = os.system('rm -rf \
|
||||
%(application_directory)s/Contents/MacOS/plugins/presentations' \
|
||||
% { 'application_directory' : app_dir })
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not remove presentations 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)
|
||||
|
||||
logging.info('[%s] copying the translations...', script_name)
|
||||
os.makedirs(app_dir + '/Contents/MacOS/i18n')
|
||||
for ts_file in glob.glob(os.path.join(settings['openlp_basedir']
|
||||
+ '/resources/i18n/', '*ts')):
|
||||
result = os.system('lconvert -i %(ts_file)s \
|
||||
-o %(target_directory)s/Contents/MacOS/i18n/%(base)s.qm' \
|
||||
% { 'ts_file' : ts_file, 'target_directory' : app_dir,
|
||||
'base': os.path.splitext(os.path.basename(ts_file))[0] })
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not copy the translations, dmg \
|
||||
creation failed!', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
# Backported from windows build script.
|
||||
logging.info('[%s] copying the media player...', script_name)
|
||||
os.makedirs(os.path.join(app_dir, 'Contents/MacOS/core/ui/media'))
|
||||
source = os.path.join(settings['openlp_basedir'], u'openlp', u'core', u'ui', u'media')
|
||||
dest = os.path.join(app_dir, u'Contents/MacOS/core/ui/media')
|
||||
for root, dirs, files in os.walk(source):
|
||||
for filename in files:
|
||||
print filename
|
||||
if not filename.endswith(u'.pyc'):
|
||||
dest_path = os.path.join(dest, root[len(source)+1:])
|
||||
if not os.path.exists(dest_path):
|
||||
os.makedirs(dest_path)
|
||||
copy(os.path.join(root, filename),
|
||||
os.path.join(dest_path, filename))
|
||||
|
||||
|
||||
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 + '/.background/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
|
||||
|
||||
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_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") or version.startswith("10.7"):
|
||||
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 get_version.py > .version')
|
||||
|
||||
# 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_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)
|
@ -1,227 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
|
||||
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
||||
# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, 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 #
|
||||
###############################################################################
|
||||
|
||||
# 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
|
||||
|
@ -1,61 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
|
||||
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
||||
# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode #
|
||||
# Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
import sys
|
||||
import os
|
||||
from bzrlib.branch import Branch
|
||||
|
||||
def get_version(path):
|
||||
b = Branch.open_containing(path)[0]
|
||||
b.lock_read()
|
||||
result = '0.0.0'
|
||||
try:
|
||||
# Get the branch's latest revision number.
|
||||
revno = b.revno()
|
||||
# Convert said revision number into a bzr revision id.
|
||||
revision_id = b.dotted_revno_to_revision_id((revno,))
|
||||
# Get a dict of tags, with the revision id as the key.
|
||||
tags = b.tags.get_reverse_tag_dict()
|
||||
# Check if the latest
|
||||
if revision_id in tags:
|
||||
result = tags[revision_id][0]
|
||||
else:
|
||||
result = '%s-bzr%s' % (sorted(b.tags.get_tag_dict().keys())[-1], revno)
|
||||
finally:
|
||||
b.unlock()
|
||||
return result
|
||||
|
||||
def get_path():
|
||||
if len(sys.argv) > 1:
|
||||
return os.path.abspath(sys.argv[1])
|
||||
else:
|
||||
return os.path.abspath('.')
|
||||
|
||||
if __name__ == u'__main__':
|
||||
path = get_path()
|
||||
print get_version(path)
|
||||
|
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 56 KiB |
@ -1,10 +0,0 @@
|
||||
[openlp]
|
||||
openlp_appname = OpenLP
|
||||
openlp_dmgname = OpenLP-1.9.6-bzrXXXX
|
||||
openlp_version = XXXX
|
||||
openlp_basedir = /Users/openlp/repo/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
|
@ -1,24 +0,0 @@
|
||||
# -*- mode: python -*-
|
||||
a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(CONFIGDIR,'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,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
@ -1,184 +0,0 @@
|
||||
; Script generated by the Inno Setup Script Wizard.
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define AppName "OpenLP"
|
||||
#define AppVerName "OpenLP 2.0"
|
||||
#define AppPublisher "OpenLP Developers"
|
||||
#define AppURL "http://openlp.org/"
|
||||
#define AppExeName "OpenLP.exe"
|
||||
|
||||
#define FileHandle FileOpen("..\..\dist\OpenLP\.version")
|
||||
#define FileLine FileRead(FileHandle)
|
||||
#define RealVersion FileLine
|
||||
#expr FileClose(FileHandle)
|
||||
|
||||
[Setup]
|
||||
; NOTE: The value of AppId uniquely identifies this application.
|
||||
; Do not use the same AppId value in installers for other applications.
|
||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||
AppID={{AA7699FA-B2D2-43F4-8A70-D497D03C9485}
|
||||
AppName={#AppName}
|
||||
AppVerName={#AppVerName}
|
||||
AppPublisher={#AppPublisher}
|
||||
AppPublisherURL={#AppURL}
|
||||
AppSupportURL={#AppURL}
|
||||
AppUpdatesURL={#AppURL}
|
||||
DefaultDirName={pf}\{#AppName}
|
||||
DefaultGroupName={#AppVerName}
|
||||
AllowNoIcons=true
|
||||
LicenseFile=LICENSE.txt
|
||||
OutputDir=..\..\dist
|
||||
OutputBaseFilename=OpenLP-{#RealVersion}-setup
|
||||
Compression=lzma/Max
|
||||
SolidCompression=true
|
||||
SetupIconFile=OpenLP.ico
|
||||
WizardImageFile=WizImageBig.bmp
|
||||
WizardSmallImageFile=WizImageSmall.bmp
|
||||
ChangesAssociations=true
|
||||
|
||||
[Languages]
|
||||
Name: english; MessagesFile: compiler:Default.isl
|
||||
Name: basque; MessagesFile: compiler:Languages\Basque.isl
|
||||
Name: brazilianportuguese; MessagesFile: compiler:Languages\BrazilianPortuguese.isl
|
||||
Name: catalan; MessagesFile: compiler:Languages\Catalan.isl
|
||||
Name: czech; MessagesFile: compiler:Languages\Czech.isl
|
||||
Name: danish; MessagesFile: compiler:Languages\Danish.isl
|
||||
Name: dutch; MessagesFile: compiler:Languages\Dutch.isl
|
||||
Name: finnish; MessagesFile: compiler:Languages\Finnish.isl
|
||||
Name: french; MessagesFile: compiler:Languages\French.isl
|
||||
Name: german; MessagesFile: compiler:Languages\German.isl
|
||||
Name: hebrew; MessagesFile: compiler:Languages\Hebrew.isl
|
||||
Name: hungarian; MessagesFile: compiler:Languages\Hungarian.isl
|
||||
Name: italian; MessagesFile: compiler:Languages\Italian.isl
|
||||
Name: japanese; MessagesFile: compiler:Languages\Japanese.isl
|
||||
Name: norwegian; MessagesFile: compiler:Languages\Norwegian.isl
|
||||
Name: polish; MessagesFile: compiler:Languages\Polish.isl
|
||||
Name: portuguese; MessagesFile: compiler:Languages\Portuguese.isl
|
||||
Name: russian; MessagesFile: compiler:Languages\Russian.isl
|
||||
Name: slovak; MessagesFile: compiler:Languages\Slovak.isl
|
||||
Name: slovenian; MessagesFile: compiler:Languages\Slovenian.isl
|
||||
Name: spanish; MessagesFile: compiler:Languages\Spanish.isl
|
||||
|
||||
[Tasks]
|
||||
Name: desktopicon; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm:AdditionalIcons}
|
||||
Name: quicklaunchicon; Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; OnlyBelowVersion: 0, 6.1
|
||||
|
||||
[Files]
|
||||
Source: ..\..\dist\OpenLP\*; DestDir: {app}; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; DLL used to check if the target program is running at install time
|
||||
Source: psvince.dll; flags: dontcopy
|
||||
; psvince is installed in {app} folder, so it will be loaded at
|
||||
; uninstall time to check if the target program is running
|
||||
Source: psvince.dll; DestDir: {app}
|
||||
|
||||
[Icons]
|
||||
Name: {group}\{#AppName}; Filename: {app}\{#AppExeName}
|
||||
Name: {group}\{#AppName} (Debug); Filename: {app}\{#AppExeName}; Parameters: -l debug
|
||||
Name: {group}\{#AppName} Help; Filename: {app}\{#AppName}.chm; Check: FileExists(ExpandConstant('{app}\{#AppName}.chm'))
|
||||
Name: {group}\{cm:ProgramOnTheWeb,{#AppName}}; Filename: {#AppURL}
|
||||
Name: {group}\{cm:UninstallProgram,{#AppName}}; Filename: {uninstallexe}
|
||||
Name: {commondesktop}\{#AppName}; Filename: {app}\{#AppExeName}; Tasks: desktopicon
|
||||
Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\{#AppName}; Filename: {app}\{#AppExeName}; Tasks: quicklaunchicon
|
||||
|
||||
[Run]
|
||||
Filename: {app}\{#AppExeName}; Description: {cm:LaunchProgram,{#AppName}}; Flags: nowait postinstall skipifsilent
|
||||
|
||||
[Registry]
|
||||
Root: HKCR; Subkey: .osz; ValueType: string; ValueName: ; ValueData: OpenLP; Flags: uninsdeletevalue
|
||||
Root: HKCR; Subkey: OpenLP; ValueType: string; ValueName: ; ValueData: OpenLP Service; Flags: uninsdeletekey
|
||||
Root: HKCR; Subkey: OpenLP\DefaultIcon; ValueType: string; ValueName: ; ValueData: {app}\OpenLP.exe,0
|
||||
Root: HKCR; Subkey: OpenLP\shell\open\command; ValueType: string; ValueName: ; ValueData: """{app}\OpenLP.exe"" ""%1"""
|
||||
|
||||
[UninstallDelete]
|
||||
; Remove support directory created when program is run:
|
||||
Type: filesandordirs; Name: {app}\support
|
||||
; Remove program directory if empty:
|
||||
Name: {app}; Type: dirifempty
|
||||
|
||||
[Code]
|
||||
// Function to call psvince.dll at install time
|
||||
function IsModuleLoadedInstall(modulename: AnsiString ): Boolean;
|
||||
external 'IsModuleLoaded@files:psvince.dll stdcall setuponly';
|
||||
|
||||
// Function to call psvince.dll at uninstall time
|
||||
function IsModuleLoadedUninstall(modulename: AnsiString ): Boolean;
|
||||
external 'IsModuleLoaded@{app}\psvince.dll stdcall uninstallonly' ;
|
||||
|
||||
function GetUninstallString(): String;
|
||||
var
|
||||
sUnInstPath: String;
|
||||
sUnInstallString: String;
|
||||
begin
|
||||
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
|
||||
sUnInstallString := '';
|
||||
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
|
||||
RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
|
||||
Result := sUnInstallString;
|
||||
end;
|
||||
|
||||
function IsUpgrade(): Boolean;
|
||||
begin
|
||||
Result := (GetUninstallString() <> '');
|
||||
end;
|
||||
|
||||
// Return Values:
|
||||
// 1 - uninstall string is empty
|
||||
// 2 - error executing the UnInstallString
|
||||
// 3 - successfully executed the UnInstallString
|
||||
function UnInstallOldVersion(): Integer;
|
||||
var
|
||||
sUnInstallString: String;
|
||||
iResultCode: Integer;
|
||||
begin
|
||||
Result := 0;
|
||||
sUnInstallString := GetUninstallString();
|
||||
if sUnInstallString <> '' then
|
||||
begin
|
||||
sUnInstallString := RemoveQuotes(sUnInstallString);
|
||||
if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
|
||||
Result := 3
|
||||
else
|
||||
Result := 2;
|
||||
end
|
||||
else
|
||||
Result := 1;
|
||||
end;
|
||||
|
||||
function InitializeSetup(): Boolean;
|
||||
begin
|
||||
Result := true;
|
||||
while IsModuleLoadedInstall( 'OpenLP.exe' ) and Result do
|
||||
begin
|
||||
if MsgBox( 'Openlp is currently running, please close it to continue the install.',
|
||||
mbError, MB_OKCANCEL ) = IDCANCEL then
|
||||
begin
|
||||
Result := false;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
begin
|
||||
if (CurStep=ssInstall) then
|
||||
begin
|
||||
if (IsUpgrade()) then
|
||||
begin
|
||||
UnInstallOldVersion();
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function InitializeUninstall(): Boolean;
|
||||
begin
|
||||
Result := true;
|
||||
while IsModuleLoadedUninstall( 'OpenLP.exe' ) and Result do
|
||||
begin
|
||||
if MsgBox( 'Openlp is currently running, please close it to continue the uninstall.',
|
||||
mbError, MB_OKCANCEL ) = IDCANCEL then
|
||||
begin
|
||||
Result := false;
|
||||
end;
|
||||
end;
|
||||
// Unload psvince.dll, otherwise it is not deleted
|
||||
UnloadDLL(ExpandConstant('{app}\psvince.dll'));
|
||||
end;
|
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 199 KiB |
Before Width: | Height: | Size: 12 KiB |
@ -1,26 +0,0 @@
|
||||
[bibles]
|
||||
status = 1
|
||||
|
||||
[media]
|
||||
status = 1
|
||||
|
||||
[alerts]
|
||||
status = 1
|
||||
|
||||
[presentations]
|
||||
status = 1
|
||||
|
||||
[custom]
|
||||
status = 1
|
||||
|
||||
[remotes]
|
||||
status = 0
|
||||
|
||||
[images]
|
||||
status = 1
|
||||
|
||||
[songusage]
|
||||
status = 1
|
||||
|
||||
[songs]
|
||||
status = 1
|
@ -1,407 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
|
||||
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
||||
# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, 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 #
|
||||
###############################################################################
|
||||
|
||||
"""
|
||||
Windows Build Script
|
||||
--------------------
|
||||
|
||||
This script is used to build the Windows binary and the accompanying installer.
|
||||
For this script to work out of the box, it depends on a number of things:
|
||||
|
||||
Python 2.6/2.7
|
||||
|
||||
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.
|
||||
|
||||
PyEnchant
|
||||
This script expects the precompiled, installable version of PyEnchant to be
|
||||
installed. You can find this on the PyEnchant site.
|
||||
|
||||
Inno Setup 5
|
||||
Inno Setup should be installed into "C:\%PROGRAMFILES%\Inno Setup 5"
|
||||
|
||||
Sphinx
|
||||
This is used to build the documentation. The documentation trunk must be at
|
||||
the same directory level as Openlp trunk and named "documentation".
|
||||
|
||||
HTML Help Workshop
|
||||
This is used to create the help file.
|
||||
|
||||
PyInstaller
|
||||
PyInstaller should be a checkout of revision 1470 of trunk, and in a
|
||||
directory called, "pyinstaller" on the same level as OpenLP's Bazaar shared
|
||||
repository directory. The revision is very important as there is currently
|
||||
a major regression in HEAD.
|
||||
|
||||
To install PyInstaller, first checkout trunk from Subversion. The easiest
|
||||
way is to install TortoiseSVN and then checkout the following URL to a
|
||||
directory called "pyinstaller"::
|
||||
|
||||
http://svn.pyinstaller.org/trunk
|
||||
|
||||
Bazaar
|
||||
You need the command line "bzr" client installed.
|
||||
|
||||
OpenLP
|
||||
A checkout of the latest code, in a branch directory, which is in a Bazaar
|
||||
shared repository directory. This means your code should be in a directory
|
||||
structure like this: "openlp\branch-name".
|
||||
|
||||
Visual C++ 2008 Express Edition
|
||||
This is to build pptviewlib.dll, the library for controlling the
|
||||
PowerPointViewer.
|
||||
|
||||
windows-builder.py
|
||||
This script, of course. It should be in the "scripts" directory of OpenLP.
|
||||
|
||||
psvince.dll
|
||||
This dll is used during the actual install of OpenLP to check if OpenLP is
|
||||
running on the users machine prior to the setup. If OpenLP is running,
|
||||
the install will fail. The dll can be obtained from here:
|
||||
|
||||
http://www.vincenzo.net/isxkb/index.php?title=PSVince)
|
||||
|
||||
The dll is presently included in .\\resources\\windows
|
||||
|
||||
Mako
|
||||
Mako Templates for Python. This package is required for building the
|
||||
remote plugin. It can be installed by going to your
|
||||
python_directory\scripts\.. and running "easy_install Mako". If you do not
|
||||
have easy_install, the Mako package can be obtained here:
|
||||
|
||||
http://www.makotemplates.org/download.html
|
||||
|
||||
Sqlalchemy Migrate
|
||||
Required for the data-bases used in OpenLP. The package can be
|
||||
obtained here:
|
||||
|
||||
http://code.google.com/p/sqlalchemy-migrate/
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from shutil import copy, rmtree
|
||||
from subprocess import Popen, PIPE
|
||||
from ConfigParser import SafeConfigParser as ConfigParser
|
||||
|
||||
# Executable paths
|
||||
python_exe = sys.executable
|
||||
innosetup_exe = os.path.join(os.getenv(u'PROGRAMFILES'), 'Inno Setup 5',
|
||||
u'ISCC.exe')
|
||||
sphinx_exe = os.path.join(os.path.split(python_exe)[0], u'Scripts',
|
||||
u'sphinx-build.exe')
|
||||
hhc_exe = os.path.join(os.getenv(u'PROGRAMFILES'), 'HTML Help Workshop',
|
||||
u'hhc.exe')
|
||||
vcbuild_exe = os.path.join(os.getenv(u'PROGRAMFILES'),
|
||||
u'Microsoft Visual Studio 9.0', u'VC', u'vcpackages', u'vcbuild.exe')
|
||||
|
||||
# Base paths
|
||||
script_path = os.path.split(os.path.abspath(__file__))[0]
|
||||
branch_path = os.path.abspath(os.path.join(script_path, u'..'))
|
||||
doc_branch_path = os.path.abspath(os.path.join(script_path, u'..',
|
||||
u'..', u'documentation'))
|
||||
site_packages = os.path.join(os.path.split(python_exe)[0], u'Lib',
|
||||
u'site-packages')
|
||||
|
||||
# Files and executables
|
||||
pyi_build = os.path.abspath(os.path.join(branch_path, u'..', u'..',
|
||||
u'pyinstaller', u'pyinstaller.py'))
|
||||
openlp_main_script = os.path.abspath(os.path.join(branch_path, 'openlp.pyw'))
|
||||
if os.path.exists(os.path.join(site_packages, u'PyQt4', u'bin')):
|
||||
# Older versions of the PyQt4 Windows installer put their binaries in the
|
||||
# "bin" directory
|
||||
lrelease_exe = os.path.join(site_packages, u'PyQt4', u'bin', u'lrelease.exe')
|
||||
else:
|
||||
# Newer versions of the PyQt4 Windows installer put their binaries in the
|
||||
# base directory of the installation
|
||||
lrelease_exe = os.path.join(site_packages, u'PyQt4', u'lrelease.exe')
|
||||
i18n_utils = os.path.join(script_path, u'translation_utils.py')
|
||||
win32_icon = os.path.join(branch_path, u'resources', u'images', 'OpenLP.ico')
|
||||
|
||||
# Paths
|
||||
source_path = os.path.join(branch_path, u'openlp')
|
||||
manual_path = os.path.join(doc_branch_path, u'manual')
|
||||
manual_build_path = os.path.join(manual_path, u'build')
|
||||
helpfile_path = os.path.join(manual_build_path, u'htmlhelp')
|
||||
i18n_path = os.path.join(branch_path, u'resources', u'i18n')
|
||||
winres_path = os.path.join(branch_path, u'resources', u'windows')
|
||||
build_path = os.path.join(branch_path, u'build')
|
||||
dist_path = os.path.join(branch_path, u'dist', u'OpenLP')
|
||||
pptviewlib_path = os.path.join(source_path, u'plugins', u'presentations',
|
||||
u'lib', u'pptviewlib')
|
||||
hooks_path = os.path.join(branch_path , u'resources', u'pyinstaller')
|
||||
|
||||
# Transifex details -- will be read in from the file.
|
||||
transifex_filename = os.path.abspath(os.path.join(branch_path, '..',
|
||||
'transifex.conf'))
|
||||
|
||||
def update_code():
|
||||
os.chdir(branch_path)
|
||||
print u'Reverting any changes to the code...'
|
||||
bzr = Popen((u'bzr', u'revert'), stdout=PIPE)
|
||||
output, error = bzr.communicate()
|
||||
code = bzr.wait()
|
||||
if code != 0:
|
||||
print output
|
||||
raise Exception(u'Error reverting the code')
|
||||
print u'Updating the code...'
|
||||
bzr = Popen((u'bzr', u'update'), stdout=PIPE)
|
||||
output, error = bzr.communicate()
|
||||
code = bzr.wait()
|
||||
if code != 0:
|
||||
print output
|
||||
raise Exception(u'Error updating the code')
|
||||
|
||||
def run_pyinstaller():
|
||||
print u'Running PyInstaller...'
|
||||
os.chdir(branch_path)
|
||||
pyinstaller = Popen((python_exe, pyi_build,
|
||||
u'--noconfirm',
|
||||
u'--windowed',
|
||||
u'--noupx',
|
||||
u'--additional-hooks-dir', hooks_path,
|
||||
u'--log-level=ERROR',
|
||||
u'-o', branch_path,
|
||||
u'-i', win32_icon,
|
||||
u'-p', branch_path,
|
||||
u'-n', 'OpenLP',
|
||||
openlp_main_script),
|
||||
stdout=PIPE)
|
||||
output, error = pyinstaller.communicate()
|
||||
code = pyinstaller.wait()
|
||||
if code != 0:
|
||||
print output
|
||||
raise Exception(u'Error running PyInstaller')
|
||||
|
||||
def write_version_file():
|
||||
print u'Writing version file...'
|
||||
os.chdir(branch_path)
|
||||
bzr = Popen((u'bzr', u'tags', u'--sort', u'time'), stdout=PIPE)
|
||||
output, error = bzr.communicate()
|
||||
code = bzr.wait()
|
||||
if code != 0:
|
||||
raise Exception(u'Error running bzr tags')
|
||||
lines = output.splitlines()
|
||||
if len(lines) == 0:
|
||||
tag = u'0.0.0'
|
||||
revision = u'0'
|
||||
else:
|
||||
tag, revision = lines[-1].split()
|
||||
bzr = Popen((u'bzr', u'log', u'--line', u'-r', u'-1'), stdout=PIPE)
|
||||
output, error = bzr.communicate()
|
||||
code = bzr.wait()
|
||||
if code != 0:
|
||||
raise Exception(u'Error running bzr log')
|
||||
outputAscii = unicode(output, errors='ignore')
|
||||
latest = outputAscii.split(u':')[0]
|
||||
versionstring = latest == revision and tag or u'%s-bzr%s' % (tag, latest)
|
||||
f = open(os.path.join(dist_path, u'.version'), u'w')
|
||||
f.write(versionstring)
|
||||
f.close()
|
||||
|
||||
def copy_plugins():
|
||||
print u'Copying plugins...'
|
||||
source = os.path.join(source_path, u'plugins')
|
||||
dest = os.path.join(dist_path, u'plugins')
|
||||
for root, dirs, files in os.walk(source):
|
||||
for filename in files:
|
||||
if not filename.endswith(u'.pyc'):
|
||||
dest_path = os.path.join(dest, root[len(source)+1:])
|
||||
if not os.path.exists(dest_path):
|
||||
os.makedirs(dest_path)
|
||||
copy(os.path.join(root, filename),
|
||||
os.path.join(dest_path, filename))
|
||||
|
||||
def copy_media_player():
|
||||
print u'Copying media player...'
|
||||
source = os.path.join(source_path, u'core', u'ui', u'media')
|
||||
dest = os.path.join(dist_path, u'core', u'ui', u'media')
|
||||
for root, dirs, files in os.walk(source):
|
||||
for filename in files:
|
||||
if not filename.endswith(u'.pyc'):
|
||||
dest_path = os.path.join(dest, root[len(source)+1:])
|
||||
if not os.path.exists(dest_path):
|
||||
os.makedirs(dest_path)
|
||||
copy(os.path.join(root, filename),
|
||||
os.path.join(dest_path, filename))
|
||||
|
||||
def copy_windows_files():
|
||||
print u'Copying extra files for Windows...'
|
||||
copy(os.path.join(winres_path, u'OpenLP.ico'),
|
||||
os.path.join(dist_path, u'OpenLP.ico'))
|
||||
copy(os.path.join(winres_path, u'LICENSE.txt'),
|
||||
os.path.join(dist_path, u'LICENSE.txt'))
|
||||
copy(os.path.join(winres_path, u'psvince.dll'),
|
||||
os.path.join(dist_path, u'psvince.dll'))
|
||||
if os.path.isfile(os.path.join(helpfile_path, u'OpenLP.chm')):
|
||||
print u' Windows help file found'
|
||||
copy(os.path.join(helpfile_path, u'OpenLP.chm'),
|
||||
os.path.join(dist_path, u'OpenLP.chm'))
|
||||
else:
|
||||
print u' WARNING ---- Windows help file not found ---- WARNING'
|
||||
|
||||
def update_translations():
|
||||
print u'Updating translations...'
|
||||
if not os.path.exists(transifex_filename):
|
||||
raise Exception(u'Could not find Transifex credentials file: %s' \
|
||||
% transifex_filename)
|
||||
config = ConfigParser()
|
||||
config.read(transifex_filename)
|
||||
if not config.has_section('transifex'):
|
||||
raise Exception(u'No section named "transifex" found.')
|
||||
if not config.has_option('transifex', 'username'):
|
||||
raise Exception(u'No option named "username" found.')
|
||||
if not config.has_option('transifex', 'password'):
|
||||
raise Exception(u'No option named "password" found.')
|
||||
username = config.get('transifex', 'username')
|
||||
password = config.get('transifex', 'password')
|
||||
os.chdir(script_path)
|
||||
translation_utils = Popen([python_exe, i18n_utils, u'-qdpu', '-U',
|
||||
username, '-P', password])
|
||||
code = translation_utils.wait()
|
||||
if code != 0:
|
||||
raise Exception(u'Error running translation_utils.py')
|
||||
|
||||
def compile_translations():
|
||||
print u'Compiling translations...'
|
||||
files = os.listdir(i18n_path)
|
||||
if not os.path.exists(os.path.join(dist_path, u'i18n')):
|
||||
os.makedirs(os.path.join(dist_path, u'i18n'))
|
||||
for file in files:
|
||||
if file.endswith(u'.ts'):
|
||||
source_path = os.path.join(i18n_path, file)
|
||||
dest_path = os.path.join(dist_path, u'i18n',
|
||||
file.replace(u'.ts', u'.qm'))
|
||||
lconvert = Popen((lrelease_exe, u'-compress', u'-silent',
|
||||
source_path, u'-qm', dest_path))
|
||||
code = lconvert.wait()
|
||||
if code != 0:
|
||||
raise Exception('Error running lconvert on %s' % source_path)
|
||||
print u'Copying qm files...'
|
||||
source = os.path.join(site_packages, u'PyQt4', u'translations')
|
||||
files = os.listdir(source)
|
||||
for filename in files:
|
||||
if filename.startswith(u'qt_') and filename.endswith(u'.qm') and \
|
||||
len(filename) == 8:
|
||||
copy(os.path.join(source, filename),
|
||||
os.path.join(dist_path, u'i18n', filename))
|
||||
|
||||
def run_sphinx():
|
||||
print u'Deleting previous manual build...', manual_build_path
|
||||
if os.path.exists(manual_build_path):
|
||||
rmtree(manual_build_path)
|
||||
print u'Running Sphinx...'
|
||||
os.chdir(manual_path)
|
||||
sphinx = Popen((sphinx_exe, u'-b', u'htmlhelp', u'-d', u'build/doctrees',
|
||||
u'source', u'build/htmlhelp'), stdout=PIPE)
|
||||
output, error = sphinx.communicate()
|
||||
code = sphinx.wait()
|
||||
if code != 0:
|
||||
print output
|
||||
raise Exception(u'Error running Sphinx')
|
||||
|
||||
def run_htmlhelp():
|
||||
print u'Running HTML Help Workshop...'
|
||||
os.chdir(os.path.join(manual_build_path, u'htmlhelp'))
|
||||
hhc = Popen((hhc_exe, u'OpenLP.chm'), stdout=PIPE)
|
||||
output, error = hhc.communicate()
|
||||
code = hhc.wait()
|
||||
if code != 1:
|
||||
print u'Exit code:', code
|
||||
print output
|
||||
raise Exception(u'Error running HTML Help Workshop')
|
||||
|
||||
def run_innosetup():
|
||||
print u'Running Inno Setup...'
|
||||
os.chdir(winres_path)
|
||||
innosetup = Popen((innosetup_exe,
|
||||
os.path.join(winres_path, u'OpenLP-2.0.iss'), u'/q'))
|
||||
code = innosetup.wait()
|
||||
if code != 0:
|
||||
raise Exception(u'Error running Inno Setup')
|
||||
|
||||
def build_pptviewlib():
|
||||
print u'Building PPTVIEWLIB.DLL...'
|
||||
vcbuild = Popen((vcbuild_exe, u'/rebuild',
|
||||
os.path.join(pptviewlib_path, u'pptviewlib.vcproj'), u'Release|Win32'))
|
||||
code = vcbuild.wait()
|
||||
if code != 0:
|
||||
raise Exception(u'Error building pptviewlib.dll')
|
||||
copy(os.path.join(pptviewlib_path, u'Release', u'pptviewlib.dll'),
|
||||
pptviewlib_path)
|
||||
|
||||
def main():
|
||||
skip_update = False
|
||||
import sys
|
||||
for arg in sys.argv:
|
||||
if arg == u'-v' or arg == u'--verbose':
|
||||
print "OpenLP main script: ......", openlp_main_script
|
||||
print "Script path: .............", script_path
|
||||
print "Branch path: .............", branch_path
|
||||
print "Source path: .............", source_path
|
||||
print "\"dist\" path: .............", dist_path
|
||||
print "PyInstaller: .............", pyi_build
|
||||
print "Documentation branch path:", doc_branch_path
|
||||
print "Help file build path: ....", helpfile_path
|
||||
print "Inno Setup path: .........", innosetup_exe
|
||||
print "Windows resources: .......", winres_path
|
||||
print "VCBuild path: ............", vcbuild_exe
|
||||
print "PPTVIEWLIB path: .........", pptviewlib_path
|
||||
print ""
|
||||
elif arg == u'--skip-update':
|
||||
skip_update = True
|
||||
elif arg == u'/?' or arg == u'-h' or arg == u'--help':
|
||||
print u'Command options:'
|
||||
print u' -v --verbose : More verbose output'
|
||||
print u' --skip-update : Do not update or revert current branch'
|
||||
exit()
|
||||
if not skip_update:
|
||||
update_code()
|
||||
build_pptviewlib()
|
||||
run_pyinstaller()
|
||||
write_version_file()
|
||||
copy_plugins()
|
||||
copy_media_player()
|
||||
if os.path.exists(manual_path):
|
||||
run_sphinx()
|
||||
run_htmlhelp()
|
||||
else:
|
||||
print u' '
|
||||
print u' WARNING ---- Documentation Trunk not found ---- WARNING'
|
||||
print u' --- Windows Help file will not be included in build ---'
|
||||
print u' '
|
||||
copy_windows_files()
|
||||
update_translations()
|
||||
compile_translations()
|
||||
run_innosetup()
|
||||
print "Done."
|
||||
|
||||
if __name__ == u'__main__':
|
||||
main()
|