diff --git a/documentation/manual/source/dualmonitors.rst b/documentation/manual/source/dualmonitors.rst index 5c29e8650..3e1a8d210 100644 --- a/documentation/manual/source/dualmonitors.rst +++ b/documentation/manual/source/dualmonitors.rst @@ -180,3 +180,42 @@ on :guilabel:`Save to X Configuration File`. Click :guilabel:`Save` and you should be set. You may want to restart X or your machine just to make sure all the settings carry over the next time you log in. + +Linux Systems With Intel Video +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Generally systems with Intel video cards work very well. They are well supported +by open source drivers. There are, however, a couple of issues that may require +some work arounds. + +**Resolution Issue** + +There is a limitation with certain cards which limits the total resolution to +2048x2048, so both monitors can not have a total resolution totaling more than +that. To work around this it may be necessary to position your monitor as a top +or bottom monitor as opposed to the typical side by side setup. This can easily +be accomplished through your desktop's control of monitors. Please see the +sections on dual monitors with KDE and GNOME above. + +**Primary Monitor Issues** + +With certain cards your system may get confused on what is the primary display. +For example many users will be using a laptop. You will want your laptop screen +to be the primary screen, and your projector to be the secondary monitor. +Certain Intel cards reverse this. To work around this you will need to know the +name of your monitor. If you are a KDE user this info is given to you in the +display settings. If you are not using KDE enter the following in a terminal +without your projector connected to your computer:: + + user@linux:~ $ xrandr -q + +This will give you a long string of output. Screen names will be something along +the lines of LVDM, VGA-0 or some convention similar to that. Without your +projector connected to your computer only one monitor will show as being +connected. That will be the monitor you will need to use as the primary. Now +connect your projector and enter:: + + user@linux:~ $ xrandr --output LVDM --primary + +**Note** it has been reported that when this issue is occurring you will not +want to connect your projector until your desktop is running. diff --git a/openlp.pyw b/openlp.pyw index 46b18f11c..8d5a17a28 100755 --- a/openlp.pyw +++ b/openlp.pyw @@ -27,9 +27,13 @@ import os import sys import logging +# 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 optparse import OptionParser from traceback import format_exception -from subprocess import Popen, PIPE from PyQt4 import QtCore, QtGui @@ -40,7 +44,8 @@ from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm from openlp.core.ui.firsttimeform import FirstTimeForm from openlp.core.ui.exceptionform import ExceptionForm from openlp.core.ui import SplashScreen, ScreenList -from openlp.core.utils import AppLocation, LanguageManager, VersionThread +from openlp.core.utils import AppLocation, LanguageManager, VersionThread, \ + get_application_version log = logging.getLogger() @@ -71,87 +76,6 @@ class OpenLP(QtGui.QApplication): The core application class. This class inherits from Qt's QApplication class in order to provide the core of the application. """ - app_version = None - - def get_version(self): - """ - Load and store current Application Version - """ - if self.app_version: - return self.app_version - if u'--dev-version' in sys.argv or u'-d' in sys.argv: - # If we're running the dev version, let's use bzr to get the version - try: - # If bzrlib is availble, use it - from bzrlib.branch import Branch - b = Branch.open_containing('.')[0] - b.lock_read() - 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: - full_version = u'%s' % tags[revision_id][0] - else: - full_version = '%s-bzr%s' % \ - (sorted(b.tags.get_tag_dict().keys())[-1], revno) - finally: - b.unlock() - except: - # Otherwise run the command line bzr client - 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') - latest = output.split(u':')[0] - full_version = latest == revision and tag or \ - u'%s-bzr%s' % (tag, latest) - else: - # We're not running the development version, let's use the file - filepath = AppLocation.get_directory(AppLocation.VersionDir) - filepath = os.path.join(filepath, u'.version') - fversion = None - try: - fversion = open(filepath, u'r') - full_version = unicode(fversion.read()).rstrip() - except IOError: - log.exception('Error in version file.') - full_version = u'0.0.0-bzr000' - finally: - if fversion: - fversion.close() - bits = full_version.split(u'-') - self.app_version = { - u'full': full_version, - u'version': bits[0], - u'build': bits[1] if len(bits) > 1 else None - } - if self.app_version[u'build']: - log.info( - u'Openlp version %s build %s', - self.app_version[u'version'], - self.app_version[u'build'] - ) - else: - log.info(u'Openlp version %s' % self.app_version[u'version']) - return self.app_version def run(self): """ @@ -183,8 +107,7 @@ class OpenLP(QtGui.QApplication): # make sure Qt really display the splash screen self.processEvents() # start the main app window - self.mainWindow = MainWindow(screens, self.app_version, - self.clipboard()) + self.mainWindow = MainWindow(screens, self.clipboard()) self.mainWindow.show() if show_splash: # now kill the splashscreen @@ -196,7 +119,7 @@ class OpenLP(QtGui.QApplication): update_check = QtCore.QSettings().value( u'general/update check', QtCore.QVariant(True)).toBool() if update_check: - VersionThread(self.mainWindow, self.app_version).start() + VersionThread(self.mainWindow).start() return self.exec_() def hookException(self, exctype, value, traceback): @@ -274,7 +197,7 @@ def main(): app.setOrganizationName(u'OpenLP') app.setOrganizationDomain(u'openlp.org') app.setApplicationName(u'OpenLP') - app.setApplicationVersion(app.get_version()[u'version']) + app.setApplicationVersion(get_application_version()[u'version']) # First time checks in settings if not QtCore.QSettings().value(u'general/has run wizard', QtCore.QVariant(False)).toBool(): diff --git a/openlp/.version b/openlp/.version index ae8249b9a..998994b7f 100644 --- a/openlp/.version +++ b/openlp/.version @@ -1 +1 @@ -1.9.2-bzr987 \ No newline at end of file +1.9.5-bzr1421 \ No newline at end of file diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 0d56d1ddd..ddb1ae9b1 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -316,8 +316,11 @@ def check_directory_exists(dir): Theme directory to make sure exists """ log.debug(u'check_directory_exists %s' % dir) - if not os.path.exists(dir): - os.makedirs(dir) + try: + if not os.path.exists(dir): + os.makedirs(dir) + except IOError: + pass from listwidgetwithdnd import ListWidgetWithDnD from displaytags import DisplayTags diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 5b96bbf00..eda8ecfa1 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -32,6 +32,7 @@ from PyQt4 import QtCore from openlp.core.lib import Receiver from openlp.core.lib.ui import UiStrings +from openlp.core.utils import get_application_version log = logging.getLogger(__name__) @@ -145,7 +146,10 @@ class Plugin(QtCore.QObject): self.textStrings = {} self.setPluginTextStrings() self.nameStrings = self.textStrings[StringContent.Name] - self.version = version if version else u'1.9.5' + if version: + self.version = version + else: + self.version = get_application_version()[u'version'] self.settingsSection = self.name.lower() self.icon = None self.mediaItemClass = mediaItemClass diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index da046d99c..776b8e349 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -49,6 +49,7 @@ class UiStrings(object): Cancel = translate('OpenLP.Ui', 'Cancel') CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:') CreateService = translate('OpenLP.Ui', 'Create a new service.') + Default = unicode(translate('OpenLP.Ui', 'Default')) Delete = translate('OpenLP.Ui', '&Delete') Edit = translate('OpenLP.Ui', '&Edit') EmptyField = translate('OpenLP.Ui', 'Empty Field') diff --git a/openlp/core/ui/aboutform.py b/openlp/core/ui/aboutform.py index 5c26167e3..4112cfd8f 100644 --- a/openlp/core/ui/aboutform.py +++ b/openlp/core/ui/aboutform.py @@ -28,25 +28,26 @@ from PyQt4 import QtCore, QtGui from aboutdialog import Ui_AboutDialog from openlp.core.lib import translate +from openlp.core.utils import get_application_version class AboutForm(QtGui.QDialog, Ui_AboutDialog): """ The About dialog """ - def __init__(self, parent, applicationVersion): + def __init__(self, parent): """ Do some initialisation stuff """ QtGui.QDialog.__init__(self, parent) - self.applicationVersion = applicationVersion + applicationVersion = get_application_version() self.setupUi(self) about_text = self.aboutTextEdit.toPlainText() about_text = about_text.replace(u'', - self.applicationVersion[u'version']) - if self.applicationVersion[u'build']: + applicationVersion[u'version']) + if applicationVersion[u'build']: build_text = unicode(translate('OpenLP.AboutForm', ' build %s')) % \ - self.applicationVersion[u'build'] + applicationVersion[u'build'] else: build_text = u'' about_text = about_text.replace(u'', build_text) diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index c6d36d741..4c7ae6880 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -26,6 +26,8 @@ from PyQt4 import QtCore, QtGui +import sys + from openlp.core.lib import translate from openlp.core.lib.ui import add_welcome_page @@ -77,7 +79,10 @@ class Ui_FirstTimeWizard(object): self.imageCheckBox.setObjectName(u'imageCheckBox') self.pluginLayout.addWidget(self.imageCheckBox) self.presentationCheckBox = QtGui.QCheckBox(self.pluginPage) - self.presentationCheckBox.setChecked(True) + if sys.platform == "darwin": + self.presentationCheckBox.setChecked(False) + else: + self.presentationCheckBox.setChecked(True) self.presentationCheckBox.setObjectName(u'presentationCheckBox') self.pluginLayout.addWidget(self.presentationCheckBox) self.mediaCheckBox = QtGui.QCheckBox(self.pluginPage) @@ -210,6 +215,8 @@ class Ui_FirstTimeWizard(object): 'Images')) self.presentationCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Presentations')) + if sys.platform == "darwin": + self.presentationCheckBox.setEnabled(False) self.mediaCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Media (Audio and Video)')) self.remoteCheckBox.setText(translate('OpenLP.FirstTimeWizard', diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index e421edd98..e6ace21cd 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -38,7 +38,7 @@ from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ ThemeManager, SlideController, PluginForm, MediaDockManager, \ ShortcutListForm, DisplayTagForm from openlp.core.utils import AppLocation, add_actions, LanguageManager, \ - ActionList + ActionList, get_application_version log = logging.getLogger(__name__) @@ -469,14 +469,13 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): actionList = ActionList() - def __init__(self, screens, applicationVersion, clipboard): + def __init__(self, screens, clipboard): """ This constructor sets up the interface, the various managers, and the plugins. """ QtGui.QMainWindow.__init__(self) self.screens = screens - self.applicationVersion = applicationVersion self.clipboard = clipboard # Set up settings sections for the main application # (not for use by plugins) @@ -487,7 +486,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.serviceNotSaved = False self.actionList = ActionList() self.settingsmanager = SettingsManager(screens) - self.aboutForm = AboutForm(self, applicationVersion) + self.aboutForm = AboutForm(self) self.settingsForm = SettingsForm(self.screens, self, self) self.displayTagForm = DisplayTagForm(self) self.shortcutForm = ShortcutListForm(self) @@ -622,9 +621,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): # Call the initialise method to setup plugins. log.info(u'initialise plugins') self.pluginManager.initialise_plugins() - # Once all components are initialised load the Themes - log.info(u'Load Themes') - self.themeManagerContents.loadThemes() log.info(u'Load data from Settings') if QtCore.QSettings().value(u'advanced/save current plugin', QtCore.QVariant(False)).toBool(): @@ -633,6 +629,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): if savedPlugin != -1: self.MediaToolBox.setCurrentIndex(savedPlugin) self.settingsForm.postSetUp() + # Once all components are initialised load the Themes + log.info(u'Load Themes') + self.themeManagerContents.loadThemes(True) Receiver.send_message(u'cursor_normal') def setAutoLanguage(self, value): @@ -651,7 +650,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): 'version from http://openlp.org/.')) QtGui.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Version Updated'), - version_text % (version, self.applicationVersion[u'full'])) + version_text % (version, get_application_version()[u'full'])) def show(self): """ @@ -679,7 +678,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): def firstTime(self): # Import themes if first time Receiver.send_message(u'openlp_process_events') - self.themeManagerContents.firstTime() for plugin in self.pluginManager.plugins: if hasattr(plugin, u'firstTime'): Receiver.send_message(u'openlp_process_events') @@ -734,7 +732,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): """ Show the About form """ - self.aboutForm.applicationVersion = self.applicationVersion self.aboutForm.exec_() def onPluginItemClicked(self): diff --git a/openlp/core/ui/printservicedialog.py b/openlp/core/ui/printservicedialog.py index 22047eefb..97f4b4060 100644 --- a/openlp/core/ui/printservicedialog.py +++ b/openlp/core/ui/printservicedialog.py @@ -64,8 +64,7 @@ class Ui_PrintServiceDialog(object): 'Options')) self.optionsButton.setToolButtonStyle( QtCore.Qt.ToolButtonTextBesideIcon) - self.optionsButton.setIcon(QtGui.QIcon( - build_icon(u':/system/system_configure.png'))) + self.optionsButton.setIcon(build_icon(u':/system/system_configure.png')) self.optionsButton.setCheckable(True) self.toolbar.addWidget(self.optionsButton) self.closeButton = self.toolbar.addAction( @@ -80,24 +79,23 @@ class Ui_PrintServiceDialog(object): translate('OpenLP.PrintServiceForm', 'Copy as HTML')) self.toolbar.addSeparator() self.zoomInButton = QtGui.QToolButton(self.toolbar) - self.zoomInButton.setIcon(QtGui.QIcon( - build_icon(u':/general/general_zoom_in.png'))) + self.zoomInButton.setIcon(build_icon(u':/general/general_zoom_in.png')) self.zoomInButton.setToolTip(translate('OpenLP.PrintServiceForm', 'Zoom In')) self.zoomInButton.setObjectName(u'zoomInButton') self.zoomInButton.setIconSize(QtCore.QSize(22, 22)) self.toolbar.addWidget(self.zoomInButton) self.zoomOutButton = QtGui.QToolButton(self.toolbar) - self.zoomOutButton.setIcon(QtGui.QIcon( - build_icon(u':/general/general_zoom_out.png'))) + self.zoomOutButton.setIcon( + build_icon(u':/general/general_zoom_out.png')) self.zoomOutButton.setToolTip(translate('OpenLP.PrintServiceForm', 'Zoom Out')) self.zoomOutButton.setObjectName(u'zoomOutButton') self.zoomOutButton.setIconSize(QtCore.QSize(22, 22)) self.toolbar.addWidget(self.zoomOutButton) self.zoomOriginalButton = QtGui.QToolButton(self.toolbar) - self.zoomOriginalButton.setIcon(QtGui.QIcon( - build_icon(u':/general/general_zoom_original.png'))) + self.zoomOriginalButton.setIcon( + build_icon(u':/general/general_zoom_original.png')) self.zoomOriginalButton.setToolTip(translate('OpenLP.PrintServiceForm', 'Zoom Original')) self.zoomOriginalButton.setObjectName(u'zoomOriginalButton') diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index d9d9c7287..cc6ee2b3b 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -481,28 +481,30 @@ class ServiceManager(QtGui.QWidget): # Usual Zip file cannot exceed 2GiB, file with Zip64 cannot be # extracted using unzip in UNIX. allow_zip_64 = (total_size > 2147483648 + len(service_content)) - log.debug(u'ServiceManager.saveFile - allowZip64 is %s' % - allow_zip_64) + log.debug(u'ServiceManager.saveFile - allowZip64 is %s' % allow_zip_64) zip = None + success = True try: zip = zipfile.ZipFile(path_file_name, 'w', zipfile.ZIP_STORED, allow_zip_64) # First we add service contents. # We save ALL filenames into ZIP using UTF-8. - zip.writestr(service_file_name.encode(u'utf-8'), - service_content) + zip.writestr(service_file_name.encode(u'utf-8'), service_content) # Finally add all the listed media files. for path_from in write_list: zip.write(path_from, path_from.encode(u'utf-8')) except IOError: log.exception(u'Failed to save service to disk') - return False + success = False finally: if zip: zip.close() - self.mainwindow.addRecentFile(path_file_name) - self.setModified(False) - return True + if success: + self.mainwindow.addRecentFile(path_file_name) + self.setModified(False) + else: + delete_file(path_file_name) + return success def saveFileAs(self): """ @@ -527,8 +529,9 @@ class ServiceManager(QtGui.QWidget): def loadFile(self, fileName): if not fileName: return False - else: - fileName = unicode(fileName) + fileName = unicode(fileName) + if not os.path.exists(fileName): + return False zip = None fileTo = None try: @@ -566,24 +569,27 @@ class ServiceManager(QtGui.QWidget): Receiver.send_message(u'%s_service_load' % serviceItem.name.lower(), serviceItem) delete_file(p_file) + self.setFileName(fileName) + self.mainwindow.addRecentFile(fileName) + self.setModified(False) + QtCore.QSettings().setValue( + 'service/last file', QtCore.QVariant(fileName)) Receiver.send_message(u'cursor_normal') else: critical_error_message_box( message=translate('OpenLP.ServiceManager', 'File is not a valid service.')) log.exception(u'File contains no service data') - except (IOError, NameError): + except (IOError, NameError, zipfile.BadZipfile): + critical_error_message_box( + message=translate('OpenLP.ServiceManager', + 'File could not be opened because it is corrupt.')) log.exception(u'Problem loading service file %s' % fileName) finally: if fileTo: fileTo.close() if zip: zip.close() - self.setFileName(fileName) - self.mainwindow.addRecentFile(fileName) - self.setModified(False) - QtCore.QSettings(). \ - setValue(u'service/last file', QtCore.QVariant(fileName)) def loadLastFile(self): """ diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 6af25d344..e4e1032f6 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -46,6 +46,7 @@ class SlideList(QtGui.QTableWidget): QtGui.QTableWidget.__init__(self, parent.controller) self.parent = parent + class SlideController(QtGui.QWidget): """ SlideController is the slide controller widget. This widget is what the diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 11d10413c..d033daeb3 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -156,10 +156,9 @@ class ThemeManager(QtGui.QWidget): file = os.path.join(self.path, file).encode(encoding) self.unzipTheme(file, self.path) delete_file(file) - self.loadThemes() Receiver.send_message(u'cursor_normal') - def configUpdated(self, firstTime=False): + def configUpdated(self): """ Triggered when Config dialog is updated. """ @@ -433,7 +432,7 @@ class ThemeManager(QtGui.QWidget): self.loadThemes() Receiver.send_message(u'cursor_normal') - def loadThemes(self): + def loadThemes(self, firstTime=False): """ Loads the theme lists and triggers updates accross the whole system using direct calls or core functions and events for the plugins. @@ -443,31 +442,44 @@ class ThemeManager(QtGui.QWidget): self.themelist = [] self.themeListWidget.clear() dirList = os.listdir(self.path) - dirList.sort() - for name in dirList: - if name.endswith(u'.png'): - # check to see file is in theme root directory - theme = os.path.join(self.path, name) - if os.path.exists(theme): - textName = os.path.splitext(name)[0] - if textName == self.global_theme: - name = unicode(translate('OpenLP.ThemeManager', - '%s (default)')) % textName - else: - name = textName - thumb = os.path.join(self.thumbPath, u'%s.png' % textName) - item_name = QtGui.QListWidgetItem(name) - if os.path.exists(thumb): - icon = build_icon(thumb) - else: - icon = build_icon(theme) - pixmap = icon.pixmap(QtCore.QSize(88, 50)) - pixmap.save(thumb, u'png') - item_name.setIcon(icon) - item_name.setData(QtCore.Qt.UserRole, - QtCore.QVariant(textName)) - self.themeListWidget.addItem(item_name) - self.themelist.append(textName) + files = SettingsManager.get_files(self.settingsSection, u'.png') + if firstTime: + self.firstTime() + # No themes have been found so create one + if len(files) == 0: + theme = ThemeXML() + theme.theme_name = UiStrings.Default + self._writeTheme(theme, None, None) + QtCore.QSettings().setValue( + self.settingsSection + u'/global theme', + QtCore.QVariant(theme.theme_name)) + self.configUpdated() + files = SettingsManager.get_files(self.settingsSection, u'.png') + files.sort() + # now process the file list of png files + for name in files: + # check to see file is in theme root directory + theme = os.path.join(self.path, name) + if os.path.exists(theme): + textName = os.path.splitext(name)[0] + if textName == self.global_theme: + name = unicode(translate('OpenLP.ThemeManager', + '%s (default)')) % textName + else: + name = textName + thumb = os.path.join(self.thumbPath, u'%s.png' % textName) + item_name = QtGui.QListWidgetItem(name) + if os.path.exists(thumb): + icon = build_icon(thumb) + else: + icon = build_icon(theme) + pixmap = icon.pixmap(QtCore.QSize(88, 50)) + pixmap.save(thumb, u'png') + item_name.setIcon(icon) + item_name.setData(QtCore.Qt.UserRole, + QtCore.QVariant(textName)) + self.themeListWidget.addItem(item_name) + self.themelist.append(textName) self._pushThemes() def _pushThemes(self): diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 751e2b3a8..3c639297e 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -33,6 +33,7 @@ import sys import time import urllib2 from datetime import datetime +from subprocess import Popen, PIPE from PyQt4 import QtGui, QtCore @@ -44,9 +45,10 @@ if sys.platform != u'win32' and sys.platform != u'darwin': XDG_BASE_AVAILABLE = False import openlp -from openlp.core.lib import Receiver, translate +from openlp.core.lib import Receiver, translate, check_directory_exists log = logging.getLogger(__name__) +APPLICATION_VERSION = {} IMAGES_FILTER = None UNO_CONNECTION_TYPE = u'pipe' #UNO_CONNECTION_TYPE = u'socket' @@ -56,9 +58,8 @@ class VersionThread(QtCore.QThread): A special Qt thread class to fetch the version of OpenLP from the website. This is threaded so that it doesn't affect the loading time of OpenLP. """ - def __init__(self, parent, app_version): + def __init__(self, parent): QtCore.QThread.__init__(self, parent) - self.app_version = app_version self.version_splitter = re.compile( r'([0-9]+).([0-9]+).([0-9]+)(?:-bzr([0-9]+))?') @@ -68,7 +69,8 @@ class VersionThread(QtCore.QThread): """ time.sleep(1) Receiver.send_message(u'maindisplay_blank_check') - version = check_latest_version(self.app_version) + app_version = get_application_version() + version = check_latest_version(app_version) remote_version = {} local_version = {} match = self.version_splitter.match(version) @@ -80,7 +82,7 @@ class VersionThread(QtCore.QThread): remote_version[u'revision'] = int(match.group(4)) else: return - match = self.version_splitter.match(self.app_version[u'full']) + match = self.version_splitter.match(app_version[u'full']) if match: local_version[u'major'] = int(match.group(1)) local_version[u'minor'] = int(match.group(2)) @@ -146,8 +148,7 @@ class AppLocation(object): Return the path OpenLP stores all its data under. """ path = AppLocation.get_directory(AppLocation.DataDir) - if not os.path.exists(path): - os.makedirs(path) + check_directory_exists(path) return path @staticmethod @@ -157,8 +158,7 @@ class AppLocation(object): """ data_path = AppLocation.get_data_path() path = os.path.join(data_path, section) - if not os.path.exists(path): - os.makedirs(path) + check_directory_exists(path) return path def _get_os_dir_path(dir_type): @@ -208,6 +208,85 @@ def _get_frozen_path(frozen_option, non_frozen_option): return frozen_option return non_frozen_option +def get_application_version(): + """ + Returns the application version of the running instance of OpenLP:: + + {u'full': u'1.9.4-bzr1249', u'version': u'1.9.4', u'build': u'bzr1249'} + """ + global APPLICATION_VERSION + if APPLICATION_VERSION: + return APPLICATION_VERSION + if u'--dev-version' in sys.argv or u'-d' in sys.argv: + # If we're running the dev version, let's use bzr to get the version. + try: + # If bzrlib is available, use it. + from bzrlib.branch import Branch + b = Branch.open_containing('.')[0] + b.lock_read() + 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: + full_version = u'%s' % tags[revision_id][0] + else: + full_version = '%s-bzr%s' % \ + (sorted(b.tags.get_tag_dict().keys())[-1], revno) + finally: + b.unlock() + except: + # Otherwise run the command line bzr client. + 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') + latest = output.split(u':')[0] + full_version = latest == revision and tag or \ + u'%s-bzr%s' % (tag, latest) + else: + # We're not running the development version, let's use the file. + filepath = AppLocation.get_directory(AppLocation.VersionDir) + filepath = os.path.join(filepath, u'.version') + fversion = None + try: + fversion = open(filepath, u'r') + full_version = unicode(fversion.read()).rstrip() + except IOError: + log.exception('Error in version file.') + full_version = u'0.0.0-bzr000' + finally: + if fversion: + fversion.close() + bits = full_version.split(u'-') + APPLICATION_VERSION = { + u'full': full_version, + u'version': bits[0], + u'build': bits[1] if len(bits) > 1 else None + } + if APPLICATION_VERSION[u'build']: + log.info(u'Openlp version %s build %s', + APPLICATION_VERSION[u'version'], APPLICATION_VERSION[u'build']) + else: + log.info(u'Openlp version %s' % APPLICATION_VERSION[u'version']) + return APPLICATION_VERSION + def check_latest_version(current_version): """ Check the latest version of OpenLP against the version file on the OpenLP diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 0c1da0aed..a9694fd0c 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -551,64 +551,52 @@ class BibleMediaItem(MediaManagerItem): further action is saved for/in each row. """ verse_separator = get_reference_match(u'sep_v_display') - version = self.parent.manager.get_meta_data(bible, u'Version') - copyright = self.parent.manager.get_meta_data(bible, u'Copyright') - permissions = self.parent.manager.get_meta_data(bible, u'Permissions') + version = self.parent.manager.get_meta_data(bible, u'Version').value + copyright = self.parent.manager.get_meta_data(bible, u'Copyright').value + permissions = \ + self.parent.manager.get_meta_data(bible, u'Permissions').value + second_version = u'' + second_copyright = u'' + second_permissions = u'' if second_bible: - second_version = self.parent.manager.get_meta_data(second_bible, - u'Version') - second_copyright = self.parent.manager.get_meta_data(second_bible, - u'Copyright') - second_permissions = self.parent.manager.get_meta_data(second_bible, - u'Permissions') - if not second_permissions: - second_permissions = u'' + second_version = self.parent.manager.get_meta_data( + second_bible, u'Version').value + second_copyright = self.parent.manager.get_meta_data( + second_bible, u'Copyright').value + second_permissions = self.parent.manager.get_meta_data( + second_bible, u'Permissions').value for count, verse in enumerate(self.search_results): + data = { + 'book': QtCore.QVariant(verse.book.name), + 'chapter': QtCore.QVariant(verse.chapter), + 'verse': QtCore.QVariant(verse.verse), + 'bible': QtCore.QVariant(bible), + 'version': QtCore.QVariant(version), + 'copyright': QtCore.QVariant(copyright), + 'permissions': QtCore.QVariant(permissions), + 'text': QtCore.QVariant(verse.text), + 'second_bible': QtCore.QVariant(second_bible), + 'second_version': QtCore.QVariant(second_version), + 'second_copyright': QtCore.QVariant(second_copyright), + 'second_permissions': QtCore.QVariant(second_permissions), + 'second_text': QtCore.QVariant(u'') + } if second_bible: try: - vdict = { - 'book': QtCore.QVariant(verse.book.name), - 'chapter': QtCore.QVariant(verse.chapter), - 'verse': QtCore.QVariant(verse.verse), - 'bible': QtCore.QVariant(bible), - 'version': QtCore.QVariant(version.value), - 'copyright': QtCore.QVariant(copyright.value), - 'permissions': QtCore.QVariant(permissions.value), - 'text': QtCore.QVariant(verse.text), - 'second_bible': QtCore.QVariant(second_bible), - 'second_version': QtCore.QVariant(second_version.value), - 'second_copyright': QtCore.QVariant( - second_copyright.value), - 'second_permissions': QtCore.QVariant( - second_permissions.value), - 'second_text': QtCore.QVariant( - self.second_search_results[count].text) - } + data[u'second_text'] = QtCore.QVariant( + self.second_search_results[count].text) except IndexError: + log.exception(u'The second_search_results does not have as ' + 'many verses as the search_results.') break bible_text = u' %s %d%s%d (%s, %s)' % (verse.book.name, - verse.chapter, verse_separator, verse.verse, version.value, - second_version.value) + verse.chapter, verse_separator, verse.verse, version, + second_version) else: - vdict = { - 'book': QtCore.QVariant(verse.book.name), - 'chapter': QtCore.QVariant(verse.chapter), - 'verse': QtCore.QVariant(verse.verse), - 'bible': QtCore.QVariant(bible), - 'version': QtCore.QVariant(version.value), - 'copyright': QtCore.QVariant(copyright.value), - 'permissions': QtCore.QVariant(permissions.value), - 'text': QtCore.QVariant(verse.text), - 'second_bible': QtCore.QVariant(u''), - 'second_version': QtCore.QVariant(u''), - 'second_copyright': QtCore.QVariant(u''), - 'second_permissions': QtCore.QVariant(u''), - 'second_text': QtCore.QVariant(u'') - } bible_text = u'%s %d%s%d (%s)' % (verse.book.name, - verse.chapter, verse_separator, verse.verse, version.value) + verse.chapter, verse_separator, verse.verse, version) bible_verse = QtGui.QListWidgetItem(bible_text) - bible_verse.setData(QtCore.Qt.UserRole, QtCore.QVariant(vdict)) + bible_verse.setData(QtCore.Qt.UserRole, QtCore.QVariant(data)) self.listView.addItem(bible_verse) self.listView.selectAll() self.search_results = {} @@ -810,11 +798,9 @@ class BibleMediaItem(MediaManagerItem): else: verse_text = unicode(verse) if self.settings.display_style == DisplayStyle.Round: - verse_text = u'{su}(' + verse_text + u'){/su}' - elif self.settings.display_style == DisplayStyle.Curly: - verse_text = u'{su}{' + verse_text + u'}{/su}' - elif self.settings.display_style == DisplayStyle.Square: - verse_text = u'{su}[' + verse_text + u']{/su}' - else: - verse_text = u'{su}' + verse_text + u'{/su}' - return verse_text + return u'{su}(%s){/su}' % verse_text + if self.settings.display_style == DisplayStyle.Curly: + return u'{su}{%s}{/su}' % verse_text + if self.settings.display_style == DisplayStyle.Square: + return u'{su}[%s]{/su}' % verse_text + return u'{su}%s{/su}' % verse_text diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 26229aed6..6009ff906 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -189,7 +189,7 @@ class PresentationMediaItem(MediaManagerItem): icon = build_icon(u':/general/general_delete.png') else: critical_error_message_box( - self, translate('PresentationPlugin.MediaItem', + translate('PresentationPlugin.MediaItem', 'Unsupported File'), translate('PresentationPlugin.MediaItem', 'This type of presentation is not supported.')) diff --git a/openlp/plugins/remotes/html/openlp.js b/openlp/plugins/remotes/html/openlp.js index c4eeb4511..cff36b42c 100644 --- a/openlp/plugins/remotes/html/openlp.js +++ b/openlp/plugins/remotes/html/openlp.js @@ -93,7 +93,6 @@ window.OpenLP = { }, setSlide: function (event) { var slide = OpenLP.getElement(event); - console.log(slide); var id = slide.attr("value"); var text = JSON.stringify({"request": {"id": id}}); $.getJSON( diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 4149548b3..87e28591e 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -68,6 +68,7 @@ from lxml import etree, objectify from openlp.plugins.songs.lib import clean_song, VerseType from openlp.plugins.songs.lib.db import Author, Book, Song, Topic +from openlp.core.utils import get_application_version log = logging.getLogger(__name__) @@ -230,8 +231,8 @@ class OpenLyrics(object): # Append the necessary meta data to the song. song_xml.set(u'xmlns', u'http://openlyrics.info/namespace/2009/song') song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION) - song_xml.set(u'createdIn', u'OpenLP 1.9.5') # Use variable - song_xml.set(u'modifiedIn', u'OpenLP 1.9.5') # Use variable + song_xml.set(u'createdIn', get_application_version()[u'version']) + song_xml.set(u'modifiedIn', get_application_version()[u'version']) song_xml.set(u'modifiedDate', datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S')) properties = etree.SubElement(song_xml, u'properties') diff --git a/resources/openlp.desktop b/resources/openlp.desktop index 684119773..07398ede8 100755 --- a/resources/openlp.desktop +++ b/resources/openlp.desktop @@ -3,11 +3,11 @@ Categories=AudioVideo; Comment[de]= Comment= Encoding=UTF-8 -Exec=openlp +Exec=openlp %F GenericName[de]=Church lyrics projection GenericName=Church lyrics projection Icon=openlp -MimeType= +MimeType=application/x-openlp-service; Name[de]=OpenLP Name=OpenLP Path= diff --git a/resources/openlp.xml b/resources/openlp.xml new file mode 100644 index 000000000..215fa2b6e --- /dev/null +++ b/resources/openlp.xml @@ -0,0 +1,26 @@ + + + + + + + OpenLP Service File + + + + + OpenLP Theme File + + + diff --git a/resources/osx/Info.plist.master b/resources/osx/Info.plist.master index e97e1faf5..45aab92a5 100755 --- a/resources/osx/Info.plist.master +++ b/resources/osx/Info.plist.master @@ -18,9 +18,9 @@ MacOS/openlp CFBundleName %(openlp_appname)s -CFBundleGetInfoString +CFBundleGetInfoString %(openlp_appname)s %(openlp_version)s -LSHasLocalizedDisplayName +LSHasLocalizedDisplayName NSAppleScriptEnabled diff --git a/resources/osx/Makefile b/resources/osx/Makefile index 422749da5..738a27aaa 100644 --- a/resources/osx/Makefile +++ b/resources/osx/Makefile @@ -25,4 +25,4 @@ clean: rm -rf OpenLP.app rm -f warnopenlp.txt rm -f *dmg - + diff --git a/resources/osx/build.py b/resources/osx/build.py index 4ffae789b..93fe453ad 100644 --- a/resources/osx/build.py +++ b/resources/osx/build.py @@ -82,6 +82,7 @@ import ConfigParser import logging import optparse import sys +import glob import platform import re import subprocess as subp @@ -122,6 +123,15 @@ def build_application(settings, app_name_lower, app_dir): 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 \ @@ -151,6 +161,19 @@ def build_application(settings, app_name_lower, app_dir): failed!', script_name) sys.exit(1) + logging.info('[%s] copying the translations...', script_name) + os.mkdir(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) + def deploy_qt(settings): logging.info('[%s] running mac deploy qt on %s.app...', script_name, settings['openlp_appname']);