diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 297e2a175..6778a9a21 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -107,6 +107,16 @@ class ItemCapabilities(object): ``CanAutoStartForLive`` The capability to ignore the do not play if display blank flag. + ``HasDisplayTitle`` + The item contains 'displaytitle' on every frame which should be + preferred over 'title' when displaying the item + + ``HasNotes`` + The item contains 'notes' + + ``HasThumbnails`` + The item has related thumbnails available + """ CanPreview = 1 CanEdit = 2 @@ -124,6 +134,9 @@ class ItemCapabilities(object): CanWordSplit = 14 HasBackgroundAudio = 15 CanAutoStartForLive = 16 + HasDisplayTitle = 17 + HasNotes = 18 + HasThumbnails = 19 class ServiceItem(object): @@ -303,7 +316,7 @@ class ServiceItem(object): self._raw_frames.append({'title': title, 'raw_slide': raw_slide, 'verseTag': verse_tag}) self._new_item() - def add_from_command(self, path, file_name, image): + def add_from_command(self, path, file_name, image, displaytitle=None, notes=None): """ Add a slide from a command. @@ -317,27 +330,8 @@ class ServiceItem(object): The command of/for the slide. """ self.service_item_type = ServiceItemType.Command - self._raw_frames.append({'title': file_name, 'image': image, 'path': path}) - self._new_item() - - def add_from_presentation(self, path, file_name, image, displaytitle, notes): - """ - Add a slide from a presentation. - - ``path`` - The path of the presentation - - ``file_name`` - The filename of the presentation - - ``image`` - Full path (including file name) to the thumbnail - - ``displaytitle`` - The title to display on the list and remote - """ - self.service_item_type = ServiceItemType.Command - self._raw_frames.append({'title': file_name, 'image': image, 'path': path, 'displaytitle': displaytitle, 'notes': notes}) + self._raw_frames.append({'title': file_name, 'image': image, 'path': path, + 'displaytitle': displaytitle, 'notes': notes}) self._new_item() def get_service_repr(self, lite_save): @@ -382,11 +376,8 @@ class ServiceItem(object): service_data = [slide['title'] for slide in self._raw_frames] elif self.service_item_type == ServiceItemType.Command: for slide in self._raw_frames: - #if len(slide['displaytitle'])>0: service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path'], 'displaytitle': slide['displaytitle'], 'notes': slide['notes']}) - #else: - # service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path']}) return {'header': service_header, 'data': service_data} def set_from_service(self, serviceitem, path=None): @@ -458,10 +449,8 @@ class ServiceItem(object): self.title = text_image['title'] if path: self.has_original_files = False - if serviceitem['serviceitem']['header']['plugin']=='presentations': - self.add_from_presentation(path, text_image['title'], text_image['image'], text_image['displaytitle'], text_image['notes']) - else: - self.add_from_command(path, text_image['title'], text_image['image']) + self.add_from_command(path, text_image['title'], text_image['image'], + text_image['displaytitle'], text_image['notes']) else: self.add_from_command(text_image['path'], text_image['title'], text_image['image']) self._new_item() diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index c00f4159b..b5cdb9471 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1185,7 +1185,14 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): # Add the children to their parent treewidgetitem. for count, frame in enumerate(serviceitem.get_frames()): child = QtGui.QTreeWidgetItem(treewidgetitem) - text = frame['title'].replace('\n', ' ') + # prefer to use a displaytitle + if serviceitem.is_capable(ItemCapabilities.HasDisplayTitle): + text = frame['displaytitle'].replace('\n',' ') + # oops, it is missing, let's make one up + if len(text.strip()) == 0: + text = '[slide ' + str(count+1) + ']' + else: + text = frame['title'].replace('\n', ' ') child.setText(0, text[:40]) child.setData(0, QtCore.Qt.UserRole, count) if service_item == item_count: diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index fc8c72967..ff36734d7 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -466,6 +466,7 @@ class ImpressDocument(PresentationDocument): shape = page.getByIndex(index) shapeType = shape.getShapetype() if shape.supportsService("com.sun.star.drawing.Text"): + # if they requested title, make sure it is the title if text_type!=TextType.Title or shapeType == "com.sun.star.presentation.TitleTextShape": text += shape.getString() + '\n' return text @@ -492,8 +493,3 @@ class ImpressDocument(PresentationDocument): fo.writelines(titles) return - def get_titles_and_notes(self): - """ - let the super class handle it - """ - return super().get_titles_and_notes() diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 5479d33ad..bd5c98245 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -250,6 +250,7 @@ class PresentationMediaItem(MediaManagerItem): return False service_item.processor = self.display_type_combo_box.currentText() service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay) + service_item.add_capability(ItemCapabilities.HasThumbnails) if not self.display_type_combo_box.currentText(): return False for bitem in items: @@ -264,6 +265,10 @@ class PresentationMediaItem(MediaManagerItem): controller = self.controllers[service_item.processor] doc = controller.add_document(filename) titles, notes = doc.get_titles_and_notes() + if len(titles) > 0: + service_item.add_capability(ItemCapabilities.HasDisplayTitle) + if len(notes) > 0: + service_item.add_capability(ItemCapabilities.HasNotes) if doc.get_thumbnail_path(1, True) is None: doc.load_presentation() i = 1 @@ -276,7 +281,7 @@ class PresentationMediaItem(MediaManagerItem): note = '' if i <= len(notes): note = notes[i-1] - service_item.add_from_presentation(path, name, img, title, note) + service_item.add_from_command(path, name, img, title, note) i += 1 img = doc.get_thumbnail_path(i, True) doc.close_presentation() diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 67d8baef5..359c482b9 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -344,12 +344,6 @@ class PowerpointDocument(PresentationDocument): fo.writelines(titles) return - def get_titles_and_notes(self): - """ - let the super class handle it - """ - return super().get_titles_and_notes() - def _get_text_from_shapes(shapes): """ Returns any text extracted from the shapes on a presentation slide. diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 4208a52dc..8829582ed 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -179,12 +179,14 @@ class PptviewDocument(PresentationDocument): index = -1 listToAdd = None + # check if it is a slide match = re.search("slides/slide(.+)\.xml", zip_info.filename) if match: index = int(match.group(1))-1 nodeType = 'ctrTitle' listToAdd = titles + # or a note match = re.search("notesSlides/notesSlide(.+)\.xml", zip_info.filename) if match: index = int(match.group(1))-1 @@ -197,7 +199,9 @@ class PptviewDocument(PresentationDocument): tree = ElementTree.parse(zipped_file) text = '' - nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t", namespaces=namespaces) + nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t", + namespaces=namespaces) + # if we found any content if nodes and len(nodes)>0: for node in nodes: if len(text) > 0: @@ -205,7 +209,7 @@ class PptviewDocument(PresentationDocument): text += node.text print( 'slide file: ' + zip_info.filename + ' ' + text ) - # let's remove the nl from the titles and just add one at the end + # let's remove the \n from the titles and just add one at the end if nodeType == 'ctrTitle': text = text.replace('\n',' ').replace('\x0b', ' ') + '\n' listToAdd[index] = text @@ -319,9 +323,3 @@ class PptviewDocument(PresentationDocument): Triggers the previous slide on the running presentation. """ self.controller.process.PrevStep(self.ppt_id) - - def get_titles_and_notes(self): - """ - let the super class handle it - """ - return super().get_titles_and_notes() diff --git a/openlp/plugins/presentations/lib/pptviewlib/ppttest.py b/openlp/plugins/presentations/lib/pptviewlib/ppttest.py index 1477e62d7..1e20146d3 100644 --- a/openlp/plugins/presentations/lib/pptviewlib/ppttest.py +++ b/openlp/plugins/presentations/lib/pptviewlib/ppttest.py @@ -209,7 +209,8 @@ class PPTViewer(QtGui.QWidget): tree = ElementTree.parse(zipped_file) text = '' - nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t", namespaces=namespaces) + nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t", + namespaces=namespaces) if nodes and len(nodes)>0: for node in nodes: if len(text) > 0: diff --git a/openlp/plugins/remotes/html/openlp.js b/openlp/plugins/remotes/html/openlp.js index dd9e7a98b..3ca8806f2 100644 --- a/openlp/plugins/remotes/html/openlp.js +++ b/openlp/plugins/remotes/html/openlp.js @@ -87,13 +87,21 @@ window.OpenLP = { var ul = $("#slide-controller > div[data-role=content] > ul[data-role=listview]"); ul.html(""); for (idx in data.results.slides) { - var text = data.results.slides[idx]["tag"]; + var slide = data.results.slides[idx]; + var text = slide["tag"]; if (text != "") text = text + ": "; - text = text + data.results.slides[idx]["text"]; + if (slide["title"]) + text += slide["title"] + else + text += slide["text"]; + if (slide["notes"]) + text += ("
" + slide["notes"] + "
"); text = text.replace(/\n/g, '
'); + if (slide["img"]) + text += ""; var li = $("
  • ").append( $("").attr("value", parseInt(idx, 10)).html(text)); - if (data.results.slides[idx]["selected"]) { + if (slide["selected"]) { li.attr("data-theme", "e"); } li.children("a").click(OpenLP.setSlide); diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index cb0310a85..2fc0647db 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -125,7 +125,7 @@ from urllib.parse import urlparse, parse_qs from mako.template import Template from PyQt4 import QtCore -from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte +from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte, resize_image, ItemCapabilities from openlp.core.utils import AppLocation, translate log = logging.getLogger(__name__) @@ -151,6 +151,7 @@ class HttpRouter(object): ('^/(stage)$', {'function': self.serve_file, 'secure': False}), ('^/(main)$', {'function': self.serve_file, 'secure': False}), (r'^/files/(.*)$', {'function': self.serve_file, 'secure': False}), + (r'^/(.*)/thumbnails/(.*)$', {'function': self.serve_thumbnail, 'secure': False}), (r'^/api/poll$', {'function': self.poll, 'secure': False}), (r'^/main/poll$', {'function': self.main_poll, 'secure': False}), (r'^/main/image$', {'function': self.main_image, 'secure': False}), @@ -347,26 +348,10 @@ class HttpRouter(object): path = os.path.normpath(os.path.join(self.html_dir, file_name)) if not path.startswith(self.html_dir): return self.do_not_found() - ext = os.path.splitext(file_name)[1] html = None - if ext == '.html': - self.send_header('Content-type', 'text/html') + if self.send_appropriate_header(file_name) == '.html': variables = self.template_vars html = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables) - elif ext == '.css': - self.send_header('Content-type', 'text/css') - elif ext == '.js': - self.send_header('Content-type', 'application/javascript') - elif ext == '.jpg': - self.send_header('Content-type', 'image/jpeg') - elif ext == '.gif': - self.send_header('Content-type', 'image/gif') - elif ext == '.ico': - self.send_header('Content-type', 'image/x-icon') - elif ext == '.png': - self.send_header('Content-type', 'image/png') - else: - self.send_header('Content-type', 'text/plain') file_handle = None try: if html: @@ -383,6 +368,44 @@ class HttpRouter(object): file_handle.close() return content + def send_appropriate_header(self, file_name): + ext = os.path.splitext(file_name)[1] + if ext == '.html': + self.send_header('Content-type', 'text/html') + elif ext == '.css': + self.send_header('Content-type', 'text/css') + elif ext == '.js': + self.send_header('Content-type', 'application/javascript') + elif ext == '.jpg': + self.send_header('Content-type', 'image/jpeg') + elif ext == '.gif': + self.send_header('Content-type', 'image/gif') + elif ext == '.ico': + self.send_header('Content-type', 'image/x-icon') + elif ext == '.png': + self.send_header('Content-type', 'image/png') + else: + self.send_header('Content-type', 'text/plain') + return ext + + def serve_thumbnail(self, controller_name=None, file_name=None): + """ + Serve an image file. If not found return 404. + """ + log.debug('serve thumbnail %s/thumbnails/%s' % (controller_name, file_name)) + content = '' + full_path = os.path.join(AppLocation.get_section_data_path(controller_name), + 'thumbnails/' + file_name.replace('/','\\') ) + full_path = urllib.parse.unquote(full_path) + + if os.path.exists(full_path): + self.send_appropriate_header(full_path) + file_handle = open(full_path, 'rb') + content = file_handle.read() + else: + content = self.do_not_found() + return content + def poll(self): """ Poll OpenLP to determine the current slide number and item name. @@ -470,12 +493,20 @@ class HttpRouter(object): item['html'] = str(frame['html']) else: item['tag'] = str(index + 1) - if current_item.name == 'presentations': - item['text'] = str(frame['displaytitle']) + '\n' + str(frame['notes']) - else: - item['text'] = str(frame['title']) + if current_item.is_capable(ItemCapabilities.HasDisplayTitle): + item['title'] = str(frame['displaytitle']) + if current_item.is_capable(ItemCapabilities.HasNotes): + item['notes'] = str(frame['notes']) + if current_item.is_capable(ItemCapabilities.HasThumbnails): + # if the file is under our app directory tree send the portion after the match + if frame['image'][0:len(AppLocation.get_data_path())] == AppLocation.get_data_path(): + item['img'] = frame['image'][len(AppLocation.get_data_path()):] + #'data:image/png;base64,' + str(image_to_byte(resize_image(frame['image'],80,80))) + item['text'] = str(frame['title']) item['html'] = str(frame['title']) item['selected'] = (self.live_controller.selected_row == index) + if current_item.notes: + item['notes'] = item.get('notes','') + '\n' + current_item.notes data.append(item) json_data = {'results': {'slides': data}} if current_item: