/*
 * Copyright (c) 2000 Rik Hemsley <rik@kde.org>
 *
 * Artistic License - See file LICENSE for details.
 */

#include <qdragobject.h>

#include <kapp.h>
#include <kstddirs.h>
#include <kdebug.h>
#include <kdesktopfile.h>
#include <kglobalsettings.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kmimetype.h>
#include <krun.h>
#include <kservicegroup.h>
#include <ksycocaentry.h>
#include <kservice.h>
#include <kservicegroup.h>

#include "servicemenu.h"
#include "servicemenu.moc"

const int idStart = 4242;

PanelServiceMenu::NameAndSycoca::NameAndSycoca(
  const QString & name,
  KSycocaEntry * entry
)
  : name_(name),
    entry_(entry),
    id_(-1)
{
  // Empty.
}

PanelServiceMenu::PanelServiceMenu(
  const QString & label,
  const QString & relPath,
  QWidget * parent,
  const char * name
)
  : PanelMenu(label, parent, name),
    relPath_(relPath),
    loaded_(false)
{
  merge_ = KGlobal::config()->readBoolEntry("MergeKDEDirs", true);
  serviceList_.setAutoDelete(true);
}

void PanelServiceMenu::initialize()
{
  if (init) return;
  init = true;

  serviceList_.clear();

  // We ask KSycoca to give us all services.

  KServiceGroup::Ptr root = KServiceGroup::group(relPath_);
  KServiceGroup::List list = root->entries();

  if (list.isEmpty()) {
    setItemEnabled(insertItem(i18n("No entries")), false);
    return;
  }

  // First we must populate the lists.
  // We have one list for groups and another for services.
  // They are separate so they can be sorted and inserted separately.

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

    switch (e->sycocaType()) {

      case KST_KService:
        serviceList_.append(new NameAndSycoca(e->name(), e));
        break;

      case KST_KServiceGroup:
        serviceList_.append(
          new NameAndSycoca(static_cast<KServiceGroup *>(e)->caption(), e)
        );
        break;

      default:
        kdDebug() <<
          "Unrecognised type for service `" << (*it)->name() << "'" << endl;
        break;
    }
  }
  
  // First insert groups, so we get groups at the top of the menu.

  int id = idStart;

  serviceList_.sort();

  // Service groups, then services.

  NameAndSycocaListIterator it(serviceList_);

  for (; it.current(); ++it)
  {
    if (!it.current()->entry()->isType(KST_KServiceGroup))
      continue;
    
    KServiceGroup::Ptr g(static_cast<KServiceGroup *>(it.current()->entry()));
   
    PanelServiceMenu * newSubMenu =
      new PanelServiceMenu(g->name(), g->relPath(), this, g->name().utf8());
    
    insertItem(SmallIcon(g->icon()), g->caption(), newSubMenu, id++);
  }

  for (it.toFirst(); it.current(); ++it)
  {
    if (!it.current()->entry()->isType(KST_KService))
      continue;
    
    KService::Ptr s(static_cast<KService *>(it.current()->entry()));
    // check for X-KDE-HideFromPanel
//    if ((s->property("X-KDE-HideFromPanel")).asBool())
//      continue;
    
    it.current()->setId(insertItem(SmallIcon(s->icon()), s->name(), id++));
  }
}

void PanelServiceMenu::slotExec(int id)
{
  kapp->propagateSessionManager();

  NameAndSycoca * ns = 0;

  for (NameAndSycocaListIterator it(serviceList_); it.current(); ++it)
    if (it.current()->id() == id) {
      ns = it.current();
      break;
    }

  if (0 == ns) {
    kdDebug() << "Cannot find service with menu id " << id << endl;
    return;
  }
  
  kdDebug() << "Running `" << ns->entry()->name() << "'" << endl;

  KService::Ptr service = static_cast<KService *>(ns->entry());

  KRun::run(*service, KURL::List());
}

void PanelServiceMenu::mousePressEvent(QMouseEvent * e)
{
  mouseDown_ = e->pos();
  QPopupMenu::mousePressEvent(e);
}

void PanelServiceMenu::mouseMoveEvent(QMouseEvent * e)
{
  PanelMenu::mouseMoveEvent(e);

  if (!(e->state() & LeftButton))
    return;

  if (!rect().contains(mouseDown_))
    return;

  int dragLength = (e->pos() - mouseDown_).manhattanLength();
  
  if (dragLength > KGlobalSettings::dndEventDelay())
  {
    int id = idAt(mouseDown_);

    // Don't drag items we didn't create.
    if (id < idStart)
      return;

    NameAndSycoca * ns = 0;

    for (NameAndSycocaListIterator it(serviceList_); it.current(); ++it)
      if (it.current()->id() == int(id)) {
        ns = it.current();
        break;
      }

    if (0 == ns) {
      kdDebug() << "Cannot find service with menu id " << id << endl;
      return;
    }

    QString iconName, filePath;

    KSycocaEntry * e(ns->entry());

    switch (ns->entry()->sycocaType()) {

      case KST_KService:
        iconName = static_cast<KService *>(e)->icon();
        filePath = static_cast<KService *>(e)->desktopEntryPath();
        break;

      case KST_KServiceGroup:
        iconName = static_cast<KServiceGroup *>(e)->icon();
        filePath = static_cast<KServiceGroup *>(e)->relPath();
        break;

      default:
        return;
        break;
    }

    // If the path to the desktop file is relative, try to get the full
    // path from KStdDirs.

    QString path = (filePath[0] == '/') ? filePath : locate("apps", filePath);

    QPixmap icon(SmallIcon(iconName));

    QPoint hotspot(icon.width() / 2, icon.height() / 2);

    QUriDrag * d(new QUriDrag(this));

    d->setPixmap(icon, hotspot);
    d->setFilenames(QStringList(path));
    d->dragCopy();
  }
}

