+import gobject
+import itertools
+from FukuiNoNamariExt import thread_view_extend
+
+
+def get_approximate_char_height(pango_context):
+ desc = pango_context.get_font_description()
+ font = pango_context.load_font(desc)
+ ink, log = font.get_glyph_extents(0)
+ return log[3] / pango.SCALE + 2
+
+
+class Rectangle:
+ def __init__(self, x, y, width, height):
+ self.x = x
+ self.y = y
+ self.width = width
+ self.height = height
+
+
+class Line:
+
+ HEIGHT = 15
+
+ def __init__(self, start_index, end_index, rectangle):
+ self.start_index = start_index
+ self.end_index = end_index
+ self.rectangle = rectangle
+
+ def is_on_xy(self, x, y):
+ left = self.rectangle.x
+ right = left + self.rectangle.width
+ top = self.rectangle.y
+ bottom = top + self.rectangle.height
+ return x >= left and x < right and y >= top and y < bottom
+
+
+class ElementEmpty:
+
+ def __init__(self, pango_layout):
+ self.pango_layout = pango_layout
+ self.initialize()
+
+ def recalc_char_widths(self):
+ pass
+
+ def initialize(self):
+ self.line_list = []
+
+ def is_on_xy(self, x, y):
+ for line in self.line_list:
+ if line.is_on_xy(x, y):
+ return True
+ return False
+
+ def xy_to_index(self, x, y):
+ return 0
+
+ def build_line_list(self, x, y, width, left_margin):
+ self.initialize()
+
+ line = Line(0, 0, Rectangle(
+ x, y, width - x,
+ get_approximate_char_height(self.pango_layout.get_context())))
+ self.line_list.append(line)
+
+ return width, y
+
+ def get_text(self, selection=False, start_index=0, end_index=0xffffff):
+ return ""
+
+ def draw(self, drawingarea, y_offset, pango_layout,
+ selection=False, start_index=0, end_index=0xffffff):
+ pass
+
+
+class ElementText:
+
+ ch_width_dict = {} # key: char, value: width
+
+ def __init__(self, text, pango_layout):
+ self.text = text
+ self.pango_layout = pango_layout
+
+ self.recalc_char_widths()
+
+ self.line_list = []
+
+ def recalc_char_widths(self):
+ self.widths = [i for i in itertools.repeat(0, len(self.text))]
+
+ dict = self._get_ch_width_dict()
+ need_to_get = False
+ for index, ch in enumerate(self.text):
+ if ch not in dict:
+ need_to_get = True
+ break
+ else:
+ width = dict[ch]
+ self.widths[index] = width
+
+ if need_to_get:
+ attrlist = self._get_attrs()
+ self.widths = thread_view_extend.get_char_width(
+ self.pango_layout.get_context(), self.text, attrlist)
+ for index, width in enumerate(self.widths):
+ dict[self.text[index]] = self.widths[index]
+
+ def _get_ch_width_dict(self):
+ return ElementText.ch_width_dict
+
+ def _get_attrs(self):
+ attrs = pango.AttrList()
+ return attrs
+
+ def is_on_xy(self, x, y):
+ for line in self.line_list:
+ if line.is_on_xy(x, y):
+ return True
+ return False
+
+ def xy_to_index(self, x, y):
+ for line in self.line_list:
+ top = line.rectangle.y
+ bottom = top + line.rectangle.height
+ if y >= top and y < bottom:
+ sum_of_widths = line.rectangle.x
+ index = line.start_index
+ for width in self.widths[line.start_index:line.end_index]:
+ if sum_of_widths + width/2 > x:
+ break
+ sum_of_widths += width
+ index += 1
+ return index
+
+ def build_line_list(self, x, y, width, left_margin):
+ self.line_list = []
+
+ current_line_start_index = 0
+ current_line_x = x
+ current_line_y = y
+ current_line_width = 0
+
+ ch_h = get_approximate_char_height(self.pango_layout.get_context())
+
+ for index, ch in enumerate(self.text):
+ ch_w = self.widths[index]
+ if current_line_x + current_line_width + ch_w > width:
+ line = Line(
+ current_line_start_index, index,
+ Rectangle(
+ current_line_x, current_line_y,
+ current_line_width, ch_h))
+ self.line_list.append(line)
+
+ current_line_start_index = index
+ current_line_x = left_margin
+ current_line_y += ch_h
+ current_line_width = ch_w
+ else:
+ current_line_width += ch_w
+
+ if current_line_start_index < len(self.text):
+ line = Line(current_line_start_index, len(self.text),
+ Rectangle(current_line_x,
+ current_line_y,
+ current_line_width,
+ ch_h))
+ self.line_list.append(line)
+
+ current_line_x += current_line_width
+
+ return current_line_x, current_line_y
+
+ def get_text(self, selection=False, start_index=0, end_index=0xffffff):
+
+ text = ""
+
+ for line in self.line_list:
+
+ t = self.text[line.start_index:line.end_index]
+ if selection:
+ s = start_index - line.start_index
+ s = max(s, 0)
+ s = min(s, line.end_index - line.start_index)
+
+ e = end_index - line.start_index
+ e = min(e, line.end_index - line.start_index)
+ e = max(e, 0)
+
+ t = t[s:e]
+
+ text += t
+
+ return text
+
+ def draw(self, drawingarea, y_offset, pango_layout,
+ selection=False, start_index=0, end_index=0xffffff):
+
+ if drawingarea.get_property("has-focus"):
+ selection_fg = drawingarea.style.text[gtk.STATE_SELECTED]
+ selection_bg = drawingarea.style.base[gtk.STATE_SELECTED]
+ else:
+ selection_fg = drawingarea.style.text[gtk.STATE_ACTIVE]
+ selection_bg = drawingarea.style.base[gtk.STATE_ACTIVE]
+
+ for line in self.line_list:
+
+ text = self.text[line.start_index:line.end_index]
+ u_text = text.encode("utf8")
+ gc = drawingarea.window.new_gc()
+ gc.set_foreground(drawingarea.style.text[gtk.STATE_NORMAL])
+ gc.set_background(drawingarea.style.base[gtk.STATE_NORMAL])
+ attrs = self._get_attrs()
+ if selection:
+
+ s = start_index - line.start_index
+ s = max(s, 0)
+ s = min(s, line.end_index - line.start_index)
+ s = len(text[:s].encode("utf8"))
+
+ e = end_index - line.start_index
+ e = min(e, line.end_index - line.start_index)
+ e = max(e, 0)
+ e = len(text[:e].encode("utf8"))
+
+ selection_all_attr_fg = pango.AttrForeground(
+ selection_fg.red, selection_fg.green, selection_fg.blue,
+ s, e)
+ selection_all_attr_bg= pango.AttrBackground(
+ selection_bg.red, selection_bg.green, selection_bg.blue,
+ s, e)
+ attrs.insert(selection_all_attr_fg)
+ attrs.insert(selection_all_attr_bg)
+
+ pango_layout.set_text(u_text)
+ pango_layout.set_attributes(attrs)
+ drawingarea.window.draw_layout(
+ gc, int(line.rectangle.x), line.rectangle.y + y_offset,
+ pango_layout)
+
+
+class ElementBoldText(ElementText):
+
+ def _get_attrs(self):
+ attrlist = pango.AttrList()
+ attr = pango.AttrWeight(pango.WEIGHT_BOLD,
+ end_index=0xffffff)
+ attrlist.insert(attr)
+ return attrlist
+
+ def recalc_char_widths(self):
+ attrlist = self._get_attrs()
+ self.widths = thread_view_extend.get_char_width(
+ self.pango_layout.get_context(), self.text, attrlist)
+
+
+class ElementLink(ElementText):
+
+ def __init__(self, text, href, pango_layout):
+ self.href = href
+ ElementText.__init__(self, text, pango_layout)
+
+ def _get_attrs(self):
+ attrlist = pango.AttrList()
+ attr = pango.AttrUnderline(pango.UNDERLINE_SINGLE,
+ end_index=0xffffff)
+ attrlist.insert(attr)
+ return attrlist
+
+
+class ResLayout:
+# represent one line
+
+ def __init__(self, left_margin, resnum, pango_layout):
+ self.element_list = [ElementEmpty(pango_layout)]
+ self.width = 0
+ self.height = 0
+ self.pango_layout = pango_layout
+ self.left_margin = left_margin
+ self.resnum = resnum
+ self.posY = 0
+ self.list_index = 0
+
+ def add_text(self, text, bold, href):
+ if isinstance(self.element_list[0], ElementEmpty):
+ self.element_list = []
+
+ if href:
+ element = ElementLink(text, href, self.pango_layout)
+ self.element_list.append(element)
+ elif bold:
+ element = ElementBoldText(text, self.pango_layout)
+ self.element_list.append(element)
+ else:
+ element = ElementText(text, self.pango_layout)
+ self.element_list.append(element)
+
+ def get_element_from_xy(self, x, y):
+ for element in self.element_list:
+ if element.is_on_xy(x, y):
+ return element
+ return None
+
+ def get_close_element_from_xy(self, x, y):
+ x= max(x, self.left_margin)
+ element = self.get_element_from_xy(x, y)
+ if element is None and len(self.element_list) != 0:
+ element = self.element_list[len(self.element_list) - 1]
+ return element
+
+ def recalc_char_widths(self):
+ for element in self.element_list:
+ element.recalc_char_widths()
+
+ def set_width(self, width):
+
+ self.width = width
+
+ current_x = self.left_margin
+ current_y = 0
+
+ for element in self.element_list:
+ current_x, current_y = element.build_line_list(
+ current_x, current_y, width, self.left_margin)
+
+ self.height = current_y + get_approximate_char_height(self.pango_layout.get_context())
+
+ def get_pixel_size(self):
+ return self.width, self.height
+
+ def get_text(self, selection_start, selection_end):
+ s_s = selection_start
+ e_s = selection_end
+ s_l, s_e, s_i = selection_start
+ e_l, e_e, e_i = selection_end
+
+ text = ""
+
+ if (s_l is None or s_e is None or s_i is None or
+ e_l is None or e_e is None or e_i is None or
+ self.posY < s_l.posY or self.posY > e_l.posY):
+
+ # nothing to do
+ pass
+
+ elif self.posY > s_s[0].posY and self.posY < e_s[0].posY:
+
+ for element in self.element_list:
+ text += element.get_text(selection=True)
+
+ elif self == s_s[0] and self == e_s[0]:
+
+ selection = False
+
+ for element in self.element_list:
+ if s_e == element:
+ selection = True
+ start = s_i
+ end = 0xffffff
+ if e_e == element:
+ end = e_i
+ selection = False
+ text += element.get_text(selection=True, start_index=start,
+ end_index=end)
+ elif e_e == element:
+ end = e_i
+ selection = False
+ text += element.get_text(
+ selection=True, end_index=end)
+ elif selection:
+ text += element.get_text(selection=True)
+
+ elif self == s_s[0]:
+
+ selection = False
+
+ for element in self.element_list:
+ if s_e == element:
+ selection = True
+ start = s_i
+ text += element.get_text(selection=True, start_index=start)
+ elif selection:
+ text += element.get_text(selection=True)
+
+ elif self == e_s[0]:
+
+ selection = True
+
+ for element in self.element_list:
+ if e_e == element:
+ end = e_i
+ text += element.get_text(selection=True, end_index=e_i)
+ selection = False
+ elif selection:
+ text += element.get_text(selection=True)
+
+ else:
+ # nothing to do
+ pass
+
+ return text
+
+
+ def draw(self, drawingarea, x_offset, y_offset,
+ start_selection, end_selection):
+
+ s_s = start_selection
+ e_s = end_selection
+
+ s_l, s_e, s_i = s_s
+ e_l, e_e, e_i = e_s
+
+ if (s_l is None or s_e is None or s_i is None or
+ e_l is None or e_e is None or e_i is None or
+ self.posY < s_l.posY or self.posY > e_l.posY):
+
+ for element in self.element_list:
+ element.draw(drawingarea, y_offset, self.pango_layout)
+
+ elif self.posY > s_s[0].posY and self.posY < e_s[0].posY:
+
+ for element in self.element_list:
+ element.draw(drawingarea, y_offset, self.pango_layout,
+ selection=True)
+
+ elif self == s_s[0] and self == e_s[0]:
+
+ selection = False
+
+ for element in self.element_list:
+ if s_e == element:
+ selection = True
+ start = s_i
+ end = 0xffffff
+ if e_e == element:
+ end = e_i
+ selection = False
+ element.draw(drawingarea, y_offset, self.pango_layout,
+ selection=True,
+ start_index=start,
+ end_index=end)
+ elif e_e == element:
+ end = e_i
+ selection = False
+ element.draw(drawingarea, y_offset, self.pango_layout,
+ selection=True, end_index=end)
+ else:
+ element.draw(drawingarea, y_offset, self.pango_layout,
+ selection=selection)
+
+ elif self == s_s[0]:
+
+ selection = False
+
+ for element in self.element_list:
+ if s_e == element:
+ selection = True
+ start = s_i
+ element.draw(drawingarea, y_offset, self.pango_layout,
+ selection=selection, start_index = start)
+ else:
+ element.draw(drawingarea, y_offset, self.pango_layout,
+ selection=selection)
+
+ elif self == e_s[0]:
+
+ selection = True
+
+ for element in self.element_list:
+ if e_e == element:
+ end = e_i
+ element.draw(drawingarea, y_offset, self.pango_layout,
+ selection=selection, end_index=e_i)
+ selection = False
+ else:
+ element.draw(drawingarea, y_offset, self.pango_layout,
+ selection=selection)
+
+ else:
+ for element in self.element_list:
+ element.draw(drawingarea, y_offset, self.pango_layout)
+
+ def clone(self):
+ import copy
+ layout = ResLayout(self.left_margin, self.resnum, self.pango_layout)
+ layout.element_list = []
+ for element in self.element_list:
+ layout.element_list.append(copy.copy(element))
+ return layout