~ubuntu-branches/debian/sid/openlp/sid

« back to all changes in this revision

Viewing changes to openlp/core/lib/serviceitem.py

  • Committer: Package Import Robot
  • Author(s): Raoul Snyman
  • Date: 2012-05-09 18:40:30 UTC
  • Revision ID: package-import@ubuntu.com-20120509184030-pqz3jwr61635nld9
Tags: upstream-1.9.9
ImportĀ upstreamĀ versionĀ 1.9.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
 
3
 
 
4
###############################################################################
 
5
# OpenLP - Open Source Lyrics Projection                                      #
 
6
# --------------------------------------------------------------------------- #
 
7
# Copyright (c) 2008-2012 Raoul Snyman                                        #
 
8
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan      #
 
9
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan,      #
 
10
# Armin Kƶhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias     #
 
11
# PƵldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,    #
 
12
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             #
 
13
# --------------------------------------------------------------------------- #
 
14
# This program is free software; you can redistribute it and/or modify it     #
 
15
# under the terms of the GNU General Public License as published by the Free  #
 
16
# Software Foundation; version 2 of the License.                              #
 
17
#                                                                             #
 
18
# This program is distributed in the hope that it will be useful, but WITHOUT #
 
19
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
 
20
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
 
21
# more details.                                                               #
 
22
#                                                                             #
 
23
# You should have received a copy of the GNU General Public License along     #
 
24
# with this program; if not, write to the Free Software Foundation, Inc., 59  #
 
25
# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 
26
###############################################################################
 
27
"""
 
28
The :mod:`serviceitem` provides the service item functionality including the
 
29
type and capability of an item.
 
30
"""
 
31
 
 
32
import cgi
 
33
import datetime
 
34
import logging
 
35
import os
 
36
import uuid
 
37
 
 
38
from openlp.core.lib import build_icon, clean_tags, expand_tags, translate
 
39
 
 
40
log = logging.getLogger(__name__)
 
41
 
 
42
class ServiceItemType(object):
 
43
    """
 
44
    Defines the type of service item
 
45
    """
 
46
    Text = 1
 
47
    Image = 2
 
48
    Command = 3
 
49
 
 
50
 
 
51
class ItemCapabilities(object):
 
52
    """
 
53
    Provides an enumeration of a serviceitem's capabilities
 
54
    """
 
55
    CanPreview = 1
 
56
    CanEdit = 2
 
57
    CanMaintain = 3
 
58
    RequiresMedia = 4
 
59
    CanLoop = 5
 
60
    CanAppend = 6
 
61
    NoLineBreaks = 7
 
62
    OnLoadUpdate = 8
 
63
    AddIfNewItem = 9
 
64
    ProvidesOwnDisplay = 10
 
65
    HasDetailedTitleDisplay = 11
 
66
    HasVariableStartTime = 12
 
67
    CanSoftBreak = 13
 
68
    CanWordSplit = 14
 
69
    HasBackgroundAudio = 15
 
70
 
 
71
 
 
72
class ServiceItem(object):
 
73
    """
 
74
    The service item is a base class for the plugins to use to interact with
 
75
    the service manager, the slide controller, and the projection screen
 
76
    compositor.
 
77
    """
 
78
    log.info(u'Service Item created')
 
79
 
 
80
    def __init__(self, plugin=None):
 
81
        """
 
82
        Set up the service item.
 
83
 
 
84
        ``plugin``
 
85
            The plugin that this service item belongs to.
 
86
        """
 
87
        if plugin:
 
88
            self.renderer = plugin.renderer
 
89
            self.name = plugin.name
 
90
        self.title = u''
 
91
        self.shortname = u''
 
92
        self.audit = u''
 
93
        self.items = []
 
94
        self.iconic_representation = None
 
95
        self.raw_footer = []
 
96
        self.foot_text = u''
 
97
        self.theme = None
 
98
        self.service_item_type = None
 
99
        self._raw_frames = []
 
100
        self._display_frames = []
 
101
        self._uuid = 0
 
102
        self.notes = u''
 
103
        self.from_plugin = False
 
104
        self.capabilities = []
 
105
        self.is_valid = True
 
106
        self.icon = None
 
107
        self.themedata = None
 
108
        self.main = None
 
109
        self.footer = None
 
110
        self.bg_image_bytes = None
 
111
        self.search_string = u''
 
112
        self.data_string = u''
 
113
        self.edit_id = None
 
114
        self.xml_version = None
 
115
        self.start_time = 0
 
116
        self.end_time = 0
 
117
        self.media_length = 0
 
118
        self.from_service = False
 
119
        self.image_border = u'#000000'
 
120
        self.background_audio = []
 
121
        self.theme_overwritten = False
 
122
        self.temporary_edit = False
 
123
        self._new_item()
 
124
 
 
125
    def _new_item(self):
 
126
        """
 
127
        Method to set the internal id of the item. This is used to compare
 
128
        service items to see if they are the same.
 
129
        """
 
130
        self._uuid = unicode(uuid.uuid1())
 
131
 
 
132
    def add_capability(self, capability):
 
133
        """
 
134
        Add an ItemCapability to a ServiceItem
 
135
 
 
136
        ``capability``
 
137
            The capability to add
 
138
        """
 
139
        self.capabilities.append(capability)
 
140
 
 
141
    def is_capable(self, capability):
 
142
        """
 
143
        Tell the caller if a ServiceItem has a capability
 
144
 
 
145
        ``capability``
 
146
            The capability to test for
 
147
        """
 
148
        return capability in self.capabilities
 
149
 
 
150
    def add_icon(self, icon):
 
151
        """
 
152
        Add an icon to the service item. This is used when displaying the
 
153
        service item in the service manager.
 
154
 
 
155
        ``icon``
 
156
            A string to an icon in the resources or on disk.
 
157
        """
 
158
        self.icon = icon
 
159
        self.iconic_representation = build_icon(icon)
 
160
 
 
161
    def render(self, use_override=False):
 
162
        """
 
163
        The render method is what generates the frames for the screen and
 
164
        obtains the display information from the renderer. At this point all
 
165
        slides are built for the given display size.
 
166
        """
 
167
        log.debug(u'Render called')
 
168
        self._display_frames = []
 
169
        self.bg_image_bytes = None
 
170
        theme = self.theme if self.theme else None
 
171
        self.main, self.footer = \
 
172
            self.renderer.set_override_theme(theme, use_override)
 
173
        self.themedata = self.renderer.theme_data
 
174
        if self.service_item_type == ServiceItemType.Text:
 
175
            log.debug(u'Formatting slides')
 
176
            for slide in self._raw_frames:
 
177
                pages = self.renderer.format_slide(slide[u'raw_slide'], self)
 
178
                for page in pages:
 
179
                    page = page.replace(u'<br>', u'{br}')
 
180
                    html = expand_tags(cgi.escape(page.rstrip()))
 
181
                    self._display_frames.append({
 
182
                        u'title': clean_tags(page),
 
183
                        u'text': clean_tags(page.rstrip()),
 
184
                        u'html': html.replace(u'&amp;nbsp;', u'&nbsp;'),
 
185
                        u'verseTag': slide[u'verseTag']
 
186
                    })
 
187
        elif self.service_item_type == ServiceItemType.Image or \
 
188
            self.service_item_type == ServiceItemType.Command:
 
189
            pass
 
190
        else:
 
191
            log.error(u'Invalid value renderer: %s' % self.service_item_type)
 
192
        self.title = clean_tags(self.title)
 
193
        # The footer should never be None, but to be compatible with a few
 
194
        # nightly builds between 1.9.4 and 1.9.5, we have to correct this to
 
195
        # avoid tracebacks.
 
196
        if self.raw_footer is None:
 
197
            self.raw_footer = []
 
198
        self.foot_text = \
 
199
            u'<br>'.join([footer for footer in self.raw_footer if footer])
 
200
 
 
201
    def add_from_image(self, path, title, background=None):
 
202
        """
 
203
        Add an image slide to the service item.
 
204
 
 
205
        ``path``
 
206
            The directory in which the image file is located.
 
207
 
 
208
        ``title``
 
209
            A title for the slide in the service item.
 
210
        """
 
211
        if background:
 
212
            self.image_border = background
 
213
        self.service_item_type = ServiceItemType.Image
 
214
        self._raw_frames.append({u'title': title, u'path': path})
 
215
        self.renderer.imageManager.add_image(title, path, u'image',
 
216
            self.image_border)
 
217
        self._new_item()
 
218
 
 
219
    def add_from_text(self, title, raw_slide, verse_tag=None):
 
220
        """
 
221
        Add a text slide to the service item.
 
222
 
 
223
        ``frame_title``
 
224
            The title of the slide in the service item.
 
225
 
 
226
        ``raw_slide``
 
227
            The raw text of the slide.
 
228
        """
 
229
        if verse_tag:
 
230
            verse_tag = verse_tag.upper()
 
231
        self.service_item_type = ServiceItemType.Text
 
232
        title = title.split(u'\n')[0]
 
233
        self._raw_frames.append(
 
234
            {u'title': title, u'raw_slide': raw_slide, u'verseTag': verse_tag})
 
235
        self._new_item()
 
236
 
 
237
    def add_from_command(self, path, file_name, image):
 
238
        """
 
239
        Add a slide from a command.
 
240
 
 
241
        ``path``
 
242
            The title of the slide in the service item.
 
243
 
 
244
        ``file_name``
 
245
            The title of the slide in the service item.
 
246
 
 
247
        ``image``
 
248
            The command of/for the slide.
 
249
        """
 
250
        self.service_item_type = ServiceItemType.Command
 
251
        self._raw_frames.append(
 
252
            {u'title': file_name, u'image': image, u'path': path})
 
253
        self._new_item()
 
254
 
 
255
    def get_service_repr(self):
 
256
        """
 
257
        This method returns some text which can be saved into the service
 
258
        file to represent this item.
 
259
        """
 
260
        service_header = {
 
261
            u'name': self.name,
 
262
            u'plugin': self.name,
 
263
            u'theme': self.theme,
 
264
            u'title': self.title,
 
265
            u'icon': self.icon,
 
266
            u'footer': self.raw_footer,
 
267
            u'type': self.service_item_type,
 
268
            u'audit': self.audit,
 
269
            u'notes': self.notes,
 
270
            u'from_plugin': self.from_plugin,
 
271
            u'capabilities': self.capabilities,
 
272
            u'search': self.search_string,
 
273
            u'data': self.data_string,
 
274
            u'xml_version': self.xml_version,
 
275
            u'start_time': self.start_time,
 
276
            u'end_time': self.end_time,
 
277
            u'media_length': self.media_length,
 
278
            u'background_audio': self.background_audio,
 
279
            u'theme_overwritten': self.theme_overwritten
 
280
        }
 
281
        service_data = []
 
282
        if self.service_item_type == ServiceItemType.Text:
 
283
            service_data = [slide for slide in self._raw_frames]
 
284
        elif self.service_item_type == ServiceItemType.Image:
 
285
            service_data = [slide[u'title'] for slide in self._raw_frames]
 
286
        elif self.service_item_type == ServiceItemType.Command:
 
287
            for slide in self._raw_frames:
 
288
                service_data.append(
 
289
                    {u'title': slide[u'title'], u'image': slide[u'image']})
 
290
        return {u'header': service_header, u'data': service_data}
 
291
 
 
292
    def set_from_service(self, serviceitem, path=None):
 
293
        """
 
294
        This method takes a service item from a saved service file (passed
 
295
        from the ServiceManager) and extracts the data actually required.
 
296
 
 
297
        ``serviceitem``
 
298
            The item to extract data from.
 
299
 
 
300
        ``path``
 
301
            Defaults to *None*. Any path data, usually for images.
 
302
        """
 
303
        log.debug(u'set_from_service called with path %s' % path)
 
304
        header = serviceitem[u'serviceitem'][u'header']
 
305
        self.title = header[u'title']
 
306
        self.name = header[u'name']
 
307
        self.service_item_type = header[u'type']
 
308
        self.shortname = header[u'plugin']
 
309
        self.theme = header[u'theme']
 
310
        self.add_icon(header[u'icon'])
 
311
        self.raw_footer = header[u'footer']
 
312
        self.audit = header[u'audit']
 
313
        self.notes = header[u'notes']
 
314
        self.from_plugin = header[u'from_plugin']
 
315
        self.capabilities = header[u'capabilities']
 
316
        # Added later so may not be present in older services.
 
317
        if u'search' in header:
 
318
            self.search_string = header[u'search']
 
319
            self.data_string = header[u'data']
 
320
        if u'xml_version' in header:
 
321
            self.xml_version = header[u'xml_version']
 
322
        if u'start_time' in header:
 
323
            self.start_time = header[u'start_time']
 
324
        if u'end_time' in header:
 
325
            self.end_time = header[u'end_time']
 
326
        if u'media_length' in header:
 
327
            self.media_length = header[u'media_length']
 
328
        if u'background_audio' in header:
 
329
            self.background_audio = []
 
330
            for filename in header[u'background_audio']:
 
331
                # Give them real file paths
 
332
                self.background_audio.append(os.path.join(path, filename))
 
333
        self.theme_overwritten = header.get(u'theme_overwritten', False)
 
334
        if self.service_item_type == ServiceItemType.Text:
 
335
            for slide in serviceitem[u'serviceitem'][u'data']:
 
336
                self._raw_frames.append(slide)
 
337
        elif self.service_item_type == ServiceItemType.Image:
 
338
            for text_image in serviceitem[u'serviceitem'][u'data']:
 
339
                filename = os.path.join(path, text_image)
 
340
                self.add_from_image(filename, text_image)
 
341
        elif self.service_item_type == ServiceItemType.Command:
 
342
            for text_image in serviceitem[u'serviceitem'][u'data']:
 
343
                filename = os.path.join(path, text_image[u'title'])
 
344
                self.add_from_command(
 
345
                    path, text_image[u'title'], text_image[u'image'])
 
346
        self._new_item()
 
347
 
 
348
    def get_display_title(self):
 
349
        """
 
350
        Returns the title of the service item.
 
351
        """
 
352
        if self.is_text():
 
353
            return self.title
 
354
        else:
 
355
            if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities:
 
356
                return self._raw_frames[0][u'title']
 
357
            elif len(self._raw_frames) > 1:
 
358
                return self.title
 
359
            else:
 
360
                return self._raw_frames[0][u'title']
 
361
 
 
362
    def merge(self, other):
 
363
        """
 
364
        Updates the _uuid with the value from the original one
 
365
        The _uuid is unique for a given service item but this allows one to
 
366
        replace an original version.
 
367
 
 
368
        ``other``
 
369
            The service item to be merged with
 
370
        """
 
371
        self._uuid = other._uuid
 
372
        self.notes = other.notes
 
373
        self.temporary_edit = other.temporary_edit
 
374
        # Copy theme over if present.
 
375
        if other.theme is not None:
 
376
            self.theme = other.theme
 
377
            self._new_item()
 
378
        self.render()
 
379
        if self.is_capable(ItemCapabilities.HasBackgroundAudio):
 
380
            log.debug(self.background_audio)
 
381
 
 
382
    def __eq__(self, other):
 
383
        """
 
384
        Confirms the service items are for the same instance
 
385
        """
 
386
        if not other:
 
387
            return False
 
388
        return self._uuid == other._uuid
 
389
 
 
390
    def __ne__(self, other):
 
391
        """
 
392
        Confirms the service items are not for the same instance
 
393
        """
 
394
        return self._uuid != other._uuid
 
395
 
 
396
    def is_media(self):
 
397
        """
 
398
        Confirms if the ServiceItem is media
 
399
        """
 
400
        return ItemCapabilities.RequiresMedia in self.capabilities
 
401
 
 
402
    def is_command(self):
 
403
        """
 
404
        Confirms if the ServiceItem is a command
 
405
        """
 
406
        return self.service_item_type == ServiceItemType.Command
 
407
 
 
408
    def is_image(self):
 
409
        """
 
410
        Confirms if the ServiceItem is an image
 
411
        """
 
412
        return self.service_item_type == ServiceItemType.Image
 
413
 
 
414
    def uses_file(self):
 
415
        """
 
416
        Confirms if the ServiceItem uses a file
 
417
        """
 
418
        return self.service_item_type == ServiceItemType.Image or \
 
419
            self.service_item_type == ServiceItemType.Command
 
420
 
 
421
    def is_text(self):
 
422
        """
 
423
        Confirms if the ServiceItem is text
 
424
        """
 
425
        return self.service_item_type == ServiceItemType.Text
 
426
 
 
427
    def get_frames(self):
 
428
        """
 
429
        Returns the frames for the ServiceItem
 
430
        """
 
431
        if self.service_item_type == ServiceItemType.Text:
 
432
            return self._display_frames
 
433
        else:
 
434
            return self._raw_frames
 
435
 
 
436
    def get_rendered_frame(self, row):
 
437
        """
 
438
        Returns the correct frame for a given list and
 
439
        renders it if required.
 
440
        """
 
441
        if self.service_item_type == ServiceItemType.Text:
 
442
            return self._display_frames[row][u'html'].split(u'\n')[0]
 
443
        elif self.service_item_type == ServiceItemType.Image:
 
444
            return self._raw_frames[row][u'title']
 
445
        else:
 
446
            return self._raw_frames[row][u'image']
 
447
 
 
448
    def get_frame_title(self, row=0):
 
449
        """
 
450
        Returns the title of the raw frame
 
451
        """
 
452
        try:
 
453
            return self._raw_frames[row][u'title']
 
454
        except IndexError:
 
455
            return u''
 
456
 
 
457
    def get_frame_path(self, row=0):
 
458
        """
 
459
        Returns the path of the raw frame
 
460
        """
 
461
        try:
 
462
            return self._raw_frames[row][u'path']
 
463
        except IndexError:
 
464
            return u''
 
465
 
 
466
    def get_media_time(self):
 
467
        """
 
468
        Returns the start and finish time for a media item
 
469
        """
 
470
        start = None
 
471
        end = None
 
472
        if self.start_time != 0:
 
473
            start = unicode(translate('OpenLP.ServiceItem',
 
474
                '<strong>Start</strong>: %s')) % \
 
475
                unicode(datetime.timedelta(seconds=self.start_time))
 
476
        if self.media_length != 0:
 
477
            end = unicode(translate('OpenLP.ServiceItem',
 
478
                '<strong>Length</strong>: %s')) % \
 
479
                unicode(datetime.timedelta(seconds=self.media_length))
 
480
        if not start and not end:
 
481
            return u''
 
482
        elif start and not end:
 
483
            return start
 
484
        elif not start and end:
 
485
            return end
 
486
        else:
 
487
            return u'%s <br>%s' % (start, end)
 
488
 
 
489
    def update_theme(self, theme):
 
490
        """
 
491
        updates the theme in the service item
 
492
 
 
493
        ``theme``
 
494
            The new theme to be replaced in the service item
 
495
        """
 
496
        self.theme_overwritten = (theme == None)
 
497
        self.theme = theme
 
498
        self._new_item()
 
499
        self.render()
 
500