#!/usr/bin/env python
# -*- mode: python; coding: koi8-r -*-


import gobject, gtk, pango

from properties import TextProperties, ScrollProperties, ScrollType

##----------------------------------------------------------------------
##----------------------------------------------------------------------

class ScrolledText(gtk.TextView):

    text_buffer = None
    vadjustment = None
    hadjustment = None

    text_wrap_mode = gtk.WRAP_WORD

    MOUSE_SCROLL_NONE = 0
    MOUSE_SCROLL_STEP = 1
    MOUSE_SCROLL_PAGE = 2
    mouse_scroll_type = MOUSE_SCROLL_NONE

    auto_timer   = None
    auto_timeout = 50
    speed = 20

    key_scroll_page_value = 0
    key_scroll_flag = 0

    SCROLL_UP      = 1
    SCROLL_DOWN    = 2
    SCROLL_PG_UP   = 3
    SCROLL_PG_DOWN = 4

    keysyms_up      = (gtk.keysyms.KP_Up,
                       gtk.keysyms.Up)
    keysyms_down    = (gtk.keysyms.KP_Down,
                       gtk.keysyms.Down)
    keysyms_pg_up   = (gtk.keysyms.KP_Page_Up,
                       gtk.keysyms.Page_Up)
    keysyms_pg_down = (gtk.keysyms.KP_Page_Down,
                       gtk.keysyms.Page_Down,
                       gtk.keysyms.space)

    keysyms_pause_toggle = (gtk.keysyms.Insert,
                            gtk.keysyms.KP_Insert)
    keysyms_speed_up     = (gtk.keysyms.equal,
                            gtk.keysyms.KP_Add)
    keysyms_speed_down   = (gtk.keysyms.minus,
                            gtk.keysyms.KP_Subtract)

    # this keys has been ignored
    keysyms_dangerous = (gtk.keysyms.A, gtk.keysyms.a, # Ctrl-A->segfault ???
                         # changed position:
                         gtk.keysyms.Delete, gtk.keysyms.KP_Delete,
                         gtk.keysyms.BackSpace, gtk.keysyms.c,
                         gtk.keysyms.v
                         )

    pause = 1


    def __init__(self):

        gtk.TextView.__init__(self)

        gobject.signal_new('pause-toggled', ScrolledText,
                           gobject.SIGNAL_RUN_LAST,
                           gobject.TYPE_NONE,
                           (gobject.TYPE_INT,))

        gobject.signal_new('speed-changed', ScrolledText,
                           gobject.SIGNAL_RUN_LAST,
                           gobject.TYPE_NONE,
                           (gobject.TYPE_INT,))

        #self.text_buffer = gtk.TextBuffer(None)
        #self.set_buffer(self.text_buffer)

        self.set_editable(False)
        self.set_cursor_visible(False)

        self.vadjustment = gtk.Adjustment()
        self.hadjustment = gtk.Adjustment()

        self.set_scroll_adjustments(self.hadjustment, self.vadjustment)

        self.text_properties = TextProperties()
        self.scroll_properties = ScrollProperties()
        self.scroll_type = ScrollType()

        self.set_properties()

        self.set_property('height_request', 200)
        self.set_property('width_request', 300)

        self.set_wrap_mode(self.text_wrap_mode)
        #self.set_justification(gtk.JUSTIFY_FILL) # don't support yet

        self.connect('key-press-event', self.key_press_event_cb)
        self.connect('key-release-event', self.key_release_event_cb)

        self.connect('scroll-event', self.scroll_event_cb)

        if (self.mouse_scroll_type):
            self.button_press_id = self.connect('button_press_event',
                                                self.button_press_event_cb)
            self.button_release_id = self.connect('button_release_event',
                                                  self.button_press_event_cb)
        else:
            self.button_press_id = None
            self.button_release_id = None

##----------------------------------------------------------------------

    def pause_toggled(self, pause):

        self.pause = pause

        vadj = self.vadjustment
        if vadj.value >= vadj.upper - vadj.page_size:
            if self.auto_timer:
                gobject.source_remove(self.auto_timer)
                self.auto_timer = None
            self.pause = 1

        elif pause:
            if self.auto_timer:
                gobject.source_remove(self.auto_timer)
                self.auto_timer = None

        else:
            if not self.auto_timer:
                vadj.set_value(vadj.value + 1)
                self.auto_timer = gobject.timeout_add(int(self.auto_timeout),
                                                      self.auto_scroll_win)

        self.emit('pause-toggled', self.pause)

##----------------------------------------------------------------------

    def set_properties(self):

        for i in self.text_properties.__dict__:
            value = self.text_properties.__dict__[i].value
            if i == 'text_left_margin':
                self.set_property('left_margin', value)
                self.notify('left_margin')
            elif i == 'text_right_margin':
                self.set_property('right_margin', value)
                self.notify('right_margin')
            elif i == 'text_pixels_above_lines':
                self.set_pixels_above_lines(value)
            elif i == 'text_pixels_below_lines':
                self.set_pixels_below_lines(value)
            elif i == 'text_pixels_inside_wrap':
                self.set_pixels_inside_wrap(value)
            elif i == 'tabs_width':
                self.set_tab_width(value)

##----------------------------------------------------------------------

    def set_tab_width(self, tab_width=None):
        if tab_width is None:
            tab_width = self.text_properties.tabs_width.value
        pl = self.create_pango_layout(' '*tab_width)
        width, height = pl.get_pixel_size()

        ta = pango.TabArray(1, True)
        ta.set_tab(0, pango.TAB_LEFT, width)
        self.set_tabs(ta)

##----------------------------------------------------------------------

    def set_mouse_scroll_type(self, type):

        self.mouse_scroll_type = type

        if type == self.MOUSE_SCROLL_NONE:
            if self.button_press_id:
                self.disconnect(self.button_press_id)
                self.button_press_id = None
                self.disconnect(self.button_release_id)
                self.button_release_id = None
        else:

            if not self.button_press_id:
                self.button_press_id = self.connect('button_press_event',
                                                    self.button_press_event_cb)
                self.button_release_id = self.connect('button_release_event',
                                                      self.button_press_event_cb)

##----------------------------------------------------------------------

    def set_position(self, position):

        vadj = self.vadjustment
        vadj.set_value(position * (vadj.upper-vadj.page_size))

##----------------------------------------------------------------------

    def get_position(self):

        vadj = self.vadjustment
        if vadj.upper != vadj.page_size:
            return vadj.value/(vadj.upper-vadj.page_size)
        else:
            return 1.0

##----------------------------------------------------------------------

    def scroll_event_cb(self, w, event):

        vadj = self.vadjustment
        if event.direction == gtk.gdk.SCROLL_UP:
            decr = min(vadj.value, vadj.step_increment)
            if decr > 0:
                vadj.set_value(vadj.value - decr)
        elif event.direction == gtk.gdk.SCROLL_DOWN:
            incr = min(vadj.step_increment,
                       vadj.upper-vadj.value-vadj.page_size)
            if incr > 0:
                vadj.set_value(vadj.value + incr)

##----------------------------------------------------------------------

    def button_press_event_cb(self, w, event):

        if event.type == gtk.gdk.BUTTON_PRESS:

            if event.button == 1 \
                   and self.mouse_scroll_type == self.MOUSE_SCROLL_STEP:
                self.scroll_step_down()
                return True

            elif event.button == 1 \
                     and self.mouse_scroll_type == self.MOUSE_SCROLL_PAGE:
                self.scroll_page_down()
                return True

            elif event.button == 2 \
                     and self.mouse_scroll_type == self.MOUSE_SCROLL_STEP:
                self.scroll_step_up()
                return True

            elif event.button == 2 \
                 and self.mouse_scroll_type == self.MOUSE_SCROLL_PAGE:
                self.scroll_page_up()
                return True

        elif event.type == gtk.gdk.BUTTON_RELEASE:
            if self.mouse_scroll_type == self.MOUSE_SCROLL_STEP:
                self.key_scroll_flag = 0
            else:
                pass
            return True

        return False

        #return True # if True text_view don't processed this event

##----------------------------------------------------------------------

    def key_press_event_cb(self, w, event):

        if event.type == gtk.gdk.KEY_PRESS:

            if event.keyval in self.keysyms_pause_toggle:

                self.pause_toggled(not self.pause)

            elif event.keyval in self.keysyms_dangerous:
                return True

            elif event.keyval in self.keysyms_speed_up:
                if self.speed < 100:
                    self.speed += 1
                    self.speed_changed()

            elif event.keyval in self.keysyms_speed_down:
                if self.speed > 1:
                    self.speed -= 1
                    self.speed_changed()

            elif self.key_scroll_flag != 0:
                return True

            elif event.keyval in self.keysyms_up:
                self.scroll_step_up()

            elif event.keyval in self.keysyms_down:
                self.scroll_step_down()

            elif event.keyval in self.keysyms_pg_up:
                self.scroll_page_up()

            elif event.keyval in self.keysyms_pg_down:
                self.scroll_page_down()

            else:
                return False


            return True
        else:
            return False

##----------------------------------------------------------------------

    def key_release_event_cb(self, w, event):

        if event.type == gtk.gdk.KEY_RELEASE:

            if event.keyval in self.keysyms_up:
                self.key_scroll_flag = 0
            elif event.keyval in self.keysyms_down:
                self.key_scroll_flag = 0
            elif event.keyval in self.keysyms_pg_up:
                pass
            elif event.keyval in self.keysyms_pg_down:
                pass

            return True

        else:
            return False

##----------------------------------------------------------------------

    def scroll_step_up(self):
        vadj = self.vadjustment
        if self.scroll_type.smooth_step_scrolling.value:
            self.key_scroll_flag = self.SCROLL_UP
            gobject.idle_add(self.key_scroll_win)
            if not self.pause:
                gobject.source_remove(self.auto_timer)
        else:
            decr = min(vadj.value, vadj.step_increment)
            if decr > 0:
                vadj.set_value(vadj.value - decr)

    def scroll_step_down(self):
        vadj = self.vadjustment
        if self.scroll_type.smooth_step_scrolling.value:
            self.key_scroll_flag = self.SCROLL_DOWN
            gobject.idle_add(self.key_scroll_win)
            if not self.pause:
                gobject.source_remove(self.auto_timer)
        else:
            incr = min(vadj.step_increment,
                       vadj.upper-vadj.value-vadj.page_size)
            if incr > 0:
                vadj.set_value(vadj.value + incr)

    def scroll_page_up(self):
        vadj = self.vadjustment
        if self.scroll_type.smooth_page_scrolling.value:
            self.key_scroll_flag = self.SCROLL_PG_UP
            self.key_scroll_page_value = vadj.page_size \
                - vadj.step_increment
            gobject.idle_add(self.key_scroll_win)
            if not self.pause:
                gobject.source_remove(self.auto_timer)
        else:
            decr = min(vadj.value, vadj.page_increment)
            if decr > 0:
                vadj.set_value(vadj.value - decr)

    def scroll_page_down(self):
        vadj = self.vadjustment
        if self.scroll_type.smooth_page_scrolling.value:
            self.key_scroll_flag = self.SCROLL_PG_DOWN
            self.key_scroll_page_value = vadj.page_size \
                - vadj.step_increment
            gobject.idle_add(self.key_scroll_win)
            if not self.pause:
                gobject.source_remove(self.auto_timer)
        else:
            incr = min(vadj.page_increment,
                       vadj.upper-vadj.value-vadj.page_size)
            if incr > 0:
                vadj.set_value(vadj.value + incr)

##----------------------------------------------------------------------

    def auto_scroll_win(self):
        vadj = self.vadjustment
        if vadj.value < vadj.upper - vadj.page_size:
            #gtk.gdk.window_process_all_updates() # NO!!!
            win = self.get_window(gtk.TEXT_WINDOW_TEXT)
            if win:
                win.freeze_updates()
                vadj.set_value(vadj.value \
                     + self.scroll_properties.auto_scroll_increment.value)
                win.thaw_updates()

            return True
        else:
            #gobject.source_remove(self.auto_timer)
            self.auto_timer = None
            self.pause_toggled(1)
            return False

##----------------------------------------------------------------------

    def key_scroll_win(self):
        vadj = self.vadjustment

        if self.key_scroll_flag == self.SCROLL_UP:

            if vadj.value > 0:
                vadj.set_value(vadj.value \
                     - self.scroll_properties.key_scroll_increment.value)
                gobject.idle_add(self.key_scroll_win)
                return
            else:
                self.key_scroll_flag = 0

        elif self.key_scroll_flag == self.SCROLL_DOWN:

            if vadj.value < vadj.upper - vadj.page_size:
                vadj.set_value(vadj.value \
                     + self.scroll_properties.key_scroll_increment.value)
                gobject.idle_add(self.key_scroll_win)
                return
            else:
                self.key_scroll_flag = 0
                if not self.pause:
                    self.pause_toggled(0)
        elif self.key_scroll_flag == self.SCROLL_PG_UP:

            if self.key_scroll_page_value <= 0 or vadj.value <= 0:
                self.key_scroll_page_value = 0
                self.key_scroll_flag = 0
            else:
                self.key_scroll_page_value = self.key_scroll_page_value \
                    - self.scroll_properties.key_scroll_page_increment.value
                vadj.set_value(vadj.value \
                    - self.scroll_properties.key_scroll_page_increment.value)

                gobject.idle_add(self.key_scroll_win)
                return

        elif self.key_scroll_flag == self.SCROLL_PG_DOWN:

            if self.key_scroll_page_value <= 0 \
                   or vadj.value >= vadj.upper - vadj.page_size:

                self.key_scroll_page_value = 0
                self.key_scroll_flag = 0
            else:
                self.key_scroll_page_value = self.key_scroll_page_value \
                    - self.scroll_properties.key_scroll_page_increment.value
                vadj.set_value(vadj.value \
                    + self.scroll_properties.key_scroll_page_increment.value)

                gobject.idle_add(self.key_scroll_win)
                return

        if not self.pause:
            self.auto_timer = gobject.timeout_add(int(self.auto_timeout),
                                                  self.auto_scroll_win)


##----------------------------------------------------------------------

    def speed_changed(self):

        self.auto_timeout = 1000 / self.speed
        self.emit('speed-changed', self.speed)
        if self.auto_timer:
            gobject.source_remove(self.auto_timer)
            self.auto_timer = gobject.timeout_add(int(self.auto_timeout),
                                                  self.auto_scroll_win)


##----------------------------------------------------------------------
##----------------------------------------------------------------------

if(__name__=="__main__"):

    window = gtk.Window()
    st = ScrolledText()

    data = (('this is the test ' * 100) + '\n') * 100

    text_buffer = gtk.TextBuffer()
    st.set_buffer(text_buffer)

    iter = text_buffer.get_iter_at_offset(0)
    st.scroll_type.smooth_step_scrolling.value = 0
    st.scroll_type.smooth_page_scrolling.value = 0

    text_buffer.insert(iter, data)

    window.add(st)
    window.connect('destroy', lambda w: gtk.main_quit())
    window.show_all()
    gtk.main()

