# Copyright 2004,2005 Pierre Martineau <pmartino@users.sourceforge.net>
# This file is part of Bibus, a bibliographic database that can
# work together with OpenOffice.org to generate bibliographic indexes.
#
# Bibus 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; either version 2 of the License, or
# (at your option) any later version.
#
# Bibus 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 Bibus; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
import wx, BIB, DnD, os, urllib
from Utilities.open_url import open as openfile

class RefList(wx.ListCtrl):
    def __init__(self,parent,bibus_frame,style=wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES,refs=()):
        """refs = list/tuple containing the references to display
        The first column = ItemData = Id in databae
        bibus_frame = main window Frame
        refs[item][0] = id in db
        refs[item][1:] = values to display
        """
        self.bibus_frame = bibus_frame
        self.refs = []
        self.datas = []
        wx.ListCtrl.__init__(self,parent,-1,style=style)
        self.resizeList()
        self.__set_evt()

    def setList(self,refs):
        self.DeleteAllItems()
        self.SetItemCount( len(refs) )
        self.refs = refs
        self.datas = [ref[0] for ref in refs]

    def OnGetItemText(self,item,column):
        if item < len(self.refs):
            return apply(BIB.LIST_FIELD_FORMAT[BIB.LIST_DISPLAY[column]],(self.refs[item][column+1],))

    def GetItemData(self,item):
        return self.datas[item]

    def FindItemData(self,start,data):
        if start == -1:
            return self.datas.index(data)
        else:
            return self.datas.index(data,start)

    def resizeList(self):
        """Insert Column names and resizes"""
        self.ClearAll()
        for i in xrange(len(BIB.LIST_DISPLAY)):
            self.InsertColumn(i,BIB.NAME_FIELD[BIB.LIST_DISPLAY[i]])
            try:
                self.SetColumnWidth(i, BIB.LIST_COL_SIZE[i])
            except IndexError:
                self.SetColumnWidth(i, BIB.LIST_COL_SIZE_DEFAULT)
        try:
            i = list(BIB.LIST_DISPLAY).index(BIB.LIST_ORDER)
            self.setArrow(i) # put the arrow corresponding to the sort order
        except ValueError:
            pass

    def __set_evt(self):
        wx.EVT_LIST_COL_CLICK(self,self.GetId(),self.onSortList)            # sort list in ascending order of the clicked column
        wx.EVT_LIST_COL_END_DRAG(self,self.GetId(),self.onDragColumn)
        #wx.EVT_LIST_ITEM_DESELECTED(self,self.GetId(),self.onDeselectRef)
        wx.EVT_LIST_ITEM_SELECTED(self,self.GetId(),self.onSelectRef)
        wx.EVT_LIST_BEGIN_DRAG(self,self.GetId(),self.onDragRef)
        wx.EVT_LIST_ITEM_ACTIVATED(self,self.GetId(),self.onEditRef)        # Double click OR enter
        wx.EVT_LIST_ITEM_RIGHT_CLICK(self,self.GetId(),self.onListRightClick)
        wx.EVT_LIST_ITEM_MIDDLE_CLICK(self,self.GetId(),self.onOpenURL)    # doesn't work with the openoffice python 2.2.2 under linux!
        #wx.EVT_LIST_KEY_DOWN(self,self.GetId(),self.onKeyDown)
        wx.EVT_LIST_KEY_DOWN(self,self.GetId(),self.onKeyDown)
        # PopUp menu ListCtrl
        [self.popTagRef,self.popEditRef, self.popNewRef, self.popDeleteRef, self.popOpenURL, self.popCite, self.popDeleteOnline] = [wx.NewId() for x in range(7)]
        wx.EVT_MENU(self,self.popTagRef,self.bibus_frame.onMenuTagRef)
        wx.EVT_MENU(self,self.popEditRef,self.bibus_frame.onMenuEditRef)
        wx.EVT_MENU(self,self.popNewRef,self.bibus_frame.onMenuNewRef)
        wx.EVT_MENU(self,self.popDeleteRef,self.bibus_frame.onMenuDelRef)
        wx.EVT_MENU(self,self.popOpenURL,self.onMenuOpenURL)
        #wx.EVT_MENU(self,self.popCite,self.onMenuCite)
        wx.EVT_MENU(self,self.popDeleteOnline,self.onMenuDeleteOnline)
        # we capture mouse Up evt to update the status bar
        # because multiple selection does not generate a SELCT evt with VirtualList in wxPython.
        # we have thus to look for mouse and key evt
        wx.EVT_LEFT_UP(self,self.onLeftUp)
        wx.EVT_KEY_UP(self,self.onKeyUp)

    def setArrow(self,col):
        """Set the arrow to show the sort order in the column col"""
        if BIB.LIST_HOW == 'ASC':
            self.SetColumnImage(col,BIB.ARROW_UP)
        else:
            self.SetColumnImage(col,BIB.ARROW_DN)

    def onSortList(self,event):
        """Sort the list in the ascending order of the clicked column"""
        if event.GetColumn()!= -1:
            if BIB.LIST_ORDER == BIB.LIST_DISPLAY[event.GetColumn()]:    # this column was the last sorted
                if BIB.LIST_HOW == 'ASC': BIB.LIST_HOW = 'DESC'            # we reverse the sort order
                else: BIB.LIST_HOW = 'ASC'
            else:
                try:
                    self.ClearColumnImage(list(BIB.LIST_DISPLAY).index(BIB.LIST_ORDER))    # clear previous arrow
                except ValueError:
                    pass
                BIB.LIST_ORDER = BIB.LIST_DISPLAY[event.GetColumn()]
                BIB.LIST_HOW = 'ASC'
            self.bibus_frame.updateRefList()    # we update the references list
            self.setArrow(event.GetColumn())    # we put the arrow

    def onDragColumn(self,event):
        """Resizing column"""
        tmp = []
        for i in xrange( self.GetColumnCount() ):
            tmp.append( self.GetColumnWidth(i) )
        BIB.LIST_COL_SIZE = tuple(tmp)

    def onDeselectRef(self,event):
        self.bibus_frame.bibframe1_statusbar.SetStatusText(_("%(total_number)s reference(s) : %(number_selected)s selected")%{'total_number':self.GetItemCount(),'number_selected':self.GetSelectedItemCount()},1)
        self.bibus_frame.set_menu(self.bibus_frame.keytree.GetSelection())    # update menu
        
    def __updateStatusBar(self):
        self.bibus_frame.bibframe1_statusbar.SetStatusText(_("%(total_number)s reference(s) : %(number_selected)s selected")%{'total_number':self.GetItemCount(),'number_selected':self.GetSelectedItemCount()},1)
        self.bibus_frame.set_menu(self.bibus_frame.keytree.GetSelection())    # update menu
        
    def onLeftUp(self,evt):
        self.__updateStatusBar()
        evt.Skip()

    def onSelectRef(self,event):
        """ Display the full selected reference. If multiple selection -> no display"""
        if self.GetSelectedItemCount() == 1:
            ref_id = self.GetItemData(self.GetFirstSelected())
            ref = self.bibus_frame.db.getRef(ref_id,BIB.BIB_FIELDS)
            id = self.bibus_frame.keytree.GetPyData(self.bibus_frame.keytree.GetSelection())[2]
            if id == BIB.ID_IMPORT_ROOT:    # we are in the import list
                ref = self.bibus_frame.db.getRefImport(ref_id,BIB.BIB_FIELDS)
            elif id == BIB.ID_ONLINE_ROOT:    # we are in the online list
                ref = self.bibus_frame.db.getRefOnline(ref_id,BIB.BIB_FIELDS)
            else:                            # we are in ref or query
                ref = self.bibus_frame.db.getRef(ref_id,BIB.BIB_FIELDS)
            # we display the reference
            self.bibus_frame.refDisplaypanel.display(ref[0])
        self.bibus_frame.set_menu(self.bibus_frame.keytree.GetSelection())
        
    def onDragRef(self,event):
        "Drag list of Id of the selected references"
        sourceitem = self.bibus_frame.keytree.GetSelection()
        oldkey_id,user,id = self.bibus_frame.keytree.GetPyData(sourceitem)
        allowed=self.bibus_frame.allowed[id]
        if allowed & BIB.REF_DRAG_OK:
            if id == BIB.ID_ONLINE_ROOT or id == BIB.ID_ONLINE:
                key = u'Online'
            elif id == BIB.ID_IMPORT_ROOT or id == BIB.ID_IMPORT:
                key = u'Import'
            else:
                key = u''
            #
            dragData = DnD.bibDragData(self.bibus_frame.db.getDbInfo(key),BIB.DnD_REF)
            item = self.GetFirstSelected()
            if id == BIB.ID_IMPORT_ROOT:    # we are in the import list
                f = self.bibus_frame.db.getRefImport
            elif id == BIB.ID_ONLINE_ROOT:    # we are in the online list
                f = self.bibus_frame.db.getRefOnline
            else:                # we are in ref or query
                f = self.bibus_frame.db.getRef
            while item != -1:
                dragData.append(tuple(apply(f,(self.GetItemData(item),BIB.BIB_FIELDS))[0]))
                # tuple is needed for sqlite since returned tuple is of class sqlite.main.PgResultSetConcreteClass
                item = self.GetNextSelected(item)
            mydrag = DnD.bibDataObject()
            mydrag.setObject(dragData)
            dragsource = wx.DropSource(self)
            dragsource.SetData(mydrag)
            result = dragsource.DoDragDrop(wx.Drag_DefaultMove)
            if allowed & BIB.REF_DEL_OK and result == wx.DragMove:
                for ref in dragData:
                    self.bibus_frame.db.delLink(oldkey_id,ref[0])
            self.bibus_frame.keytree.KeySelect(sourceitem)
            #self.bibus_frame.updateList(self.bibus_frame.db.getRefKey(oldkey_id,BIB.LIST_DISPLAY,BIB.LIST_ORDER,BIB.LIST_HOW,short=True))
        else:
            event.Veto()
            self.showError(_("Sorry, you cannot move a reference associated with this key."))

    def onEditRef(self,event):
        self.bibus_frame.onMenuEditRef(None)

    def onListRightClick(self,event):
        where = event.GetPoint()    # position relative to the window
        pw = self.GetPosition()
        where.x = where.x + pw.x
        where.y = where.y + pw.y
        # deselect everything if we did not click in a selected item
        if not self.IsSelected(event.GetIndex()):
            item = self.GetFirstSelected()
            while item != -1:
                self.Select(item,False)
                item = self.GetNextSelected(item)
            self.Select(event.GetIndex(),True)    # select the item
        self.Focus(event.GetIndex())            # focus
        key_id,user,id = self.bibus_frame.keytree.GetPyData(self.bibus_frame.keytree.GetSelection())
        #print "key_id=", key_id, " ", BIB.ID_ONLINE, " ",BIB.ID_ONLINE_ROOT
        popUp = wx.Menu()
        ap=wx.ArtProvider()
        #item = wx.MenuItem(popUp, id=self.popCite, text=_('Copy Identifier'))
        #popUp.AppendItem(item)
        if self.bibus_frame.allowed[id] & BIB.REF_TAG_OK:
            item =wx.MenuItem(popUp,id=self.popTagRef,text=_('Tag'))
            item.SetBitmap(ap.GetBitmap(wx.ART_TICK_MARK,client=wx.ART_MENU))
            popUp.AppendItem(item)
            #item = popUp.Append(self.popTagRef,_('Tag'))
        if self.bibus_frame.allowed[id] & BIB.REF_EDIT_OK:
            if popUp.GetMenuItemCount() > 0:
                popUp.AppendSeparator()
            item =wx.MenuItem(popUp,id=self.popEditRef,text=_('Edit'))
            item.SetBitmap(ap.GetBitmap(wx.ART_EXECUTABLE_FILE,client=wx.ART_MENU))
            popUp.AppendItem(item)
            #popUp.Append(self.popEditRef,_('Edit'))
        if self.bibus_frame.allowed[id] & BIB.REF_NEW_OK:
            item =wx.MenuItem(popUp,id=self.popNewRef,text=_('New'))
            item.SetBitmap(ap.GetBitmap(wx.ART_NEW,client=wx.ART_MENU))
            popUp.AppendItem(item)
            #popUp.Append(self.popNewRef,_('New'))
        if self.bibus_frame.allowed[id] & BIB.REF_DEL_OK:
            item =wx.MenuItem(popUp,id=self.popDeleteRef,text=_('Delete'))
            item.SetBitmap(ap.GetBitmap(wx.ART_DELETE,client=wx.ART_MENU))
            popUp.AppendItem(item)
            #popUp.Append(self.popDeleteRef,_('Delete'))
        #if key_id == 13: #problem here
        #    popUp.Append(self.popDeleteOnline,_('Delete All'))
        # We add an OpenURL if there is at least 1 URL and only one selected reference
        ref_Id = self.GetItemData( self.GetFirstSelected() )
        urls = self.__getURLs(ref_Id)
        if urls or self.GetSelectedItemCount() != 1:
            if popUp.GetMenuItemCount() > 0:
                popUp.AppendSeparator()
            if self.GetSelectedItemCount() != 1:
                item =wx.MenuItem(popUp,id=self.popOpenURL,text=_('Open URLs'))
            elif len(urls) == 1:
                item =wx.MenuItem(popUp,id=self.popOpenURL,text=_('Open %(url)s')%{'url':urls[0]})
            else:
                item =wx.MenuItem(popUp,id=self.popOpenURL,text=_('Open URL'))
            item.SetBitmap(ap.GetBitmap(wx.ART_GO_FORWARD,client=wx.ART_MENU))
            wx.EVT_MENU(self,item.GetId(),self.OpenURLFromSelected)
            # submenu when more than 1 URL
            if len(urls) > 1 and self.GetSelectedItemCount() == 1:
                popURL = wx.Menu()
                item.SetSubMenu( popURL )
                for fn in urls:
                    menuitem = popURL.Append(-1,fn)
                    wx.EVT_MENU(self,menuitem.GetId(),self.onMenuOpenURL)
            popUp.AppendItem(item)
        #
        if popUp.GetMenuItemCount() > 0:
            self.PopupMenu(popUp,where)

    def __getURLs(self,ref_id):
        key_id = self.bibus_frame.keytree.GetPyData(self.bibus_frame.keytree.GetSelection())[2]
        if key_id in (BIB.ID_REF_ROOT,BIB.ID_REF,BIB.ID_REF_ALL,BIB.ID_QUERY,BIB.ID_CITED,BIB.ID_TAGGED):
            url = self.bibus_frame.db.getRef(ref_id,('URL',))[0][0]
            files = self.bibus_frame.db.getFiles( ref_id )
            if url:
                url = (url,) + files
            else:
                url = files
        elif key_id in (BIB.ID_ONLINE,BIB.ID_ONLINE_ROOT):
            url = self.bibus_frame.db.getRefOnline(ref_id,('URL',))[0][0]
            if url: url = (url,)
            else: url = ()
        elif key_id in (BIB.ID_IMPORT,BIB.ID_IMPORT_ROOT):
            url = (self.bibus_frame.db.getRefImport(ref_id,('URL',))[0][0],)
            if url: url = (url,)
            else: url = ()
        else:
            url = ()
        return url    

    def __openInBrowser(self,url):
        """Open the ref ref_id in a webbbrowser or in the correct application if there is a non empty URL field"""
        if url:
            openfile( url.replace("$FILES",BIB.FILES) )

    def onMenuOpenURL(self,event):    # popUp menu
        """Open the selected refs in a WebBrowser when menu selected"""
        self.__openInBrowser( event.GetEventObject().FindItemById( event.GetId() ).GetLabel() )
        
    def OpenURLFromSelected(self,event):
        item = self.GetFirstSelected()
        while item != -1:
            self.__openInBrowser( self.__getURLs(self.GetItemData(item))[0]) # We use the first URL == Field URL
            item = self.GetNextSelected(item)

    def onMenuCite(self,event): # popUp menu
        if self.GetSelectedItemCount() == 1:
            cite_id = self.GetItemText(self.GetFirstSelected())
            if wx.TheClipboard.Open():
                wx.TheClipboard.Clear()
                wx.TheClipboard.SetData(wx.TextDataObject(cite_id))
                wx.TheClipboard.Close() 

    def onOpenURL(self,event):        # middle click
        """Open the selected refs in a WebBrowser when middle click in it"""
        # deselect everything if we did not click in a selected item
        if not self.IsSelected(event.GetIndex()):
            item = self.GetFirstSelected()
            while item != -1:
                self.Select(item,False)
                item = self.GetNextSelected(item)
            self.Select(event.GetIndex())    # select the item
        self.Focus(event.GetIndex())    # focus on the clicked reference
        item = self.GetFirstSelected()
        self.OpenURLFromSelected(None)

    def onMenuDeleteOnline(self,event):
        #print "deleting online"
        self.bibus_frame.db.deleteOnline()
        self.bibus_frame.keytree.SelectItem(self.bibus_frame.keytree.keyOnline)
        self.bibus_frame.updateList(self.bibus_frame.db.getAllRefOnline(BIB.LIST_DISPLAY,BIB.LIST_ORDER,BIB.LIST_HOW,short = True))

    def onKeyDown_old(self,event):
        id = self.bibus_frame.keytree.GetPyData(self.bibus_frame.keytree.GetSelection())[2]
        if (event.GetKeyCode() == wx.WXK_DELETE) and (self.bibus_frame.allowed[id] & BIB.REF_DEL_OK):
            self.bibus_frame.onMenuDelRef(None)
        elif ( event.GetKeyCode() == ord(BIB.KEY_TAGGED) ) and (self.bibus_frame.allowed[id] & BIB.REF_TAG_OK):
            self.bibus_frame.onMenuTagRef(None)
        elif ( event.GetKeyCode() == ord(BIB.KEY_SWITCH_DISPLAY) ):
            self.bibus_frame.refDisplaypanel.switchDisplay()
            self.SetFocus()
        else:
            event.Skip()
            
    def onKeyUp(self,event):
        """We update the StatusBar on keyUp, after the item has been selected"""
        if event.GetKeyCode() in (wx.WXK_END,wx.WXK_HOME,wx.WXK_UP,wx.WXK_DOWN,wx.WXK_PAGEUP,wx.WXK_PAGEDOWN):
            self.__updateStatusBar()
            event.Skip()

    def onKeyDown(self,event):
        key_id,user,id = self.bibus_frame.keytree.GetPyData(self.bibus_frame.keytree.GetSelection())
        if (event.GetKeyCode() == wx.WXK_DELETE) and (self.bibus_frame.allowed[id] & BIB.REF_DEL_OK):
            self.bibus_frame.onMenuDelRef(None)
        else:
            if event.GetKeyCode() not in xrange(1,256):
                event.Skip()
                return
            k = chr(event.GetKeyCode())
            if id == BIB.ID_REF or id == BIB.ID_REF_ROOT or id == BIB.ID_TAGGED:
                refs = self.bibus_frame.db.getRefKeySearch(key_id,"Identifier LIKE '%s%%'"%k,collist=())
            elif id == BIB.ID_REF_ALL:
                refs = self.bibus_frame.db.getAllRefSearch(user,"Identifier LIKE '%s%%'"%k,collist=())
            elif id == BIB.ID_ONLINE_ROOT:
                refs = self.bibus_frame.db.getOnlineRefSearch("Identifier LIKE '%s%%'"%k,collist=())
            elif id == BIB.ID_IMPORT_ROOT:
                refs = self.bibus_frame.db.getImportRefSearch("Identifier LIKE '%s%%'"%k,collist=())
            #elif id == BIB.ID_QUERY:
            #elif id == BIB.ID_QUERY_ROOT:
            #elif id == BIB.ID_CITED:
            else:
                event.Skip()
                return
            if refs:
                self.selectItem(refs[0][0])
                return
            else:
                event.Skip()

    def selectItem(self,ref_id):
        """Select item corresponding to reference with id ref_id in db"""
        item = self.GetFirstSelected()
        while item != -1:
            self.Select(item,False)
            item = self.GetNextSelected(item)
        item = self.FindItemData(-1, ref_id)
        self.EnsureVisible(item)
        self.Select(item, True)
        self.Focus(item)

