OSDN Git Service

Improvement of thread view popup functionality. Appropriate size and position.
[fukui-no-namari/fukui-no-namari.git] / src / FukuiNoNamari / thread_popup.py
1
2 import gtk
3 import gobject
4 import re
5 import urlparse
6 import copy
7 import thread_view
8
9 class ThreadPopup(gobject.GObject):
10     '''
11     classdocs
12     '''
13
14     __gsignals__ = {
15         "uri-clicked-event":
16         (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (object, object,))
17         }
18
19     _INTERVAL = 100
20
21     def __init__(self, bbs_type):
22         gobject.GObject.__init__(self)        
23         self._thread_view_list = []
24         self._bbs_type = bbs_type
25         self._timer_started = False
26
27     def push_thread_view(self, threadview):
28         threadview.connect("cursor-over-link-event", self.on_cursor_over_link)
29         self._thread_view_list.append(threadview)
30
31     def pop_thread_view(self):
32         view = self._thread_view_list.pop()
33         top = view.get_toplevel()
34         top.destroy()
35
36     def _collapse(self, threadview):
37         length = len(self._thread_view_list)
38         for idx in range(length):
39             idx = length - idx - 1
40             view = self._thread_view_list[idx]
41             if view == threadview:
42                 break
43             self.pop_thread_view()
44
45     def _is_thread_view_top(self, threadview):
46         listnum = len(self._thread_view_list)
47         if listnum == 0:
48             return False
49         last = self._thread_view_list[listnum-1]
50         return threadview == last
51
52     def on_cursor_over_link(self, widget, event, uri):
53         orig_uri = uri
54         if not uri.startswith("http://"):
55             # maybe a relative uri.
56             uri = urlparse.urljoin(self._bbs_type.get_uri_base(), uri)
57
58         strict_uri = self._bbs_type.get_thread_uri()
59         idx = self._thread_view_list.index(widget)
60         if idx != len(self._thread_view_list)-1 and orig_uri == self._thread_view_list[idx+1].get_toplevel().uri:
61             return
62         if uri != strict_uri and uri.startswith(strict_uri):
63             resnum = uri[len(strict_uri):]
64             match = re.match("\d+", resnum)
65             if match:
66                 resnum = int(match.group())
67                 if self._check_to_need_hint(widget, resnum):
68                     self._show_hint(widget, resnum, orig_uri)
69
70     def _get_owner(self, gdk_win):
71         top = gdk_win.get_toplevel()
72         for threadview in self._thread_view_list:
73             if threadview.window.get_toplevel() == top:
74                 return threadview
75         return None
76
77     def on_timeout(self):
78         ret = gtk.gdk.window_at_pointer()
79         if ret is None:
80             # print "unknown window"
81             self._collapse(self._thread_view_list[0])
82             self._timer_started = False
83             return False
84
85         gdk_win, x, y = ret
86         threadview = self._get_owner(gdk_win)
87         if threadview is None:
88             # print "unknown gdk window"
89             self._collapse(self._thread_view_list[0])
90             self._timer_started = False
91             return False
92
93         length = len(self._thread_view_list)
94         if length == 1:
95             # print "no popup window"
96             self._collapse(threadview)
97             self._timer_started = False
98             return False
99
100         if self._is_thread_view_top(threadview):
101             return True
102
103         uri, layout, element = threadview.ptrpos_to_uri(x, y)
104         view = self._thread_view_list[self._thread_view_list.index(threadview)+1]           
105         if layout is None or uri is None or uri != view.get_toplevel().uri:
106             self._collapse(threadview)
107
108         return True
109
110     def _show_hint(self, threadview, num, uri):
111         top = threadview.get_toplevel()
112         relative_x, relative_y = top.get_pointer()
113         gdk_win_x, gdk_win_y = top.window.get_origin()
114         abs_x = gdk_win_x + relative_x
115         abs_y = gdk_win_y + relative_y
116  
117         popupwin = gtk.Window(gtk.WINDOW_POPUP)
118
119         view = thread_view.ThreadView()
120         view.connect("uri-clicked-event", self.on_threadview_uri_clicked)
121         self.push_thread_view(view)
122
123         # set width
124         view.drawingarea.size_allocate(gtk.gdk.Rectangle(0, 0, 300, 200))
125
126         for layout in self._thread_view_list[0].res_layout_list:
127             # skip empty line
128             if isinstance(layout.element_list[0], thread_view.ElementEmpty):
129                 continue
130             if layout.resnum == num:
131                 clone = layout.clone()
132                 view.add_layout(clone)             
133         popupwin.view = view
134         popupwin.uri = uri
135         popupwin.add(view)
136         popupwin.set_property("border_width", 1)
137
138         view.drawingarea.size_allocate(
139             gtk.gdk.Rectangle(0, 0, 300, int(min(200, view.adjustment.upper))))
140
141         popupwin.set_default_size(view.drawingarea.allocation.width,
142                                   view.drawingarea.allocation.height+2)
143
144         view.show_all()
145         view.vscrollbar.hide()
146
147         # show the popup at an appropriate position.
148         width, height = popupwin.get_size()
149         if abs_x + width > gtk.gdk.screen_width():
150             abs_x = abs_x - width
151         if abs_y + height > gtk.gdk.screen_height():
152             abs_y = abs_y - height
153         popupwin.move(abs_x, abs_y)
154
155         popupwin.show()
156
157         if not self._timer_started:
158             self._timer_started = True
159             gobject.timeout_add(ThreadPopup._INTERVAL, self.on_timeout)
160
161     def _check_to_need_hint(self, threadview, num):
162         if self._is_thread_view_top(threadview):
163             return True
164
165     def on_threadview_uri_clicked(self, widget, uri):
166         self.emit("uri-clicked-event", widget, uri)