OSDN Git Service

aaed13fee36fec6f61fd4b3a4f768fb19c6e09e9
[fukui-no-namari/fukui-no-namari.git] / src / FukuiNoNamari / ThreadViewBase / element.py
1 # Copyright (C) 2009 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 import itertools
23
24 def get_approximate_char_height(pango_context):
25     desc = pango_context.get_font_description()
26     font = pango_context.load_font(desc)
27     ink, log = font.get_glyph_extents(0)
28     return log[3] / pango.SCALE + 2
29
30
31 class Rectangle:
32     def __init__(self, x, y, width, height):
33         self.x = x
34         self.y = y
35         self.width = width
36         self.height = height
37
38
39 class Line:
40
41     HEIGHT = 15
42
43     def __init__(self, start_index, end_index, rectangle):
44         self.start_index = start_index
45         self.end_index = end_index
46         self.rectangle = rectangle
47
48     def is_on_xy(self, x, y):
49         left = self.rectangle.x
50         right = left + self.rectangle.width
51         top = self.rectangle.y
52         bottom = top + self.rectangle.height
53         return x >= left and x < right and y >= top and y < bottom
54         
55
56 class ElementEmpty:
57
58     def __init__(self, pango_layout):
59         self.pango_layout = pango_layout
60         self.initialize()
61
62     def recalc_char_widths(self):
63         pass
64         
65     def initialize(self):
66         self.line_list = []
67
68     def is_on_xy(self, x, y):
69         for line in self.line_list:
70             if line.is_on_xy(x, y):
71                 return True
72         return False
73
74     def xy_to_index(self, x, y):
75         return 0
76
77     def build_line_list(self, x, y, width, left_margin):
78         self.initialize()
79
80         line = Line(0, 0, Rectangle(
81             x, y, width - x,
82             get_approximate_char_height(self.pango_layout.get_context())))
83         self.line_list.append(line)
84
85         return width, y
86
87     def get_text(self, selection=False, start_index=0, end_index=0xffffff):
88         return ""
89
90     def draw(self, drawingarea, y_offset, pango_layout,
91              selection=False, start_index=0, end_index=0xffffff):
92         pass
93
94     
95 class ElementText:
96
97     ch_width_dict = {}   # key: char, value: width
98
99     def __init__(self, text, pango_layout):
100         self.text = text
101         self.pango_layout = pango_layout
102
103         self.recalc_char_widths()
104
105         self.line_list = []
106
107     def recalc_char_widths(self):
108         self.widths = [i for i in itertools.repeat(0, len(self.text))]
109
110         dict = self._get_ch_width_dict()
111         for index, ch in enumerate(self.text):
112             if ch not in dict:
113                 self.pango_layout.set_attributes(self._get_attrs())
114                 self.pango_layout.set_text(ch)
115                 a, logi = self.pango_layout.get_extents()
116                 width = 1.0 * Rectangle(*logi).width / pango.SCALE
117                 self.widths[index] = width
118                 dict[ch] = width
119             else:
120                 width = dict[ch]
121                 self.widths[index] = width
122
123     def _get_ch_width_dict(self):
124         return ElementText.ch_width_dict
125
126     def _get_attrs(self):
127         attrs = pango.AttrList()
128         return attrs
129
130     def is_on_xy(self, x, y):
131         for line in self.line_list:
132             if line.is_on_xy(x, y):
133                 return True
134         return False
135
136     def xy_to_index(self, x, y):
137         for line in self.line_list:
138             top = line.rectangle.y
139             bottom = top + line.rectangle.height
140             if y >= top and y < bottom:
141                 sum_of_widths = line.rectangle.x
142                 index = line.start_index
143                 for width in self.widths[line.start_index:line.end_index]:
144                     if sum_of_widths + width/2 > x:
145                         break
146                     sum_of_widths += width
147                     index += 1
148                 return index
149         
150     def build_line_list(self, x, y, width, left_margin):
151         self.line_list = []
152
153         current_line_start_index = 0
154         current_line_x = x
155         current_line_y = y
156         current_line_width = 0
157
158         ch_h = get_approximate_char_height(self.pango_layout.get_context())
159
160         for index, ch in enumerate(self.text):
161             ch_w = self.widths[index]
162             if current_line_x + current_line_width + ch_w > width:
163                 line = Line(
164                     current_line_start_index, index,
165                     Rectangle(
166                     current_line_x, current_line_y,
167                     current_line_width, ch_h))
168                 self.line_list.append(line)
169
170                 current_line_start_index = index
171                 current_line_x = left_margin
172                 current_line_y += ch_h
173                 current_line_width = ch_w
174             else:
175                 current_line_width += ch_w
176
177         if current_line_start_index < len(self.text):
178             line = Line(current_line_start_index, len(self.text),
179                         Rectangle(current_line_x,
180                                           current_line_y,
181                                           current_line_width,
182                                           ch_h))
183             self.line_list.append(line)
184
185             current_line_x += current_line_width
186
187         return current_line_x, current_line_y
188
189     def get_text(self, selection=False, start_index=0, end_index=0xffffff):
190
191         text = ""
192
193         for line in self.line_list:
194
195             t = self.text[line.start_index:line.end_index]
196             if selection:
197                 s = start_index - line.start_index
198                 s = max(s, 0)
199                 s = min(s, line.end_index - line.start_index)
200
201                 e = end_index - line.start_index
202                 e = min(e, line.end_index - line.start_index)
203                 e = max(e, 0)
204
205                 t = t[s:e]
206
207             text += t
208
209         return text
210
211     def draw(self, drawingarea, y_offset, pango_layout,
212              selection=False, start_index=0, end_index=0xffffff):
213
214         if drawingarea.get_property("has-focus"):
215             selection_fg = drawingarea.style.text[gtk.STATE_SELECTED]
216             selection_bg = drawingarea.style.base[gtk.STATE_SELECTED]
217         else:
218             selection_fg = drawingarea.style.text[gtk.STATE_ACTIVE]
219             selection_bg = drawingarea.style.base[gtk.STATE_ACTIVE]
220
221         for line in self.line_list:
222
223             text = self.text[line.start_index:line.end_index]
224             u_text = text.encode("utf8")
225             gc = drawingarea.window.new_gc()
226             gc.set_foreground(drawingarea.style.text[gtk.STATE_NORMAL])
227             gc.set_background(drawingarea.style.base[gtk.STATE_NORMAL])
228             attrs = self._get_attrs()
229             if selection:
230
231                 s = start_index - line.start_index
232                 s = max(s, 0)
233                 s = min(s, line.end_index - line.start_index)
234                 s = len(text[:s].encode("utf8"))
235
236                 e = end_index - line.start_index
237                 e = min(e, line.end_index - line.start_index)
238                 e = max(e, 0)
239                 e = len(text[:e].encode("utf8"))
240                     
241                 selection_all_attr_fg = pango.AttrForeground(
242                     selection_fg.red, selection_fg.green, selection_fg.blue,
243                     s, e)
244                 selection_all_attr_bg= pango.AttrBackground(
245                     selection_bg.red, selection_bg.green, selection_bg.blue,
246                     s, e)
247                 attrs.insert(selection_all_attr_fg)
248                 attrs.insert(selection_all_attr_bg)
249
250             pango_layout.set_text(u_text)
251             pango_layout.set_attributes(attrs)
252             drawingarea.window.draw_layout(
253                 gc, int(line.rectangle.x), line.rectangle.y + y_offset,
254                 pango_layout)
255
256
257 class ElementBoldText(ElementText):
258
259     ch_width_dict = {}   # key: char, value: width
260
261     def _get_attrs(self):
262         attrlist = pango.AttrList()
263         attr = pango.AttrWeight(pango.WEIGHT_BOLD,
264                                    end_index=0xffffff)
265         attrlist.insert(attr)
266         return attrlist
267
268     def _get_ch_width_dict(self):
269         return ElementBoldText.ch_width_dict
270
271
272 class ElementLink(ElementText):
273     
274     def __init__(self, text, href, pango_layout):
275         self.href = href
276         ElementText.__init__(self, text, pango_layout)
277
278     def _get_attrs(self):
279         attrlist = pango.AttrList()
280         attr = pango.AttrUnderline(pango.UNDERLINE_SINGLE,
281                                    end_index=0xffffff)
282         attrlist.insert(attr)
283         return attrlist