head + resolved conflicts

This commit is contained in:
Andreas Preikschat 2010-09-19 07:59:38 +02:00
commit bd5f82096f
59 changed files with 6700 additions and 6457 deletions

View File

@ -147,6 +147,9 @@ class OpenLP(QtGui.QApplication):
return self.exec_() return self.exec_()
def hookException(self, exctype, value, traceback): def hookException(self, exctype, value, traceback):
if not hasattr(self, u'mainWindow'):
log.exception(''.join(format_exception(exctype, value, traceback)))
return
if not hasattr(self, u'exceptionForm'): if not hasattr(self, u'exceptionForm'):
self.exceptionForm = ExceptionForm(self.mainWindow) self.exceptionForm = ExceptionForm(self.mainWindow)
self.exceptionForm.exceptionTextEdit.setPlainText( self.exceptionForm.exceptionTextEdit.setPlainText(
@ -159,18 +162,18 @@ def main():
the PyQt4 Application. the PyQt4 Application.
""" """
# Set up command line options. # Set up command line options.
usage = u'Usage: %prog [options] [qt-options]' usage = 'Usage: %prog [options] [qt-options]'
parser = OptionParser(usage=usage) parser = OptionParser(usage=usage)
parser.add_option("-l", "--log-level", dest="loglevel", parser.add_option('-e', '--no-error-form', dest='no_error_form',
default="warning", metavar="LEVEL", action='store_true', help='Disable the error notification form.')
help="Set logging to LEVEL level. Valid values are " parser.add_option('-l', '--log-level', dest='loglevel',
"\"debug\", \"info\", \"warning\".") default='warning', metavar='LEVEL', help='Set logging to LEVEL '
parser.add_option("-p", "--portable", dest="portable", 'level. Valid values are "debug", "info", "warning".')
action="store_true", parser.add_option('-p', '--portable', dest='portable',
help="Specify if this should be run as a portable app, " action='store_true', help='Specify if this should be run as a '
"off a USB flash drive.") 'portable app, off a USB flash drive (not implemented).')
parser.add_option("-s", "--style", dest="style", parser.add_option('-s', '--style', dest='style',
help="Set the Qt4 style (passed directly to Qt4).") help='Set the Qt4 style (passed directly to Qt4).')
# Set up logging # Set up logging
log_path = AppLocation.get_directory(AppLocation.CacheDir) log_path = AppLocation.get_directory(AppLocation.CacheDir)
if not os.path.exists(log_path): if not os.path.exists(log_path):
@ -203,7 +206,8 @@ def main():
language = LanguageManager.get_language() language = LanguageManager.get_language()
appTranslator = LanguageManager.get_translator(language) appTranslator = LanguageManager.get_translator(language)
app.installTranslator(appTranslator) app.installTranslator(appTranslator)
sys.excepthook = app.hookException if not options.no_error_form:
sys.excepthook = app.hookException
sys.exit(app.run()) sys.exit(app.run())
if __name__ == u'__main__': if __name__ == u'__main__':

View File

@ -38,63 +38,48 @@ log = logging.getLogger(__name__)
# TODO make external and configurable in alpha 4 via a settings dialog # TODO make external and configurable in alpha 4 via a settings dialog
html_expands = [] html_expands = []
html_expands.append({u'desc':u'Red', u'start tag':u'{r}', \ html_expands.append({u'desc':u'Red', u'start tag':u'{r}',
u'start html':u'<span style="-webkit-text-fill-color:red">', \ u'start html':u'<span style="-webkit-text-fill-color:red">',
u'end tag':u'{/r}', u'end html':u'</span>', \ u'end tag':u'{/r}', u'end html':u'</span>', u'protected':False})
u'protected':False}) html_expands.append({u'desc':u'Black', u'start tag':u'{b}',
html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \ u'start html':u'<span style="-webkit-text-fill-color:black">',
u'start html':u'<span style="-webkit-text-fill-color:black">', \ u'end tag':u'{/b}', u'end html':u'</span>', u'protected':False})
u'end tag':u'{/b}', u'end html':u'</span>', \ html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}',
u'protected':False}) u'start html':u'<span style="-webkit-text-fill-color:blue">',
html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \ u'end tag':u'{/bl}', u'end html':u'</span>', u'protected':False})
u'start html':u'<span style="-webkit-text-fill-color:blue">', \ html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}',
u'end tag':u'{/bl}', u'end html':u'</span>', \ u'start html':u'<span style="-webkit-text-fill-color:yellow">',
u'protected':False}) u'end tag':u'{/y}', u'end html':u'</span>', u'protected':False})
html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \ html_expands.append({u'desc':u'Green', u'start tag':u'{g}',
u'start html':u'<span style="-webkit-text-fill-color:yellow">', \ u'start html':u'<span style="-webkit-text-fill-color:green">',
u'end tag':u'{/y}', u'end html':u'</span>', \ u'end tag':u'{/g}', u'end html':u'</span>', u'protected':False})
u'protected':False}) html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}',
html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \ u'start html':u'<span style="-webkit-text-fill-color:#CC33CC">',
u'start html':u'<span style="-webkit-text-fill-color:green">', \ u'end tag':u'{/pk}', u'end html':u'</span>', u'protected':False})
u'end tag':u'{/g}', u'end html':u'</span>', \ html_expands.append({u'desc':u'Orange', u'start tag':u'{o}',
u'protected':False}) u'start html':u'<span style="-webkit-text-fill-color:#CC0033">',
html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \ u'end tag':u'{/o}', u'end html':u'</span>', u'protected':False})
u'start html':u'<span style="-webkit-text-fill-color:#CC33CC">', \ html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}',
u'end tag':u'{/pk}', u'end html':u'</span>', \ u'start html':u'<span style="-webkit-text-fill-color:#9900FF">',
u'protected':False}) u'end tag':u'{/pp}', u'end html':u'</span>', u'protected':False})
html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \ html_expands.append({u'desc':u'White', u'start tag':u'{w}',
u'start html':u'<span style="-webkit-text-fill-color:#CC0033">', \ u'start html':u'<span style="-webkit-text-fill-color:white">',
u'end tag':u'{/o}', u'end html':u'</span>', \ u'end tag':u'{/w}', u'end html':u'</span>', u'protected':False})
u'protected':False}) html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}',
html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \ u'start html':u'<sup>', u'end tag':u'{/su}', u'end html':u'</sup>',
u'start html':u'<span style="-webkit-text-fill-color:#9900FF">', \ u'protected':True})
u'end tag':u'{/pp}', u'end html':u'</span>', \ html_expands.append({u'desc':u'Subscript', u'start tag':u'{sb}',
u'protected':False}) u'start html':u'<sub>', u'end tag':u'{/sb}', u'end html':u'</sub>',
html_expands.append({u'desc':u'White', u'start tag':u'{w}', \ u'protected':True})
u'start html':u'<span style="-webkit-text-fill-color:white">', \ html_expands.append({u'desc':u'Paragraph', u'start tag':u'{p}',
u'end tag':u'{/w}', u'end html':u'</span>', \ u'start html':u'<p>', u'end tag':u'{/p}', u'end html':u'</p>',
u'protected':False}) u'protected':True})
html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}', \ html_expands.append({u'desc':u'Bold', u'start tag':u'{st}',
u'start html':u'<sup>', \ u'start html':u'<strong>', u'end tag':u'{/st}', u'end html':u'</strong>',
u'end tag':u'{/su}', u'end html':u'</sup>', \ u'protected':True})
u'protected':True}) html_expands.append({u'desc':u'Italics', u'start tag':u'{it}',
html_expands.append({u'desc':u'Subscript', u'start tag':u'{sb}', \ u'start html':u'<em>', u'end tag':u'{/it}', u'end html':u'</em>',
u'start html':u'<sub>', \ u'protected':True})
u'end tag':u'{/sb}', u'end html':u'</sub>', \
u'protected':True})
html_expands.append({u'desc':u'Paragraph', u'start tag':u'{p}', \
u'start html':u'<p>', \
u'end tag':u'{/p}', u'end html':u'</p>', \
u'protected':True})
html_expands.append({u'desc':u'Bold', u'start tag':u'{st}', \
u'start html':u'<strong>', \
u'end tag':u'{/st}', \
u'end html':u'</strong>', \
u'protected':True})
html_expands.append({u'desc':u'Italics', u'start tag':u'{it}', \
u'start html':u'<em>', \
u'end tag':u'{/it}', u'end html':u'</em>', \
u'protected':True})
def translate(context, text, comment=None): def translate(context, text, comment=None):
""" """

View File

@ -81,17 +81,14 @@ body {
</style> </style>
<script language="javascript"> <script language="javascript">
var timer = null; var timer = null;
var video_timer = null;
var transition = %s; var transition = %s;
function show_video(state, path, volume, loop){ function show_video(state, path, volume, loop){
var vid = document.getElementById('video'); var vid = document.getElementById('video');
if(path != null) if(path != null){
vid.src = path; vid.src = path;
if(loop != null){ vid.load();
if(loop)
vid.loop = 'loop';
else
vid.loop = '';
} }
if(volume != null){ if(volume != null){
vid.volume = volume; vid.volume = volume;
@ -100,23 +97,55 @@ body {
case 'play': case 'play':
vid.play(); vid.play();
vid.style.display = 'block'; vid.style.display = 'block';
if(loop)
video_timer = setInterval('video_loop()', 200);
break; break;
case 'pause': case 'pause':
if(video_timer!=null){
clearInterval(video_timer);
video_timer = null;
}
vid.pause(); vid.pause();
vid.style.display = 'block'; vid.style.display = 'block';
break; break;
case 'stop': case 'stop':
if(video_timer!=null){
clearInterval(video_timer);
video_timer = null;
}
vid.pause(); vid.pause();
vid.style.display = 'none'; vid.style.display = 'none';
vid.load();
break; break;
case 'close': case 'close':
if(video_timer!=null){
clearInterval(video_timer);
video_timer = null;
}
vid.pause(); vid.pause();
vid.style.display = 'none'; vid.style.display = 'none';
vid.src = ''; vid.src = '';
break; break;
} }
} }
function video_loop(){
// The preferred method would be to use the video tag loop attribute
// But QtWebKit doesn't support this. Neither does it support the
// onended event, hence the setInterval()
// In addition, setting the currentTime attribute to zero to restart
// the video raises an INDEX_SIZE_ERROR: DOM Exception 1
// To complicate it further, sometimes vid.currentTime stops
// slightly short of vid.duration and vid.ended is intermittent!
//
// Note, currently the background may go black between loops. Not
// desirable. Need to investigate using two <video>'s, and hiding/
// preloading one, and toggle between the two when looping.
var vid = document.getElementById('video');
if(vid.ended||vid.currentTime+0.2>=vid.duration){
vid.load();
vid.play();
}
}
function show_image(src){ function show_image(src){
var img = document.getElementById('image'); var img = document.getElementById('image');
img.src = src; img.src = src;

View File

@ -432,7 +432,7 @@ class MediaManagerItem(QtGui.QWidget):
raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to ' raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to '
u'be defined by the plugin') u'be defined by the plugin')
def generateSlideData(self, service_item, item): def generateSlideData(self, service_item, item=None):
raise NotImplementedError(u'MediaManagerItem.generateSlideData needs ' raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
u'to be defined by the plugin') u'to be defined by the plugin')

View File

@ -199,18 +199,18 @@ class Ui_AboutDialog(object):
'Preamble\n' 'Preamble\n'
'\n' '\n'
'The licenses for most software are designed to take away your ' 'The licenses for most software are designed to take away your '
'freedom to share and change it. By contrast, the GNU General ' 'freedom to share and change it. By contrast, the GNU General '
'Public License is intended to guarantee your freedom to share ' 'Public License is intended to guarantee your freedom to share '
'and change free software--to make sure the software is free for ' 'and change free software--to make sure the software is free for '
'all its users. This General Public License applies to most of ' 'all its users. This General Public License applies to most of '
'the Free Software Foundation\'s software and to any other ' 'the Free Software Foundation\'s software and to any other '
'program whose authors commit to using it. (Some other Free ' 'program whose authors commit to using it. (Some other Free '
'Software Foundation software is covered by the GNU Lesser ' 'Software Foundation software is covered by the GNU Lesser '
'General Public License instead.) You can apply it to your ' 'General Public License instead.) You can apply it to your '
'programs, too.\n' 'programs, too.\n'
'\n' '\n'
'When we speak of free software, we are referring to freedom, not ' 'When we speak of free software, we are referring to freedom, not '
'price. Our General Public Licenses are designed to make sure ' 'price. Our General Public Licenses are designed to make sure '
'that you have the freedom to distribute copies of free software ' 'that you have the freedom to distribute copies of free software '
'(and charge for this service if you wish), that you receive ' '(and charge for this service if you wish), that you receive '
'source code or can get it if you want it, that you can change ' 'source code or can get it if you want it, that you can change '
@ -225,8 +225,8 @@ class Ui_AboutDialog(object):
'\n' '\n'
'For example, if you distribute copies of such a program, whether ' 'For example, if you distribute copies of such a program, whether '
'gratis or for a fee, you must give the recipients all the rights ' 'gratis or for a fee, you must give the recipients all the rights '
'that you have. You must make sure that they, too, receive or ' 'that you have. You must make sure that they, too, receive or '
'can get the source code. And you must show them these terms so ' 'can get the source code. And you must show them these terms so '
'they know their rights.\n' 'they know their rights.\n'
'\n' '\n'
'We protect your rights with two steps: (1) copyright the ' 'We protect your rights with two steps: (1) copyright the '
@ -235,15 +235,15 @@ class Ui_AboutDialog(object):
'\n' '\n'
'Also, for each author\'s protection and ours, we want to make ' 'Also, for each author\'s protection and ours, we want to make '
'certain that everyone understands that there is no warranty for ' 'certain that everyone understands that there is no warranty for '
'this free software. If the software is modified by someone else ' 'this free software. If the software is modified by someone else '
'and passed on, we want its recipients to know that what they ' 'and passed on, we want its recipients to know that what they '
'have is not the original, so that any problems introduced by ' 'have is not the original, so that any problems introduced by '
'others will not reflect on the original authors\' reputations.\n' 'others will not reflect on the original authors\' reputations.\n'
'\n' '\n'
'Finally, any free program is threatened constantly by software ' 'Finally, any free program is threatened constantly by software '
'patents. We wish to avoid the danger that redistributors of a ' 'patents. We wish to avoid the danger that redistributors of a '
'free program will individually obtain patent licenses, in effect ' 'free program will individually obtain patent licenses, in effect '
'making the program proprietary. To prevent this, we have made ' 'making the program proprietary. To prevent this, we have made '
'it clear that any patent must be licensed for everyone\'s free ' 'it clear that any patent must be licensed for everyone\'s free '
'use or not licensed at all.\n' 'use or not licensed at all.\n'
'\n' '\n'
@ -255,17 +255,17 @@ class Ui_AboutDialog(object):
'\n' '\n'
'0. This License applies to any program or other work which ' '0. This License applies to any program or other work which '
'contains a notice placed by the copyright holder saying it may ' 'contains a notice placed by the copyright holder saying it may '
'be distributed under the terms of this General Public License. ' 'be distributed under the terms of this General Public License. '
'The "Program", below, refers to any such program or work, and a ' 'The "Program", below, refers to any such program or work, and a '
'"work based on the Program" means either the Program or any ' '"work based on the Program" means either the Program or any '
'derivative work under copyright law: that is to say, a work ' 'derivative work under copyright law: that is to say, a work '
'containing the Program or a portion of it, either verbatim or ' 'containing the Program or a portion of it, either verbatim or '
'with modifications and/or translated into another language. ' 'with modifications and/or translated into another language. '
'(Hereinafter, translation is included without limitation in the ' '(Hereinafter, translation is included without limitation in the '
'term "modification".) Each licensee is addressed as "you".\n' 'term "modification".) Each licensee is addressed as "you".\n'
'\n' '\n'
'Activities other than copying, distribution and modification are ' 'Activities other than copying, distribution and modification are '
'not covered by this License; they are outside its scope. The ' 'not covered by this License; they are outside its scope. The '
'act of running the Program is not restricted, and the output ' 'act of running the Program is not restricted, and the output '
'from the Program is covered only if its contents constitute a ' 'from the Program is covered only if its contents constitute a '
'work based on the Program (independent of having been made by ' 'work based on the Program (independent of having been made by '
@ -305,17 +305,17 @@ class Ui_AboutDialog(object):
'notice that there is no warranty (or else, saying that you ' 'notice that there is no warranty (or else, saying that you '
'provide a warranty) and that users may redistribute the program ' 'provide a warranty) and that users may redistribute the program '
'under these conditions, and telling the user how to view a copy ' 'under these conditions, and telling the user how to view a copy '
'of this License. (Exception: if the Program itself is ' 'of this License. (Exception: if the Program itself is '
'interactive but does not normally print such an announcement, ' 'interactive but does not normally print such an announcement, '
'your work based on the Program is not required to print an ' 'your work based on the Program is not required to print an '
'announcement.)\n' 'announcement.)\n'
'\n' '\n'
'These requirements apply to the modified work as a whole. If ' 'These requirements apply to the modified work as a whole. If '
'identifiable sections of that work are not derived from the ' 'identifiable sections of that work are not derived from the '
'Program, and can be reasonably considered independent and ' 'Program, and can be reasonably considered independent and '
'separate works in themselves, then this License, and its terms, ' 'separate works in themselves, then this License, and its terms, '
'do not apply to those sections when you distribute them as ' 'do not apply to those sections when you distribute them as '
'separate works. But when you distribute the same sections as ' 'separate works. But when you distribute the same sections as '
'part of a whole which is a work based on the Program, the ' 'part of a whole which is a work based on the Program, the '
'distribution of the whole must be on the terms of this License, ' 'distribution of the whole must be on the terms of this License, '
'whose permissions for other licensees extend to the entire ' 'whose permissions for other licensees extend to the entire '
@ -350,17 +350,17 @@ class Ui_AboutDialog(object):
'medium customarily used for software interchange; or,\n' 'medium customarily used for software interchange; or,\n'
'\n' '\n'
'c) Accompany it with the information you received as to the ' 'c) Accompany it with the information you received as to the '
'offer to distribute corresponding source code. (This ' 'offer to distribute corresponding source code. (This '
'alternative is allowed only for noncommercial distribution and ' 'alternative is allowed only for noncommercial distribution and '
'only if you received the program in object code or executable ' 'only if you received the program in object code or executable '
'form with such an offer, in accord with Subsection b above.)\n' 'form with such an offer, in accord with Subsection b above.)\n'
'\n' '\n'
'The source code for a work means the preferred form of the work ' 'The source code for a work means the preferred form of the work '
'for making modifications to it. For an executable work, ' 'for making modifications to it. For an executable work, '
'complete source code means all the source code for all modules ' 'complete source code means all the source code for all modules '
'it contains, plus any associated interface definition files, ' 'it contains, plus any associated interface definition files, '
'plus the scripts used to control compilation and installation of ' 'plus the scripts used to control compilation and installation of '
'the executable. However, as a special exception, the source ' 'the executable. However, as a special exception, the source '
'code distributed need not include anything that is normally ' 'code distributed need not include anything that is normally '
'distributed (in either source or binary form) with the major ' 'distributed (in either source or binary form) with the major '
'components (compiler, kernel, and so on) of the operating system ' 'components (compiler, kernel, and so on) of the operating system '
@ -374,7 +374,7 @@ class Ui_AboutDialog(object):
'not compelled to copy the source along with the object code.\n' 'not compelled to copy the source along with the object code.\n'
'\n' '\n'
'4. You may not copy, modify, sublicense, or distribute the ' '4. You may not copy, modify, sublicense, or distribute the '
'Program except as expressly provided under this License. Any ' 'Program except as expressly provided under this License. Any '
'attempt otherwise to copy, modify, sublicense or distribute the ' 'attempt otherwise to copy, modify, sublicense or distribute the '
'Program is void, and will automatically terminate your rights ' 'Program is void, and will automatically terminate your rights '
'under this License. However, parties who have received copies, ' 'under this License. However, parties who have received copies, '
@ -383,10 +383,10 @@ class Ui_AboutDialog(object):
'compliance.\n' 'compliance.\n'
'\n' '\n'
'5. You are not required to accept this License, since you have ' '5. You are not required to accept this License, since you have '
'not signed it. However, nothing else grants you permission to ' 'not signed it. However, nothing else grants you permission to '
'modify or distribute the Program or its derivative works. These ' 'modify or distribute the Program or its derivative works. These '
'actions are prohibited by law if you do not accept this ' 'actions are prohibited by law if you do not accept this '
'License. Therefore, by modifying or distributing the Program ' 'License. Therefore, by modifying or distributing the Program '
'(or any work based on the Program), you indicate your acceptance ' '(or any work based on the Program), you indicate your acceptance '
'of this License to do so, and all its terms and conditions for ' 'of this License to do so, and all its terms and conditions for '
'copying, distributing or modifying the Program or works based on ' 'copying, distributing or modifying the Program or works based on '
@ -395,7 +395,7 @@ class Ui_AboutDialog(object):
'6. Each time you redistribute the Program (or any work based on ' '6. Each time you redistribute the Program (or any work based on '
'the Program), the recipient automatically receives a license ' 'the Program), the recipient automatically receives a license '
'from the original licensor to copy, distribute or modify the ' 'from the original licensor to copy, distribute or modify the '
'Program subject to these terms and conditions. You may not ' 'Program subject to these terms and conditions. You may not '
'impose any further restrictions on the recipients\' exercise of ' 'impose any further restrictions on the recipients\' exercise of '
'the rights granted herein. You are not responsible for enforcing ' 'the rights granted herein. You are not responsible for enforcing '
'compliance by third parties to this License.\n' 'compliance by third parties to this License.\n'
@ -405,10 +405,10 @@ class Ui_AboutDialog(object):
'patent issues), conditions are imposed on you (whether by court ' 'patent issues), conditions are imposed on you (whether by court '
'order, agreement or otherwise) that contradict the conditions of ' 'order, agreement or otherwise) that contradict the conditions of '
'this License, they do not excuse you from the conditions of this ' 'this License, they do not excuse you from the conditions of this '
'License. If you cannot distribute so as to satisfy ' 'License. If you cannot distribute so as to satisfy '
'simultaneously your obligations under this License and any other ' 'simultaneously your obligations under this License and any other '
'pertinent obligations, then as a consequence you may not ' 'pertinent obligations, then as a consequence you may not '
'distribute the Program at all. For example, if a patent license ' 'distribute the Program at all. For example, if a patent license '
'would not permit royalty-free redistribution of the Program by ' 'would not permit royalty-free redistribution of the Program by '
'all those who receive copies directly or indirectly through you, ' 'all those who receive copies directly or indirectly through you, '
'then the only way you could satisfy both it and this License ' 'then the only way you could satisfy both it and this License '
@ -423,7 +423,7 @@ class Ui_AboutDialog(object):
'any patents or other property right claims or to contest ' 'any patents or other property right claims or to contest '
'validity of any such claims; this section has the sole purpose ' 'validity of any such claims; this section has the sole purpose '
'of protecting the integrity of the free software distribution ' 'of protecting the integrity of the free software distribution '
'system, which is implemented by public license practices. Many ' 'system, which is implemented by public license practices. Many '
'people have made generous contributions to the wide range of ' 'people have made generous contributions to the wide range of '
'software distributed through that system in reliance on ' 'software distributed through that system in reliance on '
'consistent application of that system; it is up to the ' 'consistent application of that system; it is up to the '
@ -439,29 +439,29 @@ class Ui_AboutDialog(object):
'interfaces, the original copyright holder who places the Program ' 'interfaces, the original copyright holder who places the Program '
'under this License may add an explicit geographical distribution ' 'under this License may add an explicit geographical distribution '
'limitation excluding those countries, so that distribution is ' 'limitation excluding those countries, so that distribution is '
'permitted only in or among countries not thus excluded. In such ' 'permitted only in or among countries not thus excluded. In such '
'case, this License incorporates the limitation as if written in ' 'case, this License incorporates the limitation as if written in '
'the body of this License.\n' 'the body of this License.\n'
'\n' '\n'
'9. The Free Software Foundation may publish revised and/or new ' '9. The Free Software Foundation may publish revised and/or new '
'versions of the General Public License from time to time. Such ' 'versions of the General Public License from time to time. Such '
'new versions will be similar in spirit to the present version, ' 'new versions will be similar in spirit to the present version, '
'but may differ in detail to address new problems or concerns.\n' 'but may differ in detail to address new problems or concerns.\n'
'\n' '\n'
'Each version is given a distinguishing version number. If the ' 'Each version is given a distinguishing version number. If the '
'Program specifies a version number of this License which applies ' 'Program specifies a version number of this License which applies '
'to it and \"any later version\', you have the option of ' 'to it and "any later version", you have the option of '
'following the terms and conditions either of that version or of ' 'following the terms and conditions either of that version or of '
'any later version published by the Free Software Foundation. If ' 'any later version published by the Free Software Foundation. If '
'the Program does not specify a version number of this License, ' 'the Program does not specify a version number of this License, '
'you may choose any version ever published by the Free Software ' 'you may choose any version ever published by the Free Software '
'Foundation.\n' 'Foundation.\n'
'\n' '\n'
'10. If you wish to incorporate parts of the Program into other ' '10. If you wish to incorporate parts of the Program into other '
'free programs whose distribution conditions are different, write ' 'free programs whose distribution conditions are different, write '
'to the author to ask for permission. For software which is ' 'to the author to ask for permission. For software which is '
'copyrighted by the Free Software Foundation, write to the Free ' 'copyrighted by the Free Software Foundation, write to the Free '
'Software Foundation; we sometimes make exceptions for this. Our ' 'Software Foundation; we sometimes make exceptions for this. Our '
'decision will be guided by the two goals of preserving the free ' 'decision will be guided by the two goals of preserving the free '
'status of all derivatives of our free software and of promoting ' 'status of all derivatives of our free software and of promoting '
'the sharing and reuse of software generally.\n' 'the sharing and reuse of software generally.\n'
@ -470,12 +470,12 @@ class Ui_AboutDialog(object):
'\n' '\n'
'11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO ' '11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO '
'WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE ' 'WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE '
'LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT ' 'LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT '
'HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT ' 'HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT '
'WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, ' 'WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, '
'BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY ' 'BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY '
'AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE ' 'AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE '
'QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ' 'QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE '
'PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY ' 'PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY '
'SERVICING, REPAIR OR CORRECTION.\n' 'SERVICING, REPAIR OR CORRECTION.\n'
'\n' '\n'
@ -499,7 +499,7 @@ class Ui_AboutDialog(object):
'this is to make it free software which everyone can redistribute ' 'this is to make it free software which everyone can redistribute '
'and change under these terms.\n' 'and change under these terms.\n'
'\n' '\n'
'To do so, attach the following notices to the program. It is ' 'To do so, attach the following notices to the program. It is '
'safest to attach them to the start of each source file to most ' 'safest to attach them to the start of each source file to most '
'effectively convey the exclusion of warranty; and each file ' 'effectively convey the exclusion of warranty; and each file '
'should have at least the "copyright" line and a pointer to where ' 'should have at least the "copyright" line and a pointer to where '
@ -507,7 +507,7 @@ class Ui_AboutDialog(object):
'\n' '\n'
'<one line to give the program\'s name and a brief idea of what ' '<one line to give the program\'s name and a brief idea of what '
'it does.>\n' 'it does.>\n'
'Copyright (C) <year> <name of author>\n' 'Copyright (C) <year> <name of author>\n'
'\n' '\n'
'This program is free software; you can redistribute it and/or ' 'This program is free software; you can redistribute it and/or '
'modify it under the terms of the GNU General Public License as ' 'modify it under the terms of the GNU General Public License as '
@ -516,7 +516,7 @@ class Ui_AboutDialog(object):
'\n' '\n'
'This program is distributed in the hope that it will be useful, ' 'This program is distributed in the hope that it will be useful, '
'but WITHOUT ANY WARRANTY; without even the implied warranty of ' 'but WITHOUT ANY WARRANTY; without even the implied warranty of '
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ' 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the '
'GNU General Public License for more details.\n' 'GNU General Public License for more details.\n'
'\n' '\n'
'You should have received a copy of the GNU General Public ' 'You should have received a copy of the GNU General Public '
@ -537,14 +537,14 @@ class Ui_AboutDialog(object):
'under certain conditions; type "show c" for details.\n' 'under certain conditions; type "show c" for details.\n'
'\n' '\n'
'The hypothetical commands "show w" and "show c" should show ' 'The hypothetical commands "show w" and "show c" should show '
'the appropriate parts of the General Public License. Of course, ' 'the appropriate parts of the General Public License. Of course, '
'the commands you use may be called something other than "show ' 'the commands you use may be called something other than "show '
'w" and "show c"; they could even be mouse-clicks or menu items--' 'w" and "show c"; they could even be mouse-clicks or menu items--'
'whatever suits your program.\n' 'whatever suits your program.\n'
'\n' '\n'
'You should also get your employer (if you work as a programmer) ' 'You should also get your employer (if you work as a programmer) '
'or your school, if any, to sign a "copyright disclaimer" for the ' 'or your school, if any, to sign a "copyright disclaimer" for the '
'program, if necessary. Here is a sample; alter the names:\n' 'program, if necessary. Here is a sample; alter the names:\n'
'\n' '\n'
'Yoyodyne, Inc., hereby disclaims all copyright interest in the ' 'Yoyodyne, Inc., hereby disclaims all copyright interest in the '
'program "Gnomovision" (which makes passes at compilers) written ' 'program "Gnomovision" (which makes passes at compilers) written '
@ -554,9 +554,9 @@ class Ui_AboutDialog(object):
'Ty Coon, President of Vice\n' 'Ty Coon, President of Vice\n'
'\n' '\n'
'This General Public License does not permit incorporating your ' 'This General Public License does not permit incorporating your '
'program into proprietary programs. If your program is a ' 'program into proprietary programs. If your program is a '
'subroutine library, you may consider it more useful to permit ' 'subroutine library, you may consider it more useful to permit '
'linking proprietary applications with the library. If this is ' 'linking proprietary applications with the library. If this is '
'what you want to do, use the GNU Lesser General Public License ' 'what you want to do, use the GNU Lesser General Public License '
'instead of this License.')) 'instead of this License.'))
self.aboutNotebook.setTabText( self.aboutNotebook.setTabText(
@ -565,3 +565,4 @@ class Ui_AboutDialog(object):
self.contributeButton.setText(translate('OpenLP.AboutForm', self.contributeButton.setText(translate('OpenLP.AboutForm',
'Contribute')) 'Contribute'))
self.closeButton.setText(translate('OpenLP.AboutForm', 'Close')) self.closeButton.setText(translate('OpenLP.AboutForm', 'Close'))

View File

@ -73,7 +73,7 @@ class Ui_ExceptionDialog(object):
def retranslateUi(self, exceptionDialog): def retranslateUi(self, exceptionDialog):
exceptionDialog.setWindowTitle( exceptionDialog.setWindowTitle(
translate('OpenLP.ExceptionDialog', 'Error Occured')) translate('OpenLP.ExceptionDialog', 'Error Occurred'))
self.messageLabel.setText(translate('OpenLP.ExceptionDialog', 'Oops! ' self.messageLabel.setText(translate('OpenLP.ExceptionDialog', 'Oops! '
'OpenLP hit a problem, and couldn\'t recover. The text in the box ' 'OpenLP hit a problem, and couldn\'t recover. The text in the box '
'below contains information that might be helpful to the OpenLP ' 'below contains information that might be helpful to the OpenLP '

View File

@ -120,7 +120,7 @@ class MainDisplay(DisplayWidget):
self.setScene(self.scene) self.setScene(self.scene)
self.webView = QtWebKit.QGraphicsWebView() self.webView = QtWebKit.QGraphicsWebView()
self.scene.addItem(self.webView) self.scene.addItem(self.webView)
self.webView.resize(self.screen[u'size'].width(), \ self.webView.resize(self.screen[u'size'].width(),
self.screen[u'size'].height()) self.screen[u'size'].height())
self.page = self.webView.page() self.page = self.webView.page()
self.frame = self.page.mainFrame() self.frame = self.page.mainFrame()
@ -303,6 +303,9 @@ class MainDisplay(DisplayWidget):
Generates a preview of the image displayed. Generates a preview of the image displayed.
""" """
log.debug(u'preview for %s', self.isLive) log.debug(u'preview for %s', self.isLive)
# We must have a service item to preview
if not hasattr(self, u'serviceItem'):
return
if self.isLive: if self.isLive:
# Wait for the fade to finish before geting the preview. # Wait for the fade to finish before geting the preview.
# Important otherwise preview will have incorrect text if at all ! # Important otherwise preview will have incorrect text if at all !
@ -336,7 +339,7 @@ class MainDisplay(DisplayWidget):
self.loaded = False self.loaded = False
self.initialFrame = False self.initialFrame = False
self.serviceItem = serviceItem self.serviceItem = serviceItem
html = build_html(self.serviceItem, self.screen, self.parent.alertTab,\ html = build_html(self.serviceItem, self.screen, self.parent.alertTab,
self.isLive) self.isLive)
self.webView.setHtml(html) self.webView.setHtml(html)
if serviceItem.foot_text and serviceItem.foot_text: if serviceItem.foot_text and serviceItem.foot_text:

View File

@ -250,7 +250,7 @@ class Ui_MainWindow(object):
self.LanguageGroup = QtGui.QActionGroup(MainWindow) self.LanguageGroup = QtGui.QActionGroup(MainWindow)
qmList = LanguageManager.get_qm_list() qmList = LanguageManager.get_qm_list()
savedLanguage = LanguageManager.get_language() savedLanguage = LanguageManager.get_language()
self.AutoLanguageItem.setChecked(LanguageManager.AutoLanguage) self.AutoLanguageItem.setChecked(LanguageManager.auto_language)
for key in sorted(qmList.keys()): for key in sorted(qmList.keys()):
languageItem = QtGui.QAction(MainWindow) languageItem = QtGui.QAction(MainWindow)
languageItem.setObjectName(key) languageItem.setObjectName(key)
@ -258,7 +258,7 @@ class Ui_MainWindow(object):
if qmList[key] == savedLanguage: if qmList[key] == savedLanguage:
languageItem.setChecked(True) languageItem.setChecked(True)
add_actions(self.LanguageGroup, [languageItem]) add_actions(self.LanguageGroup, [languageItem])
self.LanguageGroup.setDisabled(LanguageManager.AutoLanguage) self.LanguageGroup.setDisabled(LanguageManager.auto_language)
self.ToolsAddToolItem = QtGui.QAction(MainWindow) self.ToolsAddToolItem = QtGui.QAction(MainWindow)
self.ToolsAddToolItem.setIcon(build_icon(u':/tools/tools_add.png')) self.ToolsAddToolItem.setIcon(build_icon(u':/tools/tools_add.png'))
self.ToolsAddToolItem.setObjectName(u'ToolsAddToolItem') self.ToolsAddToolItem.setObjectName(u'ToolsAddToolItem')
@ -575,7 +575,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
QtCore.SIGNAL(u'toggled(bool)'), self.setAutoLanguage) QtCore.SIGNAL(u'toggled(bool)'), self.setAutoLanguage)
self.LanguageGroup.triggered.connect(LanguageManager.set_language) self.LanguageGroup.triggered.connect(LanguageManager.set_language)
QtCore.QObject.connect(self.ModeDefaultItem, QtCore.QObject.connect(self.ModeDefaultItem,
QtCore.SIGNAL(u'triggered()'), self.setViewMode) QtCore.SIGNAL(u'triggered()'), self.onModeDefaultItemClicked)
QtCore.QObject.connect(self.ModeSetupItem, QtCore.QObject.connect(self.ModeSetupItem,
QtCore.SIGNAL(u'triggered()'), self.onModeSetupItemClicked) QtCore.SIGNAL(u'triggered()'), self.onModeSetupItemClicked)
QtCore.QObject.connect(self.ModeLiveItem, QtCore.QObject.connect(self.ModeLiveItem,
@ -640,7 +640,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def setAutoLanguage(self, value): def setAutoLanguage(self, value):
self.LanguageGroup.setDisabled(value) self.LanguageGroup.setDisabled(value)
LanguageManager.AutoLanguage = value LanguageManager.auto_language = value
LanguageManager.set_language(self.LanguageGroup.checkedAction()) LanguageManager.set_language(self.LanguageGroup.checkedAction())
def versionNotice(self, version): def versionNotice(self, version):
@ -670,6 +670,16 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.generalSettingsSection + u'/auto open', self.generalSettingsSection + u'/auto open',
QtCore.QVariant(False)).toBool(): QtCore.QVariant(False)).toBool():
self.ServiceManagerContents.onLoadService(True) self.ServiceManagerContents.onLoadService(True)
view_mode = QtCore.QSettings().value(u'%s/view mode' % \
self.generalSettingsSection, u'default')
if view_mode == u'default':
self.ModeDefaultItem.setChecked(True)
elif view_mode == u'setup':
self.setViewMode(True, True, False, True, False)
self.ModeSetupItem.setChecked(True)
elif view_mode == u'live':
self.setViewMode(False, True, False, False, True)
self.ModeLiveItem.setChecked(True)
def blankCheck(self): def blankCheck(self):
""" """
@ -677,8 +687,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
Triggered by delay thread. Triggered by delay thread.
""" """
settings = QtCore.QSettings() settings = QtCore.QSettings()
settings.beginGroup(self.generalSettingsSection) if settings.value(u'%s/screen blank' % self.generalSettingsSection,
if settings.value(u'screen blank', QtCore.QVariant(False)).toBool(): QtCore.QVariant(False)).toBool():
self.LiveController.mainDisplaySetBackground() self.LiveController.mainDisplaySetBackground()
if settings.value(u'blank warning', if settings.value(u'blank warning',
QtCore.QVariant(False)).toBool(): QtCore.QVariant(False)).toBool():
@ -687,7 +697,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
'OpenLP Main Display Blanked'), 'OpenLP Main Display Blanked'),
translate('OpenLP.MainWindow', translate('OpenLP.MainWindow',
'The Main Display has been blanked out')) 'The Main Display has been blanked out'))
settings.endGroup()
def onHelpWebSiteClicked(self): def onHelpWebSiteClicked(self):
""" """
@ -716,16 +725,31 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
""" """
self.settingsForm.exec_() self.settingsForm.exec_()
def onModeDefaultItemClicked(self):
"""
Put OpenLP into "Default" view mode.
"""
settings = QtCore.QSettings()
settings.setValue(u'%s/view mode' % self.generalSettingsSection,
u'default')
self.setViewMode(True, True, True, True, True)
def onModeSetupItemClicked(self): def onModeSetupItemClicked(self):
""" """
Put OpenLP into "Setup" view mode. Put OpenLP into "Setup" view mode.
""" """
settings = QtCore.QSettings()
settings.setValue(u'%s/view mode' % self.generalSettingsSection,
u'setup')
self.setViewMode(True, True, False, True, False) self.setViewMode(True, True, False, True, False)
def onModeLiveItemClicked(self): def onModeLiveItemClicked(self):
""" """
Put OpenLP into "Live" view mode. Put OpenLP into "Live" view mode.
""" """
settings = QtCore.QSettings()
settings.setValue(u'%s/view mode' % self.generalSettingsSection,
u'live')
self.setViewMode(False, True, False, False, True) self.setViewMode(False, True, False, False, True)
def setViewMode(self, media=True, service=True, theme=True, preview=True, def setViewMode(self, media=True, service=True, theme=True, preview=True,

View File

@ -93,6 +93,7 @@ class Ui_PluginViewDialog(object):
self.pluginListButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) self.pluginListButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
self.pluginListButtonBox.setObjectName(u'pluginListButtonBox') self.pluginListButtonBox.setObjectName(u'pluginListButtonBox')
self.pluginLayout.addWidget(self.pluginListButtonBox) self.pluginLayout.addWidget(self.pluginListButtonBox)
self.versionNumberLabel.setText(u'')
self.retranslateUi(pluginViewDialog) self.retranslateUi(pluginViewDialog)
QtCore.QObject.connect(self.pluginListButtonBox, QtCore.QObject.connect(self.pluginListButtonBox,
QtCore.SIGNAL(u'accepted()'), pluginViewDialog.close) QtCore.SIGNAL(u'accepted()'), pluginViewDialog.close)
@ -105,8 +106,6 @@ class Ui_PluginViewDialog(object):
translate('OpenLP.PluginForm', 'Plugin Details')) translate('OpenLP.PluginForm', 'Plugin Details'))
self.versionLabel.setText( self.versionLabel.setText(
translate('OpenLP.PluginForm', 'Version:')) translate('OpenLP.PluginForm', 'Version:'))
self.versionNumberLabel.setText(
translate('OpenLP.PluginForm', 'TextLabel'))
self.aboutLabel.setText( self.aboutLabel.setText(
translate('OpenLP.PluginForm', 'About:')) translate('OpenLP.PluginForm', 'About:'))
self.statusLabel.setText( self.statusLabel.setText(
@ -115,3 +114,4 @@ class Ui_PluginViewDialog(object):
translate('OpenLP.PluginForm', 'Active')) translate('OpenLP.PluginForm', 'Active'))
self.statusComboBox.setItemText(1, self.statusComboBox.setItemText(1,
translate('OpenLP.PluginForm', 'Inactive')) translate('OpenLP.PluginForm', 'Inactive'))

View File

@ -279,7 +279,8 @@ class ServiceManager(QtGui.QWidget):
self.editAction.setVisible(False) self.editAction.setVisible(False)
self.maintainAction.setVisible(False) self.maintainAction.setVisible(False)
self.notesAction.setVisible(False) self.notesAction.setVisible(False)
if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit): if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit) \
and hasattr(serviceItem[u'service_item'], u'editId'):
self.editAction.setVisible(True) self.editAction.setVisible(True)
if serviceItem[u'service_item']\ if serviceItem[u'service_item']\
.is_capable(ItemCapabilities.AllowsMaintain): .is_capable(ItemCapabilities.AllowsMaintain):
@ -632,6 +633,8 @@ class ServiceManager(QtGui.QWidget):
def onLoadService(self, lastService=False): def onLoadService(self, lastService=False):
if lastService: if lastService:
if not self.parent.recentFiles:
return
filename = self.parent.recentFiles[0] filename = self.parent.recentFiles[0]
else: else:
filename = QtGui.QFileDialog.getOpenFileName( filename = QtGui.QFileDialog.getOpenFileName(
@ -881,7 +884,8 @@ class ServiceManager(QtGui.QWidget):
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
translate('OpenLP.ServiceManager', 'Missing Display Handler'), translate('OpenLP.ServiceManager', 'Missing Display Handler'),
translate('OpenLP.ServiceManager', 'Your item cannot be ' translate('OpenLP.ServiceManager', 'Your item cannot be '
'displayed as there is no handler to display it')) 'displayed as the plugin required to display it is missing '
'or inactive'))
def remoteEdit(self): def remoteEdit(self):
""" """

View File

@ -209,7 +209,8 @@ class SlideController(QtGui.QWidget):
self.Toolbar.addToolbarSeparator(u'Close Separator') self.Toolbar.addToolbarSeparator(u'Close Separator')
self.Toolbar.addToolbarButton( self.Toolbar.addToolbarButton(
u'Edit Song', u':/general/general_edit.png', u'Edit Song', u':/general/general_edit.png',
translate('OpenLP.SlideController', 'Edit and re-preview Song'), translate('OpenLP.SlideController',
'Edit and reload song preview'),
self.onEditSong) self.onEditSong)
if isLive: if isLive:
self.Toolbar.addToolbarSeparator(u'Loop Separator') self.Toolbar.addToolbarSeparator(u'Loop Separator')
@ -269,11 +270,11 @@ class SlideController(QtGui.QWidget):
if isLive: if isLive:
self.SongMenu = QtGui.QToolButton(self.Toolbar) self.SongMenu = QtGui.QToolButton(self.Toolbar)
self.SongMenu.setText(translate('OpenLP.SlideController', self.SongMenu.setText(translate('OpenLP.SlideController',
'Go to')) 'Go To'))
self.SongMenu.setPopupMode(QtGui.QToolButton.InstantPopup) self.SongMenu.setPopupMode(QtGui.QToolButton.InstantPopup)
self.Toolbar.addToolbarWidget(u'Song Menu', self.SongMenu) self.Toolbar.addToolbarWidget(u'Song Menu', self.SongMenu)
self.SongMenu.setMenu(QtGui.QMenu( self.SongMenu.setMenu(QtGui.QMenu(
translate('OpenLP.SlideController', 'Go to'), translate('OpenLP.SlideController', 'Go To'),
self.Toolbar)) self.Toolbar))
self.Toolbar.makeWidgetsInvisible([u'Song Menu']) self.Toolbar.makeWidgetsInvisible([u'Song Menu'])
# Screen preview area # Screen preview area

View File

@ -242,14 +242,14 @@ class ThemeManager(QtGui.QWidget):
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'), translate('OpenLP.ThemeManager', 'Error'),
unicode(translate('OpenLP.ThemeManager', unicode(translate('OpenLP.ThemeManager',
'Theme %s is use in %s plugin.')) % \ 'Theme %s is used in the %s plugin.')) % \
(theme, plugin.name)) (theme, plugin.name))
return return
if unicode(self.serviceComboBox.currentText()) == theme: if unicode(self.serviceComboBox.currentText()) == theme:
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'), translate('OpenLP.ThemeManager', 'Error'),
unicode(translate('OpenLP.ThemeManager', unicode(translate('OpenLP.ThemeManager',
'Theme %s is use by the service manager.')) % theme) 'Theme %s is used by the service manager.')) % theme)
return return
row = self.themeListWidget.row(item) row = self.themeListWidget.row(item)
self.themeListWidget.takeItem(row) self.themeListWidget.takeItem(row)
@ -576,7 +576,7 @@ class ThemeManager(QtGui.QWidget):
translate('OpenLP.ThemeManager', 'Theme Exists'), translate('OpenLP.ThemeManager', 'Theme Exists'),
translate('OpenLP.ThemeManager', translate('OpenLP.ThemeManager',
'A theme with this name already ' 'A theme with this name already '
'exists. Would you like to overwrite it?'), 'exists. Would you like to overwrite it?'),
(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), (QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.No) QtGui.QMessageBox.No)
if self.saveThemeName != u'': if self.saveThemeName != u'':

View File

@ -102,6 +102,7 @@ class AppLocation(object):
PluginsDir = 4 PluginsDir = 4
VersionDir = 5 VersionDir = 5
CacheDir = 6 CacheDir = 6
LanguageDir = 7
@staticmethod @staticmethod
def get_directory(dir_type=1): def get_directory(dir_type=1):
@ -112,7 +113,11 @@ class AppLocation(object):
The directory type you want, for instance the data directory. The directory type you want, for instance the data directory.
""" """
if dir_type == AppLocation.AppDir: if dir_type == AppLocation.AppDir:
return os.path.abspath(os.path.split(sys.argv[0])[0]) if hasattr(sys, u'frozen') and sys.frozen == 1:
app_path = os.path.abspath(os.path.split(sys.argv[0])[0])
else:
app_path = os.path.split(openlp.__file__)[0]
return app_path
elif dir_type == AppLocation.ConfigDir: elif dir_type == AppLocation.ConfigDir:
if sys.platform == u'win32': if sys.platform == u'win32':
path = os.path.join(os.getenv(u'APPDATA'), u'openlp') path = os.path.join(os.getenv(u'APPDATA'), u'openlp')
@ -169,6 +174,13 @@ class AppLocation(object):
except ImportError: except ImportError:
path = os.path.join(os.getenv(u'HOME'), u'.openlp') path = os.path.join(os.getenv(u'HOME'), u'.openlp')
return path return path
if dir_type == AppLocation.LanguageDir:
if hasattr(sys, u'frozen') and sys.frozen == 1:
app_path = os.path.abspath(os.path.split(sys.argv[0])[0])
else:
app_path = os.path.split(openlp.__file__)[0]
return os.path.join(app_path, u'i18n')
@staticmethod @staticmethod
def get_data_path(): def get_data_path():

View File

@ -35,14 +35,14 @@ from PyQt4 import QtCore, QtGui
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation
from openlp.core.lib import translate from openlp.core.lib import translate
log = logging.getLogger() log = logging.getLogger(__name__)
class LanguageManager(object): class LanguageManager(object):
""" """
Helper for Language selection Helper for Language selection
""" """
__qmList__ = None __qm_list__ = {}
AutoLanguage = False auto_language = False
@staticmethod @staticmethod
def get_translator(language): def get_translator(language):
@ -52,12 +52,11 @@ class LanguageManager(object):
``language`` ``language``
The language to load into the translator The language to load into the translator
""" """
if LanguageManager.AutoLanguage: if LanguageManager.auto_language:
language = QtCore.QLocale.system().name() language = QtCore.QLocale.system().name()
lang_path = AppLocation.get_directory(AppLocation.AppDir) lang_path = AppLocation.get_directory(AppLocation.LanguageDir)
lang_path = os.path.join(lang_path, u'resources', u'i18n')
app_translator = QtCore.QTranslator() app_translator = QtCore.QTranslator()
if app_translator.load("openlp_" + language, lang_path): if app_translator.load(language, lang_path):
return app_translator return app_translator
@staticmethod @staticmethod
@ -65,8 +64,8 @@ class LanguageManager(object):
""" """
Find all available language files in this OpenLP install Find all available language files in this OpenLP install
""" """
trans_dir = AppLocation.get_directory(AppLocation.AppDir) trans_dir = QtCore.QDir(AppLocation.get_directory(
trans_dir = QtCore.QDir(os.path.join(trans_dir, u'resources', u'i18n')) AppLocation.LanguageDir))
file_names = trans_dir.entryList(QtCore.QStringList("*.qm"), file_names = trans_dir.entryList(QtCore.QStringList("*.qm"),
QtCore.QDir.Files, QtCore.QDir.Name) QtCore.QDir.Files, QtCore.QDir.Name)
for name in file_names: for name in file_names:
@ -96,7 +95,7 @@ class LanguageManager(object):
log.info(u'Language file: \'%s\' Loaded from conf file' % language) log.info(u'Language file: \'%s\' Loaded from conf file' % language)
reg_ex = QtCore.QRegExp("^\[(.*)\]") reg_ex = QtCore.QRegExp("^\[(.*)\]")
if reg_ex.exactMatch(language): if reg_ex.exactMatch(language):
LanguageManager.AutoLanguage = True LanguageManager.auto_language = True
language = reg_ex.cap(1) language = reg_ex.cap(1)
return language return language
@ -110,7 +109,7 @@ class LanguageManager(object):
""" """
action_name = u'%s' % action.objectName() action_name = u'%s' % action.objectName()
qm_list = LanguageManager.get_qm_list() qm_list = LanguageManager.get_qm_list()
if LanguageManager.AutoLanguage: if LanguageManager.auto_language:
language = u'[%s]' % qm_list[action_name] language = u'[%s]' % qm_list[action_name]
else: else:
language = u'%s' % qm_list[action_name] language = u'%s' % qm_list[action_name]
@ -127,20 +126,18 @@ class LanguageManager(object):
""" """
Initialise the list of available translations Initialise the list of available translations
""" """
LanguageManager.__qmList__ = {} LanguageManager.__qm_list__ = {}
qm_files = LanguageManager.find_qm_files() qm_files = LanguageManager.find_qm_files()
for i, qmf in enumerate(qm_files): for counter, qmf in enumerate(qm_files):
reg_ex = QtCore.QRegExp("^.*openlp_(.*).qm") name = unicode(qmf).split(u'.')[0]
if reg_ex.exactMatch(qmf): LanguageManager.__qm_list__[u'%#2i %s' % (counter + 1,
lang_name = reg_ex.cap(1) LanguageManager.language_name(qmf))] = name
LanguageManager.__qmList__[u'%#2i %s' % (i+1,
LanguageManager.language_name(qmf))] = lang_name
@staticmethod @staticmethod
def get_qm_list(): def get_qm_list():
""" """
Return the list of available translations Return the list of available translations
""" """
if LanguageManager.__qmList__ is None: if not LanguageManager.__qm_list__:
LanguageManager.init_qm_list() LanguageManager.init_qm_list()
return LanguageManager.__qmList__ return LanguageManager.__qm_list__

View File

@ -181,7 +181,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
translate('BiblesPlugin.ImportWizardForm', translate('BiblesPlugin.ImportWizardForm',
'Empty Copyright'), 'Empty Copyright'),
translate('BiblesPlugin.ImportWizardForm', translate('BiblesPlugin.ImportWizardForm',
'You need to set a copyright for your Bible! ' 'You need to set a copyright for your Bible. '
'Bibles in the Public Domain need to be marked as such.')) 'Bibles in the Public Domain need to be marked as such.'))
self.CopyrightEdit.setFocus() self.CopyrightEdit.setFocus()
return False return False
@ -189,7 +189,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
translate('BiblesPlugin.ImportWizardForm', 'Bible Exists'), translate('BiblesPlugin.ImportWizardForm', 'Bible Exists'),
translate('BiblesPlugin.ImportWizardForm', translate('BiblesPlugin.ImportWizardForm',
'This Bible already exists! Please import ' 'This Bible already exists. Please import '
'a different Bible or first delete the existing one.')) 'a different Bible or first delete the existing one.'))
self.VersionNameEdit.setFocus() self.VersionNameEdit.setFocus()
return False return False

View File

@ -353,7 +353,7 @@ class BibleDB(QtCore.QObject, Manager):
QtGui.QMessageBox.information(self.bible_plugin.mediaItem, QtGui.QMessageBox.information(self.bible_plugin.mediaItem,
translate('BiblesPlugin.BibleDB', 'Book not found'), translate('BiblesPlugin.BibleDB', 'Book not found'),
translate('BiblesPlugin.BibleDB', 'The book you requested ' translate('BiblesPlugin.BibleDB', 'The book you requested '
'could not be found in this bible. Please check your ' 'could not be found in this bible. Please check your '
'spelling and that this is a complete bible not just ' 'spelling and that this is a complete bible not just '
'one testament.')) 'one testament.'))
return verse_list return verse_list

View File

@ -246,7 +246,7 @@ class BibleManager(object):
translate('BiblesPlugin.BibleManager', translate('BiblesPlugin.BibleManager',
'Scripture Reference Error'), 'Scripture Reference Error'),
translate('BiblesPlugin.BibleManager', 'Your scripture ' translate('BiblesPlugin.BibleManager', 'Your scripture '
'reference is either not supported by OpenLP or invalid. ' 'reference is either not supported by OpenLP or is invalid. '
'Please make sure your reference conforms to one of the ' 'Please make sure your reference conforms to one of the '
'following patterns:\n\n' 'following patterns:\n\n'
'Book Chapter\n' 'Book Chapter\n'

View File

@ -777,7 +777,8 @@ class BibleMediaItem(MediaManagerItem):
self.dual_search_results[count].text) self.dual_search_results[count].text)
} }
bible_text = u' %s %d:%d (%s, %s)' % (verse.book.name, bible_text = u' %s %d:%d (%s, %s)' % (verse.book.name,
verse.chapter, verse.verse, version.value, dual_version.value) verse.chapter, verse.verse, version.value,
dual_version.value)
else: else:
vdict = { vdict = {
'book': QtCore.QVariant(verse.book.name), 'book': QtCore.QVariant(verse.book.name),

View File

@ -89,7 +89,7 @@ class OpenSongBible(BibleDB):
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
self.wizard.incrementProgressBar( self.wizard.incrementProgressBar(
QtCore.QString('%s %s %s' % ( QtCore.QString('%s %s %s' % (
translate('BiblesPlugin.Opensong', 'Importing'), \ translate('BiblesPlugin.Opensong', 'Importing'),
db_book.name, chapter.attrib[u'n']))) db_book.name, chapter.attrib[u'n'])))
self.session.commit() self.session.commit()
except IOError: except IOError:

View File

@ -74,6 +74,7 @@ class ImpressController(PresentationController):
self.process = None self.process = None
self.desktop = None self.desktop = None
self.manager = None self.manager = None
self.uno_connection_type = u'pipe' #u'socket'
def check_available(self): def check_available(self):
""" """
@ -98,7 +99,14 @@ class ImpressController(PresentationController):
self.manager._FlagAsMethod(u'Bridge_GetValueObject') self.manager._FlagAsMethod(u'Bridge_GetValueObject')
else: else:
# -headless # -headless
cmd = u'openoffice.org -nologo -norestore -minimized -invisible -nofirststartwizard -accept="socket,host=localhost,port=2002;urp;"' if self.uno_connection_type == u'pipe':
cmd = u'openoffice.org -nologo -norestore -minimized ' \
+ u'-invisible -nofirststartwizard ' \
+ u'-accept=pipe,name=openlp_pipe;urp;'
else:
cmd = u'openoffice.org -nologo -norestore -minimized ' \
+ u'-invisible -nofirststartwizard ' \
+ u'-accept=socket,host=localhost,port=2002;urp;'
self.process = QtCore.QProcess() self.process = QtCore.QProcess()
self.process.startDetached(cmd) self.process.startDetached(cmd)
self.process.waitForStarted() self.process.waitForStarted()
@ -120,8 +128,14 @@ class ImpressController(PresentationController):
while ctx is None and loop < 3: while ctx is None and loop < 3:
try: try:
log.debug(u'get UNO Desktop Openoffice - resolve') log.debug(u'get UNO Desktop Openoffice - resolve')
ctx = resolver.resolve(u'uno:socket,host=localhost,port=2002;' if self.uno_connection_type == u'pipe':
u'urp;StarOffice.ComponentContext') ctx = resolver.resolve(u'uno:' \
+ u'pipe,name=openlp_pipe;' \
+ u'urp;StarOffice.ComponentContext')
else:
ctx = resolver.resolve(u'uno:' \
+ u'socket,host=localhost,port=2002;' \
+ u'urp;StarOffice.ComponentContext')
except: except:
log.exception(u'Unable to find running instance ') log.exception(u'Unable to find running instance ')
self.start_process() self.start_process()

View File

@ -212,7 +212,7 @@ class PresentationMediaItem(MediaManagerItem):
self, translate('PresentationPlugin.MediaItem', self, translate('PresentationPlugin.MediaItem',
'Unsupported File'), 'Unsupported File'),
translate('PresentationPlugin.MediaItem', translate('PresentationPlugin.MediaItem',
'This type of presentation is not supported')) 'This type of presentation is not supported.'))
continue continue
item_name = QtGui.QListWidgetItem(filename) item_name = QtGui.QListWidgetItem(filename)
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))

0
openlp/plugins/remotes/html/jquery.js vendored Executable file → Normal file
View File

View File

@ -97,8 +97,7 @@ class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
self, translate('SongsPlugin.AuthorsForm', 'Error'), self, translate('SongsPlugin.AuthorsForm', 'Error'),
translate('SongsPlugin.AuthorsForm', translate('SongsPlugin.AuthorsForm',
'You have not set a display name for the ' 'You have not set a display name for the '
'author, would you like me to combine the first and ' 'author, combine the first and last names?'),
'last names for you?'),
QtGui.QMessageBox.StandardButtons( QtGui.QMessageBox.StandardButtons(
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
) == QtGui.QMessageBox.Yes: ) == QtGui.QMessageBox.Yes:

View File

@ -258,10 +258,11 @@ class Ui_EditSongDialog(object):
self.TopicBookLayout.addWidget(self.TopicGroupBox) self.TopicBookLayout.addWidget(self.TopicGroupBox)
self.SongBookGroup = QtGui.QGroupBox(self.TopicBookWidget) self.SongBookGroup = QtGui.QGroupBox(self.TopicBookWidget)
self.SongBookGroup.setObjectName(u'SongBookGroup') self.SongBookGroup.setObjectName(u'SongBookGroup')
self.SongbookLayout = QtGui.QGridLayout(self.SongBookGroup) self.SongbookLayout = QtGui.QFormLayout(self.SongBookGroup)
self.SongbookLayout.setMargin(8) self.SongbookLayout.setMargin(8)
self.SongbookLayout.setSpacing(8) self.SongbookLayout.setSpacing(8)
self.SongbookLayout.setObjectName(u'SongbookLayout') self.SongbookLayout.setObjectName(u'SongbookLayout')
self.SongbookNameLabel = QtGui.QLabel(self.SongBookGroup)
self.SongbookCombo = QtGui.QComboBox(self.SongBookGroup) self.SongbookCombo = QtGui.QComboBox(self.SongBookGroup)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding,
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Fixed)
@ -272,13 +273,11 @@ class Ui_EditSongDialog(object):
self.SongbookCombo.setEditable(True) self.SongbookCombo.setEditable(True)
self.SongbookCombo.setSizePolicy(sizePolicy) self.SongbookCombo.setSizePolicy(sizePolicy)
self.SongbookCombo.setObjectName(u'SongbookCombo') self.SongbookCombo.setObjectName(u'SongbookCombo')
self.SongbookLayout.addWidget(self.SongbookCombo, 0, 0, 1, 1) self.SongbookLayout.addRow(self.SongbookNameLabel, self.SongbookCombo)
self.songBookNumberLabel = QtGui.QLabel(self.SongBookGroup) self.songBookNumberLabel = QtGui.QLabel(self.SongBookGroup)
self.SongbookLayout.addWidget(self.songBookNumberLabel, 0, 1, 1, 1)
self.songBookNumberEdit = QtGui.QLineEdit(self.SongBookGroup) self.songBookNumberEdit = QtGui.QLineEdit(self.SongBookGroup)
self.songBookNumberLabel.setBuddy(self.songBookNumberEdit) self.SongbookLayout.addRow(self.songBookNumberLabel,
self.songBookNumberEdit.setMaximumWidth(35) self.songBookNumberEdit)
self.SongbookLayout.addWidget(self.songBookNumberEdit, 0, 2, 1, 1)
self.TopicBookLayout.addWidget(self.SongBookGroup) self.TopicBookLayout.addWidget(self.SongBookGroup)
self.AuthorsTabLayout.addWidget(self.TopicBookWidget) self.AuthorsTabLayout.addWidget(self.TopicBookWidget)
self.SongTabWidget.addTab(self.AuthorsTab, u'') self.SongTabWidget.addTab(self.AuthorsTab, u'')
@ -446,8 +445,10 @@ class Ui_EditSongDialog(object):
translate('SongsPlugin.EditSongForm', 'R&emove')) translate('SongsPlugin.EditSongForm', 'R&emove'))
self.SongBookGroup.setTitle( self.SongBookGroup.setTitle(
translate('SongsPlugin.EditSongForm', 'Song Book')) translate('SongsPlugin.EditSongForm', 'Song Book'))
self.SongbookNameLabel.setText(translate('SongsPlugin.EditSongForm',
'Book:'))
self.songBookNumberLabel.setText(translate('SongsPlugin.EditSongForm', self.songBookNumberLabel.setText(translate('SongsPlugin.EditSongForm',
'Song No.:')) 'Number:'))
self.SongTabWidget.setTabText( self.SongTabWidget.setTabText(
self.SongTabWidget.indexOf(self.AuthorsTab), self.SongTabWidget.indexOf(self.AuthorsTab),
translate('SongsPlugin.EditSongForm', translate('SongsPlugin.EditSongForm',

View File

@ -621,6 +621,10 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.close() self.close()
def saveSong(self): def saveSong(self):
"""
Get all the data from the widgets on the form, and then save it to the
database.
"""
self.song.title = unicode(self.TitleEditItem.text()) self.song.title = unicode(self.TitleEditItem.text())
self.song.alternate_title = unicode(self.AlternativeEdit.text()) self.song.alternate_title = unicode(self.AlternativeEdit.text())
self.song.copyright = unicode(self.CopyrightEditItem.text()) self.song.copyright = unicode(self.CopyrightEditItem.text())
@ -646,6 +650,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.song.topics.append(self.songmanager.get_object(Topic, self.song.topics.append(self.songmanager.get_object(Topic,
topicId)) topicId))
self.songmanager.save_object(self.song) self.songmanager.save_object(self.song)
self.song = None
return True return True
return False return False

View File

@ -57,6 +57,15 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.registerFields() self.registerFields()
self.finishButton = self.button(QtGui.QWizard.FinishButton) self.finishButton = self.button(QtGui.QWizard.FinishButton)
self.cancelButton = self.button(QtGui.QWizard.CancelButton) self.cancelButton = self.button(QtGui.QWizard.CancelButton)
if not SongFormat.get_availability(SongFormat.OpenLP1):
self.openLP1DisabledWidget.setVisible(True)
self.openLP1ImportWidget.setVisible(False)
if not SongFormat.get_availability(SongFormat.SongsOfFellowship):
self.songsOfFellowshipDisabledWidget.setVisible(True)
self.songsOfFellowshipImportWidget.setVisible(False)
if not SongFormat.get_availability(SongFormat.Generic):
self.genericDisabledWidget.setVisible(True)
self.genericImportWidget.setVisible(False)
self.plugin = plugin self.plugin = plugin
QtCore.QObject.connect(self.openLP2BrowseButton, QtCore.QObject.connect(self.openLP2BrowseButton,
QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
@ -64,12 +73,12 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
QtCore.QObject.connect(self.openLP1BrowseButton, QtCore.QObject.connect(self.openLP1BrowseButton,
QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
self.onOpenLP1BrowseButtonClicked) self.onOpenLP1BrowseButtonClicked)
QtCore.QObject.connect(self.openLyricsAddButton, #QtCore.QObject.connect(self.openLyricsAddButton,
QtCore.SIGNAL(u'clicked()'), # QtCore.SIGNAL(u'clicked()'),
self.onOpenLyricsAddButtonClicked) # self.onOpenLyricsAddButtonClicked)
QtCore.QObject.connect(self.openLyricsRemoveButton, #QtCore.QObject.connect(self.openLyricsRemoveButton,
QtCore.SIGNAL(u'clicked()'), # QtCore.SIGNAL(u'clicked()'),
self.onOpenLyricsRemoveButtonClicked) # self.onOpenLyricsRemoveButtonClicked)
QtCore.QObject.connect(self.openSongAddButton, QtCore.QObject.connect(self.openSongAddButton,
QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
self.onOpenSongAddButtonClicked) self.onOpenSongAddButtonClicked)
@ -145,15 +154,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.openLP1BrowseButton.setFocus() self.openLP1BrowseButton.setFocus()
return False return False
elif source_format == SongFormat.OpenLyrics: elif source_format == SongFormat.OpenLyrics:
if self.openLyricsFileListWidget.count() == 0: #if self.openLyricsFileListWidget.count() == 0:
QtGui.QMessageBox.critical(self, # QtGui.QMessageBox.critical(self,
translate('SongsPlugin.ImportWizardForm', # translate('SongsPlugin.ImportWizardForm',
'No OpenLyrics Files Selected'), # 'No OpenLyrics Files Selected'),
translate('SongsPlugin.ImportWizardForm', # translate('SongsPlugin.ImportWizardForm',
'You need to add at least one OpenLyrics ' # 'You need to add at least one OpenLyrics '
'song file to import from.')) # 'song file to import from.'))
self.openLyricsAddButton.setFocus() # self.openLyricsAddButton.setFocus()
return False # return False
return False
elif source_format == SongFormat.OpenSong: elif source_format == SongFormat.OpenSong:
if self.openSongFileListWidget.count() == 0: if self.openSongFileListWidget.count() == 0:
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
@ -252,15 +262,15 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.openLP1FilenameEdit self.openLP1FilenameEdit
) )
def onOpenLyricsAddButtonClicked(self): #def onOpenLyricsAddButtonClicked(self):
self.getFiles( # self.getFiles(
translate('SongsPlugin.ImportWizardForm', # translate('SongsPlugin.ImportWizardForm',
'Select OpenLyrics Files'), # 'Select OpenLyrics Files'),
self.openLyricsFileListWidget # self.openLyricsFileListWidget
) # )
def onOpenLyricsRemoveButtonClicked(self): #def onOpenLyricsRemoveButtonClicked(self):
self.removeSelectedItems(self.openLyricsFileListWidget) # self.removeSelectedItems(self.openLyricsFileListWidget)
def onOpenSongAddButtonClicked(self): def onOpenSongAddButtonClicked(self):
self.getFiles( self.getFiles(
@ -334,7 +344,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.formatComboBox.setCurrentIndex(0) self.formatComboBox.setCurrentIndex(0)
self.openLP2FilenameEdit.setText(u'') self.openLP2FilenameEdit.setText(u'')
self.openLP1FilenameEdit.setText(u'') self.openLP1FilenameEdit.setText(u'')
self.openLyricsFileListWidget.clear() #self.openLyricsFileListWidget.clear()
self.openSongFileListWidget.clear() self.openSongFileListWidget.clear()
self.wordsOfWorshipFileListWidget.clear() self.wordsOfWorshipFileListWidget.clear()
self.ccliFileListWidget.clear() self.ccliFileListWidget.clear()

View File

@ -131,26 +131,43 @@ class Ui_SongImportWizard(object):
# openlp.org 1.x # openlp.org 1.x
self.openLP1Page = QtGui.QWidget() self.openLP1Page = QtGui.QWidget()
self.openLP1Page.setObjectName(u'openLP1Page') self.openLP1Page.setObjectName(u'openLP1Page')
self.openLP1Layout = QtGui.QFormLayout(self.openLP1Page) self.openLP1Layout = QtGui.QVBoxLayout(self.openLP1Page)
self.openLP1Layout.setMargin(0) self.openLP1Layout.setMargin(0)
self.openLP1Layout.setSpacing(8) self.openLP1Layout.setSpacing(0)
self.openLP1Layout.setObjectName(u'openLP1Layout') self.openLP1Layout.setObjectName(u'openLP1Layout')
self.openLP1FilenameLabel = QtGui.QLabel(self.openLP1Page) self.openLP1DisabledWidget = QtGui.QWidget(self.openLP1Page)
self.openLP1DisabledLayout = QtGui.QVBoxLayout(self.openLP1DisabledWidget)
self.openLP1DisabledLayout.setMargin(0)
self.openLP1DisabledLayout.setSpacing(8)
self.openLP1DisabledLayout.setObjectName(u'openLP1DisabledLayout')
self.openLP1DisabledLabel = QtGui.QLabel(self.openLP1DisabledWidget)
self.openLP1DisabledLabel.setWordWrap(True)
self.openLP1DisabledLabel.setObjectName(u'openLP1DisabledLabel')
self.openLP1DisabledLayout.addWidget(self.openLP1DisabledLabel)
self.openLP1DisabledWidget.setVisible(False)
self.openLP1Layout.addWidget(self.openLP1DisabledWidget)
self.openLP1ImportWidget = QtGui.QWidget(self.openLP1Page)
self.openLP1ImportLayout = QtGui.QFormLayout(self.openLP1ImportWidget)
self.openLP1ImportLayout.setMargin(0)
self.openLP1ImportLayout.setSpacing(8)
self.openLP1ImportLayout.setObjectName(u'openLP1ImportLayout')
self.openLP1FilenameLabel = QtGui.QLabel(self.openLP1ImportWidget)
self.openLP1FilenameLabel.setObjectName(u'openLP1FilenameLabel') self.openLP1FilenameLabel.setObjectName(u'openLP1FilenameLabel')
self.openLP1Layout.setWidget(0, QtGui.QFormLayout.LabelRole, self.openLP1ImportLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
self.openLP1FilenameLabel) self.openLP1FilenameLabel)
self.openLP1FileLayout = QtGui.QHBoxLayout() self.openLP1FileLayout = QtGui.QHBoxLayout()
self.openLP1FileLayout.setSpacing(8) self.openLP1FileLayout.setSpacing(8)
self.openLP1FileLayout.setObjectName(u'openLP1FileLayout') self.openLP1FileLayout.setObjectName(u'openLP1FileLayout')
self.openLP1FilenameEdit = QtGui.QLineEdit(self.openLP1Page) self.openLP1FilenameEdit = QtGui.QLineEdit(self.openLP1ImportWidget)
self.openLP1FilenameEdit.setObjectName(u'openLP1FilenameEdit') self.openLP1FilenameEdit.setObjectName(u'openLP1FilenameEdit')
self.openLP1FileLayout.addWidget(self.openLP1FilenameEdit) self.openLP1FileLayout.addWidget(self.openLP1FilenameEdit)
self.openLP1BrowseButton = QtGui.QToolButton(self.openLP1Page) self.openLP1BrowseButton = QtGui.QToolButton(self.openLP1ImportWidget)
self.openLP1BrowseButton.setIcon(openIcon) self.openLP1BrowseButton.setIcon(openIcon)
self.openLP1BrowseButton.setObjectName(u'openLP1BrowseButton') self.openLP1BrowseButton.setObjectName(u'openLP1BrowseButton')
self.openLP1FileLayout.addWidget(self.openLP1BrowseButton) self.openLP1FileLayout.addWidget(self.openLP1BrowseButton)
self.openLP1Layout.setLayout(0, QtGui.QFormLayout.FieldRole, self.openLP1ImportLayout.setLayout(0, QtGui.QFormLayout.FieldRole,
self.openLP1FileLayout) self.openLP1FileLayout)
self.openLP1Layout.addWidget(self.openLP1ImportWidget)
self.formatStackedWidget.addWidget(self.openLP1Page) self.formatStackedWidget.addWidget(self.openLP1Page)
# OpenLyrics # OpenLyrics
self.openLyricsPage = QtGui.QWidget() self.openLyricsPage = QtGui.QWidget()
@ -159,26 +176,31 @@ class Ui_SongImportWizard(object):
self.openLyricsLayout.setSpacing(8) self.openLyricsLayout.setSpacing(8)
self.openLyricsLayout.setMargin(0) self.openLyricsLayout.setMargin(0)
self.openLyricsLayout.setObjectName(u'OpenLyricsLayout') self.openLyricsLayout.setObjectName(u'OpenLyricsLayout')
self.openLyricsFileListWidget = QtGui.QListWidget(self.openLyricsPage) self.openLyricsDisabledLabel = QtGui.QLabel(self.openLyricsPage)
self.openLyricsFileListWidget.setSelectionMode( self.openLyricsDisabledLabel.setWordWrap(True)
QtGui.QAbstractItemView.ExtendedSelection) self.openLyricsDisabledLabel.setObjectName(u'openLyricsDisabledLabel')
self.openLyricsFileListWidget.setObjectName(u'OpenLyricsFileListWidget') self.openLyricsLayout.addWidget(self.openLyricsDisabledLabel)
self.openLyricsLayout.addWidget(self.openLyricsFileListWidget) # Commented out for future use.
self.openLyricsButtonLayout = QtGui.QHBoxLayout() #self.openLyricsFileListWidget = QtGui.QListWidget(self.openLyricsPage)
self.openLyricsButtonLayout.setSpacing(8) #self.openLyricsFileListWidget.setSelectionMode(
self.openLyricsButtonLayout.setObjectName(u'OpenLyricsButtonLayout') # QtGui.QAbstractItemView.ExtendedSelection)
self.openLyricsAddButton = QtGui.QPushButton(self.openLyricsPage) #self.openLyricsFileListWidget.setObjectName(u'OpenLyricsFileListWidget')
self.openLyricsAddButton.setIcon(openIcon) #self.openLyricsLayout.addWidget(self.openLyricsFileListWidget)
self.openLyricsAddButton.setObjectName(u'OpenLyricsAddButton') #self.openLyricsButtonLayout = QtGui.QHBoxLayout()
self.openLyricsButtonLayout.addWidget(self.openLyricsAddButton) #self.openLyricsButtonLayout.setSpacing(8)
self.openLyricsButtonSpacer = QtGui.QSpacerItem(40, 20, #self.openLyricsButtonLayout.setObjectName(u'OpenLyricsButtonLayout')
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) #self.openLyricsAddButton = QtGui.QPushButton(self.openLyricsPage)
self.openLyricsButtonLayout.addItem(self.openLyricsButtonSpacer) #self.openLyricsAddButton.setIcon(openIcon)
self.openLyricsRemoveButton = QtGui.QPushButton(self.openLyricsPage) #self.openLyricsAddButton.setObjectName(u'OpenLyricsAddButton')
self.openLyricsRemoveButton.setIcon(deleteIcon) #self.openLyricsButtonLayout.addWidget(self.openLyricsAddButton)
self.openLyricsRemoveButton.setObjectName(u'OpenLyricsRemoveButton') #self.openLyricsButtonSpacer = QtGui.QSpacerItem(40, 20,
self.openLyricsButtonLayout.addWidget(self.openLyricsRemoveButton) # QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.openLyricsLayout.addLayout(self.openLyricsButtonLayout) #self.openLyricsButtonLayout.addItem(self.openLyricsButtonSpacer)
#self.openLyricsRemoveButton = QtGui.QPushButton(self.openLyricsPage)
#self.openLyricsRemoveButton.setIcon(deleteIcon)
#self.openLyricsRemoveButton.setObjectName(u'OpenLyricsRemoveButton')
#self.openLyricsButtonLayout.addWidget(self.openLyricsRemoveButton)
#self.openLyricsLayout.addLayout(self.openLyricsButtonLayout)
self.formatStackedWidget.addWidget(self.openLyricsPage) self.formatStackedWidget.addWidget(self.openLyricsPage)
# Open Song # Open Song
self.openSongPage = QtGui.QWidget() self.openSongPage = QtGui.QWidget()
@ -277,22 +299,52 @@ class Ui_SongImportWizard(object):
self.songsOfFellowshipLayout = QtGui.QVBoxLayout( self.songsOfFellowshipLayout = QtGui.QVBoxLayout(
self.songsOfFellowshipPage) self.songsOfFellowshipPage)
self.songsOfFellowshipLayout.setMargin(0) self.songsOfFellowshipLayout.setMargin(0)
self.songsOfFellowshipLayout.setSpacing(8) self.songsOfFellowshipLayout.setSpacing(0)
self.songsOfFellowshipLayout.setObjectName(u'songsOfFellowshipLayout') self.songsOfFellowshipLayout.setObjectName(u'songsOfFellowshipLayout')
self.songsOfFellowshipFileListWidget = QtGui.QListWidget( self.songsOfFellowshipDisabledWidget = QtGui.QWidget(
self.songsOfFellowshipPage) self.songsOfFellowshipPage)
self.songsOfFellowshipDisabledWidget.setVisible(False)
self.songsOfFellowshipDisabledWidget.setObjectName(
u'songsOfFellowshipDisabledWidget')
self.songsOfFellowshipDisabledLayout = QtGui.QVBoxLayout(
self.songsOfFellowshipDisabledWidget)
self.songsOfFellowshipDisabledLayout.setMargin(0)
self.songsOfFellowshipDisabledLayout.setSpacing(8)
self.songsOfFellowshipDisabledLayout.setObjectName(
u'songsOfFellowshipDisabledLayout')
self.songsOfFellowshipDisabledLabel = QtGui.QLabel(
self.songsOfFellowshipDisabledWidget)
self.songsOfFellowshipDisabledLabel.setWordWrap(True)
self.songsOfFellowshipDisabledLabel.setObjectName(
u'songsOfFellowshipDisabledLabel')
self.songsOfFellowshipDisabledLayout.addWidget(
self.songsOfFellowshipDisabledLabel)
self.songsOfFellowshipLayout.addWidget(
self.songsOfFellowshipDisabledWidget)
self.songsOfFellowshipImportWidget = QtGui.QWidget(
self.songsOfFellowshipPage)
self.songsOfFellowshipImportWidget.setObjectName(
u'songsOfFellowshipImportWidget')
self.songsOfFellowshipImportLayout = QtGui.QVBoxLayout(
self.songsOfFellowshipImportWidget)
self.songsOfFellowshipImportLayout.setMargin(0)
self.songsOfFellowshipImportLayout.setSpacing(8)
self.songsOfFellowshipImportLayout.setObjectName(
u'songsOfFellowshipImportLayout')
self.songsOfFellowshipFileListWidget = QtGui.QListWidget(
self.songsOfFellowshipImportWidget)
self.songsOfFellowshipFileListWidget.setSelectionMode( self.songsOfFellowshipFileListWidget.setSelectionMode(
QtGui.QAbstractItemView.ExtendedSelection) QtGui.QAbstractItemView.ExtendedSelection)
self.songsOfFellowshipFileListWidget.setObjectName( self.songsOfFellowshipFileListWidget.setObjectName(
u'songsOfFellowshipFileListWidget') u'songsOfFellowshipFileListWidget')
self.songsOfFellowshipLayout.addWidget( self.songsOfFellowshipImportLayout.addWidget(
self.songsOfFellowshipFileListWidget) self.songsOfFellowshipFileListWidget)
self.songsOfFellowshipButtonLayout = QtGui.QHBoxLayout() self.songsOfFellowshipButtonLayout = QtGui.QHBoxLayout()
self.songsOfFellowshipButtonLayout.setSpacing(8) self.songsOfFellowshipButtonLayout.setSpacing(8)
self.songsOfFellowshipButtonLayout.setObjectName( self.songsOfFellowshipButtonLayout.setObjectName(
u'songsOfFellowshipButtonLayout') u'songsOfFellowshipButtonLayout')
self.songsOfFellowshipAddButton = QtGui.QPushButton( self.songsOfFellowshipAddButton = QtGui.QPushButton(
self.songsOfFellowshipPage) self.songsOfFellowshipImportWidget)
self.songsOfFellowshipAddButton.setIcon(openIcon) self.songsOfFellowshipAddButton.setIcon(openIcon)
self.songsOfFellowshipAddButton.setObjectName( self.songsOfFellowshipAddButton.setObjectName(
u'songsOfFellowshipAddButton') u'songsOfFellowshipAddButton')
@ -303,42 +355,63 @@ class Ui_SongImportWizard(object):
self.songsOfFellowshipButtonLayout.addItem( self.songsOfFellowshipButtonLayout.addItem(
self.songsOfFellowshipButtonSpacer) self.songsOfFellowshipButtonSpacer)
self.songsOfFellowshipRemoveButton = QtGui.QPushButton( self.songsOfFellowshipRemoveButton = QtGui.QPushButton(
self.songsOfFellowshipPage) self.songsOfFellowshipImportWidget)
self.songsOfFellowshipRemoveButton.setIcon(deleteIcon) self.songsOfFellowshipRemoveButton.setIcon(deleteIcon)
self.songsOfFellowshipRemoveButton.setObjectName( self.songsOfFellowshipRemoveButton.setObjectName(
u'songsOfFellowshipRemoveButton') u'songsOfFellowshipRemoveButton')
self.songsOfFellowshipButtonLayout.addWidget( self.songsOfFellowshipButtonLayout.addWidget(
self.songsOfFellowshipRemoveButton) self.songsOfFellowshipRemoveButton)
self.songsOfFellowshipLayout.addLayout( self.songsOfFellowshipImportLayout.addLayout(
self.songsOfFellowshipButtonLayout) self.songsOfFellowshipButtonLayout)
self.songsOfFellowshipLayout.addWidget(
self.songsOfFellowshipImportWidget)
self.formatStackedWidget.addWidget(self.songsOfFellowshipPage) self.formatStackedWidget.addWidget(self.songsOfFellowshipPage)
# Generic Document/Presentation import # Generic Document/Presentation import
self.genericPage = QtGui.QWidget() self.genericPage = QtGui.QWidget()
self.genericPage.setObjectName(u'genericPage') self.genericPage.setObjectName(u'genericPage')
self.genericLayout = QtGui.QVBoxLayout(self.genericPage) self.genericLayout = QtGui.QVBoxLayout(self.genericPage)
self.genericLayout.setMargin(0) self.genericLayout.setMargin(0)
self.genericLayout.setSpacing(8) self.genericLayout.setSpacing(0)
self.genericLayout.setObjectName(u'genericLayout') self.genericLayout.setObjectName(u'genericLayout')
self.genericFileListWidget = QtGui.QListWidget(self.genericPage) self.genericDisabledWidget = QtGui.QWidget(self.genericPage)
self.genericDisabledWidget.setObjectName(u'genericDisabledWidget')
self.genericDisabledLayout = QtGui.QVBoxLayout(self.genericDisabledWidget)
self.genericDisabledLayout.setMargin(0)
self.genericDisabledLayout.setSpacing(8)
self.genericDisabledLayout.setObjectName(u'genericDisabledLayout')
self.genericDisabledLabel = QtGui.QLabel(self.genericDisabledWidget)
self.genericDisabledLabel.setWordWrap(True)
self.genericDisabledLabel.setObjectName(u'genericDisabledLabel')
self.genericDisabledWidget.setVisible(False)
self.genericDisabledLayout.addWidget(self.genericDisabledLabel)
self.genericLayout.addWidget(self.genericDisabledWidget)
self.genericImportWidget = QtGui.QWidget(self.genericPage)
self.genericImportWidget.setObjectName(u'genericImportWidget')
self.genericImportLayout = QtGui.QVBoxLayout(self.genericImportWidget)
self.genericImportLayout.setMargin(0)
self.genericImportLayout.setSpacing(8)
self.genericImportLayout.setObjectName(u'genericImportLayout')
self.genericFileListWidget = QtGui.QListWidget(self.genericImportWidget)
self.genericFileListWidget.setSelectionMode( self.genericFileListWidget.setSelectionMode(
QtGui.QAbstractItemView.ExtendedSelection) QtGui.QAbstractItemView.ExtendedSelection)
self.genericFileListWidget.setObjectName(u'genericFileListWidget') self.genericFileListWidget.setObjectName(u'genericFileListWidget')
self.genericLayout.addWidget(self.genericFileListWidget) self.genericImportLayout.addWidget(self.genericFileListWidget)
self.genericButtonLayout = QtGui.QHBoxLayout() self.genericButtonLayout = QtGui.QHBoxLayout()
self.genericButtonLayout.setSpacing(8) self.genericButtonLayout.setSpacing(8)
self.genericButtonLayout.setObjectName(u'genericButtonLayout') self.genericButtonLayout.setObjectName(u'genericButtonLayout')
self.genericAddButton = QtGui.QPushButton(self.genericPage) self.genericAddButton = QtGui.QPushButton(self.genericImportWidget)
self.genericAddButton.setIcon(openIcon) self.genericAddButton.setIcon(openIcon)
self.genericAddButton.setObjectName(u'genericAddButton') self.genericAddButton.setObjectName(u'genericAddButton')
self.genericButtonLayout.addWidget(self.genericAddButton) self.genericButtonLayout.addWidget(self.genericAddButton)
self.genericButtonSpacer = QtGui.QSpacerItem(40, 20, self.genericButtonSpacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.genericButtonLayout.addItem(self.genericButtonSpacer) self.genericButtonLayout.addItem(self.genericButtonSpacer)
self.genericRemoveButton = QtGui.QPushButton(self.genericPage) self.genericRemoveButton = QtGui.QPushButton(self.genericImportWidget)
self.genericRemoveButton.setIcon(deleteIcon) self.genericRemoveButton.setIcon(deleteIcon)
self.genericRemoveButton.setObjectName(u'genericRemoveButton') self.genericRemoveButton.setObjectName(u'genericRemoveButton')
self.genericButtonLayout.addWidget(self.genericRemoveButton) self.genericButtonLayout.addWidget(self.genericRemoveButton)
self.genericLayout.addLayout(self.genericButtonLayout) self.genericImportLayout.addLayout(self.genericButtonLayout)
self.genericLayout.addWidget(self.genericImportWidget)
self.formatStackedWidget.addWidget(self.genericPage) self.formatStackedWidget.addWidget(self.genericPage)
# Commented out for future use. # Commented out for future use.
# self.csvPage = QtGui.QWidget() # self.csvPage = QtGui.QWidget()
@ -434,10 +507,20 @@ class Ui_SongImportWizard(object):
translate('SongsPlugin.ImportWizardForm', 'Filename:')) translate('SongsPlugin.ImportWizardForm', 'Filename:'))
self.openLP1BrowseButton.setText( self.openLP1BrowseButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Browse...')) translate('SongsPlugin.ImportWizardForm', 'Browse...'))
self.openLyricsAddButton.setText( self.openLP1DisabledLabel.setText(
translate('SongsPlugin.ImportWizardForm', 'Add Files...')) translate('SongsPlugin.ImportWizardForm', 'The openlp.org 1.x '
self.openLyricsRemoveButton.setText( 'importer has been disabled due to a missing Python module. If '
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) 'you want to use this importer, you will need to install the '
'"python-sqlite" module.'))
#self.openLyricsAddButton.setText(
# translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
#self.openLyricsRemoveButton.setText(
# translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
self.openLyricsDisabledLabel.setText(
translate('SongsPlugin.ImportWizardForm', 'The OpenLyrics '
'importer has not yet been developed, but as you can see, we are '
'still intending to do so. Hopefully it will be in the next '
'release.'))
self.openSongAddButton.setText( self.openSongAddButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Add Files...')) translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
self.openSongRemoveButton.setText( self.openSongRemoveButton.setText(
@ -454,10 +537,18 @@ class Ui_SongImportWizard(object):
translate('SongsPlugin.ImportWizardForm', 'Add Files...')) translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
self.songsOfFellowshipRemoveButton.setText( self.songsOfFellowshipRemoveButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
self.songsOfFellowshipDisabledLabel.setText(
translate('SongsPlugin.ImportWizardForm', 'The Songs of '
'Fellowship importer has been disabled because OpenLP cannot '
'find OpenOffice.org on your computer.'))
self.genericAddButton.setText( self.genericAddButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Add Files...')) translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
self.genericRemoveButton.setText( self.genericRemoveButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
self.genericDisabledLabel.setText(
translate('SongsPlugin.ImportWizardForm', 'The generic document/'
'presentation importer has been disabled because OpenLP cannot '
'find OpenOffice.org on your computer.'))
# self.csvFilenameLabel.setText( # self.csvFilenameLabel.setText(
# translate('SongsPlugin.ImportWizardForm', 'Filename:')) # translate('SongsPlugin.ImportWizardForm', 'Filename:'))
# self.csvBrowseButton.setText( # self.csvBrowseButton.setText(

View File

@ -324,8 +324,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'Could not save your modified author, because he ' 'Could not save your modified author, because the '
'already exists.')) 'author already exists.'))
def onTopicEditButtonClick(self): def onTopicEditButtonClick(self):
topic_id = self._getCurrentItemId(self.TopicsListWidget) topic_id = self._getCurrentItemId(self.TopicsListWidget)

View File

@ -51,7 +51,7 @@ class TopicsForm(QtGui.QDialog, Ui_TopicsDialog):
QtGui.QMessageBox.critical( QtGui.QMessageBox.critical(
self, translate('SongsPlugin.TopicsForm', 'Error'), self, translate('SongsPlugin.TopicsForm', 'Error'),
translate('SongsPlugin.TopicsForm', translate('SongsPlugin.TopicsForm',
'You need to type in a topic name!')) 'You need to type in a topic name.'))
self.NameEdit.setFocus() self.NameEdit.setFocus()
return False return False
else: else:

View File

@ -57,7 +57,7 @@ class CCLIFileImport(SongImport):
self.filenames = kwargs[u'filenames'] self.filenames = kwargs[u'filenames']
log.debug(self.filenames) log.debug(self.filenames)
else: else:
raise KeyError(u'Keyword argument "filenames" not supplied.') raise KeyError(u'Keyword argument "filenames" not supplied.')
def do_import(self): def do_import(self):
""" """
@ -66,11 +66,11 @@ class CCLIFileImport(SongImport):
log.debug(u'Starting CCLI File Import') log.debug(u'Starting CCLI File Import')
song_total = len(self.filenames) song_total = len(self.filenames)
self.import_wizard.importProgressBar.setMaximum(song_total) self.import_wizard.importProgressBar.setMaximum(song_total)
song_count = 1 song_count = 1
for filename in self.filenames: for filename in self.filenames:
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(
u'Importing song %s of %s' % (song_count, song_total)) u'Importing song %s of %s' % (song_count, song_total))
filename = unicode(filename) filename = unicode(filename)
log.debug(u'Importing CCLI File: %s', filename) log.debug(u'Importing CCLI File: %s', filename)
lines = [] lines = []
if os.path.isfile(filename): if os.path.isfile(filename):
@ -81,33 +81,34 @@ class CCLIFileImport(SongImport):
lines = infile.readlines() lines = infile.readlines()
ext = os.path.splitext(filename)[1] ext = os.path.splitext(filename)[1]
if ext.lower() == ".usr": if ext.lower() == ".usr":
log.info(u'SongSelect .usr format file found %s: ' , filename) log.info(u'SongSelect .usr format file found %s: ',
filename)
self.do_import_usr_file(lines) self.do_import_usr_file(lines)
elif ext.lower() == ".txt": elif ext.lower() == ".txt":
log.info(u'SongSelect .txt format file found %s: ', filename) log.info(u'SongSelect .txt format file found %s: ',
filename)
self.do_import_txt_file(lines) self.do_import_txt_file(lines)
else: else:
log.info(u'Extension %s is not valid', filename) log.info(u'Extension %s is not valid', filename)
pass
song_count += 1 song_count += 1
if self.stop_import_flag: if self.stop_import_flag:
return False return False
return True return True
def do_import_usr_file(self, textList): def do_import_usr_file(self, textList):
""" """
The :method:`do_import_usr_file` method provides OpenLP The :method:`do_import_usr_file` method provides OpenLP
with the ability to import CCLI SongSelect songs in with the ability to import CCLI SongSelect songs in
*USR* file format *USR* file format
``textList`` ``textList``
An array of strings containing the usr file content. An array of strings containing the usr file content.
**SongSelect .usr file format** **SongSelect .usr file format**
``[File]`` ``[File]``
USR file format first line USR file format first line
``Type=`` ``Type=``
Indicates the file type Indicates the file type
e.g. *Type=SongSelect Import File* e.g. *Type=SongSelect Import File*
``Version=3.0`` ``Version=3.0``
File format version File format version
@ -116,7 +117,7 @@ class CCLIFileImport(SongImport):
``Title=`` ``Title=``
Contains the song title (e.g. *Title=Above All*) Contains the song title (e.g. *Title=Above All*)
``Author=`` ``Author=``
Contains a | delimited list of the song authors Contains a | delimited list of the song authors
e.g. *Author=LeBlanc, Lenny | Baloche, Paul* e.g. *Author=LeBlanc, Lenny | Baloche, Paul*
``Copyright=`` ``Copyright=``
Contains a | delimited list of the song copyrights Contains a | delimited list of the song copyrights
@ -136,7 +137,7 @@ class CCLIFileImport(SongImport):
Contains a list of the songs fields in order /t delimited Contains a list of the songs fields in order /t delimited
e.g. *Fields=Vers 1/tVers 2/tChorus 1/tAndere 1* e.g. *Fields=Vers 1/tVers 2/tChorus 1/tAndere 1*
``Words=`` ``Words=``
Contains the songs various lyrics in order as shown by the Contains the songs various lyrics in order as shown by the
*Fields* description *Fields* description
e.g. *Words=Above all powers....* [/n = CR, /n/t = CRLF] e.g. *Words=Above all powers....* [/n = CR, /n/t = CRLF]
""" """
@ -174,8 +175,8 @@ class CCLIFileImport(SongImport):
verse_type = u'O' verse_type = u'O'
verse_text = unicode(words_list[counter]) verse_text = unicode(words_list[counter])
verse_text = verse_text.replace("/n", "\n") verse_text = verse_text.replace("/n", "\n")
if len(verse_text) > 0: if len(verse_text) > 0:
self.add_verse(verse_text, verse_type); self.add_verse(verse_text, verse_type)
#Handle multiple authors #Handle multiple authors
author_list = song_author.split(u'/') author_list = song_author.split(u'/')
if len(author_list) < 2: if len(author_list) < 2:
@ -192,10 +193,10 @@ class CCLIFileImport(SongImport):
""" """
The :method:`do_import_txt_file` method provides OpenLP The :method:`do_import_txt_file` method provides OpenLP
with the ability to import CCLI SongSelect songs in with the ability to import CCLI SongSelect songs in
*TXT* file format *TXT* file format
``textList`` ``textList``
An array of strings containing the txt file content. An array of strings containing the txt file content.
**SongSelect .txt file format** **SongSelect .txt file format**
@ -225,38 +226,38 @@ class CCLIFileImport(SongImport):
e.g. CCLI Number (e.g.CCLI-Liednummer: 2672885) e.g. CCLI Number (e.g.CCLI-Liednummer: 2672885)
``Song Copyright`` ``Song Copyright``
e.g. © 1999 Integrity's Hosanna! Music | LenSongs Publishing e.g. © 1999 Integrity's Hosanna! Music | LenSongs Publishing
``Song Authors`` ``Song Authors``
e.g. Lenny LeBlanc | Paul Baloche e.g. Lenny LeBlanc | Paul Baloche
``Licencing info`` ``Licencing info``
e.g. For use solely with the SongSelect Terms of Use. e.g. For use solely with the SongSelect Terms of Use.
All rights Reserved. www.ccli.com All rights Reserved. www.ccli.com
``CCLI Licence number of user`` ``CCLI Licence number of user``
e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14 e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14
""" """
log.debug(u'TXT file text: %s', textList) log.debug(u'TXT file text: %s', textList)
self.set_defaults() self.set_defaults()
line_number = 0 line_number = 0
verse_text = u'' verse_text = u''
song_comments = u'' song_comments = u''
song_copyright = u''; song_copyright = u''
verse_start = False verse_start = False
for line in textList: for line in textList:
clean_line = line.strip() clean_line = line.strip()
if not clean_line: if not clean_line:
if line_number==0: if line_number == 0:
continue continue
elif verse_start: elif verse_start:
if verse_text: if verse_text:
self.add_verse(verse_text, verse_type) self.add_verse(verse_text, verse_type)
verse_text = '' verse_text = ''
verse_start = False verse_start = False
else: else:
#line_number=0, song title #line_number=0, song title
if line_number==0: if line_number == 0:
song_name = clean_line song_name = clean_line
line_number += 1 line_number += 1
#line_number=1, verses #line_number=1, verses
elif line_number==1: elif line_number == 1:
#line_number=1, ccli number, first line after verses #line_number=1, ccli number, first line after verses
if clean_line.startswith(u'CCLI'): if clean_line.startswith(u'CCLI'):
line_number += 1 line_number += 1
@ -285,15 +286,16 @@ class CCLIFileImport(SongImport):
verse_text = verse_text + line verse_text = verse_text + line
else: else:
#line_number=2, copyright #line_number=2, copyright
if line_number==2: if line_number == 2:
line_number += 1 line_number += 1
song_copyright = clean_line song_copyright = clean_line
#n=3, authors #n=3, authors
elif line_number==3: elif line_number == 3:
line_number += 1 line_number += 1
song_author = clean_line song_author = clean_line
#line_number=4, comments lines before last line #line_number=4, comments lines before last line
elif (line_number==4) and (not clean_line.startswith(u'CCL')): elif (line_number == 4) and \
(not clean_line.startswith(u'CCL')):
song_comments = song_comments + clean_line song_comments = song_comments + clean_line
# split on known separators # split on known separators
author_list = song_author.split(u'/') author_list = song_author.split(u'/')
@ -307,4 +309,3 @@ class CCLIFileImport(SongImport):
self.ccli_number = song_ccli self.ccli_number = song_ccli
self.comments = song_comments self.comments = song_comments
self.finish() self.finish()

View File

@ -26,14 +26,24 @@
from opensongimport import OpenSongImport from opensongimport import OpenSongImport
from olpimport import OpenLPSongImport from olpimport import OpenLPSongImport
from olp1import import OpenLP1SongImport from wowimport import WowImport
from cclifileimport import CCLIFileImport
# Imports that might fail
try:
from olp1import import OpenLP1SongImport
has_openlp1 = True
except ImportError:
has_openlp1 = False
try: try:
from sofimport import SofImport from sofimport import SofImport
from oooimport import OooImport has_sof = True
from cclifileimport import CCLIFileImport
from wowimport import WowImport
except ImportError: except ImportError:
pass has_sof = False
try:
from oooimport import OooImport
has_ooo = True
except ImportError:
has_ooo = False
class SongFormat(object): class SongFormat(object):
""" """
@ -41,6 +51,7 @@ class SongFormat(object):
plus a few helper functions to facilitate generic handling of song types plus a few helper functions to facilitate generic handling of song types
for importing. for importing.
""" """
_format_availability = {}
Unknown = -1 Unknown = -1
OpenLP2 = 0 OpenLP2 = 0
OpenLP1 = 1 OpenLP1 = 1
@ -93,4 +104,16 @@ class SongFormat(object):
SongFormat.Generic SongFormat.Generic
] ]
@staticmethod
def set_availability(format, available):
SongFormat._format_availability[format] = available
@staticmethod
def get_availability(format):
return SongFormat._format_availability.get(format, True)
SongFormat.set_availability(SongFormat.OpenLP1, has_openlp1)
SongFormat.set_availability(SongFormat.SongsOfFellowship, has_sof)
SongFormat.set_availability(SongFormat.Generic, has_ooo)
__all__ = [u'SongFormat'] __all__ = [u'SongFormat']

View File

@ -28,10 +28,8 @@ The :mod:`olp1import` module provides the functionality for importing
openlp.org 1.x song databases into the current installation database. openlp.org 1.x song databases into the current installation database.
""" """
import logging import logging
try: import chardet
import sqlite import sqlite
except:
pass
from openlp.core.lib import translate from openlp.core.lib import translate
from songimport import SongImport from songimport import SongImport
@ -43,6 +41,8 @@ class OpenLP1SongImport(SongImport):
The :class:`OpenLP1SongImport` class provides OpenLP with the ability to The :class:`OpenLP1SongImport` class provides OpenLP with the ability to
import song databases from installations of openlp.org 1.x. import song databases from installations of openlp.org 1.x.
""" """
last_encoding = u'windows-1252'
def __init__(self, manager, **kwargs): def __init__(self, manager, **kwargs):
""" """
Initialise the import. Initialise the import.
@ -56,6 +56,34 @@ class OpenLP1SongImport(SongImport):
SongImport.__init__(self, manager) SongImport.__init__(self, manager)
self.import_source = kwargs[u'filename'] self.import_source = kwargs[u'filename']
def decode_string(self, raw, guess):
"""
Use chardet to detect the encoding of the raw string, and convert it
to unicode.
``raw``
The raw bytestring to decode.
``guess``
What chardet guessed the encoding to be.
"""
if guess[u'confidence'] < 0.8:
codec = u'windows-1252'
else:
codec = guess[u'encoding']
try:
decoded = unicode(raw, codec)
self.last_encoding = codec
except UnicodeDecodeError:
log.exception(u'Error in detecting openlp.org 1.x database encoding.')
try:
decoded = unicode(raw, self.last_encoding)
except UnicodeDecodeError:
# possibly show an error form
#self.import_wizard.showError(u'There was a problem '
# u'detecting the encoding of a string')
decoded = raw
return decoded
def do_import(self): def do_import(self):
""" """
Run the import for an openlp.org 1.x song database. Run the import for an openlp.org 1.x song database.
@ -63,6 +91,11 @@ class OpenLP1SongImport(SongImport):
# Connect to the database # Connect to the database
connection = sqlite.connect(self.import_source) connection = sqlite.connect(self.import_source)
cursor = connection.cursor() cursor = connection.cursor()
# Determine if we're using a new or an old DB
cursor.execute(u'SELECT name FROM sqlite_master '
u'WHERE type = \'table\' AND name = \'tracks\'')
table_list = cursor.fetchall()
new_db = len(table_list) > 0
# Count the number of records we need to import, for the progress bar # Count the number of records we need to import, for the progress bar
cursor.execute(u'SELECT COUNT(songid) FROM songs') cursor.execute(u'SELECT COUNT(songid) FROM songs')
count = int(cursor.fetchone()[0]) count = int(cursor.fetchone()[0])
@ -71,9 +104,10 @@ class OpenLP1SongImport(SongImport):
# "cache" our list of authors # "cache" our list of authors
cursor.execute(u'SELECT authorid, authorname FROM authors') cursor.execute(u'SELECT authorid, authorname FROM authors')
authors = cursor.fetchall() authors = cursor.fetchall()
# "cache" our list of tracks if new_db:
cursor.execute(u'SELECT trackid, fulltrackname FROM tracks') # "cache" our list of tracks
tracks = cursor.fetchall() cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
tracks = cursor.fetchall()
# Import the songs # Import the songs
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, ' cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, '
u'copyrightinfo FROM songs') u'copyrightinfo FROM songs')
@ -84,9 +118,10 @@ class OpenLP1SongImport(SongImport):
success = False success = False
break break
song_id = song[0] song_id = song[0]
title = unicode(song[1], u'cp1252') guess = chardet.detect(song[2])
lyrics = unicode(song[2], u'cp1252').replace(u'\r', u'') title = self.decode_string(song[1], guess)
copyright = unicode(song[3], u'cp1252') lyrics = self.decode_string(song[2], guess).replace(u'\r', u'')
copyright = self.decode_string(song[3], guess)
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(
unicode(translate('SongsPlugin.ImportWizardForm', unicode(translate('SongsPlugin.ImportWizardForm',
'Importing "%s"...')) % title) 'Importing "%s"...')) % title)
@ -102,15 +137,12 @@ class OpenLP1SongImport(SongImport):
break break
for author in authors: for author in authors:
if author[0] == author_id[0]: if author[0] == author_id[0]:
self.parse_author(unicode(author[1], u'cp1252')) self.parse_author(self.decode_string(author[1], guess))
break break
if self.stop_import_flag: if self.stop_import_flag:
success = False success = False
break break
cursor.execute(u'SELECT name FROM sqlite_master ' if new_db:
u'WHERE type = \'table\' AND name = \'tracks\'')
table_list = cursor.fetchall()
if len(table_list) > 0:
cursor.execute(u'SELECT trackid FROM songtracks ' cursor.execute(u'SELECT trackid FROM songtracks '
u'WHERE songid = %s ORDER BY listindex' % song_id) u'WHERE songid = %s ORDER BY listindex' % song_id)
track_ids = cursor.fetchall() track_ids = cursor.fetchall()
@ -120,10 +152,11 @@ class OpenLP1SongImport(SongImport):
break break
for track in tracks: for track in tracks:
if track[0] == track_id[0]: if track[0] == track_id[0]:
self.add_media_file(unicode(track[1], u'cp1252')) self.add_media_file(self.decode_string(track[1], guess))
break break
if self.stop_import_flag: if self.stop_import_flag:
success = False success = False
break break
self.finish() self.finish()
return success return success

View File

@ -59,6 +59,7 @@ class OooImport(SongImport):
self.document = None self.document = None
self.process_started = False self.process_started = False
self.filenames = kwargs[u'filenames'] self.filenames = kwargs[u'filenames']
self.uno_connection_type = u'pipe' #u'socket'
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'song_stop_import'), self.stop_import) QtCore.SIGNAL(u'song_stop_import'), self.stop_import)
@ -106,8 +107,14 @@ class OooImport(SongImport):
loop = 0 loop = 0
while ctx is None and loop < 5: while ctx is None and loop < 5:
try: try:
ctx = resolver.resolve(u'uno:socket,host=localhost,' \ if self.uno_connection_type == u'pipe':
+ 'port=2002;urp;StarOffice.ComponentContext') ctx = resolver.resolve(u'uno:' \
+ u'pipe,name=openlp_pipe;' \
+ u'urp;StarOffice.ComponentContext')
else:
ctx = resolver.resolve(u'uno:' \
+ u'socket,host=localhost,port=2002;' \
+ u'urp;StarOffice.ComponentContext')
except: except:
pass pass
self.start_ooo_process() self.start_ooo_process()
@ -123,9 +130,14 @@ class OooImport(SongImport):
self.manager._FlagAsMethod(u'Bridge_GetStruct') self.manager._FlagAsMethod(u'Bridge_GetStruct')
self.manager._FlagAsMethod(u'Bridge_GetValueObject') self.manager._FlagAsMethod(u'Bridge_GetValueObject')
else: else:
cmd = u'openoffice.org -nologo -norestore -minimized ' \ if self.uno_connection_type == u'pipe':
+ u'-invisible -nofirststartwizard ' \ cmd = u'openoffice.org -nologo -norestore -minimized ' \
+ '-accept="socket,host=localhost,port=2002;urp;"' + u'-invisible -nofirststartwizard ' \
+ u'-accept=pipe,name=openlp_pipe;urp;'
else:
cmd = u'openoffice.org -nologo -norestore -minimized ' \
+ u'-invisible -nofirststartwizard ' \
+ u'-accept=socket,host=localhost,port=2002;urp;'
process = QtCore.QProcess() process = QtCore.QProcess()
process.startDetached(cmd) process.startDetached(cmd)
process.waitForStarted() process.waitForStarted()

View File

@ -29,7 +29,9 @@ import os
from zipfile import ZipFile from zipfile import ZipFile
from lxml import objectify from lxml import objectify
from lxml.etree import Error, LxmlError from lxml.etree import Error, LxmlError
import re
from openlp.core.lib import translate
from openlp.plugins.songs.lib.songimport import SongImport from openlp.plugins.songs.lib.songimport import SongImport
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -112,12 +114,22 @@ class OpenSongImport(SongImport):
def do_import(self): def do_import(self):
""" """
Import either a single opensong file, or a zipfile containing multiple Import either each of the files in self.filenames - each element of
opensong files. If `self.commit` is set False, the import will not be which can be either a single opensong file, or a zipfile containing
committed to the database (useful for test scripts). multiple opensong files. If `self.commit` is set False, the
import will not be committed to the database (useful for test scripts).
""" """
success = True success = True
self.import_wizard.importProgressBar.setMaximum(len(self.filenames)) numfiles = 0
for filename in self.filenames:
ext = os.path.splitext(filename)[1]
if ext.lower() == u'.zip':
z = ZipFile(filename, u'r')
numfiles += len(z.infolist())
else:
numfiles += 1
log.debug("Total number of files: %d", numfiles)
self.import_wizard.importProgressBar.setMaximum(numfiles)
for filename in self.filenames: for filename in self.filenames:
if self.stop_import_flag: if self.stop_import_flag:
success = False success = False
@ -126,9 +138,6 @@ class OpenSongImport(SongImport):
if ext.lower() == u'.zip': if ext.lower() == u'.zip':
log.debug(u'Zipfile found %s', filename) log.debug(u'Zipfile found %s', filename)
z = ZipFile(filename, u'r') z = ZipFile(filename, u'r')
self.import_wizard.importProgressBar.setMaximum(
self.import_wizard.importProgressBar.maximum() +
len(z.infolist()))
for song in z.infolist(): for song in z.infolist():
if self.stop_import_flag: if self.stop_import_flag:
success = False success = False
@ -137,6 +146,7 @@ class OpenSongImport(SongImport):
if parts[-1] == u'': if parts[-1] == u'':
#No final part => directory #No final part => directory
continue continue
log.info(u'Zip importing %s', parts[-1])
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(
unicode(translate('SongsPlugin.ImportWizardForm', unicode(translate('SongsPlugin.ImportWizardForm',
'Importing %s...')) % parts[-1]) 'Importing %s...')) % parts[-1])
@ -144,11 +154,11 @@ class OpenSongImport(SongImport):
self.do_import_file(songfile) self.do_import_file(songfile)
if self.commit: if self.commit:
self.finish() self.finish()
self.set_defaults()
if self.stop_import_flag: if self.stop_import_flag:
success = False success = False
break break
else: else:
# not a zipfile
log.info('Direct import %s', filename) log.info('Direct import %s', filename)
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(
unicode(translate('SongsPlugin.ImportWizardForm', unicode(translate('SongsPlugin.ImportWizardForm',
@ -157,9 +167,7 @@ class OpenSongImport(SongImport):
self.do_import_file(file) self.do_import_file(file)
if self.commit: if self.commit:
self.finish() self.finish()
self.set_defaults()
if not self.commit:
self.finish()
return success return success
def do_import_file(self, file): def do_import_file(self, file):
@ -167,10 +175,10 @@ class OpenSongImport(SongImport):
Process the OpenSong file - pass in a file-like object, Process the OpenSong file - pass in a file-like object,
not a filename not a filename
""" """
self.authors = [] self.set_defaults()
try: try:
tree = objectify.parse(file) tree = objectify.parse(file)
except Error, LxmlError: except (Error, LxmlError):
log.exception(u'Error parsing XML') log.exception(u'Error parsing XML')
return return
root = tree.getroot() root = tree.getroot()
@ -196,7 +204,6 @@ class OpenSongImport(SongImport):
self.topics.append(unicode(root.alttheme)) self.topics.append(unicode(root.alttheme))
# data storage while importing # data storage while importing
verses = {} verses = {}
lyrics = unicode(root.lyrics)
# keep track of a "default" verse order, in case none is specified # keep track of a "default" verse order, in case none is specified
our_verse_order = [] our_verse_order = []
verses_seen = {} verses_seen = {}
@ -204,6 +211,7 @@ class OpenSongImport(SongImport):
# erm, versetype! # erm, versetype!
versetype = u'V' versetype = u'V'
versenum = None versenum = None
lyrics = unicode(root.lyrics)
for thisline in lyrics.split(u'\n'): for thisline in lyrics.split(u'\n'):
# remove comments # remove comments
semicolon = thisline.find(u';') semicolon = thisline.find(u';')
@ -218,16 +226,18 @@ class OpenSongImport(SongImport):
continue continue
# verse/chorus/etc. marker # verse/chorus/etc. marker
if thisline[0] == u'[': if thisline[0] == u'[':
versetype = thisline[1].upper() # drop the square brackets
if versetype.isdigit(): right_bracket = thisline.find(u']')
versenum = versetype content = thisline[1:right_bracket].upper()
versetype = u'V' # have we got any digits? If so, versenumber is everything from the digits
elif thisline[2] != u']': # to the end (even if there are some alpha chars on the end)
# there's a number to go with it - extract that as well match = re.match(u'(.*)(\d+.*)', content)
right_bracket = thisline.find(u']') if match is not None:
versenum = thisline[2:right_bracket] versetype = match.group(1)
versenum = match.group(2)
else: else:
# if there's no number, assume it's no.1 # otherwise we assume number 1 and take the whole prefix as versetype
versetype = content
versenum = u'1' versenum = u'1'
continue continue
words = None words = None
@ -235,10 +245,10 @@ class OpenSongImport(SongImport):
if thisline[0].isdigit(): if thisline[0].isdigit():
versenum = thisline[0] versenum = thisline[0]
words = thisline[1:].strip() words = thisline[1:].strip()
if words is None and \ if words is None:
versenum is not None and \
versetype is not None:
words = thisline words = thisline
if not versenum:
versenum = u'1'
if versenum is not None: if versenum is not None:
versetag = u'%s%s' % (versetype, versenum) versetag = u'%s%s' % (versetype, versenum)
if not verses.has_key(versetype): if not verses.has_key(versetype):
@ -259,10 +269,13 @@ class OpenSongImport(SongImport):
versetypes.sort() versetypes.sort()
versetags = {} versetags = {}
for versetype in versetypes: for versetype in versetypes:
our_verse_type = versetype
if our_verse_type == u'':
our_verse_type = u'V'
versenums = verses[versetype].keys() versenums = verses[versetype].keys()
versenums.sort() versenums.sort()
for num in versenums: for num in versenums:
versetag = u'%s%s' % (versetype, num) versetag = u'%s%s' % (our_verse_type, num)
lines = u'\n'.join(verses[versetype][num]) lines = u'\n'.join(verses[versetype][num])
self.verses.append([versetag, lines]) self.verses.append([versetag, lines])
# Keep track of what we have for error checking later # Keep track of what we have for error checking later
@ -271,16 +284,23 @@ class OpenSongImport(SongImport):
order = [] order = []
if u'presentation' in fields and root.presentation != u'': if u'presentation' in fields and root.presentation != u'':
order = unicode(root.presentation) order = unicode(root.presentation)
order = order.split() # We make all the tags in the lyrics upper case, so match that here
# and then split into a list on the whitespace
order = order.upper().split()
else: else:
if len(our_verse_order) > 0: if len(our_verse_order) > 0:
order = our_verse_order order = our_verse_order
else: else:
log.warn(u'No verse order available for %s, skipping.', self.title) log.warn(u'No verse order available for %s, skipping.',
self.title)
for tag in order: for tag in order:
if len(tag) == 1: if tag[0].isdigit():
tag = tag + u'1' # Assume it's no.1 if it's not there # Assume it's a verse if it has no prefix
tag = u'V' + tag
elif not re.search('\d+', tag):
# Assume it's no.1 if there's no digits
tag = tag + u'1'
if not versetags.has_key(tag): if not versetags.has_key(tag):
log.warn(u'Got order %s but not in versetags, skipping', tag) log.info(u'Got order %s but not in versetags, dropping this item from presentation order', tag)
else: else:
self.verse_order_list.append(tag) self.verse_order_list.append(tag)

View File

@ -55,8 +55,12 @@ class SongImport(QtCore.QObject):
self.set_defaults() self.set_defaults()
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'songs_stop_import'), self.stop_import) QtCore.SIGNAL(u'songs_stop_import'), self.stop_import)
def set_defaults(self): def set_defaults(self):
"""
Create defaults for properties - call this before each song
if importing many songs at once to ensure a clean beginning
"""
self.authors = []
self.title = u'' self.title = u''
self.song_number = u'' self.song_number = u''
self.alternate_title = u'' self.alternate_title = u''
@ -71,13 +75,7 @@ class SongImport(QtCore.QObject):
self.song_book_pub = u'' self.song_book_pub = u''
self.verse_order_list = [] self.verse_order_list = []
self.verses = [] self.verses = []
self.versecount = 0 self.versecounts = {}
self.choruscount = 0
self.bridgecount = 0
self.introcount = 0
self.prechoruscount = 0
self.endingcount = 0
self.othercount = 0
self.copyright_string = unicode(translate( self.copyright_string = unicode(translate(
'SongsPlugin.SongImport', 'copyright')) 'SongsPlugin.SongImport', 'copyright'))
self.copyright_symbol = unicode(translate( self.copyright_symbol = unicode(translate(
@ -198,7 +196,7 @@ class SongImport(QtCore.QObject):
return return
self.media_files.append(filename) self.media_files.append(filename)
def add_verse(self, verse, versetag=None): def add_verse(self, verse, versetag=u'V'):
""" """
Add a verse. This is the whole verse, lines split by \n Add a verse. This is the whole verse, lines split by \n
Verse tag can be V1/C1/B etc, or 'V' and 'C' (will count the verses/ Verse tag can be V1/C1/B etc, or 'V' and 'C' (will count the verses/
@ -210,33 +208,14 @@ class SongImport(QtCore.QObject):
if oldverse.strip() == verse.strip(): if oldverse.strip() == verse.strip():
self.verse_order_list.append(oldversetag) self.verse_order_list.append(oldversetag)
return return
if versetag == u'V' or not versetag: if versetag[0] in self.versecounts:
self.versecount += 1 self.versecounts[versetag[0]] += 1
versetag = u'V' + unicode(self.versecount) else:
if versetag.startswith(u'C'): self.versecounts[versetag[0]] = 1
self.choruscount += 1 if len(versetag) == 1:
if versetag == u'C': versetag += unicode(self.versecounts[versetag[0]])
versetag += unicode(self.choruscount) elif int(versetag[1:]) > self.versecounts[versetag[0]]:
if versetag.startswith(u'B'): self.versecounts[versetag[0]] = int(versetag[1:])
self.bridgecount += 1
if versetag == u'B':
versetag += unicode(self.bridgecount)
if versetag.startswith(u'I'):
self.introcount += 1
if versetag == u'I':
versetag += unicode(self.introcount)
if versetag.startswith(u'P'):
self.prechoruscount += 1
if versetag == u'P':
versetag += unicode(self.prechoruscount)
if versetag.startswith(u'E'):
self.endingcount += 1
if versetag == u'E':
versetag += unicode(self.endingcount)
if versetag.startswith(u'O'):
self.othercount += 1
if versetag == u'O':
versetag += unicode(self.othercount)
self.verses.append([versetag, verse.rstrip()]) self.verses.append([versetag, verse.rstrip()])
self.verse_order_list.append(versetag) self.verse_order_list.append(versetag)
if versetag.startswith(u'V') and self.contains_verse(u'C1'): if versetag.startswith(u'V') and self.contains_verse(u'C1'):
@ -280,13 +259,16 @@ class SongImport(QtCore.QObject):
""" """
Write the song and its fields to disk Write the song and its fields to disk
""" """
log.info(u'commiting song %s to database', self.title)
song = Song() song = Song()
song.title = self.title song.title = self.title
song.search_title = self.remove_punctuation(self.title) \ song.search_title = self.remove_punctuation(self.title) \
+ '@' + self.alternate_title + '@' + self.alternate_title
song.song_number = self.song_number song.song_number = self.song_number
song.search_lyrics = u'' song.search_lyrics = u''
verses_changed_to_other = {}
sxml = SongXMLBuilder() sxml = SongXMLBuilder()
other_count = 1
for (versetag, versetext) in self.verses: for (versetag, versetext) in self.verses:
if versetag[0] == u'C': if versetag[0] == u'C':
versetype = VerseType.to_string(VerseType.Chorus) versetype = VerseType.to_string(VerseType.Chorus)
@ -301,10 +283,18 @@ class SongImport(QtCore.QObject):
elif versetag[0] == u'E': elif versetag[0] == u'E':
versetype = VerseType.to_string(VerseType.Ending) versetype = VerseType.to_string(VerseType.Ending)
else: else:
newversetag = u'O%d' % other_count
verses_changed_to_other[versetag] = newversetag
other_count += 1
versetype = VerseType.to_string(VerseType.Other) versetype = VerseType.to_string(VerseType.Other)
log.info(u'Versetype %s changing to %s' , versetag, newversetag)
versetag = newversetag
sxml.add_verse_to_lyrics(versetype, versetag[1:], versetext) sxml.add_verse_to_lyrics(versetype, versetag[1:], versetext)
song.search_lyrics += u' ' + self.remove_punctuation(versetext) song.search_lyrics += u' ' + self.remove_punctuation(versetext)
song.lyrics = unicode(sxml.extract_xml(), u'utf-8') song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
for i, current_verse_tag in enumerate(self.verse_order_list):
if verses_changed_to_other.has_key(current_verse_tag):
self.verse_order_list[i] = verses_changed_to_other[current_verse_tag]
song.verse_order = u' '.join(self.verse_order_list) song.verse_order = u' '.join(self.verse_order_list)
song.copyright = self.copyright song.copyright = self.copyright
song.comments = self.comments song.comments = self.comments

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<song> <song>
<title>Martins Test</title> <title>Martins Test</title>
<author>MartiÑ Thómpson</author> <author>MartiÑ &amp; Martin2 Thómpson</author>
<copyright>2010 Martin Thompson</copyright> <copyright>2010 Martin Thompson</copyright>
<hymn_number>1</hymn_number> <hymn_number>1</hymn_number>
<presentation>V1 C V2 C2 V3 B1 V1</presentation> <presentation>V1 C V2 C2 3a B1 V1 T U Rap1 Rap2 Rap3</presentation>
<ccli>Blah</ccli> <ccli>Blah</ccli>
<capo print="false"></capo> <capo print="false"></capo>
<key></key> <key></key>
@ -17,7 +17,12 @@
<alttheme>TestAltTheme</alttheme> <alttheme>TestAltTheme</alttheme>
<tempo></tempo> <tempo></tempo>
<time_sig></time_sig> <time_sig></time_sig>
<lyrics>;Comment <lyrics>[3a]
. G A B
V3 Line 1
. G A B
V3 Line 2
. A B C . A B C
1 v1 Line 1___ 1 v1 Line 1___
2 v2 Line 1___ 2 v2 Line 1___
@ -25,10 +30,6 @@
1 V1 Line 2 1 V1 Line 2
2 V2 Line 2 2 V2 Line 2
[3]
V3 Line 1
V3 Line 2
[b1] [b1]
Bridge 1 Bridge 1
--- ---
@ -36,12 +37,29 @@
Bridge 1 line 2 Bridge 1 line 2
[C] [C]
. A B . A B
Chorus 1 Chorus 1
[C2] [C2]
. A B . A B
Chorus 2 Chorus 2
[T]
T Line 1
[Rap]
1 Rap 1 Line 1
2 Rap 2 Line 1
1 Rap 1 Line 2
2 Rap 2 Line 2
[rap3]
Rap 3 Line 1
Rap 3 Line 2
[X]
Unreferenced verse line 1
</lyrics> </lyrics>
<style index="default_style"> <style index="default_style">
<title enabled="true" valign="bottom" align="center" include_verse="false" margin-left="0" margin-right="0" margin-top="0" margin-bottom="0" font="Helvetica" size="26" bold="true" italic="true" underline="false" color="#FFFFFF" border="true" border_color="#000000" shadow="true" shadow_color="#000000" fill="false" fill_color="#000000"/> <title enabled="true" valign="bottom" align="center" include_verse="false" margin-left="0" margin-right="0" margin-top="0" margin-bottom="0" font="Helvetica" size="26" bold="true" italic="true" underline="false" color="#FFFFFF" border="true" border_color="#000000" shadow="true" shadow_color="#000000" fill="false" fill_color="#000000"/>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<song>
<title>Test single verse</title>
<author>Martin Thompson</author>
<copyright>2010</copyright>
<ccli>123456</ccli>
<theme>Worship: Declaration</theme>
<lyrics> Line 1
Line 2
</lyrics></song>

View File

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import sys
from openlp.plugins.songs.lib.opensongimport import OpenSongImport
from openlp.core.lib.db import Manager
from openlp.plugins.songs.lib.db import init_schema
import logging
LOG_FILENAME = 'test_import_file.log'
logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO)
from test_opensongimport import wizard_stub, progbar_stub
def test(filenames):
manager = Manager(u'songs', init_schema)
o = OpenSongImport(manager, filenames=filenames)
o.import_wizard = wizard_stub()
o.commit = False
o.do_import()
o.print_song()
if __name__ == "__main__":
test(sys.argv[1:])

View File

@ -8,50 +8,29 @@ from traceback import print_exc
import sys import sys
import codecs import codecs
import logging
LOG_FILENAME = 'import.log'
logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO)
from test_opensongimport import wizard_stub, progbar_stub
# Useful test function for importing a variety of different files
# Uncomment below depending on what problem trying to make occur!
def opensong_import_lots(): def opensong_import_lots():
ziploc = u'/home/mjt/openlp/OpenSong_Data/' ziploc = u'/home/mjt/openlp/OpenSong_Data/'
files = [] files = []
#files = [u'test.opensong.zip', ziploc+u'ADond.zip'] files = [os.path.join(ziploc, u'RaoulSongs', u'Songs', u'Jesus Freak')]
files.extend(glob(ziploc+u'Songs.zip')) # files.extend(glob(ziploc+u'Songs.zip'))
#files.extend(glob(ziploc+u'SOF.zip')) # files.extend(glob(ziploc+u'RaoulSongs.zip'))
#files.extend(glob(ziploc+u'spanish_songs_for_opensong.zip')) # files.extend(glob(ziploc+u'SOF.zip'))
# files.extend(glob(ziploc+u'opensong_*.zip')) # files.extend(glob(ziploc+u'spanish_songs_for_opensong.zip'))
# files.extend(glob(ziploc+u'opensong_*.zip'))
errfile = codecs.open(u'import_lots_errors.txt', u'w', u'utf8') errfile = codecs.open(u'import_lots_errors.txt', u'w', u'utf8')
manager = Manager(u'songs', init_schema) manager = Manager(u'songs', init_schema)
for file in files: o = OpenSongImport(manager, filenames=files)
print u'Importing', file o.import_wizard=wizard_stub()
z = ZipFile(file, u'r') o.do_import()
for song in z.infolist():
# need to handle unicode filenames (CP437 - Winzip does this)
filename = song.filename#.decode('cp852')
parts = os.path.split(filename)
if parts[-1] == u'':
#No final part => directory
continue
print " ", file, ":",filename,
songfile = z.open(song)
#z.extract(song)
#songfile=open(filename, u'r')
o = OpenSongImport(manager)
try:
o.do_import_file(songfile)
# o.song_import.print_song()
except:
print "Failure",
errfile.write(u'Failure: %s:%s\n' %(file, filename.decode('cp437')))
songfile = z.open(song)
for l in songfile.readlines():
l = l.decode('utf8')
print(u' |%s\n' % l.strip())
errfile.write(u' |%s\n'%l.strip())
print_exc(3, file = errfile)
print_exc(3)
sys.exit(1)
# continue
#o.finish()
print "OK"
#os.unlink(filename)
# o.song_import.print_song()
if __name__ == "__main__": if __name__ == "__main__":
opensong_import_lots() opensong_import_lots()

View File

@ -28,62 +28,101 @@ from openlp.plugins.songs.lib.opensongimport import OpenSongImport
from openlp.core.lib.db import Manager from openlp.core.lib.db import Manager
from openlp.plugins.songs.lib.db import init_schema from openlp.plugins.songs.lib.db import init_schema
import logging
LOG_FILENAME = 'test.log'
logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO)
# Stubs to replace the UI functions for raw testing
class wizard_stub:
def __init__(self):
self.importProgressBar=progbar_stub()
def incrementProgressBar(self, str):
pass
class progbar_stub:
def __init__(self):
pass
def setMaximum(self, arg):
pass
def test(): def test():
manager = Manager(u'songs', init_schema) manager = Manager(u'songs', init_schema)
o = OpenSongImport(manager) o = OpenSongImport(manager, filenames=[u'test.opensong'])
o.do_import(u'test.opensong', commit=False) o.import_wizard = wizard_stub()
o.song_import.print_song() o.commit = False
assert o.song_import.copyright == u'2010 Martin Thompson' o.do_import()
assert o.song_import.authors == [u'MartiÑ Thómpson'] o.print_song()
assert o.song_import.title == u'Martins Test' assert o.copyright == u'2010 Martin Thompson'
assert o.song_import.alternate_title == u'' assert o.authors == [u'MartiÑ Thómpson', u'Martin2 Thómpson']
assert o.song_import.song_number == u'1' assert o.title == u'Martins Test'
assert [u'C1', u'Chorus 1'] in o.song_import.verses assert o.alternate_title == u''
assert [u'C2', u'Chorus 2'] in o.song_import.verses assert o.song_number == u'1'
assert not [u'C3', u'Chorus 3'] in o.song_import.verses assert [u'C1', u'Chorus 1'] in o.verses
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses assert [u'C2', u'Chorus 2'] in o.verses
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses assert not [u'C3', u'Chorus 3'] in o.verses
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
assert o.song_import.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1'] assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
assert o.song_import.ccli_number == u'Blah' assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
assert o.song_import.topics == [u'TestTheme', u'TestAltTheme'] assert [u'V3A', u'V3 Line 1\nV3 Line 2'] in o.verses
o.do_import(u'test.opensong.zip', commit=False) assert [u'RAP1', u'Rap 1 Line 1\nRap 1 Line 2'] in o.verses
o.song_import.print_song() assert [u'RAP2', u'Rap 2 Line 1\nRap 2 Line 2'] in o.verses
o.finish() assert [u'RAP3', u'Rap 3 Line 1\nRap 3 Line 2'] in o.verses
assert o.song_import.copyright == u'2010 Martin Thompson' assert [u'X1', u'Unreferenced verse line 1'] in o.verses
assert o.song_import.authors == [u'MartiÑ Thómpson'] assert o.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3A', u'B1', u'V1', u'T1', u'RAP1', u'RAP2', u'RAP3']
assert o.song_import.title == u'Martins Test' assert o.ccli_number == u'Blah'
assert o.song_import.alternate_title == u'' assert o.topics == [u'TestTheme', u'TestAltTheme']
assert o.song_import.song_number == u'1'
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses
assert [u'C1', u'Chorus 1'] in o.song_import.verses
assert [u'C2', u'Chorus 2'] in o.song_import.verses
assert not [u'C3', u'Chorus 3'] in o.song_import.verses
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses
assert o.song_import.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
o = OpenSongImport(manager) o.filenames = [u'test.opensong.zip']
o.do_import(u'test2.opensong', commit=False) o.set_defaults()
# o.finish() o.do_import()
o.song_import.print_song() o.print_song()
assert o.song_import.copyright == u'2010 Martin Thompson' assert o.copyright == u'2010 Martin Thompson'
assert o.song_import.authors == [u'Martin Thompson'] assert o.authors == [u'MartiÑ Thómpson']
assert o.song_import.title == u'Martins 2nd Test' assert o.title == u'Martins Test'
assert o.song_import.alternate_title == u'' assert o.alternate_title == u''
assert o.song_import.song_number == u'2' assert o.song_number == u'1'
print o.song_import.verses assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses assert [u'C1', u'Chorus 1'] in o.verses
assert [u'C1', u'Chorus 1'] in o.song_import.verses assert [u'C2', u'Chorus 2'] in o.verses
assert [u'C2', u'Chorus 2'] in o.song_import.verses assert not [u'C3', u'Chorus 3'] in o.verses
assert not [u'C3', u'Chorus 3'] in o.song_import.verses assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses print o.verse_order_list
print o.song_import.verse_order_list assert o.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
assert o.song_import.verse_order_list == [u'V1', u'V2', u'B1', u'C1', u'C2']
o.filenames = [u'test2.opensong']
o.set_defaults()
o.do_import()
o.print_song()
assert o.copyright == u'2010 Martin Thompson'
assert o.authors == [u'Martin Thompson']
assert o.title == u'Martins 2nd Test'
assert o.alternate_title == u''
assert o.song_number == u'2'
print o.verses
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
assert [u'C1', u'Chorus 1'] in o.verses
assert [u'C2', u'Chorus 2'] in o.verses
assert not [u'C3', u'Chorus 3'] in o.verses
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
print o.verse_order_list
assert o.verse_order_list == [u'V1', u'V2', u'B1', u'C1', u'C2']
o.filenames = [u'test3.opensong']
o.set_defaults()
o.do_import()
o.print_song()
assert o.copyright == u'2010'
assert o.authors == [u'Martin Thompson']
assert o.title == u'Test single verse'
assert o.alternate_title == u''
assert o.ccli_number == u'123456'
assert o.verse_order_list == [u'V1']
assert o.topics == [u'Worship: Declaration']
print o.verses[0]
assert [u'V1', u'Line 1\nLine 2'] in o.verses
print "Tests passed" print "Tests passed"
pass
if __name__ == "__main__": if __name__ == "__main__":
test() test()

View File

@ -119,7 +119,7 @@ class WowImport(SongImport):
# TODO: check that it is a valid words of worship file (could # TODO: check that it is a valid words of worship file (could
# check header for WoW File Song Word) # check header for WoW File Song Word)
self.author = u'' self.author = u''
self.copyright= u'' self.copyright = u''
# Get the song title # Get the song title
self.file_name = os.path.split(file)[1] self.file_name = os.path.split(file)[1]
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

0
resources/images/about-new.bmp Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

1
resources/openlp.desktop Normal file → Executable file
View File

@ -1,3 +1,4 @@
#!/usr/bin/env xdg-open
[Desktop Entry] [Desktop Entry]
Encoding=UTF-8 Encoding=UTF-8
Name=OpenLP Name=OpenLP

View File

@ -127,6 +127,8 @@ def import_bible():
for row in rows: for row in rows:
key = unicode(row[0], u'cp1252') key = unicode(row[0], u'cp1252')
value = unicode(row[1], u'cp1252') value = unicode(row[1], u'cp1252')
if key == u'Permission':
key = u'Permissions'
sql_insert = u'INSERT INTO metadata '\ sql_insert = u'INSERT INTO metadata '\
'("key", "value") '\ '("key", "value") '\
'VALUES (?, ?)' 'VALUES (?, ?)'

View File

@ -1,6 +1,6 @@
--- openlp/core/resources.py.old Mon Jun 21 23:16:19 2010 --- openlp/core/resources.py.old Mon Jun 21 23:16:19 2010
+++ openlp/core/resources.py Mon Jun 21 23:27:48 2010 +++ openlp/core/resources.py Mon Jun 21 23:27:48 2010
@@ -1,10 +1,31 @@ @@ -1,10 +1,32 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
@ -14,8 +14,9 @@
+# --------------------------------------------------------------------------- # +# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman # +# Copyright (c) 2008-2010 Raoul Snyman #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
+# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # +# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
+# Thompson, Jon Tibble, Carsten Tinggaard # +# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
+# Carsten Tinggaard, Frode Woldsund #
+# --------------------------------------------------------------------------- # +# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it # +# This program is free software; you can redistribute it and/or modify it #
+# under the terms of the GNU General Public License as published by the Free # +# under the terms of the GNU General Public License as published by the Free #

View File

@ -24,13 +24,31 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
# Short description
# Steps for creating languages:
# 1. make sure that the openlp_en.ts file exist
# 2. go to scripts folder and start:
# python translation_utils.py -a
###############################################################################
"""
This script is used to maintain the translation files in OpenLP. It downloads
the latest translation files from the Pootle translation server, updates the
local translation files from both the source code and the files from Pootle,
and can also generate the compiled translation files.
Create New Language
-------------------
To create a new language, simply run this script with the ``-a`` command line
option::
@:~$ ./translation_utils.py -a
Update Translation Files
------------------------
The best way to update the translations is to download the files from Pootle,
and then update the local files using both the downloaded files and the source.
This is done easily via the ``-d``, ``-p`` and ``-u`` options::
@:~$ ./translation_utils.py -dpu
"""
import os import os
import urllib import urllib
import re import re
@ -39,201 +57,277 @@ from optparse import OptionParser
from PyQt4 import QtCore from PyQt4 import QtCore
from BeautifulSoup import BeautifulSoup from BeautifulSoup import BeautifulSoup
class TranslationUtils(object): SERVER_URL = u'http://pootle.projecthq.biz/export/openlp/'
IGNORED_PATHS = [u'scripts']
IGNORED_FILES = [u'setup.py']
verbose_mode = False
class Command(object):
"""
Provide an enumeration of commands.
"""
Download = 1
Create = 2
Prepare = 3
Update = 4
Generate = 5
class CommandStack(object):
"""
This class provides an iterable stack.
"""
def __init__(self): def __init__(self):
self.ignore_paths = [u'./scripts'] self.current_index = 0
self.ignore_files = [u'setup.py'] self.data = []
self.server_url = u'http://pootle.projecthq.biz/export/openlp/'
self.cmd_stack = []
self.stack_count = 0
self.verbose = False
def process_stack(self): def __len__(self):
if len(self.cmd_stack) > 0: return len(self.data)
if len(self.cmd_stack) == self.stack_count:
print u'Process %d commands' % self.stack_count def __getitem__(self, index):
print u'%d. ' % (self.stack_count-len(self.cmd_stack)+1), if self.data[index].get(u'arguments'):
command = self.cmd_stack.pop(0) return self.data[index][u'command'], self.data[index][u'arguments']
if len(command) > 1:
command[0](command[1])
else:
command[0]()
else: else:
print "Finished all commands" return self.data[index][u'command']
def __iter__(self):
return self
def next(self):
if self.current_index == len(self.data):
raise StopIteration
else:
current_item = self.data[self.current_index][u'command']
self.current_index += 1
return current_item
def append(self, command, **kwargs):
data = {u'command': command}
if u'arguments' in kwargs:
data[u'arguments'] = kwargs[u'arguments']
self.data.append(data)
def reset(self):
self.current_index = 0
def downloadTranslations(self): def print_verbose(text):
print 'Download Translation files from HQ-Server' """
page = urllib.urlopen(u'%s' % (self.server_url)) This method checks to see if we are in verbose mode, and if so prints
soup = BeautifulSoup(page) ``text`` out.
languages = soup.findAll(text=re.compile(".*\.ts"))
for language in languages:
filename = os.path.join(u'..', u'resources', u'i18n',
u'openlp_%s' % language)
self.printVerbose(u'Get Translation File: %s' % filename)
self.get_and_write_file(language, filename)
print u' done'
self.process_stack()
def get_and_write_file(self, language, filename): ``text``
page = urllib.urlopen(u'%s%s' % (self.server_url, language)) The text to print.
content = page.read().decode('utf8') """
page.close() global verbose_mode
file = open(filename, u'w') if verbose_mode:
file.write(content.encode('utf8')) print u' %s' % text
file.close()
def creation(self, language):
print "Create new Translation File"
"""
Use this option to create a new translation file
this function:
* create the new *.ts file
"""
filename = os.path.join(u'..', u'resources', u'i18n',
u'openlp_%s.ts' % language)
self.get_and_write_file(u'en.ts', filename)
self.printVerbose("""
Please remind: For permanent providing this language:
this language name have to append to the global list
variable "translations" in this file
and this file have to be uploaded to the Pootle Server
Please contact the developers!
""")
print u' done'
self.process_stack()
def preparation(self):
print u'Generating the openlp.pro file'
stringlist = []
start_dir = os.path.join(u'..')
for root, dirs, files in os.walk(start_dir):
for file in files:
path = u'%s' % root
path = path.replace('\\','/')
path = path.replace('..','.')
if file.startswith(u'hook-') or file.startswith(u'test_'):
continue
cond = False def run(command):
for search in self.ignore_paths: """
if path.startswith(search): This method runs an external application.
cond = True
if cond: ``command``
continue The command to run.
cond = False """
for search in self.ignore_files: print_verbose(command)
if search == file: process = QtCore.QProcess()
cond = True process.start(command)
if cond: while (process.waitForReadyRead()):
continue print_verbose(u'ReadyRead: %s' % QtCore.QString(process.readAll()))
print_verbose(u'Error(s):\n%s' % process.readAllStandardError())
if file.endswith(u'.py'): print_verbose(u'Output:\n%s' % process.readAllStandardOutput())
print u' Done.'
def download_file(source_filename, dest_filename):
"""
Download a file and save it to disk.
``source_filename``
The file to download.
``dest_filename``
The new local file name.
"""
print_verbose(u'Downloading from: %s' % (SERVER_URL + source_filename))
page = urllib.urlopen(SERVER_URL + source_filename)
content = page.read().decode('utf8')
page.close()
file = open(dest_filename, u'w')
file.write(content.encode('utf8'))
file.close()
def download_translations():
"""
This method downloads the translation files from the Pootle server.
"""
print 'Download translation files from Pootle'
page = urllib.urlopen(SERVER_URL)
soup = BeautifulSoup(page)
languages = soup.findAll(text=re.compile(r'.*\.ts'))
for language in languages:
filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n',
language)
print_verbose(u'Get Translation File: %s' % filename)
download_file(language, filename)
print u' Done.'
def prepare_project():
"""
This method creates the project file needed to update the translation files
and compile them into .qm files.
"""
print u'Generating the openlp.pro file'
lines = []
start_dir = os.path.abspath(u'..')
start_dir = start_dir + os.sep
print_verbose(u'Starting directory: %s' % start_dir)
for root, dirs, files in os.walk(start_dir):
for file in files:
path = root.replace(start_dir, u'').replace(u'\\', u'/') #.replace(u'..', u'.')
if file.startswith(u'hook-') or file.startswith(u'test_'):
continue
ignore = False
for ignored_path in IGNORED_PATHS:
if path.startswith(ignored_path):
ignore = True
break
if ignore:
continue
ignore = False
for ignored_file in IGNORED_FILES:
if file == ignored_file:
ignore = True
break
if ignore:
continue
if file.endswith(u'.py') or file.endswith(u'.pyw'):
if path:
line = u'%s/%s' % (path, file) line = u'%s/%s' % (path, file)
self.printVerbose(u'Parsing "%s"' % line) else:
stringlist.append(u'SOURCES += %s' % line) line = file
elif file.endswith(u'.pyw'): print_verbose(u'Parsing "%s"' % line)
line = u'%s/%s' % (path, file) lines.append(u'SOURCES += %s' % line)
self.printVerbose(u'Parsing "%s"' % line) elif file.endswith(u'.ts'):
stringlist.append(u'SOURCES += %s' % line) line = u'%s/%s' % (path, file)
elif file.endswith(u'.ts'): print_verbose(u'Parsing "%s"' % line)
line = u'%s/%s' % (path, file) lines.append(u'TRANSLATIONS += %s' % line)
self.printVerbose(u'Parsing "%s"' % line) lines.sort()
stringlist.append(u'TRANSLATIONS += %s' % line) file = open(os.path.join(start_dir, u'openlp.pro'), u'w')
file.write(u'\n'.join(lines).encode('utf8'))
stringlist.sort() file.close()
self.write_file(os.path.join(start_dir, u'openlp.pro'), stringlist) print u' Done.'
print u' done'
self.process_stack()
def update(self): def update_translations():
print u'Update the translation files' print u'Update the translation files'
cmd = u'pylupdate4 -verbose -noobsolete ../openlp.pro' if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')):
self.start_cmd(cmd) print u'You have no generated a project file yet, please run this ' + \
u'script with the -p option.'
return
else:
os.chdir(os.path.abspath(u'..'))
run(u'pylupdate4 -verbose -noobsolete openlp.pro')
def generate(self): def generate_binaries():
print u'Generate the related *.qm files' print u'Generate the related *.qm files'
cmd = u'lrelease ../openlp.pro' if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')):
self.start_cmd(cmd) print u'You have no generated a project file yet, please run this ' + \
u'script with the -p option. It is also recommended that you ' + \
def write_file(self, filename, stringlist): u'this script with the -u option to update the translation ' + \
content = u'' u'files as well.'
for line in stringlist: return
content = u'%s%s\n' % (content, line) else:
file = open(filename, u'w') os.chdir(os.path.abspath(u'..'))
file.write(content.encode('utf8')) run(u'lrelease openlp.pro')
file.close()
def printVerbose(self, data): def create_translation(language):
if self.verbose: """
print u' %s' % data This method creates a new translation file.
def start_cmd(self, command): ``language``
self.printVerbose(command) The language file to create.
self.process = QtCore.QProcess() """
self.process.start(command) print "Create new Translation File"
while (self.process.waitForReadyRead()): filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n', language)
self.printVerbose(u'ReadyRead: %s' % QtCore.QString(self.process.readAll())) download_file(u'en.ts', filename)
self.printVerbose(self.process.readAllStandardError()) print u'\n** Please Note **\n'
self.printVerbose(self.process.readAllStandardOutput()) print u'In order to get this file into OpenLP and onto the Pootle ' + \
print u' done' u'translation server you will need to subscribe to the OpenLP' + \
self.process_stack() u'Translators mailing list, and request that your language file ' + \
u'be added to the project.\n'
print u' Done'
def process_stack(command_stack):
"""
This method looks at the commands in the command stack, and processes them
in the order they are in the stack.
``command_stack``
The command stack to process.
"""
if command_stack:
print u'Processing %d commands...' % len(command_stack)
for command in command_stack:
print u'%d.' % (command_stack.current_index),
if command == Command.Download:
download_translations()
elif command == Command.Prepare:
prepare_project()
elif command == Command.Update:
update_translations()
elif command == Command.Generate:
generate_binaries()
elif command == Command.Create:
command, arguments = command_stack[command_stack.current_index]
create_translation(*arguments)
print u'Finished processing commands.'
else:
print u'No commands to process.'
def main(): def main():
# start Main Class global verbose_mode
Util = TranslationUtils()
# Set up command line options. # Set up command line options.
usage = u''' usage = u'%prog [options]\nOptions are parsed in the order they are ' + \
This script handle the translation files for OpenLP. u'listed below. If no options are given, "-dpug" will be used.\n\n' + \
Usage: %prog [options] u'This script is used to manage OpenLP\'s translation files.'
If no option will be used, options "-d -p -u -g" will be set automatically
'''
parser = OptionParser(usage=usage) parser = OptionParser(usage=usage)
parser.add_option('-d', '--download-ts', action='store_true', parser.add_option('-d', '--download-ts', dest='download',
dest='download', help='Load languages from Pootle Server') action='store_true', help='download language files from Pootle')
parser.add_option('-c', '--create', metavar='lang', parser.add_option('-c', '--create', dest=u'create', metavar='LANG',
help='creation of new translation file, Parameter: language (e.g. "en_GB"') help='create a new translation file for language LANG, e.g. "en_GB"')
parser.add_option('-p', '--prepare', action='store_true', dest='prepare', parser.add_option('-p', '--prepare', dest='prepare', action='store_true',
help='preparation (generate pro file)') help='generate a project file, used to update the translations')
parser.add_option('-u', '--update', action='store_true', dest='update', parser.add_option('-u', '--update', action='store_true', dest='update',
help='update translation files') help='update translation files (needs a project file)')
parser.add_option('-g', '--generate', action='store_true', dest='generate', parser.add_option('-g', '--generate', dest='generate', action='store_true',
help='generate qm files') help='compile .ts files into .qm files')
parser.add_option('-v', '--verbose', action='store_true', dest='verbose', parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
help='Give more informations while processing') help='show extra information while processing translations')
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
# Create and populate the command stack
command_stack = CommandStack()
if options.download: if options.download:
Util.cmd_stack.append([Util.downloadTranslations]) command_stack.append(Command.Download)
if options.create: if options.create:
Util.cmd_stack.append([Util.creation, u'%s' % options.create]) command_stack.append(Command.Create, arguments=[options.create])
if options.prepare: if options.prepare:
Util.cmd_stack.append([Util.preparation]) command_stack.append(Command.Prepare)
if options.update: if options.update:
Util.cmd_stack.append([Util.update]) command_stack.append(Command.Update)
if options.generate: if options.generate:
Util.cmd_stack.append([Util.generate]) command_stack.append(Command.Generate)
if options.verbose: verbose_mode = options.verbose
Util.verbose = True if not command_stack:
command_stack.append(Command.Download)
command_stack.append(Command.Prepare)
command_stack.append(Command.Update)
command_stack.append(Command.Generate)
# Process the commands
process_stack(command_stack)
if len(Util.cmd_stack) == 0:
Util.cmd_stack.append([Util.downloadTranslations])
Util.cmd_stack.append([Util.preparation])
Util.cmd_stack.append([Util.update])
Util.cmd_stack.append([Util.generate])
Util.stack_count = len(Util.cmd_stack)
Util.process_stack()
if __name__ == u'__main__': if __name__ == u'__main__':
if os.path.split(os.path.abspath(u'.'))[1] != u'scripts': if os.path.split(os.path.abspath(u'.'))[1] != u'scripts':
print u'You need to run this script from the scripts directory.' print u'You need to run this script from the scripts directory.'
else: else:
main() main()

View File

@ -31,6 +31,18 @@ Windows Build Script
This script is used to build the Windows binary and the accompanying installer. This script is used to build the Windows binary and the accompanying installer.
For this script to work out of the box, it depends on a number of things: For this script to work out of the box, it depends on a number of things:
Python 2.6
This build script only works with Python 2.6.
PyQt4
You should already have this installed, OpenLP doesn't work without it. The
version the script expects is the packaged one available from River Bank
Computing.
PyEnchant
This script expects the precompiled, installable version of PyEnchant to be
installed. You can find this on the PyEnchant site.
Inno Setup 5 Inno Setup 5
Inno Setup should be installed into "C:\%PROGRAMFILES%\Inno Setup 5" Inno Setup should be installed into "C:\%PROGRAMFILES%\Inno Setup 5"
@ -41,9 +53,10 @@ UPX
add that directory to your PATH environment variable. add that directory to your PATH environment variable.
PyInstaller PyInstaller
PyInstaller should be a checkout of trunk, and in a directory called, PyInstaller should be a checkout of revision 844 of trunk, and in a
"pyinstaller" on the same level as OpenLP's Bazaar shared repository directory called, "pyinstaller" on the same level as OpenLP's Bazaar shared
directory. repository directory. The revision is very important as there is currently
a major regression in HEAD.
To install PyInstaller, first checkout trunk from Subversion. The easiest To install PyInstaller, first checkout trunk from Subversion. The easiest
way is to install TortoiseSVN and then checkout the following URL to a way is to install TortoiseSVN and then checkout the following URL to a
@ -80,17 +93,32 @@ from subprocess import Popen, PIPE
script_path = os.path.split(os.path.abspath(__file__))[0] script_path = os.path.split(os.path.abspath(__file__))[0]
branch_path = os.path.abspath(os.path.join(script_path, u'..')) branch_path = os.path.abspath(os.path.join(script_path, u'..'))
source_path = os.path.join(branch_path, u'openlp') source_path = os.path.join(branch_path, u'openlp')
i18n_path = os.path.join(branch_path, u'resources', u'i18n')
build_path = os.path.join(branch_path, u'build', u'pyi.win32', u'OpenLP')
dist_path = os.path.join(branch_path, u'dist', u'OpenLP') dist_path = os.path.join(branch_path, u'dist', u'OpenLP')
pyinstaller_path = os.path.abspath(os.path.join(branch_path, u'..', u'..', u'pyinstaller')) pyinstaller_path = os.path.abspath(os.path.join(branch_path, u'..', u'..', u'pyinstaller'))
innosetup_path = os.path.join(os.getenv(u'PROGRAMFILES'), 'Inno Setup 5') innosetup_path = os.path.join(os.getenv(u'PROGRAMFILES'), 'Inno Setup 5')
iss_path = os.path.join(branch_path, u'resources', u'innosetup') iss_path = os.path.join(branch_path, u'resources', u'innosetup')
lrelease_path = u'C:\\Python26\\Lib\\site-packages\\PyQt4\\bin\\lrelease.exe'
enchant_path = u'C:\\Python26\\Lib\\site-packages\\enchant'
def clean_build_directories():
#if not os.path.exists(build_path)
for root, dirs, files in os.walk(build_path, topdown=False):
print root
for file in files:
os.remove(os.path.join(root, file))
#os.removedirs(build_path)
for root, dirs, files in os.walk(dist_path, topdown=False):
for file in files:
os.remove(os.path.join(root, file))
#os.removedirs(dist_path)
def run_pyinstaller(): def run_pyinstaller():
print u'Running PyInstaller...' print u'Running PyInstaller...'
os.chdir(branch_path) os.chdir(branch_path)
pyinstaller = Popen((u'python', os.path.join(pyinstaller_path, u'Build.py'), pyinstaller = Popen((u'python', os.path.join(pyinstaller_path, u'Build.py'),
u'OpenLP.spec')) u'-y', u'OpenLP.spec'))
code = pyinstaller.wait() code = pyinstaller.wait()
if code != 0: if code != 0:
raise Exception(u'Error running PyInstaller Build.py') raise Exception(u'Error running PyInstaller Build.py')
@ -120,6 +148,19 @@ def write_version_file():
f.write(versionstring) f.write(versionstring)
f.close() f.close()
def copy_enchant():
print u'Copying enchant/pyenchant...'
source = enchant_path
dest = os.path.join(dist_path, u'enchant')
for root, dirs, files in os.walk(source):
for filename in files:
if not filename.endswith(u'.pyc') and not filename.endswith(u'.pyo'):
dest_path = os.path.join(dest, root[len(source)+1:])
if not os.path.exists(dest_path):
os.makedirs(dest_path)
copy(os.path.join(root, filename),
os.path.join(dest_path, filename))
def copy_plugins(): def copy_plugins():
print u'Copying plugins...' print u'Copying plugins...'
source = os.path.join(source_path, u'plugins') source = os.path.join(source_path, u'plugins')
@ -138,6 +179,21 @@ def copy_windows_files():
copy(os.path.join(iss_path, u'OpenLP.ico'), os.path.join(dist_path, u'OpenLP.ico')) copy(os.path.join(iss_path, u'OpenLP.ico'), os.path.join(dist_path, u'OpenLP.ico'))
copy(os.path.join(iss_path, u'LICENSE.txt'), os.path.join(dist_path, u'LICENSE.txt')) copy(os.path.join(iss_path, u'LICENSE.txt'), os.path.join(dist_path, u'LICENSE.txt'))
def compile_translations():
files = os.listdir(i18n_path)
if not os.path.exists(os.path.join(dist_path, u'i18n')):
os.makedirs(os.path.join(dist_path, u'i18n'))
for file in files:
if file.endswith(u'.ts'):
source_path = os.path.join(i18n_path, file)
dest_path = os.path.join(dist_path, u'i18n',
file.replace(u'.ts', u'.qm'))
lconvert = Popen(u'"%s" "%s" -qm "%s"' % (lrelease_path, \
source_path, dest_path))
code = lconvert.wait()
if code != 0:
print 'Error running lconvert on %s' % source_path
def run_innosetup(): def run_innosetup():
print u'Running Inno Setup...' print u'Running Inno Setup...'
os.chdir(iss_path) os.chdir(iss_path)
@ -150,17 +206,22 @@ def run_innosetup():
raise Exception(u'Error running Inno Setup') raise Exception(u'Error running Inno Setup')
def main(): def main():
print "Script path:", script_path import sys
print "Branch path:", branch_path if len(sys.argv) > 1 and (sys.argv[1] == u'-v' or sys.argv[1] == u'--verbose'):
print "Source path:", source_path print "Script path:", script_path
print "\"dist\" path:", dist_path print "Branch path:", branch_path
print "PyInstaller path:", pyinstaller_path print "Source path:", source_path
print "Inno Setup path:", innosetup_path print "\"dist\" path:", dist_path
print "ISS file path:", iss_path print "PyInstaller path:", pyinstaller_path
print "Inno Setup path:", innosetup_path
print "ISS file path:", iss_path
#clean_build_directories()
run_pyinstaller() run_pyinstaller()
write_version_file() write_version_file()
copy_enchant()
copy_plugins() copy_plugins()
copy_windows_files() copy_windows_files()
compile_translations()
run_innosetup() run_innosetup()
print "Done." print "Done."