#!/usr/bin/python
# Copyright (C) 2007, Erich Schubert <erich@debian.org>
#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 

import gtk, gtk.glade, gobject
import os, sys, time
import threading, Queue
import dbusinspect
import dbus

class DBusInspector:
	def __init__(self):
		# Try to locate the glade file
		gladefile = "dbus-inspector.glade"
		tryfiles = [gladefile, os.path.join(os.path.dirname(sys.argv[0]),gladefile)]
		for file in tryfiles:
			if os.access(file, os.F_OK):
				gladefile = file
				break
		self.windowname = "windowDbus"
		self.wTree = gtk.glade.XML(gladefile, self.windowname)
		self.window = self.wTree.get_widget(self.windowname)

		dic = {
			"onMenuQuit" : self.shutdown,
			"onMenuRefresh" : self.refresh,
			"onDestroy"  : self.shutdown
		}
		self.wTree.signal_autoconnect(dic)

		# prepare tree views
		textrenderer = gtk.CellRendererText()
		imgrenderer = gtk.CellRendererPixbuf()
		column = gtk.TreeViewColumn("Path")
		column.pack_start(imgrenderer, False)
		column.set_attributes(imgrenderer, stock_id=2)
		column.pack_start(textrenderer, True)
		column.set_attributes(textrenderer, text=1)
		self.getElem("treeviewSystemBus").append_column(column)
		column = gtk.TreeViewColumn("Path")
		column.pack_start(imgrenderer, False)
		column.set_attributes(imgrenderer, stock_id=2)
		column.pack_start(textrenderer, True)
		column.set_attributes(textrenderer, text=1)
		self.getElem("treeviewSessionBus").append_column(column)
		# Details tree view
		column = gtk.TreeViewColumn("Name")
		column.set_expand(True)
		column.pack_start(imgrenderer, False)
		column.set_attributes(imgrenderer, stock_id=3)
		column.pack_start(textrenderer, True)
		column.set_attributes(textrenderer, text=1)
		self.getElem("treeviewDetails").append_column(column)
		column = gtk.TreeViewColumn("Type", textrenderer, text=2)
		self.getElem("treeviewDetails").append_column(column)
		# Signature tree view
		column = gtk.TreeViewColumn("Name")
		column.set_expand(True)
		column.pack_start(imgrenderer, False)
		column.set_attributes(imgrenderer, stock_id=4)
		column.pack_start(textrenderer, True)
		column.set_attributes(textrenderer, text=1)
		self.getElem("treeviewSignature").append_column(column)
		column = gtk.TreeViewColumn("Direction", textrenderer, text=2)
		self.getElem("treeviewSignature").append_column(column)
		column = gtk.TreeViewColumn("Type", textrenderer, text=3)
		self.getElem("treeviewSignature").append_column(column)
		# selection handling
		selection = self.getElem("treeviewSystemBus").get_selection()
		selection.connect("changed", self.updateDetails)
		selection = self.getElem("treeviewSessionBus").get_selection()
		selection.connect("changed", self.updateDetails)
		selection = self.getElem("treeviewDetails").get_selection()
		selection.connect("changed", self.detailsSelected)

		self._img = { }
		self._img["service"] = gtk.STOCK_DIRECTORY
		self._img["interface"] = gtk.STOCK_INFO
		self._img["method"] = gtk.STOCK_EXECUTE
		self._img["signal"] = gtk.STOCK_CONNECT
		self._img["property"] = gtk.STOCK_INDEX
		self._img["annotation"] = gtk.STOCK_INFO
		self._img["in"] = gtk.STOCK_GO_FORWARD
		self._img["out"] = gtk.STOCK_GO_BACK

	def getElem(self, name):
		return self.wTree.get_widget(name)

	def run(self):
		self.window.show()
		gobject.idle_add(self.refresh)
		gtk.main()

	def refresh(self, widget=None):
		sysresult = dbusinspect.discover(dbus.SystemBus())
		sesresult = dbusinspect.discover(dbus.SessionBus())
		self.doneDiscover(sysresult, sesresult)

	def doneDiscover(self, sysresult, sesresult):
		systreeW = self.getElem("treeviewSystemBus")
		sestreeW = self.getElem("treeviewSessionBus")
		self.fillModel(sysresult, systreeW)
		self.fillModel(sesresult, sestreeW)

	def fillModel(self, result, view):
		def recursiveFill(model, data, parent=None):
			iter = model.append(parent)
			model.set_value(iter, 0, data)
			model.set_value(iter, 1, data.label())
			if isinstance(data, dbusinspect.service):
				model.set_value(iter, 2, self._img["service"])
			elif isinstance(data, dbusinspect.interface):
				model.set_value(iter, 2, self._img["interface"])
			else:
				model.set_value(iter, 2, None)
			for c in data.children:
				if isinstance(c, dbusinspect.service) or \
				   isinstance(c, dbusinspect.interface):
					recursiveFill(model, c, parent=iter)

		model = gtk.TreeStore(
			gobject.TYPE_PYOBJECT,
			gobject.TYPE_STRING,
			gobject.TYPE_STRING)

		for service in result:
			recursiveFill(model, service)

		gobject.idle_add(view.set_model, model)

	def updateDetails(self, treeselection):
		(treestore, iter) = treeselection.get_selected()
		if treestore and iter:
			node = treestore.get(iter, 0)[0]
			if isinstance(node, dbusinspect.service) or \
			   isinstance(node, dbusinspect.interface):
				self.populateDetails(node)

	def populateDetails(self, node):
		detailstreeW = self.getElem("treeviewDetails")
		model = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING,
			gobject.TYPE_STRING, gobject.TYPE_STRING)

		for data in node.children:
			# exclude services and interfaces
			if isinstance(data, dbusinspect.service): continue
			if isinstance(data, dbusinspect.interface): continue
			iter = model.append()
			model.set_value(iter, 0, data)
			model.set_value(iter, 1, data.label())
			if isinstance(data, dbusinspect.method):
				model.set_value(iter, 2, "Method")
				model.set_value(iter, 3, self._img["method"])
			elif isinstance(data, dbusinspect.signal):
				model.set_value(iter, 2, "Signal")
				model.set_value(iter, 3, self._img["signal"])
			elif isinstance(data, dbusinspect.property):
				model.set_value(iter, 2, "Property")
				model.set_value(iter, 3, self._img["property"])
			elif isinstance(data, dbusinspect.annotation):
				model.set_value(iter, 2, "Annotation")
				model.set_value(iter, 3, self._img["annotation"])
			else:
				model.set_value(iter, 2, "???")
				model.set_value(iter, 3, None)

		gobject.idle_add(detailstreeW.set_model,model)

	def detailsSelected(self, treeselection):
		(treestore, iter) = treeselection.get_selected()
		if treestore and iter:
			node = treestore.get(iter, 0)[0]
			self.populateSignature(node)

	def populateSignature(self, node):
		signaturetreeW = self.getElem("treeviewSignature")
		model = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING,
			gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)

		for data in node.children:
			iter = model.append()
			model.set_value(iter, 0, data)
			model.set_value(iter, 1, data.label())
			model.set_value(iter, 3, data.type)
			if isinstance(data, dbusinspect.arg):
				if data.direction == "in":
					model.set_value(iter, 2, "in")
					model.set_value(iter, 4, self._img["in"])
				elif data.direction == "out":
					model.set_value(iter, 2, "out")
					model.set_value(iter, 4, self._img["out"])
				else:
					model.set_value(iter, 2, "???")
					model.set_value(iter, 4, None)
			else:
				model.set_value(iter, 2, "???")
				model.set_value(iter, 3, "???")
				model.set_value(iter, 4, None)

		gobject.idle_add(signaturetreeW.set_model,model)

	def shutdown(self, widget=None):
		gtk.main_quit()

gobject.threads_init()
dbus.glib.threads_init()
gtk.gdk.threads_init()
ui = DBusInspector()
ui.run()
