This commit is contained in:
Tim Bentley 2009-09-24 16:51:19 +01:00
commit 9a4f80f08e
23 changed files with 398 additions and 448 deletions

View File

@ -192,7 +192,7 @@ class Renderer(object):
#Find the next space to the left #Find the next space to the left
pos = line[:pos].rfind(u' ') pos = line[:pos].rfind(u' ')
#no more spaces found #no more spaces found
if pos == 0: if pos == 0:
split_text = line split_text = line
while metrics.width(split_text, -1) > line_width: while metrics.width(split_text, -1) > line_width:
split_text = split_text[:-1] split_text = split_text[:-1]

View File

@ -78,7 +78,8 @@ class RenderManager(object):
log.debug(u'Update Display') log.debug(u'Update Display')
if self.current_display != screen_number: if self.current_display != screen_number:
self.current_display = screen_number self.current_display = screen_number
self.calculate_default(self.screen_list[self.current_display][u'size']) self.calculate_default(
self.screen_list[self.current_display][u'size'])
def set_global_theme(self, global_theme, global_style=u'Global'): def set_global_theme(self, global_theme, global_style=u'Global'):
""" """

View File

@ -101,7 +101,7 @@ class ServiceItem(object):
for line in format: for line in format:
lines += line + u'\n' lines += line + u'\n'
title = lines.split(u'\n')[0] title = lines.split(u'\n')[0]
self.frames.append({u'title': title ,u'text':lines, self.frames.append({u'title': title, u'text': lines,
u'image': frame}) u'image': frame})
elif self.service_item_type == ServiceType.Command: elif self.service_item_type == ServiceType.Command:
self.frames = self.service_frames self.frames = self.service_frames

View File

@ -43,9 +43,9 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.setupUi(self) self.setupUi(self)
#define signals #define signals
#Buttons #Buttons
QtCore.QObject.connect(self.Color1PushButton , QtCore.QObject.connect(self.Color1PushButton,
QtCore.SIGNAL(u'pressed()'), self.onColor1PushButtonClicked) QtCore.SIGNAL(u'pressed()'), self.onColor1PushButtonClicked)
QtCore.QObject.connect(self.Color2PushButton , QtCore.QObject.connect(self.Color2PushButton,
QtCore.SIGNAL(u'pressed()'), self.onColor2PushButtonClicked) QtCore.SIGNAL(u'pressed()'), self.onColor2PushButtonClicked)
QtCore.QObject.connect(self.FontMainColorPushButton, QtCore.QObject.connect(self.FontMainColorPushButton,
QtCore.SIGNAL(u'pressed()'), self.onFontMainColorPushButtonClicked) QtCore.SIGNAL(u'pressed()'), self.onFontMainColorPushButtonClicked)
@ -180,62 +180,18 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
def loadTheme(self, theme): def loadTheme(self, theme):
log.debug(u'LoadTheme %s', theme) log.debug(u'LoadTheme %s', theme)
if theme == None: if theme == None:
self.theme.parse(self.baseTheme()) self.theme.parse(self.thememanager.baseTheme())
else: else:
xml_file = os.path.join(self.path, theme, theme + u'.xml') xml_file = os.path.join(self.path, theme, theme + u'.xml')
xml = file_to_xml(xml_file) xml = file_to_xml(xml_file)
self.theme.parse(xml) self.theme.parse(xml)
self.theme.extend_image_filename(self.path) self.theme.extend_image_filename(self.path)
self.cleanTheme(self.theme) self.thememanager.cleanTheme(self.theme)
self.allowPreview = False self.allowPreview = False
self.paintUi(self.theme) self.paintUi(self.theme)
self.allowPreview = True self.allowPreview = True
self.previewTheme(self.theme) self.previewTheme(self.theme)
def cleanTheme(self, theme):
self.theme.background_color = theme.background_color.strip()
self.theme.background_direction = theme.background_direction.strip()
self.theme.background_endColor = theme.background_endColor.strip()
if theme.background_filename:
self.theme.background_filename = theme.background_filename.strip()
#self.theme.background_mode
self.theme.background_startColor = theme.background_startColor.strip()
#self.theme.background_type
if theme.display_display:
self.theme.display_display = theme.display_display.strip()
self.theme.display_horizontalAlign = \
theme.display_horizontalAlign.strip()
self.theme.display_outline = str_to_bool(theme.display_outline)
#self.theme.display_outline_color
self.theme.display_shadow = str_to_bool(theme.display_shadow)
#self.theme.display_shadow_color
self.theme.display_verticalAlign = \
theme.display_verticalAlign.strip()
self.theme.display_wrapStyle = theme.display_wrapStyle.strip()
self.theme.font_footer_color = theme.font_footer_color.strip()
self.theme.font_footer_height = theme.font_footer_height.strip()
self.theme.font_footer_italics = str_to_bool(theme.font_footer_italics)
self.theme.font_footer_name = theme.font_footer_name.strip()
#self.theme.font_footer_override
self.theme.font_footer_proportion = \
theme.font_footer_proportion.strip()
self.theme.font_footer_weight = theme.font_footer_weight.strip()
self.theme.font_footer_width = theme.font_footer_width.strip()
self.theme.font_footer_x = theme.font_footer_x.strip()
self.theme.font_footer_y = theme.font_footer_y.strip()
self.theme.font_main_color = theme.font_main_color.strip()
self.theme.font_main_height = theme.font_main_height.strip()
self.theme.font_main_italics = str_to_bool(theme.font_main_italics)
self.theme.font_main_name = theme.font_main_name.strip()
#self.theme.font_main_override
self.theme.font_main_proportion = theme.font_main_proportion.strip()
self.theme.font_main_weight = theme.font_main_weight.strip()
self.theme.font_main_x = theme.font_main_x.strip()
self.theme.font_main_y = theme.font_main_y.strip()
#self.theme.theme_mode
self.theme.theme_name = theme.theme_name.strip()
#self.theme.theme_version
def onImageToolButtonClicked(self): def onImageToolButtonClicked(self):
filename = QtGui.QFileDialog.getOpenFileName(self, 'Open file') filename = QtGui.QFileDialog.getOpenFileName(self, 'Open file')
if filename != "": if filename != "":
@ -250,13 +206,13 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.previewTheme(self.theme) self.previewTheme(self.theme)
def onFontMainWeightComboBoxSelected(self, value): def onFontMainWeightComboBoxSelected(self, value):
if value ==0: if value == 0:
self.theme.font_main_weight = u'Normal' self.theme.font_main_weight = u'Normal'
self.theme.font_main_italics = False self.theme.font_main_italics = False
elif value == 1: elif value == 1:
self.theme.font_main_weight = u'Bold' self.theme.font_main_weight = u'Bold'
self.theme.font_main_italics = False self.theme.font_main_italics = False
elif value == 2: elif value == 2:
self.theme.font_main_weight = u'Normal' self.theme.font_main_weight = u'Normal'
self.theme.font_main_italics = True self.theme.font_main_italics = True
else: else:
@ -327,13 +283,13 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
self.previewTheme(self.theme) self.previewTheme(self.theme)
def onFontFooterWeightComboBoxSelected(self, value): def onFontFooterWeightComboBoxSelected(self, value):
if value == 0: if value == 0:
self.theme.font_footer_weight = u'Normal' self.theme.font_footer_weight = u'Normal'
self.theme.font_footer_italics = False self.theme.font_footer_italics = False
elif value == 1: elif value == 1:
self.theme.font_footer_weight = u'Bold' self.theme.font_footer_weight = u'Bold'
self.theme.font_footer_italics = False self.theme.font_footer_italics = False
elif value == 2: elif value == 2:
self.theme.font_footer_weight = u'Normal' self.theme.font_footer_weight = u'Normal'
self.theme.font_footer_italics = True self.theme.font_footer_italics = True
else: else:
@ -508,21 +464,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
# #
#Local Methods #Local Methods
# #
def baseTheme(self):
log.debug(u'base theme created')
newtheme = ThemeXML()
newtheme.new_document(u'New Theme')
newtheme.add_background_solid(unicode(u'#000000'))
newtheme.add_font(unicode(QtGui.QFont().family()), unicode(u'#FFFFFF'),
unicode(30), u'False')
newtheme.add_font(unicode(QtGui.QFont().family()), unicode(u'#FFFFFF'),
unicode(12), u'False', u'footer')
newtheme.add_display(u'False', unicode(u'#FFFFFF'), u'False',
unicode(u'#FFFFFF'),
unicode(0), unicode(0), unicode(0))
return newtheme.extract_xml()
def paintUi(self, theme): def paintUi(self, theme):
self.stateChanging(theme) self.stateChanging(theme)
self.ThemeNameEdit.setText(self.theme.theme_name) self.ThemeNameEdit.setText(self.theme.theme_name)

View File

@ -105,7 +105,7 @@ class ThemeManager(QtGui.QWidget):
if oldName != newName: if oldName != newName:
self.ThemeListWidget.item(count).setText(newName) self.ThemeListWidget.item(count).setText(newName)
#Set the new name #Set the new name
if themeName == newName: if themeName == newName:
name = u'%s (%s)' % (newName, translate(u'ThemeManager', name = u'%s (%s)' % (newName, translate(u'ThemeManager',
u'default')) u'default'))
self.ThemeListWidget.item(count).setText(name) self.ThemeListWidget.item(count).setText(name)
@ -120,7 +120,7 @@ class ThemeManager(QtGui.QWidget):
self.ThemeListWidget.item(count).setText( self.ThemeListWidget.item(count).setText(
unicode(item.data(QtCore.Qt.UserRole).toString())) unicode(item.data(QtCore.Qt.UserRole).toString()))
#Set the new name #Set the new name
if count == index.row(): if count == index.row():
self.global_theme = unicode( self.global_theme = unicode(
self.ThemeListWidget.item(count).text()) self.ThemeListWidget.item(count).text())
name = u'%s (%s)' % (self.global_theme, name = u'%s (%s)' % (self.global_theme,
@ -132,7 +132,6 @@ class ThemeManager(QtGui.QWidget):
self.pushThemes() self.pushThemes()
def onAddTheme(self): def onAddTheme(self):
self.amendThemeForm.theme.parse(self.baseTheme())
self.amendThemeForm.loadTheme(None) self.amendThemeForm.loadTheme(None)
self.amendThemeForm.exec_() self.amendThemeForm.exec_()
@ -158,7 +157,7 @@ class ThemeManager(QtGui.QWidget):
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
else: else:
self.themelist.remove(theme) self.themelist.remove(theme)
th = theme + u'.png' th = theme + u'.png'
row = self.ThemeListWidget.row(item) row = self.ThemeListWidget.row(item)
self.ThemeListWidget.takeItem(row) self.ThemeListWidget.takeItem(row)
try: try:
@ -215,9 +214,8 @@ class ThemeManager(QtGui.QWidget):
def loadThemes(self): def loadThemes(self):
""" """
Loads the theme lists and triggers updates accross Loads the theme lists and triggers updates accross the whole system
the whole system using direct calls or core functions using direct calls or core functions and events for the plugins.
and events for the plugins.
The plugins will call back in to get the real list if they want it. The plugins will call back in to get the real list if they want it.
""" """
log.debug(u'Load themes from dir') log.debug(u'Load themes from dir')

View File

@ -155,13 +155,13 @@ class MigrateSongs():
song.search_title = u'' song.search_title = u''
song.search_lyrics = u'' song.search_lyrics = u''
print songs_temp.songtitle print songs_temp.songtitle
aa = self.session.execute( aa = self.session.execute(
u'select * from songauthors_temp where songid =' + \ u'select * from songauthors_temp where songid =' + \
unicode(songs_temp.songid) ) unicode(songs_temp.songid) )
for row in aa: for row in aa:
a = row['authorid'] a = row['authorid']
authors_temp = self.session.query(TAuthor).get(a) authors_temp = self.session.query(TAuthor).get(a)
bb = self.session.execute( bb = self.session.execute(
u'select * from authors where display_name = \"%s\"' % \ u'select * from authors where display_name = \"%s\"' % \
unicode(authors_temp.authorname) ).fetchone() unicode(authors_temp.authorname) ).fetchone()
if bb is None: if bb is None:

View File

@ -41,7 +41,7 @@ class BibleDBImpl(BibleCommon):
log.debug(u'Load bible %s on path %s', biblename, self.biblefile) log.debug(u'Load bible %s on path %s', biblename, self.biblefile)
db_type = self.config.get_config(u'db type', u'sqlite') db_type = self.config.get_config(u'db type', u'sqlite')
db_url = u'' db_url = u''
if db_type == u'sqlite': if db_type == u'sqlite':
db_url = u'sqlite:///' + self.biblefile db_url = u'sqlite:///' + self.biblefile
else: else:
db_url = u'%s://%s:%s@%s/%s' % \ db_url = u'%s://%s:%s@%s/%s' % \

View File

@ -73,11 +73,11 @@ class BGExtract(BibleCommon):
verseText = '' # clear out string verseText = '' # clear out string
versePos = xml_string.find(u'</span', versePos) versePos = xml_string.find(u'</span', versePos)
i = xml_string.find(VerseSearch, versePos+1) i = xml_string.find(VerseSearch, versePos+1)
#print i , versePos #print i, versePos
if i == -1: if i == -1:
i = xml_string.find(u'</div', versePos+1) i = xml_string.find(u'</div', versePos+1)
j = xml_string.find(u'<strong', versePos+1) j = xml_string.find(u'<strong', versePos+1)
#print i , j #print i, j
if j > 0 and j < i: if j > 0 and j < i:
i = j i = j
verseText = xml_string[versePos + 7 : i ] verseText = xml_string[versePos + 7 : i ]

View File

@ -141,7 +141,7 @@ class BibleOSISImpl():
# first time through # first time through
if book_ptr == None: if book_ptr == None:
# set the max book size depending on the first book read # set the max book size depending on the first book read
if p[0] == u'Gen': if p[0] == u'Gen':
dialogobject.setMax(65) dialogobject.setMax(65)
else: else:
dialogobject.setMax(27) dialogobject.setMax(27)

View File

@ -61,7 +61,7 @@ class BibleManager(object):
# dict of bible database objects # dict of bible database objects
self.bible_db_cache = None self.bible_db_cache = None
# dict of bible http readers # dict of bible http readers
self.bible_http_cache = None self.bible_http_cache = None
self.biblePath = self.config.get_data_path() self.biblePath = self.config.get_data_path()
#get proxy name for screen #get proxy name for screen
self.proxyname = self.config.get_config(u'proxy name') self.proxyname = self.config.get_config(u'proxy name')
@ -83,12 +83,12 @@ class BibleManager(object):
files = self.config.get_files(self.bibleSuffix) files = self.config.get_files(self.bibleSuffix)
log.debug(u'Bible Files %s', files ) log.debug(u'Bible Files %s', files )
self.bible_db_cache = {} self.bible_db_cache = {}
self.bible_http_cache = {} self.bible_http_cache = {}
# books of the bible with testaments # books of the bible with testaments
self.book_testaments = {} self.book_testaments = {}
# books of the bible with abbreviation # books of the bible with abbreviation
self.book_abbreviations = {} self.book_abbreviations = {}
self.web_bibles_present = False self.web_bibles_present = False
for f in files: for f in files:
nme = f.split(u'.') nme = f.split(u'.')
bname = nme[0] bname = nme[0]
@ -97,7 +97,7 @@ class BibleManager(object):
# look to see if lazy load bible exists and get create getter. # look to see if lazy load bible exists and get create getter.
biblesource = self.bible_db_cache[bname].get_meta(u'WEB') biblesource = self.bible_db_cache[bname].get_meta(u'WEB')
if biblesource: if biblesource:
self.web_bibles_present = True self.web_bibles_present = True
nhttp = BibleHTTPImpl() nhttp = BibleHTTPImpl()
# tell The Server where to get the verses from. # tell The Server where to get the verses from.
nhttp.set_bible_source(biblesource.value) nhttp.set_bible_source(biblesource.value)
@ -226,7 +226,7 @@ class BibleManager(object):
viewer. If the database exists it is deleted and the database is viewer. If the database exists it is deleted and the database is
reloaded from scratch. reloaded from scratch.
""" """
log.debug(u'register_OSIS_file_bible %s , %s', biblename, osisfile) log.debug(u'register_OSIS_file_bible %s, %s', biblename, osisfile)
if self._is_new_bible(biblename): if self._is_new_bible(biblename):
# Create new Bible # Create new Bible
nbible = BibleDBImpl(self.biblePath, biblename, self.config) nbible = BibleDBImpl(self.biblePath, biblename, self.config)
@ -240,7 +240,7 @@ class BibleManager(object):
return True return True
else: else:
log.debug( log.debug(
u'register_OSIS_file_bible %s , %s not created already exists', u'register_OSIS_file_bible %s, %s not created already exists',
biblename, osisfile) biblename, osisfile)
return False return False
@ -324,7 +324,7 @@ c
Rest can be guessed at ! Rest can be guessed at !
""" """
text = [] text = []
self.media.setQuickMsg1(u'') self.media.setQuickMsg1(u'')
self.media.setQuickMsg2(u'') self.media.setQuickMsg2(u'')
log.debug(u'get_verse_text %s,%s,%s,%s,%s,%s', log.debug(u'get_verse_text %s,%s,%s,%s,%s,%s',
@ -355,7 +355,7 @@ c
book = self.bible_db_cache[bible].create_book( book = self.bible_db_cache[bible].create_book(
bookname, self.book_abbreviations[bookname], bookname, self.book_abbreviations[bookname],
self.book_testaments[bookname]) self.book_testaments[bookname])
log.debug(u'New http book %s , %s, %s', log.debug(u'New http book %s, %s, %s',
book, book.id, book.name) book, book.id, book.name)
self.bible_db_cache[bible].create_chapter( self.bible_db_cache[bible].create_chapter(
book.id, search_results.get_chapter(), book.id, search_results.get_chapter(),
@ -414,7 +414,7 @@ c
""" """
Check cache to see if new bible Check cache to see if new bible
""" """
for b , o in self.bible_db_cache.iteritems(): for b, o in self.bible_db_cache.iteritems():
log.debug(u'Bible from cache in is_new_bible %s', b ) log.debug(u'Bible from cache in is_new_bible %s', b )
if b == name : if b == name :
return False return False

View File

@ -407,7 +407,7 @@ class BibleMediaItem(MediaManagerItem):
raw_footer.index(footer) raw_footer.index(footer)
except: except:
raw_footer.append(footer) raw_footer.append(footer)
if len(self.parent.bibles_tab.bible_theme) == 0: if len(self.parent.bibles_tab.bible_theme) == 0:
service_item.theme = None service_item.theme = None
else: else:
service_item.theme = self.parent.bibles_tab.bible_theme service_item.theme = self.parent.bibles_tab.bible_theme
@ -446,7 +446,7 @@ class BibleMediaItem(MediaManagerItem):
self.initialiseChapterVerse(bible, book.name) self.initialiseChapterVerse(bible, book.name)
def initialiseChapterVerse(self, bible, book): def initialiseChapterVerse(self, bible, book):
log.debug(u'initialiseChapterVerse %s , %s', bible, book) log.debug(u'initialiseChapterVerse %s, %s', bible, book)
self.chapters_from = self.parent.biblemanager.get_book_chapter_count( self.chapters_from = self.parent.biblemanager.get_book_chapter_count(
bible, book) bible, book)
self.verses = self.parent.biblemanager.get_book_verse_count(bible, self.verses = self.parent.biblemanager.get_book_verse_count(bible,
@ -456,8 +456,8 @@ class BibleMediaItem(MediaManagerItem):
self.adjustComboBox(1, self.verses, self.AdvancedFromVerse) self.adjustComboBox(1, self.verses, self.AdvancedFromVerse)
self.adjustComboBox(1, self.verses, self.AdvancedToVerse) self.adjustComboBox(1, self.verses, self.AdvancedToVerse)
def adjustComboBox(self, frm, to , combo): def adjustComboBox(self, frm, to, combo):
log.debug(u'adjustComboBox %s , %s , %s', combo, frm, to) log.debug(u'adjustComboBox %s, %s, %s', combo, frm, to)
combo.clear() combo.clear()
for i in range(int(frm), int(to) + 1): for i in range(int(frm), int(to) + 1):
combo.addItem(unicode(i)) combo.addItem(unicode(i))
@ -475,7 +475,7 @@ class BibleMediaItem(MediaManagerItem):
cr.setSelected(True) cr.setSelected(True)
def searchByReference(self, bible, search): def searchByReference(self, bible, search):
log.debug(u'searchByReference %s ,%s', bible, search) log.debug(u'searchByReference %s, %s', bible, search)
book = u'' book = u''
start_chapter = u'' start_chapter = u''
end_chapter = u'' end_chapter = u''

View File

@ -102,7 +102,7 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog):
self.TitleEdit.setText(self.customSlide.title) self.TitleEdit.setText(self.customSlide.title)
self.CreditEdit.setText(self.customSlide.credits) self.CreditEdit.setText(self.customSlide.credits)
songXML=SongXMLParser(self.customSlide.text) songXML = SongXMLParser(self.customSlide.text)
verseList = songXML.get_verses() verseList = songXML.get_verses()
for verse in verseList: for verse in verseList:
self.VerseListView.addItem(verse[1]) self.VerseListView.addItem(verse[1])
@ -115,13 +115,13 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog):
self.ThemeComboBox.setCurrentIndex(0) self.ThemeComboBox.setCurrentIndex(0)
def accept(self): def accept(self):
valid , message = self._validate() valid, message = self._validate()
if not valid: if not valid:
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
translate(u'customEditDialog', u'Error'), message, translate(u'customEditDialog', u'Error'), message,
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
return return
sxml=SongXMLBuilder() sxml = SongXMLBuilder()
sxml.new_document() sxml.new_document()
sxml.add_lyrics_to_song() sxml.add_lyrics_to_song()
count = 1 count = 1

View File

@ -105,7 +105,7 @@ class CustomMediaItem(MediaManagerItem):
theme = customSlide.theme_name theme = customSlide.theme_name
if len(theme) is not 0 : if len(theme) is not 0 :
service_item.theme = theme service_item.theme = theme
songXML=SongXMLParser(customSlide.text) songXML = SongXMLParser(customSlide.text)
verseList = songXML.get_verses() verseList = songXML.get_verses()
for verse in verseList: for verse in verseList:
raw_slides.append(verse[1]) raw_slides.append(verse[1])

View File

@ -24,6 +24,7 @@
from impresscontroller import ImpressController from impresscontroller import ImpressController
#from powerpointcontroller import PowerpointController #from powerpointcontroller import PowerpointController
from pptviewcontroller import PptviewController
from messagelistener import MessageListener from messagelistener import MessageListener
from mediaitem import PresentationMediaItem from mediaitem import PresentationMediaItem
from presentationtab import PresentationTab from presentationtab import PresentationTab

View File

@ -27,7 +27,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
# http://nxsy.org/comparing-documents-with-openoffice-and-python # http://nxsy.org/comparing-documents-with-openoffice-and-python
import logging import logging
import os , subprocess import os, subprocess
import time import time
import sys import sys
@ -41,9 +41,9 @@ from PyQt4 import QtCore
class ImpressController(object): class ImpressController(object):
""" """
Class to control interactions with Impress Presentations Class to control interactions with Impress presentations.
It creates the runtime Environment , Loads the and Closes the Presentation It creates the runtime environment, loads and closes the presentation as
As well as trigggering the correct activities based on the users input well as triggering the correct activities based on the users input
""" """
global log global log
log = logging.getLogger(u'ImpressController') log = logging.getLogger(u'ImpressController')
@ -57,8 +57,8 @@ class ImpressController(object):
def startOpenoffice(self): def startOpenoffice(self):
""" """
Loads a running version of OpenOffice inthe background. Loads a running version of OpenOffice in the background.
It is not displayed to the user but is available to the Uno interface It is not displayed to the user but is available to the UNO interface
when required. when required.
""" """
log.debug(u'start Openoffice') log.debug(u'start Openoffice')

View File

@ -22,128 +22,135 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
import logging
import os, subprocess
import time
import sys import sys
import win32api import win32api
from PyQt4 import QtGui, QtCore
from ctypes import * from ctypes import *
from ctypes.wintypes import RECT from ctypes.wintypes import RECT
pptdll = cdll.LoadLibrary(r'C:\Documents and Settings\jonathan\My Documents\Personal\openlp\openlp-2\trunk\openlp\libraries\pptviewlib\pptviewlib.dll') from PyQt4 import QtCore
class BoxLayout(QtGui.QWidget): class PptviewController(object):
def __init__(self, parent=None): """
QtGui.QWidget.__init__(self, parent) Class to control interactions with PowerPOint Viewer Presentations
It creates the runtime Environment , Loads the and Closes the Presentation
As well as trigggering the correct activities based on the users input
"""
global log
log = logging.getLogger(u'PptviewController')
def __init__(self):
log.debug(u'Initialising')
self.process = None
self.document = None
self.presentation = None
self.pptid = None
self.startPPTView()
def startPPTView(self):
"""
Loads the PPTVIEWLIB library
"""
log.debug(u'start PPTView')
self.presentation = cdll.LoadLibrary(r'openlp\plugins\presentations\lib\pptviewlib\pptviewlib.dll')
def kill(self):
"""
Called at system exit to clean up any running presentations
"""
log.debug(u'Kill')
self.closePresentation()
def loadPresentation(self, presentation):
"""
Called when a presentation is added to the SlideController.
It builds the environment, starts communcations with the background
OpenOffice task started earlier. If OpenOffice is not present is is
started. Once the environment is available the presentation is loaded
and started.
``presentation``
The file name of the presentatios to the run.
"""
log.debug(u'LoadPresentation')
if(self.pptid>=0):
self.CloseClick()
rect = RECT()
rect.left = 0
rect.top = 0
rect.width = 0
rect.hight = 0
try:
tempfolder = None #r'c:\temp\pptviewlib\' + presentation
self.pptid = self.presentation.OpenPPT(presentation, None, rect, tempfolder)
except:
log.exception(u'Failed to load presentation')
#self.slidecount = pptdll.GetSlideCount(self.pptid)
def closePresentation(self):
"""
Close presentation and clean up objects
Triggerent by new object being added to SlideController orOpenLP
being shut down
"""
if(self.pptid<0): return
self.presentation.Close(self.pptid)
self.pptid = -1 self.pptid = -1
self.setWindowTitle(u'box layout')
PPTLabel = QtGui.QLabel(u'Open PowerPoint file') def isActive(self):
slideLabel = QtGui.QLabel(u'Go to slide #') return self.pptid >= 0
self.PPTEdit = QtGui.QLineEdit()
self.slideEdit = QtGui.QLineEdit()
self.total = QtGui.QLabel()
PPTBtn = QtGui.QPushButton(u'Open')
PPTDlgBtn = QtGui.QPushButton(u'...')
slideBtn = QtGui.QPushButton(u'Go')
prev = QtGui.QPushButton(u'Prev')
next = QtGui.QPushButton(u'Next')
blank = QtGui.QPushButton(u'Blank')
unblank = QtGui.QPushButton(u'Unblank')
restart = QtGui.QPushButton(u'Restart')
close = QtGui.QPushButton(u'Close')
resume = QtGui.QPushButton(u'Resume')
stop = QtGui.QPushButton(u'Stop')
pptwindow = QtGui.QWidget()
grid = QtGui.QGridLayout() def resume(self):
grid.addWidget(PPTLabel, 0, 0) if(self.pptid<0): return
grid.addWidget(self.PPTEdit, 0, 1) self.presentation.Resume(self.pptid)
grid.addWidget(PPTDlgBtn, 0, 2)
grid.addWidget(PPTBtn, 0, 3)
grid.addWidget(slideLabel, 1, 0)
grid.addWidget(self.slideEdit, 1, 1)
grid.addWidget(slideBtn, 1, 3)
grid.addWidget(prev, 2, 0)
grid.addWidget(next, 2, 1)
grid.addWidget(blank, 3, 0)
grid.addWidget(unblank, 3, 1)
grid.addWidget(restart, 4, 0)
grid.addWidget(stop, 4, 1)
grid.addWidget(resume, 4, 2)
grid.addWidget(pptwindow, 5, 0, 10, 3)
self.connect(PPTBtn, QtCore.SIGNAL(u'clicked()'), self.OpenClick)
self.connect(PPTDlgBtn, QtCore.SIGNAL(u'clicked()'), self.OpenDialog)
self.connect(slideBtn, QtCore.SIGNAL(u'clicked()'), self.GotoClick)
self.connect(prev, QtCore.SIGNAL(u'clicked()'), self.PrevClick)
self.connect(next, QtCore.SIGNAL(u'clicked()'), self.NextClick)
self.connect(blank, QtCore.SIGNAL(u'clicked()'), self.BlankClick)
self.connect(unblank, QtCore.SIGNAL(u'clicked()'), self.UnblankClick)
self.connect(restart, QtCore.SIGNAL(u'clicked()'), self.RestartClick)
self.connect(close, QtCore.SIGNAL(u'clicked()'), self.CloseClick)
self.connect(stop, QtCore.SIGNAL(u'clicked()'), self.StopClick)
self.connect(resume, QtCore.SIGNAL(u'clicked()'), self.ResumeClick)
self.setLayout(grid) def pause(self):
return
self.resize(300, 150) def blankScreen(self):
if(self.pptid<0): return
self.presentation.Blank(self.pptid)
def PrevClick(self): def unblankScreen(self):
if(self.pptid<0): return
self.presentation.Unblank(self.pptid)
def stop(self):
if(self.pptid<0): return
self.presentation.Stop(self.pptid)
def go(self):
if(self.pptid<0): return
self.presentation.RestartShow(self.pptid)
def getSlideNumber(self):
if(self.pptid<0): return -1
return self.presentation.GetCurrentSlide(self.pptid)
def setSlideNumber(self, slideno):
if(self.pptid<0): return
self.presentation.GotoSlide(self.pptid, slideno)
slideNumber = property(getSlideNumber, setSlideNumber)
def nextStep(self):
"""
Triggers the next effect of slide on the running presentation
"""
if(self.pptid<0): return
self.presentation.NextStep(self.pptid)
def previousStep(self):
"""
Triggers the previous slide on the running presentation
"""
if self.pptid<0: return if self.pptid<0: return
pptdll.PrevStep(self.pptid) self.presentation.PrevStep(self.pptid)
self.slideEdit.setText(pptdll.GetCurrentSlide(self.pptid))
def NextClick(self): def NextClick(self):
if(self.pptid<0): return if(self.pptid<0): return
pptdll.NextStep(self.pptid) pptdll.NextStep(self.pptid)
self.slideEdit.setText(pptdll.GetCurrentSlide(self.pptid)) self.slideEdit.setText(pptdll.GetCurrentSlide(self.pptid))
def BlankClick(self):
if(self.pptid<0): return
pptdll.Blank(self.pptid)
def UnblankClick(self):
if(self.pptid<0): return
pptdll.Unblank(self.pptid)
def RestartClick(self):
if(self.pptid<0): return
pptdll.RestartShow(self.pptid)
self.slideEdit.setText(pptdll.GetCurrentSlide(self.pptid))
def StopClick(self):
if(self.pptid<0): return
pptdll.Stop(self.pptid)
def ResumeClick(self):
if(self.pptid<0): return
pptdll.Resume(self.pptid)
def CloseClick(self):
if(self.pptid<0): return
pptdll.Close(self.pptid)
self.pptid = -1
def OpenClick(self):
if(self.pptid>=0):
self.CloseClick()
rect = RECT()
rect.left = 100
rect.top = 100
rect.width = 900
rect.hight = 700
#self.pptid = pptdll.OpenPPT(self.PPTEdit.text, None, rect, "c:\temp\slide')
self.pptid = pptdll.OpenPPT(u'C:\\test 1.ppt', None, rect, 'c:\temp\slide')
self.total.setText(pptdll.GetSlideCount(self.pptid))
self.slideEdit.setText(unicode(pptdll.GetCurrentSlide(self.pptid)))
def GotoClick(self):
if(self.pptid<0): return
pptdll.GotoSlide(self.pptid, self.slideEdit.text)
self.slideEdit.setText(pptdll.GetCurrentSlide(self.pptid))
def OpenDialog(self):
self.PPTEdit.setText(QtGui.QFileDialog.getOpenFileName(self, 'Open file'))
app = QtGui.QApplication(sys.argv)
qb = BoxLayout()
qb.show()
sys.exit(app.exec_())

View File

@ -27,6 +27,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include "pptviewlib.h" #include "pptviewlib.h"
// Because of the callbacks used by SetWindowsHookEx, the memory used needs to be // Because of the callbacks used by SetWindowsHookEx, the memory used needs to be
// sharable across processes (the callbacks are done from a different process) // sharable across processes (the callbacks are done from a different process)
// Therefore use data_seg with RWS memory. // Therefore use data_seg with RWS memory.
@ -310,9 +311,10 @@ BOOL GetPPTViewerPath(char *pptviewerpath, int strsize)
LRESULT lresult; LRESULT lresult;
DEBUG("GetPPTViewerPath: start\n"); DEBUG("GetPPTViewerPath: start\n");
if(RegOpenKeyEx(HKEY_CLASSES_ROOT, "Applications\\PPTVIEW.EXE\\shell\\open\\command", 0, KEY_READ, &hkey)!=ERROR_SUCCESS) if(RegOpenKeyEx(HKEY_CLASSES_ROOT, "PowerPointViewer.Show.12\\shell\\Show\\command", 0, KEY_READ, &hkey)!=ERROR_SUCCESS)
if(RegOpenKeyEx(HKEY_CLASSES_ROOT, "Applications\\PPTVIEW.EXE\\shell\\Show\\command", 0, KEY_READ, &hkey)!=ERROR_SUCCESS) if(RegOpenKeyEx(HKEY_CLASSES_ROOT, "Applications\\PPTVIEW.EXE\\shell\\open\\command", 0, KEY_READ, &hkey)!=ERROR_SUCCESS)
return FALSE; if(RegOpenKeyEx(HKEY_CLASSES_ROOT, "Applications\\PPTVIEW.EXE\\shell\\Show\\command", 0, KEY_READ, &hkey)!=ERROR_SUCCESS)
return FALSE;
dwtype = REG_SZ; dwtype = REG_SZ;
dwsize = (DWORD)strsize; dwsize = (DWORD)strsize;
lresult = RegQueryValueEx(hkey, NULL, NULL, &dwtype, (LPBYTE)pptviewerpath, &dwsize ); lresult = RegQueryValueEx(hkey, NULL, NULL, &dwtype, (LPBYTE)pptviewerpath, &dwsize );

View File

@ -51,4 +51,4 @@ struct PPTVIEWOBJ
char filename[MAX_PATH]; char filename[MAX_PATH];
char previewpath[MAX_PATH]; char previewpath[MAX_PATH];
PPTVIEWSTATE state; PPTVIEWSTATE state;
}; };

View File

@ -1,203 +1,202 @@
<?xml version="1.0" encoding="Windows-1252"?> <?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject <VisualStudioProject
ProjectType="Visual C++" ProjectType="Visual C++"
Version="9.00" Version="9.00"
Name="pptviewlib" Name="pptviewlib"
ProjectGUID="{04CC20D1-DC5A-4189-8181-4011E3C21DCF}" ProjectGUID="{04CC20D1-DC5A-4189-8181-4011E3C21DCF}"
RootNamespace="pptviewlib" RootNamespace="pptviewlib"
Keyword="Win32Proj" Keyword="Win32Proj"
TargetFrameworkVersion="196613" TargetFrameworkVersion="196613"
> >
<Platforms> <Platforms>
<Platform <Platform
Name="Win32" Name="Win32"
/> />
</Platforms> </Platforms>
<ToolFiles> <ToolFiles>
</ToolFiles> </ToolFiles>
<Configurations> <Configurations>
<Configuration <Configuration
Name="Debug|Win32" Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)" OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)" IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2" ConfigurationType="2"
CharacterSet="2" CharacterSet="2"
> >
<Tool <Tool
Name="VCPreBuildEventTool" Name="VCPreBuildEventTool"
/> />
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
/> />
<Tool <Tool
Name="VCXMLDataGeneratorTool" Name="VCXMLDataGeneratorTool"
/> />
<Tool <Tool
Name="VCWebServiceProxyGeneratorTool" Name="VCWebServiceProxyGeneratorTool"
/> />
<Tool <Tool
Name="VCMIDLTool" Name="VCMIDLTool"
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
Optimization="0" Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;PPTVIEWLIB_EXPORTS" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;PPTVIEWLIB_EXPORTS"
MinimalRebuild="true" MinimalRebuild="true"
BasicRuntimeChecks="3" BasicRuntimeChecks="3"
RuntimeLibrary="3" RuntimeLibrary="3"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
DebugInformationFormat="4" DebugInformationFormat="4"
/> />
<Tool <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
/> />
<Tool <Tool
Name="VCResourceCompilerTool" Name="VCResourceCompilerTool"
/> />
<Tool <Tool
Name="VCPreLinkEventTool" Name="VCPreLinkEventTool"
/> />
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"
LinkIncremental="2" LinkIncremental="2"
ModuleDefinitionFile="" ModuleDefinitionFile=""
GenerateDebugInformation="true" GenerateDebugInformation="true"
SubSystem="2" SubSystem="2"
TargetMachine="1" TargetMachine="1"
/> />
<Tool <Tool
Name="VCALinkTool" Name="VCALinkTool"
/> />
<Tool <Tool
Name="VCManifestTool" Name="VCManifestTool"
/> />
<Tool <Tool
Name="VCXDCMakeTool" Name="VCXDCMakeTool"
/> />
<Tool <Tool
Name="VCBscMakeTool" Name="VCBscMakeTool"
/> />
<Tool <Tool
Name="VCFxCopTool" Name="VCFxCopTool"
/> />
<Tool <Tool
Name="VCAppVerifierTool" Name="VCAppVerifierTool"
/> />
<Tool <Tool
Name="VCPostBuildEventTool" Name="VCPostBuildEventTool"
/> />
</Configuration> </Configuration>
<Configuration <Configuration
Name="Release|Win32" Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)" OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)" IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2" ConfigurationType="2"
CharacterSet="1" CharacterSet="2"
WholeProgramOptimization="1" WholeProgramOptimization="1"
> >
<Tool <Tool
Name="VCPreBuildEventTool" Name="VCPreBuildEventTool"
/> />
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
/> />
<Tool <Tool
Name="VCXMLDataGeneratorTool" Name="VCXMLDataGeneratorTool"
/> />
<Tool <Tool
Name="VCWebServiceProxyGeneratorTool" Name="VCWebServiceProxyGeneratorTool"
/> />
<Tool <Tool
Name="VCMIDLTool" Name="VCMIDLTool"
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
Optimization="2" Optimization="2"
EnableIntrinsicFunctions="true" EnableIntrinsicFunctions="true"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;PPTVIEWLIB_EXPORTS" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;PPTVIEWLIB_EXPORTS"
RuntimeLibrary="2" RuntimeLibrary="2"
EnableFunctionLevelLinking="true" EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
DebugInformationFormat="3" DebugInformationFormat="3"
/> />
<Tool <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
/> />
<Tool <Tool
Name="VCResourceCompilerTool" Name="VCResourceCompilerTool"
/> />
<Tool <Tool
Name="VCPreLinkEventTool" Name="VCPreLinkEventTool"
/> />
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"
LinkIncremental="1" LinkIncremental="1"
ModuleDefinitionFile="pptviewlib.def" GenerateDebugInformation="true"
GenerateDebugInformation="true" SubSystem="2"
SubSystem="2" OptimizeReferences="2"
OptimizeReferences="2" EnableCOMDATFolding="2"
EnableCOMDATFolding="2" TargetMachine="1"
TargetMachine="1" />
/> <Tool
<Tool Name="VCALinkTool"
Name="VCALinkTool" />
/> <Tool
<Tool Name="VCManifestTool"
Name="VCManifestTool" />
/> <Tool
<Tool Name="VCXDCMakeTool"
Name="VCXDCMakeTool" />
/> <Tool
<Tool Name="VCBscMakeTool"
Name="VCBscMakeTool" />
/> <Tool
<Tool Name="VCFxCopTool"
Name="VCFxCopTool" />
/> <Tool
<Tool Name="VCAppVerifierTool"
Name="VCAppVerifierTool" />
/> <Tool
<Tool Name="VCPostBuildEventTool"
Name="VCPostBuildEventTool" />
/> </Configuration>
</Configuration> </Configurations>
</Configurations> <References>
<References> </References>
</References> <Files>
<Files> <Filter
<Filter Name="Source Files"
Name="Source Files" Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" >
> <File
<File RelativePath=".\pptviewlib.cpp"
RelativePath=".\pptviewlib.cpp" >
> </File>
</File> <File
<File RelativePath=".\README.TXT"
RelativePath=".\README.TXT" >
> </File>
</File> </Filter>
</Filter> <Filter
<Filter Name="Header Files"
Name="Header Files" Filter="h;hpp;hxx;hm;inl;inc;xsd"
Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" >
> <File
<File RelativePath=".\pptviewlib.h"
RelativePath=".\pptviewlib.h" >
> </File>
</File> </Filter>
</Filter> <Filter
<Filter Name="Resource Files"
Name="Resource Files" Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" >
> </Filter>
</Filter> </Files>
</Files> <Globals>
<Globals> </Globals>
</Globals> </VisualStudioProject>
</VisualStudioProject>

View File

@ -31,10 +31,12 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, MediaManagerItem from openlp.core.lib import Plugin, MediaManagerItem
from openlp.plugins.presentations.lib import PresentationMediaItem, \ from openlp.plugins.presentations.lib import PresentationMediaItem, \
PresentationTab, ImpressController PresentationTab, ImpressController
try: if os.name == u'nt':
from openlp.plugins.presentations.lib import PowerpointController try:
except: from openlp.plugins.presentations.lib import PowerpointController
pass except:
pass
from openlp.plugins.presentations.lib import PptviewController
class PresentationPlugin(Plugin): class PresentationPlugin(Plugin):
@ -91,26 +93,25 @@ class PresentationPlugin(Plugin):
self.registerControllers(u'Impress', openoffice) self.registerControllers(u'Impress', openoffice)
except: except:
log.exception(u'Failed to set up plugin for Impress') log.exception(u'Failed to set up plugin for Impress')
#Lets see if Powerpoint is required (Default is Not wanted) if os.name == u'nt':
if int(self.config.get_config( #Lets see if Powerpoint is required (Default is Not wanted)
u'Powerpoint', QtCore.Qt.Unchecked)) == QtCore.Qt.Checked: if int(self.config.get_config(
try: u'Powerpoint', QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
#Check to see if we are Win32 try:
from win32com.client import Dispatch #Check to see if we are Win32
powerpoint = PowerpointController() from win32com.client import Dispatch
self.registerControllers(u'Powerpoint', powerpoint) powerpoint = PowerpointController()
except: self.registerControllers(u'Powerpoint', powerpoint)
log.exception(u'Failed to set up plugin for Powerpoint') except:
#Lets see if Powerpoint Viewer is required (Default is Not wanted) log.exception(u'Failed to set up plugin for Powerpoint')
if int(self.config.get_config( #Lets see if Powerpoint Viewer is required (Default is Not wanted)
u'Powerpoint Viewer', QtCore.Qt.Unchecked)) == QtCore.Qt.Checked: if int(self.config.get_config(
try: u'Powerpoint Viewer', QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
#Check to see if we are Win32 try:
from win32com.client import Dispatch pptview = PptviewController()
powerpoint = PowerpointController() self.registerControllers(u'Powerpoint Viewer', pptview)
self.registerControllers(u'Powerpoint Viewer', powerpoint) except:
except: log.exception(u'Failed to set up plugin for Powerpoint Viewer')
log.exception(u'Failed to set up plugin for Powerpoint Viewer')
#If we have no available controllers disable plugin #If we have no available controllers disable plugin
if len(self.controllers) > 0: if len(self.controllers) > 0:
return True return True

View File

@ -442,7 +442,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
text = text.replace(u'{', u'') text = text.replace(u'{', u'')
text = text.replace(u'}', u'') text = text.replace(u'}', u'')
text = text.replace(u'?', u'') text = text.replace(u'?', u'')
self.song.search_lyrics = unicode(text) self.song.search_lyrics = unicode(text)
self.song.lyrics = unicode(sxml.extract_xml()) self.song.lyrics = unicode(sxml.extract_xml())
def processTitle(self): def processTitle(self):

View File

@ -178,7 +178,7 @@ class SongMediaItem(MediaManagerItem):
def onSearchTextButtonClick(self): def onSearchTextButtonClick(self):
search_keywords = unicode(self.SearchTextEdit.displayText()) search_keywords = unicode(self.SearchTextEdit.displayText())
search_results = [] search_results = []
search_type = self.SearchTypeComboBox.currentIndex() search_type = self.SearchTypeComboBox.currentIndex()
if search_type == 0: if search_type == 0:
log.debug(u'Titles Search') log.debug(u'Titles Search')