1 # Copyright (C) 2007 by Aiwota Programmer
2 # aiwotaprog@tetteke.tk
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.
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.
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
24 from FukuiNoNamariExt import thread_view_extend
27 def get_approximate_char_height(pango_context):
28 desc = pango_context.get_font_description()
29 font = pango_context.load_font(desc)
30 ink, log = font.get_glyph_extents(0)
31 return log[3] / pango.SCALE + 2
35 def __init__(self, x, y, width, height):
46 def __init__(self, start_index, end_index, rectangle):
47 self.start_index = start_index
48 self.end_index = end_index
49 self.rectangle = rectangle
51 def is_on_xy(self, x, y):
52 left = self.rectangle.x
53 right = left + self.rectangle.width
54 top = self.rectangle.y
55 bottom = top + self.rectangle.height
56 return x >= left and x < right and y >= top and y < bottom
61 def __init__(self, pango_layout):
62 self.pango_layout = pango_layout
65 def recalc_char_widths(self):
71 def is_on_xy(self, x, y):
72 for line in self.line_list:
73 if line.is_on_xy(x, y):
77 def xy_to_index(self, x, y):
80 def build_line_list(self, x, y, width, left_margin):
83 line = Line(0, 0, Rectangle(
85 get_approximate_char_height(self.pango_layout.get_context())))
86 self.line_list.append(line)
90 def get_text(self, selection=False, start_index=0, end_index=0xffffff):
93 def draw(self, drawingarea, y_offset, pango_layout,
94 selection=False, start_index=0, end_index=0xffffff):
100 ch_width_dict = {} # key: char, value: width
102 def __init__(self, text, pango_layout):
104 self.pango_layout = pango_layout
106 self.recalc_char_widths()
110 def recalc_char_widths(self):
111 self.widths = [i for i in itertools.repeat(0, len(self.text))]
113 dict = self._get_ch_width_dict()
115 for index, ch in enumerate(self.text):
121 self.widths[index] = width
124 attrlist = self._get_attrs()
125 self.widths = thread_view_extend.get_char_width(
126 self.pango_layout.get_context(), self.text, attrlist)
127 for index, width in enumerate(self.widths):
128 dict[self.text[index]] = self.widths[index]
130 def _get_ch_width_dict(self):
131 return ElementText.ch_width_dict
133 def _get_attrs(self):
134 attrs = pango.AttrList()
137 def is_on_xy(self, x, y):
138 for line in self.line_list:
139 if line.is_on_xy(x, y):
143 def xy_to_index(self, x, y):
144 for line in self.line_list:
145 top = line.rectangle.y
146 bottom = top + line.rectangle.height
147 if y >= top and y < bottom:
148 sum_of_widths = line.rectangle.x
149 index = line.start_index
150 for width in self.widths[line.start_index:line.end_index]:
151 if sum_of_widths + width/2 > x:
153 sum_of_widths += width
157 def build_line_list(self, x, y, width, left_margin):
160 current_line_start_index = 0
163 current_line_width = 0
165 ch_h = get_approximate_char_height(self.pango_layout.get_context())
167 for index, ch in enumerate(self.text):
168 ch_w = self.widths[index]
169 if current_line_x + current_line_width + ch_w > width:
171 current_line_start_index, index,
173 current_line_x, current_line_y,
174 current_line_width, ch_h))
175 self.line_list.append(line)
177 current_line_start_index = index
178 current_line_x = left_margin
179 current_line_y += ch_h
180 current_line_width = ch_w
182 current_line_width += ch_w
184 if current_line_start_index < len(self.text):
185 line = Line(current_line_start_index, len(self.text),
186 Rectangle(current_line_x,
190 self.line_list.append(line)
192 current_line_x += current_line_width
194 return current_line_x, current_line_y
196 def get_text(self, selection=False, start_index=0, end_index=0xffffff):
200 for line in self.line_list:
202 t = self.text[line.start_index:line.end_index]
204 s = start_index - line.start_index
206 s = min(s, line.end_index - line.start_index)
208 e = end_index - line.start_index
209 e = min(e, line.end_index - line.start_index)
218 def draw(self, drawingarea, y_offset, pango_layout,
219 selection=False, start_index=0, end_index=0xffffff):
221 if drawingarea.get_property("has-focus"):
222 selection_fg = drawingarea.style.text[gtk.STATE_SELECTED]
223 selection_bg = drawingarea.style.base[gtk.STATE_SELECTED]
225 selection_fg = drawingarea.style.text[gtk.STATE_ACTIVE]
226 selection_bg = drawingarea.style.base[gtk.STATE_ACTIVE]
228 for line in self.line_list:
230 text = self.text[line.start_index:line.end_index]
231 u_text = text.encode("utf8")
232 gc = drawingarea.window.new_gc()
233 gc.set_foreground(drawingarea.style.text[gtk.STATE_NORMAL])
234 gc.set_background(drawingarea.style.base[gtk.STATE_NORMAL])
235 attrs = self._get_attrs()
238 s = start_index - line.start_index
240 s = min(s, line.end_index - line.start_index)
241 s = len(text[:s].encode("utf8"))
243 e = end_index - line.start_index
244 e = min(e, line.end_index - line.start_index)
246 e = len(text[:e].encode("utf8"))
248 selection_all_attr_fg = pango.AttrForeground(
249 selection_fg.red, selection_fg.green, selection_fg.blue,
251 selection_all_attr_bg= pango.AttrBackground(
252 selection_bg.red, selection_bg.green, selection_bg.blue,
254 attrs.insert(selection_all_attr_fg)
255 attrs.insert(selection_all_attr_bg)
257 pango_layout.set_text(u_text)
258 pango_layout.set_attributes(attrs)
259 drawingarea.window.draw_layout(
260 gc, int(line.rectangle.x), line.rectangle.y + y_offset,
264 class ElementBoldText(ElementText):
266 def _get_attrs(self):
267 attrlist = pango.AttrList()
268 attr = pango.AttrWeight(pango.WEIGHT_BOLD,
270 attrlist.insert(attr)
273 def recalc_char_widths(self):
274 attrlist = self._get_attrs()
275 self.widths = thread_view_extend.get_char_width(
276 self.pango_layout.get_context(), self.text, attrlist)
279 class ElementLink(ElementText):
281 def __init__(self, text, href, pango_layout):
283 ElementText.__init__(self, text, pango_layout)
285 def _get_attrs(self):
286 attrlist = pango.AttrList()
287 attr = pango.AttrUnderline(pango.UNDERLINE_SINGLE,
289 attrlist.insert(attr)
296 def __init__(self, left_margin, resnum, pango_layout):
297 self.element_list = [ElementEmpty(pango_layout)]
300 self.pango_layout = pango_layout
301 self.left_margin = left_margin
306 def add_text(self, text, bold, href):
307 if isinstance(self.element_list[0], ElementEmpty):
308 self.element_list = []
311 element = ElementLink(text, href, self.pango_layout)
312 self.element_list.append(element)
314 element = ElementBoldText(text, self.pango_layout)
315 self.element_list.append(element)
317 element = ElementText(text, self.pango_layout)
318 self.element_list.append(element)
320 def get_element_from_xy(self, x, y):
321 for element in self.element_list:
322 if element.is_on_xy(x, y):
326 def get_close_element_from_xy(self, x, y):
327 x= max(x, self.left_margin)
328 element = self.get_element_from_xy(x, y)
329 if element is None and len(self.element_list) != 0:
330 element = self.element_list[len(self.element_list) - 1]
333 def recalc_char_widths(self):
334 for element in self.element_list:
335 element.recalc_char_widths()
337 def set_width(self, width):
341 current_x = self.left_margin
344 for element in self.element_list:
345 current_x, current_y = element.build_line_list(
346 current_x, current_y, width, self.left_margin)
348 self.height = current_y + get_approximate_char_height(self.pango_layout.get_context())
350 def get_pixel_size(self):
351 return self.width, self.height
353 def get_text(self, selection_start, selection_end):
354 s_s = selection_start
356 s_l, s_e, s_i = selection_start
357 e_l, e_e, e_i = selection_end
361 if (s_l is None or s_e is None or s_i is None or
362 e_l is None or e_e is None or e_i is None or
363 self.posY < s_l.posY or self.posY > e_l.posY):
368 elif self.posY > s_s[0].posY and self.posY < e_s[0].posY:
370 for element in self.element_list:
371 text += element.get_text(selection=True)
373 elif self == s_s[0] and self == e_s[0]:
377 for element in self.element_list:
385 text += element.get_text(selection=True, start_index=start,
390 text += element.get_text(
391 selection=True, end_index=end)
393 text += element.get_text(selection=True)
399 for element in self.element_list:
403 text += element.get_text(selection=True, start_index=start)
405 text += element.get_text(selection=True)
411 for element in self.element_list:
414 text += element.get_text(selection=True, end_index=e_i)
417 text += element.get_text(selection=True)
426 def draw(self, drawingarea, x_offset, y_offset,
427 start_selection, end_selection):
429 s_s = start_selection
435 if (s_l is None or s_e is None or s_i is None or
436 e_l is None or e_e is None or e_i is None or
437 self.posY < s_l.posY or self.posY > e_l.posY):
439 for element in self.element_list:
440 element.draw(drawingarea, y_offset, self.pango_layout)
442 elif self.posY > s_s[0].posY and self.posY < e_s[0].posY:
444 for element in self.element_list:
445 element.draw(drawingarea, y_offset, self.pango_layout,
448 elif self == s_s[0] and self == e_s[0]:
452 for element in self.element_list:
460 element.draw(drawingarea, y_offset, self.pango_layout,
467 element.draw(drawingarea, y_offset, self.pango_layout,
468 selection=True, end_index=end)
470 element.draw(drawingarea, y_offset, self.pango_layout,
477 for element in self.element_list:
481 element.draw(drawingarea, y_offset, self.pango_layout,
482 selection=selection, start_index = start)
484 element.draw(drawingarea, y_offset, self.pango_layout,
491 for element in self.element_list:
494 element.draw(drawingarea, y_offset, self.pango_layout,
495 selection=selection, end_index=e_i)
498 element.draw(drawingarea, y_offset, self.pango_layout,
502 for element in self.element_list:
503 element.draw(drawingarea, y_offset, self.pango_layout)
507 layout = ResLayout(self.left_margin, self.resnum, self.pango_layout)
508 layout.element_list = []
509 for element in self.element_list:
510 layout.element_list.append(copy.copy(element))
514 class ThreadView(gtk.DrawingArea):
516 "cursor-over-link-event":
517 (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (object, object, )),
519 (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (object, )),
520 "set-scroll-adjustments" : (gobject.SIGNAL_RUN_LAST,
522 (gtk.Adjustment, gtk.Adjustment)),
523 "configure-event": "override",
524 "expose-event": "override",
525 "motion-notify-event": "override",
526 "button-press-event": "override",
527 "button-release-event": "override",
528 "style-set": "override",
529 "key-press-event": "override"
532 hand_cursor = gtk.gdk.Cursor(gtk.gdk.HAND2)
533 regular_cursor = gtk.gdk.Cursor(gtk.gdk.XTERM)
534 arrow_cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)
537 super(ThreadView, self).__init__()
538 self.set_set_scroll_adjustments_signal("set-scroll-adjustments")
539 self.set_property("can_focus", True)
541 #self.adjustment = gtk.Adjustment()
544 gtk.gdk.KEY_PRESS_MASK |
545 gtk.gdk.SCROLL_MASK |
546 gtk.gdk.POINTER_MOTION_MASK |
547 gtk.gdk.BUTTON_PRESS_MASK |
548 gtk.gdk.BUTTON_RELEASE_MASK)
550 self.drawingarea_prev_width = 0
552 self.pango_layout = self.create_pango_layout("")
554 self.initialize_buffer()
556 self.button1_pressed = False
557 self.current_pressed_uri = None
559 self.popupmenu = None
560 self.menu_openuri = None
561 self.menu_copylinkaddress = None
562 self.menu_separator_link = None
563 self.menu_copyselection = None
564 self.menu_openasuri = None
565 self.menu_separator_selection = None
567 self.menud_uri = None
570 self.button_pressed_pt = (None, None, None)
571 self.button_moving_pt = (None, None, None)
573 def initialize_buffer(self):
574 self.res_layout_list = []
576 def add_layout(self, res_layout):
577 if (len(self.res_layout_list) != 0):
578 last = self.res_layout_list[len(self.res_layout_list)-1]
579 x, y = last.get_pixel_size()
580 res_layout.posY = last.posY + y
581 self.set_layout_width(res_layout)
582 res_layout.list_index = len(self.res_layout_list)
583 self.res_layout_list.append(res_layout)
585 x, y = res_layout.get_pixel_size()
586 self.adjustment.upper = res_layout.posY + y
587 # do not use this method in a loop because expensive.
590 def create_res_layout(self, left_margin, resnum):
591 return ResLayout(left_margin, resnum, self.pango_layout)
593 def set_layout_width(self, layout):
594 width = self.allocation.width
595 layout.set_width(width)
600 def wrap_relayout(self):
601 # before relayout, find top layout on gdkwindow
602 top_layout = self.get_layout_on_y(self.adjustment.value)
605 if top_layout is not None:
606 delta = top_layout.posY - self.adjustment.value
609 self.drawingarea_prev_width = self.allocation.width
611 # after relayout, set adjustment.value to top layout's posY
612 if top_layout is not None:
613 self.adjustment.value = top_layout.posY - delta
616 width = self.allocation.width
618 for layout in self.res_layout_list:
619 layout.set_width(width)
620 layout.posY = sum_height
621 x, y = layout.get_pixel_size()
624 self.adjustment.upper = sum_height
626 def _set_adjustment_value(self, value):
627 value = min(self.adjustment.upper - self.adjustment.page_size, value)
628 value = max(self.adjustment.lower, value)
629 self.adjustment.value = value
631 def jump(self, value):
632 self._set_adjustment_value(value)
634 def jump_to_layout(self, layout):
635 self.jump(layout.posY)
637 def jump_to_the_end(self):
638 value = self.adjustment.upper - self.adjustment.page_size
639 self._set_adjustment_value(value)
641 def jump_to_res(self, resnum):
642 for layout in self.res_layout_list:
643 if layout.resnum == resnum:
644 self.jump_to_layout(layout)
649 def _get_selection_start_end(self):
650 pressed_layout, pressed_element, pressed_index = self.button_pressed_pt
651 moving_layout, moving_element, moving_index = self.button_moving_pt
653 if (pressed_layout is None or pressed_element is None or
654 pressed_index is None or moving_layout is None or
655 moving_element is None or moving_index is None):
656 return (None, None, None), (None, None, None)
658 if pressed_layout == moving_layout:
659 if pressed_element == moving_element:
660 if moving_index < pressed_index:
661 return self.button_moving_pt, self.button_pressed_pt
663 pressed_element_index = pressed_layout.element_list.index(
665 moving_element_index = moving_layout.element_list.index(
667 if moving_element_index < pressed_element_index:
668 return self.button_moving_pt, self.button_pressed_pt
669 elif moving_layout.posY < pressed_layout.posY:
670 return self.button_moving_pt, self.button_pressed_pt
672 return self.button_pressed_pt, self.button_moving_pt
674 def draw_viewport(self, area):
675 view_y = self.adjustment.value
676 self.window.draw_rectangle(
677 self.style.base_gc[0],
678 True, area.x, area.y, area.width, area.height)
680 selection_start, selection_end = self._get_selection_start_end()
682 top_layout = self.get_layout_on_y(view_y)
683 if top_layout is None:
685 #area_top = view_y + area.y
686 area_bottom = view_y + area.y + area.height
688 iter = range(top_layout.list_index, len(self.res_layout_list))
689 iter = itertools.imap(lambda index: self.res_layout_list[index], iter)
690 iter = itertools.takewhile(lambda lay: lay.posY <= area_bottom, iter)
692 layout.draw(self, 0, layout.posY - int(view_y),
693 selection_start, selection_end)
695 def transform_coordinate_gdk_to_adj(self, y):
696 return y + self.adjustment.value
698 def transform_coordinate_adj_to_layout(self, x, y, layout):
699 return x, y - layout.posY
701 def transform_coordinate_gdk_to_layout(self, x, y, layout):
702 return self.transform_coordinate_adj_to_layout(
703 x, self.transform_coordinate_gdk_to_adj(y), layout)
705 def get_layout_on_y(self, y):
707 def binary_search(lst, start, end, func):
712 m = (start + end) / 2
718 return binary_search(lst, start, m, func)
719 return binary_search(lst, m+1, end, func)
721 def on_y(layout, _y):
723 width, height = layout.get_pixel_size()
724 bottom = top + height
725 if _y >= top and _y < bottom:
732 self.res_layout_list, 0, len(self.res_layout_list),
733 lambda x: on_y(x, y))
735 return self.res_layout_list[ret]
738 def ptrpos_to_layout(self, x, y):
739 # transform coordinate, GdkWindow -> adjustment
740 adj_y = self.transform_coordinate_gdk_to_adj(y)
741 return self.get_layout_on_y(adj_y)
743 def ptrpos_to_uri(self, x, y):
744 # x, y is GdkWindow coordinate
746 layout = self.ptrpos_to_layout(x, y)
749 return None, None, None
751 # transform coordinate, GdkWindow -> res_layout_list
752 lay_x, lay_y = self.transform_coordinate_gdk_to_layout(x, y, layout)
755 element = layout.get_element_from_xy(lay_x, lay_y)
756 if isinstance(element, ElementLink):
757 return element.href, layout, element
759 return None, layout, None
761 def get_selected_text(self):
762 selection_start, selection_end = self._get_selection_start_end()
763 s_l, s_e, s_i = selection_start
764 e_l, e_e, e_i = selection_end
766 if (s_l is None or s_e is None or s_i is None or
767 e_l is None or e_e is None or e_i is None):
771 index = s_l.list_index
775 layout = self.res_layout_list[index]
777 text += layout.get_text(selection_start, selection_end)
785 def _set_button_pressed_pt(self, pt):
786 self.button_pressed_pt = (None, None, None)
791 layout = self.ptrpos_to_layout(x, y)
795 x, y = self.transform_coordinate_gdk_to_layout(x, y, layout)
796 element = layout.get_element_from_xy(x, y)
798 element = layout.get_close_element_from_xy(x, y)
803 index = element.xy_to_index(x, y)
807 self.button_pressed_pt = (layout, element, index)
809 def _set_button_moving_pt(self, pt):
810 self.button_moving_pt = (None, None, None)
815 layout = self.ptrpos_to_layout(x, y)
819 x, y = self.transform_coordinate_gdk_to_layout(x, y, layout)
820 element = layout.get_element_from_xy(x, y)
822 element = layout.get_close_element_from_xy(x, y)
827 index = element.xy_to_index(x, y)
831 self.button_moving_pt = (layout, element, index)
833 def do_expose_event(self, event):
834 self.draw_viewport(event.area)
836 def do_configure_event(self, event):
837 if event.width != self.drawingarea_prev_width:
840 self.adjustment.page_size = self.allocation.height
841 self.adjustment.step_increment = 20
842 self.adjustment.page_increment = self.allocation.height
844 # re-set 'value' for prevent overflow
845 self._set_adjustment_value(self.adjustment.value)
847 def on_adjustment_value_changed(self, widget, data=None):
850 def do_motion_notify_event(self, event):
851 if event.state & gtk.gdk.BUTTON1_MASK != gtk.gdk.BUTTON1_MASK:
852 self.button1_pressed = False
854 if self.button1_pressed and self.current_pressed_uri is None:
855 old_lay, old_elem, old_idx = self.button_moving_pt
856 self._set_button_moving_pt((event.x, event.y))
857 new_lay, new_elem, new_idx = self.button_moving_pt
858 if (old_lay != new_lay
859 or old_elem != new_elem
860 or old_idx != new_idx):
861 view_y = self.adjustment.value
864 o_width, o_height = old_lay.get_pixel_size()
865 n_width, n_height = new_lay.get_pixel_size()
868 height = max(o_y, n_y) - y
869 if o_y > n_y: height += o_height
870 else: height += n_height
874 self.queue_draw_area(0, y, n_width, height+1)
875 #self.window.process_updates(False)
877 cursor = ThreadView.regular_cursor
879 uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
881 cursor = ThreadView.arrow_cursor
883 if uri is not None and uri != "":
884 cursor = ThreadView.hand_cursor
885 self.emit("cursor-over-link-event", event, uri)
887 self.window.set_cursor(cursor)
889 def do_button_press_event(self, event):
890 if event.button == 1:
891 self.current_pressed_uri = None
892 self.button1_pressed = True
893 uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
894 if uri is not None and layout is not None and element is not None:
895 self.current_pressed_uri = (uri, layout, element)
897 self._set_button_moving_pt((event.x, event.y))
898 self._set_button_pressed_pt((event.x, event.y))
901 elif event.button == 3:
903 uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
904 if uri is not None and layout is not None and element is not None:
905 self.menu_openuri.show()
906 self.menu_copylinkaddress.show()
907 self.menu_separator_link.show()
908 self.menu_openuri.uri = uri
909 self.menu_copylinkaddress.uri = uri
911 self.menu_openuri.hide()
912 self.menu_copylinkaddress.hide()
913 self.menu_separator_link.hide()
914 self.menu_openuri.uri = None
915 self.menu_copylinkaddress.uri = None
917 text = self.get_selected_text()
918 if text and len(text) > 0:
919 self.menu_copyselection.show()
920 self.menu_openasuri.show()
921 self.menu_separator_selection.show()
923 self.menu_copyselection.hide()
924 self.menu_openasuri.hide()
925 self.menu_separator_selection.hide()
927 self.popupmenu.popup(None, None, None, event.button, time)
931 def do_button_release_event(self, event):
932 if event.button == 1:
933 button1_pressed = self.button1_pressed
934 self.button1_pressed = False
936 if button1_pressed and self.current_pressed_uri is not None:
937 uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
938 p_uri, p_layout, p_element = self.current_pressed_uri
939 self.current_pressed_uri = None
940 if (uri == p_uri and layout == p_layout and
941 element == p_element):
942 self.emit("uri-clicked-event", uri)
944 def do_style_set(self, previous_style):
945 if previous_style is None:
948 new = self.style.font_desc.hash()
949 old = previous_style.font_desc.hash()
951 for layout in self.res_layout_list:
952 layout.recalc_char_widths()
955 def do_key_press_event(self, event):
956 if event.type is not gtk.gdk.KEY_PRESS:
959 if event.keyval in (gtk.keysyms.Up, gtk.keysyms.Down,
960 gtk.keysyms.Page_Up, gtk.keysyms.Page_Down,
962 value = self.adjustment.value
963 if event.keyval == gtk.keysyms.Up:
964 step_increment = self.adjustment.step_increment
965 value = value - step_increment
966 elif event.keyval == gtk.keysyms.Down:
967 step_increment = self.adjustment.step_increment
968 value = value + step_increment
969 elif event.keyval == gtk.keysyms.Page_Up:
970 step_increment = self.adjustment.page_increment
971 value = value - step_increment
972 elif event.keyval == gtk.keysyms.Page_Down:
973 step_increment = self.adjustment.page_increment
974 value = value + step_increment
975 elif event.keyval == gtk.keysyms.Home:
978 elif event.keyval == gtk.keysyms.End:
979 self.jump_to_the_end()
981 def do_set_scroll_adjustments(self, hadjustment, vadjustment):
982 self.adjustment = vadjustment
983 if self.adjustment is not None:
984 self.adjustment.connect(
985 "value-changed", self.on_adjustment_value_changed)
988 class ThreadViewScrollbar(gtk.VScrollbar):
990 def __init__(self, adjustment=None):
991 super(ThreadViewScrollbar, self).__init__(adjustment)
992 self.set_property("no-show-all", True)
993 adjustment.connect("changed", self.on_adjustment_changed)
995 def change_vscrollbar_visible(self):
996 adjustment = self.get_adjustment()
997 if adjustment.upper <= adjustment.page_size:
1002 def on_adjustment_changed(self, widget, data=None):
1003 self.change_vscrollbar_visible()
1006 class ThreadViewContainer(gtk.HBox):
1008 def __init__(self, child):
1009 super(ThreadViewContainer, self).__init__(False, 2)
1010 adjustment = gtk.Adjustment()
1011 self.drawingarea = child
1012 child.set_scroll_adjustments(None, adjustment)
1013 self.vscrollbar = ThreadViewScrollbar(adjustment)
1014 self.pack_start(child)
1015 self.pack_start(self.vscrollbar, expand=False)
1017 child.connect("scroll-event", self.on_child_scroll_event)
1019 def on_child_scroll_event(self, widget, event, data=None):
1020 self.vscrollbar.emit("scroll-event", event)