Re: [Deskbar] Man I'm gonna regret this :)

On Thu, 2005-10-20 at 15:44 +0200, Mikkel Kamstrup Erlandsen wrote:
> Just so you have the "latest and greatest". I still have uri-launching
> problems and occasional segfaults.

Attached a version that fixes IM viewing using beagle tool, and fixes
URI launching.

All in all, if it wasn't a hack, i'd really love it :)
This proof of concept could be added as an experimental file, just like
debug ones, not installed but shipped as EXTRA_DIST..

I'm seeing if i can make a proper libbeagle binding..
import os, sys, signal
import popen2
from os.path import expanduser, exists, join
from gettext import gettext as _

import gnome, gnome.ui
import gtk
from gobject import spawn_async, SPAWN_SEARCH_PATH

icon_theme = gtk.icon_theme_get_default()
thumb_factory = gnome.ui.ThumbnailFactory(16)

import deskbar
from deskbar.handler import AsyncHandler, Match
import deskbar.defs

MAX_HITS=2 # Max hits *per backend*

def _check_requirements():
	return (True, None)

	"BeagleLiveHandler" : {
		"name": _("Live Beagle Queries"),
		"description": _("Query Beagle automatically as you type."),
		"requirements" : _check_requirements

class UnknownBeagleTask (Exception):
class NoBeagleDaemon (Exception):

class BeagleProcess (popen2.Popen3):
	# Mental note to self: The beagle-query --max-hits switch sets 
	#max number of hits *per backend*.

	# Task definitions
	QUERY = 1
	def __init__ (self, task, query=None, max_hits=MAX_HITS):
		if task is self.GET_DAEMON_VERSION:
			popen2.Popen3.__init__(self, "beagle-info --daemon-version",)
		elif task is self.QUERY and query is not None:
			print "Querying for", query
						"beagle-query %s --verbose --max-hits %s" % (query, MAX_HITS))
			raise UnknownBeagleTask()

# The TYPES dict contains Beagle HitTypes as keys with
# templates for the valid fields.
# A Hit Type consists of:
#	"name"		: Template used to find a user-displayable name
#	"action"	: Command to execute
#	"icon"		: The *name* of the icon to be sued for this type. Set to None for no icon.
#	"description"	: A short description. %(*)s may refer to *=name,uri,action or any field in "extra" (see below)
# Optionally:
#	"extra"	: A dict containing key:template pairs to search for. You can use %(key)s in "description".

	"Contact"	: {
			"name"	: "fixme:FileAs = ",
			"action": "evolution",
			"icon"	: "stock_contact",
			"description": "Contact: %(name)s"
	"MailMessage" 	: {
			"name"	:"dc:title = ",
			"action": "evolution",
			"icon"	: "stock_mail",
			"extra": {"sender":"from_name = "},
			"description": "Email from %(sender)s: %(name)s"
	"File" 		: {
			"name"	: "beagle:ExactFilename = ", 
			"action": "gnome-open",
			"icon"	: None,
			"description": "%(name)s"
	"FeedItem"	: {
			"name"	:"dc:title = ",
			"action": "gnome-open",
			"icon"	: "stock_news",
			"description": "News: %(name)s",# There don't seem to be a good "sender" template :(
	"Note"		: {
			"name"	: "dc:title = ",
			"action": "tomboy",
			"action_args": "--open-note",
			"icon"	:"stock_notes",
			"description": "Note: %(name)s"
	"IMLog"		: {
			"name"	: "fixme:speakingto = ",
			"action": "beagle-imlogviewer",
			"icon"	: "im",
			"description": "Conversation with %(name)s"

class BeagleMatch (Match):

	def __init__(self, handler, result):
		result: a dict containing:
			"name" : a name sensible to display for this match
			"uri": the uri of the match as provided by the beagled 'Uri: '-field
			"type": One of the types listed in the TYPES dict

		-- and optionally extra fields as provided by the corresponding entry in TYPES.
		Fx. "MailMessage". has an extra "sender" entry.
		if result["type"] == "File":
				icon_name, num = gnome.ui.icon_lookup(icon_theme, thumb_factory,  file_uri=result["uri"], custom_icon="")
				icon = icon_theme.load_icon(icon_name, deskbar.ICON_SIZE, gtk.ICON_LOOKUP_USE_BUILTIN)
				if icon:
					Match.__init__ (self, handler, result["name"], icon)
					raise Exception()
			except Exception:
				Match.__init__ (self, handler, result["name"], handler.ICONS["File"])
				print >> sys.stderr, "BeagleLive: Failed to load icon for file %s" % result["uri"]

			# We are not a file. Just use an icon from the ICON table
			Match.__init__ (self, handler, result["name"], handler.ICONS[result["type"]])
		self.__result = result
	def get_name (self, text=None):
		# We use the result dict itself to look up words
		return self.__result
	def get_verb(self):
		# Fetch the "description" template from TYPES
		return TYPES[self.__result["type"]]["description"]
	def action(self, text=None):
		# This looks like it should work, but somehow doesn't.
		# Retrieve the associated action
		action = TYPES[self.__result["type"]]["action"]
		args = [action]

		# If the result requires additional arguments
		# prepend them to args.
		if TYPES[self.__result["type"]].has_key ("action_args"):
		# Gross hack, imlogviewer doesn't take URI as args, but a filename
		if action == "beagle-imlogviewer":
			# Strip the file://
			args.append (self.__result["uri"][7:])
			args.append (self.__result["uri"])

		print "BeagleLive spawning:", action, args
		os.spawnvp(os.P_NOWAIT, action, args)
class BeagleLiveHandler (AsyncHandler):
	def __init__ (self):
		AsyncHandler.__init__ (self)
		self.__process = None
	def initialize (self):
		Loads needed icons into memory, also
		try to connect to the beagle daemon an get its' version number.
		This method raises a NoBeagleDaemon exception if the beagle daemon
		is not running.
		# Load icons here. This method is called from the main loop, so it's ok.
		self.ICONS = self.__load_icons()
		# Check if beagled is running and get the version
		process = BeagleProcess (BeagleProcess.GET_DAEMON_VERSION)
		self.__daemon_version =  process.fromchild.readline()
		idx = self.__daemon_version.find(":")
		if idx == -1:
			print >> sys.stderr, "BeagleLive: Unable to connect to Beagle daemon!"
			raise NoBeagleDaemon()
		self.__daemon_version = self.__daemon_version[idx+2:]
		print "BeagleLive: Using Beagle daemon version %s"% self.__daemon_version
	def query (self, qstring, qmax=5):
		# Delay query
		self.check_query_changed (timeout=QUERY_DELAY)
		print "BeagleLive: Querying for %s" % qstring
		self.__process = BeagleProcess (BeagleProcess.QUERY, qstring, qmax)
		self.parse (self.__process.fromchild)
		self.__process = None
		print "BeagleLive: query for '%s' completed" % qstring
	def __clean_up (self):
		# If we have a running beagle query, send it a SIGINT
		if self.__process:
			os.kill (, signal.SIGINT)
			self.__process = None
			print "BeagleLive: Beagle query interupted."

	def parse (self, pipe):
		All the work is being done here. We basically walk line by 
		line through the beagle-query output and match each line against
		known keywords.
		result = {}
		res_type = None # Since the "type" will be really handy, keep a direct ref to it
		res_extra = None # If the result being parsed need "extra" information parsed.
		line = "a"
		while True:
			line = pipe.readline()
			if line == "": 

			line = line.strip()
			if line.startswith("Uri: ") or line == "":
				# A new hit is starting or we've hit EOF
				# Do we have a qualified match?
				if (	result.has_key("name") and 
					result.has_key("uri") and 
					# We have all we need. 
					# Emit "query-ready" to main loop
					# if the query is still valid
					match = BeagleMatch(self, result)
					self.emit_query_ready ([match])
				# Reset our result
				result = {}
				res_type = None
				res_extra = None

			if not result.has_key("uri"):
				if line.startswith ("Uri: "):
					result ["uri"] = line[5:]
			if not result.has_key("type"):
				# Lots of stuff require us to have the 'Type: '-field
				# at hand. Do all this stuff here, so we don't have to check
				# for has_key("type") all over the place.
				if line.startswith ("Type: "):
					result["type"] = line[6:]
					res_type = result["type"] # This is for handy reference
					if TYPES[res_type].has_key ("extra"):
						res_extra = TYPES[res_type]["extra"]
				# Lots of stuff require us to have the 'Type: '-field
				# at hand. Do all this stuff here, so we don't have to check
				# for has_key("type") all over the place.
				if not result.has_key("name"):
					# Try and look up a name pattern by our type
					# Since there's no common field for a display_String
					# or the like we have to heuristically determine one.
					# Look up a name pattern from NAME_PATTERNS
					idx = line.find (TYPES[res_type]["name"])
					if idx != -1:
						result["name"] = line[idx+len(TYPES[res_type]["name"]):]

				if res_extra:
					# This hit type requires us to scan for extra info
					for extra in res_extra.iterkeys():
						if not result.has_key (extra):
							idx = line.find (res_extra[extra])
							if idx != -1:
								result[extra] = line[idx+len(res_extra[extra]):]

	def __load_icons (self):
		res = {}
		for t in TYPES.iterkeys():
			icon_file = TYPES[t]["icon"]
			if not icon_file: continue
				res[t] = icon_theme.load_icon(icon_file, deskbar.ICON_SIZE, gtk.ICON_LOOKUP_USE_BUILTIN)
			except Exception:
				res[t] = None
				print >> sys.stderr, "BeagleLive: Unable to load icon for %s" % t
		return res

Attachment: signature.asc
Description: This is a digitally signed message part

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]