Merge with trunk.

This commit is contained in:
Martin Zibricky 2012-10-18 00:20:22 +02:00
commit fa7d3142db
12 changed files with 158 additions and 60 deletions

View File

@ -85,6 +85,14 @@ class PluginManager(object):
log.debug(u'finding plugins in %s at depth %d',
unicode(plugin_dir), startdepth)
for root, dirs, files in os.walk(plugin_dir):
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it. The following code will hide it
# in settings dialog.
if sys.platform == 'darwin':
present_plugin_dir = os.path.join(plugin_dir, 'presentations')
# Ignore files from the presentation plugin directory.
if root.startswith(present_plugin_dir):
continue
for name in files:
if name.endswith(u'.py') and not name.startswith(u'__'):
path = os.path.abspath(os.path.join(root, name))

View File

@ -82,13 +82,13 @@ class Ui_FirstTimeWizard(object):
self.imageCheckBox.setChecked(True)
self.imageCheckBox.setObjectName(u'imageCheckBox')
self.pluginLayout.addWidget(self.imageCheckBox)
self.presentationCheckBox = QtGui.QCheckBox(self.pluginPage)
if sys.platform == "darwin":
self.presentationCheckBox.setChecked(False)
else:
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it.
if sys.platform != 'darwin':
self.presentationCheckBox = QtGui.QCheckBox(self.pluginPage)
self.presentationCheckBox.setChecked(True)
self.presentationCheckBox.setObjectName(u'presentationCheckBox')
self.pluginLayout.addWidget(self.presentationCheckBox)
self.presentationCheckBox.setObjectName(u'presentationCheckBox')
self.pluginLayout.addWidget(self.presentationCheckBox)
self.mediaCheckBox = QtGui.QCheckBox(self.pluginPage)
self.mediaCheckBox.setChecked(True)
self.mediaCheckBox.setObjectName(u'mediaCheckBox')
@ -214,10 +214,11 @@ class Ui_FirstTimeWizard(object):
self.bibleCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Bible'))
self.imageCheckBox.setText(translate('OpenLP.FirstTimeWizard',
'Images'))
self.presentationCheckBox.setText(translate('OpenLP.FirstTimeWizard',
'Presentations'))
if sys.platform == "darwin":
self.presentationCheckBox.setEnabled(False)
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it.
if sys.platform != 'darwin':
self.presentationCheckBox.setText(translate('OpenLP.FirstTimeWizard',
'Presentations'))
self.mediaCheckBox.setText(translate('OpenLP.FirstTimeWizard',
'Media (Audio and Video)'))
self.remoteCheckBox.setText(translate('OpenLP.FirstTimeWizard',

View File

@ -284,6 +284,7 @@ class ThemeManager(QtGui.QWidget):
plugin.renameTheme(old_theme_name, new_theme_name)
self.mainwindow.renderer.update_theme(
new_theme_name, old_theme_name)
self.loadThemes()
def onCopyTheme(self):
"""

View File

@ -498,8 +498,13 @@ def locale_compare(string1, string2):
"""
# Function locale.strcol() from standard Python library does not work
# properly on Windows and probably somewhere else.
return int(QtCore.QString.localeAwareCompare(
QtCore.QString(string1).toLower(), QtCore.QString(string2).toLower()))
return QtCore.QString.localeAwareCompare(string1.lower(), string2.lower())
# For performance reasons provide direct reference to compare function
# without wrapping it in another function making te string lowercase.
# This is needed for sorting songs.
locale_direct_compare = QtCore.QString.localeAwareCompare
from languagemanager import LanguageManager
@ -508,4 +513,5 @@ from actions import ActionList
__all__ = [u'AppLocation', u'get_application_version', u'check_latest_version',
u'add_actions', u'get_filesystem_encoding', u'LanguageManager',
u'ActionList', u'get_web_page', u'get_uno_command', u'get_uno_instance',
u'delete_file', u'clean_filename', u'format_time', u'locale_compare']
u'delete_file', u'clean_filename', u'format_time', u'locale_compare',
u'locale_direct_compare']

View File

@ -153,7 +153,7 @@ class ImpressController(PresentationController):
desktop = None
try:
desktop = self.manager.createInstance(u'com.sun.star.frame.Desktop')
except AttributeError:
except (AttributeError, pywintypes.com_error):
log.warn(u'Failure to find desktop - Impress may have closed')
return desktop if desktop else None
@ -284,6 +284,8 @@ class ImpressDocument(PresentationDocument):
props = tuple(props)
doc = self.document
pages = doc.getDrawPages()
if not pages:
return
if not os.path.isdir(self.get_temp_folder()):
os.makedirs(self.get_temp_folder())
for idx in range(pages.getCount()):
@ -359,7 +361,7 @@ class ImpressDocument(PresentationDocument):
log.debug(u'is active OpenOffice')
if not self.is_loaded():
return False
return self.control is not None
return self.control.isRunning() if self.control else False
def unblank_screen(self):
"""
@ -380,7 +382,7 @@ class ImpressDocument(PresentationDocument):
Returns true if screen is blank
"""
log.debug(u'is blank OpenOffice')
if self.control:
if self.control and self.control.isRunning():
return self.control.isPaused()
else:
return False
@ -436,7 +438,11 @@ class ImpressDocument(PresentationDocument):
"""
Triggers the next effect of slide on the running presentation
"""
is_paused = self.control.isPaused()
self.control.gotoNextEffect()
time.sleep(0.1)
if not is_paused and self.control.isPaused():
self.control.gotoPreviousEffect()
def previous_step(self):
"""

View File

@ -49,6 +49,7 @@ class Controller(object):
"""
self.is_live = live
self.doc = None
self.hide_mode = None
log.info(u'%s controller loaded' % live)
def add_handler(self, controller, file, hide_mode, slide_no):
@ -67,6 +68,7 @@ class Controller(object):
# Inform slidecontroller that the action failed?
return
self.doc.slidenumber = slide_no
self.hide_mode = hide_mode
if self.is_live:
if hide_mode == HideMode.Screen:
Receiver.send_message(u'live_display_hide', HideMode.Screen)
@ -78,7 +80,7 @@ class Controller(object):
else:
self.doc.start_presentation()
Receiver.send_message(u'live_display_hide', HideMode.Screen)
self.doc.slidenumber = 0
self.doc.slidenumber = 1
if slide_no > 1:
self.slide(slide_no)
@ -88,100 +90,134 @@ class Controller(object):
Use the last slide number.
"""
log.debug(u'Live = %s, activate' % self.is_live)
if not self.doc:
return False
if self.doc.is_active():
return
return True
if not self.doc.is_loaded():
if not self.doc.load_presentation():
return
log.warn(u'Failed to activate %s' % self.doc.filepath)
return False
if self.is_live:
self.doc.start_presentation()
if self.doc.slidenumber > 1:
if self.doc.slidenumber > self.doc.get_slide_count():
self.doc.slidenumber = self.doc.get_slide_count()
self.doc.goto_slide(self.doc.slidenumber)
if self.doc.is_active():
return True
else:
log.warn(u'Failed to activate %s' % self.doc.filepath)
return False
def slide(self, slide):
"""
Go to a specific slide
"""
log.debug(u'Live = %s, slide' % self.is_live)
if not self.doc:
return
if not self.is_live:
return
if self.doc.is_blank():
if self.hide_mode:
self.doc.slidenumber = int(slide) + 1
self.poll()
return
if not self.activate():
return
self.activate()
self.doc.goto_slide(int(slide) + 1)
self.doc.poll_slidenumber(self.is_live)
self.poll()
def first(self):
"""
Based on the handler passed at startup triggers the first slide
"""
log.debug(u'Live = %s, first' % self.is_live)
if not self.doc:
return
if not self.is_live:
return
if self.doc.is_blank():
if self.hide_mode:
self.doc.slidenumber = 1
self.poll()
return
if not self.activate():
return
self.activate()
self.doc.start_presentation()
self.doc.poll_slidenumber(self.is_live)
self.poll()
def last(self):
"""
Based on the handler passed at startup triggers the last slide
"""
log.debug(u'Live = %s, last' % self.is_live)
if not self.doc:
return
if not self.is_live:
return
if self.doc.is_blank():
if self.hide_mode:
self.doc.slidenumber = self.doc.get_slide_count()
self.poll()
return
if not self.activate():
return
self.activate()
self.doc.goto_slide(self.doc.get_slide_count())
self.doc.poll_slidenumber(self.is_live)
self.poll()
def next(self):
"""
Based on the handler passed at startup triggers the next slide event
"""
log.debug(u'Live = %s, next' % self.is_live)
if not self.doc:
return
if not self.is_live:
return
if self.doc.is_blank():
if self.hide_mode:
if not self.doc.is_active():
return
if self.doc.slidenumber < self.doc.get_slide_count():
self.doc.slidenumber = self.doc.slidenumber + 1
self.poll()
return
if not self.activate():
return
# The "End of slideshow" screen is after the last slide
# Note, we can't just stop on the last slide, since it may
# contain animations that need to be stepped through.
if self.doc.slidenumber > self.doc.get_slide_count():
return
self.activate()
self.doc.next_step()
self.doc.poll_slidenumber(self.is_live)
self.poll()
def previous(self):
"""
Based on the handler passed at startup triggers the previous slide event
"""
log.debug(u'Live = %s, previous' % self.is_live)
if not self.doc:
return
if not self.is_live:
return
if self.doc.is_blank():
if self.hide_mode:
if not self.doc.is_active():
return
if self.doc.slidenumber > 1:
self.doc.slidenumber = self.doc.slidenumber - 1
self.poll()
return
if not self.activate():
return
self.activate()
self.doc.previous_step()
self.doc.poll_slidenumber(self.is_live)
self.poll()
def shutdown(self):
"""
Based on the handler passed at startup triggers slide show to shut down
"""
log.debug(u'Live = %s, shutdown' % self.is_live)
if not self.doc:
return
self.doc.close_presentation()
self.doc = None
@ -190,21 +226,30 @@ class Controller(object):
Instruct the controller to blank the presentation
"""
log.debug(u'Live = %s, blank' % self.is_live)
self.hide_mode = hide_mode
if not self.doc:
return
if not self.is_live:
return
if not self.doc.is_loaded():
return
if not self.doc.is_active():
return
if hide_mode == HideMode.Theme:
if not self.doc.is_loaded():
return
if not self.doc.is_active():
return
Receiver.send_message(u'live_display_hide', HideMode.Theme)
self.doc.blank_screen()
elif hide_mode == HideMode.Blank:
if not self.activate():
return
self.doc.blank_screen()
def stop(self):
"""
Instruct the controller to stop and hide the presentation
"""
log.debug(u'Live = %s, stop' % self.is_live)
self.hide_mode = HideMode.Screen
if not self.doc:
return
if not self.is_live:
return
if not self.doc.is_loaded():
@ -218,9 +263,13 @@ class Controller(object):
Instruct the controller to unblank the presentation
"""
log.debug(u'Live = %s, unblank' % self.is_live)
self.hide_mode = None
if not self.doc:
return
if not self.is_live:
return
self.activate()
if not self.activate():
return
if self.doc.slidenumber and \
self.doc.slidenumber != self.doc.get_slide_number():
self.doc.goto_slide(self.doc.slidenumber)
@ -228,7 +277,9 @@ class Controller(object):
Receiver.send_message(u'live_display_hide', HideMode.Screen)
def poll(self):
self.doc.poll_slidenumber(self.is_live)
if not self.doc:
return
self.doc.poll_slidenumber(self.is_live, self.hide_mode)
class MessageListener(object):

View File

@ -94,9 +94,9 @@ class PowerpointController(PresentationController):
self.docs[0].close_presentation()
if self.process is None:
return
if self.process.Presentations.Count > 0:
return
try:
if self.process.Presentations.Count > 0:
return
self.process.Quit()
except pywintypes.com_error:
pass
@ -210,6 +210,13 @@ class PowerpointDocument(PresentationDocument):
self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.State = 1
self.presentation.SlideShowWindow.Activate()
if self.presentation.Application.Version == u'14.0':
# Unblanking is broken in PowerPoint 2010, need to redisplay
slide = self.presentation.SlideShowWindow.View.CurrentShowPosition
click = self.presentation.SlideShowWindow.View.GetClickIndex()
self.presentation.SlideShowWindow.View.GotoSlide(slide)
if click:
self.presentation.SlideShowWindow.View.GotoClick(click)
def blank_screen(self):
"""
@ -253,6 +260,8 @@ class PowerpointDocument(PresentationDocument):
renderer = self.controller.plugin.renderer
rect = renderer.screens.current[u'size']
ppt_window = self.presentation.SlideShowSettings.Run()
if not ppt_window:
return
ppt_window.Top = rect.y() * 72 / dpi
ppt_window.Height = rect.height() * 72 / dpi
ppt_window.Left = rect.x() * 72 / dpi
@ -286,6 +295,8 @@ class PowerpointDocument(PresentationDocument):
"""
log.debug(u'next_step')
self.presentation.SlideShowWindow.View.Next()
if self.get_slide_number() > self.get_slide_count():
self.previous_step()
def previous_step(self):
"""

View File

@ -260,16 +260,17 @@ class PresentationDocument(object):
else:
return None
def poll_slidenumber(self, is_live):
def poll_slidenumber(self, is_live, hide_mode):
"""
Check the current slide number
"""
if not self.is_active():
return
current = self.get_slide_number()
if current == self.slidenumber:
return
self.slidenumber = current
if not hide_mode:
current = self.get_slide_number()
if current == self.slidenumber:
return
self.slidenumber = current
if is_live:
prefix = u'live'
else:

View File

@ -37,6 +37,7 @@ from openlp.core.lib import build_icon, Receiver, SettingsManager, translate, \
create_separated_list
from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.utils import locale_direct_compare
from openlp.plugins.songs.lib.db import Song
from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
@ -251,7 +252,8 @@ class SongExportForm(OpenLPWizard):
# Load the list of songs.
Receiver.send_message(u'cursor_busy')
songs = self.plugin.manager.get_all_objects(Song)
songs.sort()
songs.sort(
cmp=locale_direct_compare, key=lambda song: song.sort_string)
for song in songs:
# No need to export temporary songs.
if song.temporary:

View File

@ -31,11 +31,11 @@ the Songs plugin
"""
from sqlalchemy import Column, ForeignKey, Table, types
from sqlalchemy.orm import mapper, relation
from sqlalchemy.orm import mapper, relation, reconstructor
from sqlalchemy.sql.expression import func
from PyQt4 import QtCore
from openlp.core.lib.db import BaseModel, init_db
from openlp.core.utils import locale_compare
class Author(BaseModel):
"""
@ -64,14 +64,22 @@ class Song(BaseModel):
"""
Song model
"""
# By default sort the songs by its title considering language specific
# characters.
def __lt__(self, other):
r = locale_compare(self.title, other.title)
return True if r < 0 else False
def __init__(self):
self.sort_string = ''
def __eq__(self, other):
return 0 == locale_compare(self.title, other.title)
# This decorator tells sqlalchemy to call this method everytime
# any data on this object are updated.
@reconstructor
def init_on_load(self):
"""
Precompute string to be used for sorting.
Song sorting is performance sensitive operation.
To get maximum speed lets precompute the string
used for comparison.
"""
# Avoid the overhead of converting string to lowercase and to QString
self.sort_string = QtCore.QString(self.title.lower())
class Topic(BaseModel):

View File

@ -39,7 +39,7 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
check_directory_exists
from openlp.core.lib.ui import UiStrings, create_widget_action
from openlp.core.lib.settings import Settings
from openlp.core.utils import AppLocation
from openlp.core.utils import AppLocation, locale_direct_compare
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm, SongExportForm
from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, \
@ -259,7 +259,8 @@ class SongMediaItem(MediaManagerItem):
log.debug(u'display results Song')
self.saveAutoSelectId()
self.listView.clear()
searchresults.sort()
searchresults.sort(
cmp=locale_direct_compare, key=lambda song: song.sort_string)
for song in searchresults:
# Do not display temporary songs
if song.temporary:

View File

@ -27,6 +27,7 @@
###############################################################################
import logging
import os
import time
from PyQt4 import QtCore
@ -123,6 +124,7 @@ class OooImport(SongImport):
try:
uno_instance = get_uno_instance(resolver)
except NoConnectException:
time.sleep(0.1)
log.exception("Failed to resolve uno connection")
self.startOooProcess()
loop += 1