1 # Copyright (C) 2007 by Aiwota Programmer
2 # aiwotaprog@tetteke.tk
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.
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.
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
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)
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()
37 self.drawingarea.add_events(
39 gtk.gdk.POINTER_MOTION_MASK |
40 gtk.gdk.BUTTON_PRESS_MASK |
41 gtk.gdk.BUTTON_RELEASE_MASK)
43 self.adjustment.step_increment = 20
44 self.drawingarea_prev_width = 0
46 self.drawingarea.connect(
47 "expose-event", self.on_drawingarea_expose_event)
48 self.drawingarea.connect(
49 "size-allocate", self.on_drawingarea_size_allocate)
50 self.drawingarea.connect(
51 "button-press-event", self.on_drawingarea_button_press_event)
52 self.drawingarea.connect(
53 "scroll-event", self.on_drawingarea_scroll_event)
54 self.drawingarea.connect(
55 "motion-notify-event", self.on_drawingrarea_motion_notify_event)
56 self.drawingarea.connect(
57 "button-press-event", self.on_drawingrarea_button_press_event)
58 self.drawingarea.connect(
59 "button-release-event", self.on_drawingrarea_button_release_event)
60 self.vscrollbar.connect(
61 "value-changed", self.on_vscrollbar_value_changed)
63 self.initialize_buffer()
65 self.on_uri_clicked = self._on_uri_clicked
67 self.button1_pressed = False
68 self.current_pressed_uri = None
71 self.menu_openuri = None
72 self.menu_copylinkaddress = None
73 self.menu_separator_link = None
74 self.menu_copyselection = None
75 self.menu_openasuri = None
76 self.menu_separator_selection = None
80 def _on_uri_clicked(self, uri):
81 print uri, "clicked!!!!"
83 def initialize_buffer(self):
86 def add_layout(self, pangolayout):
87 if (len(self.pangolayout) != 0):
88 last = self.pangolayout[len(self.pangolayout)-1]
89 x, y = last.get_pixel_size()
90 pangolayout.posY = last.posY + y
91 self.set_layout_width(pangolayout)
92 self.pangolayout.append(pangolayout)
93 x, y = pangolayout.get_pixel_size()
94 self.adjustment.upper = pangolayout.posY + y
95 self.prevent_adjustment_overflow()
97 self.change_vscrollbar_visible()
99 def create_pango_layout(self, text):
100 return self.drawingarea.create_pango_layout(text)
102 def set_layout_width(self, layout):
103 width = self.drawingarea.allocation.width
104 layout.set_width((width - layout.marginleft) * pango.SCALE)
107 self.drawingarea.queue_draw()
110 width = self.drawingarea.allocation.width
112 for layout in self.pangolayout:
113 layout.set_width((width - layout.marginleft) * pango.SCALE)
114 layout.posY = sum_height
115 x, y = layout.get_pixel_size()
117 self.adjustment.upper = sum_height
118 self.prevent_adjustment_overflow()
119 self.change_vscrollbar_visible()
121 def change_vscrollbar_visible(self):
122 if self.adjustment.upper < self.adjustment.page_size:
123 self.vscrollbar.hide()
125 self.vscrollbar.show()
127 def jump(self, value):
128 if value > self.adjustment.upper - self.adjustment.page_size:
129 value = self.adjustment.upper - self.adjustment.page_size
130 self.adjustment.set_value(value)
132 def jump_to_layout(self, layout):
133 self.jump(layout.posY)
135 def jump_to_the_end(self):
136 value = self.adjustment.upper - self.adjustment.page_size
137 self.adjustment.set_value(value)
139 def jump_to_res(self, resnum):
140 for layout in self.pangolayout:
141 if layout.resnum == resnum:
142 self.jump_to_layout(layout)
146 def draw_viewport(self):
147 view_y = self.adjustment.get_value()
148 self.drawingarea.window.draw_rectangle(
149 self.drawingarea.style.base_gc[0],
151 self.drawingarea.allocation.width,
152 self.drawingarea.allocation.height)
154 gc = self.drawingarea.window.new_gc()
155 for layout in self.pangolayout:
156 w, h = layout.get_pixel_size()
157 layout_top = layout.posY
158 layout_bottom = layout.posY + h
160 area_bottom = view_y + self.drawingarea.allocation.height
161 if layout_top <= area_bottom and layout_bottom >= area_top:
162 self.drawingarea.window.draw_layout(
163 gc, layout.marginleft, layout.posY - int(view_y), layout)
165 def transform_coordinate_gdk_to_adj(self, y):
166 return y + self.adjustment.value
168 def transform_coordinate_adj_to_layout(self, x, y, layout):
169 return x - layout.marginleft, y - layout.posY
171 def transform_coordinate_gdk_to_layout(self, x, y, layout):
172 return self.transform_coordinate_adj_to_layout(
173 x, self.transform_coordinate_gdk_to_adj(y), layout)
175 def ptrpos_to_layout(self, x, y):
176 # transform coordinate, GdkWindow -> adjustment
178 adj_y = self.transform_coordinate_gdk_to_adj(y)
179 for lay in self.pangolayout:
180 width, height = lay.get_pixel_size()
181 if (adj_y >= lay.posY and adj_y < lay.posY + height and
182 adj_x >= lay.marginleft):
186 def ptrpos_to_uri(self, x, y):
187 # x, y is GdkWindow coordinate
189 layout = self.ptrpos_to_layout(x, y)
192 return None, None, None
194 # transform coordinate, GdkWindow -> pangolayout
195 lay_x, lay_y = self.transform_coordinate_gdk_to_layout(x, y, layout)
198 idx, clk = layout.xy_to_index(
199 int(lay_x)*pango.SCALE, int(lay_y)*pango.SCALE)
201 x, y, width, height = layout.index_to_pos(idx)
205 height /= pango.SCALE
206 if (lay_x >= x and lay_x < x + width and
207 lay_y >= y and lay_y < y + height):
209 for i, (start, end, href) in enumerate(layout.urilist):
210 if idx >= start and idx < end:
211 return href, layout, i
213 return None, layout, None
215 def on_drawingarea_expose_event(self, widget, event, data=None):
218 def on_drawingarea_size_allocate(self, widget, allocation, data=None):
219 if allocation.width != self.drawingarea_prev_width:
221 self.drawingarea_prev_width = allocation.width
222 self.adjustment.page_size = self.drawingarea.allocation.height
223 self.adjustment.page_increment = self.drawingarea.allocation.height
225 self.prevent_adjustment_overflow()
226 self.change_vscrollbar_visible()
228 def prevent_adjustment_overflow(self):
229 if (self.adjustment.value >
230 self.adjustment.upper - self.adjustment.page_size):
231 self.adjustment.value = (self.adjustment.upper -
232 self.adjustment.page_size)
234 if self.adjustment.value < 0:
235 self.adjustment.value = 0
237 def on_drawingarea_button_press_event(self, widget, event, data=None):
238 self.drawingarea.queue_draw()
240 def on_vscrollbar_value_changed(self, widget, data=None):
241 self.drawingarea.queue_draw()
243 def on_drawingarea_scroll_event(self, widget, event, data=None):
244 self.vscrollbar.emit("scroll-event", event)
246 def on_drawingrarea_motion_notify_event(self, widget, event, data=None):
247 if event.state & gtk.gdk.BUTTON1_MASK != gtk.gdk.BUTTON1_MASK:
248 self.button1_pressed = False
250 cursor = ThreadView.regular_cursor
252 uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
254 cursor = ThreadView.arrow_cursor
256 if uri is not None and uri != "":
257 cursor = ThreadView.hand_cursor
259 self.drawingarea.window.set_cursor(cursor)
261 def on_drawingrarea_button_press_event(self, widget, event, data=None):
262 if event.button == 1:
263 self.current_pressed_uri = None
264 self.button1_pressed = True
265 uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
266 if uri is not None and layout is not None and index is not None:
267 self.current_pressed_uri = (uri, layout, index)
268 elif event.button == 3:
270 uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
271 if uri is not None and layout is not None and index is not None:
272 self.menu_openuri.show()
273 self.menu_copylinkaddress.show()
274 self.menu_separator_link.show()
275 self.menu_openuri.uri = uri
276 self.menu_copylinkaddress.uri = uri
278 self.menu_openuri.hide()
279 self.menu_copylinkaddress.hide()
280 self.menu_separator_link.hide()
281 self.menu_openuri.uri = None
282 self.menu_copylinkaddress.uri = None
284 self.menu_copyselection.hide()
285 self.menu_openasuri.hide()
286 self.menu_separator_selection.hide()
288 self.popupmenu.popup(None, None, None, event.button, time)
292 def on_drawingrarea_button_release_event(self, widget, event, data=None):
293 if event.button == 1:
294 button1_pressed = self.button1_pressed
295 self.button1_pressed = False
297 if button1_pressed and self.current_pressed_uri is not None:
298 uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
299 p_uri, p_layout, p_index = self.current_pressed_uri
300 self.current_preesed_uri = None
301 if uri == p_uri and layout == p_layout and index == p_index:
302 self.on_uri_clicked(uri)