/***************************************************************************
 *   Copyright (C) 2005 Novell, Inc.                                       *
 *                                                                         *
 *   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 "kerryapp.h"
#include "searchdlg.h"
#include "hitwidget.h"
#include "kwidgetlistbox.h"
#include "kerrylabel.h"

#include <tqlayout.h>
#include <tqtextcodec.h>
#include <kdebug.h>
#include <tdefiledialog.h>
#include <kpushbutton.h>
#include <tdemessagebox.h>
#include <kstandarddirs.h>
#include <tdeapplication.h>
#include <tdefileitem.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <klineedit.h>
#include <kurllabel.h>
#include <krun.h>
#include <krfcdate.h>
#include <tqtable.h>
#include <tqdir.h>
#include <tqlabel.h>
#include <tqcombobox.h>
#include <tdeio/job.h>
#include <kprotocolinfo.h>
#include <ktrader.h>
#include <kprocess.h>
#include <tqregexp.h>
#include <tdeaction.h>
#include <tdepopupmenu.h>
#include <dcopclient.h>
#include <tqcheckbox.h>
#include <kdesktopfile.h>
#include <tqtoolbutton.h>
#include <kcombobox.h>
#include <kservicegroup.h>
#include <kbookmarkmanager.h>
#include <tqcursor.h>

#include <twin.h>
#include <twinmodule.h>

SearchDlg::SearchDlg(TQWidget *parent, const char *name)
 : DCOPObject("search"), HitsLayout(parent, name)
{
#define switchLabelCount 19
static TQLabel* switchLabel[switchLabelCount] = {showEverything, showApplications, showContacts, showDocuments,
                                  showConversations, showImages, showMedia, showWebPages, showFilePathName, sortByType, sortByDate, sortByName, sortByRelevance, showAnyDate, showToday, showSinceYesterday, showThisWeek, 
                                  showThisMonth, showThisYear};
#if !GLIB_CHECK_VERSION( 2,36,0 )
  g_type_init();
#endif
  beagle_search = NULL;
  editSearch->setMaxCount(MAX_HISTORY_ITEMS);
  editSearch->setDuplicatesEnabled(false);

  KLineEdit *lineedit = new KLineEdit(editSearch);
  lineedit->setClickMessage(i18n( "Applications, Contacts, Conversations, Files and more..." ) );
  editSearch->setLineEdit(lineedit);

  connect(editSearch->lineEdit(), TQT_SIGNAL(returnPressed()), TQT_SLOT(search()));
  connect(editSearch->lineEdit(), TQT_SIGNAL(textChanged(const TQString &)), TQT_SLOT(searchChanged(const TQString &)));
  showMode = Everywhere;
  dateRange = AnyDate;
  showBigTiles = false;

  TQFont f= sortByRelevance->font();
  f.setBold(true);
  TQFontMetrics fm(f);
  int minWidth=0;
  for (int i=0; i<switchLabelCount; ++i)
     minWidth = MAX(minWidth, fm.width(switchLabel[i]->text()));
  frame3->setMinimumWidth(minWidth+25);

  showEverything->installEventFilter(this);
  showApplications->installEventFilter(this);
  showContacts->installEventFilter(this);
  showDocuments->installEventFilter(this);
  showConversations->installEventFilter(this);
  showImages->installEventFilter(this);
  showMedia->installEventFilter(this);
  showWebPages->installEventFilter(this);
  showFilePathName->installEventFilter(this);
  sortByType->installEventFilter(this);
  sortByDate->installEventFilter(this);
  sortByName->installEventFilter(this);
  sortByRelevance->installEventFilter(this);
  showAnyDate->installEventFilter(this);
  showToday->installEventFilter(this);
  showSinceYesterday->installEventFilter(this);
  showThisWeek->installEventFilter(this);
  showThisMonth->installEventFilter(this);
  showThisYear->installEventFilter(this);
  connect(buttonFind, TQT_SIGNAL(clicked()), TQT_SLOT(search()));
  connect(buttonClear, TQT_SIGNAL(clicked()), TQT_SLOT(slotButtonClear()));
  connect(buttonPrevious, TQT_SIGNAL(clicked()), TQT_SLOT(slotPrevious()));
  connect(buttonNext, TQT_SIGNAL(clicked()), TQT_SLOT(slotNext()));
  connect(tableHits, TQT_SIGNAL(contextMenuRequested (int, int, const TQPoint &)), TQT_SLOT(slotContextMenu(int, int, const TQPoint &)));
  buttonFind->setIconSet( BarIconSet( "key_enter", 22 ) );
  buttonConfigure->setGuiItem(KStdGuiItem::configure());
  connect(buttonConfigure, TQT_SIGNAL(clicked()), TQT_SIGNAL(configure()));
  setMouseTracking(true);
  results.setAutoDelete(true);
  results.clear();
  displayed_results.clear();
  displayAmount = 5;
  displayOffset = 0;
  labelStatus->setAlignment(TQt::SingleLine);
  pPreviewJob = 0;
  pPreviewMimeTypes = 0;
  previewItems.clear();
  previewItems.setAutoDelete(true);
  pending_showQuickTips=true;
  updateStatus();
  defaultSortOrder = Modified;
  currentSortOrder = defaultSortOrder;
  kapp->dcopClient()->setDefaultObject( objId() );
  beagleJustStarted = false;
  m_searchPixmap->setPixmap( BarIcon( "edit-find", 32 ) );

  TQTextCodec::setCodecForCStrings(TQTextCodec::codecForName( "utf8" ));
  encodingRegexp = TQRegExp("%[\\dA-F][\\dA-F]");

  bookmarkManager = 0;
  m_addressBook = 0;
  still_searching = false;
}

SearchDlg::~SearchDlg()
{
}

void SearchDlg::showQuickTips()
{
  tableHits->clear();

  HitWidget* item = new HitWidget(TQString(), TQString());
  TQLabel *headerLabel = new TQLabel(item);
  headerLabel->setText(i18n("Quick Tips"));
  item->insertHeaderWidget(0,headerLabel);

  item->setIcon("messagebox_info");
  item->setDescriptionText("<qt>"+i18n("- You can use upper and lower case; search is case-insensitive.<br>"
                           "- To search for optional terms, use OR.  ex: <b>George OR Ringo</b><br>"
                           "- To exclude search terms, use the minus symbol in front, such as <b>-cats</b><br>"
                           "- When searching for a phrase, add quotes. ex: <b>\"There be dragons\"</b><br>"
                           "- Add ext:type to specify a file extension, ex: <b>ext:txt</b> or <b>ext:</b> for none")+"</qt>");
  tableHits->insertItem(item);

  item = new HitWidget(TQString(), TQString());
  headerLabel = new TQLabel(item);
  headerLabel->setText(i18n("Configuration"));
  item->insertHeaderWidget(0,headerLabel);

  item->setIcon("package_settings");
  item->setDescriptionText("<qt>"+i18n("- Choose what folders and resources shall be indexed - or not.<br>"
                           "- Change the sort order and the number of shown results.<br>"
                           "- Define your own shortcuts to invoke the search dialog.")+"</qt>");
  KURLLabel *buttonStart = new KURLLabel(item);
  buttonStart->setPixmap(SmallIcon("application-x-executable"));
  item->insertHitWidget(0,buttonStart);
  connect(buttonStart, TQT_SIGNAL(leftClickedURL()), TQT_SIGNAL(configure()));

  buttonStart = new KURLLabel(item);
  buttonStart->setText(i18n("Open configuration dialog"));
  item->insertHitWidget(1,buttonStart);
  connect(buttonStart, TQT_SIGNAL(leftClickedURL()), TQT_SIGNAL(configure()));

  tableHits->insertItem(item);

  labelStatus->setText("");
}

void SearchDlg::search(const TQString & search)
{
  SearchDlg::search(search, "everything");
}

void SearchDlg::search(const TQString & search, const TQString &scope)
{
#define showSwitchesCount 9
static TQLabel* showSwitches[showSwitchesCount] = {showEverything, showApplications, showContacts, showDocuments,
                                   showConversations, showImages, showMedia, showWebPages, showFilePathName};
static TQString nameSwitches[showSwitchesCount] = {"everything", "applications", "contacts", "documents",
                                  "conversations", "images", "media", "webpages", "path"};

  pending_showQuickTips=false;
  showSearchDialog();

  TQLabel* newShowLabel = showEverything;
  ScopeType newShowMode = Everywhere;
  for (int i=0; i<showSwitchesCount; ++i)
     if (scope == nameSwitches[i]) {
        newShowLabel = showSwitches[i];
        newShowMode = (ScopeType)i;
        break;
     }

  if (showMode!=newShowMode) {
     TQFont f= showSwitches[showMode]->font();
     f.setBold(false);
     showSwitches[showMode]->setFont(f);
     showMode = newShowMode;
     f.setBold(true);
     newShowLabel->setFont(f);
  }
  editSearch->lineEdit()->setText(search);
  this->search();
}

void SearchDlg::searchChanged(const TQString & search)
{
  TQString _search = search;
  buttonFind->setEnabled(_search.replace("*", TQString()).length()>2);
  if (!search.isEmpty() && displayed_results.count()==0 && tableHits->count()==1)
    tableHits->clear();
  if (search.isEmpty() && displayed_results.count()==0 && tableHits->count()==0)
    showQuickTips();
}

void SearchDlg::setDisplayAmount(int amount)
{
    if (amount<1 || displayAmount==amount)
	return;

    buttonPrevious->setShown(amount>1);
    buttonNext->setShown(amount>1);

    displayAmount = amount;
    displayOffset = 0;
    tableHits->setUpdatesEnabled(false);
    fillTableHits();
    tableHits->setUpdatesEnabled(true);
    updateStatus();
}

void SearchDlg::setSortOrder(int order)
{
static TQLabel* sortSwitches[4] = {sortByType, sortByDate, sortByName, sortByRelevance};

    defaultSortOrder = order;

    if (currentSortOrder==order)
        return;

    TQFont f= sortSwitches[currentSortOrder]->font();
    f.setBold(false);
    sortSwitches[currentSortOrder]->setFont(f);
    f.setBold(true);
    sortSwitches[order]->setFont(f);

    currentSortOrder = order;
    if (displayed_results.count())
        sortFilterResults();
}

void SearchDlg::slotButtonClear()
{
    editSearch->clear();
    slotClear();
    showQuickTips();
    currentSortOrder = defaultSortOrder;
}

void SearchDlg::slotClear()
{
    if (beagle_search != NULL) {
	kdDebug () << "Previous client w/id " << beagle_search->id << " running ... stopping it." << endl;
	beagle_search->stopClient ();
    }

    displayOffset = 0;
    stopPreview();
    tableHits->clear();
    displayed_results.clear();
    results.clear();
    updateStatus();
}

void SearchDlg::slotPrevious()
{
    if (displayOffset==0)
      return;

    displayOffset-=displayAmount;
    tableHits->setUpdatesEnabled(false);
    fillTableHits();
    tableHits->setUpdatesEnabled(true);
    updateStatus();
}

void SearchDlg::slotNext()
{
    if (displayAmount==1 || displayOffset+displayAmount>=(int)displayed_results.count())
      return;

    displayOffset+=displayAmount;
    tableHits->setUpdatesEnabled(false);
    fillTableHits();
    tableHits->setUpdatesEnabled(true);
    updateStatus();
}

void SearchDlg::sortFilterResults()
{
    displayOffset = 0;
    stopPreview();
    tableHits->clear();
    displayed_results.clear();
    displayResults(results);
    updateStatus();
}

void SearchDlg::fillTableHits()
{
    stopPreview();
    tableHits->clear();
    previewItems.clear();
    if (displayAmount==1) {
        for(int i = 0; i < (int)displayed_results.count(); ++i) {
	    insertResult(displayed_results.at(i),i);
        }
    }
    else {
      if (displayOffset+displayAmount<=(int)displayed_results.count()) {
        for(int i = displayOffset; i < displayOffset+displayAmount; ++i) {
	    insertResult(displayed_results.at(i),i-displayOffset);
        }
      }
      else
      for(uint i = displayOffset; i < (displayed_results.count() % displayAmount)+displayOffset; ++i) {
	    insertResult(displayed_results.at(i),i-displayOffset);
      }
    }
    if (previewItems.count()) {
      startPreview(previewItems);
    }
}

void SearchDlg::updateStatus()
{
    buttonPrevious->setEnabled(displayOffset>0);
    buttonNext->setEnabled(displayOffset+displayAmount<(int)displayed_results.count());
    labelStatus->setAlignment(TQt::SingleLine);
    const int count = displayed_results.count();
    TQString text;
    if (displayAmount==1)
       text=i18n("<b>%1 results</b> found.").arg(displayed_results.count());
    else if (count==0)
       text=i18n("<qt>No results.</qt>").replace("<qt>",TQString()).replace("</qt>",TQString());
    else if (displayOffset==0)
       text=i18n("Best <b>%1 results of %2</b> shown.").arg(tableHits->count()).arg(displayed_results.count());
    else
       text=i18n("Results <b>%1 through %2 of %3</b> are shown.").arg(displayOffset+1).arg(displayOffset+tableHits->count()).arg(displayed_results.count());

#if 0
    if (still_searching)
       text=text+" <b>"+i18n("(still searching)")+"</b>";
#endif

    labelStatus->setText(text);
}

void SearchDlg::search()
{
    current_query.set(editSearch->lineEdit()->text());
    if (current_query.get().replace("*", TQString()).length()<3)
       return;

    editSearch->addToHistory(current_query.get());

    if (!beagle_util_daemon_is_running()) {
       tableHits->clear();
       HitWidget* item = new HitWidget(TQString(), TQString());
       TQLabel *headerLabel = new TQLabel(item);
       headerLabel->setText(i18n("The query for \"%1\" failed.").arg(current_query.get()));
       item->insertHeaderWidget(0,headerLabel);

       item->setIcon("messagebox_critical");
       item->setDescriptionText("<qt>"+i18n("The likely cause is that the Beagle daemon is not running.")+"</qt>");

       cb_beagleStart = new TQCheckBox(i18n("Automatically start Beagle daemon at login"),item);
       item->insertTextWidget(1,cb_beagleStart);

       KURLLabel *buttonStart = new KURLLabel(item);
       buttonStart->setPixmap(SmallIcon("application-x-executable"));
       item->insertHitWidget(0,buttonStart);
       connect(buttonStart, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotStartBeagle()));

       buttonStart = new KURLLabel(item);
       buttonStart->setText(i18n("Click to start the Beagle daemon"));
       item->insertHitWidget(1,buttonStart);
       connect(buttonStart, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotStartBeagle()));

       tableHits->insertItem(item);
       labelStatus->setText("");
       return;
    }

    slotClear();
    labelStatus->setText(i18n("Searching..."));

    // Beagle search
    if (beagle_search != NULL) {
	kdDebug () << "Previous client w/id " << beagle_search->id << " still running ... ignoring it." << endl;
	beagle_search->stopClient ();
    }

    current_beagle_client_id = TDEApplication::random ();
    kdDebug () << "Creating client with id:" << current_beagle_client_id << endl;

    m_searchPixmap->setMovie(TQMovie(locate( "appdata", "search-running.mng" )));

    new_results.clear();

    kdDebug() << "searchProgramList" << endl;
    searchProgramList(TQString());

    kdDebug() << "searchBookmars" << endl;
    if (!bookmarkManager)
      bookmarkManager = KBookmarkManager::userBookmarksManager();
    searchBookmarks(bookmarkManager->root());

    kdDebug() << "searchAddressBook" << endl;
    searchAddressbook();

    displayResults(new_results);

    kdDebug() << "starting BeagleSearch" << endl;
    beagle_search = new BeagleSearch(current_beagle_client_id, TQT_TQOBJECT(this), current_query.get());
    beagle_search->start();
    still_searching = true;
}

TQString SearchDlg::takeProperty( const TQString& property, TQStringList& propertyList )
{
      TQString ret = TQString();
      BeagleSearch::PropertyList::iterator it;
      for ( it = propertyList.begin(); it != propertyList.end(); ++it ) {
         const TQString search = property+'=';
         if ((*it).startsWith(search)) {
           ret = (*it).remove(search);
           propertyList.erase(it);
           break;
         }
      }
      return ret;
}

TQDateTime SearchDlg::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));
}

void SearchDlg::slotCleanClientList ()
{
    toclean_list_mutex.lock ();
    BeagleSearch *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 SearchDlg::customEvent (TQCustomEvent *e)
{
    if (e->type () == RESULTFOUND) {
        BeagleSearch::BeagleResultList* items = (BeagleSearch::BeagleResultList*) e->data();
        if (items->count() == 0 || current_beagle_client_id != items->first()->client_id) {
            kdDebug () << "Stale result" << endl;
	    delete items;
        } else {
            kdDebug () << "Good results ...total=" << items->count() << endl;
            searchHasOutput(*items);
        }
    } else if (e->type () == RESULTGONE) {
        BeagleSearch::BeagleVanishedURIList* items = (BeagleSearch::BeagleVanishedURIList*) e->data();
        if (items->list.count() == 0 || current_beagle_client_id != items->client_id) {
            kdDebug () << "Stale leaving from " << items->client_id << endl;
	    delete items;
        } else {
            kdDebug () << "Good leavings ...total=" << items->list.count() << endl;
            searchLostOutput(items->list);
        }
    } else if (e->type () == SEARCHOVER) {
        BeagleSearch *client = (BeagleSearch *) e->data ();
	if (client == NULL) {
	    kdDebug () << "Query finished but client is already deleted" << endl;
	    searchFinished();
	    return;
	}
//        kdDebug () << "Query finished for id=" << client->id << endl;
	if (current_beagle_client_id == client->id) {
	    searchFinished();
	}
    } else if (e->type () == KILLME) {
        BeagleSearch *client = (BeagleSearch *) e->data ();
        if (client==beagle_search)
            beagle_search = NULL;

	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 (slotCleanClientList ()));
	}
    }
}

void SearchDlg::insertResult(BeagleSearch::beagle_result_struct *result,int index)
{
      KURL url(*(result->uri));

      HitWidget* item = new HitWidget(*(result->uri),*(result->mime_type),tableHits);
      item->setCollapsible(result);
      item->setCollapsed(!result->show_expanded);
      connect(item, TQT_SIGNAL(uncollapsed(HitWidget*)), TQT_SLOT(itemUncollapsed(HitWidget*)));

      item->icon->setURL(*(result->uri));
      connect(item->icon, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));

      KURLLabel *buttonGo = new KerryLabel(item);
      buttonGo->setPixmap(SmallIcon( *(result->mime_type)=="application/x-desktop" ? "application-x-executable" : "document-open") );
      buttonGo->setURL(*(result->uri));
      item->insertHitWidget(0,buttonGo);
      connect(buttonGo, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));

      buttonGo = new KerryLabel(item);
      buttonGo->setText( *(result->mime_type)=="application/x-desktop" ? i18n("Run") : i18n("Open") );
      buttonGo->setURL(*(result->uri));
      item->insertHitWidget(1,buttonGo);
      connect(buttonGo, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));

      if (result->tilegroup == BeagleSearch::Website) {
        item->setIcon("network");

        TQString description = "<qt>";
        if (result->hit_type!="Google") {
          TQDateTime datetime;
          datetime.setTime_t(result->last_index_time);
          if (datetime.date().year()>1970)
             description = description + i18n("Last viewed: %1").arg(TDEGlobal::locale()->formatDateTime(datetime,false))+"<br>";
        }

        item->setDescriptionText(description +i18n("URL:")+" "+*(result->uri)+"</qt>");

        if (result->snippet)
            item->setPropertiesText("<qt>"+*(result->snippet)+"</qt>");

        KerryLabel *headerFileLabel = new KerryLabel(item);
        TQStringList _properties(result->properties);
        TQString title = takeProperty("dc:title",_properties);
        if (title.isEmpty())
           title = takeProperty("Title",_properties);

        headerFileLabel->setText(title.isEmpty() ? i18n("Untitled Page") : title);
        headerFileLabel->setAlignment(headerFileLabel->alignment() | TQt::SingleLine);
        headerFileLabel->setURL(*(result->uri));
        item->insertHeaderWidget(0,headerFileLabel);
        connect(headerFileLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));
      }
      else if (result->tilegroup == BeagleSearch::Feed) {
        item->setIcon("network");

        TQDateTime datetime;
        datetime.setTime_t(result->last_index_time);
        item->setDescriptionText("<qt>"+ i18n("Published: %1").arg(TDEGlobal::locale()->formatDateTime(datetime,false))+"<br>");

        if (result->snippet)
          item->setPropertiesText("<qt>"+*(result->snippet)+"</qt>");

        TQLabel *headerLabel = new TQLabel(item);
        headerLabel->setText(i18n("Weblog:"));
        headerLabel->setAlignment(headerLabel->alignment() | TQt::SingleLine);
        item->insertHeaderWidget(0,headerLabel);

        TQStringList _properties(result->properties);
        TQString title = takeProperty("dc:title",_properties);
        TQString identifier = takeProperty("dc:identifier", _properties);
        item->setUri(identifier);
        buttonGo->setURL(identifier);

        KerryLabel *headerFileLabel = new KerryLabel(item);
        headerFileLabel->setText(title.isEmpty() ? i18n("Untitled Entry") : title);
        headerFileLabel->setAlignment(headerFileLabel->alignment() | TQt::SingleLine);
        headerFileLabel->setURL(identifier);
        item->insertHeaderWidget(1,headerFileLabel);
        connect(headerFileLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));
      }
      else if ((*(result->uri)).startsWith("knotes:/")) {
        item->setIcon("knotes");

        TQDateTime datetime;
        datetime.setTime_t(result->last_index_time);
        item->setDescriptionText("<qt>"+ i18n("Last modified: %1").arg(TDEGlobal::locale()->formatDateTime(datetime,false))+"<br>");

        if (result->snippet)
          item->setPropertiesText("<qt>"+*(result->snippet)+"</qt>");

        TQStringList _properties(result->properties);
        TQString title = takeProperty("dc:title",_properties);

        KerryLabel *headerNameLabel = new KerryLabel(item);
        headerNameLabel->setText(title.isEmpty() ? i18n("Untitled") : title);
        headerNameLabel->setURL(item->uri());
        item->insertHeaderWidget(0,headerNameLabel);
        connect(headerNameLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));
      }
      else if (result->tilegroup == BeagleSearch::Note) {
        item->setIcon("contents2");
        TQDateTime datetime;
        datetime.setTime_t(result->last_index_time);
        item->setDescriptionText("<qt>"+ i18n("Last modified: %1").arg(TDEGlobal::locale()->formatDateTime(datetime,false))+"<br>");


        if (result->snippet)
          item->setPropertiesText("<qt>"+*(result->snippet)+"</qt>");

        TQStringList _properties(result->properties);
        TQString title = takeProperty("dc:title",_properties);

        KerryLabel *headerNoteLabel = new KerryLabel(item);
        headerNoteLabel->setText(title.isEmpty() ? i18n("Untitled Entry") : title);
        headerNoteLabel->setAlignment(headerNoteLabel->alignment() | TQt::SingleLine);
        headerNoteLabel->setURL(item->uri());
        item->insertHeaderWidget(0,headerNoteLabel);
        connect(headerNoteLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));
      }
      else if ((*(result->uri)).startsWith("calendar:/")) {
        item->setIcon("ximian-evolution-calendar");

        TQStringList _properties(result->properties);
        TQString summary = takeProperty("fixme:summary",_properties);

        TQString properties;
        TQDateTime datetime;
        datetime = datetimeFromString(takeProperty("fixme:starttime",_properties));
        properties = properties + i18n("Start time: %1").arg(TDEGlobal::locale()->formatDateTime(datetime,false)) + "<br>";
        datetime = datetimeFromString(takeProperty("fixme:endtime",_properties));
        properties = properties + i18n("End time: %1").arg(TDEGlobal::locale()->formatDateTime(datetime,false)) + "<br>";

        BeagleSearch::PropertyList::iterator it;
        for ( it = _properties.begin(); it != _properties.end(); ++it )
                properties=properties+(*it);

        item->setDescriptionText("<qt>"+properties+"</qt>");

        KerryLabel *headerSummaryLabel = new KerryLabel(item);
        headerSummaryLabel->setText(summary.isEmpty() ? i18n("No Summary Specified") : summary);
        headerSummaryLabel->setURL(item->uri());
        item->insertHeaderWidget(0,headerSummaryLabel);
        connect(headerSummaryLabel, TQT_SIGNAL(leftClickedURL(const TQString&)), TQT_SLOT(slotOpenEvolution(const TQString&)));
      }
      else if ((*(result->uri)).startsWith("contacts:/")) {
        item->setIcon("ximian-evolution-addressbook");

        TQStringList _properties(result->properties);
        TQString name = takeProperty("fixme:Name",_properties);
        TQString email = takeProperty("fixme:Email1",_properties);

        TQString properties;
        BeagleSearch::PropertyList::iterator it;
        for ( it = _properties.begin(); it != _properties.end(); ++it )
                properties=properties+(*it);

        if (!properties.isEmpty())
           item->setDescriptionText("<qt>"+properties+"</qt>");

        KerryLabel *headerNameLabel = new KerryLabel(item);
        headerNameLabel->setText(name.isEmpty() ? i18n("No Name Known") : name);
        headerNameLabel->setURL(item->uri());
        item->insertHeaderWidget(0,headerNameLabel);
        connect(headerNameLabel, TQT_SIGNAL(leftClickedURL(const TQString&)), TQT_SLOT(slotOpenEvolution(const TQString&)));

        TQLabel *headerLabel = new TQLabel(item);
        headerLabel->setText(i18n(","));
        item->insertHeaderWidget(1,headerLabel);

        KerryLabel *headerEmailLabel = new KerryLabel(item);
        headerEmailLabel->setText(email);
        headerEmailLabel->setURL(email);
        item->insertHeaderWidget(2,headerEmailLabel);
        connect(headerEmailLabel, TQT_SIGNAL(leftClickedURL(const TQString&)), TQT_SLOT(slotMailTo(const TQString&)));
      }
      else if ((*(result->uri)).startsWith("tdeabc:/")) {
        item->setIcon("kaddressbook");

        TQStringList _properties(result->properties);
        TQString name = takeProperty("vCard:FN",_properties);
        TQString email = takeProperty("vCard:EMAIL",_properties);

        TQString properties;
        BeagleSearch::PropertyList::iterator it;
        for ( it = _properties.begin(); it != _properties.end(); ++it )
                properties=properties+(*it);

        if (!m_addressBook)
           m_addressBook = TDEABC::StdAddressBook::self( false );

        TDEABC::Addressee addresseee = m_addressBook->findByUid( item->uri().mid(8,10) );
        TQString number;

        number = addresseee.phoneNumber(TDEABC::PhoneNumber::Work).number();
        if (!number.isEmpty())
           properties=properties+i18n("Business phone: %1").arg(number)+"<br>";

        number = addresseee.phoneNumber(TDEABC::PhoneNumber::Cell).number();
        if (!number.isEmpty())
           properties=properties+i18n("Mobile phone: %1").arg(number)+"<br>";

        number = addresseee.phoneNumber(TDEABC::PhoneNumber::Home).number();
        if (!number.isEmpty())
           properties=properties+i18n("Home phone: %1").arg(number)+"<br>";

        if (!properties.isEmpty())
           item->setDescriptionText("<qt>"+properties+"</qt>");

        KerryLabel *headerNameLabel = new KerryLabel(item);
        headerNameLabel->setText(name.isEmpty() ? i18n("No Name Known") : name);
        headerNameLabel->setURL(item->uri());
        item->insertHeaderWidget(0,headerNameLabel);
        connect(headerNameLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));

        if (!email.isEmpty()) {
        	TQLabel *headerLabel = new TQLabel(item);
        	headerLabel->setText(i18n(","));
        	item->insertHeaderWidget(1,headerLabel);
		
        	KerryLabel *headerEmailLabel = new KerryLabel(item);
        	headerEmailLabel->setText(email);
        	headerEmailLabel->setURL(email);
        	item->insertHeaderWidget(2,headerEmailLabel);
        	connect(headerEmailLabel, TQT_SIGNAL(leftClickedURL(const TQString&)), TQT_SLOT(slotMailTo(const TQString&)));
        }
      }
      else if (*(result->mime_type)=="message/rfc822" || (*(result->uri)).startsWith("email:/") ) {
        item->setIcon("mail_generic");

        TQStringList _properties(result->properties);
        TQString subject = takeProperty("dc:title",_properties);
        TQString from = takeProperty("fixme:from",_properties);
        TQString received = takeProperty("fixme:date",_properties);

        TQDateTime received_datetime;
        received_datetime = datetimeFromString(received);
        if (!received.isEmpty())
          item->setDescriptionText("<qt>"+i18n("Received: %1").arg(TDEGlobal::locale()->formatDateTime(received_datetime,false))+"</qt>");

        TQString properties;
        BeagleSearch::PropertyList::iterator it;
        for ( it = _properties.begin(); it != _properties.end(); ++it )
                properties=properties+(*it);

        if (result->snippet) {
          if (!properties.isEmpty())
             properties=properties+"<br>";
          properties=properties+*(result->snippet);
        }

        if (!properties.isEmpty())
            item->setPropertiesText("<qt>"+properties+"</qt>");

        KerryLabel *headerSubjectLabel = new KerryLabel(item);
        headerSubjectLabel->setText(subject.isEmpty() ? i18n("No Subject") : subject);
        item->insertHeaderWidget(0,headerSubjectLabel);
        headerSubjectLabel->setURL(*(result->uri));
        connect(headerSubjectLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));

        TQLabel *headerLabel = new TQLabel(item);
        headerLabel->setText(i18n("From"));
        headerLabel->setAlignment(headerLabel->alignment() | TQt::SingleLine);
        item->insertHeaderWidget(1,headerLabel);

        KerryLabel *headerFromLabel = new KerryLabel(item);
        headerFromLabel->setText(from.isEmpty() ? i18n("Unknown Person") : from);
        headerFromLabel->setURL(from);
        item->insertHeaderWidget(2,headerFromLabel);
        connect(headerFromLabel, TQT_SIGNAL(leftClickedURL(const TQString&)), TQT_SLOT(slotMailTo(const TQString&)));
      }
      else if (*(result->mime_type)=="application/x-desktop") {
        KFileItem *fileitem=new KFileItem(*(result->uri),*(result->mime_type),KFileItem::Unknown);
        item->setIcon(fileitem->iconName());
        if (!item->isCollapsed() && canPreview(fileitem))
            previewItems.append(fileitem);
        else
            delete fileitem;

        KDesktopFile desktopfile(url.path(),true);
        if (!desktopfile.readGenericName().isNull()) {
	    item->setDescriptionText("<qt>"+desktopfile.readGenericName()+"</qt>");
        }

        TQLabel *headerLabel = new TQLabel(item);
        headerLabel->setText(i18n("Application:"));
        headerLabel->setAlignment(headerLabel->alignment() | TQt::SingleLine);
        item->insertHeaderWidget(0,headerLabel);

        KerryLabel *headerFileLabel = new KerryLabel(item);
        TQStringList _properties(result->properties);
        TQString title = takeProperty("fixme:Name",_properties);
        headerFileLabel->setText(title.isEmpty() ? desktopfile.readName() : title);
        headerFileLabel->setURL(*(result->uri));
        item->insertHeaderWidget(1,headerFileLabel);
        connect(headerFileLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));
      }
      else if (*(result->mime_type)=="beagle/x-kopete-log" || *(result->mime_type)=="beagle/x-gaim-log") {
        if (*(result->mime_type)=="beagle/x-kopete-log")
          item->setIcon("kopete");
        else
          item->setIcon("gaim");

        TQDateTime datetime;
        datetime.setTime_t(result->last_index_time);
        item->setDescriptionText("<qt>"+i18n("Date: %1").arg(TDEGlobal::locale()->formatDateTime(datetime,false)+"</qt>"));

        if (result->snippet)
            item->setPropertiesText("<qt>"+*(result->snippet)+ "</qt>");

        KerryLabel *headerFileLabel = new KerryLabel(item);
        headerFileLabel->setURL(*(result->uri));
        TQStringList _properties(result->properties);
        TQString person = takeProperty("fixme:speakingto",_properties);
        headerFileLabel->setText(i18n("Conversation With %1").arg(person.isEmpty() ? i18n("Unknown Person") : person));
        item->insertHeaderWidget(0,headerFileLabel);
        connect(headerFileLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));
      }
      else if (result->tilegroup == BeagleSearch::Packages) {
        TQString icon;
        if (*(result->mime_type)=="application/x-rpm")
	    icon = "application-x-rpm";
	else if (*(result->mime_type)=="application/x-deb")
	    icon = "application-x-deb";
	else
	    icon = "kuroo";
        item->setIcon(icon);
    
        KerryLabel *headerFileLabel = new KerryLabel(item);
        headerFileLabel->setText(url.fileName());
        headerFileLabel->setTipText(url.prettyURL());
        headerFileLabel->setUseTips();
        headerFileLabel->setURL(*(result->uri));
        item->insertHeaderWidget(0,headerFileLabel);
        connect(headerFileLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));
    
        TQStringList _properties(result->properties);
        TQString homepage = takeProperty("dc:source",_properties);
	if (!homepage.isEmpty())
	{
		TQLabel *headerLabel = new TQLabel(item);
		headerLabel->setText(i18n("From"));
		headerLabel->setAlignment(headerLabel->alignment() | TQt::SingleLine);
		item->insertHeaderWidget(1,headerLabel);
    
		KerryLabel *headerFileLabel = new KerryLabel(item);
		headerFileLabel->setText(homepage);
		headerFileLabel->setAlignment(headerFileLabel->alignment() | TQt::SingleLine);
		headerFileLabel->setURL(homepage);
		item->insertHeaderWidget(2,headerFileLabel);
		connect(headerFileLabel, TQT_SIGNAL(leftClickedURL(const TQString&)), TQT_SLOT(slotOpenURL(const TQString&)));
	}

        item->setDescriptionText(takeProperty("dc:subject", _properties));
    
        TQString properties;
	TQString install_time_str = takeProperty("fixme:install_time", _properties);
	if (!install_time_str.isEmpty())
	{
            properties=properties+i18n("Installed on: %1").arg(TDEGlobal::locale()->formatDateTime(datetimeFromString(install_time_str),false))+"<br>";
	}

	TQString install_size_str = takeProperty("fixme:contents_byte_count", _properties);
	if (!install_size_str.isEmpty())
	{
            properties=properties+i18n("Installed size: %1").arg(formatBytes(install_size_str))+"<br>";
	}

	TQString download_size_str = takeProperty("fixme:size", _properties);
	if (!download_size_str.isEmpty())
	{
            properties=properties+i18n("Download size: %1").arg(formatBytes(download_size_str))+"<br>";
	}
		
        BeagleSearch::PropertyList::iterator it;
        for ( it = _properties.begin(); it != _properties.end(); ++it )
	{
                properties=properties+(*it);
	}

        if (result->snippet) {
          if (!properties.isEmpty())
             properties=properties+"<br>";
          properties=properties+*(result->snippet);
        }
   
        if (!properties.isEmpty())
            item->setPropertiesText("<qt>"+properties+"</qt>");
    

        item->insertHitSpacing(2,10);
        buttonGo = new KURLLabel(item);
        buttonGo->setPixmap(SmallIcon("kfm"));
        item->insertHitWidget(3,buttonGo);
        connect(buttonGo, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpenDir()));
    
        buttonGo = new KURLLabel(item);
        buttonGo->setText(i18n("Reveal in File Manager"));
        item->insertHitWidget(4,buttonGo);
        connect(buttonGo, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpenDir()));
      }
      else if ((*(result->mime_type)).startsWith("audio/")) {
        KFileItem *fileitem=new KFileItem(*(result->uri),*(result->mime_type),KFileItem::Unknown);
        item->setIcon(fileitem->iconName());
        if (!item->isCollapsed() && canPreview(fileitem))
            previewItems.append(fileitem);
        else
            delete fileitem;
    
        TQStringList _properties(result->properties);
        TQString title = takeProperty("fixme:title",_properties);

        KerryLabel *headerFileLabel = new KerryLabel(item);
        if (title.isEmpty())
            headerFileLabel->setText(url.fileName());
        else
            headerFileLabel->setText(title);
        
        headerFileLabel->setTipText(url.prettyURL());
        headerFileLabel->setUseTips();
        headerFileLabel->setURL(*(result->uri));
        item->insertHeaderWidget(0,headerFileLabel);
        connect(headerFileLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));
    
        TQLabel *headerLabel = new TQLabel(item);
        headerLabel->setText(i18n("In Folder"));
        headerLabel->setAlignment(headerLabel->alignment() | TQt::SingleLine);
        item->insertHeaderWidget(1,headerLabel);

        KerryLabel *headerDirLabel = new KerryLabel(item);
        TQString directory=url.directory();
        int i = directory.findRev('/');
        if (i>0)
            directory=directory.mid(i+1);
        headerDirLabel->setText(directory);
        headerDirLabel->setURL(url.directory());
        headerDirLabel->setTipText(url.directory());
        headerDirLabel->setUseTips();
        item->insertHeaderWidget(2,headerDirLabel);
        connect(headerDirLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpenDir()));

        TQDateTime datetime;
        datetime.setTime_t(result->last_index_time);
        title = i18n("Last modified: %1").arg(TDEGlobal::locale()->formatDateTime(datetime,false));
        item->setDescriptionText("<qt>"+title+"</qt>");

        TQString properties;
        BeagleSearch::PropertyList::iterator it;
        for ( it = _properties.begin(); it != _properties.end(); ++it )
                properties=properties+(*it);

        if (result->snippet) {
          if (!properties.isEmpty())
             properties=properties+"<br>";
          properties=properties+*(result->snippet);
        }

        if (!properties.isEmpty())
            item->setPropertiesText("<qt>"+properties+"</qt>");

        item->insertHitSpacing(2,10);
        buttonGo = new KURLLabel(item);
        buttonGo->setPixmap(SmallIcon("kfm"));
        item->insertHitWidget(3,buttonGo);
        connect(buttonGo, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpenDir()));

        buttonGo = new KURLLabel(item);
        buttonGo->setText(i18n("Reveal in File Manager"));
        item->insertHitWidget(4,buttonGo);
        connect(buttonGo, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpenDir()));
      }
      else {
        KFileItem *fileitem=new KFileItem(*(result->uri),*(result->mime_type),KFileItem::Unknown);
        item->setIcon(fileitem->iconName());
        if (!item->isCollapsed() && canPreview(fileitem))
            previewItems.append(fileitem);
        else
            delete fileitem;
    
        KerryLabel *headerFileLabel = new KerryLabel(item);
        headerFileLabel->setText(url.fileName());
        headerFileLabel->setTipText(url.prettyURL());
        headerFileLabel->setUseTips();
        headerFileLabel->setURL(*(result->uri));
        item->insertHeaderWidget(0,headerFileLabel);
        connect(headerFileLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpen()));
    
        TQLabel *headerLabel = new TQLabel(item);
        headerLabel->setText(i18n("In Folder"));
        headerLabel->setAlignment(headerLabel->alignment() | TQt::SingleLine);
        item->insertHeaderWidget(1,headerLabel);
    
        KerryLabel *headerDirLabel = new KerryLabel(item);
        TQString directory=url.directory();
        int i = directory.findRev('/');
        if (i>0)
            directory=directory.mid(i+1);
        headerDirLabel->setText(directory);
        headerDirLabel->setURL(url.directory());
        headerDirLabel->setTipText(url.directory());
        headerDirLabel->setUseTips();
        item->insertHeaderWidget(2,headerDirLabel);
        connect(headerDirLabel, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpenDir()));


        TQString title;
        if (*(result->mime_type)=="inode/directory") {
            int count = TQDir(url.path()).count() -2 ;
            if (count==0)
              title=i18n("Empty");
            else
              title=i18n("Contains 1 item","Contains %n items",count);
        }
        else {
            TQDateTime datetime;
            datetime.setTime_t(result->last_index_time);
            title = i18n("Last modified: %1").arg(TDEGlobal::locale()->formatDateTime(datetime,false));
        }
        item->setDescriptionText("<qt>"+title+"</qt>");
    
        TQString properties;
        BeagleSearch::PropertyList::iterator it;
        for ( it = result->properties.begin(); it != result->properties.end(); ++it )
                properties=properties+(*it);

        if (result->snippet) {
          if (!properties.isEmpty())
             properties=properties+"<br>";
          properties=properties+*(result->snippet);
        }
   
        if (!properties.isEmpty())
            item->setPropertiesText("<qt>"+properties+"</qt>");
    

        item->insertHitSpacing(2,10);
        buttonGo = new KURLLabel(item);
        buttonGo->setPixmap(SmallIcon("kfm"));
        item->insertHitWidget(3,buttonGo);
        connect(buttonGo, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpenDir()));
    
        buttonGo = new KURLLabel(item);
        buttonGo->setText(i18n("Reveal in File Manager"));
        item->insertHitWidget(4,buttonGo);
        connect(buttonGo, TQT_SIGNAL(leftClickedURL()), TQT_SLOT(slotOpenDir()));
      }

      item->score->setText(i18n("<p align=\"center\">Score: %1</p>").arg(result->score,0,'f',1));
    
      tableHits->insertItem(item,index);
}

void SearchDlg::itemUncollapsed(HitWidget* item)
{
   KFileItem *fileitem=new KFileItem(item->uri(),item->mimetype(),KFileItem::Unknown);
   if (canPreview(fileitem))
       previewItems.append(fileitem);
   startPreview(previewItems);
}

void SearchDlg::searchHasOutput(BeagleSearch::BeagleResultList &items)
{
    BeagleSearch::BeagleResultList* searchResults = new BeagleSearch::BeagleResultList;

    for (BeagleSearch::BeagleResultList::ConstIterator it = items.begin(); it != items.end(); ++it) {
      BeagleSearch::beagle_result_struct* result = (*it); 
      if ((*(result->uri)).find(encodingRegexp)>0) {
          KURL kurl(*(result->uri));
          *(result->uri)=kurl.url();
      }
      if (!checkUriInResults(*(result->uri)) && !(*(result->uri)).startsWith("kbookmark:/")) {
         result->show_expanded=showBigTiles;
         results.append(result);
         searchResults->append(result);
      }
    }
    displayResults(*searchResults);
}

void SearchDlg::displayResults(BeagleSearch::BeagleResultList &items)
{
    bool rebuildDisplay = false;
    if (displayAmount==1)
      tableHits->setUpdatesEnabled(false);
    for (BeagleSearch::BeagleResultList::ConstIterator it = items.begin(); it != items.end(); ++it) {
        BeagleSearch::beagle_result_struct *result = *it;

        bool show = false;
        switch (showMode)
        {
	  case Everywhere:
		show = true;
		break;
	  case Applications:
                show = ( result->tilegroup == BeagleSearch::Application );
		break;
          case Contacts:
		show = ( result->tilegroup == BeagleSearch::Contact );
		break;
          case Documents:
		show = ( result->tilegroup == BeagleSearch::Documents );
		break;
          case Conversations:
		show = ( result->tilegroup == BeagleSearch::Conversations );
		break;
          case Images:
		show = ( result->tilegroup == BeagleSearch::Image );
		break;
          case Media:
		show = ( result->tilegroup == BeagleSearch::Audio || result->tilegroup == BeagleSearch::Video);
		break;
          case Website:
		show = ( result->tilegroup == BeagleSearch::Website);
		break;
          case FilePathName:
		show = current_query.matches(*(result->uri));
          default:
		break;
        }

        if (!show)
           continue;

        TQDateTime datetime;
        datetime.setTime_t(result->last_index_time);
        TQDate date = datetime.date();

	show = false;
        switch (dateRange)
        {
           case AnyDate:
		show = true;
		break;
           case Today:
		show = ( datetime.date()==TQDate::currentDate() );
		break;
           case SinceYesterday:
		show = ( datetime.date()>TQDate::currentDate().addDays(-1) );
		break;
           case ThisWeek:
		show = ( datetime.date().year()==TQDate::currentDate().year() &&
			datetime.date().weekNumber()==TQDate::currentDate().weekNumber() );
		break;
           case ThisMonth:
		show = ( datetime.date().year()==TQDate::currentDate().year() &&
			datetime.date().month()==TQDate::currentDate().month() );
                break;
           case ThisYear:
		show = ( datetime.date().year()==TQDate::currentDate().year() );
		break;
	   default:
                break;
        }
        if (!show)
           continue;

        int i = 0;
        for (BeagleSearch::BeagleResultList::ConstIterator result_it = displayed_results.begin(); result_it != displayed_results.end(); ++result_it) {
           bool foundplace = false;
           switch (currentSortOrder)
           {
                case Type:
                        if ( result->tilegroup < (*result_it)->tilegroup || 
                            (result->tilegroup == (*result_it)->tilegroup &&                           (*result->mime_type) < (*(*result_it)->mime_type)) ||
                            (result->tilegroup == (*result_it)->tilegroup &&                           (*result->mime_type) == (*(*result_it)->mime_type) && result->last_index_time >= (*result_it)->last_index_time) ) 
                                foundplace = true;
                        break;
		case Name:
           		if (KURL(*(result->uri)).fileName().lower() < KURL(*((*(result_it))->uri)).fileName().lower())
              			foundplace = true;
			break;
		case Modified:
           		if (result->last_index_time >= (*result_it)->last_index_time)
              			foundplace = true;
			break;
		default:
           		if (result->score >= (*result_it)->score)
              			foundplace = true;
			break;
           }
           if (foundplace)
		break;
           ++i;
        }
        if (displayed_results.count()==0)
          tableHits->clear();
        displayed_results.insert(i, result);
        if (displayAmount==1)
          insertResult(result,i);
        else if (i<=displayOffset+displayAmount)
          rebuildDisplay = true;
    }
    if (displayAmount==1)
      tableHits->setUpdatesEnabled(true);
    else if (rebuildDisplay) {
      tableHits->setUpdatesEnabled(false);
      fillTableHits();
      tableHits->setUpdatesEnabled(true);
    }
    updateStatus();
}

void SearchDlg::slotOpen()
{
    HitWidget* item = static_cast<HitWidget*>(TQT_TQWIDGET(TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(sender()))->parent()));
    if (item) {
      TQString mimetype = item->mimetype();
      if (mimetype=="beagle/x-kopete-log" || mimetype=="beagle/x-gaim-log") {
        TDEProcess *proc = new TDEProcess;
        *proc << "beagle-imlogviewer";
        KURL kuri = KURL(item->uri());
        TQString uri = kuri.path();
        if (mimetype=="beagle/x-kopete-log")
          *proc << "--client" << "kopete" << "--highlight-search" << current_query.get() << uri;
        else
          *proc << "--client" << "gaim" << "--highlight-search" << current_query.get() << uri;
        if (!proc->start()) {
//        KMessageBox::error(0,i18n("Could not start instant message log viewer."));
          if (mimetype=="beagle/x-kopete-log")
            KRun::runURL(uri, "text/plain", false, true);
          else
            KRun::runURL(uri, "text/html", false, true);
          return;
        }
      }
      else if (item->uri().startsWith("calendar:/") || item->uri().startsWith("contacts:/") 
            || item->uri().startsWith("email:/"))
      {
        slotOpenEvolution(item->uri());
      }
      else if (item->uri().startsWith("mailbox:/") && item->uri().find("thunderbird")>0)
      {
        slotOpenThunderbird(item->uri());
      }
      else if (item->uri().startsWith("tdeabc:/") )
      {
        slotOpenKAddressBook(item->uri().mid(8,10));
      }
      else if (item->uri().startsWith("knotes:/") )
      {
        slotOpenKNotes(item->uri().mid(10,21));
      }
      else if (item->uri().startsWith("note:/")) {
        TDEProcess *proc = new TDEProcess;
        *proc << "tomboy";
        *proc << "--open-note" << item->uri() << "--highligh-search" << "\""+current_query.get()+"\"";
        if (!proc->start()) {
          KMessageBox::error(0,i18n("Could not start Tomboy."));
          return;
        }
      }
      else {
        if (mimetype=="beagle/x-konq-cache")
          mimetype = "text/html";
        KRun::runURL(item->uri(), mimetype, false, true);
      }
    }
}

void SearchDlg::slotOpenDir()
{
    HitWidget* item = static_cast<HitWidget*>(TQT_TQWIDGET(TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(sender()))->parent()));
    if (item)
      KRun::runURL(KURL(item->uri()).directory(), "inode/directory", false, true);
}

void SearchDlg::slotMailTo(const TQString &address)
{
    kapp->invokeMailer(address, TQString());
}

void SearchDlg::slotOpenEvolution(const TQString &address)
{
    TDEProcess *proc = new TDEProcess;
    *proc << "evolution";
    *proc << address;
    if (!proc->start()) {
      KMessageBox::error(0,i18n("Could not start Evolution."));
      return;
    }
}

void SearchDlg::slotOpenThunderbird(const TQString &address)
{
    TDEProcess *proc = new TDEProcess;
    *proc << "thunderbird";
    *proc << "-mail";
    *proc << address;
    if (!proc->start()) {
      KMessageBox::error(0,i18n("Could not start Thunderbird."));
      return;
    }
}

void SearchDlg::slotOpenKAddressBook(const TQString &uid)
{
    TDEProcess *proc = new TDEProcess;
    *proc << "kaddressbook";
    *proc << "--uid" << uid;
    if (!proc->start()) {
      KMessageBox::error(0,i18n("Could not start KAddressBook."));
      return;
    }
}

void SearchDlg::slotOpenKNotes(const TQString& noteid)
{
    if (ensureServiceRunning("knotes")) {
       TQByteArray data;
       TQDataStream arg(data, IO_WriteOnly);
       arg << noteid;
       kapp->dcopClient()->send("knotes","KNotesIface","showNote(TQString)", data);
    }
}

void SearchDlg::slotOpenURL(const TQString& url)
{
    (void) new KRun( url, parentWidget());
}

TQString SearchDlg::formatBytes(TQString& bytesStr) const
{
        static double kilobyte = 1024.0;
        static double megabyte = 1024.0*1024.0;
	double bytes = TDEGlobal::locale()->readNumber(bytesStr);
        if (bytes < megabyte)
                return TDEGlobal::locale()->formatNumber(bytes/kilobyte, 2) + "K";
        return TDEGlobal::locale()->formatNumber(bytes/megabyte, 2) + "M";
}

void SearchDlg::slotStartBeagle()
{
    beagleJustStarted = true;
    if (cb_beagleStart->isChecked()) {
      TDEConfig *config = TDEGlobal::config();
      config->setGroup("Beagle");
      config->writeEntry("AutoStart",true);
      config->sync();
    }

    TDEProcess *proc = new TDEProcess;
    *proc << "beagled";
    *proc << "--indexing-delay 2";
    if (!proc->start()) {
      KMessageBox::error(0,i18n("Could not start Beagle daemon."));
      return;
    }
    slotClear();
    TQTimer::singleShot(5000, this, TQT_SLOT(search()));
}

void SearchDlg::searchLostOutput(BeagleSearch::VanishedURIList &items)
{
    bool rebuildDisplay = false;
    for (BeagleSearch::VanishedURIList::ConstIterator it = items.begin(); it != items.end(); ++it) {
      int i;
      for(i = 0; i < (int)displayed_results.count(); ++i)
      {
         BeagleSearch::beagle_result_struct *result = displayed_results.at(i);
         if (*(result->uri)==(*it)) {
           displayed_results.remove(i);
           if (displayed_results.count()==0)
              searchFinished();
           else if (displayAmount==1)
              tableHits->removeItem(i);
           else if (i<=displayOffset+displayAmount) {
             rebuildDisplay = true;
             if (displayOffset>=(int)displayed_results.count())
               displayOffset-=displayAmount;
           }
           break;
        }
      }
      for(i = 0; i < (int)results.count(); ++i)
      {
         BeagleSearch::beagle_result_struct *result = results.at(i);
         if (*(result->uri)==(*it)) {
           results.remove(i);
           break;
        }
      }
    }

    if (rebuildDisplay) {
      tableHits->setUpdatesEnabled(false);
      fillTableHits();
      tableHits->setUpdatesEnabled(true);
    }

    updateStatus();
}

void SearchDlg::searchFinished()
{
    m_searchPixmap->setPixmap( BarIcon( "edit-find", 32 ) );

    still_searching = false;

    if (displayed_results.count()) {
       updateStatus();
       return;
    }

    if (editSearch->lineEdit()->text().isEmpty()) {
       showQuickTips();
       return;
    }

    tableHits->clear();
    HitWidget* item = new HitWidget(TQString(), TQString());
    TQLabel *headerLabel = new TQLabel(item);
    headerLabel->setText(i18n("No results for \"%1\" were found.").arg(current_query.get()));
    item->insertHeaderWidget(0,headerLabel);

    item->setIcon("messagebox_warning");

    TQString text = "<qt>";
    if (showMode)
	text += i18n("- A broader search scope might produce more results.")+"<br>";
    text += i18n("- You should check the spelling of your search words.");
    if (beagleJustStarted) {
        text += "<br>"+i18n("- The Beagle daemon was just started. Please be patient until it finished its indexing.");
        beagleJustStarted = false;
    }
    item->setDescriptionText(text+"</qt>");
    labelStatus->setText("");

    tableHits->insertItem(item);
}

void SearchDlg::searchError(const TQString& error)
{
     kdDebug() << "SearchDlg::searchError() " << error << endl;
}

bool SearchDlg::canPreview( KFileItem* item )
{
    if ( !TDEGlobalSettings::showFilePreview( item->url() ) )
        return false;

    if ( pPreviewMimeTypes == 0L )
        updatePreviewMimeTypes();

    return mimeTypeMatch( item->mimetype(), *( pPreviewMimeTypes ) );
}

void SearchDlg::updatePreviewMimeTypes()
{
    if ( pPreviewMimeTypes == 0L )
        pPreviewMimeTypes = new TQStringList;
    else
        pPreviewMimeTypes->clear();

    // Load the list of plugins to determine which mimetypes are supported
    TDETrader::OfferList plugins = TDETrader::self()->query("ThumbCreator");
    TDETrader::OfferList::ConstIterator it;

    for ( it = plugins.begin(); it != plugins.end(); ++it ) {
        TQStringList mimeTypes = (*it)->property("MimeTypes").toStringList();
        for (TQStringList::ConstIterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
           pPreviewMimeTypes->append(*mt);
    }
}

bool SearchDlg::mimeTypeMatch( const TQString& mimeType, const TQStringList& mimeList ) const
{
    for (TQStringList::ConstIterator mt = mimeList.begin(); mt != mimeList.end(); ++mt)
    {
        if ( mimeType == *mt )
            return true;
        // Support for *mt == "image/*"
        TQString tmp( mimeType );
        if ( (*mt).endsWith("*") && tmp.replace(TQRegExp("/.*"), "/*") == (*mt) )
            return true;
    }
    return false;
}

void SearchDlg::startPreview( const KFileItemList& items )
{
    stopPreview(); // just in case
    int iconSize = TDEGlobal::iconLoader()->currentSize( TDEIcon::Desktop );

    pPreviewJob = TDEIO::filePreview( items, TDEIcon::SizeHuge, TDEIcon::SizeHuge, iconSize,
        true /*m_pSettings->textPreviewIconTransparency()*/, true /* scale */,
        true /* save */, 0); //&(d->previewSettings) );
    connect( pPreviewJob, TQT_SIGNAL( gotPreview( const KFileItem *, const TQPixmap & ) ),
             this, TQT_SLOT( slotPreview( const KFileItem *, const TQPixmap & ) ) );
    connect( pPreviewJob, TQT_SIGNAL( result( TDEIO::Job * ) ),
             this, TQT_SLOT( slotPreviewResult() ) );
}


void SearchDlg::stopPreview()
{
    if (pPreviewJob)
    {
        pPreviewJob->kill();
        pPreviewJob = 0;
    }
}

void SearchDlg::slotPreview(const KFileItem *item, const TQPixmap &pix)
{
  for(uint i=0; i < tableHits->count(); ++i)
  {
    HitWidget* w = static_cast<HitWidget*>(tableHits->item(i));
    if (w && KURL(w->uri()).prettyURL() == item->url().prettyURL() ) {
      w->icon->setPixmap(pix);
      break;
    }
  }
}

void SearchDlg::slotPreviewResult()
{
    pPreviewJob = 0;
    previewItems.clear();
}

void SearchDlg::keyPressEvent(TQKeyEvent *e)
{
    if (e->key()==Key_PageDown && displayAmount!=1) {
      if (e->state()==ControlButton) {
        if (displayOffset+displayAmount>=(int)displayed_results.count())
          return;

        displayOffset=((displayed_results.count() -1) / displayAmount) * displayAmount;
        tableHits->setUpdatesEnabled(false);
        fillTableHits();
        tableHits->setUpdatesEnabled(true);
        updateStatus();
      }
      else
        slotNext();
    }
    else if (e->key()==Key_PageUp && displayAmount!=1) {
      if (e->state()==ControlButton) {
        if (displayOffset==0)
          return;

        displayOffset=0;
        tableHits->setUpdatesEnabled(false);
        fillTableHits();
        tableHits->setUpdatesEnabled(true);
        updateStatus();
      }
      else
        slotPrevious();
    }
    else
      HitsLayout::keyPressEvent(e);
}

void SearchDlg::showEvent(TQShowEvent *e )
{
    HitsLayout::showEvent( e );
    if (pending_showQuickTips) {
      showQuickTips();
      pending_showQuickTips=false;
    }
}

void SearchDlg::slotContextMenu( int /*row*/, int /*col*/, const TQPoint & pos )
{
    TDEPopupMenu *popup = new TDEPopupMenu(this);
    popup->insertTitle(i18n("Collapse") + " / " + i18n("Expand"));
    popup->insertItem(i18n("Collapse All"), 1);
    popup->insertItem(i18n("Expand All"), 2);
    int selected = popup->exec(pos);
    if (selected==1 || selected==2)
      for(uint i=0; i < tableHits->count(); ++i) {
         HitWidget* w = static_cast<HitWidget*>(tableHits->item(i));
         w->setCollapsed(selected==1);
      }
    delete popup;
}


void SearchDlg::showSearchDialog()
{
    show();
    KWin::setOnDesktop( winId(), KWin::currentDesktop() );
    kapp->updateUserTimestamp();
    KWin::forceActiveWindow( winId() );
    editSearch->setFocus();
    editSearch->lineEdit()->selectAll();
}

void SearchDlg::configChanged()
{
    TDEConfig *config = TDEGlobal::config();
    config->reparseConfiguration();
    config->setGroup("General");

    showBigTiles=config->readBoolEntry("ShowBigTiles",false);
    setDisplayAmount(config->readNumEntry("DisplayAmount", 20));
    setSortOrder(config->readNumEntry("DefaultSortOrder",0));
}

void SearchDlg::searchProgramList(TQString relPath)
{
    KServiceGroup::Ptr group = KServiceGroup::group(relPath);
    if (!group || !group->isValid())
      return;

    KServiceGroup::List list = group->entries();
    if (list.isEmpty())
      return;

    KServiceGroup::List::ConstIterator it = list.begin();
    for(; it != list.end(); ++it) {
	KSycocaEntry *e = *it;

	if(e != 0) {
		if(e->isType(KST_KServiceGroup)) {
			KServiceGroup::Ptr g(static_cast<KServiceGroup *>(e));
			if(!g->noDisplay())
				searchProgramList(g->relPath());
		} else if(e->isType(KST_KService)) {
			KService::Ptr s(static_cast<KService *>(e));
			if(s->type() == "Application" && !s->noDisplay() && !checkUriInResults(locate("apps", s->desktopEntryPath()))) {
				if (!current_query.matches(s->name().lower()+' '+s->genericName()+' '+
                                    s->exec()+' '+s->keywords().join(",")+' '+s->comment()+' ' +
                                    group->caption()+' '+s->categories().join(",")))
					continue;

				BeagleSearch::beagle_result_struct *result = new BeagleSearch::beagle_result_struct;
				result->mime_type=new TQString("application/x-desktop");
                                result->tilegroup = BeagleSearch::Application;
				result->uri=new TQString("file://"+locate("apps", s->desktopEntryPath()));
				*(result->properties).append("fixme:Name="+s->name());
				result->tilegroup = BeagleSearch::Application;
				result->score = 0.0;
				result->show_expanded=showBigTiles;
				results.append(result);
				new_results.append(result);
			}
		}
	}
    }
}

bool SearchDlg::checkUriInResults(const TQString& path) const
{
    for (BeagleSearch::BeagleResultList::ConstIterator it = results.begin(); it != results.end(); ++it) {
        BeagleSearch::beagle_result_struct *result = *it;
        if (*(result->uri) == path)
           return true;
    }
    return false;
}

void SearchDlg::searchBookmarks(KBookmarkGroup group)
{
	KBookmark bookmark = group.first();
	while(!bookmark.isNull()) {
		if (bookmark.isGroup()) {
			searchBookmarks(bookmark.toGroup());
		} else if (!bookmark.isSeparator() && !bookmark.isNull()) {
				if (!current_query.matches(bookmark.fullText()+' '+bookmark.url().url())) {
                                        bookmark = group.next(bookmark);
	    				continue;
                                }

                                if (checkUriInResults(bookmark.url().prettyURL())) {
                                        bookmark = group.next(bookmark);
                                        continue;
                                }

				BeagleSearch::beagle_result_struct *result = new BeagleSearch::beagle_result_struct;
				result->mime_type=new TQString("text/html");
				result->uri=new TQString(bookmark.url().prettyURL());
				*(result->properties).append("dc:title="+bookmark.fullText());
				result->tilegroup = BeagleSearch::Website;
				result->score = 0.0;
				result->show_expanded=showBigTiles;
				results.append(result);
				new_results.append(result);
		}
		bookmark = group.next(bookmark);
	}
}

void SearchDlg::searchAddressbook()
{
    if (!m_addressBook)
      m_addressBook = TDEABC::StdAddressBook::self( false );

    TDEABC::AddressBook::ConstIterator it = m_addressBook->begin();
    while (it!=m_addressBook->end()) {
        if (!current_query.matches((*it).assembledName()+' '+(*it).fullEmail())) {
            it++;
            continue;
        }

        if (checkUriInResults("tdeabc:///"+(*it).uid())) {
             it++;
             continue;
        }

        TQString realName = (*it).realName();
        if (realName.isEmpty())
            realName=(*it).preferredEmail();

	BeagleSearch::beagle_result_struct *result = new BeagleSearch::beagle_result_struct;
	result->mime_type=new TQString("text/html");
        result->uri=new TQString("tdeabc:///"+(*it).uid());
	*(result->properties).append("vCard:FN="+realName);
        *(result->properties).append("vCard:EMAIL="+(*it).preferredEmail());
	result->tilegroup = BeagleSearch::Contact;
	result->score = 0.0;
	result->show_expanded=showBigTiles;
	results.append(result);
	new_results.append(result);

       it++;
    }
}


bool SearchDlg::eventFilter(TQObject *obj, TQEvent *e)
{
#define showSwitchesCount 9
static TQLabel* showSwitches[showSwitchesCount] = {showEverything, showApplications, showContacts, showDocuments,
                                   showConversations, showImages, showMedia, showWebPages, showFilePathName};

#define sortSwitchesCount 4
static TQLabel* sortSwitches[sortSwitchesCount] = {sortByType, sortByDate, sortByName, sortByRelevance};

#define dateSwitchesCount 6
static TQLabel* dateSwitches[dateSwitchesCount] = {showAnyDate, showToday, showSinceYesterday, showThisWeek, 
                                  showThisMonth, showThisYear};

    if (e->type()==TQEvent::Enter) {
       TQApplication::setOverrideCursor( TQCursor(TQt::PointingHandCursor) );
    } else if (e->type()==TQEvent::Leave) {
       TQApplication::restoreOverrideCursor();
    } else if (e->type()==TQEvent::MouseButtonPress) {
       for (int i=0; i<showSwitchesCount; ++i)
	  if (TQT_BASE_OBJECT(obj) ==TQT_BASE_OBJECT(showSwitches[i])) {
             if (i!=showMode) {
                TQFont f= showSwitches[showMode]->font();
                f.setBold(false);
                showSwitches[showMode]->setFont(f);
                showMode = (ScopeType)i;
                f.setBold(true);
                showSwitches[showMode]->setFont(f);
                sortFilterResults();
             }
             return true;
          }

       for (int i=0; i<sortSwitchesCount; ++i)
	  if (TQT_BASE_OBJECT(obj) ==TQT_BASE_OBJECT(sortSwitches[i])) {
             setSortOrder(i);
             return true;
          }

       for (int i=0; i<dateSwitchesCount; ++i)
	  if (TQT_BASE_OBJECT(obj) ==TQT_BASE_OBJECT(dateSwitches[i])) {
             if (i!=dateRange) {
                TQFont f= dateSwitches[dateRange]->font();
                f.setBold(false);
                dateSwitches[dateRange]->setFont(f);
                dateRange = (DateRange)i;
                f.setBold(true);
                dateSwitches[dateRange]->setFont(f);
                sortFilterResults();
             }
             return true;
          }
    }

    return false;
}

bool SearchDlg::ensureServiceRunning(const TQString& name)
{
    TQCString appname = name.utf8();
    if (kapp->dcopClient()->isApplicationRegistered(appname)) {
        return true;
    }
    else {
        TQStringList URLs;
        TQByteArray data, replyData;
        TQCString replyType;
        TQDataStream arg(data, IO_WriteOnly);
        arg << name << URLs;

        if ( !kapp->dcopClient()->call( "tdelauncher", "tdelauncher", "start_service_by_desktop_name(TQString,TQStringList)",
                          data, replyType, replyData) ) {
            tqWarning( "call to tdelauncher failed.");
            return false;
        }
        TQDataStream reply(replyData, IO_ReadOnly);

        if ( replyType != "serviceResult" )
        {
            tqWarning( "unexpected result '%s' from tdelauncher.", replyType.data());
            return false;
        }
        int result;
        TQCString dcopName;
        TQString error;
        reply >> result >> dcopName >> error;
        if (result != 0)
        {
            tqWarning("Error starting: %s", error.local8Bit().data());
            return false;
        }
        return true;
    }
}

#include "searchdlg.moc"
