OSDN Git Service

selection thread_view
authorAiwota Programmer <aiwotaprog@tetteke.tk>
Mon, 24 Dec 2007 20:50:30 +0000 (05:50 +0900)
committerAiwota Programmer <aiwotaprog@tetteke.tk>
Mon, 24 Dec 2007 20:50:30 +0000 (05:50 +0900)
src/FukuiNoNamari/thread_view.py

index f3d2e4b..460ba76 100644 (file)
@@ -39,6 +39,36 @@ class Line:
         return x >= left and x < right and y >= top and y < bottom
         
 
+class ElementEmpty:
+
+    def __init__(self):
+        self.initialize()
+
+    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, gtk.gdk.Rectangle(x, y, width - x, Line.HEIGHT))
+        self.line_list.append(line)
+
+        return width, y
+
+    def draw(self, drawingarea, y_offset, pango_layout,
+             selection=False, start_index=0, end_index=0xffffff):
+        pass
+
+    
 class ElementText:
 
     def __init__(self, text, pango_layout):
@@ -60,6 +90,20 @@ class ElementText:
                 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 = []
 
@@ -98,18 +142,44 @@ class ElementText:
 
         return current_line_x, current_line_y
 
-    def draw(self, drawable, y_offset, pango_layout):
+    def draw(self, drawingarea, y_offset, pango_layout,
+             selection=False, start_index=0, end_index=0xffffff):
+
+        selection_fg = drawingarea.style.fg[3]
+        selection_bg = drawingarea.style.bg[3]
 
         for line in self.line_list:
+
             text = self.text[line.start_index:line.end_index]
-            gc = drawable.new_gc()
-            pango_layout.set_text(text)
+            u_text = text.encode("utf8")
+            gc = drawingarea.window.new_gc()
             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)
+            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, line.rectangle.x, line.rectangle.y + y_offset,
+                pango_layout)
 
 
 class ElementBoldText(ElementText):
@@ -140,7 +210,7 @@ class ResLayout:
 # represent one line
 
     def __init__(self, left_margin, resnum, pango_layout):
-        self.element_list = []
+        self.element_list = [ElementEmpty()]
         self.width = 0
         self.height = 0
         self.pango_layout = pango_layout
@@ -149,6 +219,9 @@ class ResLayout:
         self.posY = 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)
@@ -165,6 +238,13 @@ class ResLayout:
                 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 set_width(self, width):
 
         self.width = width
@@ -181,10 +261,85 @@ class ResLayout:
     def get_pixel_size(self):
         return self.width, self.height
 
-    def draw(self, drawable, x_offset, y_offset):
+    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)
 
-        for element in self.element_list:
-            element.draw(drawable, y_offset, self.pango_layout)
+        else:
+            for element in self.element_list:
+                element.draw(drawingarea, y_offset, self.pango_layout)
+                
 
 
 class ThreadView(gtk.HBox):
@@ -242,6 +397,10 @@ class ThreadView(gtk.HBox):
 
         self.menud_uri = None
 
+        # for selection
+        self.button_pressed_pt = (None, None, None)
+        self.button_moving_pt = (None, None, None)
+
     def _on_uri_clicked(self, uri):
         print uri, "clicked!!!!"
 
@@ -304,6 +463,32 @@ class ThreadView(gtk.HBox):
                 return True
         return False
 
+
+    def _get_selection_start_end(self):
+        pressed_layout, pressed_element, pressed_index = self.button_pressed_pt
+        moving_layout, moving_element, moving_index = self.button_moving_pt
+
+        if (pressed_layout is None or pressed_element is None or
+            pressed_index is None or moving_layout is None or
+            moving_element is None or moving_index is None):
+            return (None, None, None), (None, None, None)
+        
+        if pressed_layout == moving_layout:
+            if pressed_element == moving_element:
+                if moving_index < pressed_index:
+                    return self.button_moving_pt, self.button_pressed_pt
+            else:
+                pressed_element_index = pressed_layout.element_list.index(
+                    pressed_element)
+                moving_element_index = moving_layout.element_list.index(
+                    moving_element)
+                if moving_element_index < pressed_element_index:
+                    return self.button_moving_pt, self.button_pressed_pt
+        elif moving_layout.posY < pressed_layout.posY:
+            return self.button_moving_pt, self.button_pressed_pt
+
+        return self.button_pressed_pt, self.button_moving_pt
+
     def draw_viewport(self):
         view_y = self.vscrollbar.get_value()
         self.drawingarea.window.draw_rectangle(
@@ -312,7 +497,8 @@ class ThreadView(gtk.HBox):
             self.drawingarea.allocation.width,
             self.drawingarea.allocation.height)
 
-        gc = self.drawingarea.window.new_gc()
+        selection_start, selection_end = self._get_selection_start_end()
+
         for layout in self.res_layout_list:
             w, h = layout.get_pixel_size()
             layout_top = layout.posY
@@ -320,8 +506,9 @@ class ThreadView(gtk.HBox):
             area_top = view_y
             area_bottom = view_y + self.drawingarea.allocation.height
             if layout_top <= area_bottom and layout_bottom >= area_top:
-                layout.draw(self.drawingarea.window,
-                            0, layout.posY - int(view_y))
+                layout.draw(self.drawingarea,
+                            0, layout.posY - int(view_y),
+                            selection_start, selection_end)
 
     def transform_coordinate_gdk_to_adj(self, y):
         return y + self.vscrollbar.get_value()
@@ -340,7 +527,7 @@ class ThreadView(gtk.HBox):
         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.left_margin):
+                adj_x >= 0):
                 return lay
         return None
 
@@ -362,6 +549,55 @@ class ThreadView(gtk.HBox):
 
         return None, layout, None
 
+
+    def _set_button_pressed_pt(self, pt):
+        self.button_pressed_pt = (None, None, None)
+        if pt == None:
+            return
+
+        x, y = pt
+        layout = self.ptrpos_to_layout(x, y)
+        if layout is None:
+            return
+
+        x, y = self.transform_coordinate_gdk_to_layout(x, y, layout)
+        element = layout.get_element_from_xy(x, y)
+        if element is None:
+            element = layout.get_close_element_from_xy(x, y)
+
+        if element is None:
+            return
+
+        index = element.xy_to_index(x, y)
+        if index is None:
+            return
+
+        self.button_pressed_pt = (layout, element, index)
+
+    def _set_button_moving_pt(self, pt):
+        self.button_moving_pt = (None, None, None)
+        if pt == None:
+            return
+
+        x, y = pt
+        layout = self.ptrpos_to_layout(x, y)
+        if layout is None:
+            return
+
+        x, y = self.transform_coordinate_gdk_to_layout(x, y, layout)
+        element = layout.get_element_from_xy(x, y)
+        if element is None:
+            element = layout.get_close_element_from_xy(x, y)
+
+        if element is None:
+            return
+
+        index = element.xy_to_index(x, y)
+        if index is None:
+            return
+
+        self.button_moving_pt = (layout, element, index)
+
     def on_drawingarea_expose_event(self, widget, event, data=None):
         self.draw_viewport()
 
@@ -403,6 +639,16 @@ class ThreadView(gtk.HBox):
         if event.state & gtk.gdk.BUTTON1_MASK != gtk.gdk.BUTTON1_MASK:
             self.button1_pressed = False
 
+        if self.button1_pressed:
+            old_lay, old_elem, old_idx = self.button_moving_pt
+            self._set_button_moving_pt((event.x, event.y))
+            new_lay, new_elem, new_idx = self.button_moving_pt
+            if (old_lay != new_lay
+                or old_elem != new_elem
+                or old_idx != new_idx):
+                self.drawingarea.queue_draw()
+                #self.drawingarea.window.process_updates(False)
+
         cursor = ThreadView.regular_cursor
 
         uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
@@ -421,6 +667,11 @@ class ThreadView(gtk.HBox):
             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)
+            else:
+                self._set_button_moving_pt(None)
+                self._set_button_pressed_pt((event.x, event.y))
+                self.drawingarea.queue_draw()
+                
         elif event.button == 3:
             time = event.time
             uri, layout, element = self.ptrpos_to_uri(event.x, event.y)