This commit is contained in:
Tim Bentley 2011-05-25 18:13:10 +01:00
commit b3158b7f84
13 changed files with 148 additions and 63 deletions

View File

@ -85,10 +85,13 @@ class OpenLP(QtGui.QApplication):
QtGui.QApplication.exec_() QtGui.QApplication.exec_()
self.sharedMemory.detach() self.sharedMemory.detach()
def run(self): def run(self, args):
""" """
Run the OpenLP application. Run the OpenLP application.
""" """
# On Windows, the args passed into the constructor are
# ignored. Not very handy, so set the ones we want to use.
self.args = args
# provide a listener for widgets to reqest a screen update. # provide a listener for widgets to reqest a screen update.
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_process_events'), self.processEvents) QtCore.SIGNAL(u'openlp_process_events'), self.processEvents)
@ -115,7 +118,7 @@ class OpenLP(QtGui.QApplication):
# make sure Qt really display the splash screen # make sure Qt really display the splash screen
self.processEvents() self.processEvents()
# start the main app window # start the main app window
self.mainWindow = MainWindow(self.clipboard(), self.arguments()) self.mainWindow = MainWindow(self.clipboard(), self.args)
self.mainWindow.show() self.mainWindow.show()
if show_splash: if show_splash:
# now kill the splashscreen # now kill the splashscreen
@ -250,7 +253,7 @@ def main():
log.debug(u'Could not find default_translator.') log.debug(u'Could not find default_translator.')
if not options.no_error_form: if not options.no_error_form:
sys.excepthook = app.hookException sys.excepthook = app.hookException
sys.exit(app.run()) sys.exit(app.run(qt_args))
if __name__ == u'__main__': if __name__ == u'__main__':
""" """

View File

@ -515,18 +515,18 @@ class MediaManagerItem(QtGui.QWidget):
# multiple service items? # multiple service items?
if self.singleServiceItem or self.remoteTriggered: if self.singleServiceItem or self.remoteTriggered:
log.debug(u'%s Add requested', self.plugin.name) log.debug(u'%s Add requested', self.plugin.name)
serviceItem = self.buildServiceItem(None, True) self.addToService(replace=self.remoteTriggered)
if serviceItem:
serviceItem.from_plugin = False
self.parent.serviceManager.addServiceItem(serviceItem,
replace=self.remoteTriggered)
else: else:
items = self.listView.selectedIndexes() items = self.listView.selectedIndexes()
for item in items: for item in items:
serviceItem = self.buildServiceItem(item, True) self.addToService(item)
if serviceItem:
serviceItem.from_plugin = False def addToService(self, item=None, replace=None):
self.parent.serviceManager.addServiceItem(serviceItem) serviceItem = self.buildServiceItem(item, True)
if serviceItem:
serviceItem.from_plugin = False
self.parent.serviceManager.addServiceItem(serviceItem,
replace=replace)
def onAddEditClick(self): def onAddEditClick(self):
""" """

View File

@ -335,7 +335,7 @@ class Renderer(object):
styled_text += styled_line styled_text += styled_line
html = self.page_shell + styled_text + HTML_END html = self.page_shell + styled_text + HTML_END
self.web.setHtml(html) self.web.setHtml(html)
# Text too long so go to next page # Text too long so go to next page.
if self.web_frame.contentsSize().height() > self.page_height: if self.web_frame.contentsSize().height() > self.page_height:
if force_page and line_count > 0: if force_page and line_count > 0:
Receiver.send_message(u'theme_line_count', line_count) Receiver.send_message(u'theme_line_count', line_count)
@ -366,7 +366,7 @@ class Renderer(object):
""" """
log.debug(u'_paginate_slide_words - Start') log.debug(u'_paginate_slide_words - Start')
line_end = u'' line_end = u' '
if line_break: if line_break:
line_end = u'<br>' line_end = u'<br>'
formatted = [] formatted = []
@ -374,10 +374,11 @@ class Renderer(object):
previous_raw = u'' previous_raw = u''
lines = text.split(u'\n') lines = text.split(u'\n')
for line in lines: for line in lines:
line = line.strip()
styled_line = expand_tags(line) styled_line = expand_tags(line)
html = self.page_shell + previous_html + styled_line + HTML_END html = self.page_shell + previous_html + styled_line + HTML_END
self.web.setHtml(html) self.web.setHtml(html)
# Text too long so go to next page # Text too long so go to next page.
if self.web_frame.contentsSize().height() > self.page_height: if self.web_frame.contentsSize().height() > self.page_height:
# Check if there was a verse before the current one and append # Check if there was a verse before the current one and append
# it, when it fits on the page. # it, when it fits on the page.
@ -401,24 +402,56 @@ class Renderer(object):
previous_html = styled_line + line_end previous_html = styled_line + line_end
previous_raw = line + line_end previous_raw = line + line_end
continue continue
words = self._words_split(line) # Figure out how many words of the line will fit on screen by
for word in words: # using the algorithm known as "binary chop".
styled_word = expand_tags(word) raw_words = self._words_split(line)
html = self.page_shell + previous_html + styled_word + \ html_words = [expand_tags(word) for word in raw_words]
HTML_END smallest_index = 0
highest_index = len(html_words) - 1
index = int(highest_index / 2)
while True:
html = self.page_shell + previous_html + \
u''.join(html_words[:index + 1]).strip() + HTML_END
self.web.setHtml(html) self.web.setHtml(html)
# Text too long so go to next page
if self.web_frame.contentsSize().height() > \ if self.web_frame.contentsSize().height() > \
self.page_height: self.page_height:
while previous_raw.endswith(u'<br>'): # We know that it does not fit, so change/calculate the
previous_raw = previous_raw[:-4] # new index and highest_index accordingly.
formatted.append(previous_raw) highest_index = index
index = int(index - (index - smallest_index) / 2)
else:
smallest_index = index
index = int(index + (highest_index - index) / 2)
# We found the number of words which will fit.
if smallest_index == index or highest_index == index:
index = smallest_index
formatted.append(previous_raw.rstrip(u'<br>') +
u''.join(raw_words[:index + 1]))
previous_html = u'' previous_html = u''
previous_raw = u'' previous_raw = u''
previous_html += styled_word else:
previous_raw += word continue
previous_html += line_end # Check if the rest of the line fits on the slide. If it
previous_raw += line_end # does we do not have to do the much more intensive "word by
# word" checking.
html = self.page_shell + \
u''.join(html_words[index + 1:]).strip() + HTML_END
self.web.setHtml(html)
if self.web_frame.contentsSize().height() <= \
self.page_height:
previous_html = \
u''.join(html_words[index + 1:]).strip() + line_end
previous_raw = \
u''.join(raw_words[index + 1:]).strip() + line_end
break
else:
# The other words do not fit, thus reset the indexes,
# create a new list and continue with "word by word".
raw_words = raw_words[index + 1:]
html_words = html_words[index + 1:]
smallest_index = 0
highest_index = len(html_words) - 1
index = int(highest_index / 2)
else: else:
previous_html += styled_line + line_end previous_html += styled_line + line_end
previous_raw += line + line_end previous_raw += line + line_end

View File

@ -625,11 +625,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
if self.liveController.display.isVisible(): if self.liveController.display.isVisible():
self.liveController.display.setFocus() self.liveController.display.setFocus()
self.activateWindow() self.activateWindow()
# On Windows, arguments contains the entire commandline if len(self.arguments):
# So args[0]=='python' args[1]=='openlp.pyw'
# Therefore this approach is not going to work
# Bypass for now.
if len(self.arguments) and os.name != u'nt':
args = [] args = []
for a in self.arguments: for a in self.arguments:
args.extend([a]) args.extend([a])

View File

@ -757,7 +757,7 @@ class ServiceManager(QtGui.QWidget):
""" """
Called by a signal to select a specific item. Called by a signal to select a specific item.
""" """
self.setItem(int(message[0])) self.setItem(int(message))
def setItem(self, index): def setItem(self, index):
""" """

View File

@ -25,6 +25,7 @@
############################################################################### ###############################################################################
import logging import logging
import locale
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from sqlalchemy.sql import or_, func from sqlalchemy.sql import or_, func
@ -133,15 +134,19 @@ class CustomMediaItem(MediaManagerItem):
self.onPreviewClick() self.onPreviewClick()
self.onRemoteEditClear() self.onRemoteEditClear()
def loadList(self, list): def loadList(self, custom_slides):
self.listView.clear() self.listView.clear()
for customSlide in list: # Sort the customs by its title considering language specific
custom_name = QtGui.QListWidgetItem(customSlide.title) # characters. lower() is needed for windows!
custom_slides.sort(
cmp=locale.strcoll, key=lambda custom: custom.title.lower())
for custom_slide in custom_slides:
custom_name = QtGui.QListWidgetItem(custom_slide.title)
custom_name.setData( custom_name.setData(
QtCore.Qt.UserRole, QtCore.QVariant(customSlide.id)) QtCore.Qt.UserRole, QtCore.QVariant(custom_slide.id))
self.listView.addItem(custom_name) self.listView.addItem(custom_name)
# Auto-select the item if name has been set # Auto-select the item if name has been set
if customSlide.title == self.autoSelectItem : if custom_slide.title == self.autoSelectItem:
self.listView.setCurrentItem(custom_name) self.listView.setCurrentItem(custom_name)
def onNewClick(self): def onNewClick(self):

View File

@ -149,20 +149,18 @@ class PresentationMediaItem(MediaManagerItem):
else: else:
self.presentationWidget.hide() self.presentationWidget.hide()
def loadList(self, list, initialLoad=False): def loadList(self, files, initialLoad=False):
""" """
Add presentations into the media manager Add presentations into the media manager
This is called both on initial load of the plugin to populate with This is called both on initial load of the plugin to populate with
existing files, and when the user adds new files via the media manager existing files, and when the user adds new files via the media manager
""" """
currlist = self.getFileList() currlist = self.getFileList()
titles = [] titles = [os.path.split(file)[1] for file in currlist]
for file in currlist:
titles.append(os.path.split(file)[1])
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
if not initialLoad: if not initialLoad:
self.parent.formparent.displayProgressBar(len(list)) self.parent.formparent.displayProgressBar(len(files))
for file in list: for file in files:
if not initialLoad: if not initialLoad:
self.parent.formparent.incrementProgressBar() self.parent.formparent.incrementProgressBar()
if currlist.count(file) > 0: if currlist.count(file) > 0:

View File

@ -219,10 +219,11 @@ window.OpenLP = {
} }
else { else {
$.each(data.results.items, function (idx, value) { $.each(data.results.items, function (idx, value) {
var li = $("<li data-icon=\"false\">").append( var item = $("<li>").text(value[1]);
$("<a href=\"#\">").attr("value", value[0]).text(value[1])); var golive = $("<a href=\"#\">Go Live</a>").attr("value", value[0]).click(OpenLP.goLive);
li.children("a").click(OpenLP.goLive); var additem = $("<a href=\"#\">Add To Service</a>").attr("value", value[0]).click(OpenLP.addToService);
ul.append(li); item.append($("<ul>").append($("<li>").append(golive)).append($("<li>").append(additem)));
ul.append(item);
}); });
} }
ul.listview("refresh"); ul.listview("refresh");
@ -231,16 +232,28 @@ window.OpenLP = {
return false; return false;
}, },
goLive: function (event) { goLive: function (event) {
var slide = OpenLP.getElement(event); var item = OpenLP.getElement(event);
var id = slide.attr("value"); var id = item.attr("value");
var text = JSON.stringify({"request": {"id": id}}); var text = JSON.stringify({"request": {"id": id}});
$.getJSON( $.getJSON(
"/api/" + $("#search-plugin").val() + "/live", "/api/" + $("#search-plugin").val() + "/live",
{"data": text}) {"data": text})
$.mobile.changePage("slide-controller"); $.mobile.changePage("slide-controller");
return false; return false;
},
addToService: function (event) {
var item = OpenLP.getElement(event);
var id = item.attr("value");
var text = JSON.stringify({"request": {"id": id}});
$.getJSON(
"/api/" + $("#search-plugin").val() + "/add",
{"data": text},
function () {
history.back();
}
);
return false;
} }
} }
// Service Manager // Service Manager
$("#service-manager").live("pagebeforeshow", OpenLP.loadService); $("#service-manager").live("pagebeforeshow", OpenLP.loadService);

View File

@ -253,7 +253,8 @@ class HttpConnection(object):
(r'^/api/alert$', self.alert), (r'^/api/alert$', self.alert),
(r'^/api/plugin/(search)$', self.pluginInfo), (r'^/api/plugin/(search)$', self.pluginInfo),
(r'^/api/(.*)/search$', self.search), (r'^/api/(.*)/search$', self.search),
(r'^/api/(.*)/live$', self.go_live) (r'^/api/(.*)/live$', self.go_live),
(r'^/api/(.*)/add$', self.add_to_service)
] ]
QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'), QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'),
self.ready_read) self.ready_read)
@ -490,6 +491,16 @@ class HttpConnection(object):
if plugin.status == PluginStatus.Active and plugin.mediaItem: if plugin.status == PluginStatus.Active and plugin.mediaItem:
plugin.mediaItem.goLive(id) plugin.mediaItem.goLive(id)
def add_to_service(self, type):
"""
Add item of type ``type`` to the end of the service
"""
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
plugin = self.parent.parent.pluginManager.get_plugin_by_name(type)
if plugin.status == PluginStatus.Active and plugin.mediaItem:
item_id = plugin.mediaItem.createItemFromId(id)
plugin.mediaItem.addToService(item_id)
def send_response(self, response): def send_response(self, response):
http = u'HTTP/1.1 %s\r\n' % response.code http = u'HTTP/1.1 %s\r\n' % response.code
for header, value in response.headers.iteritems(): for header, value in response.headers.iteritems():

View File

@ -229,7 +229,10 @@ class SongMediaItem(MediaManagerItem):
def displayResultsSong(self, searchresults): def displayResultsSong(self, searchresults):
log.debug(u'display results Song') log.debug(u'display results Song')
self.listView.clear() self.listView.clear()
searchresults.sort(cmp=self.collateSongTitles) # Sort the songs by its title considering language specific characters.
# lower() is needed for windows!
searchresults.sort(
cmp=locale.strcoll, key=lambda song: song.title.lower())
for song in searchresults: for song in searchresults:
author_list = [author.display_name for author in song.authors] author_list = [author.display_name for author in song.authors]
song_title = unicode(song.title) song_title = unicode(song.title)
@ -475,13 +478,6 @@ class SongMediaItem(MediaManagerItem):
Receiver.send_message(u'service_item_update', Receiver.send_message(u'service_item_update',
u'%s:%s' % (editId, item._uuid)) u'%s:%s' % (editId, item._uuid))
def collateSongTitles(self, song_1, song_2):
"""
Locale aware collation of song titles
"""
return locale.strcoll(unicode(song_1.title.lower()),
unicode(song_2.title.lower()))
def search(self, string): def search(self, string):
""" """
Search for some songs Search for some songs

View File

@ -16,7 +16,7 @@
; NOTE: The value of AppId uniquely identifies this application. ; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications. ; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{AA7699FA-B2D2-43F4-8A70-D497D03C9485} AppID={{AA7699FA-B2D2-43F4-8A70-D497D03C9485}
AppName={#AppName} AppName={#AppName}
AppVerName={#AppVerName} AppVerName={#AppVerName}
AppPublisher={#AppPublisher} AppPublisher={#AppPublisher}
@ -29,11 +29,12 @@ AllowNoIcons=true
LicenseFile=LICENSE.txt LicenseFile=LICENSE.txt
OutputDir=..\..\dist OutputDir=..\..\dist
OutputBaseFilename=OpenLP-{#RealVersion}-setup OutputBaseFilename=OpenLP-{#RealVersion}-setup
Compression=lzma Compression=lzma/Max
SolidCompression=true SolidCompression=true
SetupIconFile=OpenLP.ico SetupIconFile=OpenLP.ico
WizardImageFile=WizImageBig.bmp WizardImageFile=WizImageBig.bmp
WizardSmallImageFile=WizImageSmall.bmp WizardSmallImageFile=WizImageSmall.bmp
ChangesAssociations=true
[Languages] [Languages]
Name: english; MessagesFile: compiler:Default.isl Name: english; MessagesFile: compiler:Default.isl
@ -64,6 +65,7 @@ Name: quicklaunchicon; Description: {cm:CreateQuickLaunchIcon}; GroupDescription
[Files] [Files]
Source: ..\..\dist\OpenLP\*; DestDir: {app}; Flags: ignoreversion recursesubdirs createallsubdirs Source: ..\..\dist\OpenLP\*; DestDir: {app}; Flags: ignoreversion recursesubdirs createallsubdirs
Source: psvince.dll; Flags: dontcopy
; NOTE: Don't use "Flags: ignoreversion" on any shared system files ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons] [Icons]
@ -79,8 +81,15 @@ Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\{#AppName}; Filenam
Filename: {app}\{#AppExeName}; Description: {cm:LaunchProgram,{#AppName}}; Flags: nowait postinstall skipifsilent Filename: {app}\{#AppExeName}; Description: {cm:LaunchProgram,{#AppName}}; Flags: nowait postinstall skipifsilent
[Registry] [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"""
[Code] [Code]
function IsModuleLoaded(modulename: String ): Boolean;
external 'IsModuleLoaded@files:psvince.dll stdcall';
function GetUninstallString(): String; function GetUninstallString(): String;
var var
sUnInstPath: String; sUnInstPath: String;
@ -121,6 +130,19 @@ begin
Result := 1; Result := 1;
end; end;
function InitializeSetup(): Boolean;
begin
Result := true;
while IsModuleLoaded( '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); procedure CurStepChanged(CurStep: TSetupStep);
begin begin
if (CurStep=ssInstall) then if (CurStep=ssInstall) then

Binary file not shown.

View File

@ -95,6 +95,12 @@ Visual C++ 2008 Express Edition
windows-builder.py windows-builder.py
This script, of course. It should be in the "scripts" directory of OpenLP. 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)
""" """
import os import os
@ -225,6 +231,8 @@ def copy_windows_files():
os.path.join(dist_path, u'OpenLP.ico')) os.path.join(dist_path, u'OpenLP.ico'))
copy(os.path.join(winres_path, u'LICENSE.txt'), copy(os.path.join(winres_path, u'LICENSE.txt'),
os.path.join(dist_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')): if os.path.isfile(os.path.join(helpfile_path, u'Openlp.chm')):
print u' Windows help file found' print u' Windows help file found'
copy(os.path.join(helpfile_path, u'Openlp.chm'), copy(os.path.join(helpfile_path, u'Openlp.chm'),