Re: [Deskbar] first attempt at a cuemiac/pearl merge



On Sat, 2006-01-07 at 12:53 +0100, Mikkel Kamstrup Erlandsen wrote:

> 
> Just to follow up on self... Here's a revised version of the pearl.py
> headers so you won't have to to rumage around with diffs to get a clear
> view of how far we/I are/am.

Quick fix:
remove Makefile and Makefile.in from patch
remove _() around "Actions"

Note: you must remove the pearl.py and categories.py from the previous
tarball (if you have it) 
patch will complain anyway if they are present.

apply with:
cd deskbar-applet
patch -p2 < pearl-0.1.patch

enjoy
The rest is coming later

Raf
diff -ruN ../deskbar-applet/deskbar/categories.py deskbar/categories.py
--- ../deskbar-applet/deskbar/categories.py	1970-01-01 01:00:00.000000000 +0100
+++ deskbar/categories.py	2006-01-07 03:46:06.000000000 +0100
@@ -0,0 +1,73 @@
+
+class UnknownCategory (Exception):
+	def __init__ (self, category_name, match):
+		print "** Unknown Category '%s' requested by %s" % (category_name, match.__class__)
+
+
+CATEGORIES = {
+	"Files"		: {	
+				"name": "Files",
+				"nest": "<b>%(count)s</b> <i>more files</i>", 
+				"threshold": 3
+			},
+	"Actions"	: {
+				"name": "Actions",
+				"nest": "<b>%(count)s</b> <i>more actions</i>", 
+				"threshold": 1
+			},
+	"News"		: {
+				"name": "News",
+				"nest": "<b>%(count)s</b> <i>more news items</i>", 
+				"threshold": 3
+			},
+	"Contacts"	: {
+				"name": "Contacts",
+				"nest": "<b>%(count)s</b> <i>more contacts</i>",
+				"threshold": 3
+			},
+	"Emails"	: {
+				"name": "Emails",
+				"nest": "<b>%(count)s</b> <i>more emails</i>", 
+				"threshold": 3
+			},
+	"Notes"	: {
+				"name": "Notes",
+				"nest": "<b>%(count)s</b> <i>more notes</i>", 
+				"threshold": 3
+			},
+	"Volumes"	: {
+				"name": "Volumes",
+				"nest": "<b>%(count)s</b> <i>more volumes</i>", 
+				"threshold": 3
+			},
+	"Google Search"	: {
+				"name": "Google Search",
+				"nest": "<b>%(count)s</b> <i>more online hits</i>", 
+				"threshold": 2
+			},
+	"Calendar"	: {
+				"name": "Calendar",
+				"nest": "<b>%(count)s</b> <i>more calendar items</i>", 
+				"threshold": 1
+			},
+	"Conversation"	: {
+				"name": "Conversation",
+				"nest": "<b>%(count)s</b> <i>more conversations</i>", 
+				"threshold": 1
+			},
+	"Web Browser" : {
+		"name":"Web Browser",
+		"nest":"<b>%(count)s</b> <i>more items</i>",
+		"threshold":5,
+	},
+	"Programs" : {
+		"name":"Programs",
+		"nest":"<b>%(count)s</b> <i>more programs</i>",
+		"threshold":3,
+	},
+	"Debug" : {
+		"name":"Debug",
+		"nest":"<b>%(count)s</b> <i>more debugging handlers</i>",
+		"threshold":2,
+	},
+}
diff -ruN ../deskbar-applet/deskbar/handlers/beagle-live.py deskbar/handlers/beagle-live.py
--- ../deskbar-applet/deskbar/handlers/beagle-live.py	2005-12-21 17:17:49.000000000 +0100
+++ deskbar/handlers/beagle-live.py	2006-01-07 03:44:12.000000000 +0100
@@ -122,6 +122,16 @@
 		
 		self.__result = result
 	
+	def get_category (self):
+		t = self.__result["type"]
+		if t == "MailMessage" : return "Emails"
+		elif t == "Contact": return "Contacts"
+		elif t == "File": return "Files"
+		elif t == "FeedItem": return "News"
+		elif t == "Note": return "Notes"
+		elif t == "IMLog": return "Conversation"
+		elif t == "Calendar": return "Calendar"
+	
 	def get_name (self, text=None):
 		# We use the result dict itself to look up words
 		return self.__result
Binary files ../deskbar-applet/deskbar/handlers/beagle-live.pyc and deskbar/handlers/beagle-live.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/beagle.py deskbar/handlers/beagle.py
--- ../deskbar-applet/deskbar/handlers/beagle.py	2005-12-26 18:37:06.000000000 +0100
+++ deskbar/handlers/beagle.py	2006-01-07 03:44:12.000000000 +0100
@@ -29,6 +29,9 @@
 	def action(self, text=None):
 		gobject.spawn_async(["best", '--no-tray', '--show-window', self._name], flags=gobject.SPAWN_SEARCH_PATH)
 	
+	def get_category(self):
+		return "Actions"
+	
 	def get_verb(self):
 		return _("Search for %s using Beagle") % "<b>%(name)s</b>"
 	
Binary files ../deskbar-applet/deskbar/handlers/beagle.pyc and deskbar/handlers/beagle.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/debug-async_handler.py deskbar/handlers/debug-async_handler.py
--- ../deskbar-applet/deskbar/handlers/debug-async_handler.py	2005-11-15 05:27:26.000000000 +0100
+++ deskbar/handlers/debug-async_handler.py	2006-01-07 03:44:12.000000000 +0100
@@ -22,6 +22,9 @@
 		
 	def action(self, text=None):
 		print str(self.__class__) + " : action triggered"
+		
+	def get_category (self):
+		return "Debug"
 
 class AsyncDebugHandler (AsyncHandler): 
 
Binary files ../deskbar-applet/deskbar/handlers/debug-async_handler.pyc and deskbar/handlers/debug-async_handler.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/debug-requirements.py deskbar/handlers/debug-requirements.py
--- ../deskbar-applet/deskbar/handlers/debug-requirements.py	2005-11-28 21:16:27.000000000 +0100
+++ deskbar/handlers/debug-requirements.py	2006-01-07 03:44:12.000000000 +0100
@@ -33,6 +33,9 @@
 		
 	def action(self, text=None):
 		pass
+		
+	def get_category (self):
+		return "Debug"
 
 class DebugRequirementsModule(deskbar.handler.Handler):
 	def __init__ (self):
Binary files ../deskbar-applet/deskbar/handlers/debug-requirements.pyc and deskbar/handlers/debug-requirements.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/debug-signal_handler.py deskbar/handlers/debug-signal_handler.py
--- ../deskbar-applet/deskbar/handlers/debug-signal_handler.py	2005-11-15 05:27:26.000000000 +0100
+++ deskbar/handlers/debug-signal_handler.py	2006-01-07 03:44:12.000000000 +0100
@@ -19,6 +19,9 @@
 		
 	def action(self, text=None):
 		print str(self.__class__) + " : action triggered"
+		
+	def get_category (self):
+		return "Debug"
 
 
 class SignallingDebugHandler(SignallingHandler):
Binary files ../deskbar-applet/deskbar/handlers/debug-signal_handler.pyc and deskbar/handlers/debug-signal_handler.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/email_address.py deskbar/handlers/email_address.py
--- ../deskbar-applet/deskbar/handlers/email_address.py	2005-11-15 05:27:27.000000000 +0100
+++ deskbar/handlers/email_address.py	2006-01-07 03:44:12.000000000 +0100
@@ -21,6 +21,9 @@
 		
 	def action(self, text=None):
 		gnomevfs.url_show("mailto:"+self._email)
+		
+	def get_category(self):
+		return "Emails"
 	
 	def get_verb(self):
 		return _("Send Email to %s") % "<b>%(name)s</b>"
Binary files ../deskbar-applet/deskbar/handlers/email_address.pyc and deskbar/handlers/email_address.pyc differ
Binary files ../deskbar-applet/deskbar/handlers/epiphany.pyc and deskbar/handlers/epiphany.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/evolution.py deskbar/handlers/evolution.py
--- ../deskbar-applet/deskbar/handlers/evolution.py	2006-01-02 01:57:54.000000000 +0100
+++ deskbar/handlers/evolution.py	2006-01-07 03:44:12.000000000 +0100
@@ -29,6 +29,9 @@
 		
 	def action(self, text=None):
 		gnomevfs.url_show("mailto:"+self._email)
+		
+	def get_category(self):
+		return "Contacts"
 	
 	def get_name(self, text=None):
 		return {
Binary files ../deskbar-applet/deskbar/handlers/evolution.pyc and deskbar/handlers/evolution.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/files.py deskbar/handlers/files.py
--- ../deskbar-applet/deskbar/handlers/files.py	2005-11-18 15:14:25.000000000 +0100
+++ deskbar/handlers/files.py	2006-01-07 03:44:12.000000000 +0100
@@ -27,6 +27,9 @@
 	def action(self, text=None):
 		gobject.spawn_async(["gnome-open", self._filename], flags=gobject.SPAWN_SEARCH_PATH)
 		
+	def get_category(self):
+		return "Files"
+		
 	def get_verb(self):
 		return _("Open %s") % "<b>%(name)s</b>"
 	
Binary files ../deskbar-applet/deskbar/handlers/files.pyc and deskbar/handlers/files.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/galago.py deskbar/handlers/galago.py
--- ../deskbar-applet/deskbar/handlers/galago.py	2006-01-02 01:57:54.000000000 +0100
+++ deskbar/handlers/galago.py	2006-01-07 03:44:12.000000000 +0100
@@ -21,6 +21,9 @@
 		
 	def action(self, text=None):
 		gnomevfs.url_show("mailto:"+self._email)
+		
+	def get_category(self):
+		return "Contacts"
 	
 	def get_verb(self):
 		return _("Send Email to %s") % "<b>%(name)s</b>"
Binary files ../deskbar-applet/deskbar/handlers/galago.pyc and deskbar/handlers/galago.pyc differ
Binary files ../deskbar-applet/deskbar/handlers/galeon.pyc and deskbar/handlers/galeon.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/google-live.py deskbar/handlers/google-live.py
--- ../deskbar-applet/deskbar/handlers/google-live.py	2005-11-28 21:16:27.000000000 +0100
+++ deskbar/handlers/google-live.py	2006-01-07 03:44:12.000000000 +0100
@@ -62,6 +62,9 @@
 		
 	def action(self, text=None):
 		gnomevfs.url_show(self.__url)
+		
+	def get_category(self):
+		return "Google Search"
 	
 	def get_hash(self, text=None):
 		return self.__url
Binary files ../deskbar-applet/deskbar/handlers/google-live.pyc and deskbar/handlers/google-live.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/gtkbookmarks.py deskbar/handlers/gtkbookmarks.py
--- ../deskbar-applet/deskbar/handlers/gtkbookmarks.py	2006-01-02 01:57:54.000000000 +0100
+++ deskbar/handlers/gtkbookmarks.py	2006-01-07 03:44:12.000000000 +0100
@@ -23,6 +23,9 @@
 		
 	def action(self, text=None):
 		gobject.spawn_async(["nautilus", self._path], flags=gobject.SPAWN_SEARCH_PATH)
+		
+	def get_category(self):
+		return "Files"
 	
 	def get_verb(self):
 		return _("Open location %s") % "<b>%(name)s</b>"
diff -ruN ../deskbar-applet/deskbar/handlers/pathprograms.py deskbar/handlers/pathprograms.py
--- ../deskbar-applet/deskbar/handlers/pathprograms.py	2006-01-02 01:57:54.000000000 +0100
+++ deskbar/handlers/pathprograms.py	2006-01-07 03:44:12.000000000 +0100
@@ -25,6 +25,9 @@
 	def action(self, text=None):
 		gobject.spawn_async(text.split(" "), flags=gobject.SPAWN_SEARCH_PATH)
 	
+	def get_category(self):
+		return "Programs"
+	
 	def get_verb(self):
 		return _("Execute %s") % "<b>%(text)s</b>"
 
Binary files ../deskbar-applet/deskbar/handlers/pathprograms.pyc and deskbar/handlers/pathprograms.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/programs.py deskbar/handlers/programs.py
--- ../deskbar-applet/deskbar/handlers/programs.py	2006-01-02 01:57:54.000000000 +0100
+++ deskbar/handlers/programs.py	2006-01-07 03:44:12.000000000 +0100
@@ -58,6 +58,9 @@
 		else:
 			self._desktop.launch([])
 	
+	def get_category(self):
+		return "Programs"
+	
 	def get_verb(self):
 		#translators: First %s is the programs full name, second is the executable name
 		#translators: For example: Launch Text Editor (gedit)
Binary files ../deskbar-applet/deskbar/handlers/programs.pyc and deskbar/handlers/programs.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/volumes.py deskbar/handlers/volumes.py
--- ../deskbar-applet/deskbar/handlers/volumes.py	2006-01-02 01:57:54.000000000 +0100
+++ deskbar/handlers/volumes.py	2006-01-07 03:44:12.000000000 +0100
@@ -28,6 +28,9 @@
 	
 	def action(self, text=None):
 		gobject.spawn_async(["nautilus", self.__drive.get_activation_uri()], flags=gobject.SPAWN_SEARCH_PATH)
+	
+	def get_category(self):
+		return "Files"
 	 
 	def get_verb(self):
 		activation = self.__drive.get_activation_uri()
Binary files ../deskbar-applet/deskbar/handlers/volumes.pyc and deskbar/handlers/volumes.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers/web_address.py deskbar/handlers/web_address.py
--- ../deskbar-applet/deskbar/handlers/web_address.py	2006-01-02 01:57:54.000000000 +0100
+++ deskbar/handlers/web_address.py	2006-01-07 03:44:12.000000000 +0100
@@ -28,6 +28,9 @@
 			gnomevfs.url_show(self._url)
 		else:
 			gobject.spawn_async(["nautilus", self._url], flags=gobject.SPAWN_SEARCH_PATH)
+			
+	def get_category(self):
+		return "Actions"
 	
 	def get_verb(self):
 		if not self._has_method:
Binary files ../deskbar-applet/deskbar/handlers/web_address.pyc and deskbar/handlers/web_address.pyc differ
diff -ruN ../deskbar-applet/deskbar/handlers_browsers.py deskbar/handlers_browsers.py
--- ../deskbar-applet/deskbar/handlers_browsers.py	2006-01-02 01:57:53.000000000 +0100
+++ deskbar/handlers_browsers.py	2006-01-07 03:46:06.000000000 +0100
@@ -24,6 +24,9 @@
 	def action(self, text=None):
 		gnomevfs.url_show(self._url)
 		
+	def get_category(self):
+		return "Web Browser"
+		
 	def get_verb(self):
 		if self._is_history:
 			return _("Open History Item %s") % "<b>%(name)s</b>"
diff -ruN ../deskbar-applet/deskbar/pearl.py deskbar/pearl.py
--- ../deskbar-applet/deskbar/pearl.py	1970-01-01 01:00:00.000000000 +0100
+++ deskbar/pearl.py	2006-01-07 03:46:06.000000000 +0100
@@ -0,0 +1,584 @@
+#
+# Known issues/ Ideas
+#
+# - (EASY) Expanding should be possible with enter/space as well
+#
+# - (?) Multiscreen logic.
+#
+# - (HARD) Detach the search window to "store" the search
+#
+# - (MEDIUM) Drag hits onto desktop/nautilus
+#
+# - (?) The entry loose focus sometimes when the CuemiacWindow appears,
+#   really annoying!
+#
+# - (MEDIUM) Store expandedness state of categories
+#
+# - (MEDIUM) User defined (non-static) categories *WITHOUT PERFOMANCE HIT*
+#
+# - (?) Optimize
+
+from os.path import *
+
+import cgi
+import sys
+
+import gtk, gobject
+import gnome
+import gnome.ui, gnomeapplet
+import pango
+
+from categories import CATEGORIES
+
+from gettext import gettext as _
+
+class Nest :
+	"""
+	A class used to handle nested results in the CuemiacModel
+	"""
+	def __init__(self, category_name, parent):
+		self.__nest_msg = category_name
+		self.__parent = parent # The CuemiacCategory to which this nest belongs
+	
+	def get_name (self, text=None):
+		return {"count" : self.__parent.get_count () - self.__parent.get_threshold ()}
+	
+	def get_verb (self):
+		return self.__nest_msg
+		
+	def get_count (self):
+		return self.__parent.get_count () - self.__parent.get_threshold ()
+
+class CuemiacCategory :
+
+	def __init__ (self, name, parent, threshold=5):
+		self.__category_row_ref = None
+		self.__nest_row_ref = None
+		self.__parent = parent
+
+		self.__name = name
+		self.__threshold = threshold
+		self.__count = 0
+
+	def get_category_row_path (self):
+		return self.__category_row_ref.get_path ()
+		
+	def get_nest_row_path (self):
+		return self.__nest_row_ref.get_path ()
+
+	def set_category_iter (self, iter):
+		self.__category_row_ref = gtk.TreeRowReference (self.__parent, self.__parent.get_path(iter))
+		
+	def get_category_iter (self):
+		"""
+		Returns a gtk.TreeIter pointing at the category
+		"""
+		return self.__parent.get_iter (self.__category_row_ref.get_path())
+		
+	def set_nest_iter (self, iter):
+		self.__nest_row_ref = gtk.TreeRowReference (self.__parent, self.__parent.get_path(iter))	
+		
+	def get_nest_iter (self):
+		"""
+		Returns a gtk.TreeIter pointing at the nested row
+		"""
+		return self.__parent.get_iter (self.__nest_row_ref.get_path())
+	
+	def get_name (self):
+		return self.__name
+	
+	def inc_count (self):
+		self.__count = self.__count + 1
+	
+	def get_count (self):
+		return self.__count
+	
+	def get_threshold (self):
+		return self.__threshold
+
+class CuemiacModel (gtk.TreeStore):
+
+	# Column name
+	MATCHES = 0
+
+	def __init__ (self):
+		"""
+		"""
+		gtk.TreeStore.__init__ (self, 
+					gobject.TYPE_PYOBJECT)	# Match object
+		self.__categories = {}
+		
+	def append (self, match):
+		"""
+		Automagically append a match or list of matches 
+		to correct category(s), or create a new one(s) if needed.
+		"""
+		if type (match) == list:
+			for hit in match:
+				self.__append (hit)
+		else:
+			self.__append (match)
+		
+	def __append (self, match):
+		if self.__categories.has_key (match.get_category()):
+			self.__append_to_category (match)
+		else:
+			self.__create_category_with_match (match)
+			
+			
+	def __create_category_with_match (self, match):
+		"""
+		Assumes that the category for the match does not exist.
+		"""
+		#FIXME: Check validity of category name and use  proper i18n
+		# Set up a new category
+		cat = CuemiacCategory (match.get_category(), self)
+		iter = gtk.TreeStore.append (self, None, [cat])
+		cat.set_category_iter (iter)
+		self.__categories [match.get_category()] = cat
+
+		# Append the match to the category		
+		gtk.TreeStore.append (self, iter, [match])
+		cat.inc_count ()
+		
+	
+	def __append_to_category (self, match):
+
+		cat = self.__categories [match.get_category ()]
+		row_iter = None
+		if cat.get_count() < cat.get_threshold() :
+			cat.inc_count ()
+			gtk.TreeStore.append (self, cat.get_category_iter(), [match])
+			
+		elif cat.get_count() == cat.get_threshold():
+			nest = Nest (CATEGORIES[match.get_category ()]["nest"], cat)
+			nest_iter = gtk.TreeStore.append (self, cat.get_category_iter(), [nest])
+			cat.set_nest_iter (nest_iter)
+			
+			cat.inc_count ()
+			gtk.TreeStore.append (self, nest_iter, [match])
+		else:
+			cat.inc_count ()
+			gtk.TreeStore.append (self, cat.get_nest_iter(), [match])
+			# Update the nested count in the nest row:
+			self.row_changed (cat.get_nest_row_path(), cat.get_nest_iter())
+			
+		# Update the row count in the view:
+		self.row_changed (cat.get_category_row_path(), cat.get_category_iter())
+		
+	def clear (self):
+		"""
+		Clears this model of data.
+		"""
+		gtk.TreeStore.clear (self)
+		self.__categories = {}
+
+class CellRendererCuemiacCategory (gtk.CellRendererText):
+
+	__gproperties__ = {
+        		'category-header' : (gobject.TYPE_STRING, 'markup for category title string',
+                  	'markup for category title string, None if this is not a category header',
+                 	 None, gobject.PARAM_READWRITE),
+                 	 
+                 'match-count' : (gobject.TYPE_INT, 'number of hits in the category',
+                  	'the number of hits for the CuemiacCategory to be rendered',
+                 	 0,1000,0, gobject.PARAM_READWRITE)
+        }
+	
+	def __init__ (self):
+		gtk.CellRendererText.__init__ (self)
+		self.__category_header = None
+		self.__match_count = 0
+	
+	def do_render (self, window, widget, background_area, cell_area, expose_area, flags):
+		if not self.get_property ("category-header"):
+			gtk.CellRendererText.do_render (self, window, widget, background_area, cell_area, expose_area, flags)
+		else:
+			self.render_category (window, widget, background_area, cell_area, expose_area, flags)
+	
+	def render_category (self, window, widget, background_area, cell_area, expose_area, flag):
+		"""
+		Renders the category title from the "markup" property and displays a rigth aligned
+		hit count (read from the "match-count" property).
+		"""
+		ctx = window.cairo_create ()
+		font_desc = pango.FontDescription ("sans bold 8") #FIXME: Use gtk theme
+		
+		# Set up a pango.Layout for the category title
+		cat_layout = ctx.create_layout ()
+		cat_layout.set_text (self.get_property("category-header"))
+		cat_layout.set_font_description (font_desc)
+		
+		# Set up a pango.Layout for the hit count
+		count_layout = ctx.create_layout ()
+		count_layout.set_text ("(" + str(self.get_property("match-count")) + ")")
+		count_layout.set_font_description (font_desc)
+		
+		# Position and draw the layouts
+		ctx.move_to (18, background_area.y + 6)
+		ctx.show_layout (cat_layout)
+		w, h = count_layout.get_pixel_size()
+		ctx.move_to (background_area.width - w + 10, background_area.y + 6)
+		ctx.show_layout (count_layout)
+		
+		ctx.set_source_rgb (1,1,1)
+		ctx.move_to (0, background_area.y + 1)
+		ctx.line_to (background_area.width + 100, background_area.y + 1)
+		ctx.stroke ()
+		
+	def do_get_property(self, property):
+		if property.name == 'category-header':
+			return self.__category_header
+		elif property.name == 'match-count':
+			return self.__match_count
+		else:
+			raise AttributeError, 'unknown property %s' % property.name
+
+	def do_set_property(self, property, value):
+		if property.name == 'category-header':
+			self.__category_header = value
+		elif property.name == 'match-count':
+			self.__match_count = value
+		else:
+			raise AttributeError, 'unknown property %s' % property.name
+		
+
+class CuemiacTreeView (gtk.TreeView):
+	"""
+	Shows a DeskbarCategoryModel. Used internally in the CuemiacWidget.
+	"""
+	
+	__gsignals__ = {
+		"match-selected" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_PYOBJECT]),
+	}
+	
+	def __init__ (self, model):
+		gtk.TreeView.__init__ (self, model)
+				
+		icon = gtk.CellRendererPixbuf ()
+		hit_title = CellRendererCuemiacCategory ()
+		hit_title.set_property ("ellipsize", pango.ELLIPSIZE_END)
+		hit_title.set_property ("width-chars", 50) #FIXME: Pick width according to screen size
+		hits = gtk.TreeViewColumn ("Hits")
+		hits.pack_start (icon)
+		hits.pack_start (hit_title)
+		hits.set_cell_data_func(hit_title, self.__get_match_title_for_cell)			
+		hits.set_cell_data_func(icon, self.__get_match_icon_for_cell)
+		self.append_column (hits)
+		
+		self.connect ("cursor-changed", self.__on_cursor_changed)
+		self.set_property ("headers-visible", False)
+		self.set_property ("hover-selection", True)
+		self.set_reorderable(True)
+		self.connect ("button-press-event", self.__on_click)
+		
+	def __on_cursor_changed (self, view):
+		model, iter = self.get_selection().get_selected()
+	
+	def __get_match_icon_for_cell (self, column, cell, model, iter, data=None):
+		
+		match = model[iter][model.MATCHES]
+		
+		if match.__class__ == CuemiacCategory:
+			cell.set_property ("pixbuf", None)
+			cell.set_property ("cell-background", "gray")
+			
+		else:
+			cell.set_property ("cell-background", "white")
+			if match.__class__ == Nest:
+				cell.set_property ("pixbuf", None)		
+			else:
+				cell.set_property ("pixbuf", match.get_icon())
+
+		
+	def __get_match_title_for_cell (self, column, cell, model, iter, data=None):
+
+		match = model[iter][model.MATCHES]
+		
+		if match.__class__ == CuemiacCategory:
+			# Look up i18n category name
+			cell.set_property ("cell-background", "gray")
+			#cell.set_property ("height", 20)
+			cell.set_property ("category-header", match.get_name())
+			cell.set_property ("match-count", match.get_count ())
+			return
+		else:
+			cell.set_property ("category-header", None)
+			cell.set_property ("height", -1)
+			cell.set_property ("cell-background", "white")
+		
+		t = entry.get_text().strip () # FIXME: This will have to be changed in a proper implementation
+		# Pass unescaped query to the matches
+		verbs = {"text" : t}
+		verbs.update(match.get_name(t))
+		# Escape the query now for display
+		verbs["text"] = cgi.escape(verbs["text"])
+		
+		cell.set_property ("markup", match.get_verb () % verbs)
+
+
+	def clear (self):
+		self.model.clear ()
+
+	def __on_click (self, widget, event):
+		
+		model, iter = self.get_selection().get_selected()
+		match = model[iter][model.MATCHES]
+		self.emit ("match-selected", match)
+	
+class CuemiacWindow (gtk.Window):
+	"""
+	Borderless window aligning itself to a given widget
+	"""
+	def __init__(self, widgetToAlignWith, alignment):
+		"""
+		alignment should be one of
+			gnomeapplet.ORIENT_{DOWN,UP,LEFT,RIGHT}
+		
+		The window will be placed accordingly relative to the given widget
+		upon invoking .show().
+		"""
+		gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
+		self.set_decorated(False)
+		
+		self.widgetToAlignWith = widgetToAlignWith
+		self.alignment = alignment
+		self.__is_shown = False
+		
+		self.scroll_win = gtk.ScrolledWindow ()
+		self.scroll_win.set_policy (gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+		gtk.Window.add (self, self.scroll_win)
+		
+		self.screen_height = self.get_screen().get_height ()
+		self.max_window_height = int (0.6 * self.screen_height)
+		
+	def add (self, child):
+		# Register a callback to respond to resizing of the child
+		self.scroll_win.add_with_viewport (child)
+		child.connect ("size-request", self.adjust_size)
+
+	def adjust_size (self, child, event):
+		# FIXME: SHould we handle width intelligently also?
+		w, h = child.size_request ()
+		h = h + 4 # To ensure we don't always show scrollbars
+		h = min (h, self.max_window_height)
+		self.resize (w, h)
+
+	def show (self):
+		"""
+		Calculates the position and shows the window
+		"""
+		# Get our own dimensions & position
+		self.realize()
+		gtk.gdk.flush()
+		window_width  = (self.window.get_geometry())[2]
+	   	window_height = (self.window.get_geometry())[3]
+
+		# Skip the taskbar, and the pager, stick and stay on top
+		self.stick()
+		self.set_keep_above(True)
+		self.set_skip_pager_hint(True)
+		self.set_skip_taskbar_hint(True)
+		self.set_type_hint (gtk.gdk.WINDOW_TYPE_HINT_DOCK)
+
+		# Get the dimensions/position of the widgetToAlignWith
+		self.widgetToAlignWith.realize()
+		(x, y) = self.widgetToAlignWith.window.get_origin()
+
+		(w, h) = self.get_size()
+		(w, h) = self.size_request()
+
+		target_w = self.widgetToAlignWith.allocation.width
+		target_h = self.widgetToAlignWith.allocation.height
+
+		screen = self.get_screen()
+
+		found_monitor = False
+		n = screen.get_n_monitors()
+		for i in range(0, n):
+				monitor = screen.get_monitor_geometry(i)
+				if (x >= monitor.x and x <= monitor.x + monitor.width and \
+					y >= monitor.y and y <= monitor.y + monitor.height):
+						found_monitor = True
+						break
+		
+		if not found_monitor:
+				monitor = gtk.gdk.Rectangle(0, 0, screen.get_width(), screen.get_width())
+		
+		self.alignment
+		if self.alignment == gnomeapplet.ORIENT_RIGHT:
+				x += target_w
+
+				if ((y + h) > monitor.y + monitor.height):
+						y -= (y + h) - (monitor.y + monitor.height)
+				
+				if ((y + h) > (monitor.height / 2)):
+						gravity = gtk.gdk.GRAVITY_SOUTH_WEST	
+				else:
+						gravity = gtk.gdk.GRAVITY_NORTH_WEST
+		elif self.alignment == gnomeapplet.ORIENT_LEFT:
+				x -= w
+
+				if ((y + h) > monitor.y + monitor.height):
+						y -= (y + h) - (monitor.y + monitor.height)
+				
+				if ((y + h) > (monitor.height / 2)):
+						gravity = gtk.gdk.GRAVITY_SOUTH_EAST
+				else:
+						gravity = gtk.gdk.GRAVITY_NORTH_EAST
+		elif self.alignment == gnomeapplet.ORIENT_DOWN:
+				y += target_h
+
+				if ((x + w) > monitor.x + monitor.width):
+						x -= (x + w) - (monitor.x + monitor.width)
+
+				gravity = gtk.gdk.GRAVITY_NORTH_WEST
+		elif self.alignment == gnomeapplet.ORIENT_UP:
+				y -= h
+
+				if ((x + w) > monitor.x + monitor.width):
+						x -= (x + w) - (monitor.x + monitor.width)
+
+				gravity = gtk.gdk.GRAVITY_SOUTH_WEST
+		
+		# -"Coordinates locked in captain."
+		# -"Engage."
+		self.move(x, y)
+		#print "Move win to "+x+","+y
+		self.set_gravity(gravity)
+		gtk.Window.show (self)
+		self.__is_shown = True
+		
+	def show_all (self):
+		self.show ()
+		gtk.Window.show_all (self)
+		
+	def hide (self):
+		self.__is_shown = False
+		gtk.Window.hide (self)
+	
+	def is_shown (self):
+		return self.__is_shown
+
+# --------------------BEGIN CUT-N-PASTE FROM DESKBAR-APPLET ---------------
+import gtk
+gtk.threads_init()
+import gnome.ui, gnomeapplet
+
+import getopt, sys
+from os.path import *
+
+# Allow to use uninstalled
+def _check(path):
+	return exists(path) and isdir(path) and isfile(path+"/AUTHORS")
+
+name = join(dirname(__file__), "..")
+if _check(name):
+	print 'Running uninstalled deskbar, modifying PYTHONPATH'
+	sys.path.insert(0, abspath(name))
+else:
+	print "Running installed deskbar, using normal PYTHONPATH"
+
+# Now the path is set, import our applet
+import deskbar, deskbar.applet, deskbar.defs
+
+import gettext, locale
+gettext.bindtextdomain('deskbar-applet', abspath(join(deskbar.defs.DATA_DIR, "locale")))
+gettext.textdomain('deskbar-applet')
+
+locale.bindtextdomain('deskbar-applet', abspath(join(deskbar.defs.DATA_DIR, "locale")))
+locale.textdomain('deskbar-applet')
+# --------------------END CUT-N-PASTE FROM DESKBAR-APPLET ---------------
+
+#
+# Load modules and set up async handling of matches
+#
+modules = []
+def on_module_loaded (loader, ctx):
+	loader.initialize_module (ctx)
+	modules.append (ctx)
+	if ctx.module.is_async ():
+		ctx.module.connect ("query-ready", lambda sender, match: cmodel.append(match))
+
+from module_list import ModuleLoader, ModuleList
+mloader = ModuleLoader (deskbar.MODULES_DIRS)
+mlist = ModuleList ()
+mloader.connect ("module-loaded", on_module_loaded)
+
+mloader.load_all()
+
+#
+# Callback when somehting is typed into the entry
+#
+def on_entry_changed (entry):
+	# FIXME: We should store the expandedness state of the categories somehow
+	cmodel.clear()
+	text = entry.get_text().strip()
+	
+	if text == "":
+		cwindow.hide ()
+		return
+	
+	for ctx in modules:
+		if not ctx.enabled:
+			continue
+			
+		if ctx.module.is_async ():
+			ctx.module.query_async (text, 10)
+		else:
+			cmodel.append (ctx.module.query (text, 10))
+	
+	if not cwindow.is_shown ():
+		cwindow.show_all ()
+#
+# Callback to monitor special keys (like Esc)
+#
+def on_entry_key_press (entry, event):
+	if event.keyval == gtk.keysyms.Escape:
+			# bind Escape to clear the GtkEntry
+			if not entry.get_text().strip() == "":
+				# If we cleared some text, tell async handlers to stop.
+				for ctx in modules:
+					if ctx.module.is_async ():
+						ctx.module.stop_query ()
+			entry.set_text("")
+
+#
+# Setup up the UI and create the Cuemiac
+#
+vbox = gtk.VBox ()
+
+entry = gtk.Entry()
+entry.connect ("changed", on_entry_changed)
+entry.connect ("key-press-event", on_entry_key_press)
+
+cmodel = CuemiacModel ()
+cview = CuemiacTreeView (cmodel)
+cwindow = CuemiacWindow (entry, gnomeapplet.ORIENT_DOWN)
+cwindow.add (cview)
+#cwindow.connect ("focus-out-event", lambda widget,event: cwindow.hide())
+
+#
+# Callback to trigger the action associated to a match
+#
+def do_action(sender, match):
+	if hasattr(match, 'action'):
+		match.action(entry.get_text())
+
+
+#
+# Setup final GUI stuff
+#	
+cview.connect ("match-selected", do_action)
+
+vbox.pack_start (entry, False)
+
+window = gtk.Window()
+window.connect ("destroy", gtk.main_quit)
+window.add (vbox)
+window.show_all()
+
+	
+cview.show_all()
+
+gtk.main ()


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