summaryrefslogtreecommitdiffstats
path: root/kicker/kicker/plugins/kickoff-beagle-plugin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kicker/kicker/plugins/kickoff-beagle-plugin.cpp')
-rw-r--r--kicker/kicker/plugins/kickoff-beagle-plugin.cpp499
1 files changed, 499 insertions, 0 deletions
diff --git a/kicker/kicker/plugins/kickoff-beagle-plugin.cpp b/kicker/kicker/plugins/kickoff-beagle-plugin.cpp
new file mode 100644
index 000000000..3cad77ca4
--- /dev/null
+++ b/kicker/kicker/plugins/kickoff-beagle-plugin.cpp
@@ -0,0 +1,499 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Stephan Binner <binner@kde.org> *
+ * Copyright (c) 2006 Debajyoti Bera <dbera.web@gmail.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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "kickoff-beagle-plugin.h"
+
+#include <tqregexp.h>
+#include <tqtimer.h>
+
+#include <kapplication.h>
+#include <kdesktopfile.h>
+#include <kgenericfactory.h>
+#include <kservice.h>
+
+TQString dc_identifier = "dc:identifier";
+TQString dc_title = "dc:title";
+TQString parent_dc_title = "parent:dc:title";
+TQString exactfilename = "beagle:ExactFilename";
+TQString fixme_name = "fixme:Name";
+TQString beagle_filename = "beagle:Filename";
+TQString fixme_attachment_title = "fixme:attachment_title";
+TQString fixme_hasattachments = "fixme:hasAttachments";
+TQString parent_prefix = "parent:";
+TQString fixme_folder = "fixme:folder";
+TQString fixme_categories = "fixme:Categories";
+TQString fixme_comment = "fixme:Comment";
+TQString fixme_width = "fixme:width";
+TQString fixme_height = "fixme:height";
+TQString fixme_from_address = "fixme:from_address";
+TQString fixme_artist = "fixme:artist";
+TQString fixme_album = "fixme:album";
+TQString dc_source = "dc:source";
+TQString dc_publisher = "dc:publisher";
+TQString digikam_tag = "digikam:Tag";
+TQString fixme_speakingto = "fixme:speakingto";
+TQString fixme_starttime = "fixme:starttime";
+TQString comma_string = ",";
+TQString vCard_FN = "vCard:FN";
+TQString vCard_PREFEMAIL = "vCard:PREFEMAIL";
+TQString fixme_uid = "fixme:uid";
+
+static CATEGORY getHitCategory (Hit *hit)
+{
+ TQString hittype = hit->getType();
+ TQString hitsource = hit->getSource();
+
+ // if hit source is None, dont handle it. Might be anthrax-envelope :)
+ if (hitsource.isNull())
+ return OTHER;
+
+ if (hitsource == "documentation")
+ return DOCS;
+
+ if (hittype == "IMLog")
+ return CHATS;
+
+ // sure shots
+ if (hittype == "FeedItem")
+ return FEEDS;
+ if (hittype == "WebHistory")
+ return WEBHIST;
+ if (hittype == "MailMessage")
+ return MAILS;
+ if (hittype == "Note")
+ return NOTES;
+
+ // check for applications
+ if (hittype == "File" && (*hit) ["beagle:FilenameExtension"] == ".desktop")
+ return APPS;
+
+ // check for music
+ TQString hitmimetype = hit->getMimeType();
+ if (hitsource == "Amarok"
+ || hitmimetype.startsWith ("audio")
+ || hitmimetype == "application/ogg")
+ return MUSIC; // not an exhaustive search
+
+ // check for images from files
+ if (hitsource == "Files" && hitmimetype.startsWith ("image"))
+ return PICS;
+
+ if (hitsource == "Files" && hitmimetype.startsWith ("video"))
+ return VIDEOS;
+
+ if (hitsource == "Files")
+ return FILES;
+
+ if (hitsource == "KAddressBook")
+ return ACTIONS;
+
+ return OTHER;
+}
+
+K_EXPORT_COMPONENT_FACTORY( kickoffsearch_beagle,
+ KGenericFactory<KickoffBeaglePlugin>( "kickoffsearch_beagle" ) )
+
+KickoffBeaglePlugin::KickoffBeaglePlugin(TQObject *parent, const char* name, const TQStringList&)
+ : KickoffSearch::Plugin(parent, name ), genericTitle( true )
+{
+ g_type_init ();
+ current_beagle_client = NULL;
+}
+
+bool KickoffBeaglePlugin::daemonRunning()
+{
+ return beagle_util_daemon_is_running();
+}
+
+void KickoffBeaglePlugin::query(TQString term, bool _genericTitle)
+{
+ genericTitle = _genericTitle;
+ current_query_str = term;
+
+ // Beagle search
+ if (current_beagle_client != NULL) {
+ kdDebug () << "Previous client w/id " << current_beagle_client->id << " still running ... ignoring it." << endl;
+ current_beagle_client->stopClient ();
+ }
+ current_beagle_client_id = KApplication::random ();
+ kdDebug () << "Creating client with id:" << current_beagle_client_id << endl;
+
+ BeagleClient *beagle_client = beagle_client_new (NULL);
+ if (beagle_client == NULL) {
+ kdDebug() << "beagle service not running ..." << endl;
+ return;
+ }
+
+ TQStringList sources, types;
+ BeagleQuery *beagle_query = BeagleUtil::createQueryFromString (term, sources, types, 99); // maximum 99 results, if this doesnt work, blame the stars
+
+ current_beagle_client = new BeagleSearchClient (
+ current_beagle_client_id,
+ this,
+ beagle_client,
+ beagle_query,
+ false);
+ current_beagle_client->start();
+// kdDebug () << "Query dispatched at " << time (NULL) << endl;
+}
+
+void KickoffBeaglePlugin::cleanClientList ()
+{
+ toclean_list_mutex.lock ();
+ BeagleSearchClient *old_client = toclean_client_list.take (0);
+ if (old_client != NULL) { // failsafe
+ kdDebug () << "Cleanup old client " << old_client->id << endl;
+ delete old_client;
+ }
+ toclean_list_mutex.unlock ();
+}
+
+void KickoffBeaglePlugin::customEvent (TQCustomEvent *e)
+{
+ if (e->type () == RESULTFOUND) {
+// kdDebug () << "Quick query thread at " << time (NULL) << " with current_id=" << current_beagle_client_id << " finished ..." << endl;
+ BeagleSearchResult *result = (BeagleSearchResult *) e->data ();
+ if (current_beagle_client_id != result->client_id) {
+ kdDebug () << "Stale result from " << result->client_id << endl;
+ delete result;
+ // FIXME: Should I also free e ?
+ } else {
+ kdDebug () << "Good results ...total=" << result->total << endl;
+ showResults (result);
+ }
+ //KPassivePopup::message( "This is the message", this );
+ } else if (e->type () == SEARCHOVER) {
+ BeagleSearchClient *client = (BeagleSearchClient *) e->data ();
+ if (client == NULL) {
+// kdDebug () << "Query finished event at " << time (NULL) << " but client is already deleted" << endl;
+ return;
+ }
+// kdDebug () << "Query finished event at " << time (NULL) << " for id=" << client->id << endl;
+ if (current_beagle_client_id == client->id) {
+ kickoffSearchInterface()->searchOver();
+ current_beagle_client = NULL; // important !
+ }
+ } else if (e->type () == KILLME) {
+ BeagleSearchClient *client = (BeagleSearchClient *) e->data ();
+ if (client->finished ())
+ delete client;
+ else {
+ // add client to cleanup list
+ toclean_list_mutex.lock ();
+ toclean_client_list.append (client);
+ kdDebug () << "Scheduling client to be deleted in 500ms" << endl;
+ toclean_list_mutex.unlock ();
+ TQTimer::singleShot (500, this, TQT_SLOT (cleanClientList ()));
+ }
+ }
+}
+
+// this method decides what to display in the result list
+HitMenuItem *KickoffBeaglePlugin::hitToHitMenuItem (int category, Hit *hit)
+{
+ TQString title, info, mimetype, icon;
+ int score = 0;
+ KURL uri;
+
+#if 0
+ kdDebug() << "*** " << hit->getUri() << endl;
+ TQDict<TQStringList> all = hit->getAllProperties();
+ TQDictIterator<TQStringList> it( all );
+ for( ; it.current(); ++it )
+ kdDebug() << it.currentKey() << ": " << *(it.current()) << endl;
+#endif
+
+ switch (category) {
+ case FILES:
+ {
+ uri = hit->getUri ();
+ TQString uristr = uri.path ();
+ title = (*hit) [exactfilename];
+ int last_slash = uristr.findRev ('/', -1);
+ info = i18n("Folder: %1").arg(last_slash == 0 ? "/"
+ : uristr.section ('/', -2, -2));
+ }
+ break;
+ case ACTIONS:
+ {
+ if (hit->getSource()=="KAddressBook"){
+ title = i18n("Send Email to %1").arg((*hit)[vCard_FN]);
+ info = (*hit)[vCard_PREFEMAIL];
+ uri = "mailto:"+(*hit)[vCard_PREFEMAIL];
+ mimetype = hit->getMimeType ();
+ icon = "mail_new";
+
+ HitMenuItem * first_item=new HitMenuItem (title, info, uri, mimetype, 0, category, icon, score);
+ kickoffSearchInterface()->addHitMenuItem(first_item);
+
+ title =i18n("Open Addressbook at %1").arg((*hit)[vCard_FN]);
+ uri = "kaddressbook:/"+(*hit)[fixme_uid];
+ icon = "kaddressbook";
+ }
+ break;
+ }
+ case MAILS:
+ {
+ TQString prefix = TQString::null;
+ bool is_attachment = ((*hit) [parent_prefix + fixme_hasattachments] == "true");
+ bool has_parent = (! hit->getParentUri ().isEmpty ());
+ bool parent_mbox_file = false;
+ if (has_parent)
+ parent_mbox_file = ((*hit) [parent_prefix + fixme_folder] == TQString::null);
+
+ // Logic:
+ // If has_parent == false, everything is normal
+ // If has_parent == true, parent_mbox_file == false, everything is normal, use uri
+ // FIXME: If has_parent == true, parent_mbox_file == true, ???
+ // If has_parent == true, is_attachment == true, hit is attach and access with prefix "parent:", use parenturi
+ // Else, not attachment (multipart), access with prefix "parent:", use parenturi
+
+ if (has_parent && !parent_mbox_file) {
+ uri = hit->getParentUri ();
+ prefix = parent_prefix;
+ if (is_attachment)
+ title = (*hit) [fixme_attachment_title];
+ if (title.isEmpty ())
+ title = (*hit) [prefix + dc_title];
+ if (title.isEmpty ())
+ title = i18n("No subject");
+ if (is_attachment)
+ title = title.prepend (i18n("(Attachment) "));
+ info = (i18n("From %1").arg((*hit) [prefix + fixme_from_address]));
+ } else {
+ uri = hit->getUri ();
+ title = (*hit) [dc_title];
+ info = (i18n("From %1").arg((*hit) [fixme_from_address]));
+ }
+ }
+ mimetype = "message/rfc822"; // to handle attachment results
+ break;
+ case MUSIC:
+ uri = hit->getUri ();
+ title = (*hit) [exactfilename];
+ {
+ TQString artist = (*hit) [fixme_artist];
+ TQString album = (*hit) [fixme_album];
+ if (! artist.isEmpty ())
+ info = (i18n("By %1").arg(artist));
+ else if (! album.isEmpty ())
+ info = (i18n("From Album %1").arg(album));
+ else {
+ TQString uristr = uri.path ();
+ int last_slash = uristr.findRev ('/', -1);
+ info = i18n("Folder: %1")
+ .arg(last_slash == 0 ? "/" : uristr.section ('/', -2, -2));
+ }
+ }
+ break;
+ case VIDEOS:
+ uri = hit->getUri ();
+ title = (*hit) [exactfilename];
+ {
+ TQString uristr = uri.path ();
+ int last_slash = uristr.findRev ('/', -1);
+ info = i18n("Folder: %1").arg(last_slash == 0 ? "/" : uristr.section ('/', -2, -2));
+ }
+ break;
+ case WEBHIST:
+ uri = hit->getUri ();
+ title = (*hit) [dc_title];
+ title = title.replace(TQRegExp("\n")," ");
+ mimetype = "text/html";
+ if (title.isEmpty () || title.stripWhiteSpace ().isEmpty ()) {
+ title = uri.prettyURL ();
+ } else {
+ info = uri.host () + uri.path ();
+ }
+ break;
+ case FEEDS:
+ {
+ uri = KURL ((*hit) [dc_identifier]);
+ title = (*hit) [dc_title];
+ mimetype = "text/html";
+ TQString publisher = (*hit) [dc_publisher];
+ TQString source = (*hit) [dc_source];
+ if (! publisher.isEmpty ())
+ info = publisher;
+ else if (! source.isEmpty ())
+ info = source;
+ }
+ break;
+ case PICS:
+ {
+ uri = hit->getUri ();
+ title = (*hit) [exactfilename];
+ TQString width = (*hit) [fixme_width];
+ TQString height = (*hit) [fixme_height];
+ if (width.isEmpty () || height.isEmpty ()) {
+ TQString uristr = uri.path ();
+ int last_slash = uristr.findRev ('/', -1);
+ info = i18n("Folder: %1")
+ .arg(last_slash == 0 ? "/" : uristr.section ('/', -2, -2));
+ break;
+ }
+ info = (TQString (" (%1x%2)").arg (width).arg (height));
+ const TQStringList *tags = hit->getProperties (digikam_tag);
+ if (tags == NULL)
+ break;
+ TQString tags_string = tags->join (comma_string);
+ info += (" " + tags_string);
+ }
+ break;
+ case APPS:
+ {
+ uri = hit->getUri ();
+ title = (*hit) [dc_title];
+ KDesktopFile desktopfile(uri.path(),true);
+ if (genericTitle && !desktopfile.readGenericName().isEmpty()) {
+ title = desktopfile.readGenericName();
+ info = desktopfile.readName();
+ }
+ else {
+ title = desktopfile.readName();
+ info = desktopfile.readGenericName();
+ }
+ icon = desktopfile.readIcon();
+ TQString input = current_query_str.lower();
+ TQString command = desktopfile.readEntry("Exec");
+ if (command==input)
+ score = 100;
+ else if (command.find(input)==0)
+ score = 50;
+ else if (command.find(input)!=-1)
+ score = 10;
+ else if (title==input)
+ score = 100;
+ else if (title.find(input)==0)
+ score = 50;
+ else if (title.find(input)!=-1)
+ score = 10;
+ break;
+ }
+ break;
+ case NOTES:
+ {
+ uri = hit->getUri ();
+ title = (*hit) [dc_title];
+ title = i18n("Title: %1").arg(title.isEmpty() ? i18n("Untitled") : title);
+
+ if (hit->getSource()=="KNotes")
+ icon="knotes";
+ else
+ icon="contents2";
+ }
+ break;
+ case CHATS:
+ {
+ uri = hit->getUri ();
+ title = (*hit) [fixme_speakingto];
+ title = i18n("Conversation With %1").arg(title.isEmpty() ? i18n("Unknown Person") : title);
+ TQDateTime datetime;
+ datetime = datetimeFromString((*hit) [fixme_starttime]);
+ info=i18n("Date: %1").arg(KGlobal::locale()->formatDateTime(datetime,false));
+ if (hit->getMimeType()=="beagle/x-kopete-log")
+ icon="kopete";
+ else
+ icon="gaim";
+ }
+ break;
+ case DOCS:
+ {
+ uri = hit->getUri ();
+ title = (*hit) [dc_title];
+ if (title.isEmpty () || title.stripWhiteSpace ().isEmpty ())
+ title = uri.prettyURL ();
+ else {
+ TQString uristr = uri.path ();
+ int last_slash = uristr.findRev ('/', -1);
+ info = i18n("Folder: %1").arg(last_slash == 0 ? "/" : uristr.section ('/',
+ -2, -2));
+ }
+ }
+ break;
+ default:
+ return NULL;
+ }
+ if (mimetype.isEmpty ())
+ mimetype = hit->getMimeType ();
+ return new HitMenuItem (title, info, uri, mimetype, 0, category, icon, score);
+}
+
+void KickoffBeaglePlugin::showResults(BeagleSearchResult *result)
+{
+ if (result->total == 0 ) {
+ // Dont report error from here ...
+ kdDebug() << "No matches found" << endl;
+ delete result;
+ return;
+ }
+
+ const TQPtrList<Hit> *hits = result->getHits();
+ if (hits == NULL) {
+ kdDebug () << "Hmm... null" << endl;
+ delete result;
+ return;
+ }
+ kickoffSearchInterface()->initCategoryTitlesUpdate();
+
+ TQPtrListIterator<Hit> it (*hits);
+ Hit *hit;
+ for (; (hit = it.current ()) != NULL; ++it) {
+ CATEGORY category = getHitCategory (hit);
+
+ // if category is not handled, continue
+ if (category == OTHER)
+ continue;
+
+ if ( category == APPS ) {
+ // we need to check if this is useful
+ KService cs( hit->getUri().path() );
+ if ( cs.noDisplay() )
+ continue;
+ }
+
+ if (!kickoffSearchInterface()->anotherHitMenuItemAllowed(category))
+ continue;
+
+ HitMenuItem *hit_item = hitToHitMenuItem (category, hit);
+
+ if (!hit_item)
+ continue;
+
+ kickoffSearchInterface()->addHitMenuItem(hit_item);
+ }
+
+ kickoffSearchInterface()->updateCategoryTitles();
+
+ delete result;
+}
+
+TQDateTime KickoffBeaglePlugin::datetimeFromString( const TQString& s)
+{
+ int year( s.mid( 0, 4 ).toInt() );
+ int month( s.mid( 4, 2 ).toInt() );
+ int day( s.mid( 6, 2 ).toInt() );
+ int hour( s.mid( 8, 2 ).toInt() );
+ int min( s.mid( 10, 2 ).toInt() );
+ int sec( s.mid( 12, 2 ).toInt() );
+ return TQDateTime(TQDate(year,month,day),TQTime(hour,min,sec));
+}
+
+#include "kickoff-beagle-plugin.moc"