# 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
import BIB, DnD, Search

class KeyTree(wx.TreeCtrl):
	def __init__(self,bibus_frame,parent):
		self.bibus_frame = bibus_frame
		self.user = None	# current user
		self.restrict = ''	# we display only the keys LIKE %restrict%
		self.grants = 'rw'	# current grants for the keytree
		self.allowed = BIB.ALLOWED[self.grants]
		# keys to access main keys
		self.keyroot = None
		self.keyImport = None
		self.keyOnline = None
		self.keyRef = None
		self.keyAll = None
		self.keyQueryRoot = None
		self.keyTagged = None
		self.keyCited = None	# Display the citations present in the current OOo doc
		self.keyTrash = None
		# Key PopUpMenu item
		self.popRename = wx.NewId()
		self.popAddChild = wx.NewId()
		self.popNewRef = wx.NewId()
		self.popNewQuery = wx.NewId()
		self.popRenameQuery = wx.NewId()
		self.popEditQuery = wx.NewId()
		self.popDeleteKey = wx.NewId()
		self.popDeleteQuery = wx.NewId()
		#
		wx.TreeCtrl.__init__(self,parent, -1, style=wx.TR_HAS_BUTTONS|wx.TR_EDIT_LABELS|wx.TR_LINES_AT_ROOT|wx.TR_HIDE_ROOT|wx.TR_SINGLE)
		self.__set_evt_tree()
		self.SetImageList(BIB.IMAGESKEY)

	def __set_evt_tree(self):
		id = self.GetId()
		wx.EVT_TREE_SEL_CHANGING(self,id,self.onKeySelect)
		wx.EVT_TREE_ITEM_EXPANDING(self,id,self.onKeyExpand)
		wx.EVT_TREE_ITEM_RIGHT_CLICK(self,id,self.onKeyRightClick)
		wx.EVT_TREE_END_LABEL_EDIT(self,id,self.onKeyEndEdit)
		wx.EVT_TREE_BEGIN_LABEL_EDIT(self,id,self.onKeyBeginEdit)
		wx.EVT_TREE_BEGIN_DRAG(self,id,self.onDragKey)
		# PopUp menu keyCtrl
		wx.EVT_MENU(self,self.popRename,self.onPopRenameKey)
		wx.EVT_MENU(self,self.popAddChild,self.onMenuAddChild)
		wx.EVT_MENU(self,self.popNewRef,self.bibus_frame.onMenuNewRef)
		wx.EVT_MENU(self,self.popDeleteKey,self.onPopDeleteKey)
		wx.EVT_MENU(self,self.popNewQuery,self.onPopNewQuery)
		wx.EVT_MENU(self,self.popRenameQuery,self.onPopRenameQuery)
		wx.EVT_MENU(self,self.popEditQuery,self.onPopEditQuery)
		wx.EVT_MENU(self,self.popDeleteQuery,self.onPopDeleteQuery)

	def __set_drag_tree(self):
		# DnD of references and keys
		target = DnD.keyDropTarget(self.bibus_frame)
		self.SetDropTarget(target)

	def __KeyExpand(self,item):
		parent_data = self.GetPyData(item)
		keychildren = self.bibus_frame.db.getKeyChildren(self.user,parent_data[0],self.restrict)
		self.DeleteChildren(item)
		for child,child_id in keychildren:	# Add children
			childitem = self.AppendItem(item,child)
			self.SetPyData(childitem,(child_id,parent_data[1],BIB.ID_CHILDREN[parent_data[2]]))
			if self.bibus_frame.db.getKeyChildren(self.user,child_id,self.restrict):
				self.SetItemHasChildren(childitem,True)

	def __queryRootExpand(self):
		self.DeleteChildren(self.keyQueryRoot)
		if self.bibus_frame.db.getQueries(self.user):	# we look if there are children. restrict=''
			self.SetItemHasChildren(self.keyQueryRoot,True)
			for query,query_id in self.bibus_frame.db.getQueries(self.user,self.restrict):
				childitem = self.AppendItem(self.keyQueryRoot,query)
				self.SetPyData(childitem,(query_id,self.allowed[BIB.ID_QUERY],BIB.ID_CHILDREN[BIB.ID_QUERY_ROOT]))
		else:
			self.SetItemHasChildren(self.keyQueryRoot,False)

	def onKeyExpand(self,event):
		item = event.GetItem()
		id = self.GetPyData(item)[2]
		if id == BIB.ID_REF or id == BIB.ID_REF_ROOT:
			self.__KeyExpand(item)
		elif id == BIB.ID_QUERY_ROOT:
			self.__queryRootExpand()
		else:
			pass

	def onEndDragKey(self,event):
		return

	def onDragKey(self,event):
		if self.allowed[self.GetPyData(event.GetItem())[2]] & BIB.DRAG_OK:
			event.Allow()
			movedKey = event.GetItem()	# key we are moving
			movedKeyID  = self.GetPyData(movedKey)[0]
			oldparent = self.GetItemParent(movedKey)
			oldparentId = self.GetPyData(oldparent)[0]
			#
			dragData = DnD.bibDragData(self.bibus_frame.db.getDbInfo(),BIB.DnD_KEY)
			dragData.append(movedKeyID)
			mydrag = DnD.bibDataObject()
			mydrag.setObject(dragData)
			dragsource = wx.DropSource(self)
			dragsource.SetData(mydrag)
			result = dragsource.DoDragDrop(wx.Drag_DefaultMove)
			childrenIDs = [y for x,y in self.bibus_frame.db.getKeyChildren(self.user,oldparentId,'')]
			if movedKeyID not in childrenIDs:	# the key has been moved
				self.Delete(movedKey)
				if childrenIDs:
					self.SetItemHasChildren(oldparent,True)
				else:
					self.SetItemHasChildren(oldparent,False)
		event.Veto()	# the event has been treated in DnD.keyDropTarget.OnData

	def onKeySelect(self,event):
		"Selection of a Key -> showing the ref in the reflist view"
		#print self.bibus_frame.IsOK
		if event.GetItem():
			self.bibus_frame.toolbar.clearSearch()	# clear the text in the search box
			self.KeySelect(event.GetItem())
		else:
			event.Veto()

	def KeySelect(self,item): #,order=None,how=BIB.LIST_HOW):
		order = BIB.LIST_ORDER
		how = BIB.LIST_HOW
		try:
			key_id,user,id = self.GetPyData(item)	# item is sometimes invalid
		except TypeError:
			return
		allowed=self.allowed[id]
		if allowed & BIB.SELECT_OK:
			try:
				self.bibus_frame.refDisplaypanel.Clear()	
			except wx.PyDeadObjectError: #Prevent the bug occuring when bibus is closed
				# I must have a look at the callbacks. Must use custom Events ?
				return
			# clear display of old ref
			if id == BIB.ID_REF or id == BIB.ID_REF_ROOT or id == BIB.ID_TAGGED:
				self.bibus_frame.updateList(self.bibus_frame.db.getRefKey(key_id,BIB.LIST_DISPLAY,order,how,short=True))
			if id == BIB.ID_REF_ALL:				# show everything
				self.bibus_frame.updateList(self.bibus_frame.db.getAllRef(self.user,BIB.LIST_DISPLAY,order,how,short=True))
			elif id == BIB.ID_ONLINE_ROOT:			# display the last online search
				self.bibus_frame.updateList(self.bibus_frame.db.getAllRefOnline(BIB.LIST_DISPLAY,order,how,short=True))
			elif id == BIB.ID_IMPORT_ROOT:			# display the last import
				self.bibus_frame.updateList(self.bibus_frame.db.getAllRefImport(BIB.LIST_DISPLAY,order,how,short=True))
			elif id == BIB.ID_QUERY:
				self.bibus_frame.updateList(self.bibus_frame.db.getQueryRef(self.user,key_id,BIB.LIST_DISPLAY,order,how,short=True))
			elif id == BIB.ID_QUERY_ROOT:
				self.bibus_frame.updateList(())
			elif id == BIB.ID_TRASH:
				self.bibus_frame.updateList(self.bibus_frame.db.getTrashRef(self.user,BIB.LIST_DISPLAY,order,how,short=True))
			elif id == BIB.ID_CITED:
				if BIB.WP == "mswDoc":
					self.bibus_frame.updateList(self.bibus_frame.MSWSelectCit(order,short=True))
				elif BIB.WP == "OOo":
					self.bibus_frame.updateList(self.bibus_frame.OOoSelectCit(order,short=True))
		self.bibus_frame.set_menu(item)						# update menu

	def getKeyType(self):
		item = self.GetSelection()
		id = self.GetPyData(item)[2]
		if id == BIB.ID_ONLINE_ROOT:
			return "online"
		elif id == BIB.ID_IMPORT_ROOT:
			return "import"
		else:
			return "db"

	def onKeyBeginEdit(self,event):
		"refuse to edit not editable keys"
		if not self.allowed[self.GetPyData(event.GetItem())[2]] & BIB.RENAME_OK:
			event.Veto()
			#self.showError(_("Sorry, you cannot modify this key."))

	def __changeKeyName(self,oldName,newName,item):
		"Refuse key with a '%' or '_' inside; Refuse duplicated names; return False in that cases."
		if ('%' in newName) or ('_' in newName):
			self.showError(_("You cannot use '%' and '_' characters.\nPlease choose a new key name."))
			return False
		else:
			key_id = self.GetPyData(item)[0]
			parent_id = self.bibus_frame.db.getKeyParent(self.user,key_id)
			if not self.bibus_frame.db.keyExist(self.user,parent_id,newName):
			# the key does not exist
				self.bibus_frame.db.modifyKeyName(self.user,newName,key_id)
				return True
			else:	# refuse the change because the key already exists
				self.showError(_("The key must be unique.\nPlease choose a new key name."))
				#wx.LogError("The key must be unique.\nPlease choose a new key name.")
				return False

	def onKeyEndEdit(self,event):
		"Refuse key with a '%' or '_' inside; Refuse duplicated names"
		#print "getlabel=","%r"%event.GetLabel()
		if event.GetLabel() != u'':	# Bug in eventtree ? Waiting for event != ''
			key_id = self.GetPyData(event.GetItem())[0]
			oldname = self.bibus_frame.db.getKeyName(self.user,key_id)
			ret = self.__changeKeyName(oldname,event.GetLabel(),event.GetItem())
			if not ret:
				event.Veto()

	def __setGrants(self):
		self.grants = self.bibus_frame.db.getGrants()
		if self.grants == 'rr' or self.user == self.bibus_frame.db.user:
			self.grants = self.grants
		elif self.user != self.bibus_frame.db.user and self.grants != 'rw':
			self.grants = 'rk'
		elif BIB.ADMIN == ('*',) or self.bibus_frame.db.user in BIB.ADMIN:
			self.grants = 'rw'
		else:
			self.grants = 'rk'
		if self.grants == 'rk' and not BIB.MULTI_USER:	# 'rk' does bot mean anything if we are not in multi-user mode
			self.grants = 'rr'

	def getGrants(self):
		return self.grants

	def setkeytree(self,user=None):
		self.keyroot,self.keyImport,self.keyOnline,self.keyRef,self.keyAll,self.keyQueryRoot,self.keyTagged,self.keyCited,self.keyTrash = None,None,None,None,None,None,None,None,None
		if user:
			self.user = user
		else:
			self.user = self.bibus_frame.db.user
		#print "tester sous Windows KeyTree.setkeytree, pbl de self.KeyCited"
		if self.keyCited: self.Delete(self.keyCited)	# otherwise under Win
		self.DeleteAllItems()					# this generates a keyselect => error for OOo
		#
		self.__setGrants()
		self.allowed = BIB.ALLOWED[self.grants]
		#
		if self.grants != 'rr' and not(self.grants == 'rk' and not self.bibus_frame.db.getRoot(self.user)):
			# look for root and create it if not present
			if not self.bibus_frame.db.getRoot(self.user):
				self.bibus_frame.db.writeKey(self.user,(None,BIB.ROOT))
			root_id = self.bibus_frame.db.getRoot(self.user)[0][0]
			self.keyroot = self.AddRoot(BIB.ROOT)
			#
			for name in BIB.CATEGORIES:
				if not self.bibus_frame.db.keyExist(self.user,root_id,name):
					self.bibus_frame.db.writeKey(self.user,(root_id,name))
			# Add children to root
			tmp={}
			for child,child_id in self.bibus_frame.db.getKeyChildren(self.user,root_id,):	# Get children
				tmp[child] = child_id
			for child in BIB.CATEGORIES:							# Add children in the same order than in BIB.CATEGORIES
				childitem = self.AppendItem(self.keyroot,child)
				self.SetPyData(childitem,(tmp[child],self.user,None))
				if self.bibus_frame.db.getKeyChildren(self.user,tmp[child],''):
					self.SetItemHasChildren(childitem,True)
			# configure children
			try:
				cookie = 0
				item,cookie = self.GetFirstChild(self.keyroot,cookie)	# wxPython2.4
			except TypeError:
				item,cookie = self.GetFirstChild(self.keyroot)	# wxPython2.5
			#
			while item.IsOk():
				#if self.GetItemText(item) in BIB.CATEGORIES:
				db_key_name = self.GetItemText(item)
				#self.categories[db_key_name] = item
				if db_key_name == BIB.QUERY:
					self.SetPyData(item,self.GetPyData(item)[0:2]+(BIB.ID_QUERY_ROOT,))
					self.keyQueryRoot = item
					if self.bibus_frame.db.getQueries(self.user):
						self.SetItemHasChildren(item,True)
					self.SetItemImage(item,6)
				elif db_key_name == BIB.IMPORT:
					self.keyImport = item
					self.SetPyData(item,self.GetPyData(item)[0:2]+(BIB.ID_IMPORT_ROOT,))
					self.SetItemImage(item,1)
				elif db_key_name == BIB.ONLINE:
					self.keyOnline = item
					self.SetPyData(item,self.GetPyData(item)[0:2]+(BIB.ID_ONLINE_ROOT,))
					self.SetItemImage(item,2)
				elif db_key_name == BIB.REF:
					self.keyRef = item
					self.SetPyData(item,self.GetPyData(item)[0:2]+(BIB.ID_REF_ROOT,))
					self.SetItemImage(item,5)
				elif db_key_name == BIB.ALL:
					self.keyAll = item
					self.SetPyData(item,self.GetPyData(item)[0:2]+(BIB.ID_REF_ALL,))
					self.SetItemImage(item,7)
				elif db_key_name == BIB.CITED:
					self.keyCited = item
					self.SetPyData(item,self.GetPyData(item)[0:2]+(BIB.ID_CITED,))
					self.SetItemImage(item,3)
				elif db_key_name == BIB.TAGGED:
					self.keyTagged = item
					self.SetPyData(item,self.GetPyData(item)[0:2]+(BIB.ID_TAGGED,))
					self.SetItemImage(item,4)
				elif db_key_name == BIB.TRASH:
					self.keyTrash = item
					self.SetPyData(item,self.GetPyData(item)[0:2]+(BIB.ID_TRASH,))
					self.SetItemImage(item,0)
				else:
					print u"Warning: There is a abnormal child to root in the database."
				#print self.GetItemText(item).encode('utf8')
				try:
					self.SetItemText(item,BIB.TRANSLATE[self.GetItemText(item)])
				except KeyError:
					pass	# no translation
				item,cookie = self.GetNextChild(self.keyroot,cookie)
			#
			# if OOo or mswDoc are not working we remove the Cited key
			if not BIB.WP:
				self.Delete(self.keyCited)
				self.keyCited = None
			#
			self.__set_evt_tree()
			if self.grants != 'rk': self.__set_drag_tree()	# if 'rk' we can just see the keytree => no drop
		else:	# restricted read-only user => we create a keyAll,self.keyCited without writing it to the db
			self.keyroot = self.AddRoot(BIB.ROOT)
			self.keyAll = self.AppendItem(self.keyroot,BIB.TRANSLATE[BIB.ALL])
			self.SetPyData(self.keyAll,(None,self.user,BIB.ID_REF_ALL))
			if BIB.WP:
				self.keyCited = self.AppendItem(self.keyroot,BIB.TRANSLATE[BIB.CITED])
				self.SetPyData(self.keyCited,(None,self.user,BIB.ID_CITED))
		# if we don't have the necessary grants, we delete some keys
		if self.grants == 'ro':
			self.Delete(self.keyImport)
			self.Delete(self.keyOnline)
			self.keyImport,self.keyOnline = None,None
		elif self.grants == 'rk':
			if self.keyImport: self.Delete(self.keyImport)
			if self.keyOnline: self.Delete(self.keyOnline)
			if self.keyTagged: self.Delete(self.keyTagged)
			self.keyImport,self.keyOnline,self.keyTagged = None,None,None
		# we select the "All" key because it always exists
		self.SelectItem(self.keyAll)

	def __addChild(self,event,parent_item):
		parent_id,user,id = self.GetPyData(parent_item)
		key_name = BIB.NEWKEY
		written = False
		while not written:
			if not self.bibus_frame.db.keyExist(self.user,parent_id,key_name):
				self.bibus_frame.db.writeKey(self.user,(parent_id,key_name))
				written = True
			else:
				if key_name == BIB.NEWKEY:
					key_name = key_name + BIB.SEP_DUP + u'1'
				else:
					keyleft,keyright = key_name.split(BIB.SEP_DUP)
					key_name = keyleft + BIB.SEP_DUP + `int(keyright) + 1`
		newitem = self.AppendItem(parent_item,key_name)
		newID = self.bibus_frame.db.get_insert_id()
		self.SetPyData(newitem,(newID,user,BIB.ID_CHILDREN[id]))
		return newID

	def onMenuAddChild(self,event):
		"""Add a child to the selected key"""
		parent = self.GetSelection()
		if self.allowed[self.GetPyData(parent)[2]] & BIB.CHILD_OK:
			newID = self.__addChild(event,parent)
			self.Expand(parent)
			newitem,cookie = self.GetFirstChild(parent)
			while newitem.IsOk():
				if self.GetPyData(newitem)[0] == newID:
					self.EditLabel(newitem)		# we select it for editing
					self.EnsureVisible(newitem)
					return
				newitem,cookie = self.GetNextChild(parent,cookie)

	def onKeyRightClick(self,event):
		#print "right click"
		where = event.GetPoint()	# position relative to the window
		pw = self.bibus_frame.reflist.GetPosition()
		where.x = where.x + pw.x
		where.y = where.y + pw.y
		#
		self.SelectItem(event.GetItem())	# select the item
		key_id,user,id = self.GetPyData(event.GetItem())
		popUp = wx.Menu()
		ap=wx.ArtProvider()
		if self.allowed[id] & BIB.RENAME_OK:
			popUp.Append(self.popRename,_('Rename this key'))
		if self.allowed[id] & BIB.CHILD_OK:
			item =wx.MenuItem(popUp,id=self.popAddChild,text=_('Add child'))
			item.SetBitmap(ap.GetBitmap(wx.ART_ADD_BOOKMARK,client=wx.ART_MENU))
			popUp.AppendItem(item)
			#popUp.Append(self.popAddChild,_('Add child'))
		if self.allowed[id] & BIB.DELETE_OK:
			item =wx.MenuItem(popUp,id=self.popDeleteKey,text=_('Delete this key'))
			item.SetBitmap(ap.GetBitmap(wx.ART_DEL_BOOKMARK,client=wx.ART_MENU))
			popUp.AppendItem(item)
			#popUp.Append(self.popDeleteKey,_('Delete this key'))
		if self.allowed[id] & BIB.REF_NEW_OK:
			if popUp.GetMenuItemCount(): popUp.AppendSeparator()
			item =wx.MenuItem(popUp,id=self.popNewRef,text=_('New reference'))
			item.SetBitmap(ap.GetBitmap(wx.ART_NEW,client=wx.ART_MENU))
			popUp.AppendItem(item)
			#popUp.Append(self.popNewRef,_('New reference'))
		if id == BIB.ID_QUERY_ROOT and self.grants == 'rw':
			item =wx.MenuItem(popUp,id=self.popNewQuery,text=_('New query'))
			item.SetBitmap(ap.GetBitmap(wx.ART_NEW,client=wx.ART_MENU))
			popUp.AppendItem(item)
			#popUp.Append(self.popNewQuery,_('New query'))
		if id == BIB.ID_QUERY and self.grants == 'rw':	
			popUp.Append(self.popRenameQuery,_('Rename query'))
			item =wx.MenuItem(popUp,id=self.popEditQuery,text=_('Edit query'))
			item.SetBitmap(ap.GetBitmap(wx.ART_EXECUTABLE_FILE,client=wx.ART_MENU))
			popUp.AppendItem(item)			
			#popUp.Append(self.popEditQuery,_('Edit query'))
			item =wx.MenuItem(popUp,id=self.popDeleteQuery,text=_('Delete query'))
			item.SetBitmap(ap.GetBitmap(wx.ART_DEL_BOOKMARK,client=wx.ART_MENU))
			popUp.AppendItem(item)
			#popUp.Append(self.popDeleteQuery,_('Delete query'))
		if popUp.GetMenuItemCount() > 0:
			self.PopupMenu(popUp,where)

# menu popUp functions
	def onPopRenameKey(self,event):
		item = self.GetSelection()
		ret = None
		oldName = self.GetItemText(self.GetSelection())
		dlg = wx.TextEntryDialog(self,_('Please, enter the new name'),caption=_('Rename folder'),defaultValue=oldName)
		try:
			ret = dlg.ShowModal()
		finally:
			dlg.Destroy()
		newName = dlg.GetValue()
		if ret == wx.ID_OK and newName != oldName:
			ret = self.__changeKeyName(oldName,newName,item)
			if ret:
				#self.__KeyExpand(self.GetItemParent(item))
				self.SetItemText(item,newName)

	def onPopDeleteKey(self,event):
		item = self.GetSelection()
		parent = self.GetItemParent(item)
		key_id = self.GetPyData(self.GetSelection())[0]
		self.bibus_frame.db.deleteKey(self.user,key_id)
		self.__KeyExpand(parent)	# update the view
		self.SelectItem(parent)

	def onPopNewQuery(self,event):
		dlg = wx.TextEntryDialog(self,_('Please, enter the query name'),caption=_('New Query'),defaultValue=u'')
		try:
			ret = dlg.ShowModal()
		finally:
			dlg.Destroy()
		newName = dlg.GetValue()
		if newName != u'':
			search = Search.Query(self.user,newName,self.bibus_frame,self.bibus_frame,-1,u'')
			try:
				search.ShowModal()
			finally:
				search.Destroy()
		self.__queryRootExpand()
		self.Expand(self.keyQueryRoot)

	def onPopRenameQuery(self,event):
		query_id = self.GetPyData(self.GetSelection())[0]
		oldName = self.bibus_frame.db.getQueryName(query_id)
		dlg = wx.TextEntryDialog(self,_('Please, enter the new query name'),caption=_('Rename Query'),defaultValue=oldName)
		try:
			ret = dlg.ShowModal()
		finally:
			dlg.Destroy()
		newName = dlg.GetValue()
		if newName != u'':
			self.bibus_frame.db.renameQuery(query_id,newName)
			self.__queryRootExpand()

	def onPopEditQuery(self,event):
		query_id = self.GetPyData(self.GetSelection())[0]
		name = self.bibus_frame.db.getQueryName(query_id)
		oldQuery = self.bibus_frame.db.getQuery(query_id)
		search = Search.EditQuery(self.user,name,oldQuery,query_id,self.bibus_frame,self.bibus_frame,-1,u'')
		try:
			search.ShowModal()
		finally:
			search.Destroy()
		self.__queryRootExpand()

	def onPopDeleteQuery(self,event):
		query_id = self.GetPyData(self.GetSelection())[0]
		self.bibus_frame.db.deleteQuery(query_id)
		self.__queryRootExpand()			# update the view
		self.SelectItem(self.keyQueryRoot)	# select parent = QueryRoot

	def SelectUserTree(self):
		"""Do nothing. Needed in Multi_KeyTree"""
		return

	def showError(self,message=u''):
		wx.LogError(message)
