OSDN Git Service

change the mouse cursor according to text area, uri area and non text area.
[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
41         self.adjustment.step_increment = 20
42         self.drawingarea_prev_width = 0
43
44         self.drawingarea.connect(
45             "expose-event", self.on_drawingarea_expose_event)
46         self.drawingarea.connect(
47             "size-allocate", self.on_drawingarea_size_allocate)
48         self.drawingarea.connect(
49             "button-press-event", self.on_drawingarea_button_press_event)
50         self.drawingarea.connect(
51             "scroll-event", self.on_drawingarea_scroll_event)
52         self.drawingarea.connect(
53             "motion-notify-event", self.on_drawingrarea_motion_notify_event)
54         self.vscrollbar.connect(
55             "value-changed", self.on_vscrollbar_value_changed)
56
57         self.initialize_buffer()
58
59     def initialize_buffer(self):
60         self.pangolayout = []
61
62     def add_layout(self, pangolayout):
63         if (len(self.pangolayout) != 0):
64             last = self.pangolayout[len(self.pangolayout)-1]
65             x, y = last.get_pixel_size()
66             pangolayout.posY = last.posY + y
67         self.set_layout_width(pangolayout)
68         self.pangolayout.append(pangolayout)
69         x, y = pangolayout.get_pixel_size()
70         self.adjustment.upper = pangolayout.posY + y
71         self.prevent_adjustment_overflow()
72         self.redraw()
73         self.change_vscrollbar_visible()
74
75     def create_pango_layout(self, text):
76         return self.drawingarea.create_pango_layout(text)
77
78     def set_layout_width(self, layout):
79         width = self.drawingarea.allocation.width
80         layout.set_width((width - layout.marginleft) * pango.SCALE)
81         
82     def redraw(self):
83         self.drawingarea.queue_draw()
84
85     def relayout(self):
86         width = self.drawingarea.allocation.width
87         sum_height = 0
88         for layout in self.pangolayout:
89             layout.set_width((width - layout.marginleft) * pango.SCALE)
90             layout.posY = sum_height
91             x, y = layout.get_pixel_size()
92             sum_height += y
93         self.adjustment.upper = sum_height
94         self.prevent_adjustment_overflow()
95         self.change_vscrollbar_visible()
96
97     def change_vscrollbar_visible(self):
98         if self.adjustment.upper < self.adjustment.page_size:
99             self.vscrollbar.hide()
100         else:
101             self.vscrollbar.show()
102
103     def jump(self, value):
104         if value > self.adjustment.upper - self.adjustment.page_size:
105             value = self.adjustment.upper - self.adjustment.page_size
106         self.adjustment.set_value(value)
107
108     def jump_to_layout(self, layout):
109         self.jump(layout.posY)
110         
111     def jump_to_the_end(self):
112         value = self.adjustment.upper - self.adjustment.page_size
113         self.adjustment.set_value(value)
114
115     def jump_to_res(self, resnum):
116         for layout in self.pangolayout:
117             if layout.resnum == resnum:
118                 self.jump_to_layout(layout)
119                 return True
120         return False
121
122     def draw_viewport(self):
123         view_y = self.adjustment.get_value()
124         self.drawingarea.window.draw_rectangle(
125             self.drawingarea.style.base_gc[0],
126             True, 0, 0,
127             self.drawingarea.allocation.width,
128             self.drawingarea.allocation.height)
129
130         gc = self.drawingarea.window.new_gc()
131         for layout in self.pangolayout:
132             w, h = layout.get_pixel_size()
133             layout_top = layout.posY
134             layout_bottom = layout.posY + h
135             area_top = view_y
136             area_bottom = view_y + self.drawingarea.allocation.height
137             if layout_top <= area_bottom and layout_bottom >= area_top:
138                 self.drawingarea.window.draw_layout(
139                     gc, layout.marginleft, layout.posY - int(view_y), layout)
140
141     def transform_coordinate_gdk_to_adj(self, y):
142         return y + self.adjustment.value
143
144     def transform_coordinate_adj_to_layout(self, x, y, layout):
145         return x - layout.marginleft, y - layout.posY
146
147     def transform_coordinate_gdk_to_layout(self, x, y, layout):
148         return self.transform_coordinate_adj_to_layout(
149             x, self.transform_coordinate_gdk_to_adj(y), layout)
150
151     def ptrpos_to_layout(self, x, y):
152         # transform coordinate, GdkWindow -> adjustment
153         adj_x = x
154         adj_y = self.transform_coordinate_gdk_to_adj(y)
155         for lay in self.pangolayout:
156             width, height = lay.get_pixel_size()
157             if (adj_y >= lay.posY and adj_y < lay.posY + height and
158                 adj_x >= lay.marginleft):
159                 return lay
160         return None
161
162     def ptrpos_to_uri(self,  x, y):
163         # x, y is GdkWindow coordinate
164
165         layout = self.ptrpos_to_layout(x, y)
166
167         if layout is None:
168             return None, None, None
169
170         # transform coordinate, GdkWindow -> pangolayout
171         lay_x, lay_y = self.transform_coordinate_gdk_to_layout(x, y, layout)
172
173         # xy -> index
174         idx, clk = layout.xy_to_index(
175             int(lay_x)*pango.SCALE, int(lay_y)*pango.SCALE)
176
177         x, y, width, height = layout.index_to_pos(idx)
178         x /= pango.SCALE
179         y /= pango.SCALE
180         width /= pango.SCALE
181         height /= pango.SCALE
182         if (lay_x >= x and lay_x < x + width and
183             lay_y >= y and lay_y < y + height):
184
185             for i, (start, end, href) in enumerate(layout.urilist):
186                 if idx >= start and idx < end:
187                     return href, layout, i
188
189         return None, layout, None
190
191     def on_drawingarea_expose_event(self, widget, event, data=None):
192         self.draw_viewport()
193
194     def on_drawingarea_size_allocate(self, widget, allocation, data=None):
195         if allocation.width != self.drawingarea_prev_width:
196             self.relayout()
197             self.drawingarea_prev_width = allocation.width
198         self.adjustment.page_size = self.drawingarea.allocation.height
199         self.adjustment.page_increment = self.drawingarea.allocation.height
200
201         self.prevent_adjustment_overflow()
202         self.change_vscrollbar_visible()
203
204     def prevent_adjustment_overflow(self):
205         if (self.adjustment.value >
206             self.adjustment.upper - self.adjustment.page_size):
207             self.adjustment.value = (self.adjustment.upper -
208                                      self.adjustment.page_size)
209
210         if self.adjustment.value < 0:
211             self.adjustment.value = 0
212
213     def on_drawingarea_button_press_event(self, widget, event, data=None):
214         self.drawingarea.queue_draw()
215
216     def on_vscrollbar_value_changed(self, widget, data=None):
217         self.drawingarea.queue_draw()
218
219     def on_drawingarea_scroll_event(self, widget, event, data=None):
220         if event.direction == gtk.gdk.SCROLL_UP:
221             self.adjustment.value -= 66.476200804
222             if self.adjustment.value < self.adjustment.lower:
223                 self.adjustment.value = self.adjustment.lower
224         if event.direction == gtk.gdk.SCROLL_DOWN:
225             self.adjustment.value += 66.476200804
226             max_value = self.adjustment.upper - self.adjustment.page_size
227             if self.adjustment.value > max_value:
228                 self.adjustment.value = max_value
229         self.prevent_adjustment_overflow()
230
231     def on_drawingrarea_motion_notify_event(self, widget, event, data=None):
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)