~vorlon/ubuntu/saucy/gourmet/trunk

« back to all changes in this revision

Viewing changes to src/lib/gtk_extras/LinkedTextView.py

  • Committer: Bazaar Package Importer
  • Author(s): Rolf Leggewie
  • Date: 2008-07-26 13:29:41 UTC
  • Revision ID: james.westby@ubuntu.com-20080726132941-6ldd73qmacrzz0bn
Tags: upstream-0.14.0
ImportĀ upstreamĀ versionĀ 0.14.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
### Copyright (C) 2005 Thomas M. Hinkle
 
2
###
 
3
### This library is free software; you can redistribute it and/or
 
4
### modify it under the terms of the GNU General Public License as
 
5
### published by the Free Software Foundation; either version 2 of the
 
6
### License, or (at your option) any later version.
 
7
###
 
8
### This library is distributed in the hope that it will be useful,
 
9
### but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
11
### General Public License for more details.
 
12
###
 
13
### You should have received a copy of the GNU General Public License
 
14
### along with this library; if not, write to the Free Software
 
15
### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
16
### 02111-1307, USA.
 
17
 
 
18
# Largely based on hypertext.py example in pygtk docs by
 
19
# Maik Hertha <maik.hertha@berlin.de>
 
20
 
 
21
import pango,gtk,gobject
 
22
import re, xml.sax.saxutils
 
23
from TextBufferMarkup import PangoBuffer
 
24
from gourmet.gdebug import *
 
25
 
 
26
class LinkedPangoBuffer (PangoBuffer):
 
27
 
 
28
    href_regexp = re.compile(r"<a href=['\"]([^'\"]+)['\"][^>]*>(.*?)</a>")
 
29
 
 
30
    url_markup = 'underline="single" color="blue"'
 
31
 
 
32
    url_props = [('underline',pango.UNDERLINE_SINGLE),
 
33
                 ('foreground-gdk',gtk.gdk.color_parse('blue')),
 
34
                 ]
 
35
 
 
36
    markup_dict = {}
 
37
 
 
38
    def set_text (self, txt):
 
39
        m = self.href_regexp.search(txt)
 
40
        if m:
 
41
            while m:
 
42
                href = m.groups()[0]
 
43
                body = xml.sax.saxutils.unescape(m.groups()[1])
 
44
                if self.markup_dict.has_key(body) and self.markup_dict[body]!=href:
 
45
                    raise """Damn -- our not-so-clever implementation of <a href=""> parsing requires
 
46
                    that no two distinct links have the same text describing them!"""
 
47
                self.markup_dict[body]=href
 
48
                m = self.href_regexp.search(txt,m.end())
 
49
            txt = self.href_regexp.sub(r'<span %s>\2</span>'%self.url_markup,txt)
 
50
        PangoBuffer.set_text(self,txt)
 
51
 
 
52
    def insert_with_tags (self, itr, text, *tags):
 
53
        match = True
 
54
        for p,v in self.url_props:
 
55
            match = False
 
56
            for t in tags:
 
57
                if isinstance(v,gtk.gdk.Color):
 
58
                    c = t.get_property(p)
 
59
                    if v.red==c.red and v.blue==c.blue and v.green==c.green:
 
60
                        match=True
 
61
                elif t.get_property(p)==v:
 
62
                    match=True
 
63
            if not match:
 
64
                break
 
65
        text = unicode(text)
 
66
        if match and self.markup_dict.has_key(text):
 
67
            new_tag = self.create_tag()
 
68
            new_tag.set_data('href',self.markup_dict[text])
 
69
            tags = list(tags)
 
70
            tags.append(new_tag)
 
71
        elif match:
 
72
            print 'Funny',text,'looks like a link, but is not in markup_dict',self.markup_dict
 
73
        PangoBuffer.insert_with_tags(self,itr,text,*tags)
 
74
 
 
75
class LinkedTextView (gtk.TextView):
 
76
 
 
77
    hovering_over_link = False
 
78
    hand_cursor = gtk.gdk.Cursor(gtk.gdk.HAND2)
 
79
    regular_cursor = gtk.gdk.Cursor(gtk.gdk.XTERM)
 
80
 
 
81
    __gsignals__ = {
 
82
        'link-activated':(gobject.SIGNAL_RUN_LAST,
 
83
                          gobject.TYPE_STRING,
 
84
                          [gobject.TYPE_STRING]),
 
85
        }
 
86
    
 
87
    def __init__ (self):
 
88
        gobject.GObject.__init__(self)
 
89
        gtk.TextView.__init__(self)
 
90
        self.set_buffer(self.make_buffer())
 
91
        buf = self.get_buffer()
 
92
        self.set_text = buf.set_text
 
93
        self.connect('key-press-event',self.key_press_event)
 
94
        self.connect('event-after',self.event_after)
 
95
        self.connect('motion-notify-event',self.motion_notify_event)
 
96
        self.connect('visibility-notify-event',self.visibility_notify_event)
 
97
 
 
98
    def make_buffer (self):
 
99
        return LinkedPangoBuffer()
 
100
 
 
101
    # Links can be activated by pressing Enter.
 
102
    def key_press_event(self, text_view, event):
 
103
        keyname = gtk.gdk.keyval_name(event.keyval)
 
104
        if keyname in ['Return','KP_Enter']:
 
105
            buffer = text_view.get_buffer()
 
106
            iter = buffer.get_iter_at_mark(buffer.get_insert())
 
107
            return self.follow_if_link(text_view, iter)
 
108
        return False
 
109
    # Links can also be activated by clicking.
 
110
    def event_after(self, text_view, event):
 
111
        if event.type != gtk.gdk.BUTTON_RELEASE:
 
112
            return False
 
113
        if event.button != 1:
 
114
            return False
 
115
        buffer = text_view.get_buffer()
 
116
 
 
117
        # we shouldn't follow a link if the user has selected something
 
118
        try:
 
119
            start, end = buffer.get_selection_bounds()
 
120
        except ValueError:
 
121
            # If there is nothing selected, None is return
 
122
            pass
 
123
        else:
 
124
            if start.get_offset() != end.get_offset():
 
125
                return False
 
126
 
 
127
        x, y = text_view.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET,
 
128
            int(event.x), int(event.y))
 
129
        iter = text_view.get_iter_at_location(x, y)
 
130
        self.follow_if_link(text_view, iter)
 
131
        return False
 
132
 
 
133
    # Looks at all tags covering the position (x, y) in the text view,
 
134
    # and if one of them is a link, change the cursor to the "hands" cursor
 
135
    # typically used by web browsers.
 
136
    def set_cursor_if_appropriate(self, text_view, x, y):
 
137
        hovering = False
 
138
        buffer = text_view.get_buffer()
 
139
        iter = text_view.get_iter_at_location(x, y)
 
140
        tags = iter.get_tags()
 
141
 
 
142
        for tag in tags:
 
143
            href = tag.get_data("href")
 
144
            if href:
 
145
                hovering = True
 
146
                break
 
147
        if hovering != self.hovering_over_link:
 
148
            self.hovering_over_link = hovering
 
149
 
 
150
        if self.hovering_over_link:
 
151
            text_view.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(self.hand_cursor)
 
152
        else:
 
153
            text_view.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(self.regular_cursor)
 
154
 
 
155
    # Update the cursor image if the pointer moved.
 
156
    def motion_notify_event(self, text_view, event):
 
157
        x, y = text_view.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET,
 
158
            int(event.x), int(event.y))
 
159
        self.set_cursor_if_appropriate(text_view, x, y)
 
160
        text_view.window.get_pointer()
 
161
        return False
 
162
 
 
163
    # Also update the cursor image if the window becomes visible
 
164
    # (e.g. when a window covering it got iconified).
 
165
    def visibility_notify_event(self, text_view, event):
 
166
        wx, wy, mod = text_view.window.get_pointer()
 
167
        bx, by = text_view.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, wx, wy)
 
168
 
 
169
        self.set_cursor_if_appropriate (text_view, bx, by)
 
170
        return False
 
171
 
 
172
    def follow_if_link (self, text_view, iter):
 
173
        ''' Looks at all tags covering the position of iter in the text view,
 
174
            and if one of them is a link, follow it by showing the page identified
 
175
            by the data attached to it.
 
176
        '''
 
177
        tags = iter.get_tags()
 
178
        for tag in tags:
 
179
            href = tag.get_data('href')
 
180
            if href:
 
181
                self.emit('link-activated',href)
 
182
                return True
 
183
 
 
184
if gtk.pygtk_version[1] < 8:
 
185
    gobject.type_register(LinkedTextView)    
 
186
 
 
187
if __name__ == '__main__':
 
188
    def print_link (tv,l):
 
189
        print l
 
190
    tv = LinkedTextView()
 
191
    tv.connect('link-activated',print_link)    
 
192
    w = gtk.Window()
 
193
    w.add(tv)
 
194
    tv.get_buffer().set_text(u"""This is some text
 
195
    Some <i>fancy</i>, <u>fancy</u>, text.
 
196
    This is <a href="foo">a link</a>, a
 
197
    <a href="fancy_desc">fancy, fancy</a> link.
 
198
 
 
199
    <a href="123:foo">recipe link</a>
 
200
 
 
201
    <a href="456:boo">\xbc recipe boogoochooboo</a>
 
202
    
 
203
    <b>Yeah!</b>
 
204
    """)
 
205
 
 
206
    w.show_all()
 
207
    w.connect('delete-event',lambda *args: gtk.main_quit())
 
208
    gtk.main()