diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 2f6d973e2..764875f4f 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -81,6 +81,7 @@ class ServiceItem(object): self.notes = u'' self.from_plugin = False self.capabilities = [] + self.isValid = True def add_capability(self, capability): self.capabilities.append(capability) diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index ac4f92336..f55d81a70 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -44,7 +44,7 @@ class GeneralTab(SettingsTab): settings = QtCore.QSettings() settings.beginGroup(self.settingsSection) self.MonitorNumber = settings.value(u'monitor', - QtCore.QVariant(self.screens.monitor_number)).toInt()[0] + QtCore.QVariant(self.screens.display_count - 1)).toInt()[0] self.screens.set_current_display(self.MonitorNumber) self.screens.monitor_number = self.MonitorNumber self.DisplayOnMonitor = settings.value( diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 03cdbcecc..3a35a7a58 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -101,6 +101,7 @@ class ServiceManager(QtGui.QWidget): self.parent = parent self.serviceItems = [] self.serviceName = u'' + self.suffixes = [] self.droppos = 0 #is a new service and has not been saved self.isNew = True @@ -228,6 +229,9 @@ class ServiceManager(QtGui.QWidget): self.themeMenu = QtGui.QMenu(self.trUtf8(u'&Change Item Theme')) self.menu.addMenu(self.themeMenu) + def supportedSuffixes(self, suffix): + self.suffixes.append(suffix) + def contextMenu(self, point): item = self.ServiceManagerList.itemAt(point) if item is None: @@ -326,7 +330,7 @@ class ServiceManager(QtGui.QWidget): Called by a signal to select a specific item """ self.setItem(int(message[0])) - + def setItem(self, index): """ Makes a specific item in the service live @@ -495,19 +499,22 @@ class ServiceManager(QtGui.QWidget): for itemcount, item in enumerate(self.serviceItems): serviceitem = item[u'service_item'] treewidgetitem = QtGui.QTreeWidgetItem(self.ServiceManagerList) - if serviceitem.notes: - icon = QtGui.QImage(serviceitem.icon) - icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation) - overlay = QtGui.QImage(':/services/service_item_notes.png') - overlay = overlay.scaled(80, 80, QtCore.Qt.KeepAspectRatio, - QtCore.Qt.SmoothTransformation) - painter = QtGui.QPainter(icon) - painter.drawImage(0, 0, overlay) - painter.end() - treewidgetitem.setIcon(0, build_icon(icon)) + if serviceitem.isValid: + if serviceitem.notes: + icon = QtGui.QImage(serviceitem.icon) + icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation) + overlay = QtGui.QImage(':/services/service_item_notes.png') + overlay = overlay.scaled(80, 80, QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation) + painter = QtGui.QPainter(icon) + painter.drawImage(0, 0, overlay) + painter.end() + treewidgetitem.setIcon(0, build_icon(icon)) + else: + treewidgetitem.setIcon(0, serviceitem.iconic_representation) else: - treewidgetitem.setIcon(0, serviceitem.iconic_representation) + treewidgetitem.setIcon(0, build_icon(u':/general/general_delete.png')) treewidgetitem.setText(0, serviceitem.title) treewidgetitem.setToolTip(0, serviceitem.notes) treewidgetitem.setData(0, QtCore.Qt.UserRole, @@ -651,8 +658,8 @@ class ServiceManager(QtGui.QWidget): serviceitem = ServiceItem() serviceitem.RenderManager = self.parent.RenderManager serviceitem.set_from_service(item, self.servicePath) - if self.validateItem(serviceitem): - self.addServiceItem(serviceitem) + self.validateItem(serviceitem) + self.addServiceItem(serviceitem) try: if os.path.isfile(p_file): os.remove(p_file) @@ -671,12 +678,14 @@ class ServiceManager(QtGui.QWidget): self.parent.serviceChanged(True, self.serviceName) def validateItem(self, serviceItem): -# print "---" -# print serviceItem.name -# print serviceItem.title -# print serviceItem.service_item_path -# print serviceItem.service_item_type - return True + """ + Validates the service item and if the suffix matches an accepted + one it allows the item to be displayed + """ + if serviceItem.is_command(): + type = serviceItem._raw_frames[0][u'title'].split(u'.')[1] + if type not in self.suffixes: + serviceItem.isValid = False def cleanUp(self): """ @@ -760,8 +769,17 @@ class ServiceManager(QtGui.QWidget): Send the current item to the Preview slide controller """ item, count = self.findServiceItem() - self.parent.PreviewController.addServiceManagerItem( - self.serviceItems[item][u'service_item'], count) + if self.serviceItems[item][u'service_item'].isValid: + self.parent.PreviewController.addServiceManagerItem( + self.serviceItems[item][u'service_item'], count) + else: + QtGui.QMessageBox.critical(self, + self.trUtf8('Missing Display Handler'), + self.trUtf8('Your item cannot be displayed as ' + 'there is no handler to display it'), + QtGui.QMessageBox.StandardButtons( + QtGui.QMessageBox.Ok), + QtGui.QMessageBox.Ok) def getServiceItem(self): """ @@ -778,17 +796,26 @@ class ServiceManager(QtGui.QWidget): Send the current item to the Live slide controller """ item, count = self.findServiceItem() - self.parent.LiveController.addServiceManagerItem( - self.serviceItems[item][u'service_item'], count) - if QtCore.QSettings().value( - self.parent.generalSettingsSection + u'/auto preview', - QtCore.QVariant(False)).toBool(): - item += 1 - if self.serviceItems and item < len(self.serviceItems) and \ - self.serviceItems[item][u'service_item'].is_capable( - ItemCapabilities.AllowsPreview): - self.parent.PreviewController.addServiceManagerItem( - self.serviceItems[item][u'service_item'], 0) + if self.serviceItems[item][u'service_item'].isValid: + self.parent.LiveController.addServiceManagerItem( + self.serviceItems[item][u'service_item'], count) + if QtCore.QSettings().value( + self.parent.generalSettingsSection + u'/auto preview', + QtCore.QVariant(False)).toBool(): + item += 1 + if self.serviceItems and item < len(self.serviceItems) and \ + self.serviceItems[item][u'service_item'].is_capable( + ItemCapabilities.AllowsPreview): + self.parent.PreviewController.addServiceManagerItem( + self.serviceItems[item][u'service_item'], 0) + else: + QtGui.QMessageBox.critical(self, + self.trUtf8('Missing Display Handler'), + self.trUtf8('Your item cannot be displayed as ' + 'there is no handler to display it'), + QtGui.QMessageBox.StandardButtons( + QtGui.QMessageBox.Ok), + QtGui.QMessageBox.Ok) def remoteEdit(self): """ @@ -920,7 +947,7 @@ class ServiceManager(QtGui.QWidget): return item.data(0, QtCore.Qt.UserRole).toInt()[0] else: return parentitem.data(0, QtCore.Qt.UserRole).toInt()[0] - + def listRequest(self, message=None): data = [] curindex, count = self.findServiceItem() diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index c0d0d915f..15502423b 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -339,16 +339,16 @@ class SlideController(QtGui.QWidget): QtCore.SIGNAL(u'slidecontroller_%s_change' % self.type_prefix), self.onSlideChange) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'slidecontroller_%s_set' % self.type_prefix), + QtCore.SIGNAL(u'slidecontroller_%s_set' % self.type_prefix), self.onSlideSelectedIndex) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'slidecontroller_%s_blank' % self.type_prefix), + QtCore.SIGNAL(u'slidecontroller_%s_blank' % self.type_prefix), self.onSlideBlank) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'slidecontroller_%s_unblank' % self.type_prefix), + QtCore.SIGNAL(u'slidecontroller_%s_unblank' % self.type_prefix), self.onSlideUnblank) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'slidecontroller_%s_text_request' % self.type_prefix), + QtCore.SIGNAL(u'slidecontroller_%s_text_request' % self.type_prefix), self.onTextRequest) QtCore.QObject.connect(self.Splitter, QtCore.SIGNAL(u'splitterMoved(int, int)'), self.trackSplitter) @@ -486,6 +486,7 @@ class SlideController(QtGui.QWidget): Display the slide number passed """ log.debug(u'processManagerItem') + self.onStopLoop() #If old item was a command tell it to stop if self.serviceItem and self.serviceItem.is_command(): self.onMediaStop() @@ -589,8 +590,8 @@ class SlideController(QtGui.QWidget): data_item[u'selected'] = \ (self.PreviewListWidget.currentRow() == framenumber) data.append(data_item) - Receiver.send_message(u'slidecontroller_%s_text_response' - % self.type_prefix, data) + Receiver.send_message(u'slidecontroller_%s_text_response' + % self.type_prefix, data) #Screen event methods def onSlideSelectedFirst(self): @@ -614,7 +615,7 @@ class SlideController(QtGui.QWidget): index = int(message[0]) if not self.serviceItem: return - Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(), + Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive, index]) if self.serviceItem.is_command(): self.updatePreview() @@ -674,10 +675,10 @@ class SlideController(QtGui.QWidget): self.themeButton.setChecked(False) if checked: Receiver.send_message(u'maindisplay_hide', HideMode.Screen) - self.blankPlugin(True) + self.hidePlugin(True) else: Receiver.send_message(u'maindisplay_show') - self.blankPlugin(False) + self.hidePlugin(False) def blankPlugin(self, blank): """ @@ -693,6 +694,20 @@ class SlideController(QtGui.QWidget): % self.serviceItem.name.lower(), [self.serviceItem, self.isLive]) + def hidePlugin(self, hide): + """ + Blank the display screen. + """ + if self.serviceItem is not None: + if hide: + Receiver.send_message(u'%s_hide' + % self.serviceItem.name.lower(), + [self.serviceItem, self.isLive]) + else: + Receiver.send_message(u'%s_unblank' + % self.serviceItem.name.lower(), + [self.serviceItem, self.isLive]) + def onSlideSelected(self): """ Generate the preview when you click on a slide. @@ -827,7 +842,9 @@ class SlideController(QtGui.QWidget): """ Stop the timer loop running """ - self.killTimer(self.timer_id) + if self.timer_id != 0: + self.killTimer(self.timer_id) + self.timer_id = 0 def timerEvent(self, event): """ diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 306664fe3..02101d260 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -143,7 +143,7 @@ class ImageMediaItem(MediaManagerItem): def generateSlideData(self, service_item, item=None): items = self.ListView.selectedIndexes() if items: - service_item.title = self.trUtf8('Image(s)') + service_item.title = unicode(self.trUtf8('Image(s)')) service_item.add_capability(ItemCapabilities.AllowsMaintain) service_item.add_capability(ItemCapabilities.AllowsPreview) service_item.add_capability(ItemCapabilities.AllowsLoop) diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 1fb7d7405..bdf28c8a4 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -58,6 +58,7 @@ class MediaPlugin(Plugin): if len(value) == 2: if list.find(value[1]) == -1: list += u'*.%s ' % value[1] + self.service_manager.supportedSuffixes(value[1]) type = u'' return list, type diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 9c654b198..51663ad26 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -74,6 +74,7 @@ class PresentationMediaItem(MediaManagerItem): for type in types: if fileType.find(type) == -1: fileType += u'*%s ' % type + self.parent.service_manager.supportedSuffixes(type) self.OnNewFileMasks = self.trUtf8('Presentations (%s)' % fileType) def requiredIcons(self): diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index b15f25642..b5b6b6879 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -160,6 +160,16 @@ class Controller(object): return self.doc.blank_screen() + def stop(self): + log.debug(u'Live = %s, stop' % self.isLive) + if not self.isLive: + return + if not self.doc.is_loaded(): + return + if not self.doc.is_active(): + return + self.doc.stop_presentation() + def unblank(self): log.debug(u'Live = %s, unblank' % self.isLive) if not self.isLive: @@ -190,6 +200,8 @@ class MessageListener(object): QtCore.SIGNAL(u'presentations_start'), self.startup) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'presentations_stop'), self.shutdown) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'presentations_hide'), self.hide) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'presentations_first'), self.first) QtCore.QObject.connect(Receiver.get_receiver(), @@ -279,6 +291,11 @@ class MessageListener(object): else: self.previewHandler.shutdown() + def hide(self, message): + isLive, item = self.decode_message(message) + if isLive: + self.liveHandler.stop() + def blank(self, message): isLive, item = self.decode_message(message) if isLive: diff --git a/openlp/plugins/remotes/html/index.html b/openlp/plugins/remotes/html/index.html index 25b08fd43..13fc6d094 100644 --- a/openlp/plugins/remotes/html/index.html +++ b/openlp/plugins/remotes/html/index.html @@ -63,7 +63,7 @@ function response(eventname, req){ html += ' style="font-weight: bold"'; html += '>'; html += '' + data[row]['tag'] + ''; - html += '' + data[row]['text'].replace(/\\n/g, '
'); + html += '' + data[row]['text'].replace(/\n/g, '
'); html += ''; } html += ''; @@ -105,10 +105,12 @@ send_event("remotes_poll_request");
+ (Click service item to go live.)

+ (Click verse to display.)

OpenLP website diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 2c4105dab..b66f0e215 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -152,44 +152,60 @@ class HttpConnection(object): log.debug(u'received: ' + data) words = data.split(u' ') html = None + mimetype = None if words[0] == u'GET': url = urlparse.urlparse(words[1]) params = self.load_params(url.query) folders = url.path.split(u'/') if folders[1] == u'': - html = self.serve_file(u'') + mimetype, html = self.serve_file(u'') elif folders[1] == u'files': - html = self.serve_file(folders[2]) + mimetype, html = self.serve_file(os.sep.join(folders[2:])) elif folders[1] == u'send': html = self.process_event(folders[2], params) elif folders[1] == u'request': if self.process_request(folders[2], params): return if html: - html = self.get_200_ok() + html + u'\n' + if mimetype: + self.send_200_ok(mimetype) + else: + self.send_200_ok() + self.socket.write(html) else: - html = self.get_404_not_found() - self.socket.write(html) + self.send_404_not_found() self.close() def serve_file(self, filename): """ - Send a file to the socket. For now, just .html files + Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder. If subfolders requested return 404, easier for security for the present. Ultimately for i18n, this could first look for xx/file.html before falling back to file.html... where xx is the language, e.g. 'en' """ - log.debug(u'serve file request %s' % filename) + log.debug(u'serve file request %s' % filename) if not filename: filename = u'index.html' - if os.path.basename(filename) != filename: + path = os.path.normpath(os.path.join(self.parent.html_dir, filename)) + if not path.startswith(self.parent.html_dir): return None (fileroot, ext) = os.path.splitext(filename) - if ext != u'.html': - return None - path = os.path.join(self.parent.html_dir, filename) + if ext == u'.html': + mimetype = u'text/html' + elif ext == u'.css': + mimetype = u'text/css' + elif ext == u'.js': + mimetype = u'application/x-javascript' + elif ext == u'.jpg': + mimetype = u'image/jpeg' + elif ext == u'.gif': + mimetype = u'image/gif' + elif ext == u'.png': + mimetype = u'image/png' + else: + return (None, None) try: f = open(path, u'rb') except: @@ -198,12 +214,13 @@ class HttpConnection(object): log.debug(u'Opened %s' % path) html = f.read() f.close() - return html - + return (mimetype, html) + def load_params(self, query): """ Decode the query string parameters sent from the browser """ + log.debug(u'loading params %s' % query) params = urlparse.parse_qs(query) if not params: return None @@ -216,6 +233,7 @@ class HttpConnection(object): Currently lets anything through. Later we should restrict and perform basic parameter checking, otherwise rogue clients could crash openlp """ + log.debug(u'Processing event %s' % event) if params: Receiver.send_message(event, params) else: @@ -233,6 +251,7 @@ class HttpConnection(object): is just waiting for slide change/song change activity. This can wait longer (one minute) """ + log.debug(u'Processing request %s' % event) if not event.endswith(u'_request'): return False self.event = event @@ -258,36 +277,36 @@ class HttpConnection(object): The recipient of a _request signal has sent data. Convert this to json and return it to client """ + log.debug(u'Processing response for %s' % self.event) if not self.socket: return self.timer.stop() html = json.dumps(data) - html = self.get_200_ok() + html + u'\n' + self.send_200_ok() self.socket.write(html) self.close() - def get_200_ok(self): + def send_200_ok(self, mimetype='text/html; charset="utf-8"'): """ Successful request. Send OK headers. Assume html for now. """ - return u'HTTP/1.1 200 OK\r\n' + \ - u'Content-Type: text/html; charset="utf-8"\r\n' + \ - u'\r\n' + self.socket.write(u'HTTP/1.1 200 OK\r\n' + \ + u'Content-Type: %s\r\n\r\n' % mimetype) - def get_404_not_found(self): + def send_404_not_found(self): """ Invalid url. Say so """ - return u'HTTP/1.1 404 Not Found\r\n'+ \ + self.socket.write(u'HTTP/1.1 404 Not Found\r\n'+ \ u'Content-Type: text/html; charset="utf-8"\r\n' + \ - u'\r\n' + u'\r\n') - def get_408_timeout(self): + def send_408_timeout(self): """ A _request hasn't returned anything in the timeout period. Return timeout """ - return u'HTTP/1.1 408 Request Timeout\r\n' + self.socket.write(u'HTTP/1.1 408 Request Timeout\r\n') def timeout(self): """ @@ -295,8 +314,7 @@ class HttpConnection(object): """ if not self.socket: return - html = self.get_408_timeout() - self.socket.write(html) + html = self.send_408_timeout() self.close() def disconnected(self):