diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index c74b89144..1b5261773 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -28,11 +28,14 @@ The :mod:`serviceitem` provides the service item functionality including the type and capability of an item. """ +import datetime import logging +import mutagen import os import uuid from openlp.core.lib import build_icon, clean_tags, expand_tags +from openlp.core.lib.ui import UiStrings log = logging.getLogger(__name__) @@ -60,6 +63,7 @@ class ItemCapabilities(object): AddIfNewItem = 9 ProvidesOwnDisplay = 10 AllowsDetailedTitleDisplay = 11 + AllowsVarableStartTime = 12 class ServiceItem(object): @@ -105,6 +109,7 @@ class ServiceItem(object): self.data_string = u'' self.edit_id = None self.xml_version = None + self.start_time = 0 self._new_item() def _new_item(self): @@ -257,7 +262,8 @@ class ServiceItem(object): u'capabilities': self.capabilities, u'search': self.search_string, u'data': self.data_string, - u'xml_version': self.xml_version + u'xml_version': self.xml_version, + u'start_time': self.start_time } service_data = [] if self.service_item_type == ServiceItemType.Text: @@ -301,6 +307,8 @@ class ServiceItem(object): self.data_string = header[u'data'] if u'xml_version' in header: self.xml_version = header[u'xml_version'] + if u'start_time' in header: + self.start_time = header[u'start_time'] if self.service_item_type == ServiceItemType.Text: for slide in serviceitem[u'serviceitem'][u'data']: self._raw_frames.append(slide) @@ -420,3 +428,30 @@ class ServiceItem(object): return self._raw_frames[row][u'path'] except IndexError: return u'' + + def get_media_time(self): + """ + Returns the start and finish time for a media item + """ + tooltip = None + start = None + end = None + if self.start_time != 0: + start = UiStrings.StartTimeCode % \ + unicode(datetime.timedelta(seconds=self.start_time)) + path = os.path.join(self.get_frames()[0][u'path'], + self.get_frames()[0][u'title']) + if os.path.isfile(path): + file = mutagen.File(path) + if file is not None: + seconds = int(file.info.length) + end = UiStrings.LengthTime % \ + unicode(datetime.timedelta(seconds=seconds)) + if not start and not end: + return None + elif start and not end: + return start + elif not start and end: + return end + else: + return u'%s : %s' % (start, end) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 67ce33ca4..225e1335c 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -170,8 +170,8 @@ class HorizontalType(object): Type enumeration for horizontal alignment. """ Left = 0 - Center = 1 - Right = 2 + Center = 2 + Right = 1 @staticmethod def to_string(horizontal_type): diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 4852bc3ca..f335c165f 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -63,6 +63,7 @@ class UiStrings(object): Image = translate('OpenLP.Ui', 'Image') Import = translate('OpenLP.Ui', 'Import') ImportType = unicode(translate('OpenLP.Ui', 'Import %s')) + LengthTime = unicode(translate('OpenLP.Ui', 'Length %s')) Live = translate('OpenLP.Ui', 'Live') LivePanel = translate('OpenLP.Ui', 'Live Panel') Load = translate('OpenLP.Ui', 'Load') @@ -92,6 +93,7 @@ class UiStrings(object): 'Send the selected %s live.')) Service = translate('OpenLP.Ui', 'Service') ServiceManager = translate('OpenLP.Ui', 'Service Manager') + StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s')) Theme = translate('OpenLP.Ui', 'Theme', 'Singular') Themes = translate('OpenLP.Ui', 'Themes', 'Plural') ThemeManager = translate('OpenLP.Ui', 'Theme Manager') @@ -100,7 +102,6 @@ class UiStrings(object): 'Toggle the visibility of the %s.')) Version = translate('OpenLP.Ui', 'Version') - def add_welcome_page(parent, image): """ Generate an opening welcome page for a wizard using a provided image. diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index d820c9a5b..45218802e 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -53,6 +53,7 @@ class HideMode(object): from themeform import ThemeForm from filerenameform import FileRenameForm +from starttimeform import StartTimeForm from maindisplay import MainDisplay from servicenoteform import ServiceNoteForm from serviceitemeditform import ServiceItemEditForm diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 05a301bd7..cc2f2f72d 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -65,6 +65,7 @@ class MainDisplay(DisplayWidget): self.parent = parent self.screens = screens self.isLive = live + self.alertTab = None self.hideMode = None self.override = {} mainIcon = build_icon(u':/icon/openlp-logo-16x16.png') @@ -105,6 +106,9 @@ class MainDisplay(DisplayWidget): self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject) Phonon.createPath(self.mediaObject, self.videoWidget) Phonon.createPath(self.mediaObject, self.audio) + QtCore.QObject.connect(self.mediaObject, + QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'), + self.videoStart) self.webView = QtWebKit.QWebView(self) self.webView.setGeometry(0, 0, self.screen[u'size'].width(), self.screen[u'size'].height()) @@ -144,7 +148,7 @@ class MainDisplay(DisplayWidget): serviceItem = ServiceItem() serviceItem.bg_image_bytes = image_to_byte(initialFrame) self.webView.setHtml(build_html(serviceItem, self.screen, - self.parent.alertTab, self.isLive, None)) + self.alertTab, self.isLive, None)) self.initialFrame = True # To display or not to display? if not self.screen[u'primary']: @@ -340,6 +344,13 @@ class MainDisplay(DisplayWidget): Receiver.send_message(u'maindisplay_active') return self.preview() + def videoStart(self, newState, oldState): + """ + Start the video at a predetermined point. + """ + if newState == 2: + self.mediaObject.seek(self.serviceItem.start_time * 1000) + def isWebLoaded(self): """ Called by webView event to show display is fully loaded @@ -405,7 +416,7 @@ class MainDisplay(DisplayWidget): if self.serviceItem.themedata.background_filename: self.serviceItem.bg_image_bytes = self.imageManager. \ get_image_bytes(self.serviceItem.themedata.theme_name) - html = build_html(self.serviceItem, self.screen, self.parent.alertTab, + html = build_html(self.serviceItem, self.screen, self.alertTab, self.isLive, background) log.debug(u'buildHtml - pre setHtml') self.webView.setHtml(html) diff --git a/openlp/core/ui/serviceitemeditform.py b/openlp/core/ui/serviceitemeditform.py index 70518f30c..ae09cb0b2 100644 --- a/openlp/core/ui/serviceitemeditform.py +++ b/openlp/core/ui/serviceitemeditform.py @@ -110,11 +110,12 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog): temp = self.itemList[row] self.itemList.remove(self.itemList[row]) if direction == u'up': - self.itemList.insert(row - 1, temp) + row -= 1 else: - self.itemList.insert(row + 1, temp) + row += 1 + self.itemList.insert(row, temp) self.loadData() - self.listWidget.setCurrentRow(row + 1) + self.listWidget.setCurrentRow(row) def onCurrentRowChanged(self, row): """ diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 9041b47a7..f97a96e4c 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -36,7 +36,7 @@ from openlp.core.lib import OpenLPToolbar, ServiceItem, context_menu_action, \ Receiver, build_icon, ItemCapabilities, SettingsManager, translate, \ ThemeLevel from openlp.core.lib.ui import UiStrings, critical_error_message_box -from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm +from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceorderform import PrintServiceOrderForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ split_filename @@ -88,6 +88,7 @@ class ServiceManager(QtGui.QWidget): self._fileName = u'' self.serviceNoteForm = ServiceNoteForm(self.mainwindow) self.serviceItemEditForm = ServiceItemEditForm(self.mainwindow) + self.startTimeForm = StartTimeForm(self.mainwindow) # start with the layout self.layout = QtGui.QVBoxLayout(self) self.layout.setSpacing(0) @@ -269,16 +270,19 @@ class ServiceManager(QtGui.QWidget): self.notesAction = self.menu.addAction( translate('OpenLP.ServiceManager', '&Notes')) self.notesAction.setIcon(build_icon(u':/services/service_notes.png')) + self.timeAction = self.menu.addAction( + translate('OpenLP.ServiceManager', '&Start Time')) + self.timeAction.setIcon(build_icon(u':/media/media_time.png')) self.deleteAction = self.menu.addAction( translate('OpenLP.ServiceManager', '&Delete From Service')) self.deleteAction.setIcon(build_icon(u':/general/general_delete.png')) self.sep1 = self.menu.addAction(u'') self.sep1.setSeparator(True) self.previewAction = self.menu.addAction( - translate('OpenLP.ServiceManager', '&Preview Verse')) + translate('OpenLP.ServiceManager', 'Show &Preview')) self.previewAction.setIcon(build_icon(u':/general/general_preview.png')) self.liveAction = self.menu.addAction( - translate('OpenLP.ServiceManager', '&Live Verse')) + translate('OpenLP.ServiceManager', 'Show &Live')) self.liveAction.setIcon(build_icon(u':/general/general_live.png')) self.sep2 = self.menu.addAction(u'') self.sep2.setSeparator(True) @@ -562,6 +566,7 @@ class ServiceManager(QtGui.QWidget): self.editAction.setVisible(False) self.maintainAction.setVisible(False) self.notesAction.setVisible(False) + self.timeAction.setVisible(False) if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit)\ and serviceItem[u'service_item'].edit_id: self.editAction.setVisible(True) @@ -570,6 +575,9 @@ class ServiceManager(QtGui.QWidget): self.maintainAction.setVisible(True) if item.parent() is None: self.notesAction.setVisible(True) + if serviceItem[u'service_item']\ + .is_capable(ItemCapabilities.AllowsVarableStartTime): + self.timeAction.setVisible(True) self.themeMenu.menuAction().setVisible(False) if serviceItem[u'service_item'].is_text(): self.themeMenu.menuAction().setVisible(True) @@ -582,6 +590,8 @@ class ServiceManager(QtGui.QWidget): self.onDeleteFromService() if action == self.notesAction: self.onServiceItemNoteForm() + if action == self.timeAction: + self.onStartTimeForm() if action == self.previewAction: self.makePreview() if action == self.liveAction: @@ -596,6 +606,16 @@ class ServiceManager(QtGui.QWidget): self.serviceNoteForm.textEdit.toPlainText() self.repaintServiceList(item, -1) + def onStartTimeForm(self): + item = self.findServiceItem()[0] + self.startTimeForm.item = self.serviceItems[item] + if self.startTimeForm.exec_(): + self.serviceItems[item][u'service_item'].start_time = \ + self.startTimeForm.hourSpinBox.value() * 3600 + \ + self.startTimeForm.minuteSpinBox.value() * 60 + \ + self.startTimeForm.secondSpinBox.value() + self.repaintServiceList(item, -1) + def onServiceItemEditForm(self): item = self.findServiceItem()[0] self.serviceItemEditForm.setServiceItem( @@ -842,6 +862,11 @@ class ServiceManager(QtGui.QWidget): text = frame[u'title'].replace(u'\n', u' ') child.setText(0, text[:40]) child.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(count)) + if item[u'service_item'] \ + .is_capable(ItemCapabilities.AllowsVarableStartTime): + tip = item[u'service_item'].get_media_time() + if tip: + child.setToolTip(0, tip) if serviceItem == itemcount: if item[u'expanded'] and serviceItemChild == count: self.serviceManagerList.setCurrentItem(child) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index cd43cddb4..52626f24f 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -415,6 +415,7 @@ class SlideController(QtGui.QWidget): # rebuild display as screen size changed self.display = MainDisplay(self, self.screens, self.isLive) self.display.imageManager = self.parent.renderManager.image_manager + self.display.alertTab = self.alertTab self.display.setup() if self.isLive: self.__addActionsToWidget(self.display) diff --git a/openlp/core/ui/starttimedialog.py b/openlp/core/ui/starttimedialog.py new file mode 100644 index 000000000..8dcc2c9ee --- /dev/null +++ b/openlp/core/ui/starttimedialog.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2011 Raoul Snyman # +# Portions copyright (c) 2008-2011 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 # +############################################################################### + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import translate +from openlp.core.lib.ui import create_accept_reject_button_box + +class Ui_StartTimeDialog(object): + def setupUi(self, StartTimeDialog): + StartTimeDialog.setObjectName(u'StartTimeDialog') + StartTimeDialog.resize(300, 10) + self.dialogLayout = QtGui.QGridLayout(StartTimeDialog) + self.dialogLayout.setObjectName(u'dialogLayout') + self.hourLabel = QtGui.QLabel(StartTimeDialog) + self.hourLabel.setObjectName("hourLabel") + self.dialogLayout.addWidget(self.hourLabel, 0, 0, 1, 1) + self.hourSpinBox = QtGui.QSpinBox(StartTimeDialog) + self.hourSpinBox.setObjectName("hourSpinBox") + self.dialogLayout.addWidget(self.hourSpinBox, 0, 1, 1, 1) + self.minuteLabel = QtGui.QLabel(StartTimeDialog) + self.minuteLabel.setObjectName("minuteLabel") + self.dialogLayout.addWidget(self.minuteLabel, 1, 0, 1, 1) + self.minuteSpinBox = QtGui.QSpinBox(StartTimeDialog) + self.minuteSpinBox.setObjectName("minuteSpinBox") + self.dialogLayout.addWidget(self.minuteSpinBox, 1, 1, 1, 1) + self.secondLabel = QtGui.QLabel(StartTimeDialog) + self.secondLabel.setObjectName("secondLabel") + self.dialogLayout.addWidget(self.secondLabel, 2, 0, 1, 1) + self.secondSpinBox = QtGui.QSpinBox(StartTimeDialog) + self.secondSpinBox.setObjectName("secondSpinBox") + self.dialogLayout.addWidget(self.secondSpinBox, 2, 1, 1, 1) + self.buttonBox = create_accept_reject_button_box(StartTimeDialog, True) + self.dialogLayout.addWidget(self.buttonBox, 4, 0, 1, 2) + self.retranslateUi(StartTimeDialog) + self.setMaximumHeight(self.sizeHint().height()) + QtCore.QMetaObject.connectSlotsByName(StartTimeDialog) + + def retranslateUi(self, StartTimeDialog): + self.setWindowTitle(translate('OpenLP.StartTimeForm', + 'Item Start Time')) + self.hourLabel.setText(translate('OpenLP.StartTimeForm', 'Hours:')) + self.hourSpinBox.setSuffix(translate('OpenLP.StartTimeForm', 'h')) + self.minuteSpinBox.setSuffix(translate('OpenLP.StartTimeForm', 'm')) + self.secondSpinBox.setSuffix(translate('OpenLP.StartTimeForm', 's')) + self.minuteLabel.setText(translate('OpenLP.StartTimeForm', 'Minutes:')) + self.secondLabel.setText(translate('OpenLP.StartTimeForm', 'Seconds:')) diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py new file mode 100644 index 000000000..1280931d5 --- /dev/null +++ b/openlp/core/ui/starttimeform.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2011 Raoul Snyman # +# Portions copyright (c) 2008-2011 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 # +############################################################################### + +from PyQt4 import QtGui + +from starttimedialog import Ui_StartTimeDialog + +class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog): + """ + The exception dialog + """ + def __init__(self, parent): + QtGui.QDialog.__init__(self, parent) + self.setupUi(self) + + def exec_(self): + """ + Run the Dialog with correct heading. + """ + seconds = self.item[u'service_item'].start_time + hours = seconds / 3600 + seconds -= 3600 * hours + minutes = seconds / 60 + seconds -= 60 * minutes + self.hourSpinBox.setValue(hours) + self.minuteSpinBox.setValue(minutes) + self.secondSpinBox.setValue(seconds) + return QtGui.QDialog.exec_(self) + diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index a589c7309..a23077c39 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -184,7 +184,7 @@ def _get_os_dir_path(dir_type): elif dir_type == AppLocation.CacheDir: return os.path.join(BaseDirectory.xdg_cache_home, u'openlp') if dir_type == AppLocation.DataDir: - return os.path.join(os.getenv(u'HOME'), u'openlp', u'data') + return os.path.join(os.getenv(u'HOME'), u'.openlp', u'data') return os.path.join(os.getenv(u'HOME'), u'.openlp') def _get_frozen_path(frozen_option, non_frozen_option): diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 758bd2e64..e50740be9 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -123,6 +123,7 @@ class MediaMediaItem(MediaManagerItem): service_item.title = unicode( self.plugin.getString(StringContent.Name)[u'singular']) service_item.add_capability(ItemCapabilities.RequiresMedia) + service_item.add_capability(ItemCapabilities.AllowsVarableStartTime) # force a nonexistent theme service_item.theme = -1 frame = u':/media/image_clapperboard.png'