You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/kicker/kicker/plugins/kickoff-beagle-plugin.cpp

500 lines
17 KiB

/***************************************************************************
* 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 <tdeapplication.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 = TDEApplication::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-message-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(TDEGlobal::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"