OSDN Git Service

draw efficiently, ThreadView
[fukui-no-namari/fukui-no-namari.git] / src / FukuiNoNamari / thread_view.py
1 # Copyright (C) 2007 by Aiwota Programmer
2 # aiwotaprog@tetteke.tk
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 import pygtk
19 pygtk.require('2.0')
20 import gtk
21 import pango
22 from FukuiNoNamariExt import thread_view_extend
23
24
25 class Line:
26
27     HEIGHT = 15
28
29     def __init__(self, start_index, end_index, rectangle):
30         self.start_index = start_index
31         self.end_index = end_index
32         self.rectangle = rectangle
33
34     def is_on_xy(self, x, y):
35         left = self.rectangle.x
36         right = left + self.rectangle.width
37         top = self.rectangle.y
38         bottom = top + self.rectangle.height
39         return x >= left and x < right and y >= top and y < bottom
40         
41
42 class ElementEmpty:
43
44     def __init__(self):
45         self.initialize()
46
47     def initialize(self):
48         self.line_list = []
49
50     def is_on_xy(self, x, y):
51         for line in self.line_list:
52             if line.is_on_xy(x, y):
53                 return True
54         return False
55
56     def xy_to_index(self, x, y):
57         return 0
58
59     def build_line_list(self, x, y, width, left_margin):
60         self.initialize()
61
62         line = Line(0, 0, gtk.gdk.Rectangle(x, y, width - x, Line.HEIGHT))
63         self.line_list.append(line)
64
65         return width, y
66
67     def draw(self, drawingarea, y_offset, pango_layout,
68              selection=False, start_index=0, end_index=0xffffff):
69         pass
70
71     
72 class ElementText:
73
74     def __init__(self, text, pango_layout):
75         self.text = text
76
77         attrlist = self._get_attrs()
78         self.widths = thread_view_extend.get_char_width(
79             pango_layout.get_context(), text, attrlist)
80
81         self.line_list = []
82
83     def _get_attrs(self):
84         attrs = pango.AttrList()
85         return attrs
86
87     def is_on_xy(self, x, y):
88         for line in self.line_list:
89             if line.is_on_xy(x, y):
90                 return True
91         return False
92
93     def xy_to_index(self, x, y):
94         for line in self.line_list:
95             top = line.rectangle.y
96             bottom = top + line.rectangle.height
97             if y >= top and y < bottom:
98                 sum_of_widths = line.rectangle.x
99                 index = line.start_index
100                 for width in self.widths[line.start_index:line.end_index]:
101                     if sum_of_widths + width/2 > x:
102                         break
103                     sum_of_widths += width
104                     index += 1
105                 return index
106         
107     def build_line_list(self, x, y, width, left_margin):
108         self.line_list = []
109
110         current_line_start_index = 0
111         current_line_x = x
112         current_line_y = y
113         current_line_width = 0
114
115         for index, ch in enumerate(self.text):
116             ch_w = self.widths[index]
117             ch_h = Line.HEIGHT
118             if current_line_x + current_line_width + ch_w > width:
119                 line = Line(
120                     current_line_start_index, index,
121                     gtk.gdk.Rectangle(
122                     current_line_x, current_line_y,
123                     current_line_width, Line.HEIGHT))
124                 self.line_list.append(line)
125
126                 current_line_start_index = index
127                 current_line_x = left_margin
128                 current_line_y += Line.HEIGHT
129                 current_line_width = ch_w
130             else:
131                 current_line_width += ch_w
132
133         if current_line_start_index < len(self.text):
134             line = Line(current_line_start_index, len(self.text),
135                         gtk.gdk.Rectangle(current_line_x,
136                                           current_line_y,
137                                           current_line_width,
138                                           Line.HEIGHT))
139             self.line_list.append(line)
140
141             current_line_x += current_line_width
142
143         return current_line_x, current_line_y
144
145     def draw(self, drawingarea, y_offset, pango_layout,
146              selection=False, start_index=0, end_index=0xffffff):
147
148         selection_fg = drawingarea.style.fg[3]
149         selection_bg = drawingarea.style.bg[3]
150
151         for line in self.line_list:
152
153             text = self.text[line.start_index:line.end_index]
154             u_text = text.encode("utf8")
155             gc = drawingarea.window.new_gc()
156             attrs = self._get_attrs()
157             if selection:
158
159                 s = start_index - line.start_index
160                 s = max(s, 0)
161                 s = min(s, line.end_index - line.start_index)
162                 s = len(text[:s].encode("utf8"))
163
164                 e = end_index - line.start_index
165                 e = min(e, line.end_index - line.start_index)
166                 e = max(e, 0)
167                 e = len(text[:e].encode("utf8"))
168                     
169                 selection_all_attr_fg = pango.AttrForeground(
170                     selection_fg.red, selection_fg.green, selection_fg.blue,
171                     s, e)
172                 selection_all_attr_bg= pango.AttrBackground(
173                     selection_bg.red, selection_bg.green, selection_bg.blue,
174                     s, e)
175                 attrs.insert(selection_all_attr_fg)
176                 attrs.insert(selection_all_attr_bg)
177
178             pango_layout.set_text(u_text)
179             pango_layout.set_attributes(attrs)
180             drawingarea.window.draw_layout(
181                 gc, line.rectangle.x, line.rectangle.y + y_offset,
182                 pango_layout)
183
184
185 class ElementBoldText(ElementText):
186
187     def _get_attrs(self):
188         attrlist = pango.AttrList()
189         attr = pango.AttrWeight(pango.WEIGHT_BOLD,
190                                    end_index=0xffffff)
191         attrlist.insert(attr)
192         return attrlist
193
194
195 class ElementLink(ElementText):
196     
197     def __init__(self, text, href, pango_layout):
198         self.href = href
199         ElementText.__init__(self, text, pango_layout)
200
201     def _get_attrs(self):
202         attrlist = pango.AttrList()
203         attr = pango.AttrUnderline(pango.UNDERLINE_SINGLE,
204                                    end_index=0xffffff)
205         attrlist.insert(attr)
206         return attrlist
207
208
209 class ResLayout:
210 # represent one line
211
212     def __init__(self, left_margin, resnum, pango_layout):
213         self.element_list = [ElementEmpty()]
214         self.width = 0
215         self.height = 0
216         self.pango_layout = pango_layout
217         self.left_margin = left_margin
218         self.resnum = resnum
219         self.posY = 0
220         self.list_index = 0
221
222     def add_text(self, text, bold, href):
223         if isinstance(self.element_list[0], ElementEmpty):
224             self.element_list = []
225
226         if href:
227             element = ElementLink(text, href, self.pango_layout)
228             self.element_list.append(element)
229         elif bold:
230             element = ElementBoldText(text, self.pango_layout)
231             self.element_list.append(element)
232         else:
233             element = ElementText(text, self.pango_layout)
234             self.element_list.append(element)
235
236     def get_element_from_xy(self, x, y):
237         for element in self.element_list:
238             if element.is_on_xy(x, y):
239                 return element
240         return None
241
242     def get_close_element_from_xy(self, x, y):
243         x= max(x, self.left_margin)
244         element = self.get_element_from_xy(x, y)
245         if element is None and len(self.element_list) != 0:
246             element = self.element_list[len(self.element_list) - 1]
247         return element
248
249     def set_width(self, width):
250
251         self.width = width
252         
253         current_x = self.left_margin
254         current_y = 0
255
256         for element in self.element_list:
257             current_x, current_y = element.build_line_list(
258                 current_x, current_y, width, self.left_margin)
259
260         self.height = current_y + Line.HEIGHT
261
262     def get_pixel_size(self):
263         return self.width, self.height
264
265     def draw(self, drawingarea, x_offset, y_offset,
266              start_selection, end_selection):
267
268         s_s = start_selection
269         e_s = end_selection
270
271         s_l, s_e, s_i = s_s
272         e_l, e_e, e_i = e_s
273
274         if (s_l is None or s_e is None or s_i is None or
275             e_l is None or e_e is None or e_i is None or
276             self.posY < s_l.posY or self.posY > e_l.posY):
277
278             for element in self.element_list:
279                 element.draw(drawingarea, y_offset, self.pango_layout)
280
281         elif self.posY > s_s[0].posY and self.posY < e_s[0].posY:
282
283             for element in self.element_list:
284                 element.draw(drawingarea, y_offset, self.pango_layout,
285                              selection=True)
286
287         elif self == s_s[0] and self == e_s[0]:
288
289             selection = False
290
291             for element in self.element_list:
292                 if s_e == element:
293                     selection = True
294                     start = s_i
295                     end = 0xffffff
296                     if e_e == element:
297                         end = e_i
298                         selection = False
299                     element.draw(drawingarea, y_offset, self.pango_layout,
300                                  selection=True,
301                                  start_index=start,
302                                  end_index=end)
303                 elif e_e == element:
304                     end = e_i
305                     selection = False
306                     element.draw(drawingarea, y_offset, self.pango_layout,
307                                  selection=True, end_index=end)
308                 else:
309                     element.draw(drawingarea, y_offset, self.pango_layout,
310                                  selection=selection)
311
312         elif self == s_s[0]:
313
314             selection = False
315
316             for element in self.element_list:
317                 if s_e == element:
318                     selection = True
319                     start = s_i
320                     element.draw(drawingarea, y_offset, self.pango_layout,
321                                  selection=selection, start_index = start)
322                 else:
323                     element.draw(drawingarea, y_offset, self.pango_layout,
324                                  selection=selection)
325
326         elif self == e_s[0]:
327
328             selection = True
329
330             for element in self.element_list:
331                 if e_e == element:
332                     end = e_i
333                     element.draw(drawingarea, y_offset, self.pango_layout,
334                                  selection=selection, end_index=e_i)
335                     selection = False
336                 else:
337                     element.draw(drawingarea, y_offset, self.pango_layout,
338                                  selection=selection)
339
340         else:
341             for element in self.element_list:
342                 element.draw(drawingarea, y_offset, self.pango_layout)
343                 
344
345
346 class ThreadView(gtk.HBox):
347     hand_cursor = gtk.gdk.Cursor(gtk.gdk.HAND2)
348     regular_cursor = gtk.gdk.Cursor(gtk.gdk.XTERM)
349     arrow_cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)
350
351     def __init__(self):
352         gtk.HBox.__init__(self, False, 0)
353         self.drawingarea = gtk.DrawingArea()
354         self.vscrollbar = gtk.VScrollbar()
355         self.pack_start(self.drawingarea)
356         self.pack_start(self.vscrollbar, expand=False)
357         self.adjustment  = self.vscrollbar.get_adjustment()
358
359         self.drawingarea.add_events(
360             gtk.gdk.SCROLL_MASK |
361             gtk.gdk.POINTER_MOTION_MASK |
362             gtk.gdk.BUTTON_PRESS_MASK |
363             gtk.gdk.BUTTON_RELEASE_MASK)
364
365         self.drawingarea_prev_width = 0
366
367         self.drawingarea.connect(
368             "expose-event", self.on_drawingarea_expose_event)
369         self.drawingarea.connect(
370             "configure-event", self.on_drawingarea_configure_event)
371         self.drawingarea.connect(
372             "scroll-event", self.on_drawingarea_scroll_event)
373         self.drawingarea.connect(
374             "motion-notify-event", self.on_drawingrarea_motion_notify_event)
375         self.drawingarea.connect(
376             "button-press-event", self.on_drawingarea_button_press_event)
377         self.drawingarea.connect(
378             "button-release-event", self.on_drawingarea_button_release_event)
379         self.vscrollbar.connect(
380             "value-changed", self.on_vscrollbar_value_changed)
381
382         self.pango_layout = self.drawingarea.create_pango_layout("")
383
384         self.initialize_buffer()
385
386         self.on_uri_clicked = self._on_uri_clicked
387
388         self.button1_pressed = False
389         self.current_pressed_uri = None
390             
391         self.popupmenu = None
392         self.menu_openuri = None
393         self.menu_copylinkaddress = None
394         self.menu_separator_link = None
395         self.menu_copyselection = None
396         self.menu_openasuri = None
397         self.menu_separator_selection = None
398
399         self.menud_uri = None
400
401         # for selection
402         self.button_pressed_pt = (None, None, None)
403         self.button_moving_pt = (None, None, None)
404
405     def _on_uri_clicked(self, uri):
406         print uri, "clicked!!!!"
407
408     def initialize_buffer(self):
409         self.res_layout_list = []
410         self.layout_posY_map = [(0, [])]
411
412     def add_layout(self, res_layout):
413         if (len(self.res_layout_list) != 0):
414             last = self.res_layout_list[len(self.res_layout_list)-1]
415             x, y = last.get_pixel_size()
416             res_layout.posY = last.posY + y
417         self.set_layout_width(res_layout)
418         res_layout.list_index = len(self.res_layout_list)
419         self.res_layout_list.append(res_layout)
420
421         if len(self.layout_posY_map[len(self.layout_posY_map)-1][1]) == 128:
422             self.layout_posY_map.append((res_layout.posY, []))
423         self.layout_posY_map[len(self.layout_posY_map)-1][1].append(res_layout)
424
425         x, y = res_layout.get_pixel_size()
426         self.adjustment.upper = res_layout.posY + y
427         self.redraw()
428         self.change_vscrollbar_visible()
429
430     def create_res_layout(self, left_margin, resnum):
431         return ResLayout(left_margin, resnum, self.pango_layout)
432
433     def set_layout_width(self, layout):
434         width = self.drawingarea.allocation.width
435         layout.set_width(width)
436         
437     def redraw(self):
438         self.drawingarea.queue_draw()
439
440     def relayout(self):
441         self.layout_posY_map = [(0, [])]
442
443         width = self.drawingarea.allocation.width
444         sum_height = 0
445         for layout in self.res_layout_list:
446             layout.set_width(width)
447             layout.posY = sum_height
448             x, y = layout.get_pixel_size()
449             sum_height += y
450
451             if len(self.layout_posY_map[len(self.layout_posY_map)-1][1])==128:
452                 self.layout_posY_map.append((layout.posY, []))
453             self.layout_posY_map[len(self.layout_posY_map)-1][1].append(layout)
454
455         self.vscrollbar.set_range(0, sum_height)
456         self.change_vscrollbar_visible()
457
458     def change_vscrollbar_visible(self):
459         if self.adjustment.upper < self.adjustment.page_size:
460             self.vscrollbar.hide()
461         else:
462             self.vscrollbar.show()
463
464     def jump(self, value):
465         self.vscrollbar.set_value(value)
466
467     def jump_to_layout(self, layout):
468         self.jump(layout.posY)
469         
470     def jump_to_the_end(self):
471         value = self.adjustment.upper - self.adjustment.page_size
472         self.vscrollbar.set_value(value)
473
474     def jump_to_res(self, resnum):
475         for layout in self.res_layout_list:
476             if layout.resnum == resnum:
477                 self.jump_to_layout(layout)
478                 return True
479         return False
480
481
482     def _get_selection_start_end(self):
483         pressed_layout, pressed_element, pressed_index = self.button_pressed_pt
484         moving_layout, moving_element, moving_index = self.button_moving_pt
485
486         if (pressed_layout is None or pressed_element is None or
487             pressed_index is None or moving_layout is None or
488             moving_element is None or moving_index is None):
489             return (None, None, None), (None, None, None)
490         
491         if pressed_layout == moving_layout:
492             if pressed_element == moving_element:
493                 if moving_index < pressed_index:
494                     return self.button_moving_pt, self.button_pressed_pt
495             else:
496                 pressed_element_index = pressed_layout.element_list.index(
497                     pressed_element)
498                 moving_element_index = moving_layout.element_list.index(
499                     moving_element)
500                 if moving_element_index < pressed_element_index:
501                     return self.button_moving_pt, self.button_pressed_pt
502         elif moving_layout.posY < pressed_layout.posY:
503             return self.button_moving_pt, self.button_pressed_pt
504
505         return self.button_pressed_pt, self.button_moving_pt
506
507     def draw_viewport(self, area):
508         view_y = self.vscrollbar.get_value()
509         self.drawingarea.window.draw_rectangle(
510             self.drawingarea.style.base_gc[0],
511             True, area.x, area.y, area.width, area.height)
512
513         selection_start, selection_end = self._get_selection_start_end()
514
515
516         top_layout = self.get_layout_on_y(view_y)
517         index = 0
518         if top_layout:
519             index = top_layout.list_index
520         while index < len(self.res_layout_list):
521             layout = self.res_layout_list[index]
522             w, h = layout.get_pixel_size()
523             layout_top = layout.posY
524             layout_bottom = layout.posY + h
525             area_top = view_y + area.y
526             area_bottom = view_y + area.y + area.height
527             if layout_top <= area_bottom and layout_bottom >= area_top:
528                 layout.draw(self.drawingarea,
529                             0, layout.posY - int(view_y),
530                             selection_start, selection_end)
531             if layout_top > area_bottom:
532                 break
533
534             index += 1
535
536     def transform_coordinate_gdk_to_adj(self, y):
537         return y + self.vscrollbar.get_value()
538
539     def transform_coordinate_adj_to_layout(self, x, y, layout):
540         return x, y - layout.posY
541
542     def transform_coordinate_gdk_to_layout(self, x, y, layout):
543         return self.transform_coordinate_adj_to_layout(
544             x, self.transform_coordinate_gdk_to_adj(y), layout)
545
546     def get_layout_on_y(self, y):
547         layout_list = None
548         for pos, lay_lst in self.layout_posY_map:
549             if pos > y:
550                 break
551             layout_list = lay_lst
552         if layout_list:
553             layout = None
554             for lay in layout_list:
555                 if lay.posY > y:
556                     break
557                 layout = lay
558             return layout
559         return None
560
561     def ptrpos_to_layout(self, x, y):
562         # transform coordinate, GdkWindow -> adjustment
563         adj_y = self.transform_coordinate_gdk_to_adj(y)
564         return self.get_layout_on_y(adj_y)
565
566     def ptrpos_to_uri(self,  x, y):
567         # x, y is GdkWindow coordinate
568
569         layout = self.ptrpos_to_layout(x, y)
570
571         if layout is None:
572             return None, None, None
573
574         # transform coordinate, GdkWindow -> res_layout_list
575         lay_x, lay_y = self.transform_coordinate_gdk_to_layout(x, y, layout)
576
577         # xy -> element
578         element = layout.get_element_from_xy(lay_x, lay_y)
579         if isinstance(element, ElementLink):
580             return element.href, layout, element
581
582         return None, layout, None
583
584
585     def _set_button_pressed_pt(self, pt):
586         self.button_pressed_pt = (None, None, None)
587         if pt == None:
588             return
589
590         x, y = pt
591         layout = self.ptrpos_to_layout(x, y)
592         if layout is None:
593             return
594
595         x, y = self.transform_coordinate_gdk_to_layout(x, y, layout)
596         element = layout.get_element_from_xy(x, y)
597         if element is None:
598             element = layout.get_close_element_from_xy(x, y)
599
600         if element is None:
601             return
602
603         index = element.xy_to_index(x, y)
604         if index is None:
605             return
606
607         self.button_pressed_pt = (layout, element, index)
608
609     def _set_button_moving_pt(self, pt):
610         self.button_moving_pt = (None, None, None)
611         if pt == None:
612             return
613
614         x, y = pt
615         layout = self.ptrpos_to_layout(x, y)
616         if layout is None:
617             return
618
619         x, y = self.transform_coordinate_gdk_to_layout(x, y, layout)
620         element = layout.get_element_from_xy(x, y)
621         if element is None:
622             element = layout.get_close_element_from_xy(x, y)
623
624         if element is None:
625             return
626
627         index = element.xy_to_index(x, y)
628         if index is None:
629             return
630
631         self.button_moving_pt = (layout, element, index)
632
633     def on_drawingarea_expose_event(self, widget, event, data=None):
634         self.draw_viewport(event.area)
635
636     def on_drawingarea_configure_event(self, widget, event, data=None):
637         if event.width != self.drawingarea_prev_width:
638
639             # before relayout, find top layout on gdkwindow
640             top_layout = None
641             delta = 0
642             for lay in self.res_layout_list:
643                 if lay.posY > self.adjustment.value:
644                     break
645                 top_layout = lay
646
647             if top_layout is not None:
648                 delta = top_layout.posY - self.vscrollbar.get_value()
649
650             self.relayout()
651             self.drawingarea_prev_width = event.width
652
653             # after relayout, set vscrollbar.value to top layout's posY
654             if top_layout is not None:
655                 self.vscrollbar.set_value(top_layout.posY - delta)
656
657         self.adjustment.page_size = self.drawingarea.allocation.height
658         self.vscrollbar.set_increments(20, self.drawingarea.allocation.height)
659
660         # re-set 'value' for prevent overflow
661         self.vscrollbar.set_value(self.vscrollbar.get_value())
662         self.change_vscrollbar_visible()
663
664     def on_vscrollbar_value_changed(self, widget, data=None):
665         self.drawingarea.queue_draw()
666
667     def on_drawingarea_scroll_event(self, widget, event, data=None):
668         self.vscrollbar.emit("scroll-event", event)
669
670     def on_drawingrarea_motion_notify_event(self, widget, event, data=None):
671         if event.state & gtk.gdk.BUTTON1_MASK != gtk.gdk.BUTTON1_MASK:
672             self.button1_pressed = False
673
674         if self.button1_pressed:
675             old_lay, old_elem, old_idx = self.button_moving_pt
676             self._set_button_moving_pt((event.x, event.y))
677             new_lay, new_elem, new_idx = self.button_moving_pt
678             if (old_lay != new_lay
679                 or old_elem != new_elem
680                 or old_idx != new_idx):
681                 view_y = self.vscrollbar.get_value()
682                 o_y = old_lay.posY
683                 n_y = new_lay.posY
684                 o_width, o_height = old_lay.get_pixel_size()
685                 n_width, n_height = new_lay.get_pixel_size()
686
687                 y = min(o_y, n_y)
688                 height = max(o_y, n_y) - y
689                 if o_y > n_y: height += o_height
690                 else: height += n_height
691
692                 y -= view_y
693
694                 self.drawingarea.queue_draw_area(0, y, n_width, height+1)
695                 #self.drawingarea.window.process_updates(False)
696
697         cursor = ThreadView.regular_cursor
698
699         uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
700         if layout is None:
701             cursor = ThreadView.arrow_cursor
702         else:
703             if uri is not None and uri != "":
704                 cursor = ThreadView.hand_cursor
705
706         self.drawingarea.window.set_cursor(cursor)
707
708     def on_drawingarea_button_press_event(self, widget, event, data=None):
709         if event.button == 1:
710             self.current_pressed_uri = None
711             self.button1_pressed = True
712             uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
713             if uri is not None and layout is not None and element is not None:
714                 self.current_pressed_uri = (uri, layout, element)
715             else:
716                 self._set_button_moving_pt((event.x, event.y))
717                 self._set_button_pressed_pt((event.x, event.y))
718                 self.drawingarea.queue_draw()
719                 
720         elif event.button == 3:
721             time = event.time
722             uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
723             if uri is not None and layout is not None and element is not None:
724                 self.menu_openuri.show()
725                 self.menu_copylinkaddress.show()
726                 self.menu_separator_link.show()
727                 self.menu_openuri.uri = uri
728                 self.menu_copylinkaddress.uri = uri
729             else:
730                 self.menu_openuri.hide()
731                 self.menu_copylinkaddress.hide()
732                 self.menu_separator_link.hide()
733                 self.menu_openuri.uri = None
734                 self.menu_copylinkaddress.uri = None
735
736             self.menu_copyselection.hide()
737             self.menu_openasuri.hide()
738             self.menu_separator_selection.hide()
739
740             self.popupmenu.popup(None, None, None, event.button, time)
741             return True
742             
743
744     def on_drawingarea_button_release_event(self, widget, event, data=None):
745         if event.button == 1:
746             button1_pressed = self.button1_pressed
747             self.button1_pressed = False
748
749             if button1_pressed and self.current_pressed_uri is not None:
750                 uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
751                 p_uri, p_layout, p_element = self.current_pressed_uri
752                 self.current_preesed_uri = None
753                 if (uri == p_uri and layout == p_layout and
754                     element == p_element):
755                     self.on_uri_clicked(uri)