home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / deskbar-applet / handlers / beagle-live.py next >
Encoding:
Python Source  |  2006-08-29  |  11.3 KB  |  375 lines

  1. import os, sys, cgi, re
  2. import gobject,gtk, gnome, gnome.ui, gnomevfs
  3. import deskbar, deskbar.Handler, deskbar.Utils, deskbar.Match
  4. from gettext import gettext as _
  5. from os.path import exists
  6. from deskbar.defs import VERSION
  7.  
  8. MAX_RESULTS = 20 # per handler
  9.  
  10. try:
  11.     import beagle
  12. except:
  13.     # If this fails we complain about it in _check_requirements()
  14.     # so do nothing now
  15.     pass
  16.  
  17. def _show_start_beagle_dialog (dialog):
  18.     dialog = gtk.Dialog(_("Start Beagle Daemon?"), dialog,
  19.                 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
  20.     
  21.     dialog.set_default_size (350, 150)
  22.     dialog.add_button (_("Start Beagle Daemon"), gtk.RESPONSE_ACCEPT)
  23.     dialog.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT)
  24.     label = gtk.Label (_("The Beagle daemon does not appear to be running.\n You need to start it to use the Beagle Live handler."))
  25.     dialog.vbox.add (label)
  26.     label.show()
  27.  
  28.     response = dialog.run()
  29.     dialog.destroy()
  30.     
  31.     
  32.     if response == gtk.RESPONSE_ACCEPT :
  33.         print "Starting Beagle Daemon."
  34.         gobject.spawn_async(["beagled"], flags=gobject.SPAWN_SEARCH_PATH)
  35.  
  36.  
  37. def _check_requirements():
  38.     # Check if we have python bindings for beagle
  39.     try:
  40.         import deskbar
  41.         import beagle
  42.     except Exception, e:
  43.         return (deskbar.Handler.HANDLER_IS_NOT_APPLICABLE, "Could not load beagle, libbeagle has been compiled without python bindings:"+str(e), None)
  44.  
  45.     # Check if beagled is running        
  46.     if not beagle.beagle_util_daemon_is_running () :
  47.         return (deskbar.Handler.HANDLER_HAS_REQUIREMENTS, "Beagle daemon is not running.", _show_start_beagle_dialog)
  48.     else:
  49.         return (deskbar.Handler.HANDLER_IS_HAPPY, None, None)
  50.     
  51. HANDLERS = {
  52.     "BeagleLiveHandler" : {
  53.         "name": _("Beagle Live"),
  54.         "description": _("Search all of your documents (using Beagle), as you type"),
  55.         # We must see how to detect properly beagle, for now it will fail on creating a new client
  56.         # when beagle is not available.
  57.         "requirements" : _check_requirements,
  58.         "version": VERSION,
  59.     }
  60. }
  61.  
  62. # The TYPES dict contains Beagle HitTypes as keys with
  63. # templates for the valid fields.
  64. #
  65. # A Hit Type consists of:
  66. #    "name"        : Template used to find a user-displayable name
  67. #    "action"    : Command to execute
  68. #    "icon"        : The *name* of the icon to be sued for this type. Set to None for no icon.
  69. #    "description"    : A short description. %(*)s may refer to *=name,uri,action or any field in "extra" (see below)
  70. #
  71. # Optionally:
  72. #    "extra"    : A dict containing key:template pairs to search for. You can use %(key)s in "description".
  73. #
  74. # Note:
  75. #  The templates are a tuple of strings which should be tested in order to retreive the beagle property
  76.  
  77. TYPES = {
  78.     "Contact"    : {
  79.         "name"    : ("fixme:FileAs",),
  80.         "action": "evolution %(uri)s",
  81.         "icon"    : "stock_contact",
  82.         "description": _("Edit contact %s") % "<b>%(name)s</b>",
  83.         "category": "people",
  84.         },
  85.     
  86.     "MailMessage"     : {
  87.         "name"    :("dc:title", "parent:dc:title"),
  88.         "action": "evolution %(uri)s",
  89.         "icon"    : "stock_mail",
  90.         "extra": {"sender":("fixme:from_name", "parent:fixme:from_name")},
  91.         "description": (_("From %s") % "<i>%(sender)s</i>" ) + "\n<b>%(name)s</b>",
  92.         "category": "emails",
  93.         },
  94.     "File"         : {
  95.         "name"    : ("beagle:ExactFilename",), 
  96.         "action": lambda d: gnome.url_show(d["uri"]),
  97.         "icon"    : "stock_new",
  98.         #translators: This is a file.
  99.         "description": _("Open %s") % "<b>%(name)s</b>",
  100.         "snippet": True,
  101.         "category": "files",
  102.         },
  103.     "FeedItem"    : {
  104.         "name"    : ("dc:title",),
  105.         "action": lambda d: gnome.url_show(d["identifier"]),
  106.         "icon"    : "stock_news",
  107.         "description": (_("News from %s") % "<i>%(publisher)s</i>" ) + "\n<b>%(name)s</b>",
  108.         "snippet": True,
  109.         "category": "news",
  110.         "extra": {"publisher":("dc:publisher",), "identifier": ("dc:identifier",)},
  111.         },
  112.     "Note"        : {
  113.         "name"    : ("dc:title",),
  114.         "action": "tomboy --open-note %(uri)s",
  115.         "icon"    :"stock_notes",
  116.         "description": _("Note: %s") % "<b>%(name)s</b>",
  117.         "snippet": True,
  118.         "category": "notes",
  119.         },
  120.     "IMLog"        : {
  121.         "name"    : ("fixme:speakingto",),
  122.         "extra" : {"client": ("fixme:client",)},
  123.         "action": "beagle-imlogviewer --client %(client)s --highlight-search '%(text)s' %(uri)s",
  124.         "icon"    : "im",
  125.         "description": _("With %s") % "<b>%(name)s</b>",
  126.         "snippet": True,
  127.         "category": "conversations",
  128.         },
  129.     "Calendar"    : {
  130.         "name"    : ("fixme:summary",),
  131.         "action": "evolution %(uri)s",
  132.         "icon"    : "stock_calendar",
  133.         "description": _("Calendar: %s") % "<b>%(name)s</b>",
  134.         "category": "documents",
  135.         },
  136.     "WebHistory": {
  137.         "name"    : ("dc:title",), # FIX-BEAGLE bug #330053, dc:title returns as None even though it _is_ set
  138.         "action": lambda d: gnome.url_show(d["uri"]),
  139.         "icon"    : "stock_bookmark",
  140.         "description": (_("Open History Item %s") % "<i>%(name)s</i>") + "\n%(escaped_uri)s",
  141.         "category": "web",
  142.         },
  143. }
  144.  
  145. # Append snippet text for snippet-enabled handlers
  146. for key, val in TYPES.items():
  147.     if "snippet" in val and val["snippet"]:
  148.         val["description"] += "%(snippet)s"
  149.         
  150. class BeagleLiveMatch (deskbar.Match.Match):
  151.     def __init__(self, handler, result=None, **args):
  152.         """
  153.         result: a dict containing:
  154.             "name" : a name sensible to display for this match
  155.             "uri": the uri of the match as provided by the beagled 'Uri: '-field
  156.             "type": One of the types listed in the TYPES dict
  157.  
  158.         -- and optionally extra fields as provided by the corresponding entry in TYPES.
  159.         Fx. "MailMessage". has an extra "sender" entry.
  160.         """
  161.         deskbar.Match.Match.__init__ (self, handler, name=result["name"], **args)
  162.         self.result = result
  163.  
  164.         # IM Log viewer take loca paths only        
  165.         action = TYPES[self.result["type"]]["action"]
  166.         if not callable(action) and action.startswith("beagle-imlogviewer"):
  167.             # Strip the uti descriptor, because imlogviewer takes a local path
  168.             self.result["uri"] = gnomevfs.get_local_path_from_uri(self.result["uri"])            
  169.         
  170.         # Load the correct icon
  171.         
  172.         #
  173.         # There is bug http://bugzilla.gnome.org/show_bug.cgi?id=319549
  174.         # which has been fixed and comitted, so we re-enable this snippet
  175.         #
  176.         
  177.         self._icon = None
  178.         if result["type"] == "File":
  179.             try:
  180.                 self._icon = deskbar.Utils.load_icon_for_file(result["uri"])
  181.             except Exception:
  182.                 pass
  183.         
  184.         if self._icon == None:
  185.             # Just use an icon from the ICON table
  186.             self._icon = handler.ICONS[result["type"]]
  187.         
  188.     def get_category (self):
  189.         try:
  190.             return TYPES[self.result["type"]]["category"]
  191.         except:
  192.             return "default"
  193.         
  194.     def get_name (self, text=None):
  195.         # We use the result dict itself to look up words
  196.         if text:
  197.             self.result["text"] = text
  198.             # Escape text since we use '%(text)s' as parameter
  199.             self.result["text"] = self.result["text"].replace("'", "\\'")
  200.         return self.result
  201.     
  202.     def get_verb(self):
  203.         # Fetch the "description" template from TYPES
  204.         return TYPES[self.result["type"]]["description"]
  205.         
  206.     def action(self, text=None):
  207.         # The call to get_name(text) ensures that we have
  208.         # the text field in the result dict
  209.         self.get_name(text)
  210.         
  211.         action = TYPES[self.result["type"]]["action"]
  212.         if callable(action):
  213.             print "BeagleLive url_show()", self.result
  214.             action(self.result)
  215.         else:
  216.             # Retrieve the associated action
  217.             action = action % self.result
  218.             args = action.split(" ")
  219.  
  220.             print "BeagleLive spawning:", action, args
  221.             gobject.spawn_async(args, flags=gobject.SPAWN_SEARCH_PATH)
  222.     
  223.     def get_hash(self, text=None):
  224.         if "uri" in self.result:
  225.             return self.result["uri"]
  226.  
  227. class SnippetContainer:
  228.     def __init__(self, hit):
  229.         self.hit = hit
  230.         self.snippet = None
  231.     
  232. class BeagleLiveHandler(deskbar.Handler.SignallingHandler):
  233.     def __init__(self):
  234.         deskbar.Handler.SignallingHandler.__init__(self, ("system-search", "best"))
  235.         self.counter = {}
  236.         self.snippets = {}
  237.         self.set_delay (500)
  238.         
  239.     def initialize (self):
  240.         self.beagle = beagle.Client()
  241.         self.ICONS = self.__load_icons()
  242.     
  243.     def __load_icons (self):
  244.         res = {}
  245.         for t in TYPES.iterkeys():
  246.             icon_file = TYPES[t]["icon"]
  247.             if not icon_file: continue
  248.             res[t] = deskbar.Utils.load_icon(icon_file)
  249.         return res
  250.         
  251.     def query (self, qstring):
  252.         beagle_query = beagle.Query()
  253.         beagle_query.add_text(qstring)
  254.         beagle_query.connect("hits-added", self.hits_added, qstring, MAX_RESULTS)
  255.         try:
  256.             self.beagle.send_request_async(beagle_query)
  257.         except:
  258.             return
  259.             
  260.         self.counter[qstring] = {}
  261.         
  262.     def _on_snippet_received(self, request, response, query, container, qstring, qmax):
  263.         container.snippet = response.get_snippet()
  264.         self._on_hit_added(query, container, qstring, qmax)
  265.     
  266.     def _on_snippet_closed(self, request, query, container, qstring, qmax):
  267.         if container.snippet == None:
  268.             self._on_hit_added(query, container, qstring, qmax)
  269.             
  270.         container.hit.unref()
  271.             
  272.     def _on_hit_added(self, query, hit, qstring, qmax):
  273.         fire_signal = False
  274.         snippet = None
  275.         if hit.__class__ == SnippetContainer:
  276.             hit, snippet = hit.hit, hit.snippet
  277.             fire_signal = True
  278.             
  279.         if not hit.get_type() in self.counter[qstring]:
  280.             self.counter[qstring][hit.get_type()] = 0
  281.  
  282.         if self.counter[qstring][hit.get_type()] >= qmax:
  283.             return
  284.             
  285.         hit_type = TYPES[hit.get_type()]
  286.         result = {
  287.             "uri":  hit.get_uri(),
  288.             "type": hit.get_type(),
  289.         }
  290.         
  291.         if snippet != None:
  292.             tmp = re.sub(r"<.*?>", "", snippet)
  293.             tmp = re.sub(r"</.*?>", "", tmp)
  294.             result["snippet"] = "\n<span foreground='grey' size='small'>%s</span>" % cgi.escape(tmp)
  295.         else:
  296.             result["snippet"] = ""
  297.         
  298.         name = None
  299.         for prop in hit_type["name"]:
  300.             try:
  301.                 name = hit.get_properties(prop)[0] # get_property_one() would be cleaner, but this works around bug #330053
  302.             except:
  303.                 try:
  304.                     # Beagle < 0.2
  305.                     name = hit.get_property(prop)
  306.                 except:
  307.                     pass
  308.                     
  309.             if name != None:
  310.                 result["name"] = cgi.escape(name)
  311.                 break
  312.         
  313.         if name == None:
  314.             #translators: This is used for unknown values returned by beagle
  315.             #translators: for example unknown email sender, or unknown note title
  316.             result["name"] = _("?")
  317.             
  318.         if "extra" in hit_type:
  319.             for prop, keys in hit_type["extra"].items():
  320.                 val = None
  321.                 for key in keys:
  322.                     try:
  323.                         val = hit.get_properties(key)[0] # get_property_one() would be cleaner, but this works around bug #330053
  324.                     except:
  325.                         try:
  326.                             # Beagle < 0.2
  327.                             val = hit.get_property(key)
  328.                         except:
  329.                             pass
  330.                             
  331.                     if val != None:
  332.                         if prop == "uri" or prop == "identifier":
  333.                             result[prop] = val
  334.                             result["escaped_"+prop] = cgi.escape(val)
  335.                         else:
  336.                             result[prop] = cgi.escape(val)
  337.                         break
  338.                     
  339.                 if val == None:
  340.                     #translators: This is used for unknown values returned by beagle
  341.                     #translators: for example unknown email sender, or unknown note title
  342.                     result[prop] = _("?")
  343.                     
  344.         self.counter[qstring][hit.get_type()] = self.counter[qstring][hit.get_type()] +1
  345.  
  346.         match = BeagleLiveMatch(self, result)
  347.         if fire_signal:
  348.             self.emit_query_ready(qstring, [match])
  349.         else:    
  350.             return match
  351.         
  352.     def hits_added(self, query, response, qstring, qmax):
  353.         hit_matches = []
  354.         for hit in response.get_hits():
  355.             if hit.get_type() not in TYPES:
  356.                 print 'WARNING: Beagle live seen an unknown type:', hit.get_type()
  357.                 continue
  358.  
  359.             if "snippet" in TYPES[hit.get_type()] and TYPES[hit.get_type()]["snippet"]:
  360.                 req = beagle.SnippetRequest()
  361.                 req.set_query(query)
  362.                 req.set_hit(hit)
  363.                 container = SnippetContainer(hit)
  364.                 hit.ref()
  365.                 req.connect('response', self._on_snippet_received, query, container, qstring, qmax)
  366.                 req.connect('closed', self._on_snippet_closed, query, container, qstring, qmax)
  367.                 self.beagle.send_request_async(req)
  368.                 continue
  369.                             
  370.             match = self._on_hit_added(query, hit, qstring, qmax)
  371.             if match != None:
  372.                 hit_matches.append(match)                
  373.             
  374.         self.emit_query_ready(qstring, hit_matches)
  375.