pygtk.require('2.0')
import gtk
import pango
+from FukuiNoNamariExt import thread_view_extend
+
+
+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 ElementText:
+
+ def __init__(self, text, pango_layout):
+ self.text = text
+
+ attrlist = self._get_attrs()
+ self.widths = thread_view_extend.get_char_width(
+ pango_layout.get_context(), text, attrlist)
+
+ self.line_list = []
+
+ 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 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
+
+ for index, ch in enumerate(self.text):
+ ch_w = self.widths[index]
+ ch_h = Line.HEIGHT
+ if current_line_x + current_line_width + ch_w > width:
+ line = Line(
+ current_line_start_index, index,
+ gtk.gdk.Rectangle(
+ current_line_x, current_line_y,
+ current_line_width, Line.HEIGHT))
+ self.line_list.append(line)
+
+ current_line_start_index = index
+ current_line_x = left_margin
+ current_line_y += Line.HEIGHT
+ 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),
+ gtk.gdk.Rectangle(current_line_x,
+ current_line_y,
+ current_line_width,
+ Line.HEIGHT))
+ self.line_list.append(line)
+
+ current_line_x += current_line_width
+
+ return current_line_x, current_line_y
+
+ def draw(self, drawable, y_offset, pango_layout):
+
+ for line in self.line_list:
+ text = self.text[line.start_index:line.end_index]
+ gc = drawable.new_gc()
+ pango_layout.set_text(text)
+ attrs = self._get_attrs()
+ if attrs:
+ pango_layout.set_attributes(attrs)
+ drawable.draw_layout(gc,
+ 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
+
+
+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 = []
+ self.width = 0
+ self.height = 0
+ self.pango_layout = pango_layout
+ self.left_margin = left_margin
+ self.resnum = resnum
+ self.posY = 0
+
+ def add_text(self, text, bold, href):
+ 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 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 + Line.HEIGHT
+
+ def get_pixel_size(self):
+ return self.width, self.height
+
+ def draw(self, drawable, x_offset, y_offset):
+
+ for element in self.element_list:
+ element.draw(drawable, y_offset, self.pango_layout)
class ThreadView(gtk.HBox):
self.vscrollbar.connect(
"value-changed", self.on_vscrollbar_value_changed)
+ self.pango_layout = self.drawingarea.create_pango_layout("")
+
self.initialize_buffer()
self.on_uri_clicked = self._on_uri_clicked
print uri, "clicked!!!!"
def initialize_buffer(self):
- self.pangolayout = []
+ self.res_layout_list = []
- def add_layout(self, pangolayout):
- if (len(self.pangolayout) != 0):
- last = self.pangolayout[len(self.pangolayout)-1]
+ def add_layout(self, res_layout):
+ if (len(self.res_layout_list) != 0):
+ last = self.res_layout_list[len(self.res_layout_list)-1]
x, y = last.get_pixel_size()
- pangolayout.posY = last.posY + y
- self.set_layout_width(pangolayout)
- self.pangolayout.append(pangolayout)
- x, y = pangolayout.get_pixel_size()
- self.vscrollbar.set_range(0, pangolayout.posY + y)
+ res_layout.posY = last.posY + y
+ self.set_layout_width(res_layout)
+ self.res_layout_list.append(res_layout)
+ x, y = res_layout.get_pixel_size()
+ self.adjustment.upper = res_layout.posY + y
self.redraw()
self.change_vscrollbar_visible()
- def create_pango_layout(self, text):
- return self.drawingarea.create_pango_layout(text)
+ def create_res_layout(self, left_margin, resnum):
+ return ResLayout(left_margin, resnum, self.pango_layout)
def set_layout_width(self, layout):
width = self.drawingarea.allocation.width
- layout.set_width((width - layout.marginleft) * pango.SCALE)
+ layout.set_width(width)
def redraw(self):
self.drawingarea.queue_draw()
def relayout(self):
width = self.drawingarea.allocation.width
sum_height = 0
- for layout in self.pangolayout:
- layout.set_width((width - layout.marginleft) * pango.SCALE)
+ for layout in self.res_layout_list:
+ layout.set_width(width)
layout.posY = sum_height
x, y = layout.get_pixel_size()
sum_height += y
self.vscrollbar.set_value(value)
def jump_to_res(self, resnum):
- for layout in self.pangolayout:
+ for layout in self.res_layout_list:
if layout.resnum == resnum:
self.jump_to_layout(layout)
return True
self.drawingarea.allocation.height)
gc = self.drawingarea.window.new_gc()
- for layout in self.pangolayout:
+ for layout in self.res_layout_list:
w, h = layout.get_pixel_size()
layout_top = layout.posY
layout_bottom = layout.posY + h
area_top = view_y
area_bottom = view_y + self.drawingarea.allocation.height
if layout_top <= area_bottom and layout_bottom >= area_top:
- self.drawingarea.window.draw_layout(
- gc, layout.marginleft, layout.posY - int(view_y), layout)
+ layout.draw(self.drawingarea.window,
+ 0, layout.posY - int(view_y))
def transform_coordinate_gdk_to_adj(self, y):
return y + self.vscrollbar.get_value()
def transform_coordinate_adj_to_layout(self, x, y, layout):
- return x - layout.marginleft, y - layout.posY
+ return x, y - layout.posY
def transform_coordinate_gdk_to_layout(self, x, y, layout):
return self.transform_coordinate_adj_to_layout(
# transform coordinate, GdkWindow -> adjustment
adj_x = x
adj_y = self.transform_coordinate_gdk_to_adj(y)
- for lay in self.pangolayout:
+ for lay in self.res_layout_list:
width, height = lay.get_pixel_size()
if (adj_y >= lay.posY and adj_y < lay.posY + height and
- adj_x >= lay.marginleft):
+ adj_x >= lay.left_margin):
return lay
return None
if layout is None:
return None, None, None
- # transform coordinate, GdkWindow -> pangolayout
+ # transform coordinate, GdkWindow -> res_layout_list
lay_x, lay_y = self.transform_coordinate_gdk_to_layout(x, y, layout)
- # xy -> index
- idx, clk = layout.xy_to_index(
- int(lay_x)*pango.SCALE, int(lay_y)*pango.SCALE)
-
- x, y, width, height = layout.index_to_pos(idx)
- x /= pango.SCALE
- y /= pango.SCALE
- width /= pango.SCALE
- height /= pango.SCALE
- if (lay_x >= x and lay_x < x + width and
- lay_y >= y and lay_y < y + height):
-
- for i, (start, end, href) in enumerate(layout.urilist):
- if idx >= start and idx < end:
- return href, layout, i
+ # xy -> element
+ element = layout.get_element_from_xy(lay_x, lay_y)
+ if isinstance(element, ElementLink):
+ return element.href, layout, element
return None, layout, None
# before relayout, find top layout on gdkwindow
top_layout = None
delta = 0
- for lay in self.pangolayout:
+ for lay in self.res_layout_list:
if lay.posY > self.adjustment.value:
break
top_layout = lay
cursor = ThreadView.regular_cursor
- uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
+ uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
if layout is None:
cursor = ThreadView.arrow_cursor
else:
if event.button == 1:
self.current_pressed_uri = None
self.button1_pressed = True
- uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
- if uri is not None and layout is not None and index is not None:
- self.current_pressed_uri = (uri, layout, index)
+ uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
+ if uri is not None and layout is not None and element is not None:
+ self.current_pressed_uri = (uri, layout, element)
elif event.button == 3:
time = event.time
- uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
- if uri is not None and layout is not None and index is not None:
+ uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
+ if uri is not None and layout is not None and element is not None:
self.menu_openuri.show()
self.menu_copylinkaddress.show()
self.menu_separator_link.show()
self.button1_pressed = False
if button1_pressed and self.current_pressed_uri is not None:
- uri, layout, index = self.ptrpos_to_uri(event.x, event.y)
- p_uri, p_layout, p_index = self.current_pressed_uri
+ uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
+ p_uri, p_layout, p_element = self.current_pressed_uri
self.current_preesed_uri = None
- if uri == p_uri and layout == p_layout and index == p_index:
+ if (uri == p_uri and layout == p_layout and
+ element == p_element):
self.on_uri_clicked(uri)