~ubuntu-branches/debian/stretch/electrum/stretch

« back to all changes in this revision

Viewing changes to gui/kivy/tools/.buildozer/android/platform/python-for-android/dist/kivy/python-install/lib/python2.7/site-packages/kivy/uix/rst.py

  • Committer: Package Import Robot
  • Author(s): Tristan Seligmann
  • Date: 2016-04-04 03:02:39 UTC
  • mfrom: (1.1.10)
  • Revision ID: package-import@ubuntu.com-20160404030239-0szgkio8yryjv7c9
Tags: 2.6.3-1
* New upstream release.
  - Drop backported install-wizard-connect.patch.
* Add Suggests: python-zbar and update the installation hint to suggest
  apt-get instead of pip (closes: #819517).
* Bump Standards-Version to 3.9.7 (no changes).
* Update Vcs-* links.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
'''
 
2
reStructuredText renderer
 
3
=========================
 
4
 
 
5
.. versionadded:: 1.1.0
 
6
 
 
7
`reStructuredText <http://docutils.sourceforge.net/rst.html>`_ is an
 
8
easy-to-read, what-you-see-is-what-you-get plaintext markup syntax and parser
 
9
system.
 
10
 
 
11
.. warning::
 
12
 
 
13
    This widget is highly experimental. The whole styling and
 
14
    implementation are not stable until this warning has been removed.
 
15
 
 
16
Usage with Text
 
17
---------------
 
18
 
 
19
::
 
20
 
 
21
    text = """
 
22
    .. _top:
 
23
 
 
24
    Hello world
 
25
    ===========
 
26
 
 
27
    This is an **emphased text**, some ``interpreted text``.
 
28
    And this is a reference to top_::
 
29
 
 
30
        $ print("Hello world")
 
31
 
 
32
    """
 
33
    document = RstDocument(text=text)
 
34
 
 
35
The rendering will output:
 
36
 
 
37
.. image:: images/rstdocument.png
 
38
 
 
39
Usage with Source
 
40
-----------------
 
41
 
 
42
You can also render a rst file using the :attr:`RstDocument.source` property::
 
43
 
 
44
    document = RstDocument(source='index.rst')
 
45
 
 
46
You can reference other documents with the role ``:doc:``. For example, in the
 
47
document ``index.rst`` you can write::
 
48
 
 
49
    Go to my next document: :doc:`moreinfo.rst`
 
50
 
 
51
It will generate a link that, when clicked, opens the ``moreinfo.rst``
 
52
document.
 
53
 
 
54
'''
 
55
 
 
56
__all__ = ('RstDocument', )
 
57
 
 
58
import os
 
59
from os.path import dirname, join, exists, abspath
 
60
from kivy.clock import Clock
 
61
from kivy.compat import PY2
 
62
from kivy.properties import ObjectProperty, NumericProperty, \
 
63
    DictProperty, ListProperty, StringProperty, \
 
64
    BooleanProperty, OptionProperty, AliasProperty
 
65
from kivy.lang import Builder
 
66
from kivy.utils import get_hex_from_color, get_color_from_hex
 
67
from kivy.uix.widget import Widget
 
68
from kivy.uix.scrollview import ScrollView
 
69
from kivy.uix.gridlayout import GridLayout
 
70
from kivy.uix.label import Label
 
71
from kivy.uix.image import AsyncImage, Image
 
72
from kivy.uix.videoplayer import VideoPlayer
 
73
from kivy.uix.anchorlayout import AnchorLayout
 
74
from kivy.animation import Animation
 
75
from kivy.logger import Logger
 
76
from docutils.parsers import rst
 
77
from docutils.parsers.rst import roles
 
78
from docutils import nodes, frontend, utils
 
79
from docutils.parsers.rst import Directive, directives
 
80
from docutils.parsers.rst.roles import set_classes
 
81
from kivy.parser import parse_color
 
82
 
 
83
 
 
84
#
 
85
# Handle some additional roles
 
86
#
 
87
if 'KIVY_DOC' not in os.environ:
 
88
 
 
89
    class role_doc(nodes.Inline, nodes.TextElement):
 
90
        pass
 
91
 
 
92
    class role_video(nodes.General, nodes.TextElement):
 
93
        pass
 
94
 
 
95
    class VideoDirective(Directive):
 
96
        has_content = False
 
97
        required_arguments = 1
 
98
        optional_arguments = 0
 
99
        final_argument_whitespace = True
 
100
        option_spec = {'width': directives.nonnegative_int,
 
101
                       'height': directives.nonnegative_int}
 
102
 
 
103
        def run(self):
 
104
            set_classes(self.options)
 
105
            node = role_video(source=self.arguments[0], **self.options)
 
106
            return [node]
 
107
 
 
108
    generic_docroles = {
 
109
        'doc': role_doc}
 
110
 
 
111
    for rolename, nodeclass in generic_docroles.items():
 
112
        generic = roles.GenericRole(rolename, nodeclass)
 
113
        role = roles.CustomRole(rolename, generic, {'classes': [rolename]})
 
114
        roles.register_local_role(rolename, role)
 
115
 
 
116
    directives.register_directive('video', VideoDirective)
 
117
 
 
118
Builder.load_string('''
 
119
#:import parse_color kivy.parser.parse_color
 
120
 
 
121
 
 
122
 
 
123
<RstDocument>:
 
124
    content: content
 
125
    scatter: scatter
 
126
    do_scroll_x: False
 
127
    canvas.before:
 
128
        Color:
 
129
            rgba: parse_color(root.colors['background'])
 
130
        Rectangle:
 
131
            pos: self.pos
 
132
            size: self.size
 
133
 
 
134
    Scatter:
 
135
        id: scatter
 
136
        size_hint_y: None
 
137
        height: content.minimum_height
 
138
        width: root.width
 
139
        scale: 1
 
140
        do_translation: False, False
 
141
        do_scale: False
 
142
        do_rotation: False
 
143
 
 
144
        GridLayout:
 
145
            id: content
 
146
            cols: 1
 
147
            height: self.minimum_height
 
148
            width: root.width
 
149
            padding: 10
 
150
 
 
151
<RstTitle>:
 
152
    markup: True
 
153
    valign: 'top'
 
154
    font_size:
 
155
        sp(self.document.base_font_size - self.section * (
 
156
        self.document.base_font_size / 31.0 * 2))
 
157
    size_hint_y: None
 
158
    height: self.texture_size[1] + dp(20)
 
159
    text_size: self.width, None
 
160
    bold: True
 
161
 
 
162
    canvas:
 
163
        Color:
 
164
            rgba: parse_color(self.document.underline_color)
 
165
        Rectangle:
 
166
            pos: self.x, self.y + 5
 
167
            size: self.width, 1
 
168
 
 
169
 
 
170
<RstParagraph>:
 
171
    markup: True
 
172
    valign: 'top'
 
173
    size_hint_y: None
 
174
    height: self.texture_size[1] + self.my
 
175
    text_size: self.width - self.mx, None
 
176
    font_size: sp(self.document.base_font_size / 2.0)
 
177
 
 
178
<RstTerm>:
 
179
    size_hint: None, None
 
180
    height: label.height
 
181
    anchor_x: 'left'
 
182
    Label:
 
183
        id: label
 
184
        text: root.text
 
185
        markup: True
 
186
        valign: 'top'
 
187
        size_hint: None, None
 
188
        size: self.texture_size[0] + dp(10), self.texture_size[1] + dp(10)
 
189
        font_size: sp(root.document.base_font_size / 2.0)
 
190
 
 
191
<RstBlockQuote>:
 
192
    cols: 2
 
193
    content: content
 
194
    size_hint_y: None
 
195
    height: content.height
 
196
    Widget:
 
197
        size_hint_x: None
 
198
        width: 20
 
199
    GridLayout:
 
200
        id: content
 
201
        cols: 1
 
202
        size_hint_y: None
 
203
        height: self.minimum_height
 
204
 
 
205
<RstLiteralBlock>:
 
206
    cols: 1
 
207
    content: content
 
208
    size_hint_y: None
 
209
    height: content.texture_size[1] + dp(20)
 
210
    canvas:
 
211
        Color:
 
212
            rgb: parse_color('#cccccc')
 
213
        Rectangle:
 
214
            pos: self.x - 1, self.y - 1
 
215
            size: self.width + 2, self.height + 2
 
216
        Color:
 
217
            rgb: parse_color('#eeeeee')
 
218
        Rectangle:
 
219
            pos: self.pos
 
220
            size: self.size
 
221
    Label:
 
222
        id: content
 
223
        markup: True
 
224
        valign: 'top'
 
225
        text_size: self.width - 20, None
 
226
        font_name: 'data/fonts/RobotoMono-Regular.ttf'
 
227
        color: (0, 0, 0, 1)
 
228
 
 
229
<RstList>:
 
230
    cols: 2
 
231
    size_hint_y: None
 
232
    height: self.minimum_height
 
233
 
 
234
<RstListItem>:
 
235
    cols: 1
 
236
    size_hint_y: None
 
237
    height: self.minimum_height
 
238
 
 
239
<RstSystemMessage>:
 
240
    cols: 1
 
241
    size_hint_y: None
 
242
    height: self.minimum_height
 
243
    canvas:
 
244
        Color:
 
245
            rgba: 1, 0, 0, .3
 
246
        Rectangle:
 
247
            pos: self.pos
 
248
            size: self.size
 
249
 
 
250
<RstWarning>:
 
251
    content: content
 
252
    cols: 1
 
253
    padding: 20
 
254
    size_hint_y: None
 
255
    height: self.minimum_height
 
256
    canvas:
 
257
        Color:
 
258
            rgba: 1, 0, 0, .5
 
259
        Rectangle:
 
260
            pos: self.x + 10, self.y + 10
 
261
            size: self.width - 20, self.height - 20
 
262
    GridLayout:
 
263
        cols: 1
 
264
        id: content
 
265
        size_hint_y: None
 
266
        height: self.minimum_height
 
267
 
 
268
<RstNote>:
 
269
    content: content
 
270
    cols: 1
 
271
    padding: 20
 
272
    size_hint_y: None
 
273
    height: self.minimum_height
 
274
    canvas:
 
275
        Color:
 
276
            rgba: 0, 1, 0, .5
 
277
        Rectangle:
 
278
            pos: self.x + 10, self.y + 10
 
279
            size: self.width - 20, self.height - 20
 
280
    GridLayout:
 
281
        cols: 1
 
282
        id: content
 
283
        size_hint_y: None
 
284
        height: self.minimum_height
 
285
 
 
286
<RstImage>:
 
287
    size_hint: None, None
 
288
    size: self.texture_size[0], self.texture_size[1] + dp(10)
 
289
 
 
290
<RstAsyncImage>:
 
291
    size_hint: None, None
 
292
    size: self.texture_size[0], self.texture_size[1] + dp(10)
 
293
 
 
294
<RstDefinitionList>:
 
295
    cols: 1
 
296
    size_hint_y: None
 
297
    height: self.minimum_height
 
298
    font_size: sp(self.document.base_font_size / 2.0)
 
299
 
 
300
<RstDefinition>:
 
301
    cols: 2
 
302
    size_hint_y: None
 
303
    height: self.minimum_height
 
304
    font_size: sp(self.document.base_font_size / 2.0)
 
305
 
 
306
<RstFieldList>:
 
307
    cols: 2
 
308
    size_hint_y: None
 
309
    height: self.minimum_height
 
310
 
 
311
<RstFieldName>:
 
312
    markup: True
 
313
    valign: 'top'
 
314
    size_hint: 0.2, 1
 
315
    color: (0, 0, 0, 1)
 
316
    bold: True
 
317
    text_size: self.width-10, self.height - 10
 
318
    valign: 'top'
 
319
    font_size: sp(self.document.base_font_size / 2.0)
 
320
 
 
321
<RstFieldBody>:
 
322
    cols: 1
 
323
    size_hint_y: None
 
324
    height: self.minimum_height
 
325
 
 
326
<RstTable>:
 
327
    size_hint_y: None
 
328
    height: self.minimum_height
 
329
 
 
330
<RstEntry>:
 
331
    cols: 1
 
332
    size_hint_y: None
 
333
    height: self.minimum_height
 
334
 
 
335
    canvas:
 
336
        Color:
 
337
            rgb: .2, .2, .2
 
338
        Line:
 
339
            points: [\
 
340
            self.x,\
 
341
            self.y,\
 
342
            self.right,\
 
343
            self.y,\
 
344
            self.right,\
 
345
            self.top,\
 
346
            self.x,\
 
347
            self.top,\
 
348
            self.x,\
 
349
            self.y]
 
350
 
 
351
<RstTransition>:
 
352
    size_hint_y: None
 
353
    height: 20
 
354
    canvas:
 
355
        Color:
 
356
            rgb: .2, .2, .2
 
357
        Line:
 
358
            points: [self.x, self.center_y, self.right, self.center_y]
 
359
 
 
360
<RstListBullet>:
 
361
    markup: True
 
362
    valign: 'top'
 
363
    size_hint_x: None
 
364
    width: self.texture_size[0] + dp(10)
 
365
    text_size: None, self.height - dp(10)
 
366
    font_size: sp(self.document.base_font_size / 2.0)
 
367
 
 
368
<RstEmptySpace>:
 
369
    size_hint: 0.01, 0.01
 
370
 
 
371
<RstDefinitionSpace>:
 
372
    size_hint: None, 0.1
 
373
    width: 50
 
374
    font_size: sp(self.document.base_font_size / 2.0)
 
375
 
 
376
<RstVideoPlayer>:
 
377
    options: {'allow_stretch': True}
 
378
    canvas.before:
 
379
        Color:
 
380
            rgba: (1, 1, 1, 1)
 
381
        BorderImage:
 
382
            source: 'atlas://data/images/defaulttheme/player-background'
 
383
            pos: self.x - 25, self.y - 25
 
384
            size: self.width + 50, self.height + 50
 
385
            border: (25, 25, 25, 25)
 
386
''')
 
387
 
 
388
 
 
389
class RstVideoPlayer(VideoPlayer):
 
390
    pass
 
391
 
 
392
 
 
393
class RstDocument(ScrollView):
 
394
    '''Base widget used to store an Rst document. See module documentation for
 
395
    more information.
 
396
    '''
 
397
    source = StringProperty(None)
 
398
    '''Filename of the RST document.
 
399
 
 
400
    :attr:`source` is a :class:`~kivy.properties.StringProperty` and
 
401
    defaults to None.
 
402
    '''
 
403
 
 
404
    source_encoding = StringProperty('utf-8')
 
405
    '''Encoding to be used for the :attr:`source` file.
 
406
 
 
407
    :attr:`source_encoding` is a :class:`~kivy.properties.StringProperty` and
 
408
    defaults to `utf-8`.
 
409
 
 
410
    .. Note::
 
411
        It is your responsibility to ensure that the value provided is a
 
412
        valid codec supported by python.
 
413
    '''
 
414
 
 
415
    source_error = OptionProperty('strict',
 
416
                                  options=('strict', 'ignore', 'replace',
 
417
                                           'xmlcharrefreplace',
 
418
                                           'backslashreplac'))
 
419
    '''Error handling to be used while encoding the :attr:`source` file.
 
420
 
 
421
    :attr:`source_error` is an :class:`~kivy.properties.OptionProperty` and
 
422
    defaults to `strict`. Can be one of 'strict', 'ignore', 'replace',
 
423
    'xmlcharrefreplace' or 'backslashreplac'.
 
424
    '''
 
425
 
 
426
    text = StringProperty(None)
 
427
    '''RST markup text of the document.
 
428
 
 
429
    :attr:`text` is a :class:`~kivy.properties.StringProperty` and defaults to
 
430
    None.
 
431
    '''
 
432
 
 
433
    document_root = StringProperty(None)
 
434
    '''Root path where :doc: will search for rst documents. If no path is
 
435
    given, it will use the directory of the first loaded source file.
 
436
 
 
437
    :attr:`document_root` is a :class:`~kivy.properties.StringProperty` and
 
438
    defaults to None.
 
439
    '''
 
440
 
 
441
    base_font_size = NumericProperty(31)
 
442
    '''Font size for the biggest title, 31 by default. All other font sizes are
 
443
    derived from this.
 
444
 
 
445
    .. versionadded:: 1.8.0
 
446
    '''
 
447
 
 
448
    show_errors = BooleanProperty(False)
 
449
    '''Indicate whether RST parsers errors should be shown on the screen
 
450
    or not.
 
451
 
 
452
    :attr:`show_errors` is a :class:`~kivy.properties.BooleanProperty` and
 
453
    defaults to False.
 
454
    '''
 
455
 
 
456
    def _get_bgc(self):
 
457
        return get_color_from_hex(self.colors.background)
 
458
 
 
459
    def _set_bgc(self, value):
 
460
        self.colors.background = get_hex_from_color(value)[1:]
 
461
 
 
462
    background_color = AliasProperty(_get_bgc, _set_bgc, bind=('colors',))
 
463
    '''Specifies the background_color to be used for the RstDocument.
 
464
 
 
465
    .. versionadded:: 1.8.0
 
466
 
 
467
    :attr:`background_color` is an :class:`~kivy.properties.AliasProperty`
 
468
    for colors['background'].
 
469
    '''
 
470
 
 
471
    colors = DictProperty({
 
472
        'background': 'e5e6e9ff',
 
473
        'link': 'ce5c00ff',
 
474
        'paragraph': '202020ff',
 
475
        'title': '204a87ff',
 
476
        'bullet': '000000ff'})
 
477
    '''Dictionary of all the colors used in the RST rendering.
 
478
 
 
479
    .. warning::
 
480
 
 
481
        This dictionary is needs special handling. You also need to call
 
482
        :meth:`RstDocument.render` if you change them after loading.
 
483
 
 
484
    :attr:`colors` is a :class:`~kivy.properties.DictProperty`.
 
485
    '''
 
486
 
 
487
    title = StringProperty('')
 
488
    '''Title of the current document.
 
489
 
 
490
    :attr:`title` is a :class:`~kivy.properties.StringProperty` and defaults to
 
491
    ''. It is read-only.
 
492
    '''
 
493
 
 
494
    toctrees = DictProperty({})
 
495
    '''Toctree of all loaded or preloaded documents. This dictionary is filled
 
496
    when a rst document is explicitly loaded or where :meth:`preload` has been
 
497
    called.
 
498
 
 
499
    If the document has no filename, e.g. when the document is loaded from a
 
500
    text file, the key will be ''.
 
501
 
 
502
    :attr:`toctrees` is a :class:`~kivy.properties.DictProperty` and defaults
 
503
    to {}.
 
504
    '''
 
505
 
 
506
    underline_color = StringProperty('204a9699')
 
507
    '''underline color of the titles, expressed in html color notation
 
508
 
 
509
    :attr:`underline_color` is a
 
510
    :class:`~kivy.properties.StringProperty` and defaults to '204a9699'.
 
511
 
 
512
    .. versionadded: 1.9.0
 
513
    '''
 
514
 
 
515
    # internals.
 
516
    content = ObjectProperty(None)
 
517
    scatter = ObjectProperty(None)
 
518
    anchors_widgets = ListProperty([])
 
519
    refs_assoc = DictProperty({})
 
520
 
 
521
    def __init__(self, **kwargs):
 
522
        self._trigger_load = Clock.create_trigger(self._load_from_text, -1)
 
523
        self._parser = rst.Parser()
 
524
        self._settings = frontend.OptionParser(
 
525
            components=(rst.Parser, )).get_default_values()
 
526
        super(RstDocument, self).__init__(**kwargs)
 
527
 
 
528
    def on_source(self, instance, value):
 
529
        if not value:
 
530
            return
 
531
        if self.document_root is None:
 
532
            # set the documentation root to the directory name of the
 
533
            # first tile
 
534
            self.document_root = abspath(dirname(value))
 
535
        self._load_from_source()
 
536
 
 
537
    def on_text(self, instance, value):
 
538
        self._trigger_load()
 
539
 
 
540
    def render(self):
 
541
        '''Force document rendering.
 
542
        '''
 
543
        self._load_from_text()
 
544
 
 
545
    def resolve_path(self, filename):
 
546
        '''Get the path for this filename. If the filename doesn't exist,
 
547
        it returns the document_root + filename.
 
548
        '''
 
549
        if exists(filename):
 
550
            return filename
 
551
        return join(self.document_root, filename)
 
552
 
 
553
    def preload(self, filename, encoding='utf-8', errors='strict'):
 
554
        '''Preload a rst file to get its toctree and its title.
 
555
 
 
556
        The result will be stored in :attr:`toctrees` with the ``filename`` as
 
557
        key.
 
558
        '''
 
559
 
 
560
        with open(filename, 'rb') as fd:
 
561
            text = fd.read().decode(encoding, errors)
 
562
        # parse the source
 
563
        document = utils.new_document('Document', self._settings)
 
564
        self._parser.parse(text, document)
 
565
        # fill the current document node
 
566
        visitor = _ToctreeVisitor(document)
 
567
        document.walkabout(visitor)
 
568
        self.toctrees[filename] = visitor.toctree
 
569
        return text
 
570
 
 
571
    def _load_from_source(self):
 
572
        filename = self.resolve_path(self.source)
 
573
        self.text = self.preload(filename,
 
574
                                 self.source_encoding,
 
575
                                 self.source_error)
 
576
 
 
577
    def _load_from_text(self, *largs):
 
578
        try:
 
579
            # clear the current widgets
 
580
            self.content.clear_widgets()
 
581
            self.anchors_widgets = []
 
582
            self.refs_assoc = {}
 
583
 
 
584
            # parse the source
 
585
            document = utils.new_document('Document', self._settings)
 
586
            text = self.text
 
587
            if PY2 and type(text) is str:
 
588
                text = text.decode('utf-8')
 
589
            self._parser.parse(text, document)
 
590
 
 
591
            # fill the current document node
 
592
            visitor = _Visitor(self, document)
 
593
            document.walkabout(visitor)
 
594
 
 
595
            self.title = visitor.title or 'No title'
 
596
        except:
 
597
            Logger.exception('Rst: error while loading text')
 
598
 
 
599
    def on_ref_press(self, node, ref):
 
600
        self.goto(ref)
 
601
 
 
602
    def goto(self, ref, *largs):
 
603
        '''Scroll to the reference. If it's not found, nothing will be done.
 
604
 
 
605
        For this text::
 
606
 
 
607
            .. _myref:
 
608
 
 
609
            This is something I always wanted.
 
610
 
 
611
        You can do::
 
612
 
 
613
            from kivy.clock import Clock
 
614
            from functools import partial
 
615
 
 
616
            doc = RstDocument(...)
 
617
            Clock.schedule_once(partial(doc.goto, 'myref'), 0.1)
 
618
 
 
619
        .. note::
 
620
 
 
621
            It is preferable to delay the call of the goto if you just loaded
 
622
            the document because the layout might not be finished or the
 
623
            size of the RstDocument has not yet been determined. In
 
624
            either case, the calculation of the scrolling would be
 
625
            wrong.
 
626
 
 
627
            You can, however, do a direct call if the document is already
 
628
            loaded.
 
629
 
 
630
        .. versionadded:: 1.3.0
 
631
        '''
 
632
        # check if it's a file ?
 
633
        if ref.endswith('.rst'):
 
634
            # whether it's a valid or invalid file, let source deal with it
 
635
            self.source = ref
 
636
            return
 
637
 
 
638
        # get the association
 
639
        ref = self.refs_assoc.get(ref, ref)
 
640
 
 
641
        # search into all the nodes containing anchors
 
642
        ax = ay = None
 
643
        for node in self.anchors_widgets:
 
644
            if ref in node.anchors:
 
645
                ax, ay = node.anchors[ref]
 
646
                break
 
647
 
 
648
        # not found, stop here
 
649
        if ax is None:
 
650
            return
 
651
 
 
652
        # found, calculate the real coordinate
 
653
 
 
654
        # get the anchor coordinate inside widget space
 
655
        ax += node.x
 
656
        ay = node.top - ay
 
657
        #ay += node.y
 
658
 
 
659
        # what's the current coordinate for us?
 
660
        sx, sy = self.scatter.x, self.scatter.top
 
661
        #ax, ay = self.scatter.to_parent(ax, ay)
 
662
 
 
663
        ay -= self.height
 
664
 
 
665
        dx, dy = self.convert_distance_to_scroll(0, ay)
 
666
        dy = max(0, min(1, dy))
 
667
        Animation(scroll_y=dy, d=.25, t='in_out_expo').start(self)
 
668
 
 
669
    def add_anchors(self, node):
 
670
        self.anchors_widgets.append(node)
 
671
 
 
672
 
 
673
class RstTitle(Label):
 
674
 
 
675
    section = NumericProperty(0)
 
676
 
 
677
    document = ObjectProperty(None)
 
678
 
 
679
 
 
680
class RstParagraph(Label):
 
681
 
 
682
    mx = NumericProperty(10)
 
683
 
 
684
    my = NumericProperty(10)
 
685
 
 
686
    document = ObjectProperty(None)
 
687
 
 
688
 
 
689
class RstTerm(AnchorLayout):
 
690
 
 
691
    text = StringProperty('')
 
692
 
 
693
    document = ObjectProperty(None)
 
694
 
 
695
 
 
696
class RstBlockQuote(GridLayout):
 
697
    content = ObjectProperty(None)
 
698
 
 
699
 
 
700
class RstLiteralBlock(GridLayout):
 
701
    content = ObjectProperty(None)
 
702
 
 
703
 
 
704
class RstList(GridLayout):
 
705
    pass
 
706
 
 
707
 
 
708
class RstListItem(GridLayout):
 
709
    content = ObjectProperty(None)
 
710
 
 
711
 
 
712
class RstListBullet(Label):
 
713
 
 
714
    document = ObjectProperty(None)
 
715
 
 
716
 
 
717
class RstSystemMessage(GridLayout):
 
718
    pass
 
719
 
 
720
 
 
721
class RstWarning(GridLayout):
 
722
    content = ObjectProperty(None)
 
723
 
 
724
 
 
725
class RstNote(GridLayout):
 
726
    content = ObjectProperty(None)
 
727
 
 
728
 
 
729
class RstImage(Image):
 
730
    pass
 
731
 
 
732
 
 
733
class RstAsyncImage(AsyncImage):
 
734
    pass
 
735
 
 
736
 
 
737
class RstDefinitionList(GridLayout):
 
738
 
 
739
    document = ObjectProperty(None)
 
740
 
 
741
 
 
742
class RstDefinition(GridLayout):
 
743
 
 
744
    document = ObjectProperty(None)
 
745
 
 
746
 
 
747
class RstFieldList(GridLayout):
 
748
    pass
 
749
 
 
750
 
 
751
class RstFieldName(Label):
 
752
 
 
753
    document = ObjectProperty(None)
 
754
 
 
755
 
 
756
class RstFieldBody(GridLayout):
 
757
    pass
 
758
 
 
759
 
 
760
class RstGridLayout(GridLayout):
 
761
    pass
 
762
 
 
763
 
 
764
class RstTable(GridLayout):
 
765
    pass
 
766
 
 
767
 
 
768
class RstEntry(GridLayout):
 
769
    pass
 
770
 
 
771
 
 
772
class RstTransition(Widget):
 
773
    pass
 
774
 
 
775
 
 
776
class RstEmptySpace(Widget):
 
777
    pass
 
778
 
 
779
 
 
780
class RstDefinitionSpace(Widget):
 
781
 
 
782
    document = ObjectProperty(None)
 
783
 
 
784
 
 
785
class _ToctreeVisitor(nodes.NodeVisitor):
 
786
 
 
787
    def __init__(self, *largs):
 
788
        self.toctree = self.current = []
 
789
        self.queue = []
 
790
        self.text = ''
 
791
        nodes.NodeVisitor.__init__(self, *largs)
 
792
 
 
793
    def push(self, tree):
 
794
        self.queue.append(tree)
 
795
        self.current = tree
 
796
 
 
797
    def pop(self):
 
798
        self.current = self.queue.pop()
 
799
 
 
800
    def dispatch_visit(self, node):
 
801
        cls = node.__class__
 
802
        if cls is nodes.section:
 
803
            section = {
 
804
                'ids': node['ids'],
 
805
                'names': node['names'],
 
806
                'title': '',
 
807
                'children': []}
 
808
            if isinstance(self.current, dict):
 
809
                self.current['children'].append(section)
 
810
            else:
 
811
                self.current.append(section)
 
812
            self.push(section)
 
813
        elif cls is nodes.title:
 
814
            self.text = ''
 
815
        elif cls is nodes.Text:
 
816
            self.text += node
 
817
 
 
818
    def dispatch_departure(self, node):
 
819
        cls = node.__class__
 
820
        if cls is nodes.section:
 
821
            self.pop()
 
822
        elif cls is nodes.title:
 
823
            self.current['title'] = self.text
 
824
 
 
825
 
 
826
class _Visitor(nodes.NodeVisitor):
 
827
 
 
828
    def __init__(self, root, *largs):
 
829
        self.root = root
 
830
        self.title = None
 
831
        self.current_list = []
 
832
        self.current = None
 
833
        self.idx_list = None
 
834
        self.text = ''
 
835
        self.text_have_anchor = False
 
836
        self.section = 0
 
837
        self.do_strip_text = False
 
838
        nodes.NodeVisitor.__init__(self, *largs)
 
839
 
 
840
    def push(self, widget):
 
841
        self.current_list.append(self.current)
 
842
        self.current = widget
 
843
 
 
844
    def pop(self):
 
845
        self.current = self.current_list.pop()
 
846
 
 
847
    def dispatch_visit(self, node):
 
848
        cls = node.__class__
 
849
        if cls is nodes.document:
 
850
            self.push(self.root.content)
 
851
 
 
852
        elif cls is nodes.section:
 
853
            self.section += 1
 
854
 
 
855
        elif cls is nodes.title:
 
856
            label = RstTitle(section=self.section, document=self.root)
 
857
            self.current.add_widget(label)
 
858
            self.push(label)
 
859
            #assert(self.text == '')
 
860
 
 
861
        elif cls is nodes.Text:
 
862
            if self.do_strip_text:
 
863
                node = node.replace('\n', ' ')
 
864
                node = node.replace('  ', ' ')
 
865
                node = node.replace('\t', ' ')
 
866
                node = node.replace('  ', ' ')
 
867
                if node.startswith(' '):
 
868
                    node = ' ' + node.lstrip(' ')
 
869
                if node.endswith(' '):
 
870
                    node = node.rstrip(' ') + ' '
 
871
                if self.text.endswith(' ') and node.startswith(' '):
 
872
                    node = node[1:]
 
873
            self.text += node
 
874
 
 
875
        elif cls is nodes.paragraph:
 
876
            self.do_strip_text = True
 
877
            label = RstParagraph(document=self.root)
 
878
            if isinstance(self.current, RstEntry):
 
879
                label.mx = 10
 
880
            self.current.add_widget(label)
 
881
            self.push(label)
 
882
 
 
883
        elif cls is nodes.literal_block:
 
884
            box = RstLiteralBlock()
 
885
            self.current.add_widget(box)
 
886
            self.push(box)
 
887
 
 
888
        elif cls is nodes.emphasis:
 
889
            self.text += '[i]'
 
890
 
 
891
        elif cls is nodes.strong:
 
892
            self.text += '[b]'
 
893
 
 
894
        elif cls is nodes.literal:
 
895
            self.text += '[font=fonts/RobotoMono-Regular.ttf]'
 
896
 
 
897
        elif cls is nodes.block_quote:
 
898
            box = RstBlockQuote()
 
899
            self.current.add_widget(box)
 
900
            self.push(box.content)
 
901
            assert(self.text == '')
 
902
 
 
903
        elif cls is nodes.enumerated_list:
 
904
            box = RstList()
 
905
            self.current.add_widget(box)
 
906
            self.push(box)
 
907
            self.idx_list = 0
 
908
 
 
909
        elif cls is nodes.bullet_list:
 
910
            box = RstList()
 
911
            self.current.add_widget(box)
 
912
            self.push(box)
 
913
            self.idx_list = None
 
914
 
 
915
        elif cls is nodes.list_item:
 
916
            bullet = '-'
 
917
            if self.idx_list is not None:
 
918
                self.idx_list += 1
 
919
                bullet = '%d.' % self.idx_list
 
920
            bullet = self.colorize(bullet, 'bullet')
 
921
            item = RstListItem()
 
922
            self.current.add_widget(RstListBullet(
 
923
                text=bullet, document=self.root))
 
924
            self.current.add_widget(item)
 
925
            self.push(item)
 
926
 
 
927
        elif cls is nodes.system_message:
 
928
            label = RstSystemMessage()
 
929
            if self.root.show_errors:
 
930
                self.current.add_widget(label)
 
931
            self.push(label)
 
932
 
 
933
        elif cls is nodes.warning:
 
934
            label = RstWarning()
 
935
            self.current.add_widget(label)
 
936
            self.push(label.content)
 
937
            assert(self.text == '')
 
938
 
 
939
        elif cls is nodes.note:
 
940
            label = RstNote()
 
941
            self.current.add_widget(label)
 
942
            self.push(label.content)
 
943
            assert(self.text == '')
 
944
 
 
945
        elif cls is nodes.image:
 
946
            uri = node['uri']
 
947
            if uri.startswith('/') and self.root.document_root:
 
948
                uri = join(self.root.document_root, uri[1:])
 
949
            if uri.startswith('http://') or uri.startswith('https://'):
 
950
                image = RstAsyncImage(source=uri)
 
951
            else:
 
952
                image = RstImage(source=uri)
 
953
 
 
954
            align = node.get('align', 'center')
 
955
            root = AnchorLayout(size_hint_y=None, anchor_x=align,
 
956
                                height=image.height)
 
957
            image.bind(height=root.setter('height'))
 
958
            root.add_widget(image)
 
959
            self.current.add_widget(root)
 
960
 
 
961
        elif cls is nodes.definition_list:
 
962
            lst = RstDefinitionList(document=self.root)
 
963
            self.current.add_widget(lst)
 
964
            self.push(lst)
 
965
 
 
966
        elif cls is nodes.term:
 
967
            assert(isinstance(self.current, RstDefinitionList))
 
968
            term = RstTerm(document=self.root)
 
969
            self.current.add_widget(term)
 
970
            self.push(term)
 
971
 
 
972
        elif cls is nodes.definition:
 
973
            assert(isinstance(self.current, RstDefinitionList))
 
974
            definition = RstDefinition(document=self.root)
 
975
            definition.add_widget(RstDefinitionSpace(document=self.root))
 
976
            self.current.add_widget(definition)
 
977
            self.push(definition)
 
978
 
 
979
        elif cls is nodes.field_list:
 
980
            fieldlist = RstFieldList()
 
981
            self.current.add_widget(fieldlist)
 
982
            self.push(fieldlist)
 
983
 
 
984
        elif cls is nodes.field_name:
 
985
            name = RstFieldName(document=self.root)
 
986
            self.current.add_widget(name)
 
987
            self.push(name)
 
988
 
 
989
        elif cls is nodes.field_body:
 
990
            body = RstFieldBody()
 
991
            self.current.add_widget(body)
 
992
            self.push(body)
 
993
 
 
994
        elif cls is nodes.table:
 
995
            table = RstTable(cols=0)
 
996
            self.current.add_widget(table)
 
997
            self.push(table)
 
998
 
 
999
        elif cls is nodes.colspec:
 
1000
            self.current.cols += 1
 
1001
 
 
1002
        elif cls is nodes.entry:
 
1003
            entry = RstEntry()
 
1004
            self.current.add_widget(entry)
 
1005
            self.push(entry)
 
1006
 
 
1007
        elif cls is nodes.transition:
 
1008
            self.current.add_widget(RstTransition())
 
1009
 
 
1010
        elif cls is nodes.reference:
 
1011
            name = node.get('name', node.get('refuri'))
 
1012
            self.text += '[ref=%s][color=%s]' % (
 
1013
                name, self.root.colors.get(
 
1014
                    'link', self.root.colors.get('paragraph')))
 
1015
            if 'refname' in node and 'name' in node:
 
1016
                self.root.refs_assoc[node['name']] = node['refname']
 
1017
 
 
1018
        elif cls is nodes.target:
 
1019
            name = None
 
1020
            if 'ids' in node:
 
1021
                name = node['ids'][0]
 
1022
            elif 'names' in node:
 
1023
                name = node['names'][0]
 
1024
            self.text += '[anchor=%s]' % name
 
1025
            self.text_have_anchor = True
 
1026
 
 
1027
        elif cls is role_doc:
 
1028
            self.doc_index = len(self.text)
 
1029
 
 
1030
        elif cls is role_video:
 
1031
            pass
 
1032
 
 
1033
    def dispatch_departure(self, node):
 
1034
        cls = node.__class__
 
1035
        if cls is nodes.document:
 
1036
            self.pop()
 
1037
 
 
1038
        elif cls is nodes.section:
 
1039
            self.section -= 1
 
1040
 
 
1041
        elif cls is nodes.title:
 
1042
            assert(isinstance(self.current, RstTitle))
 
1043
            if not self.title:
 
1044
                self.title = self.text
 
1045
            self.set_text(self.current, 'title')
 
1046
            self.pop()
 
1047
 
 
1048
        elif cls is nodes.Text:
 
1049
            pass
 
1050
 
 
1051
        elif cls is nodes.paragraph:
 
1052
            self.do_strip_text = False
 
1053
            assert(isinstance(self.current, RstParagraph))
 
1054
            self.set_text(self.current, 'paragraph')
 
1055
            self.pop()
 
1056
 
 
1057
        elif cls is nodes.literal_block:
 
1058
            assert(isinstance(self.current, RstLiteralBlock))
 
1059
            self.set_text(self.current.content, 'literal_block')
 
1060
            self.pop()
 
1061
 
 
1062
        elif cls is nodes.emphasis:
 
1063
            self.text += '[/i]'
 
1064
 
 
1065
        elif cls is nodes.strong:
 
1066
            self.text += '[/b]'
 
1067
 
 
1068
        elif cls is nodes.literal:
 
1069
            self.text += '[/font]'
 
1070
 
 
1071
        elif cls is nodes.block_quote:
 
1072
            self.pop()
 
1073
 
 
1074
        elif cls is nodes.enumerated_list:
 
1075
            self.idx_list = None
 
1076
            self.pop()
 
1077
 
 
1078
        elif cls is nodes.bullet_list:
 
1079
            self.pop()
 
1080
 
 
1081
        elif cls is nodes.list_item:
 
1082
            self.pop()
 
1083
 
 
1084
        elif cls is nodes.system_message:
 
1085
            self.pop()
 
1086
 
 
1087
        elif cls is nodes.warning:
 
1088
            self.pop()
 
1089
 
 
1090
        elif cls is nodes.note:
 
1091
            self.pop()
 
1092
 
 
1093
        elif cls is nodes.definition_list:
 
1094
            self.pop()
 
1095
 
 
1096
        elif cls is nodes.term:
 
1097
            assert(isinstance(self.current, RstTerm))
 
1098
            self.set_text(self.current, 'term')
 
1099
            self.pop()
 
1100
 
 
1101
        elif cls is nodes.definition:
 
1102
            self.pop()
 
1103
 
 
1104
        elif cls is nodes.field_list:
 
1105
            self.pop()
 
1106
 
 
1107
        elif cls is nodes.field_name:
 
1108
            assert(isinstance(self.current, RstFieldName))
 
1109
            self.set_text(self.current, 'field_name')
 
1110
            self.pop()
 
1111
 
 
1112
        elif cls is nodes.field_body:
 
1113
            self.pop()
 
1114
 
 
1115
        elif cls is nodes.table:
 
1116
            self.pop()
 
1117
 
 
1118
        elif cls is nodes.colspec:
 
1119
            pass
 
1120
 
 
1121
        elif cls is nodes.entry:
 
1122
            self.pop()
 
1123
 
 
1124
        elif cls is nodes.reference:
 
1125
            self.text += '[/color][/ref]'
 
1126
 
 
1127
        elif cls is role_doc:
 
1128
            docname = self.text[self.doc_index:]
 
1129
            rst_docname = docname
 
1130
            if rst_docname.endswith('.rst'):
 
1131
                docname = docname[:-4]
 
1132
            else:
 
1133
                rst_docname += '.rst'
 
1134
 
 
1135
            # try to preload it
 
1136
            filename = self.root.resolve_path(rst_docname)
 
1137
            self.root.preload(filename)
 
1138
 
 
1139
            # if exist, use the title of the first section found in the
 
1140
            # document
 
1141
            title = docname
 
1142
            if filename in self.root.toctrees:
 
1143
                toctree = self.root.toctrees[filename]
 
1144
                if len(toctree):
 
1145
                    title = toctree[0]['title']
 
1146
 
 
1147
            # replace the text with a good reference
 
1148
            text = '[ref=%s]%s[/ref]' % (
 
1149
                rst_docname,
 
1150
                self.colorize(title, 'link'))
 
1151
            self.text = self.text[:self.doc_index] + text
 
1152
 
 
1153
        elif cls is role_video:
 
1154
            width = node['width'] if 'width' in node.attlist() else 400
 
1155
            height = node['height'] if 'height' in node.attlist() else 300
 
1156
            uri = node['source']
 
1157
            if uri.startswith('/') and self.root.document_root:
 
1158
                uri = join(self.root.document_root, uri[1:])
 
1159
            video = RstVideoPlayer(
 
1160
                source=uri,
 
1161
                size_hint=(None, None),
 
1162
                size=(width, height))
 
1163
            anchor = AnchorLayout(size_hint_y=None, height=height + 20)
 
1164
            anchor.add_widget(video)
 
1165
            self.current.add_widget(anchor)
 
1166
 
 
1167
    def set_text(self, node, parent):
 
1168
        text = self.text
 
1169
        if parent == 'term' or parent == 'field_name':
 
1170
            text = '[b]%s[/b]' % text
 
1171
        # search anchors
 
1172
        node.text = self.colorize(text, parent)
 
1173
        node.bind(on_ref_press=self.root.on_ref_press)
 
1174
        if self.text_have_anchor:
 
1175
            self.root.add_anchors(node)
 
1176
        self.text = ''
 
1177
        self.text_have_anchor = False
 
1178
 
 
1179
    def colorize(self, text, name):
 
1180
        return '[color=%s]%s[/color]' % (
 
1181
            self.root.colors.get(name, self.root.colors['paragraph']),
 
1182
            text)
 
1183
 
 
1184
if __name__ == '__main__':
 
1185
    from kivy.base import runTouchApp
 
1186
    import sys
 
1187
    runTouchApp(RstDocument(source=sys.argv[1]))