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
23 from FukuiNoNamariExt import thread_view_extend
26 def get_approximate_char_height(pango_context):
27 desc = pango_context.get_font_description()
28 font = pango_context.load_font(desc)
29 ink, log = font.get_glyph_extents(0)
30 return log[3] / pango.SCALE + 2
37 def __init__(self, start_index, end_index, rectangle):
38 self.start_index = start_index
39 self.end_index = end_index
40 self.rectangle = rectangle
42 def is_on_xy(self, x, y):
43 left = self.rectangle.x
44 right = left + self.rectangle.width
45 top = self.rectangle.y
46 bottom = top + self.rectangle.height
47 return x >= left and x < right and y >= top and y < bottom
52 def __init__(self, pango_layout):
53 self.pango_layout = pango_layout
56 def recalc_char_widths(self):
62 def is_on_xy(self, x, y):
63 for line in self.line_list:
64 if line.is_on_xy(x, y):
68 def xy_to_index(self, x, y):
71 def build_line_list(self, x, y, width, left_margin):
74 line = Line(0, 0, gtk.gdk.Rectangle(
76 get_approximate_char_height(self.pango_layout.get_context())))
77 self.line_list.append(line)
81 def get_text(self, selection=False, start_index=0, end_index=0xffffff):
84 def draw(self, drawingarea, y_offset, pango_layout,
85 selection=False, start_index=0, end_index=0xffffff):
91 def __init__(self, text, pango_layout):
93 self.pango_layout = pango_layout
95 attrlist = self._get_attrs()
96 self.widths = thread_view_extend.get_char_width(
97 pango_layout.get_context(), text, attrlist)
101 def recalc_char_widths(self):
102 attrlist = self._get_attrs()
103 self.widths = thread_view_extend.get_char_width(
104 self.pango_layout.get_context(), self.text, attrlist)
106 def _get_attrs(self):
107 attrs = pango.AttrList()
110 def is_on_xy(self, x, y):
111 for line in self.line_list:
112 if line.is_on_xy(x, y):
116 def xy_to_index(self, x, y):
117 for line in self.line_list:
118 top = line.rectangle.y
119 bottom = top + line.rectangle.height
120 if y >= top and y < bottom:
121 sum_of_widths = line.rectangle.x
122 index = line.start_index
123 for width in self.widths[line.start_index:line.end_index]:
124 if sum_of_widths + width/2 > x:
126 sum_of_widths += width
130 def build_line_list(self, x, y, width, left_margin):
133 current_line_start_index = 0
136 current_line_width = 0
138 ch_h = get_approximate_char_height(self.pango_layout.get_context())
140 for index, ch in enumerate(self.text):
141 ch_w = self.widths[index]
142 if current_line_x + current_line_width + ch_w > width:
144 current_line_start_index, index,
146 current_line_x, current_line_y,
147 current_line_width, ch_h))
148 self.line_list.append(line)
150 current_line_start_index = index
151 current_line_x = left_margin
152 current_line_y += ch_h
153 current_line_width = ch_w
155 current_line_width += ch_w
157 if current_line_start_index < len(self.text):
158 line = Line(current_line_start_index, len(self.text),
159 gtk.gdk.Rectangle(current_line_x,
163 self.line_list.append(line)
165 current_line_x += current_line_width
167 return current_line_x, current_line_y
169 def get_text(self, selection=False, start_index=0, end_index=0xffffff):
173 for line in self.line_list:
175 t = self.text[line.start_index:line.end_index]
177 s = start_index - line.start_index
179 s = min(s, line.end_index - line.start_index)
181 e = end_index - line.start_index
182 e = min(e, line.end_index - line.start_index)
191 def draw(self, drawingarea, y_offset, pango_layout,
192 selection=False, start_index=0, end_index=0xffffff):
194 selection_fg = drawingarea.style.fg[3]
195 selection_bg = drawingarea.style.bg[3]
197 for line in self.line_list:
199 text = self.text[line.start_index:line.end_index]
200 u_text = text.encode("utf8")
201 gc = drawingarea.window.new_gc()
202 attrs = self._get_attrs()
205 s = start_index - line.start_index
207 s = min(s, line.end_index - line.start_index)
208 s = len(text[:s].encode("utf8"))
210 e = end_index - line.start_index
211 e = min(e, line.end_index - line.start_index)
213 e = len(text[:e].encode("utf8"))
215 selection_all_attr_fg = pango.AttrForeground(
216 selection_fg.red, selection_fg.green, selection_fg.blue,
218 selection_all_attr_bg= pango.AttrBackground(
219 selection_bg.red, selection_bg.green, selection_bg.blue,
221 attrs.insert(selection_all_attr_fg)
222 attrs.insert(selection_all_attr_bg)
224 pango_layout.set_text(u_text)
225 pango_layout.set_attributes(attrs)
226 drawingarea.window.draw_layout(
227 gc, line.rectangle.x, line.rectangle.y + y_offset,
231 class ElementBoldText(ElementText):
233 def _get_attrs(self):
234 attrlist = pango.AttrList()
235 attr = pango.AttrWeight(pango.WEIGHT_BOLD,
237 attrlist.insert(attr)
241 class ElementLink(ElementText):
243 def __init__(self, text, href, pango_layout):
245 ElementText.__init__(self, text, pango_layout)
247 def _get_attrs(self):
248 attrlist = pango.AttrList()
249 attr = pango.AttrUnderline(pango.UNDERLINE_SINGLE,
251 attrlist.insert(attr)
258 def __init__(self, left_margin, resnum, pango_layout):
259 self.element_list = [ElementEmpty(pango_layout)]
262 self.pango_layout = pango_layout
263 self.left_margin = left_margin
268 def add_text(self, text, bold, href):
269 if isinstance(self.element_list[0], ElementEmpty):
270 self.element_list = []
273 element = ElementLink(text, href, self.pango_layout)
274 self.element_list.append(element)
276 element = ElementBoldText(text, self.pango_layout)
277 self.element_list.append(element)
279 element = ElementText(text, self.pango_layout)
280 self.element_list.append(element)
282 def get_element_from_xy(self, x, y):
283 for element in self.element_list:
284 if element.is_on_xy(x, y):
288 def get_close_element_from_xy(self, x, y):
289 x= max(x, self.left_margin)
290 element = self.get_element_from_xy(x, y)
291 if element is None and len(self.element_list) != 0:
292 element = self.element_list[len(self.element_list) - 1]
295 def recalc_char_widths(self):
296 for element in self.element_list:
297 element.recalc_char_widths()
299 def set_width(self, width):
303 current_x = self.left_margin
306 for element in self.element_list:
307 current_x, current_y = element.build_line_list(
308 current_x, current_y, width, self.left_margin)
310 self.height = current_y + get_approximate_char_height(self.pango_layout.get_context())
312 def get_pixel_size(self):
313 return self.width, self.height
315 def get_text(self, selection_start, selection_end):
316 s_s = selection_start
318 s_l, s_e, s_i = selection_start
319 e_l, e_e, e_i = selection_end
323 if (s_l is None or s_e is None or s_i is None or
324 e_l is None or e_e is None or e_i is None or
325 self.posY < s_l.posY or self.posY > e_l.posY):
330 elif self.posY > s_s[0].posY and self.posY < e_s[0].posY:
332 for element in self.element_list:
333 text += element.get_text(selection=True)
335 elif self == s_s[0] and self == e_s[0]:
339 for element in self.element_list:
347 text += element.get_text(selection=True, start_index=start,
352 text += element.get_text(
353 selection=True, end_index=end)
355 text += element.get_text(selection=True)
361 for element in self.element_list:
365 text += element.get_text(selection=True, start_index=start)
367 text += element.get_text(selection=True)
373 for element in self.element_list:
376 text += element.get_text(selection=True, end_index=e_i)
379 text += element.get_text(selection=True)
388 def draw(self, drawingarea, x_offset, y_offset,
389 start_selection, end_selection):
391 s_s = start_selection
397 if (s_l is None or s_e is None or s_i is None or
398 e_l is None or e_e is None or e_i is None or
399 self.posY < s_l.posY or self.posY > e_l.posY):
401 for element in self.element_list:
402 element.draw(drawingarea, y_offset, self.pango_layout)
404 elif self.posY > s_s[0].posY and self.posY < e_s[0].posY:
406 for element in self.element_list:
407 element.draw(drawingarea, y_offset, self.pango_layout,
410 elif self == s_s[0] and self == e_s[0]:
414 for element in self.element_list:
422 element.draw(drawingarea, y_offset, self.pango_layout,
429 element.draw(drawingarea, y_offset, self.pango_layout,
430 selection=True, end_index=end)
432 element.draw(drawingarea, y_offset, self.pango_layout,
439 for element in self.element_list:
443 element.draw(drawingarea, y_offset, self.pango_layout,
444 selection=selection, start_index = start)
446 element.draw(drawingarea, y_offset, self.pango_layout,
453 for element in self.element_list:
456 element.draw(drawingarea, y_offset, self.pango_layout,
457 selection=selection, end_index=e_i)
460 element.draw(drawingarea, y_offset, self.pango_layout,
464 for element in self.element_list:
465 element.draw(drawingarea, y_offset, self.pango_layout)
469 layout = ResLayout(self.left_margin, self.resnum, self.pango_layout)
470 layout.element_list = []
471 for element in self.element_list:
472 layout.element_list.append(copy.copy(element))
476 class ThreadView(gtk.HBox):
478 "cursor-over-link-event":
479 (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (object, object, ))
482 hand_cursor = gtk.gdk.Cursor(gtk.gdk.HAND2)
483 regular_cursor = gtk.gdk.Cursor(gtk.gdk.XTERM)
484 arrow_cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)
487 gtk.HBox.__init__(self, False, 0)
488 self.drawingarea = gtk.DrawingArea()
489 self.drawingarea.set_property("can_focus", True)
492 top = self.drawingarea.get_toplevel()
493 if top.get_property("type") == gtk.WINDOW_TOPLEVEL:
494 top.set_focus(self.drawingarea)
495 gobject.idle_add(set_focus)
497 self.vscrollbar = gtk.VScrollbar()
498 self.pack_start(self.drawingarea)
499 self.pack_start(self.vscrollbar, expand=False)
500 self.adjustment = self.vscrollbar.get_adjustment()
502 self.drawingarea.add_events(
503 gtk.gdk.KEY_PRESS_MASK |
504 gtk.gdk.SCROLL_MASK |
505 gtk.gdk.POINTER_MOTION_MASK |
506 gtk.gdk.BUTTON_PRESS_MASK |
507 gtk.gdk.BUTTON_RELEASE_MASK)
509 self.drawingarea_prev_width = 0
511 self.drawingarea.connect(
512 "expose-event", self.on_drawingarea_expose_event)
513 self.drawingarea.connect(
514 "configure-event", self.on_drawingarea_configure_event)
515 self.drawingarea.connect(
516 "scroll-event", self.on_drawingarea_scroll_event)
517 self.drawingarea.connect(
518 "motion-notify-event", self.on_drawingrarea_motion_notify_event)
519 self.drawingarea.connect(
520 "button-press-event", self.on_drawingarea_button_press_event)
521 self.drawingarea.connect(
522 "button-release-event", self.on_drawingarea_button_release_event)
523 self.drawingarea.connect(
524 "style-set", self.on_drawingarea_style_set)
525 self.drawingarea.connect(
526 "key-press-event", self.on_drawingarea_key_press_event)
527 self.vscrollbar.connect(
528 "value-changed", self.on_vscrollbar_value_changed)
530 self.pango_layout = self.drawingarea.create_pango_layout("")
532 self.initialize_buffer()
534 self.on_uri_clicked = self._on_uri_clicked
536 self.button1_pressed = False
537 self.current_pressed_uri = None
539 self.popupmenu = None
540 self.menu_openuri = None
541 self.menu_copylinkaddress = None
542 self.menu_separator_link = None
543 self.menu_copyselection = None
544 self.menu_openasuri = None
545 self.menu_separator_selection = None
547 self.menud_uri = None
550 self.button_pressed_pt = (None, None, None)
551 self.button_moving_pt = (None, None, None)
553 def _on_uri_clicked(self, uri):
554 print uri, "clicked!!!!"
556 def initialize_buffer(self):
557 self.res_layout_list = []
559 def add_layout(self, res_layout):
560 if (len(self.res_layout_list) != 0):
561 last = self.res_layout_list[len(self.res_layout_list)-1]
562 x, y = last.get_pixel_size()
563 res_layout.posY = last.posY + y
564 self.set_layout_width(res_layout)
565 res_layout.list_index = len(self.res_layout_list)
566 self.res_layout_list.append(res_layout)
568 x, y = res_layout.get_pixel_size()
569 self.adjustment.upper = res_layout.posY + y
570 # do not use this method in a loop because expensive.
572 self.change_vscrollbar_visible()
574 def create_res_layout(self, left_margin, resnum):
575 return ResLayout(left_margin, resnum, self.pango_layout)
577 def set_layout_width(self, layout):
578 width = self.drawingarea.allocation.width
579 layout.set_width(width)
582 self.drawingarea.queue_draw()
584 def wrap_relayout(self):
585 # before relayout, find top layout on gdkwindow
586 top_layout = self.get_layout_on_y(self.adjustment.value)
589 if top_layout is not None:
590 delta = top_layout.posY - self.vscrollbar.get_value()
593 self.drawingarea_prev_width = self.drawingarea.allocation.width
595 # after relayout, set vscrollbar.value to top layout's posY
596 if top_layout is not None:
597 self.vscrollbar.set_value(top_layout.posY - delta)
600 width = self.drawingarea.allocation.width
602 for layout in self.res_layout_list:
603 layout.set_width(width)
604 layout.posY = sum_height
605 x, y = layout.get_pixel_size()
608 self.vscrollbar.set_range(0, sum_height)
609 self.change_vscrollbar_visible()
611 def change_vscrollbar_visible(self):
612 if self.adjustment.upper < self.adjustment.page_size:
613 self.vscrollbar.hide()
615 self.vscrollbar.show()
617 def jump(self, value):
618 self.vscrollbar.set_value(value)
620 def jump_to_layout(self, layout):
621 self.jump(layout.posY)
623 def jump_to_the_end(self):
624 value = self.adjustment.upper - self.adjustment.page_size
625 self.vscrollbar.set_value(value)
627 def jump_to_res(self, resnum):
628 for layout in self.res_layout_list:
629 if layout.resnum == resnum:
630 self.jump_to_layout(layout)
635 def _get_selection_start_end(self):
636 pressed_layout, pressed_element, pressed_index = self.button_pressed_pt
637 moving_layout, moving_element, moving_index = self.button_moving_pt
639 if (pressed_layout is None or pressed_element is None or
640 pressed_index is None or moving_layout is None or
641 moving_element is None or moving_index is None):
642 return (None, None, None), (None, None, None)
644 if pressed_layout == moving_layout:
645 if pressed_element == moving_element:
646 if moving_index < pressed_index:
647 return self.button_moving_pt, self.button_pressed_pt
649 pressed_element_index = pressed_layout.element_list.index(
651 moving_element_index = moving_layout.element_list.index(
653 if moving_element_index < pressed_element_index:
654 return self.button_moving_pt, self.button_pressed_pt
655 elif moving_layout.posY < pressed_layout.posY:
656 return self.button_moving_pt, self.button_pressed_pt
658 return self.button_pressed_pt, self.button_moving_pt
660 def draw_viewport(self, area):
661 view_y = self.vscrollbar.get_value()
662 self.drawingarea.window.draw_rectangle(
663 self.drawingarea.style.base_gc[0],
664 True, area.x, area.y, area.width, area.height)
666 selection_start, selection_end = self._get_selection_start_end()
669 top_layout = self.get_layout_on_y(view_y)
672 index = top_layout.list_index
673 while index < len(self.res_layout_list):
674 layout = self.res_layout_list[index]
675 w, h = layout.get_pixel_size()
676 layout_top = layout.posY
677 layout_bottom = layout.posY + h
678 area_top = view_y + area.y
679 area_bottom = view_y + area.y + area.height
680 if layout_top <= area_bottom and layout_bottom >= area_top:
681 layout.draw(self.drawingarea,
682 0, layout.posY - int(view_y),
683 selection_start, selection_end)
684 if layout_top > area_bottom:
689 def transform_coordinate_gdk_to_adj(self, y):
690 return y + self.vscrollbar.get_value()
692 def transform_coordinate_adj_to_layout(self, x, y, layout):
693 return x, y - layout.posY
695 def transform_coordinate_gdk_to_layout(self, x, y, layout):
696 return self.transform_coordinate_adj_to_layout(
697 x, self.transform_coordinate_gdk_to_adj(y), layout)
699 def get_layout_on_y(self, y):
701 def binary_search(lst, start, end, func):
706 m = (start + end) / 2
712 return binary_search(lst, start, m, func)
713 return binary_search(lst, m+1, end, func)
715 def on_y(layout, _y):
717 width, height = layout.get_pixel_size()
718 bottom = top + height
719 if _y >= top and _y < bottom:
726 self.res_layout_list, 0, len(self.res_layout_list),
727 lambda x: on_y(x, y))
729 return self.res_layout_list[ret]
732 def ptrpos_to_layout(self, x, y):
733 # transform coordinate, GdkWindow -> adjustment
734 adj_y = self.transform_coordinate_gdk_to_adj(y)
735 return self.get_layout_on_y(adj_y)
737 def ptrpos_to_uri(self, x, y):
738 # x, y is GdkWindow coordinate
740 layout = self.ptrpos_to_layout(x, y)
743 return None, None, None
745 # transform coordinate, GdkWindow -> res_layout_list
746 lay_x, lay_y = self.transform_coordinate_gdk_to_layout(x, y, layout)
749 element = layout.get_element_from_xy(lay_x, lay_y)
750 if isinstance(element, ElementLink):
751 return element.href, layout, element
753 return None, layout, None
755 def get_selected_text(self):
756 selection_start, selection_end = self._get_selection_start_end()
757 s_l, s_e, s_i = selection_start
758 e_l, e_e, e_i = selection_end
760 if (s_l is None or s_e is None or s_i is None or
761 e_l is None or e_e is None or e_i is None):
765 index = s_l.list_index
769 layout = self.res_layout_list[index]
771 text += layout.get_text(selection_start, selection_end)
779 def _set_button_pressed_pt(self, pt):
780 self.button_pressed_pt = (None, None, None)
785 layout = self.ptrpos_to_layout(x, y)
789 x, y = self.transform_coordinate_gdk_to_layout(x, y, layout)
790 element = layout.get_element_from_xy(x, y)
792 element = layout.get_close_element_from_xy(x, y)
797 index = element.xy_to_index(x, y)
801 self.button_pressed_pt = (layout, element, index)
803 def _set_button_moving_pt(self, pt):
804 self.button_moving_pt = (None, None, None)
809 layout = self.ptrpos_to_layout(x, y)
813 x, y = self.transform_coordinate_gdk_to_layout(x, y, layout)
814 element = layout.get_element_from_xy(x, y)
816 element = layout.get_close_element_from_xy(x, y)
821 index = element.xy_to_index(x, y)
825 self.button_moving_pt = (layout, element, index)
827 def on_drawingarea_expose_event(self, widget, event, data=None):
828 self.draw_viewport(event.area)
830 def on_drawingarea_configure_event(self, widget, event, data=None):
831 if event.width != self.drawingarea_prev_width:
834 self.adjustment.page_size = self.drawingarea.allocation.height
835 self.vscrollbar.set_increments(20, self.drawingarea.allocation.height)
837 # re-set 'value' for prevent overflow
838 self.vscrollbar.set_value(self.vscrollbar.get_value())
839 self.change_vscrollbar_visible()
841 def on_vscrollbar_value_changed(self, widget, data=None):
842 self.drawingarea.queue_draw()
844 def on_drawingarea_scroll_event(self, widget, event, data=None):
845 self.vscrollbar.emit("scroll-event", event)
847 def on_drawingrarea_motion_notify_event(self, widget, event, data=None):
848 if event.state & gtk.gdk.BUTTON1_MASK != gtk.gdk.BUTTON1_MASK:
849 self.button1_pressed = False
851 if self.button1_pressed and self.current_pressed_uri is None:
852 old_lay, old_elem, old_idx = self.button_moving_pt
853 self._set_button_moving_pt((event.x, event.y))
854 new_lay, new_elem, new_idx = self.button_moving_pt
855 if (old_lay != new_lay
856 or old_elem != new_elem
857 or old_idx != new_idx):
858 view_y = self.vscrollbar.get_value()
861 o_width, o_height = old_lay.get_pixel_size()
862 n_width, n_height = new_lay.get_pixel_size()
865 height = max(o_y, n_y) - y
866 if o_y > n_y: height += o_height
867 else: height += n_height
871 self.drawingarea.queue_draw_area(0, y, n_width, height+1)
872 #self.drawingarea.window.process_updates(False)
874 cursor = ThreadView.regular_cursor
876 uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
878 cursor = ThreadView.arrow_cursor
880 if uri is not None and uri != "":
881 cursor = ThreadView.hand_cursor
882 self.emit("cursor-over-link-event", event, uri)
884 self.drawingarea.window.set_cursor(cursor)
886 def on_drawingarea_button_press_event(self, widget, event, data=None):
887 if event.button == 1:
888 self.current_pressed_uri = None
889 self.button1_pressed = True
890 uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
891 if uri is not None and layout is not None and element is not None:
892 self.current_pressed_uri = (uri, layout, element)
894 self._set_button_moving_pt((event.x, event.y))
895 self._set_button_pressed_pt((event.x, event.y))
896 self.drawingarea.queue_draw()
898 elif event.button == 3:
900 uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
901 if uri is not None and layout is not None and element is not None:
902 self.menu_openuri.show()
903 self.menu_copylinkaddress.show()
904 self.menu_separator_link.show()
905 self.menu_openuri.uri = uri
906 self.menu_copylinkaddress.uri = uri
908 self.menu_openuri.hide()
909 self.menu_copylinkaddress.hide()
910 self.menu_separator_link.hide()
911 self.menu_openuri.uri = None
912 self.menu_copylinkaddress.uri = None
914 text = self.get_selected_text()
915 if text and len(text) > 0:
916 self.menu_copyselection.show()
917 self.menu_openasuri.show()
918 self.menu_separator_selection.show()
920 self.menu_copyselection.hide()
921 self.menu_openasuri.hide()
922 self.menu_separator_selection.hide()
924 self.popupmenu.popup(None, None, None, event.button, time)
928 def on_drawingarea_button_release_event(self, widget, event, data=None):
929 if event.button == 1:
930 button1_pressed = self.button1_pressed
931 self.button1_pressed = False
933 if button1_pressed and self.current_pressed_uri is not None:
934 uri, layout, element = self.ptrpos_to_uri(event.x, event.y)
935 p_uri, p_layout, p_element = self.current_pressed_uri
936 self.current_pressed_uri = None
937 if (uri == p_uri and layout == p_layout and
938 element == p_element):
939 self.on_uri_clicked(uri)
941 def on_drawingarea_style_set(self, widget, previous_style, data=None):
942 if previous_style is None:
945 new = widget.style.font_desc.hash()
946 old = previous_style.font_desc.hash()
948 for layout in self.res_layout_list:
949 layout.recalc_char_widths()
952 def on_drawingarea_key_press_event(self, widget, event, data=None):
953 if event.type is not gtk.gdk.KEY_PRESS:
956 if event.keyval in (gtk.keysyms.Up, gtk.keysyms.Down,
957 gtk.keysyms.Page_Up, gtk.keysyms.Page_Down,
959 value = self.vscrollbar.get_value()
960 if event.keyval == gtk.keysyms.Up:
961 step_increment = self.adjustment.get_property("step-increment")
962 value = value - step_increment
963 elif event.keyval == gtk.keysyms.Down:
964 step_increment = self.adjustment.get_property("step-increment")
965 value = value + step_increment
966 elif event.keyval == gtk.keysyms.Page_Up:
967 step_increment = self.adjustment.get_property("page-increment")
968 value = value - step_increment
969 elif event.keyval == gtk.keysyms.Page_Down:
970 step_increment = self.adjustment.get_property("page-increment")
971 value = value + step_increment
972 elif event.keyval == gtk.keysyms.Home:
975 elif event.keyval == gtk.keysyms.End:
976 self.jump_to_the_end()