summaryrefslogtreecommitdiff
path: root/examples/pygtk-demo/demos/textscroll.py
blob: aa59ca2c98910ac67ae203d91886b099240c679d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env python
"""Text Widget/Automatic scrolling

This example demonstrates how to use the gravity of
GtkTextMarks to keep a text view scrolled to the bottom
while appending text."""

import gobject
import gtk

class AutomaticScrollingDemo(gtk.Window):
    def __init__(self, parent=None):
        # Create the toplevel window
        gtk.Window.__init__(self)
        try:
            self.set_screen(parent.get_screen())
        except AttributeError:
            self.connect('destroy', lambda *w: gtk.main_quit())

        self.set_title(self.__class__.__name__)
        self.set_default_size(600, 400)
        self.set_border_width(0)

        hbox = gtk.HBox(True, 6)
        self.add(hbox)

        self.create_text_view(hbox, True)
        self.create_text_view(hbox, False)
        self.count_sb = 0
        self.count_se = 0

        self.show_all()

    def create_text_view(self, hbox, scroll_to_end):
        swindow = gtk.ScrolledWindow()
        hbox.pack_start(swindow)
        textview = gtk.TextView()
        swindow.add(textview)

        timeout = self.setup_scroll(textview, scroll_to_end)

        # Remove the timeout in destroy handler, so we don't try to
        # scroll destroyed widget.
        textview.connect("destroy", lambda widget: gobject.source_remove(timeout))

    def setup_scroll(self, textview, scroll_to_end):
        buf = textview.get_buffer()
        itr = buf.get_end_iter()

        if scroll_to_end:
            # If we want to scroll to the end, including horizontal scrolling,
            # then we just create a mark with right gravity at the end of the
            # buffer. It will stay at the end unless explicitely moved with
            # gtk_text_buffer_move_mark.
            buf.create_mark("end", itr, False)

            # Add scrolling timeout.
            return gobject.timeout_add(50, self.scroll_to_end, textview)
        else:
            # If we want to scroll to the bottom, but not scroll horizontally,
            # then an end mark won't do the job. Just create a mark so we can
            # use it with gtk_text_view_scroll_mark_onscreen, we'll position it
            # explicitely when needed. Use left gravity so the mark stays where
            # we put it after inserting new text.
            buf.create_mark("scroll", itr, True)

            # Add scrolling timeout.
            return gobject.timeout_add(100, self.scroll_to_bottom, textview)

    """ Scroll to the end of the buffer. """
    def scroll_to_end(self, textview):
        buf = textview.get_buffer()

        # Get the "end" mark. It's located at the end of buffer because
        # of right gravity
        mark = buf.get_mark("end")
        itr = buf.get_iter_at_mark(mark)

        # and insert some text at its position, the iter will be
        # revalidated after insertion to point to the end of inserted text
        buf.insert(itr, "\n")
        buf.insert(itr, " " * self.count_se)
        buf.insert(itr, "Scroll to end scroll to end scroll to end scroll to end ")

        # Now scroll the end mark onscreen.
        textview.scroll_mark_onscreen(mark)

        # Emulate typewriter behavior, shift to the left if we 
        # are far enough to the right.
        self.count_se += 1
        if self.count_se > 150:
            self.count_se = 0

        return True

    """ Scroll to the bottom of the buffer. """
    def scroll_to_bottom(self, textview):
        buf = textview.get_buffer()

        # Get the end iterator
        itr = buf.get_end_iter()

        # and insert some text at it, the iter will be revalidated
        # after insertion to point to the end of inserted text
        buf.insert(itr, "\n")
        buf.insert(itr, " " * self.count_sb)
        buf.insert(itr, "Scroll to bottom scroll to bottom scroll to bottom scroll to bottom")

        # Move the iterator to the beginning of line, so we don't scroll
        # in horizontal direction
        itr.set_line_offset(0)

        # and place the mark at iter. the mark will stay there after we
        # insert some text at the end because it has right gravity.
        mark = buf.get_mark("scroll")
        buf.move_mark(mark, itr)

        # Scroll the mark onscreen.
        textview.scroll_mark_onscreen(mark)

        # Shift text back if we got enough to the right.
        self.count_sb += 1
        if self.count_sb > 40:
            self.count_sb = 0

        return True

def main():
    AutomaticScrollingDemo()
    gtk.main()

if __name__ == '__main__':
    main()