#!BPY

"""
Name: 'BPy Doc Browser'
Blender: 232
Group: 'Misc'
Tip: 'Browse BPython (scripting API) modules doc strings.'
"""

# $Id: doc_browser.py,v 1.1 2004/07/03 05:17:00 ianwill Exp $
#
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) 2004:  Daniel Dunbar, ddunbar _at_ diads.com
#
# 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; 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.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------

#####
# Blender help browser
# By Daniel Dunbar, 
#
# This should function as a self-explanatory (interface, not code)
# (mostly) help browser. The code is wacky and nasty and or fun,
# but mainly bad, just cause it works doesn't mean its readable! 
# 
# TEEHEE!
#
# The row_draw function could easily be made into a more generic
# and usefull table drawing function... 
#

import Blender
from types import ListType, IntType, FloatType, StringType, ModuleType
from Blender.Draw import *
from Blender.BGL import *

# Simple version check, since I use the
# buffer calls... DONT use this code,
# assume everyone has 1.73+, and force
# them to upgrade if they dont
try:
	a= BufList
	version= 172
except:
	version= 173
	
# I could have used the split from the string module,
# but some people might not have it
def split(str, on):
	out= [""]
	for s in str:
		if s in on: out.append("")
		else: out[-1]= out[-1]+s

	if out[-1]=="": del(out[-1])

	return out

last_sort= 1; direction= 1
def sort_browselist(type):
	global browselist
	global last_sort, direction

	if (type==last_sort): direction= -direction
	else: direction= 1

	last_sort= type

	if (direction==1):
		def byname(x, y): return cmp(x[0],y[0]);
		def bytype(x, y): return cmp(x[1],y[1])
		def bydata(x, y): return cmp(x[2],y[2])
	else:
		def byname(x, y): return cmp(y[0],x[0]);
		def bytype(x, y): return cmp(y[1],x[1])
		def bydata(x, y): return cmp(y[2],x[2])

	if (type==1): browselist.sort(byname)
	elif (type==2): browselist.sort(bytype)
	elif (type==3): browselist.sort(bydata)

selected= -1
def view_doc(num):
	global selected, selected_page

	if (selected==num): selected= -1
	else: selected= num

	selected_page= 0

function_filter= 0
def toggle_function_filter():
	global function_filter

	function_filter= not function_filter
	make_browselist()

def view_page(dir):
	global selected_page

	selected_page= selected_page + dir

browse_scrollstart= 0
def browse_module(num):
	global browsing, selected, browse_scrollstart
	
	if (num>=0): newstr= browsing.val + "." + browselist[num][0]
	else:
		modules= split(browsing.val, ".")
		newstr= ""
		for m in modules[:-1]:
			newstr= newstr+m
	try:
		browsing= Create(newstr)
		make_browselist()
	except:
		browsing= Create('Blender')
		make_browselist()
		
	browse_scrollstart= 0
	scrolling= 0
	selected= -1

def make_browselist():
	global browselist

	browselist= []

	module= eval(browsing.val)
	items= dir(module)

	for item_name in items:
		if (item_name[:2]=='__'): continue

		data= [item_name, 'None', '', '']
		item= eval(item_name,module.__dict__)
		t= type(item)

		if (t==IntType): data[1]= 'Int'; data[2]= `item`
		elif (t==FloatType): data[1]= 'Float'; data[2]= `item`
		elif (t==StringType): data[1]= 'String'
		elif (t==ModuleType): data[1]= 'Module'
		elif (callable(item)):
			data[1]= 'Function'
			doc= item.__doc__
			if (doc): data[3]= doc

		if (function_filter and data[1]!='Function'): continue

		browselist.append(data)

browsing= Create('Blender')
make_browselist()

BROWSE_EVT= 1

SORT_BYNAME= 2
SORT_BYTYPE= 3
SORT_BYDATA= 4

DOC_PAGE_UP= 5
DOC_PAGE_DOWN= 6

BACK_MODULE= 7
CLOSE_VIEW= 8
FILTER_DISPLAY= 9

SCROLLBAR= 10

VIEW_DOC= 100
BROWSE_MODULE= 20000

scr= Create(0)
browse_scrollstart= 0

winrect= [0.0, 0.0, 0.0, 0.0]
def draw():
	global browsing, winrect, scr, browse_scrollstart

	# Blender doesn't give us direct access to
	# the window size yet, but it does set the
	# GL scissor box for it, so we can get the 
	# size from that.

	if (version<173):
		size= Buffer(GL_FLOAT, None, 4)
		glGetFloat(GL_SCISSOR_BOX, size)
		size= BufList(size)
	else:
		size= Buffer(GL_FLOAT, 4)
		glGetFloatv(GL_SCISSOR_BOX, size)
		size= size.list

	winrect= size[:]

	size[0]= size[1]= 0.0
	
	# Shrink the size to make a nice frame
	# (also a good technique so you can be sure you are clipping things properly)
	size[0], size[1]= int(size[0]+10), int(size[1]+10)
	size[2], size[3]= int(size[2]-12), int(size[3]-10)

	glClearColor(0.6, 0.5, 0.3, 0.0)
	glClear(GL_COLOR_BUFFER_BIT)

	# The frame
	glColor3f(0.4, 0.5, 0.2)
	glRectf(size[0], size[1], size[2], size[3])

	# Window header	
	glColor3f(0.2, 0.2, 0.4)
	glRectf(size[0], size[3]-25, size[2], size[3])

	glColor3f(0.6, 0.6, 0.6)
	glRasterPos2f(size[0]+15, size[3]-17)
	Text("Zr's Help Browser")

	Button("Filter", FILTER_DISPLAY, size[2]-400, size[3]-22, 45, 18)
	Button("Back", BACK_MODULE, size[2]-300, size[3]-22, 45, 18)
	browsing= String("Browse: ", BROWSE_EVT, size[2]-250, size[3]-22, 245, 18, browsing.val, 30)

	# The real table
	def row_draw(rect, data, cols, cell_colors, text_colors):
		if (len(data)!=len(cols)):
			print "Must have same length data and columns"
			return

		if (type(cell_colors)!=ListType): cell_colors= [cell_colors]
		if (type(text_colors)!=ListType): text_colors= [text_colors]

		sx= rect[0]
		for i in range(len(data)):
			d= data[i]
			c= cols[i]
	
			c, align= c[0], c[1]

			if (type(c)==FloatType): c= c*(rect[2]-rect[0])
			ex= sx + c

			color= cell_colors[i%len(cell_colors)]
			apply(glColor3f, color)
			glRectf(sx, rect[1], ex, rect[3])

			color= text_colors[i%len(text_colors)]
			apply(glColor3f, color)

			if (type(d)==StringType):
				str_width= len(d)*8
				if (align=='left'): glRasterPos2f(sx+3, rect[1]+5)
				elif (align=='center'): glRasterPos2f((sx+ex)/2 - str_width/2 +3, rect[1]+5)
				elif (align=='right'): glRasterPos2f(ex - str_width -3, rect[1]+5)

				Text(d)
			else:
				d(map(int,[sx, rect[1], ex, rect[3]]))

			sx= ex
	# Some colors
	black= (0.0, 0.0, 0.0)
	white= (1.0, 1.0, 1.0)
	red= (0.8, 0.1, 0.1)

	gray0= (0.17, 0.17, 0.17)
	gray1= (0.25, 0.25, 0.25)
	gray2= (0.33, 0.33, 0.33)
	gray3= (0.41, 0.41, 0.41)
	gray4= (0.49, 0.49, 0.49)
	gray5= (0.57, 0.57, 0.57)
	gray6= (0.65, 0.65, 0.65)

	cols= [[.3, 'left'], [.2, 'left'], [.4, 'right'], [.1, 'center']]

	header= [size[0]+20, size[3]-60, size[2]-40, size[3]-40]

	def sort_byname(co): Button("Name",SORT_BYNAME, co[0]+3, co[1], co[2]-co[0]-4, 19)
	def sort_bytype(co): Button("Type",SORT_BYTYPE, co[0]+3, co[1], co[2]-co[0]-4, 19)
	def sort_bydata(co): Button("Data",SORT_BYDATA, co[0]+3, co[1], co[2]-co[0]-4, 19)

	row_draw(header, [sort_byname, sort_bytype, sort_bydata,'Link'], cols, [gray0, gray1], gray6)

	if (selected!=-1):
		table= [size[0]+20, size[1]+220, size[2]-40, size[3]-60]
	else:
		table= [size[0]+20, size[1]+20, size[2]-40, size[3]-60]

	row_height= 25
	items= (table[3]-table[1])/row_height

	items= 10
	if (items>len(browselist)): items= len(browselist)

	end= len(browselist)-items
	if (end>0):
		scr= Scrollbar(SCROLLBAR, table[2]+5, table[1], 20, table[3]-table[1], scr.val, 0.0, end, 0, "Page Up/Down scrolls list.")

	row= table
	row[1]= row[3]-row_height
	start= browse_scrollstart
	if (start+items>len(browselist)): items= len(browselist)-start
	for i in range(items):
		i= start+i
		data= browselist[i][:]

		if (i%2): colors= [gray1, gray2]
		else: colors= [gray2, gray3]

		# Strange pythonic code
		def view_doc(co,num=i):
			Button("Doc",VIEW_DOC+num, co[0]+3, co[1]+2, co[2]-co[0]-4, 19)

		def browse_module(co,num=i):
			Button("Browse",BROWSE_MODULE+num, co[0]+3, co[1]+2, co[2]-co[0]-4, 19)

		if (data[1]=='Function'):
			if data[3]:
				data[3]= view_doc
				tcolor= black
			else:
				tcolor= red
				data[2]= 'NO DOC STRING'
				data[3]= ''
		else:
			if (data[1]=='Module'): data[3]= browse_module
			else: data[3]= ''

			tcolor= black

		row_draw(row, data, cols, colors, tcolor)

		row[1]= row[1]-row_height
		row[3]= row[3]-row_height

	if (selected!=-1):
		table= [size[0]+20, size[1]+20, size[2]-40, size[1]+180]

		apply(glColor3f, gray5)
		glRectf(table[0], table[3], table[2], table[3]+20)
		apply(glColor3f, gray2)
		glRectf(table[0], table[1], table[2], table[3])

		apply(glColor3f, black)
		glRasterPos2f(table[0]+3, table[3]+5)
		Text("Function: " + browsing.val + "." + browselist[selected][0])

		Button("Close", CLOSE_VIEW, table[2]-50, table[3], 45, 18)

		row_height= 20
		view_lines= int((table[3]-table[1])/row_height)-1

		lines= split(browselist[selected][3], "\n")
		doc_lines= len(lines)

		sindex= view_lines*selected_page
		eindex= view_lines*(selected_page+1)
		if (sindex>0):
			sindex= sindex-1
			eindex= eindex-1

		lines= lines[sindex:eindex]

		y= table[3]-20
		for line in lines:
			glRasterPos2f(table[0]+3, y)
			Text(line)

			y= y-20

		if (sindex): Button("Page up", DOC_PAGE_UP, table[2]-100, table[3]-20, 90, 18)
		if (eindex<doc_lines): Button("Page down", DOC_PAGE_DOWN, table[2]-100, table[1]+5, 90, 18)

lmouse= [0, 0]
def event(evt, val):
	global browse_scrollstart

	if (evt==QKEY or evt==ESCKEY): Exit()
	elif (evt in [PAGEUPKEY, PAGEDOWNKEY] and val): 
		if (evt==PAGEUPKEY): browse_scrollstart= browse_scrollstart-5
		else: browse_scrollstart= browse_scrollstart+5
		
		if (browse_scrollstart<0): browse_scrollstart= 0
		elif (browse_scrollstart>=len(browselist)): browse_scrollstart= len(browselist)-1

		Redraw()

def bevent(evt):
	if (evt==BROWSE_EVT): make_browselist()

	elif (evt==SORT_BYNAME): sort_browselist(1)
	elif (evt==SORT_BYTYPE): sort_browselist(2)
	elif (evt==SORT_BYDATA): sort_browselist(3)
	
	elif (evt==DOC_PAGE_UP): view_page(-1)
	elif (evt==DOC_PAGE_DOWN): view_page(1)

	elif (evt==BACK_MODULE): browse_module(-1)
	elif (evt==CLOSE_VIEW): view_doc(-1)
	elif (evt==FILTER_DISPLAY): toggle_function_filter()

	elif (evt==SCROLLBAR):
		global browse_scrollstart
		browse_scrollstart= int(scr.val)

	elif (evt>=BROWSE_MODULE): browse_module(evt-BROWSE_MODULE)
	elif (evt>=VIEW_DOC): view_doc(evt-VIEW_DOC)	

	Redraw()

Register(draw, event, bevent)
