#	Programmer:	Daniel Pozmanter
#	E-mail:		drpython@bluebottle.com
#	Note:		You must reply to the verification e-mail to get through.
#
#	Copyright 2003-2005 Daniel Pozmanter
#
#	Distributed under the terms of the GPL (GNU Public License)
#
#    DrPython 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.
#
#    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

#	Icons taken from "Noia Kde 100" by Carles Carbonell Bernado from the KDE-LOOK site (some edited a bit).
#	An excellent artist.

#Source Browser

import wx
import drScrolledMessageDialog
from drProperty import *
import wx.stc
import re

recolour = re.compile('#\w+')

def GetCount(line, compchar):
	l = len(line)
	x = 0
	y = 0
	while(x < l):
		if (line[x] == compchar):
			y = y + 1
		elif (not line[x].isspace()):
			x = l
		x = x + 1
	return y

class drTree(wx.TreeCtrl):
	def __init__(self, parent, id, point, size, style, ancestor):
		wx.TreeCtrl.__init__(self, parent, id, point, size, style)

		self.grandparent = ancestor
		self.parent = parent

		style =	self.grandparent.prefs.sourcebrowserstyle
		yarrr = convertStyleStringToWXFontArray(style)

		imagesize = (16,16)

		self.imagelist = wx.ImageList(imagesize[0], imagesize[1])
		self.images = [wx.BitmapFromImage(wx.Image(self.grandparent.bitmapdirectory + "/16/class.png", wx.BITMAP_TYPE_PNG)),
		wx.BitmapFromImage(wx.Image(self.grandparent.bitmapdirectory + "/16/def.png", wx.BITMAP_TYPE_PNG)),
		wx.BitmapFromImage(wx.Image(self.grandparent.bitmapdirectory + "/16/import.png", wx.BITMAP_TYPE_PNG)),
		wx.BitmapFromImage(wx.Image(self.grandparent.bitmapdirectory + "/16/transparent.png", wx.BITMAP_TYPE_PNG))]

		map(self.imagelist.Add, self.images)

		self.AssignImageList(self.imagelist)

		w = wx.Font(yarrr[1], wx.NORMAL, wx.NORMAL, wx.NORMAL, yarrr[2])

		w.SetFaceName(yarrr[0])

		if (yarrr[3]):
			w.SetWeight(wx.BOLD)
		else:
			w.SetWeight(wx.NORMAL)
		if (yarrr[4]):
			w.SetStyle(wx.ITALIC)
		else:
			w.SetStyle(wx.NORMAL)

		self.SetFont(w)

		f = convertColorPropertyToColorArray(getStyleProperty("fore", style))
		b = convertColorPropertyToColorArray(getStyleProperty("back", style))

		self.TextColor = wx.Colour(f[0], f[1], f[2])

		self.SetForegroundColour(self.TextColor)

		self.SetBackgroundColour(wx.Colour(b[0], b[1], b[2]))

		self.Bind(wx.EVT_TREE_ITEM_ACTIVATED,  self.OnItemActivated, id=id)
		self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK,  self.OnItemActivated, id=id)

	def OnCompareItems(self, item1, item2):
		#Overriding Base, Return -1 for <, 0 for ==, +1 for >
		t1 = self.GetItemText(item1).lower()
		t2 = self.GetItemText(item2).lower()

		x = 0
		l = len(t1)
		if (l > len(t2)):
			l = len(t2)
		while(x < l):
			if (t1[x] < t2[x]):
				return -1
			elif (t1[x] > t2[x]):
				return 1
			x = x + 1

		if (l == len(t2)):
			return -1

		return 0

	def OnItemActivated(self, event):
		sel = self.GetSelection()
		if (not sel.IsOk()):
			return
		t = self.GetItemText(sel)
		lparen = t.find('(')
		if lparen > -1:
			length = len(t[:lparen])
		else:
			length = len(t)
		try:
			i = self.parent.ItemsIndex.index(sel)
			pos = self.parent.ItemsPos[i]
			line = self.grandparent.txtDocument.LineFromPosition(pos)
			if (self.grandparent.prefs.docfolding):
				#Make sure the line is visible.
				self.grandparent.txtDocument.EnsureVisible(line)
			self.grandparent.txtDocument.ScrollToLine(line)
			self.grandparent.txtDocument.GotoLine(line)
			self.grandparent.txtDocument.GotoPos(pos)
			self.grandparent.Raise()
			self.grandparent.SetFocus()
			if (self.grandparent.prefs.sourcebrowsercloseonactivate):
				self.parent.OnbtnClose(event)
			else:
				self.grandparent.txtDocument.SetFocus()
		except:
			drScrolledMessageDialog.ShowMessage(self.parent, 'Error Activating Item', 'Source Browser Error')

class drSourceBrowserPanel(wx.Panel):
	def __init__(self, parent, id, Position, Index):
		wx.Panel.__init__(self, parent, id)

		self.theSizer = wx.BoxSizer(wx.VERTICAL)

		self.mixed = 0
		
		self.renext = re.compile(r'^[ \t]*[^#^\s]', re.M)

		self.reinspect = re.compile(r'(^[ \t]*?class\s.*[(:])|(^[ \t]*?def\s.*[(:])|(^[ \t]*?import\s.*$)|(^[ \t]*?from\s.*$)|(^\s*#---.+)', re.MULTILINE)
		
		self.panelparent = parent.GetGrandParent().GetParent()

		self.parent = parent.GetGrandParent().GetGrandParent()

		self.classtree = drTree(self, -1, wx.Point(0, 0), wx.Size(400, 200), wx.TR_DEFAULT_STYLE|wx.TR_HIDE_ROOT, self.parent)

		self.btnClose = wx.Button(self, 101, "&Close")
		self.btnRefresh = wx.Button(self, 102, "&Refresh")

		self.theSizer.Add(self.classtree, 9, wx.EXPAND)
		self.bSizer = wx.BoxSizer(wx.HORIZONTAL)
		self.bSizer.Add(self.btnRefresh, 0,  wx.SHAPED | wx.ALIGN_LEFT)
		self.bSizer.Add(self.btnClose, 0,  wx.SHAPED | wx.ALIGN_RIGHT)

		self.theSizer.Add(self.bSizer, 0, wx.EXPAND)

		self.Position = Position
		self.Index = Index

		self.Bind(wx.EVT_BUTTON, self.OnbtnClose, id=101)
		self.Bind(wx.EVT_BUTTON, self.OnbtnRefresh, id=102)
		self.parent.PBind(self.parent.EVT_DRPY_DOCUMENT_CHANGED, self.OnbtnRefresh, None)

		self.eol = self.parent.txtDocument.GetEndOfLineCharacter()

		if not self.Browse():
			self.mixed = 1
			msg = 'This document is mixed.  It uses tabs and spaces for indentation.\nDrPython may not be able to correctly display the class browser.  Please use "Edit:Whitespace:Clean Up Indentation" to fix this.'
			drScrolledMessageDialog.ShowMessage(self, msg, "Check Indentation Results")

		self.SetAutoLayout(True)
		self.SetSizer(self.theSizer)

	def Browse(self):
		#Submitted Patch:  Christian Daven
		self.classtree.Freeze()
		#/Submitted Patch:  Christian Daven

		self.classtree.DeleteAllItems()

		self.root = self.classtree.AddRoot("")

		self.ItemsIndex = []

		self.ItemsPos = []

		labelQueue = []

		self.targetText = self.parent.txtDocument.GetText()
		
		if self.mixed:
			return 1
		RootArray = [self.root]
		Roots = [self.root]
		currentRoot = 0
		Indents = [0]
		currentIndent = 0
		eol = self.parent.txtDocument.GetEndOfLineCharacter()

		#What is this document using?
		result = self.parent.txtDocument.CheckIndentation()
		wasnotmixed = 1
		if result == 0:
			wasnotmixed = 0
			if self.parent.prefs.docusetabs[self.parent.txtDocument.filetype]:
				result = 1
			else:
				result = -1
		if (result == 1):
			compchar = '\t'
			dec = 1
		else:
			compchar = ' '
			dec = self.parent.prefs.doctabwidth[0]

		#Handle Triple Quoted Strings:
		self.targetText = self.RemoveTripleQuotedString(self.targetText)

		matcher = self.reinspect.finditer(self.targetText)

		#Get On With It!
		try:
			match = matcher.next()
		except:
			match = None
		while (match is not None):
			matchedtext = match.group().strip()
			if matchedtext[0] == '#':
				nextmatch = self.renext.search(self.targetText[match.end():])
				
				indent = 0
				
				if nextmatch is not None:
					indent = GetCount(nextmatch.group(), compchar)
				
				cR = currentRoot
				cI = currentIndent

				while (indent < Indents[cI]):
						cR = cR - 1
						cI = cI - 1

				i = matchedtext[4:].find('---')
				if i > -1:
					a = matchedtext[4:i+4]
				else:
					a = matchedtext[4:]

				m = match.group().find('#')

				currentitem = self.classtree.AppendItem(Roots[cI], a)
				self.ItemsIndex.append(currentitem)
				self.ItemsPos.append(match.start() + m)

				colours = recolour.findall(matchedtext)

				if len(colours) > 1:
					try:
						self.classtree.SetItemTextColour(currentitem, convertColorPropertyToColorArray(colours[0]))
						self.classtree.SetItemBackgroundColour(currentitem, convertColorPropertyToColorArray(colours[1]))
					except Exception, e:
						print 'Error Setting Label Colour:', e
				self.classtree.SetItemImage(currentitem, 3, wx.TreeItemIcon_Normal)
				self.classtree.SetItemImage(currentitem, 3, wx.TreeItemIcon_Expanded)
			else:
				indent = GetCount(match.group(), compchar)
				
				while (indent < Indents[currentIndent]):
						Roots.pop()
						currentRoot = currentRoot - 1
						Indents.pop()
						currentIndent = currentIndent - 1
						
				Indents.append(indent + dec)
				currentIndent = currentIndent + 1
				currentitem = self.classtree.AppendItem(Roots[currentRoot], matchedtext)
				Roots.append(currentitem)
				#Submitted bugfix, Franz Steinhausler
				self.classtree.SetPyData(Roots[-1], None)
				currentRoot = currentRoot + 1
				RootArray.append(Roots[currentRoot])
				self.ItemsIndex.append(Roots[currentRoot])
				self.ItemsPos.append(match.start())
				if (matchedtext[0] == 'c'):
					try:
						fg, bg = convertStyleToColorArray(self.parent.prefs.PythonStyleDictionary[5])
						self.classtree.SetItemTextColour(Roots[currentRoot], fg)
						self.classtree.SetItemBackgroundColour(Roots[currentRoot], bg)
					except Exception, e:
						print 'Error Setting Class Colour:', e
					self.classtree.SetItemImage(Roots[currentRoot], 0, wx.TreeItemIcon_Normal)
					self.classtree.SetItemImage(Roots[currentRoot], 0, wx.TreeItemIcon_Expanded)
				elif (matchedtext[0] == 'd'):
					try:
						fg, bg = convertStyleToColorArray(self.parent.prefs.PythonStyleDictionary[8])
						self.classtree.SetItemTextColour(Roots[currentRoot], fg)
						self.classtree.SetItemBackgroundColour(Roots[currentRoot], bg)
					except Exception, e:
						print 'Error Setting Def Colour:', e
					self.classtree.SetItemImage(Roots[currentRoot], 1, wx.TreeItemIcon_Normal)
					self.classtree.SetItemImage(Roots[currentRoot], 1, wx.TreeItemIcon_Expanded)
				else:
					self.classtree.SetItemImage(Roots[currentRoot], 2, wx.TreeItemIcon_Normal)
					self.classtree.SetItemImage(Roots[currentRoot], 2, wx.TreeItemIcon_Expanded)
			try:
				match = matcher.next()
			except:
				match = None

		if (self.parent.prefs.sourcebrowserissorted):
			self.classtree.SortChildren(self.root)
			x = 0
			l = len(RootArray)
			while (x < l):
				self.classtree.SortChildren(RootArray[x])
				x = x + 1

		#Submitted Patch:  Christian Daven
		self.classtree.Thaw()
		#/Submitted Patch:  Christian Daven

		return wasnotmixed

	def OnbtnClose(self, event):
		self.parent.PUnbind(self.parent.EVT_DRPY_DOCUMENT_CHANGED, self.OnbtnRefresh)
		self.parent.SourceBrowser = None
		self.panelparent.ClosePanel(self.Position, self.Index)

	def OnbtnRefresh(self, event):
		self.mixed = 0
		if not self.Browse():
			self.mixed = 1
			msg = 'This document is mixed.  It uses tabs and spaces for indentation.\nDrPython may not be able to correctly display the class browser.  Please use "Edit:Whitespace:Clean Up Indentation" to fix this.'
			drScrolledMessageDialog.ShowMessage(self, msg, "Check Indentation Results")
		if event is not None:
			event.Skip()

	def RemoveTripleQuotedString(self, text):
		text = self.removeStringTripleQuotedWith(text, "'''")
		text = self.removeStringTripleQuotedWith(text, '"""')
		return text

	def removeStringTripleQuotedWith(self, text, target):
		start = text.find(target)
		while start > -1:
			end = text[start+3:].find(target)
			if end == -1:
				text = text[:start]
			else:
				end = start + 3 + end
				text = text[:start] + "".zfill((end - start) + 3) + text[end+3:]
			start = text.find(target)
		return text


syntax highlighted by Code2HTML, v. 0.9.1