#!/usr/bin/python2.5
# rapier: a small bible reader for maemo.
# Copyright 2007 Pierre Amadio 
# pierre.amadio@libertysurf.fr
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import sys
sys.path.append("/usr/share/rapier")
import gtk
import hildon
import osso
import Sword
import threading
import Queue
import time
import gobject
import re
from gnome import gconf
from gnome import gnomevfs
import swordutils
import os
import sqlite3
import string
"""
Does not work as expected...should change this.

http://maemo.org/development/documentation/tutorials/Maemo_tutorial_bora.html#internet

if os.environ.has_key("LD_PRELOAD"):
    os.environ["LD_PRELOAD"]=os.environ["LD_PRELOAD"]+':/usr/lib/libosso-ic-preload.so'
else:
    os.environ["LD_PRELOAD"]='/usr/lib/libosso-ic-preload.so'
"""


class aJob:
    """
     In order not to freeze the GUI when some time consuming tasks are being
     perfomed, the GUI is in a separate thread, and the time consuming tasks
     are being handled in a separate thread. Communication between the two
     thread is done by two Queue.Queue in wich instances of aJob are
     being sent.
    """

    def __init__(self,id,type,args={}):
        """
          instance variables:
          id: a unique job id
          type: the kind of job it is ('rightrpm_list','existing_rpm_list tree'
          and so on)
          args: some data as input
          result: this will store the result (content of the file, whatever)
        """
        self.id=id
        self.type=type
        self.args=args
        self.result=""

class ModuleManage:
    """This is the module management GUI"""
    
    def __init__(gPart,args={}):
        print "Entering ModuleManage"

class GuiPart(hildon.Program):
    """
       This class handle the actual GUI code.
    """
    def __init__(self,qQuery,qAnswer):
        swordutils.debug('------------------------------------------------')
        swordutils.debug('Rapier starts')
        hildon.Program.__init__(self)
        self.gconf=gconf.client_get_default()
        self.reload_manager()
        self.mainWindow = hildon.Window()
        self.mainWindow.connect("destroy",self.application_quit)
        self.mainWindow.connect("key-press-event",self.on_key_press)
        self.mainWindow.connect("window-state-event",\
                                self.on_window_state_change)
        self.timeoutHandler=gobject.timeout_add(100,self.processAnswer)
        self.jobCounter=0
        self.watchCursor=gtk.gdk.Cursor(gtk.gdk.WATCH)
        """False or: a sentence describing the last job """
        self.ioWait=False
        self.fullScreen=False
        self.searchDisplay=False
        self.osso_context = osso.Context("org.maemo.rapier", "0.0.1", False)  
        self.osso_rpc=osso.Rpc(self.osso_context)
        self.booksInfo=self.getAllBooks()
        self.installedModules=self.get_installedModules()
        self.remoteModules=[]
        self.selected_module=''
        self.selected_book=''
        self.selected_chapter=0
        self.maxChapter=0
        self.maxVerse=0
        self.selected_verse=0
        #is the touch screen currently pressed ?
        self.buttonPressed=False
        #how many unit of motion to change the display ?
        self.motion_adjust=0
        self.waitForSearchFlag=False
        self.waitForRemoteModulesFlag=False
        self.waitForModuleManagementFlag=False
        self.showStrongNumbers=False
        """ coordinate of the mouse last time there was a click.  """
        self.y={'old':0,'new':0}
        """indice=verse number, value=verse text"""
        self.textArray=[]
        self.textMarks=[]
        """ QueueQueue storing the job to perform"""
        self.qQuery=qQuery
        """ QueueQueue storing a job that has been performed."""
        self.qAnswer=qAnswer
        self.add_window(self.mainWindow)


        if not self.gconf.get_string("/apps/osso/rapier/strong_db"):
            self.strong_db=False
        else:
            self.strong_db=self.gconf.get_string("/apps/osso/rapier/strong_db")

        if not self.gconf.get_bool("/apps/osso/rapier/modules/carriage_return"):
            self.append_carriage_return=False
        else:
            self.append_carriage_return=True
        
        self.prepareInterface()
        self.preparePrefWindow()
        self.prepareManageModuleWindow()
        self.prepareWaitingWindow()
        self.fillBooks()        


        self.mainWindow.show_all()
        self.searchProgressbar.hide()

        """
         If there is no data_path gconf variable, or if it looks dodgy
         then let set it
         to /home/user/sword
        """
        tmp_dataPath=self.gconf.get_string("/apps/osso/rapier/data_path")
        if not tmp_dataPath\
               or not self.is_valid_datapath(tmp_dataPath):
            defdst="/home/user/sword"
            src_uri=gnomevfs.URI("/usr/share/sword")
            dst_uri=gnomevfs.URI(defdst)
            try:
                test=gnomevfs.xfer_uri_list([src_uri],[dst_uri],\
                                       gnomevfs.XFER_RECURSIVE,\
                                       gnomevfs.XFER_ERROR_MODE_ABORT,\
                                       gnomevfs.XFER_OVERWRITE_ACTION_REPLACE_ALL)

                
                self.gconf.set_string("/apps/osso/rapier/data_path",defdst)
            except gnomevfs.Error,fault:
                snt="Problem setting data_path to /usr/share/sword: %s"%fault
                swordutils.debug(snt)
                self.show_message(snt)

        """
        First thing to do is to fill the menu with
        the list of available modules
        """
        self.refresh_module_list()
        if  self.gconf.get_string("/apps/osso/rapier/bookmarks/start"):
            self.go_to_bookmark(\
                                self.gconf.get_string("/apps/osso/rapier/bookmarks/start"))

    def application_quit(self,win):
        gtk.main_quit()
        position="%s %d:%d"%\
                  (self.selected_book,\
                   self.selected_chapter,\
                   self.selected_verse)
        swordutils.debug("Setting bookmarks to:'%s'"%position)
        self.gconf.set_string("/apps/osso/rapier/bookmarks/start",position)

    def reload_manager(self):
        self.mgr = swordutils.get_manager()

        
    def get_defaultModule(self):
        return self.gconf.get_string('/apps/osso/rapier/modules/default')

    def get_installedModules(self):
        out={}
        for m in self.mgr.getModules().values():
            out[m.Name()]=m.Description()
        return out
    
    def getAllBooks(self):
        """
         Return an array:
         [{'testament': 1, 'bookCount': 1, 'name': 'Genesis', 'abbr': 'Gen'},
         {'testament': 1, 'bookCount': 2, 'name': 'Exodus', 'abbr': 'Exod'},
        """
        vk=Sword.VerseKey()
        out=[]
        for i in range(1,3):
          vk.Testament(i)
          for j in range(1,vk.bookCount(i)+1):
             vk.Book(j)
             tmp={}
             tmp['name']=vk.bookName(i,j)
             tmp['abbr']=vk.getBookAbbrev()
             tmp['testament']=i
             tmp['bookCount']=j
             out.append(tmp)
        return out

    def getInfoBasedOnAbbr(self,abbr):
        """
        Return info related to a book based on its abbreviation (ie 'Gen')
        """
        #vk=Sword.VerseKey()
        for cur in self.booksInfo:
            if cur['abbr']==abbr:
                return cur
        sys.exit("no such book : %s"%abbr)    
        

    def get_info_from_refs(self,ref):
        reg=re.compile("^(\S+)\s+(\d+):(\d+)$")
        search=reg.search(ref)
        if search:
            out={}
            out['abbr']=search.group(1)
            out['chapter']=search.group(2)
            out['verse']=search.group(3)
        else:
            print "Cannot regexp '%s'"%ref
            swordutils.debug("Cannot regexp '%s'"%ref)
            out={}
            out['abbr']='Gen'
            out['chapter']='1'
            out['verse']='1'

        return out 

    def fillBooks(self):
        for cur in self.booksInfo:
            abr=cur['abbr']
            self.mainWindow.book_tb_combo.append_text(abr)
        self.mainWindow.book_tb_combo.set_active_iter(\
            self.mainWindow.book_tb_combo.get_model().get_iter((0,)))

    def fillChapters(self):
        """ fill the chapter combo box"""
        tmp={}
        vk=Sword.VerseKey()
        tmp=self.getInfoBasedOnAbbr(self.selected_book)
        nbr_chapters=vk.chapterCount(tmp['testament'],tmp['bookCount'])
        self.maxChapter=nbr_chapters
        self.mainWindow.chapter_tb_combo.get_model().clear()
        for i in range(nbr_chapters):
            nbr=i+1
            self.mainWindow.chapter_tb_combo.append_text(str(nbr))
        self.mainWindow.chapter_tb_combo.set_active_iter(\
            self.mainWindow.chapter_tb_combo.get_model().get_iter((0,)))
        

    def fillVerses(self):
        """ fill the verses combo box"""
        vk=Sword.VerseKey()
        tmp=self.getInfoBasedOnAbbr(self.selected_book)
        self.mainWindow.verse_tb_combo.get_model().clear()
        self.maxVerse=vk.verseCount(tmp['testament'],tmp['bookCount'],\
                                     self.selected_chapter)
        for i in range(self.maxVerse):
            nbr=1+i
            self.mainWindow.verse_tb_combo.append_text(str(nbr))
        self.mainWindow.verse_tb_combo.set_active_iter(\
            self.mainWindow.verse_tb_combo.get_model().get_iter((0,)))
            
        
    def get_combo_active_text(self,combobox):
        model=combobox.get_model()
        active=combobox.get_active()
        if active<0:
            return None
        out= model[active][0]
        return out


    def switchSearchWidget(self,widget):
        """ hide or show the search horizontal pannel"""
        if self.searchDisplay:
            self.textDisplay_hpaned.set_position(0)
            self.searchDisplay=False
        else:
            self.textDisplay_hpaned.set_position(150)
            self.searchDisplay=True

            
    def prepareInterface(self):
        self.mainWindow.MainMenu=gtk.Menu()
        self.mainWindow.ModuleChooserMenu=gtk.Menu()
        self.mainWindow.itemSubMenuModuleChooser=gtk.MenuItem("Modules")
        self.mainWindow.MainMenu.append(\
            self.mainWindow.itemSubMenuModuleChooser)

        #Manage menu item
        self.mainWindow.itemMenuManage=gtk.MenuItem("Manage")
        self.mainWindow.MainMenu.append(self.mainWindow.itemMenuManage)
        self.mainWindow.itemMenuManage.connect(\
            "activate",self.displayManageModuleWindow)

        self.mainWindow.itemMenuPref=gtk.MenuItem("Preferences")
        self.mainWindow.MainMenu.append(self.mainWindow.itemMenuPref)
        self.mainWindow.itemMenuPref.connect('activate',self.displayPrefWindow)
        
        self.mainWindow.set_menu(self.mainWindow.MainMenu)
                
        self.mainWindow.toolbar=gtk.Toolbar()
        self.mainWindow.toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL);
        self.mainWindow.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ);

        #Book ComboBox
        self.mainWindow.book_tb_item=gtk.ToolItem()
        self.mainWindow.book_tb_combo=gtk.combo_box_new_text()
        self.mainWindow.book_tb_item.add(self.mainWindow.book_tb_combo)
        self.mainWindow.book_tb_combo.connect('changed',\
                                              self.book_combo_changed)
        self.mainWindow.toolbar.insert(self.mainWindow.book_tb_item,0)

        #Chapter Combox
        self.mainWindow.chapter_tb_item=gtk.ToolItem()
        self.mainWindow.chapter_tb_combo=gtk.combo_box_new_text()
        self.mainWindow.chapter_tb_item.add(self.mainWindow.chapter_tb_combo)
        self.mainWindow.chapter_tb_combo.connect('changed',\
                                                 self.chapter_combo_changed)
        self.mainWindow.toolbar.insert(self.mainWindow.chapter_tb_item,1)

        #Verse Combo
        self.mainWindow.verse_tb_item=gtk.ToolItem()
        self.mainWindow.verse_tb_combo=gtk.combo_box_new_text()
        self.mainWindow.verse_tb_item.add(self.mainWindow.verse_tb_combo)
        self.mainWindow.verse_tb_combo.connect(\
            'changed',self.verse_combo_changed)
        self.mainWindow.toolbar.insert(self.mainWindow.verse_tb_item,2)

        #Strong Number checkbox.
        self.mainWindow.strong_number_item=gtk.ToolItem()
        self.mainWindow.strong_number_check_b=gtk.CheckButton(\
            label="Strong's Number",use_underline=False)
        self.mainWindow.strong_number_check_b.connect("clicked",\
            self.strong_number_check_b_clicked)
        self.mainWindow.strong_number_item.add(\
            self.mainWindow.strong_number_check_b)
        self.mainWindow.toolbar.insert(\
            self.mainWindow.strong_number_item,3)

        self.mainWindow.back_b_item=gtk.ToolItem()
        self.mainWindow.back_b=gtk.Button()
        self.mainWindow.back_b_item.add(self.mainWindow.back_b)
        self.mainWindow.back_b.connect('clicked',self.back_b_clicked)
        self.mainWindow.toolbar.insert(self.mainWindow.back_b_item,3)
        self.mainWindow.back_image=gtk.Image()
        self.mainWindow.back_image.set_from_file(\
            '/usr/share/themes/default/images/qgn_plat_arrow_left_normal.png')
        self.mainWindow.back_b.add(self.mainWindow.back_image)

        self.mainWindow.next_b_item=gtk.ToolItem()
        self.mainWindow.next_b=gtk.Button()
        self.mainWindow.next_b_item.add(self.mainWindow.next_b)
        self.mainWindow.next_b.connect('clicked',self.next_b_clicked)
        self.mainWindow.next_image=gtk.Image()
        self.mainWindow.next_image.set_from_file(\
            '/usr/share/themes/default/images/qgn_plat_arrow_right_normal.png')
        self.mainWindow.next_b.add(self.mainWindow.next_image)

        self.mainWindow.toolbar.insert(self.mainWindow.next_b_item,4)

        
        self.mainWindow.displaySearch_item=gtk.ToolItem()
        self.mainWindow.displaySearch_b=gtk.Button()
        self.mainWindow.displaySearch_item.add(self.mainWindow.displaySearch_b)
        self.mainWindow.displaySearch_b.connect('clicked',\
                                                self.switchSearchWidget)
        self.mainWindow.switchSearch_image=gtk.Image()
        self.mainWindow.switchSearch_image.set_from_file(\
            '/usr/share/icons/hicolor/26x26/hildon/qgn_addr_icon_search_service.png')
        self.mainWindow.displaySearch_b.add(self.mainWindow.switchSearch_image)
        self.mainWindow.toolbar.insert(self.mainWindow.displaySearch_item,5)
        

        self.mainWindow.toolbar.show()
        self.mainWindow.add_toolbar(self.mainWindow.toolbar)

        #Main Text Window
        self.textDisplay_Vbox=gtk.VBox(True, 1)
        self.textDisplay_hpaned=gtk.HPaned()
        self.textDisplay_scrolledWindow=gtk.ScrolledWindow()
        """
        hildon.hildon_helper_set_thumb_scrollbar(\
            self.textDisplay_scrolledWindow, True)
        """
        self.textDisplay_textview= gtk.TextView()
        self.textDisplay_textview.set_editable(False)
        self.textDisplay_textview.set_cursor_visible(True)
        self.textDisplay_textview.connect('button-press-event',\
                                          self.button_press)
        self.textDisplay_textview.connect('button-release-event',\
                                          self.button_release) 
        self.textDisplay_textview.connect('motion-notify-event',\
                                          self.motion_notify)
        gtk.settings_get_default().props.gtk_font_name = "Sans 24"
        
        self.textDisplay_textview.set_wrap_mode(gtk.WRAP_WORD)
        self.fontSize=18
        self.textDisplay_buffer = self.textDisplay_textview.get_buffer()
        self.textDisplay_buffer.connect('mark_set', self.mark_set) 

        self.indiceTag=self.textDisplay_buffer.create_tag('indiceTag')
        self.indiceTag.set_property('size-points',self.fontSize)
        self.indiceTag.set_property('scale',.5)
        self.highlightTag=self.textDisplay_buffer.create_tag('highlightTag')
        self.highlightTag.set_property('size-points',self.fontSize)
                
        self.highlightTag.set_property('background','grey')
        self.normalTag=self.textDisplay_buffer.create_tag('normalTag')
        self.normalTag.set_property('size-points',self.fontSize)

        self.normalTag.set_property('background','white')
        self.textDisplay_scrolledWindow.set_policy(\
            gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

        
        

        self.mainWindow.add(self.textDisplay_Vbox)
        self.textDisplay_Vbox.pack_start(self.textDisplay_hpaned)

        
        #Search slide.
        self.searchVbox=gtk.VBox(False,0)
        #self.searchInputBox=gtk.HBox(False,0)
        self.searchEntry=gtk.Entry()
        self.searchGo=gtk.Button("Search")
        self.searchGo.connect("clicked",self.search_b_clicked)
        self.searchProgressbar=gtk.ProgressBar()

        self.searchResult_scrolled_window=gtk.ScrolledWindow()
        
        self.searchResult_TreeView=gtk.TreeView()
        self.searchResult_TreeView.set_fixed_height_mode(True)
        
        self.searchResult_scrolled_window.add(self.searchResult_TreeView)
        self.searchResult_TreeView_selection=self.searchResult_TreeView.get_selection()
        self.searchResult_TreeView_selection.connect(\
            'changed',self.result_selection_changed)
        self.result_ts=gtk.TreeStore(str,str)

        
        self.searchVbox.pack_start(self.searchEntry,False,False)
        self.searchVbox.pack_start(self.searchGo,False,False)
        self.searchVbox.pack_start(self.searchProgressbar,False,False)
        #self.searchVbox.pack_start(self.searchInputBox,False,False)
        #self.searchInputBox.pack_start(self.searchEntry)
        #self.searchInputBox.pack_start(self.searchGo)

        self.searchVbox.pack_start(gtk.HSeparator(),False,False)
        self.searchVbox.pack_start(self.searchResult_scrolled_window,True,True)

        self.textDisplay_hpaned.set_position(0)
        self.textDisplay_hpaned.add(self.searchVbox)
        self.textDisplay_hpaned.add(self.textDisplay_scrolledWindow)
        self.textDisplay_scrolledWindow.add(self.textDisplay_textview)

        #Dictionary Display Window.
        self.dictDisplay_w=hildon.Window()
        self.dictDisplay_w.set_title("Dictionary")
        self.dictDisplay_w.set_modal(True)
        self.dictDisplay_w.set_transient_for(self.mainWindow)
        self.dictDisplay_w.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
        self.dictDisplay_text_sw=gtk.ScrolledWindow()
        self.dictDisplay_textview=gtk.TextView()
        self.dictDisplay_textview.set_editable(False)
        self.dictDisplay_textview.set_cursor_visible(False)
        self.dictDisplay_textview.set_wrap_mode(gtk.WRAP_NONE)
        self.dictDisplay_buffer = self.dictDisplay_textview.get_buffer()
        self.dictDisplay_text_sw.set_policy(\
            gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        
        self.dictDisplay_text_sw.add(self.dictDisplay_textview)
        
        self.dictDisplay_okb=gtk.Button('Ok')
        self.dictDisplay_okb.connect("clicked",self.hide_dic_window)
        self.dictDisplay_vb=gtk.VBox()
        self.dictDisplay_w.add(self.dictDisplay_vb)
        self.dictDisplay_vb.pack_start(self.dictDisplay_text_sw,True)
        self.dictDisplay_vb.pack_start(self.dictDisplay_okb,False)
        self.dictDisplay_w.resize(700,300)
        #self.dictDisplay_w.show_all()
        
        return True


    def refillPrefModuleList(self):
        defaultModule=self.get_defaultModule()
        selectedFlag=False
        iter=False
        self.prefModuleTabModel.clear()
        for m in self.installedModules.keys():
            curiter=self.prefModuleTabModel.append([m])
            if m==defaultModule:
                selectedFlag=curiter

        if selectedFlag:
            self.prefModuleTabCombo.set_active_iter(selectedFlag)
        else:
            self.prefModuleTabCombo.set_active(0)

            

    def getDeviceInfoFromFilename(self,filename):
        """
        filename is a string representing a ....filename (/media/mmc2/plop)

        It returns a dictionnary with the followinf key/values pair:
        device: tablet,mmc1 or mmc2
        icon: a picture path that match the device type
        basename: the file name (without prefix)

        If the filename is not /media/mmc1,/media/mmc2,or /home/user/MyDocs
        then it just returns some null data as this is not a valid location to
        store stuff anyway.
        """

        out={}
        mmreg=re.compile("^/media/(mmc[12])/(.*)$")
        tabletreg=re.compile("^/home/user/MyDocs/(.*)$")
        mmsearch=mmreg.search(filename)
        if mmsearch:
            out['device']=mmsearch.group(1)
            out['icon']='/usr/share/icons/hicolor/26x26/hildon/qgn_list_gene_removable_memory_card.png'
            out['basename']=mmsearch.group(2)
            return out

        tabletsearch=tabletreg.search(filename)
        if tabletsearch:
            out['device']='tablet'
            out['icon']='/usr/share/icons/hicolor/26x26/hildon/qgn_list_shell_mydevice.png'
            out['basename']=tabletsearch.group(1)
            return out

        out['device']=''
        out['icon']='/usr/share/icons/hicolor/26x26/hildon/qgn_list_filemanager.png'
        out['basename']=''

        return out


    def setCorrectFileChooserIcon(self, devInfo):

        self.prefModuleTabDirCurLocation.set_from_file(devInfo['icon'])

        self.prefModuleTabDirDevLabel.set_text(devInfo['device'])

        self.prefModuleTabDirFileLabel.set_text(devInfo['basename'])
        
    def preparePrefWindow(self):
        self.prefWindow = gtk.Dialog()
        self.prefWindow.set_title("Preferences")
        #self.prefWindow.set_decorated(False)
        self.prefWindow.set_modal(True)
        self.prefWindow.set_transient_for(self.mainWindow)

        """
        preference window countain a vbox, a notebook and 2 button in a Hbox
           -------------------
          |                   |
          |   ------------    |
          |  |            |   |                  
          |  |  Notebook  |   |
          |  |            |   |        
          |   ------------    |
          |-------------------|
          |   -----------     |
          |  | Ok |Cancel|    |
          |   -----------     |
           ------------------- 

        """
        self.prefVbox = self.prefWindow.vbox
        """
        http://www.pygtk.org/docs/pygtk/class-gtknotebook.html
        """
        self.prefNotebook=gtk.Notebook()
        self.prefVbox.pack_start(self.prefNotebook,False,False)

        #Module Page
        self.prefModuleTab=gtk.VBox(False,0)
        #Module: Default.
        self.prefModuleTabDefault=gtk.HBox(True,0)
        self.prefModuleTabLabel=gtk.Label()
        self.prefModuleTabLabel.set_text('Default Module')
        self.prefModuleTabDefault.pack_start(\
            self.prefModuleTabLabel,True,False)
        self.prefModuleTabModel=gtk.ListStore(gobject.TYPE_STRING)
        self.prefModuleTabCombo=gtk.ComboBox(self.prefModuleTabModel)
        self.prefModuleTabCell=gtk.CellRendererText()
        self.prefModuleTabCombo.pack_start(self.prefModuleTabCell, True)
        self.prefModuleTabCombo.add_attribute(self.prefModuleTabCell,'text', 0)
        self.prefModuleTabDefault.pack_start(\
            self.prefModuleTabCombo,True,False)
        #Module: Directory
        self.prefModuleTabDir=gtk.HBox(True,0)
        self.prefModuleTabLabel=gtk.Label()
        self.prefModuleTabLabel.set_text('Storage Directory')
        self.prefModuleTabDir.pack_start(self.prefModuleTabLabel,True,False)
        self.prefModuleTabDirValue=gtk.HBox(False,5)
        self.prefModuleTabDir.pack_start(self.prefModuleTabDirValue)
        if self.mgr.prefixPath:
            devInfo=self.getDeviceInfoFromFilename(self.mgr.prefixPath)
        else:
            devInfo=self.getDeviceInfoFromFilename('')
        self.prefSelectedSwordDataPath=self.mgr.prefixPath
        self.prefModuleTabDirCurLocation=gtk.Image()
        self.prefModuleTabDirDevLabel=gtk.Label()
        self.prefModuleTabDirFileLabel=gtk.Label()
        self.prefModuleTabDirValue.pack_start(self.prefModuleTabDirCurLocation\
                                              ,True,False)
        self.prefModuleTabDirValue.pack_start(self.prefModuleTabDirDevLabel\
                                              ,True,False)
        self.prefModuleTabDirValue.pack_start(self.prefModuleTabDirFileLabel,\
                                              True,False)
        self.setCorrectFileChooserIcon(devInfo)
        self.prefModuleTabDirChange_b=gtk.Button('Change')
        self.prefModuleTabDirChange_b.connect(\
            'clicked',self.prefModuleTabDirChange_bClicked)
        self.prefModuleTabDirValue.pack_start(self.prefModuleTabDirChange_b,\
                                           True,False)

        #Modules: Append carriage return
        self.prefModuleTabCR=gtk.HBox(True,0)
        self.prefModuleTabCRLabel=gtk.Label()
        self.prefModuleTabCRLabel.set_label("Append new line\nat end of verse")
        self.prefModuleTabCR.pack_start(self.prefModuleTabCRLabel,True)
        self.prefModuleTabCR_box=gtk.CheckButton('')
        self.prefModuleTabCR.pack_start(self.prefModuleTabCR_box,True)
        


        self.prefModuleTab.pack_start(self.prefModuleTabDefault,False,False)
        self.prefModuleTab.pack_start(self.prefModuleTabCR,False,False)
        self.prefModuleTab.pack_start(self.prefModuleTabDir,False,False)
        
        self.refillPrefModuleList()
        self.prefNotebook.append_page(self.prefModuleTab,None)
        self.prefNotebook.set_tab_label_text(self.prefModuleTab,"Modules")

        #Debug page
        self.prefDebugTab=gtk.VBox(False,0)
        self.prefDebugHbox1=gtk.HBox(False,0)
        self.prefDebugLabel1=gtk.Label()
        self.prefDebugLabel1.set_text("Enable debug: ")
        self.prefDebugCheckBox=gtk.CheckButton('')
        self.prefDebugHbox1.pack_start(self.prefDebugLabel1,True)
        self.prefDebugHbox1.pack_start(self.prefDebugCheckBox,True)
        self.prefDebugTab.pack_start(self.prefDebugHbox1,True)
        self.prefDebugLabel2=gtk.Label()
        self.prefDebugLabel2.set_text(\
            "Location: %s"%swordutils.debugFile)
        self.prefDebugTab.pack_start(self.prefDebugLabel2,True)
        self.prefNotebook.append_page(self.prefDebugTab,None)
        self.prefNotebook.set_tab_label_text(self.prefDebugTab,"Debug")

        #Strong Number Database.
        self.prefStrongTabDir=gtk.VBox(True,1)
        self.prefStrongTabLabel=gtk.Label()
        self.prefStrongTabLabel.set_text("Strong number database location:")
        self.prefStrongTabDir.pack_start(self.prefStrongTabLabel,True,False)
        self.prefStrongTabDirValue=gtk.HBox(False,5)
        self.prefStrongTabDir.pack_start(self.prefStrongTabDirValue)
        if self.strong_db:
            devInfo=self.getDeviceInfoFromFilename(self.strong_db)
        else:
            devInfo=self.getDeviceInfoFromFilename("")
        self.prefStrongTabCurLocation=gtk.Image()
        self.prefStrongTabDevLabel=gtk.Label()
        self.prefStrongTabFileLabel=gtk.Label()
        self.prefStrongTabDirValue.pack_start(\
            self.prefStrongTabCurLocation,True,False)
        self.prefStrongTabDirValue.pack_start(\
            self.prefStrongTabDevLabel,True,False)
        self.prefStrongTabDirValue.pack_start(\
            self.prefStrongTabFileLabel)
        self.prefStrongTabCurLocation.set_from_file(devInfo['icon'])
        self.prefStrongTabDevLabel.set_text(devInfo["device"])
        self.prefStrongTabFileLabel.set_text(devInfo["basename"])
        self.prefStrongTabDirChange_b=gtk.Button("Change")
        self.prefStrongTabDirChange_b.connect(\
            'clicked',self.prefStrongTabDirChange_bClicked)
        self.prefStrongTabDirValue.pack_start(self.prefStrongTabDirChange_b,\
                                           True,False)
          
        self.prefNotebook.append_page(self.prefStrongTabDir,None)
        self.prefNotebook.set_tab_label_text(\
            self.prefStrongTabDir,"Strong Number")

        self.prefWindow.add_button("Apply", gtk.RESPONSE_OK)
        self.prefWindow.add_button("Cancel", gtk.RESPONSE_CANCEL)
        self.prefVbox.show_all()

    def prefModuleTabDirChange_bClicked(self,widget):
        prefModuleTabDirFileSelector=\
                                       hildon.FileChooserDialog(\
            self.prefWindow,gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
        rep=prefModuleTabDirFileSelector.run()
        prefModuleTabDirFileSelector.hide()
        a=prefModuleTabDirFileSelector.get_filename()
        if rep==gtk.RESPONSE_OK:
            devInfo=self.getDeviceInfoFromFilename(a)
            self.setCorrectFileChooserIcon(devInfo)
            self.prefSelectedSwordDataPath=a
            
        elif rep==gtk.RESPONSE_CANCEL:
            prefModuleTabDirFileSelector.destroy()



    def is_valid_sqldb(self,fileName):
        try:
            db=sqlite3.connect(fileName)
            c=db.cursor()
            c.execute("Select count(*) from Words")
            db.close()
        except:
            return False
        return True
            
    def is_valid_datapath(self,path):
        if not  os.path.isdir(path):
            return False
        if not os.path.isdir(path+"/mods.d"):
            return False
        if not os.path.isdir(path+"/locales.d"):
            return False
        if not os.path.isdir(path+"/modules"):
            return False
        
        return True



    def prefStrongTabDirChange_bClicked(self,widget):
        prefStrongTabDirFileSelector=\
                                       hildon.FileChooserDialog(\
            self.prefWindow,gtk.FILE_CHOOSER_ACTION_OPEN)
        rep=prefStrongTabDirFileSelector.run()
        prefStrongTabDirFileSelector.hide()
        a=prefStrongTabDirFileSelector.get_filename()

        if not self.is_valid_sqldb(a):
            print "not a valid db"
            self.show_message("This does not seems like a sqlite database.")
            prefStrongTabDirFileSelector.destroy()
            return
        
        if rep==gtk.RESPONSE_OK:
            devInfo=self.getDeviceInfoFromFilename(a)
            self.prefStrongTabCurLocation.set_from_file(devInfo['icon'])
            self.prefStrongTabDevLabel.set_text(devInfo["device"])
            self.prefStrongTabFileLabel.set_text(devInfo["basename"])
            self.prefSelected_strong_db=a
            
        elif rep==gtk.RESPONSE_CANCEL:
            prefStrongTabDirFileSelector.destroy()


    def changePrefs(self):

        #Module preferences.
        iter=self.prefModuleTabCombo.get_active_iter()
        if iter:
            selectedModule=self.prefModuleTabModel.get_value(iter,0)
            self.gconf.set_string('/apps/osso/rapier/modules/default',\
                                  selectedModule)

        if self.prefSelectedSwordDataPath!=self.mgr.prefixPath:
            swordutils.debug(\
                "Changing data path to %s"%self.prefSelectedSwordDataPath)
            src_uri=gnomevfs.URI(self.mgr.prefixPath)
            dst_uri=gnomevfs.URI(self.prefSelectedSwordDataPath)

            try:
                gnomevfs.xfer_uri_list([src_uri],[dst_uri],\
                                       gnomevfs.XFER_RECURSIVE+\
                                       gnomevfs.XFER_REMOVESOURCE,\
                                       gnomevfs.XFER_ERROR_MODE_ABORT,\
                                       gnomevfs.XFER_OVERWRITE_ACTION_REPLACE_ALL)
                self.gconf.set_string('/apps/osso/rapier/data_path',
                                     self.prefSelectedSwordDataPath)
            except gnomevfs.Error,fault:
                snt="Problem changing data_path from %s to %s:\n%s"%\
                     (self.mgr.prefixPath,self.prefSelectedSwordDataPath,fault)
                self.show_message(snt)
                swordutils.debug(snt)
            
            self.reload_manager()

        #Debug
        self.gconf.set_bool('/apps/osso/rapier/debug/enabled',\
                              self.prefDebugCheckBox.get_active())

        #Strong Number database
        if self.prefSelected_strong_db!=self.strong_db:
            self.strong_db=self.prefSelected_strong_db
            self.gconf.set_string('/apps/osso/rapier/strong_db',\
                                self.strong_db)
        #Carriage_return
        self.gconf.set_bool('/apps/osso/rapier/modules/carriage_return',\
                            self.prefModuleTabCR_box.get_active())
        self.append_carriage_return=self.prefModuleTabCR_box.get_active()
        

    def cancelPrefs(self):
        #Let s put all default selection back.
        self.refillPrefModuleList()
        if self.mgr.prefixPath:
            devInfo=self.getDeviceInfoFromFilename(self.mgr.prefixPath)
        else:
            devInfo=self.getDeviceInfoFromFilename('')
        self.setCorrectFileChooserIcon(devInfo)

        self.prefDebugCheckBox.set_active(\
            self.gconf.get_bool('/apps/osso/rapier/debug/enabled'))

        self.prefModuleTabCR_box.set_active(\
            self.gconf.get_bool('/apps/osso/rapier/modules/carriage_return'))
        
    def displayPrefWindow(self,menuitem):
        self.prefDebugCheckBox.set_active(\
            self.gconf.get_bool('/apps/osso/rapier/debug/enabled'))
        self.prefSelected_strong_db=self.strong_db

        self.prefModuleTabCR_box.set_active(self.append_carriage_return)
        
        resp = self.prefWindow.run()
        self.prefWindow.hide()
        if resp == gtk.RESPONSE_OK:
            self.changePrefs()
        else:
            self.cancelPrefs()

    def displayManageModuleWindow(self,widget):
        self.fill_mgModLocalModel()
        self.refresh_remote_modules_tv()

        resp = self.mgModWindow.run()
        if resp == gtk.RESPONSE_OK:
            self.mgModOk_b_clicked()
        self.mgModWindow.hide()

    def mgModRemoteInstallCell_toggled(self,cell,path):
        origValue=self.mgModRemoteModel[path][0]
        isDirectory=self.mgModRemoteModel[path][4]
        curIter=self.mgModRemoteModel.get_iter(path)
        
        if isDirectory:
            nbr_children=self.mgModRemoteModel.iter_n_children(curIter)
            for index in range(0,nbr_children):
                newPath="%s:%i"%(path,index)
                self.mgModRemoteModel[newPath][0]=not origValue
                """
                self.mgModRemoteTv.expand_row(path,False)
                """
        else:
            if origValue:
                iter_parent=self.mgModRemoteModel.iter_parent(curIter)
                path_parent=self.mgModRemoteModel.get_path(iter_parent)
                self.mgModRemoteModel[path_parent][0]=False

        self.mgModRemoteModel[path][0] = not self.mgModRemoteModel[path][0]



    def mgModLocalIndex_toggled(self,cell, path):
        origValue=self.mgModLocalModel[path][2]

        if origValue:
            dialog = hildon.Note ("confirmation",\
                                  (self.mgModWindow,\
                                   "Are you sure you want to "+\
                                   " destroy this index ?",\
                                   gtk.STOCK_DIALOG_WARNING)\
                                  )
            dialog.set_button_texts ("Yes", "No")
            response = dialog.run()
            dialog.destroy()

            if response != gtk.RESPONSE_OK:
                return


        self.mgModLocalModel[path][2] = not self.mgModLocalModel[path][2]


    def mgModLocalRemove_toggled(self,cell, path):
        origValue=self.mgModLocalModel[path][3]
        if not origValue:
            dialog = hildon.Note ("confirmation",\
                                  (self.mgModWindow,\
                                   "Are you sure you want to"+ \
                                   " uninstall this module ?",\
                                   gtk.STOCK_DIALOG_WARNING)\
                                  )
            dialog.set_button_texts ("Yes", "No")
            response = dialog.run()
            dialog.destroy()

            if response != gtk.RESPONSE_OK:
                return
                
        self.mgModLocalModel[path][3] = not self.mgModLocalModel[path][3]
        
    def fill_mgModLocalModel(self):
        self.mgModLocalTv.freeze_child_notify()
        self.mgModLocalModel.clear()
        try:
            for c in self.mgModLocalTv.get_columns():
                self.mgModLocalTv.remove_column(c)
        except:
            print "oups"


        self.mgModLocalNameCell=gtk.CellRendererText()
        self.mgModLocalNameCol=gtk.TreeViewColumn('Name')
        self.mgModLocalTv.append_column(self.mgModLocalNameCol)
        self.mgModLocalNameCol.pack_start(self.mgModLocalNameCell,True)
        self.mgModLocalNameCol.set_attributes(self.mgModLocalNameCell,text=0)

        self.mgModLocalLangCell=gtk.CellRendererText()
        self.mgModLocalLangCol=gtk.TreeViewColumn('Lang')
        self.mgModLocalTv.append_column(self.mgModLocalLangCol)
        self.mgModLocalLangCol.pack_start(self.mgModLocalLangCell,True)
        self.mgModLocalLangCol.set_attributes(self.mgModLocalLangCell,text=1)

        self.mgModLocalIndexCell=gtk.CellRendererToggle()
        self.mgModLocalIndexCell.set_property('activatable', True)
        self.mgModLocalIndexCell.connect(\
            'toggled',self.mgModLocalIndex_toggled) 
        self.mgModLocalIndexCol=gtk.TreeViewColumn('Index')
        self.mgModLocalTv.append_column(self.mgModLocalIndexCol)
        self.mgModLocalIndexCol.pack_start(self.mgModLocalIndexCell,True)
        self.mgModLocalIndexCol.set_attributes(\
            self.mgModLocalIndexCell,active=2)

        
        self.mgModLocalRemoveCell=gtk.CellRendererToggle()
        self.mgModLocalRemoveCell.set_property('activatable', True)
        self.mgModLocalRemoveCell.connect(\
            'toggled',self.mgModLocalRemove_toggled) 
        self.mgModLocalRemoveCol=gtk.TreeViewColumn('Uninstall')
        self.mgModLocalTv.append_column(self.mgModLocalRemoveCol)
        self.mgModLocalRemoveCol.pack_start(self.mgModLocalRemoveCell,True)
        self.mgModLocalRemoveCol.set_attributes(\
            self.mgModLocalRemoveCell,active=3)
        
        
        for m in self.mgr.getModules().values():
            cur_name=m.Name()
            cur_lang=m.Lang()
            cur_clucene=swordutils.has_clucene_index(self.mgr, cur_name)
            self.mgModLocalModel.append(None,\
                                        [cur_name,cur_lang,cur_clucene,False])

        self.mgModLocalTv.thaw_child_notify()

    def mgModRemoteRefresh_b_clicked(self,widget):
        args={}
        args['osso_context']=self.osso_context
        args['data_path'] = swordutils.get_data_path()
        
        newJob=aJob(self.jobCounter,"remote_modules_list",args)
        self.putJob(newJob)
        self.waitForRemoteModulesFlag=True
        self.waitingWindow.set_property('description',
                "Waiting for remote modules list")
        self.waitingWindow.show_all()


    def mgModOk_b_clicked(self):
        mod_to_remove=[]
        idx_to_remove=[]
        idx_to_build=[]
        mod_to_install=[]
        
        locals=[]
        iter=self.mgModLocalModel.get_iter_first()

        if iter:
            while True:
                locals.append(\
                    {'name':self.mgModLocalModel.get_value(iter,0),\
                     'index':self.mgModLocalModel.get_value(iter,2),\
                     'uninstall':self.mgModLocalModel.get_value(iter,3)\
                     })
                iter=self.mgModLocalModel.iter_next(iter)
                if not iter:
                    break

        for cur in locals:
            #print cur
            curName=cur['name']
            if cur['uninstall']:
                mod_to_remove.append(curName)
            else:
                if (cur['index'] and not
                        swordutils.has_clucene_index(self.mgr, curName)):
                    idx_to_build.append(curName)
                if (not cur['index'] and
                        swordutils.has_clucene_index(self.mgr, curName)):
                    idx_to_remove.append(curName)


        iter=self.mgModRemoteModel.get_iter_first()

        if iter:
            while True:
                cur_path=self.mgModRemoteModel.get_path(iter)
                nbr_children=self.mgModRemoteModel.iter_n_children(iter)
                for index in range(0,nbr_children):
                    child_iter=self.mgModRemoteModel.iter_nth_child(\
                        iter,index)
                    ch_name=self.mgModRemoteModel.get_value(child_iter,1)
                    ch_get=self.mgModRemoteModel.get_value(child_iter,0)
                    if ch_get:
                        mod_to_install.append(ch_name)

                iter=self.mgModRemoteModel.iter_next(iter)
                if not iter:
                    break

        args={}
        args['mod_remove']=mod_to_remove
        args['install']=mod_to_install
        args['idx_build']=idx_to_build
        args['idx_remove']=idx_to_remove
        args['remote']=self.remoteModules
        args['osso_context']=self.osso_context
        args['data_path'] = swordutils.get_data_path()
        newJob=aJob(self.jobCounter,'module_management',args)
        self.putJob(newJob)
        self.waitForModuleManagementFlag=True
        self.waitingWindow.set_property('description',
                "Waiting for operation to perform.\n"+
                "(Index building may take a very,"+
                " very long time.)")
                                      
        self.waitingWindow.show_all()

    def prepareManageModuleWindow(self):
        self.mgModWindow = gtk.Dialog()
        self.mgModWindow.set_title("Manage Modules")
        self.mgModWindow.set_modal(True)
        self.mgModWindow.set_transient_for(self.mainWindow)
        self.mgModWindow.set_default_size(750,300)
        self.mgModVBox = self.mgModWindow.vbox
        self.mgModNotebook=gtk.Notebook()
        self.mgModVBox.pack_start(self.mgModNotebook)

        #Local Modules
        self.mgModLocalTab=gtk.VBox(False,0)
        self.mgModNotebook.append_page(self.mgModLocalTab)
        self.mgModNotebook.set_tab_label_text(self.mgModLocalTab,'Installed')
        self.mgModNotebook.set_tab_label_packing(self.mgModLocalTab,True,\
                                                 True,gtk.PACK_START)
        self.mgModLocalScrollWin=gtk.ScrolledWindow()
        self.mgModLocalTv=gtk.TreeView()
        self.mgModLocalTv.set_headers_visible(True)
        #http://maemo.org/pipermail/maemo-developers/2007-March/009258.html
        #self.mgModLocalTv.set_property('allow-checkbox-mode',False)
        self.mgModLocalScrollWin.add(self.mgModLocalTv)
        """
        name,Lang,Clucene index, Uninstall
        """
        self.mgModLocalModel=gtk.TreeStore(str,str,bool,bool)
        self.mgModLocalTv.set_model(self.mgModLocalModel)
        #self.mgModLocalTv.set_fixed_height_mode(True)
        self.mgModLocalTab.pack_start(self.mgModLocalScrollWin,True,True)
        
        
        #Remote Modules
        self.mgModRemoteTab=gtk.VBox(False,0)
        self.mgModNotebook.append_page(self.mgModRemoteTab)
        self.mgModNotebook.set_tab_label_text(self.mgModRemoteTab,'Remote')
        self.mgModRemoteScrollWin=gtk.ScrolledWindow()
        self.mgModRemoteTv=gtk.TreeView()
        #self.mgModRemoteTv.set_headers_visible(True)        
        #self.mgModRemoteTv.set_property('allow-checkbox-mode',False)
        self.mgModRemoteScrollWin.add(self.mgModRemoteTv)
        """
        install,name,lang,description,is_directory
        """
        self.mgModRemoteModel=gtk.TreeStore(bool,str,str,str,bool)
        self.mgModRemoteModel.set_sort_column_id(1,gtk.SORT_ASCENDING)
        self.mgModRemoteTv.set_model(self.mgModRemoteModel)
        self.mgModRemoteTab.pack_start(self.mgModRemoteScrollWin,True,True)
        self.mgModRemoteRefresh_b=gtk.Button("Refresh")
        self.mgModRemoteRefresh_b.connect("clicked",\
                                          self.mgModRemoteRefresh_b_clicked)
        self.mgModRemoteTab.pack_start(self.mgModRemoteRefresh_b,False,False)
        
        self.mgModWindow.add_button("OK", gtk.RESPONSE_OK)
        self.mgModWindow.add_button("Cancel", gtk.RESPONSE_CANCEL)
        self.mgModVBox.show_all()


    def mgModRemoteInstallCell_data_func(self,column,renderer,model,iter):
        """
        Checkbox is only displayed in leaf nodes.
        """
        is_directory=model.get_value(iter,4)
        if is_directory:
            renderer.set_property('visible',False)
        else:
            renderer.set_property('visible',True)

    def refresh_remote_modules_tv(self):
        self.mgModRemoteModel.clear()
        for c in self.mgModRemoteTv.get_columns():
            self.mgModRemoteTv.remove_column(c)


        self.mgModRemoteInstallCell=gtk.CellRendererToggle()
        self.mgModRemoteInstallCell.set_property('activatable',True)
        self.mgModRemoteInstallCell.connect(\
            'toggled',self.mgModRemoteInstallCell_toggled)
                                            
        self.mgModRemoteInstallCol=gtk.TreeViewColumn('Install')
        self.mgModRemoteTv.append_column(self.mgModRemoteInstallCol)
        self.mgModRemoteInstallCol.pack_start(self.mgModRemoteInstallCell,True)

        self.mgModRemoteInstallCol.set_cell_data_func(\
            self.mgModRemoteInstallCell,self.mgModRemoteInstallCell_data_func)
        
        self.mgModRemoteInstallCol.set_attributes(\
            self.mgModRemoteInstallCell,active=0)

        self.mgModRemoteNameCell=gtk.CellRendererText()
        self.mgModRemoteInstallCol.pack_start(self.mgModRemoteNameCell,True)
        self.mgModRemoteInstallCol.set_attributes(\
            self.mgModRemoteNameCell,text=1)

        self.mgModRemoteDescCell=gtk.CellRendererText()
        self.mgModRemoteDescCol=gtk.TreeViewColumn('Description')
        self.mgModRemoteTv.append_column(self.mgModRemoteDescCol)
        self.mgModRemoteDescCol.pack_start(self.mgModRemoteDescCell,True)
        self.mgModRemoteDescCol.set_attributes(\
            self.mgModRemoteDescCell,text=3)

        byLang={}
        for i in self.remoteModules:

            if i['Name'] in self.installedModules:
                continue
            
            if 'Lang'not in i.keys():
                continue
            if i['Lang'] not in byLang.keys():
                byLang[i['Lang']]=[]
            byLang[i['Lang']].append(i)


        self.mgModRemoteTv.freeze_child_notify()

        localeMgr=Sword.LocaleMgr()
        for lang in byLang.keys():
            curlocale=localeMgr.getLocale(lang)
            if curlocale:
                lang_label=curlocale.getDescription()
            else:
                lang_label=lang
            
            iter=self.mgModRemoteModel.append(None,\
                                              [False,lang_label,'','',True]\
                                              )
            for mod in byLang[lang]:
                self.mgModRemoteModel.append(iter,\
                                             [\
                    False,mod['Name'],mod['Lang'],mod['Description'],False\
                    ])

        self.mgModRemoteTv.thaw_child_notify()
        
        
    def refresh_remote_modules(self,modules):
        self.waitForRemoteModulesFlag=False
        self.waitingWindow.hide()
        self.remoteModules=modules
        self.refresh_remote_modules_tv()        


    def show_message(self,message):
        note = hildon.Note("information",
                (self.mainWindow, message, gtk.STOCK_DIALOG_INFO))
        note.set_modal(True)
        def cleanup(*args):
            note.destroy()
        note.connect('response', cleanup)
        note.show()

    def show_dic_window(self,message):
        self.dictDisplay_buffer.set_text(message)
        self.dictDisplay_w.show_all()

    def hide_dic_window(self,widget):
        self.dictDisplay_w.hide()

    def process_module_management(self,result):
        self.waitForModuleManagementFlag=False

        if result:
            snt="There were problems..."
        else:
            snt="Operation successful !"

        self.waitingWindow.hide()
        self.mgModWindow.hide()
        self.show_message(snt)
        self.reload_manager()
        self.refresh_module_list()
        self.refillPrefModuleList()
        
        return True


    def process_panic(self,job):
        message='Fatal error:%s'%job.args['message']
        note = hildon.Note("information",
                           (self.mainWindow, message, gtk.STOCK_DIALOG_INFO))
        note.set_modal(True)
        def cleanup(*args):
            sys.exit()
        note.connect('response', cleanup)
        note.show()

        

    def prepareWaitingWindow(self):
        """self.mgModWindow"""
        self.waitingWinProgressBar=gtk.ProgressBar()
        self.waitingWindow = hildon.Note("cancel_with_progress_bar",
                (self.mainWindow, "Waiting...", self.waitingWinProgressBar))
        
    def on_key_press(self,widget,event):
        if event.keyval ==gtk.keysyms.F6:
            """Full screen key"""
            if self.fullScreen:
                self.mainWindow.unfullscreen()
                self.fullScreen=False
            else:
                self.mainWindow.fullscreen()
                self.fullScreen=True


        if event.keyval==65361:
            #print "left"
            if self.selected_chapter>2:
                tmp=int(self.selected_chapter)-1
                self.mainWindow.chapter_tb_combo.set_active(tmp-1)
                self.refresh_textArray()



        #if event.keyval==65362:
        #    print "up"

            
        if event.keyval==65363:
        #    print "right"
            
            if self.selected_chapter<self.maxChapter:
                self.mainWindow.chapter_tb_combo.set_active(\
                    self.selected_chapter)



        #if event.keyval==65364:
        #    print "down"


        if event.keyval==65477:
            self.change_font_size(-1)

        if event.keyval==65476:
            self.change_font_size(1)

    def button_press(self,widget,event):
        self.buttonPressed=True
        self.y['old']=self.y['new']
        self.y['new']=event.y

        """
        x=event.x
        y=event.y
        w=self.textDisplay_textview.get_window(gtk.TEXT_WINDOW_TEXT)
        buf_coord=self.textDisplay_textview.window_to_buffer_coords(\
            gtk.TEXT_WINDOW_TEXT,x,y)
        iter=self.textDisplay_textview.get_iter_at_location(buf_coord[0],buf_coord[1])
        line=iter.get_line()
        startIter=self.textDisplay_buffer.get_iter_at_line(line)
        upToTap=self.textDisplay_buffer.get_text(startIter,iter)
        allLine=self.textArray[line-1]

        print self.detect_strong_word(upToTap,allLine)
        """

        

    def button_release(self,widget,event):
        self.buttonPressed=False

    def mark_set(self,buffer,ite,mark):
        bound=self.textDisplay_buffer.get_selection_bounds()

        if not self.showStrongNumbers:
            return

        if len(bound) and mark.get_name()=="selection_bound":
            word=self.textDisplay_buffer.get_text(bound[0],bound[1])
            testament=self.getInfoBasedOnAbbr(self.selected_book)['testament']
            if re.compile("\D").search(word):
                return

            if testament==1:
                searchString="H"+str(word)
            else:
                searchString="G"+str(word)
            self.searchEntry.set_text(searchString)
            self.searchGo.clicked()
            if not self.searchDisplay:
                self.textDisplay_hpaned.set_position(150)
                self.searchDisplay=True

            
        
    def motion_notify(self,widget,event):
        if not self.buttonPressed:
            return

        adj=self.textDisplay_scrolledWindow.get_vadjustment()
        adjval=adj.get_value()

        cur=event.y
        modif=self.y['new']-cur
        self.motion_adjust+=modif
            
        self.y['old']=self.y['new']
        self.y['new']=cur

    def change_font_size(self,modif):
        self.fontSize+=modif
        self.normalTag.set_property('size-points',self.fontSize)
        self.highlightTag.set_property('size-points',self.fontSize)
        #testSize=self.fontSize-int(self.fontSize/10)
        #print "fontSize=",self.fontSize
        #print "testSize=",testSize
        self.indiceTag.set_property('size-points',self.fontSize)
        
    def on_window_state_change(self,widget,events):
        self.scrollToSelected()
        
    def putJob(self,newJob):
        """
        Push aJob ob in qQuery. 
        
        """
        self.jobCounter+=1
        #self.ioWait="Query #"+str(newJob.id)
        #self.statusbar.push(self.statusbar.get_context_id('cont'),self.ioWait)
        #self.mainWindow.set_cursor(self.watchCursor)
        self.qQuery.put(newJob,True,10)

    def refresh_module_list(self):
        self.installedModules=self.get_installedModules()

        self.mainWindow.ModuleChooserMenu.destroy()
        self.mainWindow.ModuleChooserMenu=gtk.Menu()
        self.mainWindow.itemSubMenuModuleChooser.set_submenu(\
            self.mainWindow.ModuleChooserMenu)
        defaultModule=self.get_defaultModule()

        targetItem=False

        if not self.installedModules.keys():
            return
        
        for m in self.installedModules.keys():
            if m=="StrongsHebrew" or m=="StrongsGreek":
                continue
            
            cur=gtk.MenuItem(m)
            if m==defaultModule:
                targetItem=cur
            self.mainWindow.ModuleChooserMenu.append(cur)
            cur.connect_object("activate",self.moduleActivate,m)
            cur.show()

        if targetItem:
            targetItem.activate()
        else:
            cur.activate()

    def refresh_textArray(self):
       book=self.getInfoBasedOnAbbr(self.selected_book)
       self.textArray=[]
       bName=book['name']
       vk=Sword.VerseKey()
       markup=Sword.MarkupFilterMgr(Sword.FMT_PLAIN)
       markup.thisown=False
       mgr=swordutils.get_manager(markup)
       mod=mgr.getModule(self.selected_module)
       if not mod:
          return
       nbr_verses=vk.verseCount(book['testament'],\
                                book['bookCount'],self.selected_chapter)
       for i in range(nbr_verses):
          n=i+1
          key="%s %d:%d"%(bName,self.selected_chapter,n)
          vk=Sword.VerseKey(key)
          mod.setKey(vk)
          #verseTxt=mod.RenderText()
          mgr.setGlobalOption("Cross-references","Off")
          if self.showStrongNumbers:
              mgr.setGlobalOption("Strong's Numbers","On")
          else:
              mgr.setGlobalOption("Strong's Numbers","Off")
          verseTxt=mod.RenderText()

          #On Diablo, there is a strange issue with displaying modules such
          #as ABP
          # PS 1:4 is rendered the following way:
          #
          #Not so the impious, not so; but <G237.1> is as dust <G5515.2> which [3casts forth <G1610.1> 1the 2wind] from the face of the earth.
          
          verseTxt=re.sub(r'<\S+>','',verseTxt)

          if self.append_carriage_return:
              verseTxt.rstrip("\r")
              verseTxt.rstrip("\n")
              verseTxt=verseTxt+"\n"
              
          self.textArray.append(verseTxt)
          
       self.refreshTextDisplay()


    def scrollToSelected(self):
        mark=self.textDisplay_buffer.get_mark(str(self.selected_verse))
        if mark:
            self.textDisplay_textview.scroll_to_mark(mark,0.05, True, 0.0, 1.0)
        
    def refreshTextDisplay(self):
        for mark in self.textMarks:
            self.textDisplay_buffer.delete_mark(mark)
        self.textDisplay_buffer.set_text("")
        v=0
        for verse in self.textArray:
            v=v+1
            #versesnt="(%d) %s"%(v,verse)
            indicatorSnt="(%d) "%v
            verse_indicator_markLabel="indicator"+str(v)
            verse_begin_markLabel=str(v)
            #print verse_begin_markLabel
            verse_end_markLabel="end"+str(v)
            endIter=self.textDisplay_buffer.get_end_iter()
            indic_mark=self.textDisplay_buffer.create_mark(\
                verse_indicator_markLabel,endIter,left_gravity=True)
            self.textMarks.append(indic_mark)
            self.textDisplay_buffer.insert_with_tags(\
                endIter,indicatorSnt,self.indiceTag)
            begin_mark=self.textDisplay_buffer.create_mark(\
                verse_begin_markLabel,endIter,left_gravity=True)
            self.textMarks.append(begin_mark)
            if self.selected_verse==v:
                tmpTag=self.highlightTag
            else:
                tmpTag=self.normalTag
            self.textDisplay_buffer.insert_with_tags(endIter,verse,tmpTag)
            self.textMarks.append(\
                self.textDisplay_buffer.create_mark(\
                verse_end_markLabel,endIter,left_gravity=True))
            

        self.highlight_verse(self.selected_verse,self.highlightTag)
        self.scrollToSelected()
        
    def highlight_verse(self, verse,tag):
        if not verse:
            return
        beginMark=self.textDisplay_buffer.get_mark(str(verse))
        if not beginMark:
            print "no mark for verse ",verse
            return
        beginIter=self.textDisplay_buffer.get_iter_at_mark(beginMark)
        endMark=self.textDisplay_buffer.get_mark("end"+str(verse))
        endIter=self.textDisplay_buffer.get_iter_at_mark(endMark)

        self.textDisplay_buffer.apply_tag(tag,beginIter,endIter)
        #self.textDisplay_buffer.place_cursor(beginIter)
        
    def moduleActivate(self,moduleName):
        self.selected_module=moduleName
        self.mainWindow.set_title(moduleName)
        self.refresh_textArray()

        if swordutils.has_strong_number(self.mgr,moduleName):
            self.mainWindow.strong_number_item.show()
        else:
            self.showStrongNumbers=False
            self.mainWindow.strong_number_check_b.set_active(False)
            self.mainWindow.strong_number_item.hide()




    def book_combo_changed(self,widget):
        self.selected_book=self.get_combo_active_text(widget)
        self.fillChapters()
        
    def chapter_combo_changed(self,widget):
        self.selected_chapter=int(self.get_combo_active_text(widget))
        self.fillVerses()
        #self.refresh_textArray()

    def verse_combo_changed(self,widget):
        self.highlight_verse(self.selected_verse,self.normalTag)
        self.selected_verse=int(self.get_combo_active_text(widget))
        self.refresh_textArray()
        """

        print "verse_combo_changed:",self.selected_verse
        print self.selected_verse
        #
        #mark.set_visible(True)
        self.highlight_verse(self.selected_verse,self.highlightTag)
        mark=self.textDisplay_buffer.get_mark(str(self.selected_verse))
        self.textDisplay_textview.scroll_to_mark(mark,0.05)
        """
    def display_b_clicked(self,widget):
        self.refresh_textArray()

    def search_b_clicked(self,widget):
        if not swordutils.has_clucene_index(self.mgr, self.selected_module)\
               and not self.showStrongNumbers:
            dialog = hildon.Note ("information", \
                                  (self.mainWindow, \
                                   "You need to build an index for "+\
                                   "this module first.",\
                                   gtk.STOCK_DIALOG_INFO)\
                                  )
            dialog.set_button_text("Ok")
            dialog.run()
            dialog.destroy()
            return

        if self.showStrongNumbers and not self.strong_db:
            dialog = hildon.Note ("information", \
                                  (self.mainWindow, \
                                   "You need to download a Strong Number "+\
                                   "database and change "+\
                                   "the Preferences to point to its "+\
                                   "location.\n"+\
                                   "Such a database is available at \n"+\
                                   
                                   "http://hurdygurdy.dyndns.org/maemosword/strong.zip",\
                                   gtk.STOCK_DIALOG_INFO)\
                                  )
            dialog.set_button_text("Ok")
            dialog.run()
            dialog.destroy()
            return
            
        
        filterSnt=self.searchEntry.get_text()
        args={'filter':filterSnt,'module':self.selected_module,\
              'useStrongNumber':self.showStrongNumbers,\
              'database':self.strong_db}
        self.searchProgressbar.show()
        self.waitForSearchFlag=True
        self.putJob(aJob(self.jobCounter,"search_string",args))
        self.clean_searchResult_TreeView()

    def back_b_clicked(self,widget):
        if self.selected_chapter>1:
            tmp=int(self.selected_chapter)-1
            self.mainWindow.chapter_tb_combo.set_active(tmp-1)
            self.refresh_textArray()

    def next_b_clicked(self,widget):
        if self.selected_chapter<self.maxChapter:
            self.mainWindow.chapter_tb_combo.set_active(self.selected_chapter)

    def strong_number_check_b_clicked(self,widget):
        self.showStrongNumbers= widget.get_active()
        self.refresh_textArray()

    def set_display_to_ref(self,ref):
        info=self.get_info_from_refs(ref)

        bIndex=0
        for i in self.booksInfo:
            if i['abbr']==info['abbr']:
                self.mainWindow.book_tb_combo.set_active(bIndex)
            bIndex+=1

        self.mainWindow.chapter_tb_combo.set_active(int(info['chapter'])-1)
        self.mainWindow.verse_tb_combo.set_active(int(info['verse'])-1)


    def result_selection_changed(self,selection):
        model,iter=selection.get_selected()
        if not iter:
            return
        id=self.result_ts.get_value(iter,1)
        if id:
            self.set_display_to_ref(id)
    
    def clean_searchResult_TreeView(self):
        self.result_ts.clear()
        try:
            for c in self.searchResult_TreeView.get_columns():
                self.searchResult_TreeView.remove_column(c)
        except:
            print "oups"

    def sort_books(self,model,iter1,iter2):
        """
         There is a need to sort result in the search tree view.
        """
        book1=model.get_value(iter1,0)
        long1=model.get_value(iter1,1)

        book2=model.get_value(iter2,0)
        long2=model.get_value(iter2,1)

        if (not long1 and long2) or (long1 and not long2):
            return 0

        if (not long1 and  not long2):
            """
            This is a book directory, not an actual content.
            Sorting according to book indice
            """
            #print "%s vs %s"%(book1,book2)
            info1=self.getInfoBasedOnAbbr(book1)
            info2=self.getInfoBasedOnAbbr(book2)
            if info1['testament']!=info2['testament']:
                return int(info1['testament']-info2['testament'])
            else:
                return int(info1['bookCount']-info2['bookCount'])
            print "this should not happen"

            return 1

            
        elif long1 and long2:
            """ This is 2 actual reference to be sorted"""
            
            info1=self.get_info_from_refs(long1)
            info2=self.get_info_from_refs(long2)

            if info1['abbr']!=info2['abbr']:
                print "How can this happen ???"
                return 0
            
            """so, we are sure we are in the same book"""
            if info1['chapter']==info2['chapter']:
                return int(info1['verse'])-int(int(info2['verse']))
            else:
                return int(info1['chapter'])-int(int(info2['chapter']))

            print book1,book2
            print "grumble, dont know how to sort that !"
            return 0


    def go_to_bookmark(self,position):
        """
        Set the page to display so it match position.
        position is like 'Gen 1:1'
        """
        infos=self.get_info_from_refs(position)
        cnt=0
        for i in self.booksInfo:
            if i['abbr']==infos['abbr']:
                self.mainWindow.book_tb_combo.set_active(cnt)
            cnt+=1

        for i in range(self.maxChapter):
            if int(i)==int(infos['chapter'])-1:
                self.mainWindow.chapter_tb_combo.set_active(i)

        for i in range(self.maxVerse):
            if int(i)==int(infos['verse'])-1:
                self.mainWindow.verse_tb_combo.set_active(i)


        self.refresh_textArray()
        self.scrollToSelected()


    def display_strong_info(self,afilter):
        afilter=string.upper(afilter)
        if afilter[0]=='G':
            tmpMod="StrongsGreek"
        else:
            tmpMod="StrongsHebrew"

        isInstalled=False
        
        for curMod in self.mgr.getModules().values():
            if tmpMod == curMod.Name():
                isInstalled=True

        if not isInstalled:
            return

        mod=self.mgr.getModule(tmpMod)
        mod.KeyText(afilter[1:])
        test=mod.RenderText()
        self.show_dic_window(mod.RenderText())
        
        


    def search_string(self,job):
        """ display the result of a search"""
        result=job.result
        self.searchProgressbar.hide()
        self.waitForSearchFlag=False


        if job.args['useStrongNumber']:
            self.display_strong_info(job.args['filter'])

        
        self.clean_searchResult_TreeView()
        #self.result_ts=gtk.TreeStore(str,str)
        self.result_ts.set_sort_func(0,self.sort_books)
        self.result_ts.set_sort_column_id(0,gtk.SORT_ASCENDING)
        self.searchResult_TreeView.set_model(self.result_ts)
        labelCell=gtk.CellRendererText()
        labelcol=gtk.TreeViewColumn('Ref')
        labelcol.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
        self.searchResult_TreeView.append_column(labelcol)
        labelcol.pack_start(labelCell,True)
        labelcol.set_attributes(labelCell,text=0)
        books={}
        self.searchResult_TreeView.freeze_child_notify()
        for cur in result:
            info=self.get_info_from_refs(cur)
            if not books.has_key(info['abbr']):
                """First data for the abbr book"""
                books[info['abbr']]=self.result_ts.append(\
                    None,[info['abbr'],''])
            iter=books[info['abbr']]
            self.result_ts.append(iter,[cur,cur])



        self.searchResult_TreeView.thaw_child_notify()

        

    def processAnswer(self):
        """Handle all jobs currently in qOut, if any"""
        if self.waitForSearchFlag:
            self.searchProgressbar.pulse()
        if self.waitForRemoteModulesFlag:
            self.waitingWinProgressBar.pulse()
        if self.waitForModuleManagementFlag:
            self.waitingWinProgressBar.pulse()


        #textview motion update:
        adj=self.textDisplay_scrolledWindow.get_vadjustment()
        adjval=adj.get_value()
        if self.motion_adjust!=0:
            adj.set_value(adjval+self.motion_adjust)
            self.motion_adjust=0

            
        while self.qAnswer.qsize():
            curJob=self.qAnswer.get(0)
            if curJob.type=="idle":
                return True
            elif curJob.type=="search_string":
                self.search_string(curJob)
                return True
            elif curJob.type=="remote_modules_list":
                self.refresh_remote_modules(curJob.result)
                return True
            elif curJob.type=="module_management":
                self.process_module_management(curJob.result)
                return True
            elif curJob.type=="panic":
                self.process_panic(curJob)
                return True
            elif curJob.type=="iowaitmsg":
                return True
            else:
                print "Cannot process this kind of answer:"
                print curJob.id
                print curJob.type
                #print curJob.args
        return True

class ThreadedClient:
    """
    This class launch the GuiPart and the worker thread.
    """

    def __init__(self):
        """
        This start the gui in a asynchronous thread.
        We are in the "main" thread of the application, wich will later be
        used by the gui as well. We spawn a new thread for the worker.
        
        """
        #gtk.threads_init()
        gtk.gdk.threads_init()
        self.qQuery=Queue.Queue()
        self.qAnswer=Queue.Queue()
        self.gui=GuiPart(self.qQuery,self.qAnswer)
        self.running=True
        #self.mgr=Sword.SWMgr() 
        self.incomingThread=threading.Thread(target=self.processQuery)
        self.incomingThread.setDaemon(True)
        self.incomingThread.start()
        gtk.main()
        self.running=False
       
    def search_string(self,args):
        out=[]

        useStrong=args['useStrongNumber']
        markup=Sword.MarkupFilterMgr(Sword.FMT_PLAIN)
        markup.thisown=False
        mgr=swordutils.get_manager(markup)
        mod=mgr.getModule(args['module'])
        if not useStrong:
            res=mod.doSearch(args['filter'],-4)
            for n in range(res.Count()):
                vk=res.GetElement(n)
                out.append(vk.getShortText())
            return out
        else:
            db=sqlite3.connect(args['database'])
            c=db.cursor()
            for row in \
                c.execute("select verse from  Words_Per_Verse where word=?",\
                      (string.upper(args['filter']),)):
                out.append(row[0])
            return out

    def remote_modules_list(self,args):
        data_path = args['data_path']
        tmp=swordutils.InstallMgr(dataPath=data_path,
                                  osso_context=args['osso_context'])
        return tmp.get_remote_modules_list()

    def module_management(self,args):
        #print "args=",args
        problems=False
        data_path = args['data_path']
        instmgr=swordutils.InstallMgr(dataPath=data_path,
                                      osso_context=args['osso_context'])
        for mod in args['mod_remove']:
            swordutils.debug("let s remove '%s'"%mod)
            if not instmgr.remove_module(mod):
                problems=True
        for mod in args['install']:
            remoteM={}
            for r in args['remote']:
                if r['Name']==mod:
                    remoteM=r
            swordutils.debug("let's install %s"%mod)
            swordutils.debug("dataPath=%s"%remoteM['DataPath'])
            swordutils.debug("conf file=%s"%remoteM['conf_file_name'])
            if not instmgr.install_module(remoteM['DataPath'],
                                          remoteM['conf_file_name']):
                swordutils.debug("Installing %s failed"%mod) 
                problems=True
            else:
                swordutils.debug("%s module installation is ok"%mod)
            
        for idx in args['idx_remove']:
            swordutils.debug("let's remove '%s' "%idx)
            if not instmgr.remove_index(idx):
                swordutils.debug("Removing %s failed"%idx)
                problems=True
        for idx in args['idx_build']:
            swordutils.debug("let's create index for %s"%idx)
            if not instmgr.create_index(idx):
                swordutils.debug("Creating index for %s failed"%idx)
                problems=True
        return problems
        
    def processQuery(self):
       """
       This is where the blocking I/O job is being done.
       """

       while self.running:
           while self.qQuery.qsize():
               try:
                   job=self.qQuery.get(False,40)
                   job.result='we would store the resutl here'
                   ioWaitMsg=aJob(0,'iowaitmsg',\
                                  {'jobid':job.id,'type':job.type})
                   self.qAnswer.put(ioWaitMsg)
                   if job.type=="search_string":
                       job.result=self.search_string(job.args)
                   elif job.type=="remote_modules_list":
                       job.result=self.remote_modules_list(job.args)
                   elif job.type=="module_management":
                       job.result=self.module_management(job.args)
                   else:
                       swordutils.debug(\
                           "Cannot cope with this kind of job:%s"%job.type)
                       print "Cannot cope with this kind of job:"
                       print job.id
                       print job.type
                       print job.args
                   self.qAnswer.put(job)
               except Queue.Empty:
                   print "Queue should not be empty"
                   pass
               except:
                   print "ke passa ?"
                   swordutils.debug('Ke passa ?')
                   swordutils.debug(\
                       "Yet another unexpected error:%s"%sys.exc_info()[0]
                       )
                   quitJob=aJob(0,"panic",{"message":sys.exc_info()[0]})
                   self.qAnswer.put(quitJob)
                   sys.exit()
               
           self.gui.currentJobId=None
           idlMsg=aJob(0,'idle',{})
           self.qAnswer.put(idlMsg)
           time.sleep(1)

if __name__=="__main__":
    plop=ThreadedClient()
    
