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


import sys, string, os
import time

import gtk, gtk.glade, pango, gobject
glade = gtk.glade
gdk = gtk.gdk

from config import *
from miscutils import *
from bookmarkswindow import BookmarksWindow
from bookinfowindow import BookInfoWindow
from bookmarks import Bookmarks
from scrolledtext import ScrolledText
#from pybookreader.pagedtext import *
from bookbuffer import BookBuffer
from propswindow import PropsWindow
from properties import MainProperties
from dictclient.pygtkdict import PyGtkDict
import filters


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

class PyBookReader:

    MOUSE_SCROLL_NONE = 0
    MOUSE_SCROLL_STEP = 1
    MOUSE_SCROLL_PAGE = 2

    current_book = None

    bookmarks_manager = None
    book_info = None
    dict_client = None

    # search vars
    # TODO: to bookbuffer

    search_string = ''
    search_regexp = ''

    search_string_flag = 0
    search_regexp_flag = 0

    backward_queue = []
    foreward_queue = []

    current_cursor = gdk.XTERM


    def __init__(self):

        self.make_config_dir()

        self.create_widgets()

        self.main_properties = MainProperties()

        self.bookmarks = Bookmarks()

        self.read_config()

        # show/hide widgets
        a = self.main_properties.hide_toolbar.value
        self.wTree.get_widget('hide_toolbar_menuitem').set_active(a)
        a = self.main_properties.hide_statusbar.value
        self.wTree.get_widget('hide_statusbar_menuitem').set_active(a)
        a = self.main_properties.hide_scrollbar.value
        self.wTree.get_widget('hide_scrollbar_menuitem').set_active(a)

        self.fullscreen_mode = False
        # geometry in unfullscreen mode
        self.prev_geometry = (0, 0)


    def set_current_book(self, filename=None):

        if not filename:
            book = self.bookmarks.get_current_book()
            if book:
                filename = book.filename

        if filename:
            # --- check file ---
            try:
                open(filename)
            except Exception, err:
                error_dialog(self.main_window, 'Can\'t open file ',
                             '%s: %s' % (filename, str(err)))
                self.update_books_menu()
                return

            gobject.idle_add(self.insert_file, filename)



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


    def create_widgets(self):

        glade_file=get_glade_file()
        self.wTree=glade.XML(glade_file, 'main_window')

        # callbacks
        self.wTree.signal_autoconnect(self)

        self.main_window = self.wTree.get_widget('main_window')

        # text widget
        text_widget_box = self.wTree.get_widget('text_widget_box')
        self.text_widget = ScrolledText()
        text_widget_box.pack_end(self.text_widget)
        self.text_widget.show()

        self.scrollbar = self.wTree.get_widget('scrollbar')
        self.scrollbar.set_adjustment(self.text_widget.vadjustment)

        # buttons
        self.backward_button = self.wTree.get_widget('backward_button')
        self.backward_button.set_sensitive(False)
        self.foreward_button = self.wTree.get_widget('foreward_button')
        self.foreward_button.set_sensitive(False)

        self.pause_button = self.wTree.get_widget('pause_button')
        self.pause_button.set_active(True)

        # text buffer
        self.text_buffer = BookBuffer()
        self.text_widget.set_buffer(self.text_buffer)
        self.text_buffer.connect('goto-link', self.text_buffer_goto_link)

        self.text_widget.vadjustment.connect('value_changed',
                                             self.position_changed_cb)
        self.text_widget.connect('motion-notify-event',
                                 self.motion_notify_event)
        self.text_widget.connect('pause-toggled', self.pause_toggled)
        self.text_widget.connect('speed-changed', self.speed_changed)
        self.text_widget.connect('unrealize', self.text_widget_unrealize)

        self.text_widget.grab_focus()

        #self.text_widget.connect('button_press_event',
        #                         self.text_widget_button_press_event_cb)

        self.books_menu = self.wTree.get_widget('books_menu')
        self.bookmarks_menu = self.wTree.get_widget('bookmarks_menu')
        self.content_menu = self.wTree.get_widget('content_menu')

        if gtk.pygtk_version < (2, 2):
            wTree.get_widget('fullscreen_menuitem').set_sensitive(False)

        # charsets menu
        self.charsets_menu = CharsetMenu(self.menu_charset_cb)
        self.wTree.get_widget('charsets_menu').set_submenu(self.charsets_menu)

        # filters menu
        self.filters_menu = FiltersMenu(self.main_window, self.menu_filters_cb)
        self.wTree.get_widget('filters_menu').set_submenu(self.filters_menu)

        self.toolbar = self.wTree.get_widget('toolbar')
        self.statusbar = self.wTree.get_widget('statusbar')
        self.percent_status = self.wTree.get_widget('percent_status')
        self.speed_status = self.wTree.get_widget('speed_status')

        # clock
        self.clock_label = self.wTree.get_widget('clock_label')
        self.clock_cb()
        self.clock_timer = gobject.timeout_add(10000, self.clock_cb)

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

    def exit_cb(self, *args):
        self.main_window.destroy()

    def reload_cb(self, *args):
        self.insert_file(self.current_book.filename)

    def menu_quit_without_saving_cb(self, *args):
        gtk.main_quit()

    def menu_fullscreen_cb(self, *args):
        if self.fullscreen_mode:
            self.main_window.unfullscreen()
        else:
            self.prev_geometry = self.main_window.get_size()
            self.main_window.fullscreen()
        self.fullscreen_mode = not self.fullscreen_mode

    def menu_dict_client_cb(self, w):
        text = ''
        sel = self.text_buffer.get_selection_bounds()
        if sel:
            text = self.text_buffer.get_text(sel[0], sel[1], True)

        if self.dict_client:
            self.dict_client.window.present()
        else:
            # search glade file
            gf = 'pybookreader/dictclient/pygtkdict.glade'
            dictclient_glade_file = None
            for d in sys.path:
                f = os.path.join(d, gf)
                if os.path.exists(f):
                    dictclient_glade_file = f
                    break

            if not dictclient_glade_file: return
            self.dict_client = PyGtkDict(dictclient_glade_file,
                                         dictclient_rc_file,
                                         dictclient_history_file)

            self.dict_client.window.connect('destroy',
                                            self.dict_client_destroy_cb)
        if text:
            self.dict_client.do_search_word(text)

    def dict_client_destroy_cb(self, w):
        try:
            # exit from thread
            # это не нужно, но на всякий случай...
            self.dict_client.words_queue.put(None)
        except:
            pass
        self.dict_client = None

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

    def menu_goto_begin_cb(self, w):
        self.backward_queue_append_pos()
        self.text_widget.set_position(0.0)

    def menu_goto_end_cb(self, w):
        self.backward_queue_append_pos()
        self.text_widget.set_position(1.0)


    def text_buffer_goto_link(self, w, offset):

        self.backward_queue_append_pos()
        iter = self.text_buffer.get_iter_at_offset(offset)
        self.text_widget.scroll_to_iter(iter, 0.0,
                                        use_align=True, xalign=0.0, yalign=0.0)

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

    def backward_queue_append_pos(self):
        pos = self.text_widget.get_position()
        self.backward_queue.append(pos)
        self.backward_button.set_sensitive(True)
        self.foreward_queue = []
        self.foreward_button.set_sensitive(False)

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

    def debug_cb(self, w):
        import locale
        print locale.getlocale()
##         from pprint import pprint
##         print '** categories_list **'
##         pprint(self.bookmarks.categories_list)
##         print '** books_list **'
##         pprint(self.bookmarks.books_list)
##         print '** titles_list **'
##         pprint(self.bookmarks.titles_list)

##         all_books = self.bookmarks.get_all_books()
##         print '**** books ****'
##         for filename in all_books:
##             book = all_books[filename]
##             print 'filename', book.filename
##             #print 'file_type', book.file_type
##             #print 'author', book.author
##             #print 'name', book.name
##             print 'genres', book.genres
##             print 'categories', book.categories
##             print 'categories is genres', book.categories is book.genres



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

    def menu_book_information_cb(self, w):
        if not self.current_book: return

        if self.book_info:
            self.book_info.window.present()
        else:
            BookInfoWindow.categories_list = list(self.bookmarks.categories_list)
            BookInfoWindow.width = self.main_properties.book_info_width.value
            BookInfoWindow.height = self.main_properties.book_info_height.value
            i = BookInfoWindow(self.current_book)
            self.book_info = i
            w = self.book_info.window
            w.connect('unrealize', self.book_info_unrealize_cb)
            i.connect('bookmark-removed', self.book_info_bookmark_removed_cb)
            i.connect('book-info-changed', self.book_info_changed_cb)
            i.connect('categories-changed', self.book_info_categories_changed_cb)
            i.connect('goto-bookmark', self.book_info_goto_bookmark_cb)
            i.bookmarks_paned.set_position(self.main_properties.book_info_bookmarks_paned_pos.value)

    def book_info_bookmark_removed_cb(self, w):
        self.update_bookmarks_menu()
        self.text_buffer.update_highlight_bookmarks()
        if self.bookmarks_manager:
            self.bookmarks_manager.update_treeview()


    def book_info_changed_cb(self, w):
        #self.book_changed()
        # save for change titles list (FIXME)
        #self.bookmarks.save_book(self.current_book)
        #title = self.bookmarks.titles_list[self.current_book.filename]

        title = self.current_book.get_title()
        self.bookmarks.titles_list[self.current_book.filename] = title

        self.main_window.set_title('%s: %s' % (program_name, title))
        self.update_books_menu()
        if self.bookmarks_manager:
            self.bookmarks_manager.update_treeview()


    def book_info_categories_changed_cb(self, w, cat, state):
        if state:
            self.bookmarks.categories_list[cat].append(self.current_book.filename)
            self.current_book.categories.append(cat)
        else:
            self.bookmarks.remove_book(self.current_book.filename, cat)
        #self.book_changed()
        if self.bookmarks_manager:
            self.bookmarks_manager.update_treeview()


    def book_info_goto_bookmark_cb(self, w, mark_number):
        m = self.bookmarks.get_mark(self.current_book, mark_number)
        if m:
            self.backward_queue_append_pos()
            self.current_book.current_position = m.position
            self.current_book.current_offset = m.offset
            self.set_position()


    def book_info_unrealize_cb(self, w):

        width, height = w.get_size()
        self.main_properties.book_info_width.value = width
        self.main_properties.book_info_height.value = height
        self.main_properties.book_info_bookmarks_paned_pos.value \
            = self.book_info.bookmarks_paned.get_position()
        self.book_info = None

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

    def clock_cb(self):
        self.clock_label.set_text(time.strftime('%H:%M', time.localtime()))
        return True

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

    def main_window_destroy_cb(self, *args):
        gobject.idle_add(self.exit)

    def exit(self):
        try: # debug
##             self.main_window.hide()
##             if self.bookmarks_manager:
##                 self.bookmarks_manager.window.hide()
##                 self.bookmarks_manager.window.destroy()
            if self.make_config_dir():
                self.bookmarks.save_bookmarks()
                self.bookmarks.save_current_book(self.current_book)
                self.save_config()
                #self.save_filetypes()

        finally:
            gtk.main_quit()

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

    def main_window_unrealize_cb(self, *args):
        # save geometry
        if not self.fullscreen_mode:
            width, height = self.main_window.get_size()
        else:
            width, height = self.prev_geometry
        self.main_properties.width.value = width
        self.main_properties.height.value = height

        self.main_window.hide()
        if self.bookmarks_manager:
            self.bookmarks_manager.window.hide()
            self.bookmarks_manager.window.destroy()
        if self.book_info:
            self.book_info.window.hide()
            self.book_info.window.destroy()
        if self.dict_client:
            self.dict_client.window.destroy()

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

    def save_as_cb(self, w):
        if not self.current_book: return

        dialog = OpenFileDialog(self.main_window, save_as=1)
        filename = dialog.filename
        if filename:
            start, end = self.text_buffer.get_bounds()
            text = self.text_buffer.get_text(start, end, True)
            text = unicode(text).encode(self.current_book.encoding)
            try:
                open(filename, 'w').write(text)
            except IOError, err:
                error_dialog(self.main_window,
                             'Can\'t write file %s: %s' % (filename, str(err)))
            else:
                # change book.filename
                self.bookmarks.remove_book(self.current_book.filename)
                self.current_book.filename = filename
                self.bookmarks.add_new_book(self.current_book)
                # change book attrs
                self.current_book.file_type = 'plain_text'
                self.current_book.internalfilters = None
                self.current_book.externalfilter = None
                # change main window title
                title = self.bookmarks.titles_list[self.current_book.filename]
                self.main_window.set_title('%s: %s' % (program_name, title))
                # updates
                self.update_books_menu()
                self.filters_menu.update(self.current_book)
                if self.bookmarks_manager:
                    self.bookmarks_manager.update_treeview()
                if self.book_info:
                    self.book_info.set_book(self.current_book)

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

    def text_widget_button_press_event_cb(self, w, event):
        # пока не используется: TODO
        if event.type == gdk.BUTTON_PRESS:
            if event.button == 3:

                x, y = self.text_widget.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT,
                                                                event.x, event.y)

                iter = self.text_widget.get_iter_at_location(x, y)
                mark = self.current_book.get_mark_at_offset(iter.get_offset())

                if mark:

                    menu = gtk.Menu()
                    menuitem = gtk.MenuItem(mark.description)
                    menu.add(menuitem)
                    menuitem.show()
                    #menuitem = gtk.MenuItem('Edit bookmark')
                    menuitem.connect('activate',
                                     self.popup_menu_activate_mark_cb,
                                     mark)
                    #menu.add(menuitem)
                    #menuitem.show()

                    menu.popup(None, None, None, event.button, event.time)
                    menu.show()

                return True

        return False


    def popup_menu_activate_mark_cb(self, w, mark):

        desc = text_dialog(self, 'Description', mark.description)
        if desc:
            mark.description = desc

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

    def hide_toolbar_cb(self, w):
        if w.get_active(): #self.toolbar.get_property('visible'):
            self.main_properties.hide_toolbar.value = 1
            self.toolbar.hide()
        else:
            self.main_properties.hide_toolbar.value = 0
            self.toolbar.show()


    def hide_status_bar_cb(self, w):
        if w.get_active():
            self.main_properties.hide_statusbar.value = 1
            self.statusbar.hide()
        else:
            self.main_properties.hide_statusbar.value = 0
            self.statusbar.show()


    def hide_scroll_bar_cb(self, w):
        if w.get_active():
            self.main_properties.hide_scrollbar.value = 1
            self.scrollbar.hide()
        else:
            self.main_properties.hide_scrollbar.value = 0
            self.scrollbar.show()

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

    def menu_toolbar_both_cb(self, w):
        self.toolbar.set_style(gtk.TOOLBAR_BOTH)
        self.main_properties.toolbar_style.value = 'both'

    def menu_toolbar_icons_cb(self, w):
        self.toolbar.set_style(gtk.TOOLBAR_ICONS)
        self.main_properties.toolbar_style.value = 'icons'

    def menu_toolbar_text_cb(self, w):
        self.toolbar.set_style(gtk.TOOLBAR_TEXT)
        self.main_properties.toolbar_style.value = 'text'

    def menu_toolbar_both_horiz_cb(self, w):
        self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
        self.main_properties.toolbar_style.value = 'both_horiz'

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

    def menu_regexp_search_cb(self, w):

        strng = entry_dialog(self.main_window, 'Search', 'Regexp: ',
                             self.search_regexp)
        if not strng: return

        self.search_regexp = strng

        iter = self.text_buffer.search_regexp(strng)
        if iter:
            self.text_widget.scroll_to_iter(iter, 0.0)
            self.search_regexp_flag = 1
        else:
            info_dialog(self.main_window, 'Not found')


    def menu_regexp_search_again_cb(self, w):

        if self.search_regexp_flag:
            iter = self.text_buffer.search_regexp_again()
            if iter:
                self.text_widget.scroll_to_iter(iter, 0.0)
            else:
                info_dialog(self.main_window, 'Not found')

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

    def menu_search_cb(self, w):

        strng = entry_dialog(self.main_window, 'Search',
                             'Search: ', self.search_string)
        if not strng: return # or not strng.strip()

        self.search_string = strng

        iter = self.text_buffer.search_string(strng)
        if iter:
            self.text_widget.scroll_to_iter(iter, 0.0)
            self.search_string_flag = 1
        else:
            info_dialog(self.main_window, 'Not found')


    def menu_search_again_forward_cb(self, w):

        if not self.search_string_flag: return

        iter = self.text_buffer.search_string_again_forward()
        if iter:
            self.text_widget.scroll_to_iter(iter, 0.0)
        else:
            info_dialog(self.main_window, 'Not found')


    def menu_search_again_backward_cb(self, w):

        if not self.search_string_flag: return

        iter = self.text_buffer.search_string_again_backward()
        if iter:
            self.text_widget.scroll_to_iter(iter, 0.0)
        else:
            info_dialog(self.main_window, 'Not found')

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

    def menu_regexp_bookmarks_cb(self, w):

        strng = entry_dialog(self.main_window, 'Regexp', 'Regexp: ')

        if strng:
            # пытался сделать progress bar
##             dialog = gtk.Dialog('Progress', self.main_window,
##                                 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
##                                 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
##             hbox = gtk.HBox()
##             dialog.vbox.pack_start(hbox, True, True, 0)
##             label = gtk.Label('Progress: ')
##             hbox.pack_start(label, expand=False)
##             progress = gtk.ProgressBar()
##             hbox.pack_start(progress)
##             dialog.show_all()

            #gdk.window_process_all_updates()
            #gdk.flush()

            self.text_buffer.regexp_bookmarks_init(strng)

            while 1:
                if not self.text_buffer.regexp_bookmarks_next():
                    break

##                 fract = float(self.text_buffer.line_index) \
##                        / self.text_buffer.line_count
##                 progress.set_fraction(fract)
##                 gdk.window_process_all_updates()
##                 gdk.flush()

            numder_bookmarks = self.text_buffer.regexp_bookmarks_finally()
            info_dialog(self.main_window,
                        '%d bookmarks created' % numder_bookmarks)
            if self.bookmarks_manager:
                self.bookmarks_manager.update_treeview()
            if self.book_info:
                self.book_info.set_book(self.current_book)

            self.update_bookmarks_menu()

##             dialog.destroy()

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

    def motion_notify_event(self, widget, event):
        x, y, z = widget.window.get_pointer()
        x, y = widget.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, x, y)
        tags = widget.get_iter_at_location(x, y).get_tags()
        is_over_anchor = False
        if self.text_buffer.link_tag in tags or \
           self.text_buffer.note_tag in tags:
            is_over_anchor = True
        if is_over_anchor:
            if self.current_cursor is gdk.XTERM:
                self.current_cursor = gdk.HAND2
                window = widget.get_window(gtk.TEXT_WINDOW_TEXT)
                window.set_cursor(gdk.Cursor(gdk.HAND2))
        else:
            if self.current_cursor is gdk.HAND2:
                self.current_cursor = gdk.XTERM
                window = widget.get_window(gtk.TEXT_WINDOW_TEXT)
                window.set_cursor(gdk.Cursor(gdk.XTERM))
        return False

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

    def pause_toggled(self, w, a):
        self.pause_button.set_active(a)

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

    def speed_changed(self, w, a):
        self.speed_status.set_text('Speed: %d' % self.text_widget.speed)

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

    def text_widget_unrealize(self, w):
        self.save_bookmark()

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

    def position_changed_cb(self, w):

        self.bookmark_value = self.text_widget.get_position()
        percent = int(self.bookmark_value*100)
        self.percent_status.set_text('%d%%' % percent)

##----------------------------------------------------------------------
##-- signals from bookmarks manager ------------------------------------

    def bookmarks_manager_unrealize_cb(self, w):
        width, height = w.get_size()
        self.main_properties.bookmarks_manager_width.value = width
        self.main_properties.bookmarks_manager_height.value = height
        self.main_properties.bookmarks_manager_paned_position.value \
             = self.bookmarks_manager.paned.get_position()
        self.main_properties.bookmarks_manager_show_category.value \
             = self.bookmarks_manager.show_category
        self.main_properties.bookmarks_manager_hide_bookmarks.value \
             = self.bookmarks_manager.hide_bookmarks
        self.main_properties.bookmarks_manager_hide_cover.value \
             = self.bookmarks_manager.hide_cover
        self.bookmarks_manager = None

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

    def bookmarks_manager_load_file_cb(self, w, filename):

        self.insert_file(filename)
##         if self.main_properties['filename'][1] != filename:
##             self.insert_file(filename)
##         else:
##             self.set_position()

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

    def bookmarks_manager_book_removed_cb(self, w, filename):

        if self.current_book and self.current_book.filename == filename:
            book = self.bookmarks.get_any_book()
            if book:
                # чтобы не сохранять файл
                self.current_book = None
                self.insert_file(book.filename)
            else:
                self.main_window.set_title(program_name)
                # clear buffer
                start, end = self.text_buffer.get_bounds()
                self.text_buffer.delete(start, end)

                self.current_book = None
                if self.book_info:
                    self.book_info.window.destroy()

        self.update_books_menu()

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

    def bookmarks_manager_book_changed_cb(self, w, filename):

        book = self.bookmarks.get_book(filename)
        title = book.get_title()
        self.bookmarks.titles_list[filename] = title
        self.update_books_menu()
        #self.bookmarks.save_book(self.bookmarks.get_book(filename))

        if self.current_book.filename == filename:
            title = self.bookmarks.titles_list[self.current_book.filename]
            self.main_window.set_title('%s: %s' % (program_name, title))
            if self.book_info:
                self.book_info.set_book(self.current_book)

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

    def bookmarks_manager_bookmarks_changed_cb(self, w, filename):
        if not self.current_book: return
        if  self.current_book.filename == filename or filename is None:
            self.update_bookmarks_menu()
            self.text_buffer.update_highlight_bookmarks()
            if self.book_info:
                self.book_info.set_book(self.current_book)

##----------------------------------------------------------------------
##-- menu signals ------------------------------------------------------

    def open_cb(self, w):

        dialog = OpenFileDialog(self.main_window)
        if dialog.filename:
            self.insert_file(dialog.filename) #, 1)
            #if self.bookmarks_manager: # and update_treeview:
            #    self.bookmarks_manager.update_treeview()

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

    def menu_bookmarks_manager_cb(self, w):
        # create and initialize bookmarks manager window
        if not self.bookmarks_manager:
            BookmarksWindow.hide_bookmarks \
                = self.main_properties.bookmarks_manager_hide_bookmarks.value
            BookmarksWindow.hide_cover \
                = self.main_properties.bookmarks_manager_hide_cover.value
            BookmarksWindow.show_category \
                = self.main_properties.bookmarks_manager_show_category.value

            m = BookmarksWindow(self.bookmarks)
            self.bookmarks_manager = m
            w = m.window
            #w.connect('destroy', self.bookmarks_manager_destroy_cb)
            w.connect('unrealize', self.bookmarks_manager_unrealize_cb)
            m.connect('load-file', self.bookmarks_manager_load_file_cb)
            m.connect('book-removed', self.bookmarks_manager_book_removed_cb)
            m.connect('book-changed', self.bookmarks_manager_book_changed_cb)
            m.connect('bookmarks-changed',
                      self.bookmarks_manager_bookmarks_changed_cb)
            m.connect('book-added', lambda w: self.update_books_menu())

            width = self.main_properties.bookmarks_manager_width.value
            height = self.main_properties.bookmarks_manager_height.value
            w.resize(width, height)
            pos = self.main_properties.bookmarks_manager_paned_position.value
            m.paned.set_position(pos)
            w.show()


        else:
            self.bookmarks_manager.window.present()

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

    def menu_goto_position_cb(self, w):

        strng = entry_dialog(self.main_window, 'Position', 'Position(%): ')
        if strng:
            try:
                num = float(strng)
            except:
                pass
            else:
                if 0 <= num <= 100:
                    self.backward_queue_append_pos()
                    self.text_widget.set_position(num/100.0)

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

    # FONT
    def menu_font_cb(self, w):

        dialog = gtk.FontSelectionDialog('%s: Select font' % program_name)

##         dialog.apply_button.show()
##         dialog.cancel_button.set_label(gtk.STOCK_CLOSE)

        dialog.ok_button.hide()
        #dialog.apply_button.hide()
        dialog.cancel_button.hide()

        dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
        dialog.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
        dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_REJECT)

        fontname = self.text_widget.get_style().font_desc.to_string()
        dialog.set_font_name(fontname)
        dialog.set_transient_for(self.main_window)
        dialog.show()

        while 1:

            response = dialog.run()

            if response in (gtk.RESPONSE_OK, gtk.RESPONSE_APPLY):
                font = dialog.get_font_name()
                fd = pango.FontDescription(font)
                self.text_widget.modify_font(fd)
                self.text_widget.set_tab_width()
                if self.current_book.exclusive_view:
                    self.current_book.font = font
                else:
                    self.main_properties.font.value = font

            if response in (gtk.RESPONSE_OK, gtk.RESPONSE_REJECT,
                              gtk.RESPONSE_DELETE_EVENT):
                break

        dialog.destroy()

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

    # COLOR
    def menu_fg_color_cb(self, w):
        self.change_color(0)

    def menu_bg_color_cb(self, w):
        self.change_color(1)

    def menu_bm_color_cb(self, w):
        self.change_color(2)

    def menu_links_color_cb(self, w):
        self.change_color(3)

    def change_color(self, a):

        dialog = gtk.ColorSelectionDialog('%s: Select color' % program_name)

        dialog.ok_button.hide()
        dialog.cancel_button.hide()

        dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
        dialog.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
        dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_REJECT)
        dialog.colorsel.set_has_palette(True)
        dialog.set_transient_for(self.main_window)
        dialog.show()

        while 1:

            response = dialog.run()

            if response in (gtk.RESPONSE_OK, gtk.RESPONSE_APPLY):

                color = dialog.colorsel.get_current_color()
                color_val = '#%02x%02x%02x' \
                            % (color.red>>8,
                               color.green>>8,
                               color.blue>>8)
                if a == 0:  # foreground
                    self.text_widget.modify_text(gtk.STATE_NORMAL, color)
                    if self.current_book.exclusive_view:
                        self.current_book.fg_color = color_val
                    else:
                        self.main_properties.fg_color.value = color_val
                elif a == 1: # background
                    self.text_widget.modify_base(gtk.STATE_NORMAL, color)
                    if self.current_book.exclusive_view:
                        self.current_book.bg_color = color_val
                    else:
                        self.main_properties.bg_color.value = color_val
                elif a == 2: # bookmarks
                    self.text_buffer.change_bookmark_tag_color(color_val)
                    if self.current_book.exclusive_view:
                        self.current_book.bm_color = color_val
                    else:
                        self.main_properties.bookmark_tag_color.value = color_val
                elif a == 3: # links
                    self.text_buffer.change_link_tag_color(color_val)
                    if self.current_book.exclusive_view:
                        self.current_book.link_color = color_val
                    else:
                        self.main_properties.link_tag_color.value = color_val

            if response in (gtk.RESPONSE_OK, gtk.RESPONSE_REJECT,
                            gtk.RESPONSE_DELETE_EVENT):
                break


        dialog.destroy()

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

    def menu_props_cb(self, w):

        dialog = PropsWindow(self)

        while 1:

            response = dialog.run()

            if response in (gtk.RESPONSE_OK, gtk.RESPONSE_APPLY):

                for i in dialog.scroll_properties:
                    self.text_widget.scroll_properties.__dict__[i].value \
                        = dialog.scroll_properties[i]

                for i in dialog.scroll_type:
                    self.text_widget.scroll_type.__dict__[i].value \
                         = dialog.scroll_type[i]

                for i in dialog.text_properties:
                    self.text_widget.text_properties.__dict__[i].value \
                         = dialog.text_properties[i]
                if len(dialog.text_properties) != 0:
                    self.text_widget.set_properties()

                if not dialog.mouse_scroll_type is None:
                    self.text_widget.set_mouse_scroll_type(dialog.mouse_scroll_type)

            if response in (gtk.RESPONSE_OK, gtk.RESPONSE_REJECT,
                            gtk.RESPONSE_DELETE_EVENT):
                break

        dialog.destroy()

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

    def menu_set_bookmark_cb(self, w):

        # search offset
        rect = self.text_widget.get_visible_rect()
        iter = self.text_widget.get_iter_at_location(rect.x, rect.y)
        offset = iter.get_offset()

        desc = self.text_buffer.mark_description(offset)

        self.current_book.add_bookmark(self.text_widget.get_position(),
                                       offset, 0, desc)
        self.text_buffer.highlight_bookmark(offset)
        if self.bookmarks_manager:
            self.bookmarks_manager.update_treeview()
        if self.book_info:
            self.book_info.set_book(self.current_book)

        self.update_bookmarks_menu()

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

    def menu_set_bookmark_with_cb(self, w):

        desc = entry_dialog(self.main_window, 'Description', 'Description: ')
        if desc != None:
            rect = self.text_widget.get_visible_rect()
            iter = self.text_widget.get_iter_at_location(rect.x, rect.y)
            offset = iter.get_offset()
            self.current_book.add_bookmark(self.text_widget.get_position(),
                                           offset, 0, desc)
            self.text_buffer.highlight_bookmark(offset)
            if self.bookmarks_manager:
                self.bookmarks_manager.update_treeview()
            if self.book_info:
                self.book_info.set_book(self.current_book)

        self.update_bookmarks_menu()

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

    def backward_cb(self, w):
        if self.backward_queue:
            pos = self.backward_queue[-1]
            self.foreward_queue.append(self.text_widget.get_position())
            self.text_widget.set_position(pos)
            del self.backward_queue[-1]
            self.foreward_button.set_sensitive(True)
            if not self.backward_queue:
                self.backward_button.set_sensitive(False)


    def foreward_cb(self, w):
        if self.foreward_queue:
            pos = self.foreward_queue[-1]
            self.backward_queue.append(self.text_widget.get_position())
            self.text_widget.set_position(pos)
            del self.foreward_queue[-1]
            self.backward_button.set_sensitive(True)
            if not self.foreward_queue:
                self.foreward_button.set_sensitive(False)

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

    def fast_cb(self, w):
        self.text_widget.speed = min(self.text_widget.speed+1, 100)
        self.text_widget.speed_changed()

    def slow_cb(self, w):
        self.text_widget.speed = max(self.text_widget.speed-1, 1)
        self.text_widget.speed_changed()

    def pause_cb(self, w):
        self.text_widget.pause_toggled(w.get_active())

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

    def menu_charset_cb(self, w, cs):
        if not self.current_book: return
        if self.current_book.encoding == cs: return

        self.current_book.encoding = cs
        self.insert_file(self.current_book.filename)
        if self.bookmarks_manager:
            self.bookmarks_manager.update_treeview()

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

    def insert_file(self, filename): #, update_treeview=0):

        # --- check file ---
        try:
            fd =  open(filename)
        except Exception, err:
            error_dialog(self.main_window,
                         'Can\'t open file ', '%s: %s' % (filename, str(err)))
            return

        fd.close()

        #print 'insert file'

        book = self.bookmarks.get_book(filename)
        if book:
            found = 1
        else:
            found = 0

        if self.current_book:
            self.save_bookmark()

        if found:
            self.text_buffer.set_book(book)
        else:
            try:
                self.text_buffer.new_book(filename)
            except Exception, err:
                error_dialog(self.main_window,
                             'Can\'t open file ',
                             '%s: %s' % (filename, str(err)))
                return

        try:
            self.text_buffer.load_file()
        except Exception, err:
            print '>>', err
            error_dialog(self.main_window,
                         'Can\'t open file ', '%s: %s' % (filename, str(err)))
            return

        self.current_book = self.text_buffer.book

        # set title
        title = self.current_book.get_title()
        self.main_window.set_title('%s: %s' % (program_name, title))
        # if new book
        if not found:
            self.bookmarks.add_new_book(self.current_book)

        # set position
        if found:
            # FIXME: how???
            gobject.idle_add(self.set_position)
            #gobject.timeout_add(1000, self.set_position)
            #self.set_position()

        #print 'insert file - end'

        # update menus
        self.update_bookmarks_menu()
        self.update_content_menu()
        self.update_books_menu()
        self.charsets_menu.set_charset(self.current_book.encoding)
        self.filters_menu.update(self.current_book)

        self.foreward_queue = []
        self.backward_queue = []
        self.foreward_button.set_sensitive(False)
        self.backward_button.set_sensitive(False)

        if self.current_book.exclusive_view:
            self.wTree.get_widget('set_exclusive_view_menuitem').set_active(True)
        else:
            self.wTree.get_widget('set_exclusive_view_menuitem').set_active(False)

        if not found and self.bookmarks_manager:
            # update bookmarks manager
            self.bookmarks_manager.update_treeview()
        if self.book_info:
            self.book_info.set_book(self.current_book)

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

    def menu_set_exclusive_view_cb(self, w):
        if not self.current_book: return

        if w.get_active():
            self.current_book.exclusive_view = 1
            if not self.current_book.font:
                self.current_book.font = self.main_properties.font.value
                self.current_book.fg_color = self.main_properties.fg_color.value
                self.current_book.bg_color = self.main_properties.bg_color.value
                self.current_book.bm_color = self.main_properties.bookmark_tag_color.value
                self.current_book.link_color = self.main_properties.link_tag_color.value
            else:
                fd = pango.FontDescription(self.current_book.font)
                self.text_widget.modify_font(fd)
                color = gdk.color_parse(self.current_book.fg_color)
                self.text_widget.modify_text(gtk.STATE_NORMAL, color)
                color = gdk.color_parse(self.current_book.bg_color)
                self.text_widget.modify_base(gtk.STATE_NORMAL, color)
                self.text_buffer.change_bookmark_tag_color(self.current_book.bm_color)
                self.text_buffer.change_link_tag_color(self.current_book.link_color)


        else: # not active
            self.current_book.exclusive_view = 0

            font = pango.FontDescription(self.main_properties.font.value)
            self.text_widget.modify_font(font)

            color = gdk.color_parse(self.main_properties.fg_color.value)
            self.text_widget.modify_text(gtk.STATE_NORMAL, color)

            color = gdk.color_parse(self.main_properties.bg_color.value)
            self.text_widget.modify_base(gtk.STATE_NORMAL, color)

            self.text_buffer.change_bookmark_tag_color(self.main_properties.bookmark_tag_color.value)
            self.text_buffer.change_link_tag_color(self.main_properties.link_tag_color.value)


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

    def set_position(self):
        iter = self.text_buffer.get_iter_at_offset(self.current_book.current_offset)
        self.text_widget.scroll_to_iter(iter, 0.0, True, 0.0, 0.0)

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

    def update_bookmarks_menu(self):
        #return

        # make bookmarks menu
        self.bookmarks_submenu = gtk.Menu()

        if self.current_book:
            for i in range(len(self.current_book.marks_list)):
                s = self.current_book.marks_list[i].description
                menuitem = gtk.MenuItem(fix_description(s, 50)) # FIXME: length
                menuitem.get_child().set_use_underline(False)
                menuitem.connect('activate', self.menu_activate_bookmark_cb,
                                 self.current_book.marks_list[i].number)
                self.bookmarks_submenu.add(menuitem)
                menuitem.show()

        self.bookmarks_menu.set_submenu( self.bookmarks_submenu)

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

    def menu_activate_bookmark_cb(self, w, n):

        m = self.bookmarks.get_mark(self.current_book, n)
        if m:
            self.backward_queue_append_pos()
            self.current_book.current_position = m.position
            self.current_book.current_offset = m.offset
            self.set_position()

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

    def update_content_menu(self):
        #print 'update_content_menu'

        self.content_submenu = gtk.Menu()

        if self.current_book:
            content = self.text_buffer.get_content()
            for i in range(len(content)):
                #s = self.current_book.marks_list[i].description
                menuitem = gtk.MenuItem(fix_description(content[i][2], 50))
                menuitem.get_child().set_use_underline(False)
                menuitem.connect('activate', self.menu_activate_content_cb,
                                 content[i][0], content[i][1])
                self.content_submenu.add(menuitem)
                menuitem.show()

        self.content_menu.set_submenu(self.content_submenu)

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

    def menu_activate_content_cb(self, w, offset, position):
        #print offset, position
        self.backward_queue_append_pos()
        self.current_book.current_position = position
        self.current_book.current_offset = offset
        self.set_position()

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

    def update_books_menu(self):
        #return

        self.books_submenu = gtk.Menu()

        #all_books = self.bookmarks.get_all_books()
        titles_list = self.bookmarks.titles_list
        for book in titles_list:
            #name = '%s (%s)' % (b.get_title(), fix_filename(b.filename))
            title = titles_list[book]
            menuitem = gtk.MenuItem(title)
            menuitem.get_child().set_use_underline(False)
            menuitem.connect('activate', self.menu_activate_book_cb, book)
            self.books_submenu.add(menuitem)
            menuitem.show()

        self.books_menu.set_submenu( self.books_submenu)

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

    def menu_activate_book_cb(self, w, filename):
        if self.current_book and self.current_book.filename != filename:
            self.insert_file(filename)
        elif not self.current_book:
            self.insert_file(filename)

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

    def menu_filters_cb(self):
        if not self.current_book: return
        self.insert_file(self.current_book.filename)
        if self.bookmarks_manager:
            self.bookmarks_manager.update_treeview()
        if self.book_info:
            self.book_info.set_book(self.current_book)

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

    def save_bookmark(self):
        # save current positin and current offset
        if not self.current_book: return

        self.current_book.current_position = self.text_widget.get_position()
        rect = self.text_widget.get_visible_rect()
        iter = self.text_widget.get_iter_at_location(rect.x, rect.y)
        offset = iter.get_offset()
        self.current_book.current_offset = offset
        self.bookmarks.save_book(self.current_book)

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

    def make_config_dir(self):

        if os.path.exists(config_dir) and os.path.isdir(config_dir):
            return 1
        else:
            try:
                os.mkdir(config_dir)
            except OSError, err:
                print >> sys.stderr, "ERROR: can't make config dir:", err[1]
                return 0
            return 1

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

    def save_config(self):

        self.main_properties.speed.value = self.text_widget.speed
        self.main_properties.file_select_path.value \
            = OpenFileDialog.file_select_path
        #if self.current_book:
        #    self.main_properties.filename.value = self.current_book.filename
        #else:
        #self.main_properties.filename.value = None

        try:
            fd = open(config_file, 'w')
        except IOError, err:
            print >> sys.stderr, \
                  "ERROR: can't open configuration file: %s: %s", \
                  config_file, err[1]
            return

        fd.write('[pybookreader]\n')

        self.main_properties.save_properties(fd)
        self.text_widget.text_properties.save_properties(fd)
        self.text_widget.scroll_properties.save_properties(fd)
        self.text_widget.scroll_type.save_properties(fd)

        fd.write('mouse_scroll_type=%d\n' % self.text_widget.mouse_scroll_type)

        fd.write('\n')

        fd.close()

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

    def read_config(self):

        try:
            fd = open(config_file)
        except:
            return

        properties = {}
        flag = 0
        for line in fd.readlines():
            if line == '[pybookreader]\n':
                flag = 1
                continue
            elif line[0] == '[':
                flag = 0
            elif flag == 1:
                indx = string.find(line, '=')
                if indx == -1:
                    continue
                properties[line[0:indx]] = line[indx+1:-1]

        self.main_properties.set_properties(properties)

        self.main_window.resize(self.main_properties.width.value,
                                self.main_properties.height.value)

        font = pango.FontDescription(self.main_properties.font.value)
        self.text_widget.modify_font(font)

        color = gdk.color_parse(self.main_properties.fg_color.value)
        self.text_widget.modify_text(gtk.STATE_NORMAL, color)

        color = gdk.color_parse(self.main_properties.bg_color.value)
        self.text_widget.modify_base(gtk.STATE_NORMAL, color)

        self.text_buffer.change_bookmark_tag_color(self.main_properties.bookmark_tag_color.value)
        self.text_buffer.change_link_tag_color(self.main_properties.link_tag_color.value)

        self.text_widget.speed = self.main_properties.speed.value
        self.text_widget.speed_changed()

        OpenFileDialog.file_select_path \
            = self.main_properties.file_select_path.value

        self.text_widget.text_properties.set_properties(properties)
        self.text_widget.scroll_properties.set_properties(properties)
        self.text_widget.scroll_type.set_properties(properties)

        toolbar_style = self.main_properties.toolbar_style.value
        if toolbar_style == 'both_horiz':
            self.wTree.get_widget('toolbar_both_horiz_menu').set_active(True)
        elif toolbar_style == 'icons':
            self.wTree.get_widget('toolbar_icons_menu').set_active(True)
        elif toolbar_style == 'text':
            self.wTree.get_widget('toolbar_text_menu').set_active(True)
        else: # both
            self.wTree.get_widget('toolbar_both_menu').set_active(True)

        if properties.has_key('mouse_scroll_type'):
            self.text_widget.set_mouse_scroll_type(int(properties['mouse_scroll_type']))

        self.text_widget.set_properties()

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

class CharsetMenu(gtk.Menu):

    def __init__(self, callback):
        gtk.Menu.__init__(self)

        self.block_connect = 0
        self.menuitems = {}
        self.callback = callback

        first_menuitem = gtk.RadioMenuItem(None, charsets_list[0])
        first_menuitem.connect('activate', self.activate_cb, charsets_list[0])
        self.add(first_menuitem)
        first_menuitem.show()
        self.menuitems[charsets_list[0]] = first_menuitem
        for cs in charsets_list[1:]:
            menuitem = gtk.RadioMenuItem(first_menuitem, cs)
            menuitem.connect('activate', self.activate_cb, cs)
            self.add(menuitem)
            menuitem.show()
            self.menuitems[cs] = menuitem

    def set_charset(self, charset):
        menuitem = self.menuitems[charset]
        self.block_connect = 1
        menuitem.set_active(True)
        self.block_connect = 0

    def activate_cb(self, w, a):
        if self.block_connect: return
        self.callback(w, a)

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

class FiltersMenu(gtk.Menu):

    def __init__(self, parrent, callback):
        gtk.Menu.__init__(self)

        self.parrent = parrent
        self.callback = callback

        #self.filters_submenu = gtk.Menu()
        filters_list = filters.filters
        self.internal_filters_menuitems = {}
        for fltr in filters_list:
            name = filters_list[fltr]
            menuitem = gtk.CheckMenuItem(name)
            id = menuitem.connect('activate', self.activate_cb, fltr)
            self.add(menuitem)
            menuitem.show()
            self.internal_filters_menuitems[fltr] = [menuitem, id]

        menuitem = gtk.MenuItem('External filter')
        menuitem.connect('activate', self.activate_cb, 1)
        self.add(menuitem)
        menuitem.show()
        self.external_filer_menuitem = menuitem


    def activate_cb(self, w, a):
        if not self.book: return

        if a == 1: # external filter
            command = entry_dialog(self.parrent, 'External filter', 'Command: ')
            if command != None:
                if command == '':
                    w.get_child().set_text('External filter ...')
                    self.book.externalfilter = None
                else:
                    w.get_child().set_text('External filter: ' + command)
                    self.book.externalfilter = command

                self.callback()

        else:
            active = w.get_active()

            if active:
                if self.book.internalfilters:
                    self.book.internalfilters.append(a)
                else:
                    self.book.internalfilters = [a]
            else:
                self.book.internalfilters.remove(a)

            self.callback()



    def update(self, book):
        self.book = book
        if not book: return

        for fltr in self.internal_filters_menuitems:
            menuitem = self.internal_filters_menuitems[fltr][0]
            cb_id = self.internal_filters_menuitems[fltr][1]
            menuitem.handler_block(cb_id)
            if book.internalfilters and fltr in book.internalfilters:
                menuitem.set_active(True)
            else:
                menuitem.set_active(False)
            menuitem.handler_unblock(cb_id)

        if book.externalfilter:
            self.external_filer_menuitem.get_child().set_text('External filter: '
                                                          + book.externalfilter)

        else:
            self.external_filer_menuitem.get_child().set_text('External filter ...')

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

if __name__ == "__main__":

    br = PyBookReader()
    gtk.main()

