OSDN Git Service

57c1b1b35bf0ceb8170a8de24b3fbfd63b795c8e
[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
23
24 class ThreadView(gtk.HBox):
25     hand_cursor = gtk.gdk.Cursor(gtk.gdk.HAND2)
26     regular_cursor = gtk.gdk.Cursor(gtk.gdk.XTERM)
27     arrow_cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)
28
29     def __init__(self):
30         gtk.HBox.__init__(self, False, 0)
31         self.drawingarea = gtk.DrawingArea()
32         self.vscrollbar = gtk.VScrollbar()
33         self.pack_start(self.drawingarea)
34         self.pack_start(self.vscrollbar, expand=False)
35         self.adjustment  = self.vscrollbar.get_adjustment()
36
37         self.drawingarea.add_events(
38             gtk.gdk.SCROLL_MASK |
39             gtk.gdk.POINTER_MOTION_MASK |
40             gtk.gdk.BUTTON_PRESS_MASK |
41             gtk.gdk.BUTTON_RELEASE_MASK)
42
43         self.drawingarea_prev_width = 0
44
45         self.drawingarea.connect(
46             "expose-event", self.on_drawingarea_expose_event)
47         self.drawingarea.connect(
48             "configure-event", self.on_drawingarea_configure_event)
49         self.drawingarea.connect(
50             "scroll-event", self.on_drawingarea_scroll_event)
51         self.drawingarea.connect(
52             "motion-notify-event", self.on_drawingrarea_motion_notify_event)
53         self.drawingarea.connect(
54             "button-press-event", self.on_drawingarea_button_press_event)
55         self.drawingarea.connect(
56             "button-release-event", self.on_drawingarea_button_release_event)
57         self.vscrollbar.connect(
58             "value-changed", self.on_vscrollbar_value_changed)
59
60         self.initialize_buffer()
61
62         self.on_uri_clicked = self._on_uri_clicked
63
64         self.button1_pressed = False
65         self.current_pressed_uri = None
66             
67         self.popupmenu = None
68         self.menu_openuri = None
69         self.menu_copylinkaddress = None
70         self.menu_separator_link = None
71         self.menu_copyselection = None
72         self.menu_openasuri = None
73         self.menu_separator_selection = None
74
75         self.menud_uri = None
76
77     def _on_uri_clicked(self, uri):
78         print uri, "clicked!!!!"
79
80     def initialize_buffer(self):
81         self.pangolayout = []
82
83     def add_layout(self, pangolayout):
84         if (len(self.pangolayout) != 0):
85             last = self.pangolayout[len(self.pangolayout)-1]
86             x, y = last.get_pixel_size()
87             pangolayout.posY = last.posY + y
88         self.set_layout_width(pangolayout)
89         self.pangolayout.append(pangolayout)
90         x, y = pangolayout.get_pixel_size()
91         self.vscrollbar.set_range(0, pangolayout.posY + y)
92         self.redraw()
93         self.change_vscrollbar_visible()
94
95     def create_pango_layout(self, text):
96         return self.drawingarea.create_pango_layout(text)
97
98     def set_layout_width(self, layout):
99         width = self.drawingarea.allocation.width
100         layout.set_width((width - layout.marginleft) * pango.SCALE)
101         
102     def redraw(self):
103         self.drawingarea.queue_draw()
104
105     def relayout(self):
106         width = self.drawingarea.allocation.width
107         sum_height = 0
108         for layout in self.pangolayout:
109             layout.set_width((width - layout.marginleft) * pango.SCALE)
110             layout.posY = sum_height
111             x, y = layout.get_pixel_size()
112             sum_height += y
113         self.vscrollbar.set_range(0, sum_height)
114         self.change_vscrollbar_visible()
115
116     def change_vscrollbar_visible(self):
117         if self.adjustment.upper < self.adjustment.page_size:
118             self.vscrollbar.hide()
119         else:
120             self.vscrollbar.show()
121
122     def jump(self, value):
123         self.vscrollbar.set_value(value)
124
125     def jump_to_layout(self, layout):
126         self.jump(layout.posY)
127         
128     def jump_to_the_end(self):
129         value = self.adjustment.upper - self.adjustment.page_size
130         self.vscrollbar.set_value(value)
131
132     def jump_to_res(self, resnum):
133         for layout in self.pangolayout:
134             if layout.resnum == resnum:
135                 self.jump_to_layout(layout)
136                 return True
137         return False
138
139     def draw_viewport(self):
140         view_y = self.vscrollbar.get_value()
141         self.drawingarea.window.draw_rectangle(
142             self.drawingarea.style.base_gc[0],
143             True, 0, 0,
144             self.drawingarea.allocation.width,
145             self.drawingarea.allocation.height)
146
147         gc = self.drawingarea.window.new_gc()
148         for layout in self.pangolayout:
149             w, h = layout.get_pixel_size()
150             layout_top = layout.posY
151             layout_bottom = layout.posY + h
152             area_top = view_y
153             area_bottom = view_y + self.drawingarea.allocation.height
154             if layout_top <= area_bottom and layout_bottom >= area_top:
155                 self.drawingarea.window.draw_layout(
156                     gc, layout.marginleft, layout.posY - int(view_y), layout)
157
158     def transform_coordinate_gdk_to_adj(self, y):
159         return y + self.vscrollbar.get_value()
160
161     def transform_coordinate_adj_to_layout(self, x, y, layout):
162         return x - layout.marginleft, y - layout.posY
163
164     def transform_coordinate_gdk_to_layout(self, x, y, layout):
165         return self.transform_coordinate_adj_to_layout(
166             x, self.transform_coordinate_gdk_to_adj(y), layout)
167
168     def ptrpos_to_layout(self, x, y):
169         # transform coordinate, GdkWindow -> adjustment
170         adj_x = x
171         adj_y = self.transform_coordinate_gdk_to_adj(y)
172         for lay in self.pangolayout:
173             width, height = lay.get_pixel_size()
174             if (adj_y >= lay.posY and adj_y < lay.posY + height and
175                 adj_x >= lay.marginleft):
176                 return lay
177         return None
178
179     def ptrpos_to_uri(self,  x, y):
180         # x, y is GdkWindow coordinate
181
182         layout = self.ptrpos_to_layout(x, y)
183
184         if layout is None:
185             return None, None, None
186
187         # transform coordinate, GdkWindow -> pangolayout
188         lay_x, lay_y = self.transform_coordinate_gdk_to_layout(x, y, layout)
189
190         # xy -> index
191         idx, clk = layout.xy_to_index(
192             int(lay_x)*pango.SCALE, int(lay_y)*pango.SCALE)
193
194         x, y, width, height = layout.index_to_pos(idx)
195         x /= pango.SCALE
196         y /= pango.SCALE
197         width /= pango.SCALE
198         height /= pango.SCALE
199         if (lay_x >= x and lay_x < x + width and
200             lay_y >= y and lay_y < y + height):
201
202             for i, (start, end, href) in enumerate(layout.urilist):
203                 if idx >= start and idx < end:
204                     return href, layout, i
205
206         return None, layout, None
207
208     def on_drawingarea_expose_event(self, widget, event, data=None):
209         self.draw_viewport()
210
211     def on_drawingarea_configure_event(self, widget, event, data=None):
212         if event.width != self.drawingarea_prev_width:
213             self.relayout()
214             self.drawingarea_prev_width = event.width
215         self.adjustment.page_size = self.drawingarea.allocation.height
216         self.vscrollbar.set_increments(20, self.drawingarea.allocation.height)
217
218         # re-set 'value' for prevent overflow
219         self.vscrollbar.set_value(self.vscrollbar.get_value())
220         self.change_vscrollbar_visible()
221
222     def on_vscrollbar_value_changed(self, widget, data=None):
223         self.drawingarea.queue_draw()
224
225     def on_drawingarea_scroll_event(self, widget, event, data=None):
226         self.vscrollbar.emit("scroll-event", event)
227
228     def on_drawingrarea_motion_notify_event(self, widget, event, data=None):
229         if event.state & gtk.gdk.BUTTON1_MASK != gtk.gdk.BUTTON1_MASK:
230             self.button1_pressed = False
231
232         cursor = ThreadView.regular_cursor
233
234         uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
235         if layout is None:
236             cursor = ThreadView.arrow_cursor
237         else:
238             if uri is not None and uri != "":
239                 cursor = ThreadView.hand_cursor
240
241         self.drawingarea.window.set_cursor(cursor)
242
243     def on_drawingarea_button_press_event(self, widget, event, data=None):
244         if event.button == 1:
245             self.current_pressed_uri = None
246             self.button1_pressed = True
247             uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
248             if uri is not None and layout is not None and index is not None:
249                 self.current_pressed_uri = (uri, layout, index)
250         elif event.button == 3:
251             time = event.time
252             uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
253             if uri is not None and layout is not None and index is not None:
254                 self.menu_openuri.show()
255                 self.menu_copylinkaddress.show()
256                 self.menu_separator_link.show()
257                 self.menu_openuri.uri = uri
258                 self.menu_copylinkaddress.uri = uri
259             else:
260                 self.menu_openuri.hide()
261                 self.menu_copylinkaddress.hide()
262                 self.menu_separator_link.hide()
263                 self.menu_openuri.uri = None
264                 self.menu_copylinkaddress.uri = None
265
266             self.menu_copyselection.hide()
267             self.menu_openasuri.hide()
268             self.menu_separator_selection.hide()
269
270             self.popupmenu.popup(None, None, None, event.button, time)
271             return True
272             
273
274     def on_drawingarea_button_release_event(self, widget, event, data=None):
275         if event.button == 1:
276             button1_pressed = self.button1_pressed
277             self.button1_pressed = False
278
279             if button1_pressed and self.current_pressed_uri is not None:
280                 uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
281                 p_uri, p_layout, p_index = self.current_pressed_uri
282                 self.current_preesed_uri = None
283                 if uri == p_uri and layout == p_layout and index == p_index:
284                     self.on_uri_clicked(uri)