#include <tqcheckbox.h>
#include <tqcursor.h>
#include <tqdatetime.h>
#include <tqdir.h>
#include <tdepopupmenu.h>
#include <tqobjectlist.h>
#include <tqpainter.h>
#include <tqptrlist.h>
#include <tqstrlist.h>
#include <tqstringlist.h>
#include <tqvbox.h>
#include <dcopclient.h>
#include <tdeapplication.h>
#include <tdeconfig.h>
#include <kcombobox.h>
#include <tdeversion.h>
#include <kurl.h>
#include <tdefileitem.h>
#include <kicondialog.h>
#include <kiconloader.h>
#include <kkeybutton.h>
#include <tdelistbox.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kpixmapeffect.h>
#include <kprocess.h>
#include <kurifilter.h>
#include <kurl.h>
#include <krun.h>
#include <kservice.h>
#include <tdeshortcut.h>
#include <ksqueezedtextlabel.h>
#include <kstandarddirs.h>
#include <tdesycocaentry.h>
#include <ktextedit.h>
#include <kurlrequester.h>
#include <twin.h>
//WARNING: THIS IS NOT PORTABLE!
// #include <X11/Xlib.h>
// #include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/extensions/XTest.h>
#include <fixx11h.h>
#include <stdlib.h>
// TO HERE --------
//#include "kdrawer.h"
#include "baghiralinkdrag.h"
#include "menu.h"
#include "configdialog.h"
#include "help.h"
#include "linkconfig.h"
#define OPAQUE	0xffffffff
#define OPACITY	"_KDE_WM_WINDOW_OPACITY"

#define _BIGSIZE_(_s_) ((_s_ == 16) ? 22 :\
(_s_ == 22) ? 32 :\
(_s_ == 32) ? 48 :\
(_s_ == 48) ? 64 :\
(_s_ == 64) ? 128 :\
(int)(_s_*1.4))

//TODO: sort functions alphabetically, split files by classes... refactoring sucks ;)

static TQColor commentColor;
static TQColor infoColor;
static TDEConfig *config;
static bool useKTTS;


TQString spell(const TQString text)
{
   TQString result;
   for (uint i = 0; i < text.length(); i++)
   {
      result += " "; result += text[i];
   }
   return result;
}

/*
  Internal class to get access to protected TQBoxLayout-members
*/
class MyVBoxLayout : public TQVBoxLayout
{
   friend class AppList;
public:
   MyVBoxLayout( TQLayout * parentLayout, int spacing = -1, const char * name = 0 )
      : TQVBoxLayout( parentLayout, spacing, name ) {}
};

StartMenuButton::StartMenuButton( int size, TQString icon, TQString title, TQString command, Orientation orientation, TQWidget* parent, const char * name) : TQWidget(parent, name), m_title(title), m_command(command), m_icon(icon), m_orientation(orientation), _moving(false)
{
   setBackgroundOrigin(TQWidget::ParentOrigin);
   int bigSize = _BIGSIZE_(size);
   m_pix = TDEGlobal::iconLoader()->loadIcon(icon, TDEIcon::Desktop, size);
   m_hoverPix = TDEGlobal::iconLoader()->loadIcon(icon, TDEIcon::Desktop, bigSize);
   m_pixmapLabel = new TQLabel(this, name);
   m_pixmapLabel->setPixmap(m_pix);
   m_pixmapLabel->setBackgroundOrigin(TQWidget::AncestorOrigin);
   TQBoxLayout* layout;
   if (orientation == Horizontal)
   {
      m_titleLabel = new TQLabel("<qt><b>" + title + "</b></qt>", this, name);
      m_titleLabel->setBackgroundOrigin(TQWidget::AncestorOrigin);
      m_titleLabel->setTextFormat( TQt::RichText );
      m_titleLabel->setAlignment ( TQt::AlignLeft | TQt::AlignVCenter );
      m_pixmapLabel->setFixedSize(bigSize+2,bigSize+2);
      m_pixmapLabel->setAlignment ( TQt::AlignCenter );
      layout = new TQHBoxLayout ( this );
      layout->addSpacing ( 5 );
      layout->addWidget(m_pixmapLabel,0,TQt::AlignCenter);
      layout->addSpacing ( 2 );
      layout->addWidget(m_titleLabel,1);
      layout->addSpacing ( 5 );
   }
   else if (orientation == Vertical)
   {
      m_titleLabel = new TQLabel("<qt><b>" + title + "</b></qt>", this, name);
      m_titleLabel->setBackgroundOrigin(TQWidget::AncestorOrigin);
      m_titleLabel->setTextFormat( TQt::RichText );
      m_titleLabel->setAlignment ( TQt::AlignHCenter | TQt::AlignTop );
      m_pixmapLabel->setFixedSize(bigSize+2,bigSize+2);
      m_pixmapLabel->setAlignment ( TQt::AlignCenter );
      layout = new TQVBoxLayout ( this );
      layout->addSpacing ( 5 );
      layout->addWidget(m_pixmapLabel,0,TQt::AlignCenter);
      layout->addSpacing ( 2 );
      layout->addWidget(m_titleLabel,1);
      layout->addSpacing ( 5 );
   }
   else
   {
      setFixedSize(bigSize+2,bigSize+2);
      m_pixmapLabel->setAlignment ( TQt::AlignCenter );
      m_pixmapLabel->setFixedSize(bigSize+2,bigSize+2);
   }
   setCursor(TQt::PointingHandCursor);
}

void StartMenuButton::reloadIcon(int size)
{
   int bigSize = _BIGSIZE_(size);
   if (m_orientation == Status)
      setFixedSize(bigSize+2,bigSize+2);
   m_pixmapLabel->setFixedSize(bigSize+2,bigSize+2);
   m_pix = TDEGlobal::iconLoader()->loadIcon(m_icon, TDEIcon::Desktop, size);
   m_hoverPix = TDEGlobal::iconLoader()->loadIcon(m_icon, TDEIcon::Desktop, bigSize);
   m_pixmapLabel->setPixmap(m_pix);
}

void StartMenuButton::smartMove(TQPoint &pt)
{
}

void StartMenuButton::smartMove(int x, int y)
{
   if (!dynamic_cast<Panel*>(parentWidget()))
      return;
   StartMenuButton *bt = 0L;
   TQObjectList *kids = const_cast<TQObjectList*>(parentWidget()->children());
   TQRect dRect(TQPoint(x,y), size());
   TQRect bRect;
   if (kids && !kids->isEmpty())
   {
      TQObject *o;
      for ( o = kids->first(); o; o = kids->next() )
         if ((o != this) && (bt = (dynamic_cast<StartMenuButton*>(o))))
         {
            bRect = TQRect(bt->pos(), bt->size());
            if (dRect.intersects(bRect))
               break;
            bt = 0L;
         }
   }
   if (!bt)
   {
      move(dRect.topLeft());
      int dst;
      if (((Panel*)parentWidget())->orientation() == TQt::Horizontal)
      {
         dst = pos().x() + width() - parentWidget()->width();
         if (dst > 0)
            emit updateSize(1);
         else if (pos().x() < 0)
            emit updateSize(-1);
      }
      else
      {
         dst = pos().y() + height() - parentWidget()->height();
         if (dst > 0)
            emit updateSize(1);
         else if (pos().y() < 0)
            emit updateSize(-1);
      }
      ((Panel*)parentWidget())->ensureVisible(dRect);
   }
   else if (
            // left of center of left
            (dRect.right() > bRect.right() && dRect.x() < bRect.right() - bRect.width() / 2) ||
            // right of center of right
            (dRect.x() < bRect.x() && dRect.right() > bRect.x() + bRect.width() / 2) ||
            // up of center of upper
            (dRect.bottom() > bRect.bottom() && dRect.y() < bRect.bottom() - bRect.height() / 2) ||
            // below center of lower
            (dRect.y() < bRect.y() && dRect.bottom() > bRect.y() + bRect.height() / 2)
           )
   {
      TQPoint bPt = bt->pos();
      bt->move(pos());
      move(bPt);
   }
}

void StartMenuButton::mouseReleaseEvent ( TQMouseEvent * mre)
{
   if (mre->state() & TQt::LeftButton)
   {
      if (_moving) { _moving = false; return; }
      emit pressed(m_command);
      return;
   }
   if (mre->state() & TQt::RightButton)
   {
      if (!dynamic_cast<Panel*>(parentWidget()))
         return;
      ((Panel*)parentWidget())->linkConfigDialog->setCaption ( i18n("Configure Link") );
      ((Panel*)parentWidget())->linkConfigDialog->title->setText(m_title);
      ((Panel*)parentWidget())->linkConfigDialog->command->setText(m_command);
      ((Panel*)parentWidget())->linkConfigDialog->icon->setIcon(m_icon);
      disconnect(((Panel*)parentWidget())->linkConfigDialog->buttonOk, SIGNAL(clicked()), 0, 0);
      connect(((Panel*)parentWidget())->linkConfigDialog->buttonOk, SIGNAL(clicked()), ((Panel*)parentWidget())->linkConfigDialog, SLOT(accept()));
      connect(((Panel*)parentWidget())->linkConfigDialog->buttonOk, SIGNAL(clicked()), this, SLOT(edit()));
      ((Panel*)parentWidget())->linkConfigDialog->exec();
   }
}

void StartMenuButton::edit()
{
   if (!dynamic_cast<Panel*>(parentWidget()))
      return;
   m_command = ((Panel*)parentWidget())->linkConfigDialog->command->text();
   if (m_command.isEmpty())
   {
      deleteLater();
      return;
   }
   m_icon = ((Panel*)parentWidget())->linkConfigDialog->icon->icon();
   m_title = ((Panel*)parentWidget())->linkConfigDialog->title->text();
   int bigSize = _BIGSIZE_(((Panel*)parentWidget())->_size);
   m_pix = TDEGlobal::iconLoader()->loadIcon(m_icon, TDEIcon::Desktop, ((Panel*)parentWidget())->_size);
   m_hoverPix = TDEGlobal::iconLoader()->loadIcon(m_icon, TDEIcon::Desktop, bigSize);
   m_pixmapLabel->setPixmap(m_pix);
   if (m_orientation == Horizontal || m_orientation == Vertical)
      m_titleLabel->setText("<qt><b>" + m_title + "</b></qt>");
   ((Panel*)parentWidget())->linkConfigDialog->close();
}

void Panel::addIcon()
{
   if (!linkConfigDialog->command->text().isEmpty())
      addIcon ( linkConfigDialog->icon->icon(), linkConfigDialog->title->text(), linkConfigDialog->command->text(), iconAddPosition );
}

void StartMenuButton::mouseMoveEvent ( TQMouseEvent * mme )
{
   if (!dynamic_cast<Panel*>(parentWidget()))
      return;
   if (mme->state() & TQt::LeftButton)
   {
      _moving = true;
      TQPoint pt = mapToParent(mme->pos());
      if (!(mme->state() & TQt::ShiftButton))
      {
         if (pt.y() < -5 || pt.y() > parentWidget()->height() + 5 || pt.x() < -5 || pt.x() > parentWidget()->width() + 5)
         {
            BaghiraLinkDrag *d = new BaghiraLinkDrag( m_title, m_command, m_icon, -1, parentWidget() );
            d->setPixmap(m_hoverPix, TQPoint(m_hoverPix.width()/2, m_hoverPix.height()/2));
            d->drag();
            if ((mme->state() & TQt::ControlButton) || BaghiraLinkDrag::accepted())
               return;
            ((Panel*)parentWidget())->poof();
            // do NOT delete d.
            return;
         }
      }
      if (((Panel*)parentWidget())->orientation() == TQt::Horizontal)
         smartMove(pt.x() - width()/2, pos().y());
      else
         smartMove(pos().x(), pt.y() - height()/2);
   }
   else
      _moving = false;
}


void StartMenuButton::enterEvent( TQEvent * )
{
   if (m_orientation == Status) emit hovered(m_title);
   m_pixmapLabel->setPixmap(m_hoverPix);
}

void StartMenuButton::leaveEvent( TQEvent * )
{
   if (m_orientation == Status) emit unhovered();
   m_pixmapLabel->setPixmap(m_pix);
}


StartMenuEntry::StartMenuEntry(KService * service, TQString relPath, int size, bool newbie, TQWidget * parent) : TQWidget(parent)
{
   groupPath = relPath;
   forNewbie = newbie;
   if (config) // might be first use ever...
   {
      TQString tmpString = config->readEntry(service->desktopEntryName());
      if (tmpString != TQString::null)
      {
         usage = tmpString.section ( ' ', 0, 0 ).toUInt();
         lastUse = TQDate::fromString(tmpString.section ( ' ', 1, 1 ), TQt::ISODate);
         // ranking is naiv but hopefully usefull for the beginning: often usage increases rank, time to the last usage decreases. "8" is just a "random" offset - the idea is that apps that have been used within the last week should have a higher rank than apps that don't appear in the list - setting these to - infinity isn't a good idea as well, as they might be brand new
         rank = 8 + usage - lastUse.daysTo(TQDate::currentDate());
      }
      else
      {
         usage = 0;
         rank = 0; // neutral rank
      }
   }
   else
   {
      tqWarning("no valid config!");
      usage = 0;
      rank = 0; // neutral rank
   }
   int bigSize = _BIGSIZE_(size);
   isCurrent = false;
   m_service = service;
   exec = m_service->exec();
   display = false;
   m_pix = m_service->pixmap( TDEIcon::Desktop, size );
   m_hoverPix = m_service->pixmap( TDEIcon::Desktop, bigSize );
   m_titleLabel = new TQLabel("<qt><h3>" + m_service->name() + "</h3></qt>", this);
   m_titleLabel->setTextFormat( TQt::RichText );
   m_commentLabel = new TQLabel(m_service->comment(), this);
   m_commentLabel->setPaletteForegroundColor(commentColor);
   m_commentLabel->setTextFormat( TQt::RichText );
   m_pixmapLabel = new TQLabel(this);
   m_pixmapLabel->setFixedSize ( bigSize+2, bigSize+2 );
   m_pixmapLabel->setAlignment(TQt::AlignCenter);
   m_pixmapLabel->setPixmap(m_pix);
   TQVBoxLayout* spacer = new TQVBoxLayout ( this );
   spacer->addSpacing ( 1 );
   TQHBoxLayout* layout = new TQHBoxLayout ( spacer );
   layout->addWidget(m_pixmapLabel);
   layout->addSpacing ( 2 );
   TQVBoxLayout* textLayout = new TQVBoxLayout ( layout );
   layout->setStretchFactor ( textLayout, 1 );
   textLayout->addWidget(m_titleLabel);
   textLayout->addWidget(m_commentLabel);
   layout->addStretch();
   spacer->addSpacing ( 1 );
   setCursor(TQt::PointingHandCursor);
}

void StartMenuEntry::reloadIcon(int size)
{
   int bigSize = _BIGSIZE_(size);
   m_pixmapLabel->setFixedSize(bigSize+2,bigSize+2);
   m_pix = m_service->pixmap( TDEIcon::Desktop, size );
   m_hoverPix = m_service->pixmap( TDEIcon::Desktop, bigSize );
   m_pixmapLabel->setPixmap(m_pix);
}

StartMenuEntry::~StartMenuEntry()
{
//   if (m_service) delete m_service; m_service = 0L;
}

void StartMenuEntry::saveStats()
{
   if (usage > 0)
      config->writeEntry(m_service->desktopEntryName(), TQString::number(usage) +  " " + lastUse.toString(TQt::ISODate));
}

bool StartMenuEntry::operator==( const StartMenuEntry& se ) const
{
   return rank == se.rank;
}
bool StartMenuEntry::operator!=( const StartMenuEntry& se ) const
{
   return rank != se.rank;
}
bool StartMenuEntry::operator<( const StartMenuEntry& se ) const
{
   /*
   if (rank == se.rank)
      return m_titleLabel->text() < se.title();
   else
      */
      return rank > se.rank; // to have descending sort order (we could also use a negative ranking...)
}
bool StartMenuEntry::operator>( const StartMenuEntry& se ) const
{
   /*
   if (rank == se.rank)
      return m_titleLabel->text() > se.title();
   else
   */
      return rank < se.rank; // to have descending sort order (we could also use a negative ranking...)
}
bool StartMenuEntry::operator==( const double& d ) const
{
   return rank == d;
}
bool StartMenuEntry::operator!=( const double& d ) const
{
   return rank != d;
}
bool StartMenuEntry::operator<( const double& d ) const
{
   return rank > d; // to have descending sort order (we could also use a negative ranking...)
}
bool StartMenuEntry::operator>( const double& d ) const
{
   return rank < d; // to have descending sort order (we could also use a negative ranking...)
}

void StartMenuEntry::mouseReleaseEvent ( TQMouseEvent * e )
//TODO: do funny stuff with other buttons (e.g. provide edit dialog)
{
   if (e->button() == TQt::LeftButton)
   {
      m_pixmapLabel->setPixmap(m_pix);
      execute();
      if (!(e->state() & TQt::ControlButton))
         emit pressed();
   }
   if (e->button() == TQt::RightButton)
   {
      emit popup(this);
   }
}

void StartMenuEntry::mouseMoveEvent ( TQMouseEvent * mme )
{
   if (mme->state() & TQt::LeftButton && (mme->pos().y() < 0 || mme->pos().y() > height() || mme->pos().x() < 0 || mme->pos().x() > width()))
   {
      BaghiraLinkDrag *d = new BaghiraLinkDrag( m_service->name(), m_service->exec(), m_service->icon(), -1, parentWidget() );
      d->setPixmap(m_hoverPix, TQPoint(m_hoverPix.width()/2, m_hoverPix.height()/2));
      d->dragCopy();
      // do NOT delete d.
   }
}

void StartMenuEntry::keyPressEvent ( TQKeyEvent * e )
{
   switch (e->key())
   {
   case TQt::Key_Escape:
      emit closeMenu();
      break;
   case TQt::Key_Return:
   case TQt::Key_Enter:
      execute();
      if (!(e->state() & TQt::ControlButton))
         emit pressed();
      break;
   case TQt::Key_Down:
      emit appDown();
      break;
   case TQt::Key_Up:
      emit appUp();
      break;
   case TQt::Key_Home: // ->searchline + select
   case TQt::Key_Left:
      emit appLeft();
      break;
   default:
      break;
   }
}

TQString StartMenuEntry::title()
{
   return m_titleLabel->text();
}

void StartMenuEntry::focusInEvent ( TQFocusEvent * )
{
   isCurrent = true;
   emit hovered("[ " + groupPath + " ]    " + exec);
   if (useKTTS)
   {
      TQString text = i18n("for TTS output, telling which item is focussed (keyboard) and than reads the comment", "%1 focussed. %2").arg(m_titleLabel->text()).arg(m_commentLabel->text());
      emit sayText(text);
   }
   setPaletteBackgroundColor(TDEGlobalSettings::highlightColor());
   m_commentLabel->setPaletteBackgroundColor(TDEGlobalSettings::highlightColor());
   setPaletteForegroundColor(TDEGlobalSettings::highlightedTextColor());
   m_commentLabel->setPaletteForegroundColor(TDEGlobalSettings::highlightedTextColor());
}

void StartMenuEntry::focusOutEvent ( TQFocusEvent * )
{
   emit unhovered();
   isCurrent = false;
   setPaletteBackgroundColor(TDEGlobalSettings::baseColor());
   m_commentLabel->setPaletteBackgroundColor(TDEGlobalSettings::baseColor());
   if (hasMouse())
   {
      setPaletteForegroundColor(TDEGlobalSettings::highlightColor());
      m_commentLabel->setPaletteForegroundColor(TDEGlobalSettings::highlightColor());
   }
   else
   {
      setPaletteForegroundColor(TDEGlobalSettings::textColor());
      m_commentLabel->setPaletteForegroundColor(commentColor);
   }
}

void StartMenuEntry::enterEvent( TQEvent * )
{
   emit hovered("[ " + groupPath + " ]    " + exec);
   if (useKTTS)
   {
      TQString text = i18n("for TTS output, telling which item is hovered (mouse) and than reads the comment", "%1 hovered. %2").arg(m_titleLabel->text()).arg(m_commentLabel->text());
      emit sayText(text);
   }
   if (!isCurrent)
   {
      setPaletteForegroundColor(TDEGlobalSettings::highlightColor());
      m_commentLabel->setPaletteForegroundColor(TDEGlobalSettings::highlightColor());
   }
   m_pixmapLabel->setPixmap(m_hoverPix);
}

void StartMenuEntry::leaveEvent( TQEvent * )
{
   emit unhovered();
   if (!isCurrent)
   {
      setPaletteForegroundColor(TDEGlobalSettings::textColor());
      m_commentLabel->setPaletteForegroundColor(commentColor);
   }
   m_pixmapLabel->setPixmap(m_pix);
}

void StartMenuEntry::execute()
{
   usage++;
   lastUse = TQDate::currentDate();
   rank = 8 + usage;
   TDEApplication::startServiceByDesktopPath(m_service->desktopEntryPath(), TQStringList(), 0, 0, 0, "", true);
   emit executed();
}

Panel::Panel(int size, TQWidget * parent, const char * name) : TQWidget(parent, name), _size(size), _draggedMe(false), _count(0), _orientation(TQt::Horizontal), _poof(0), _poofIndex(0), _poofAnimPix(0), _poofPix(0)
{
   linkConfigDialog = new LinkConfig();
   setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed);
//    setPaletteBackgroundColor(TDEGlobalSettings::highlightColor());
   setAcceptDrops(true);
   config->setGroup("Panel");
   TQStringList commands = config->readListEntry("Commands", '§');
   TQStringList icons = config->readListEntry("Icons", '§');
   TQStringList offsets = config->readListEntry("Offsets", '§');
   TQStringList titles = config->readListEntry("Titles", '§');
   TQStringList::Iterator it1 = commands.begin();
   TQStringList::Iterator it2 = icons.begin();
   TQStringList::Iterator it3 = offsets.begin();
   TQStringList::Iterator it4 = titles.begin();
   while (it1 != commands.end() &&
          it2 != icons.end() &&
          it3 != offsets.end() &&
          it4 != titles.end())
   {
      addIcon(*it2, *it4, *it1, TQPoint((*it3).toInt(),0));
      ++it1;
      ++it2;
      ++it3;
      ++it4;
   }
}

void Panel::save(TDEConfig *config)
{
   config->setGroup("Panel");
   TQObjectList *kids = const_cast<TQObjectList*>(children());
   StartMenuButton *bt = 0;
   if (kids && !kids->isEmpty())
   {
      TQStringList icons;
      TQStringList titles;
      TQStringList commands;
      TQStringList offsets;
      TQObject *o;
      for ( o = kids->first(); o; o = kids->next() )
         if (bt = (dynamic_cast<StartMenuButton*>(o)))
         {
            icons.append(bt->icon());
            titles.append(bt->title());
            commands.append(bt->command());
            _orientation == TQt::Horizontal ?
               offsets.append(TQString::number(bt->x())) :
               offsets.append(TQString::number(bt->y()));
         }
      config->writeEntry("Commands", commands, '§');
      config->writeEntry("Icons", icons, '§');
      config->writeEntry("Offsets", offsets, '§');
      config->writeEntry("Titles", titles, '§');
   }
}

void Panel::reloadIcons( int size)
{
   _size = size;
   if (_orientation == TQt::Horizontal)
   {
      setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed);
      setFixedHeight(_BIGSIZE_(_size)+4);
      setMaximumWidth(32767);
   }
   else
   {
      setSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Expanding);
      setFixedWidth(_BIGSIZE_(_size)+4);
      setMaximumHeight(32767);
   }
   TQObjectList *kids = const_cast<TQObjectList*>(children());
   StartMenuButton *bt = 0;
   if (kids && !kids->isEmpty())
   {
      TQObject *o;
      for ( o = kids->first(); o; o = kids->next() )
         if (bt = (dynamic_cast<StartMenuButton*>(o)))
         {
            bt->reloadIcon(size);
         }
   }
}

void Panel::ensureVisible(TQRect & rect)
{
   int dx = 0; int dy = 0;
   if (rect.width() > clipRegion().boundingRect().width())
      dx = (rect.width() - clipRegion().boundingRect().width())/2 - rect.x();
   else if (rect.right() > clipRegion().boundingRect().right())
      dx = clipRegion().boundingRect().right() - rect.right();
   else if (rect.x() < clipRegion().boundingRect().x())
      dx = clipRegion().boundingRect().x() - rect.x();
   if (rect.height() > clipRegion().boundingRect().height())
      dy = (rect.height() - clipRegion().boundingRect().height())/2 - rect.y();
   else if (rect.bottom() > clipRegion().boundingRect().bottom())
      dy = clipRegion().boundingRect().bottom() - rect.bottom();
   else if (rect.y() < clipRegion().boundingRect().y())
      dy = clipRegion().boundingRect().y() - rect.y();
   scroll(dx, dy);
}

void Panel::updateSize(int dst)
{
   if (dst > 0)
   {
      if (_orientation == TQt::Horizontal)
      {
         resize(width()+dst, height());
      }
      else
      {
         resize(width(), height()+dst);
      }
   }
   else // more complex: resize and reposition all children, so first one is on (0,0)
   {
      TQObjectList *kids = const_cast<TQObjectList*>(children());
      if (kids && !kids->isEmpty())
      {
         TQObject *o;
         StartMenuButton *bt;
         if (_orientation == TQt::Horizontal)
         {
            resize(width()-dst, height());
            for ( o = kids->first(); o; o = kids->next() )
               if (bt = (dynamic_cast<StartMenuButton*>(o)))
                  bt->move(TQPoint(bt->x()-dst, bt->y()));
         }
         else
         {
            resize(width(), height()-dst);
            for ( o = kids->first(); o; o = kids->next() )
               if (bt = (dynamic_cast<StartMenuButton*>(o)))
                  bt->move(TQPoint(bt->x(), bt->y()-dst));
         }
      }
   }
}

void Panel::wheelEvent ( TQWheelEvent * we )
{
   if (_orientation == TQt::Vertical)
   {
      if (we->delta() > 0)
      {
         if (childrenRect().y() < clipRegion().boundingRect().y())
         {
            if (childrenRect().y() + we->delta() < clipRegion().boundingRect().y())
               scroll ( 0, we->delta() );
            else
               scroll ( 0, clipRegion().boundingRect().y() - childrenRect().y() );
         }
      }
      else
      {
         if (childrenRect().bottom() > clipRegion().boundingRect().bottom())
         {
            if (childrenRect().bottom() + we->delta() > clipRegion().boundingRect().bottom())
               scroll ( 0, we->delta() );
            else
               scroll ( 0, clipRegion().boundingRect().bottom() - childrenRect().bottom() );
         }
      }
   }
   else
   {
      if (we->delta() > 0)
      {
         if (childrenRect().x() < clipRegion().boundingRect().x())
         {
            if (childrenRect().x() + we->delta() < clipRegion().boundingRect().x())
               scroll ( we->delta(), 0 );
            else
               scroll ( clipRegion().boundingRect().x() - childrenRect().x(), 0 );
         }
      }
      else
      {
         if (childrenRect().right() > clipRegion().boundingRect().right())
         {
            if (childrenRect().right() + we->delta() > clipRegion().boundingRect().right())
               scroll ( we->delta(), 0 );
            else
               scroll ( clipRegion().boundingRect().right() - childrenRect().right(), 0 );
         }
      }
   }
}

void Panel::resizeEvent ( TQResizeEvent * e)
{
   if (_orientation == TQt::Horizontal)
   {
      if (e->size().height() != e->oldSize().height())
      {
         int h = e->size().height();
         KPixmap bgPix = TQPixmap(32, h);
         KPixmap bgPix1 = TQPixmap(32, h/2);
         KPixmap bgPix2 = TQPixmap(32, h - bgPix1.height());
         TQColor color = palette().color(TQPalette::Active, TQColorGroup::Background);
         KPixmapEffect::gradient( bgPix1, color.light(130), color.dark(105), KPixmapEffect::VerticalGradient, 0);
         KPixmapEffect::gradient( bgPix2, color.dark(120), color.light(110), KPixmapEffect::VerticalGradient, 0);
         TQPainter p(&bgPix);
         p.drawPixmap(0,0,bgPix1);
         p.drawPixmap(0,bgPix1.height(),bgPix2);
         p.end();
         setPaletteBackgroundPixmap( bgPix );
      }
   }
   else if (_orientation == TQt::Vertical)
   {
      if (e->size().width() != e->oldSize().width())
      {
         int w = e->size().width();
         KPixmap bgPix = TQPixmap(w, 32);
         KPixmap bgPix1 = TQPixmap(w/2, 32);
         KPixmap bgPix2 = TQPixmap(w - bgPix1.width(), 32);
         TQColor color = palette().color(TQPalette::Active, TQColorGroup::Background);
         KPixmapEffect::gradient( bgPix1, color.light(110), color.dark(120), KPixmapEffect::HorizontalGradient, 0);
         KPixmapEffect::gradient( bgPix2, color.dark(105), color.light(130), KPixmapEffect::HorizontalGradient, 0);
         TQPainter p(&bgPix);
         p.drawPixmap(0,0,bgPix1);
         p.drawPixmap(bgPix1.width(),0,bgPix2);
         p.end();
         setPaletteBackgroundPixmap( bgPix );
      }
   }
   TQWidget::resizeEvent( e );
}

void Panel::poof()
{
   /*if (_draggedMe)
   {
      _draggedMe = false;
      return;
   }*/
   TQObjectList *kids = const_cast<TQObjectList*>(children());
   if (kids && !kids->isEmpty())
   {
      TQObject *o;
      StartMenuButton *bt = 0L;
      for ( o = kids->first(); o; o = kids->next() )
      {
         bt = 0L;
         if ((bt = (dynamic_cast<StartMenuButton*>(o))) && bt->isMoving())
         {
            bt->hide(); bt->deleteLater(); bt = 0L;
            --_count;
         }
      }
      _poofIndex = 0;
      _poofPix = new TQPixmap(locateLocal("data", "baghira/poof.png"), "png");
      _poofAnimPix = new TQPixmap(_poofPix->width(), _poofPix->width());
      if (!_poof)
         _poof = new TQWidget(0,0, TQt::WType_TopLevel | TQt::WStyle_NoBorder | TQt::WStyle_StaysOnTop | TQt::WX11BypassWM);

      KWin::setShadowSize(_poof->winId(), 0);
      _poof->setFixedSize(_poofPix->width(), _poofPix->width());
      int x = TQCursor::pos().x() - _poof->width()/2;
      int y = TQCursor::pos().y() - _poof->height()/2;
      TQPixmap bgPix = TQPixmap::grabWindow( tqt_xrootwin(), x, y, _poofPix->width(), _poofPix->width());
      _poof->move(x,y);
      _poof->show();
      _poof->setBackgroundOrigin(TQWidget::WidgetOrigin);
      _poof->setPaletteBackgroundPixmap( bgPix );
      runPoof();
   }
}

void Panel::runPoof()
{
   if (_poofIndex > 4)
   {
      _poof->hide();
      delete _poofPix;
      _poofPix = 0L;
//       delete _poof;
//       _poof = 0L;
      delete _poofAnimPix;
      _poofAnimPix = 0L;
      _poofIndex = 0;
      return;
   }
   _poof->erase();
   bitBlt(_poof, 0 ,0, _poofPix, 0, _poofIndex * _poofPix->width(), _poofPix->width(), _poofPix->width(), TQt::AndROP);
   ++_poofIndex;
   TQTimer::singleShot ( 70, this, SLOT(runPoof()) ); // around 15 fps
}

void Panel::mouseReleaseEvent ( TQMouseEvent * mre )
{
   if (mre->state() & TQt::RightButton)
   {
      iconAddPosition = mre->pos();
      linkConfigDialog->setCaption ( i18n("New Link") );
      linkConfigDialog->title->clear();
      linkConfigDialog->command->clear();
      linkConfigDialog->icon->resetIcon();
      disconnect(linkConfigDialog->buttonOk, SIGNAL(clicked()), 0, 0);
      connect(linkConfigDialog->buttonOk, SIGNAL(clicked()), linkConfigDialog, SLOT(accept()));
      connect(linkConfigDialog->buttonOk, SIGNAL(clicked()), this, SLOT(addIcon()));
      linkConfigDialog->exec();
   }
}

void Panel::dragEnterEvent ( TQDragEnterEvent *dee )
{
   if (BaghiraLinkDrag::canDecode(dee))
   {
      TQObjectList *kids = const_cast<TQObjectList*>(children());
      if (kids && !kids->isEmpty())
      {
         TQObject *o;
         StartMenuButton *bt = 0L;
         for ( o = kids->first(); o; o = kids->next() )
            if ((bt = (dynamic_cast<StartMenuButton*>(o))) && bt->isMoving())
               // this is just some icon the user drags around ad that accidently left the panel
            {
               BaghiraLinkDrag::setAccepted();
                  // as we cannot access TQts dragmanager and qdragobject doesn't provide a function to cancel the drag, we just emit a virtual escape key...
               _draggedMe = true; // ensure to please not poof ;P
               XTestFakeKeyEvent(tqt_xdisplay(),XKeysymToKeycode(tqt_xdisplay(), XK_Escape), true, 0);
               XTestFakeKeyEvent(tqt_xdisplay(),XKeysymToKeycode(tqt_xdisplay(), XK_Escape), false, 0);
               XFlush(tqt_xdisplay());
               repositionIcon(bt, mapFromGlobal(dee->pos()));
               return;
            }
      }
      dee->accept(TRUE);
   }
   else if (TQUriDrag::canDecode(dee) || TQTextDrag::canDecode(dee))
      dee->accept(TRUE);
}

void Panel::dropEvent ( TQDropEvent *de )
{
   TQStrList list;
   TQString title;
   TQString command;
   TQString icon;
   int index;
   if ( BaghiraLinkDrag::decode(de, &title, &command, &icon, &index) )
   {
      addIcon ( icon, title, command, TQPoint(de->pos().x() - (_BIGSIZE_(_size)/2), de->pos().y() - (_BIGSIZE_(_size)/2)));
      BaghiraLinkDrag::setAccepted();
   }
   else if ( TQUriDrag::decode(de, list) )
   {
      char *uri;
      KURL url;
      for ( uri = list.first(); uri; uri = list.next() )
      {
         url = KURL(uri);
         if (url.protocol() == "http")
            addIcon ( "html", url.host()+(url.path()=="/"?TQString(""):url.path()), uri, de->pos() );
         else
         {
            KFileItem item = KFileItem(KFileItem::Unknown, KFileItem::Unknown, url, true);
            addIcon ( item.iconName(), url.fileName().isEmpty()?url.prettyURL():url.fileName(), uri, de->pos() );
         }
      }
   }
   else if (TQTextDrag::decode(de, command))
   {
      KURL url(command);
      if (url.isValid())
      {
         if (url.protocol() == "http")
            addIcon ( "html", url.host()+(url.path()=="/"?TQString(""):url.path()), command, de->pos() );
         else
         {
            KFileItem item = KFileItem(KFileItem::Unknown, KFileItem::Unknown, url, true);
            addIcon ( item.iconName(), url.fileName().isEmpty()?url.prettyURL():url.fileName(), command, de->pos() );
         }
      }
      else if (command.contains('@'))
      {
         command.replace(" ","");
         addIcon ( "kmail", command, "mailto:"+command, de->pos() );
      }
      else if (command.contains("'at'")) //last chance for anti-spam addy
      {
         command.replace(" ","");
         command.replace("'at'","@");
         addIcon ( "kmail", command, "mailto:"+command, de->pos() );
      }
      else // ok, just take this as siple unknown command and hope the user knows, what she's ;) doing...
      {
         StartMenuButton *bt = addIcon ( command, command, command, de->pos() );
      }
   }
}

void Panel::repositionIcon(StartMenuButton *button, TQPoint pt)
{
   TQObjectList *kids = const_cast<TQObjectList*>(children());
   if (!kids || kids->isEmpty()) // the easy one ;)
      return;
   if (kids->count() == 1) // button's for certain the only child ;)
   {
      if (_orientation == TQt::Horizontal)
         button->move(pt.x(),0);
      else
         button->move(0,pt.y());
      return;
   }
   TQObject *o;
   StartMenuButton *bt = 0L;
   int xy = 0;
   if (_orientation == TQt::Horizontal)
   {
      if (pt.x() < 0) // append horizontally
      {
         for ( o = kids->first(); o; o = kids->next() )
            if ((bt = dynamic_cast<StartMenuButton*>(o)) && bt != button && bt->x() + bt->width() > xy )
               xy = bt->x() + bt->width();
         button->move(xy,0);
      }
      else // inject horizontally
      {
         // first find possible icon under the position
         for ( o = kids->first(); o; o = kids->next() )
         {
            if ((bt = dynamic_cast<StartMenuButton*>(o)) && TQRect(bt->pos(), bt->size()).contains(pt))
               break;
            else
               bt = 0l;
         }
         if (bt) // found? - decide whether to insert left or right
         {
            if (bt->x() + bt->width()/2 > pt.x()) // move to old icon place
            {
               button->move(bt->x(), 0);
            }
            else // move to right
            {
               button->move(bt->x() + bt->width(), 0);
            }
            // adjust right icons
            for ( o = kids->first(); o; o = kids->next() )
               if ((bt = dynamic_cast<StartMenuButton*>(o)) && bt->x() >= button->x() && bt != button )
               {
                  bt->move(bt->x() + button->width(), 0);
               }
         }
         else // no collision, just move there
            button->move(pt.x(), 0);
      }
   }
   else
   {
      if (pt.y() < 0) // append vertically
      {
         for ( o = kids->first(); o; o = kids->next() )
            if ((bt = dynamic_cast<StartMenuButton*>(o)) && bt != button && bt->y() + bt->height() > xy )
               xy = bt->y() + bt->height();
         button->move(0, xy);
      }
      else // inject vertically
      {
         // first find possible icon under the position
         for ( o = kids->first(); o; o = kids->next() )
         {
            if ((bt = dynamic_cast<StartMenuButton*>(o)) && TQRect(bt->pos(), bt->size()).contains(pt))
               break;
            else
               bt = 0l;
         }
         if (bt) // found? - decide whether to insert up or down
         {
            if (bt->y() + bt->height()/2 > pt.y()) // move to old icon place
               button->move(0, bt->y());
            else // move to right
               button->move(0, bt->y() + bt->height());
            // adjust lower icons
            for ( o = kids->first(); o; o = kids->next() )
               if ((bt = dynamic_cast<StartMenuButton*>(o)) && bt->y() >= button->y() && bt != button )
                  bt->move(0, bt->y() + button->height());
         }
         else // no collision, just move there
            button->move(0, pt.y());
      }
   }
}

StartMenuButton* Panel::addIcon ( TQString icon, TQString title, TQString command, TQPoint pt )
{
   StartMenuButton *tmpButton = new StartMenuButton(_size, icon, title, command, StartMenuButton::Status, this);
   // reposition icon
   repositionIcon(tmpButton, pt);
   // connections
   connect (tmpButton, SIGNAL(hovered(const TQString &)), this, SIGNAL(message(const TQString &)));
   connect (tmpButton, SIGNAL(unhovered()), this, SIGNAL(clearStatus()));
   connect (tmpButton, SIGNAL(updateSize(int)), this, SLOT(updateSize(int)));
   connect (tmpButton, SIGNAL(pressed(const TQString &)), parent(), SLOT(execute(const TQString &)));
   connect (tmpButton, SIGNAL(pressed(const TQString &)), parent(), SLOT(close()));
   // done
   // inc counter
   _count++;
   tmpButton->show();
   return tmpButton;
}

void Panel::setOrientation ( Orientation ori )
{
   if (_orientation == ori)
      return;
   _orientation = ori;
   TQObjectList *kids = const_cast<TQObjectList*>(children());
   if (!kids || kids->isEmpty())
      return;
   TQObject *o;
   StartMenuButton *bt = 0L;
   for ( o = kids->first(); o; o = kids->next() )
      if (bt = (dynamic_cast<StartMenuButton*>(o)))
         bt->move(TQPoint(bt->pos().y(), bt->pos().x()));
}

AppList::AppList(int size, TQWidget * parent) : TQScrollView(parent), _size(size)
{
   popupBlocked_ = false;
   enableClipper( true );
   setFrameStyle(TQFrame::LineEditPanel | TQFrame::Sunken );
   configDialog_ = new ConfigDialog;
   helpDialog_ = new HelpDialog;
   connect (((TQObject*)configDialog_->buttonHelp), SIGNAL(clicked()), ((TQObject*)helpDialog_), SLOT(exec()));
   connect (((TQObject*)configDialog_->buttonCancel), SIGNAL(clicked()), this, SLOT(unblockPopup()));
   m_widget = new TQFrame(viewport());
   m_widget->setSizePolicy(TQSizePolicy::Minimum, TQSizePolicy::Minimum);
   m_widget->setPaletteBackgroundColor(TDEGlobalSettings::baseColor());
   addChild(m_widget,0,0);
   setResizePolicy(TQScrollView::AutoOneFit);
   m_widget->show();
   infoLayout = new TQVBoxLayout(m_widget);
   infoLabel = new TQLabel(m_widget);
   infoLabel->setPaletteBackgroundColor(infoColor);
   infoLabel->setTextFormat( TQt::RichText );
   infoLayout->addWidget(infoLabel);
   m_VLayout = new TQVBoxLayout(infoLayout);
   m_iconLoader = TDEGlobal::iconLoader();
   m_popup = new TDEPopupMenu(this);
   m_popup->insertItem(i18n("Edit Entry"), this, SLOT(editDialog()));
   m_popup->insertItem(i18n("Remove Entry"), this, SLOT(removeEntry()));
   m_popup->insertSeparator();
   m_popup->insertItem(i18n("Add Entry"), this, SLOT(addDialog()));
   init();
}

void AppList::mouseReleaseEvent ( TQMouseEvent * e )
{
   if (e->button() == TQt::RightButton)
      addDialog();
}

void AppList::windowActivationChange ( bool oldActive )
{
   if (isActiveWindow() && entryList.current())
      entryList.current()->setFocus();
   TQScrollView::windowActivationChange ( oldActive );
}

void AppList::reloadIcons( int size)
{
   _size = size;
   TQPtrListIterator<StartMenuEntry> it(entryList);
   StartMenuEntry *runner;
   while( (runner = it.current()) != 0 )
   {
      ++it;
      runner->reloadIcon(size);
   }
}

void AppList::addDialog()
{
   disconnect (((TQObject*)configDialog_->buttonOk), SIGNAL(clicked()), this, 0);
   connect (((TQObject*)configDialog_->buttonOk), SIGNAL(clicked()), this, SLOT(addEntry()));
   configDialog_->appName->clear();
   configDialog_->category->clearEdit();
   configDialog_->command->clear();
   configDialog_->genericName->clear();
   configDialog_->iconButton->resetIcon();
   configDialog_->keywords->clear();
   configDialog_->startupFeedback->setChecked( true );
   configDialog_->showInSystray->setChecked( false );
   configDialog_->description->clear();
   configDialog_->startInTerminal->setChecked( false );
   configDialog_->terminalSettings->clear();
   configDialog_->startAsUser->setChecked( false );
   configDialog_->username->clear();
   configDialog_->workingDir->clear();
   configDialog_->shortcut->setShortcut(TDEShortcut::null(), false);

   configDialog_->setCaption ( i18n("New Entry") );
   ((TQWidget*)(configDialog_->extendedGroup))->hide();
   configDialog_->buttonDetail->setDown ( false );
   configDialog_->adjustSize();
   configDialog_->show();
}

void AppList::addEntry()
{
   TQString fullpath = configDialog_->category->currentText();
   while (fullpath[0] == '/') // remove leading "/"'s
   {
      fullpath.remove(0,1);
   }
   fullpath = KService::newServicePath(true, fullpath + configDialog_->appName->text());
//tqWarning("%s",fullpath.ascii());
   writeEntry(fullpath);
   KService *s = new KService(fullpath);
   TQStringList list(KServiceGroup::group(configDialog_->category->currentText())->caption());
   StartMenuEntry *sme = addApp(s, list, configDialog_->category->currentText());
   sme->rank = 0xFFFFFF; // hype rank to max, i.e. keep this entry on top of everything - for this session or until first use
   sme->show();
   sort();
}

void AppList::removeEntry()
{
   popupBlocked_ = true;
   if (KMessageBox::questionYesNo (this, i18n("<qt>Are you sure you want to remove<br> %1</qt>").arg(handledEntry->title()), i18n("Remove ALI entry")) == KMessageBox::Yes)
   {
   tqWarning("gonna delete!");
      writeEntry(handledEntry->m_service->locateLocal(), true);
      handledEntry->hide();
      entryList.removeRef(handledEntry);
      delete handledEntry;
      handledEntry = 0L;
   }
   popupBlocked_ = false;
}

void AppList::editDialog()
{
   if (!handledEntry)
      return;
   popupBlocked_ = true;
   disconnect (((TQObject*)configDialog_->buttonOk), SIGNAL(clicked()), this, 0);
   connect (((TQObject*)configDialog_->buttonOk), SIGNAL(clicked()), this, SLOT(editEntry()));
   configDialog_->appName->setText(handledEntry->m_service->name());
   configDialog_->category->setCurrentItem (handledEntry->groupPath, false);
   configDialog_->showInSystray->setChecked( handledEntry->m_service->exec().contains("ksystraycmd ") );
   if (!configDialog_->showInSystray->isChecked())
      configDialog_->command->setURL(handledEntry->m_service->exec());
   else
      configDialog_->command->setURL(handledEntry->m_service->exec().right(handledEntry->m_service->exec().length() - (handledEntry->m_service->exec().findRev("ksystraycmd ") + 12)));
   configDialog_->genericName->setText(handledEntry->m_service->genericName());
   configDialog_->iconButton->setIcon(handledEntry->m_service->icon ());
   configDialog_->keywords->setText(handledEntry->m_service->keywords().join(","));
   configDialog_->startupFeedback->setChecked( handledEntry->m_service->property ( "StartupNotify" ).toBool());
   configDialog_->description->setText(handledEntry->m_service->comment());
   configDialog_->startInTerminal->setChecked( handledEntry->m_service->terminal() );
   configDialog_->terminalSettings->setText(handledEntry->m_service->terminalOptions());
   configDialog_->startAsUser->setChecked( handledEntry->m_service->substituteUid() );
   configDialog_->username->setText(handledEntry->m_service->username());
   configDialog_->workingDir->setURL(handledEntry->m_service->path());
//    KKeyButton* shortcut;

   configDialog_->setCaption ( i18n("Edit Entry") );
   ((TQWidget*)(configDialog_->extendedGroup))->hide();
   configDialog_->buttonDetail->setDown ( false );
   configDialog_->adjustSize();
   configDialog_->show();
}

void AppList::editEntry()
{
   TQString fullpath;
   if (handledEntry->groupPath == configDialog_->category->currentText()) // group not changed
      fullpath = handledEntry->m_service->locateLocal(); // find a local replacement path
   else // more complex: remove (del/shadow) entry from old group and create one in the new group
   {
      writeEntry(handledEntry->m_service->locateLocal(), true); //hide from old group
      fullpath = configDialog_->category->currentText();
      while (fullpath[0] == '/') // remove leading "/"'s
         fullpath.remove(0,1);
      fullpath = KService::newServicePath(true, fullpath + configDialog_->appName->text()); // find new path
   }
   writeEntry(fullpath);
   handledEntry->hide();
   TQDate lu = handledEntry->lastUse;
   uint u = handledEntry->usage;
   entryList.removeRef(handledEntry);
   delete handledEntry;
   handledEntry = 0L;
   KService *s = new KService(fullpath);
   TQStringList list(KServiceGroup::group(configDialog_->category->currentText())->caption());
   StartMenuEntry *sme = addApp(s, list, configDialog_->category->currentText());
   sme->lastUse = lu;
   sme->usage = u;
   sme->rank = 8 + u - lu.daysTo(TQDate::currentDate());
   if (!config) config = new TDEConfig("bStarter", false, false);
   config->setGroup("Statistics");
   sme->saveStats();
   delete config;
   config = 0L;
   sme->show();
   sort();
   popupBlocked_ = false;
}

void AppList::writeEntry(TQString path, bool hidden )
{
   TDEConfig *config = new TDEConfig(path);
   config->setDesktopGroup();
   
   if (!configDialog_->description->text().isEmpty())
      config->writeEntry("Comment", configDialog_->description->text());
   if (!configDialog_->command->url().isEmpty())
      if (configDialog_->showInSystray->isChecked())
         config->writeEntry("Exec", "ksystraycmd " + configDialog_->command->url());
      else
         config->writeEntry("Exec", configDialog_->command->url());
   if (!configDialog_->genericName->text().isEmpty())
      config->writeEntry("GenericName", configDialog_->genericName->text());
   if (hidden)
      config->writeEntry("Hidden", true);
   else if (config->readBoolEntry("Hidden", false))
      config->writeEntry("Hidden", false);
   if (!configDialog_->iconButton->icon().isNull())
      config->writeEntry("Icon", configDialog_->iconButton->icon());
   if (!configDialog_->keywords->text().isEmpty())
      config->writeEntry("Keywords", TQStringList::split(',', configDialog_->keywords->text()));
   if (!configDialog_->appName->text().isEmpty())
      config->writeEntry("Name", configDialog_->appName->text());
   if (!configDialog_->workingDir->url().isEmpty())
      config->writeEntry("Path", configDialog_->workingDir->url());
   if (configDialog_->startupFeedback->isChecked())
      config->writeEntry("StartupNotify", true);
   if (configDialog_->startInTerminal->isChecked())
   {
      config->writeEntry("Terminal", 1);
      if (!configDialog_->terminalSettings->text().isEmpty())
         config->writeEntry("TerminalOptions", configDialog_->terminalSettings->text());
   }
   config->writeEntry("Type", "Application");
   if (configDialog_->startAsUser->isChecked())
   {
      config->writeEntry("X-TDE-SubstituteUID", true);
      if (!configDialog_->username->text().isEmpty())
         config->writeEntry("X-TDE-Username", configDialog_->username->text());
   }
   
   delete config;
   
//    configDialog_->category->setCurrentItem (handledEntry->m_service->categories().join("/"), true);
}

void AppList::popup(StartMenuEntry* entry)
{
   if (popupBlocked_ && configDialog_->isShown())
      return;
   handledEntry = entry;
   m_popup->popup(TQCursor::pos());
}

void AppList::sort()
{
   // first clean the layout (i assume that TQLayout::remove() will search from beginning so removing over the current order will be quite fast)
   TQPtrListIterator<StartMenuEntry> it(entryList);
   StartMenuEntry *runner;
   while( (runner = it.current()) != 0 )
   {
      ++it;
      m_VLayout->remove ( runner );
   }
   ((MyVBoxLayout*)m_VLayout)->deleteAllItems(); // get rid of the stretch
   // then sort the list
   entryList.sort();
   it.toFirst();
   // now rebuild the layout from new list
   while( (runner = it.current()) != 0 )
   {
      ++it;
      m_VLayout->addWidget ( runner );
   }
   m_VLayout->addStretch(1); // add final stretch
}

void AppList::init()
{
   m_root = KServiceGroup::group(TQString::null);
   
   if (!m_root || !m_root->isValid())
   {
      tqWarning("ROOT NOT FOUND");
      return;
   }
   favItemAmount = config->readNumEntry("FavItemAmount", 10);
   neewbieApps << "konqueror" << "kmail" << "kppp";
   if (newbie = config->readBoolEntry("firstUse", true)) // '/' is certainly not unescaped part of a filename ;)
   {
      infoLabel->setText ( i18n("<qt><b>First Session Applications</b></qt>") );
//       delete config;
//       config = 0L;
   }
   else
   {
      infoLabel->setText ( i18n("<qt><b>Favorite Applications</b><br></qt>") );
   }
   config->setGroup("Statistics");
   TQStringList captions, paths;
   insertGroup(m_root, captions, paths);
   paths.sort();
   configDialog_->category->insertStringList(paths);
   configDialog_->category->completionObject()->setCompletionMode( TDEGlobalSettings::CompletionPopupAuto );
   configDialog_->category->completionObject()->insertItems(paths);
//    if (config) { delete config; config = 0L; }
   sort();
   reset();
}

void AppList::save(TDEConfig *config)
{
   config->setGroup("Statistics");
   TQPtrListIterator<StartMenuEntry> it(entryList);
   StartMenuEntry *runner;
   while( (runner = it.current()) != 0 )
   {
      ++it;
      runner->saveStats();
   }
}

void AppList::insertGroup(KServiceGroup *g, TQStringList & captions, TQStringList & paths)
{
   KServiceGroup::List list = g->entries(true, true, false, false);
   
   if (list.isEmpty())
   {
      return;
   }
   else
   {
      captions.append(g->caption());
      if (!categories.contains(g->caption()))
         categories.append(g->caption());
      if (!paths.contains(g->relPath()))
         paths.append(g->relPath());
      for( KServiceGroup::List::ConstIterator it = list.begin(); it != list.end(); it++)
      {
         KSycocaEntry *p = (*it);
         if (p->isType(KST_KService))
         {
            KService *s = static_cast<KService *>(p);
            if ((s->name().at(0) == '.'))
               continue;
            if (s->type() == "Application")
            {
               addApp(s, captions, g->relPath());
            }
         }
         else if (p->isType(KST_KServiceGroup))
         {
            KServiceGroup *g2 = static_cast<KServiceGroup *>(p);
            if ((g2->name().at(0) == '.'))
               continue;
            insertGroup(g2,captions, paths);
         }
      }
      captions.remove(g->caption());
   }
}

StartMenuEntry* AppList::addApp(KService * s, TQStringList & captions, TQString relPath)
{
   StartMenuEntry * tmp = new StartMenuEntry(s, relPath, _size, neewbieApps.find(s->desktopEntryName()) != neewbieApps.end(), m_widget);
   connect (tmp, SIGNAL(appUp()), this, SLOT(appUp()));
   connect (tmp, SIGNAL(appDown()), this, SLOT(appDown()));
   connect (tmp, SIGNAL(appLeft()), this, SLOT(appLeft()));
   connect (tmp, SIGNAL(hovered(const TQString&)), this, SIGNAL(message(const TQString&)));
   if (useKTTS) connect (tmp, SIGNAL(sayText(const TQString&)), this, SIGNAL(sayText(const TQString&)));
   connect (tmp, SIGNAL(unhovered()), this, SIGNAL(clearStatus()));
   connect (tmp, SIGNAL(pressed()), parent(), SLOT(close()));
   connect (tmp, SIGNAL(popup(StartMenuEntry*)), this, SLOT(popup(StartMenuEntry*)));
   connect (tmp, SIGNAL(closeMenu()), parent(), SLOT(close()));
   connect (tmp, SIGNAL(executed()), this, SLOT(sort()));
   m_VLayout->addWidget(tmp);
   tmp->hide();
   entryList.append(tmp);
   KeyWordList::Iterator it;
   if (!(s->name().isNull() || s->name().isEmpty()))
   {
      it = m_keywordList.insert(s->name(), StartMenuEntryList(), false);
      it.data().append( tmp );
   }
   TQStringList kw;
#if 0
   kw = s->categories(); // THIS IS ****IMPORTANT***** kicker will crash on init if you try to grep through the pointers!
   if (!kw.isEmpty())
   {
      TQStringList::Iterator key;
      for ( key = kw.begin(); key != kw.end(); ++key )
      {
         if (!((*key).isNull() || (*key).isEmpty()))
         {
            it = m_keywordList.insJediKnightert(*key, StartMenuEntryList(), false);
            it.data().append( tmp );
         }
      }
   }
#endif
   kw = s->keywords(); // THIS IS ****IMPORTANT***** kicker will crash on init if you try to grep through the pointers!
   if (!kw.isEmpty())
   {
      for ( TQStringList::Iterator key = kw.begin(); key != kw.end(); ++key )
      {
         if (!((*key).isNull() || (*key).isEmpty()))
         {
            it = m_keywordList.insert(*key, StartMenuEntryList(), false);
            it.data().append( tmp );
         }
      }
   }
   // group captions (so Games/IDSoftware/Quake3 will appear in Games and IDSoftware)
   if (!captions.isEmpty())
   {
      for ( TQStringList::Iterator key = captions.begin(); key != captions.end(); ++key )
      {
         if (!((*key).isNull() || (*key).isEmpty()))
         {
            it = m_groupList.insert(*key, StartMenuEntryList(), false);
            it.data().append( tmp );
         }
      }
   }
   return tmp;
}

void AppList::finish()
{
   entryList.last(); entryList.next();
   m_VLayout->addStretch(1);
   categories.sort();
}

void AppList::appDown()
{
   StartMenuEntry *save;
   if (entryList.current() == 0L/*entryList.getLast()*/)
   {
      entryList.first();
      save = 0L;
   }
   else
   {
      save = entryList.current();
      entryList.next();
   }
   for ( StartMenuEntry *runner = entryList.current(); runner; runner = entryList.next() )
      if (runner->isShown())
      {
         if (save) save->clearFocus();
         runner->setFocus();
         TQPoint pt(0,runner->height());
         pt = runner->mapToParent(pt);
         ensureVisible ( pt.x(), pt.y());
         return;
      }
//    if (currentEntry == entryList.end())
//       currentEntry = entryList.begin(); // keep this somewhere valid
}

void AppList::appUp()
{
   if (entryList.current() == entryList.getFirst())
   {
      entryList.current()->clearFocus();
      entryList.last(); entryList.next(); // we jump out
      emit looseKey();
      return;
   }
   StartMenuEntry *save = entryList.current();
   StartMenuEntry *runner;
   if (entryList.current())
      runner = entryList.prev();
   else
      runner = entryList.last();
   for ( ; runner != entryList.getFirst(); runner =  entryList.prev())
      if (runner->isShown())
      {
         if (save) save->clearFocus();
         runner->setFocus();
         TQPoint pt(0,0);
         pt = runner->mapToParent(pt);
         ensureVisible ( pt.x(), pt.y());
         return;
      }
   if (runner == entryList.getFirst())
   {
      if (save) save->clearFocus();
      if (runner->isShown())
      {
         runner->setFocus();
         TQPoint pt(0,0);
         pt = runner->mapToParent(pt);
         ensureVisible ( pt.x(), pt.y());
      }
      else
      {
         entryList.last(); entryList.next();// we jump out
         emit looseKey();
      }
   }
}
void AppList::appLeft()
{
   if (entryList.current())
   {
      entryList.current()->clearFocus();
      entryList.last(); entryList.next(); // we jump out
   }
   emit looseKey();
}

void AppList::showCategory(const TQString & string)
{
   infoLabel->setText(string);
   infoLabel->show();
   StartMenuEntry *it2;
   KeyWordList::Iterator it;
   for ( it = m_groupList.begin(); it != m_groupList.end(); ++it )
   {
      if (it.key() == string)
      {
         for ( it2 = it.data().first(); it2; it2 = it.data().next())
            it2->display = true;
      }
      else
      {
         for ( it2 = it.data().first(); it2; it2 = it.data().next())
            it2->display = it2->display || false;
      }
   }
   uint visibleItems = 0;
   for ( it2 = entryList.first(); it2; it2 = entryList.next())
   {
      if (it2->display)
      {
         visibleItems++;
         it2->show();
      }
      else
         it2->hide();
      it2->display = false;
   }
   if (useKTTS && visibleItems == 0)
   {
      TQString text = i18n("for TTS output, informs the user that no entries are in the currently selected group", "Warning! No Applications in group %1").arg(string);
      emit sayText(text);
   }
}

void AppList::search(const TQString & string)
{
   StartMenuEntry *it2;
   if (string == TQString::null || string == "") // empty line - remove all and exit
   {
      infoLabel->setText ( i18n("<qt><b>Favorite Applications</b><br></qt>") );
      infoLabel->show();
      int i = 0;
      for ( it2 = entryList.first(); it2; it2 = entryList.next() )
      {
         i < favItemAmount ? it2->show() : it2->hide();
         i++;
      }
      return;
   }
   infoLabel->hide();
   // ok, we need a 2pass search: 1st to figure out which to show, 2nd to show or hide things
   // the implementation also prevents us from spending time on showing/hiding things and some onscreen flicker, O(k*n(k)*n)
   KeyWordList::Iterator it;
   //keywords
   for ( it = m_keywordList.begin(); it != m_keywordList.end(); ++it )
   {
      if (it.key().contains(string, false) > 0)
      {
         for ( it2 = it.data().first(); it2; it2 = it.data().next())
            it2->display = true;
      }
      else
      {
         for ( it2 = it.data().first(); it2; it2 = it.data().next())
            it2->display = it2->display || false;
      }
   }
   //groups
   for ( it = m_groupList.begin(); it != m_groupList.end(); ++it )
   {
      if (it.key().contains(string, false) > 0)
      {
         for ( it2 = it.data().first(); it2; it2 = it.data().next())
            it2->display = true;
      }
      else
      {
         for ( it2 = it.data().first(); it2; it2 = it.data().next())
            it2->display = it2->display || false;
      }
   }
   //items
   uint visibleItems = 0;
   for ( it2 = entryList.first(); it2; it2 = entryList.next())
   {
      if (it2->display && visibleItems < 50) //limit this to a healthy size
      {
         visibleItems++;
         it2->show();
      }
      else
         it2->hide();
      it2->display = false;
   }
   if (useKTTS && visibleItems == 0)
   {
      TQString text = i18n("for TTS output, no entries match the current search text", "Warning! No more Applications left. The entered Text is %1").arg(spell(string));
      emit sayText(text);
   }
}

void AppList::clear()
{
   for ( StartMenuEntry *it = entryList.first(); it; it = entryList.next())
      it->hide();
}

void AppList::reset()
{
   newbie ?
      infoLabel->setText ( i18n("<qt><b>First Session Applications</b></qt>") ) :
      infoLabel->setText ( i18n("<qt><b>Favorite Applications</b><br></qt>") );
   infoLabel->show();
   if (newbie)
   {
      for ( StartMenuEntry *it = entryList.first(); it; it = entryList.next())
      {
         it->forNewbie ? it->show() : it->hide();
      }
   }
   else
   {
      uint i = 0;
      for ( StartMenuEntry *it = entryList.first(); it; it = entryList.next())
      {
         i < favItemAmount ? it->show() : it->hide();
         i++;
      }
   }
   setContentsPos(0, 0);
}

StartMenu::StartMenu( int size, TQWidget * parent, WFlags f ) : TQWidget(parent, "StartMenu", f), _size(size), inMove(false)
{
   m_panelPos = StartMenu::Nowhere;
   panelLayout = new TQGridLayout ( this, 3, 3 );
   config = new TDEConfig("bStarter", true, false);
   config->setGroup("Shell");
   history = config->readListEntry("History");
   config->setGroup("Settings");
   if (useKTTS = config->readBoolEntry("useKTTS", false))
      m_spokenText = 0;
   _filterData = new KURIFilterData();
   int r,g,b,r2,g2,b2;
   TDEGlobalSettings::baseColor().getRgb(&r,&g,&b);
   TDEGlobalSettings::textColor().getRgb(&r2,&g2,&b2);
   commentColor.setRgb((r+r2)/2,(g+g2)/2,(b+b2)/2);
   infoColor.setRgb((3*r+r2)/4,(3*g+g2)/4,(3*b+b2)/4);
   currentHistoryItem = history.end();
   header = new TQWidget(this, "_B_ALI_HEADER");
   header->installEventFilter(this);
   TQHBoxLayout *headerLayout = new TQHBoxLayout(header, 5, 3);
   userButton = new StartMenuButton(_size, "folder_home", TQString(getenv("USER")), "~", StartMenuButton::Status, header, "_B_ALI_HEADER");
   headerLayout->addWidget(userButton);
   header->setPaletteBackgroundColor(TDEGlobalSettings::highlightColor());
   connect (userButton, SIGNAL(pressed(const TQString &)), this, SLOT(execute(const TQString &)));
   connect (userButton, SIGNAL(pressed(const TQString &)), this, SLOT(close()));
   TQBoxLayout *mainLayout = new TQVBoxLayout();
   panelLayout->addLayout(mainLayout, 1, 1);
   mainLayout->addWidget(header);
   mainLayout->addSpacing ( 3 );
   TQBoxLayout *centerLayout = new TQHBoxLayout( mainLayout );
   centerLayout->addSpacing ( 3 );

   appList = new AppList(_size, this);
   appList->setSizePolicy(TQSizePolicy::Expanding,TQSizePolicy::Expanding);
   appList->finish();

   searchLine = new SearchLine(header);
   categoryCombo = new TQComboBox(header, "_B_ALI_HEADER");
   headerLayout->addWidget(categoryCombo);
   categoryCombo->setLineEdit ( searchLine );
   categoryCombo->insertStringList(appList->categories);
   categoryCombo->setEditable ( true );
   categoryCombo->setInsertionPolicy(TQComboBox::NoInsertion);
   searchLine->installEventFilter(this);
   searchLine->setCompletionMode(TDEGlobalSettings::CompletionAuto);
   config->setGroup("Shortcuts");
   // read shortcuts...
   TQStringList cuts = config->readListEntry ("Shortcuts", ',');
   TQStringList cats = config->readListEntry ("Categories", ',');
   TQStringList::Iterator it;
   TQStringList::Iterator it2 = cats.begin();
   for ( it = cuts.begin(); it != cuts.end(); ++it )
   {
      if (it2 == cats.end()) break;
      shortcutList.insert(MyKey((*it)),(*it2));
      it2++;
   }
   //---
   TQString PATH(getenv("PATH"));
   int n = PATH.contains(':', false);
   TQStringList list;
   for (int i = 0; i < n; i++)
   {
      TQDir execDir(PATH.section(':', i, i));
      list = execDir.entryList(TQDir::Files | TQDir::Executable, TQDir::Name | TQDir::IgnoreCase );
      searchLine->completionObject()->insertItems(list);
   }

   connect (categoryCombo, SIGNAL(activated( const TQString &)), appList, SLOT(showCategory(const TQString &)));
   connect (searchLine, SIGNAL(typedTextChanged(const TQString &)), appList, SLOT(search(const TQString &)));
   connect (searchLine, SIGNAL(textChanged(const TQString &)), this, SLOT(endHistory()));
   connect (appList, SIGNAL(looseKey()), searchLine, SLOT(setFocus()));
   connect (appList, SIGNAL(looseKey()), searchLine, SLOT(selectAll() ));
   connect (kapp, SIGNAL(shutDown()), this, SLOT(save() ));
   if (useKTTS) connect (appList, SIGNAL(sayText(const TQString&)), this, SLOT(sayText(const TQString&) ));

   centerLayout->addWidget(appList,10);
   centerLayout->addSpacing ( 3 );
   
   m_panel = new Panel(_size, this, "_B_ALI_HEADER");
//    m_panel->installEventFilter(this);
   m_panel->setFixedHeight(_BIGSIZE_(_size)+4);
   
   statusBar = new KSqueezedTextLabel(this);
   TQFont tmpFnt = statusBar->font();
   tmpFnt.setBold(true);
   statusBar->setFont(tmpFnt);
   connect (appList, SIGNAL(message(const TQString&)), this, SLOT(message(const TQString&) ));
   connect (appList, SIGNAL(clearStatus()), this, SLOT(clearStatus() ));
   connect (m_panel, SIGNAL(message(const TQString&)), this, SLOT(centerMessage(const TQString&) ));
   connect (m_panel, SIGNAL(clearStatus()), this, SLOT(clearStatus() ));
   
   mainLayout->addSpacing ( 3 );
   mainLayout->addWidget ( statusBar );
   panelLayout->addWidget ( m_panel, 2, 1 );
   m_panel->hide();
   
   if (config) { delete config; config = 0L; }
//    KDrawer *drawer = new KDrawer(this, KDrawer::Bottom);
}

void StartMenu::save()
{
   config = new TDEConfig("bStarter", false, false);
   config->setGroup("Shell");
   TQStringList lst;
   for ( TQStringList::Iterator it = history.begin(); it != history.end(); ++it )
      lst.prepend(*it);
   config->writeEntry("History", lst);
   config->setGroup("Settings");
   config->writeEntry("firstUse", false);
   appList->save(config);
   m_panel->save(config);
   delete config; config = 0L;
}
#if 0
extern int kicker_screen_number;

void StartMenu::slotLock()
{
   TQCString appname( "kdesktop" );
//    if ( kicker_screen_number )
//       appname.sprintf("kdesktop-screen-%d", kicker_screen_number);
   kapp->dcopClient()->send(appname, "KScreensaverIface", "lock()", "");
}
#endif

void StartMenu::reloadIcons( int size)
{
   _size = size;
   m_panel->reloadIcons(size);
   appList->reloadIcons(size);
}

void StartMenu::setCategory(const TQString & category)
{
   categoryCombo->setCurrentItem ( categoryCombo->listBox()->index(categoryCombo->listBox()->findItem(category, TQt::ExactMatch)));
   //emit categoryCombo->activated ( category ); // dunno if i'll need that once, needs protected access
   appList->showCategory(category);
}

void StartMenu::updateShortcuts(ShortcutList & list)
{
   shortcutList = list;
}

void StartMenu::toggleKTTS(bool on)
{
   useKTTS = on;
   if (useKTTS)
   {
      m_spokenText = 0;
      TQPtrListIterator<StartMenuEntry> it(appList->entryList);
      StartMenuEntry *runner;
      while( (runner = it.current()) != 0 )
      {
         ++it;
         connect (runner, SIGNAL(sayText(const TQString&)), this, SIGNAL(sayText(const TQString&)));
      }
      connect (appList, SIGNAL(sayText(const TQString&)), this, SLOT(sayText(const TQString&) ));
   }
   else
   {
      TQPtrListIterator<StartMenuEntry> it(appList->entryList);
      StartMenuEntry *runner;
      while( (runner = it.current()) != 0 )
      {
         ++it;
         disconnect (runner, SIGNAL(sayText(const TQString&)), this, SIGNAL(sayText(const TQString&)));
      }
      disconnect (appList, SIGNAL(sayText(const TQString&)), this, SLOT(sayText(const TQString&) ));
   }
}

SearchLine::SearchLine( TQWidget * parent ) : KLineEdit(parent){blocked = false;};

void SearchLine::makeCompletion (const TQString & string)
{
   if (blocked) {blocked = false; return;}
   emit typedTextChanged(string);
   KLineEdit::makeCompletion (string);
}

StartMenu::~StartMenu()
{
//    appList->save();
}

void StartMenu::sayText(const TQString &text)
{
   // strip tags
   TQString cleanText;
   bool copy = true;
   for (uint i = 0; i < text.length(); i++)
   {
      if (!copy && text[i] == '>') // end tag, set copy true and move on
      {
         copy = true;
         continue;
      }
      if (copy && text[i] == '<') // tag start, set copy false and move on
      {
         copy = false;
         continue;
      }
      if (copy) //copy char ;)
         cleanText += text[i];
   }
   // done
   TQByteArray data1;
   TQDataStream arg1(data1, IO_WriteOnly);
   arg1 << m_spokenText; // stop what we messaged before (if)
   if (!kapp->dcopClient()->send("kttsd", "kspeech", "stopText(uint)", data1))
      tqDebug("there was some error using DCOP.");
   
   TQByteArray data, replyData;
   TQCString replyType;
   TQDataStream arg(data, IO_WriteOnly);
   arg << cleanText << ""; // ask for the full list
   if (!kapp->dcopClient()->call("kttsd", "kspeech", "sayText(TQString, TQString)", data, replyType, replyData))
      tqDebug("there was some error using DCOP.");
   else
   {
      TQDataStream reply(replyData, IO_ReadOnly);
      if (replyType == "uint")
         reply >> m_spokenText;
      else
         tqWarning("properties() returned an unexpected type of reply (%s)!",TQString(replyType).ascii());
   }
}

void StartMenu::show()
{
   m_panel->setBackgroundOrigin(TQWidget::WidgetOrigin);
   statusBar->setBackgroundOrigin(TQWidget::ParentOrigin);
   searchLine->setText(i18n("Type to search or enter a command"));
   searchLine->selectAll();
   searchLine->setFocus();
//    KWin::setOpacity(winId(), 80);
   TQWidget::show();
}

void StartMenu::hide()
{
   emit aboutToHide();
   searchLine->clear();
   appList->reset();
   TQWidget::hide();
}

void StartMenu::message(const TQString &text)
{
   statusBar->setAlignment ( TQt::AlignAuto | TQt::AlignVCenter );
   statusBar->setText(text);
}

void StartMenu::centerMessage(const TQString &text)
{
   statusBar->setAlignment ( TQt::AlignCenter );
   statusBar->setText(text);
}

void StartMenu::clearStatus()
{
   statusBar->clear();
}

void StartMenu::setPanelPosition(PanelPosition p)
{
   if (p == m_panelPos)
      return;
   panelLayout->remove(m_panel);
   switch (p)
   {
   case StartMenu::South:
      m_panel->show();
      if (m_panelPos == StartMenu::Nowhere || m_panelPos == StartMenu::West || m_panelPos == StartMenu::East )
      {
//          m_panel->set2SizePolicies(TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed);
         m_panel->setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed);
         m_panel->setOrientation ( TQt::Horizontal );
         m_panel->setFixedHeight(_BIGSIZE_(_size)+4);
         m_panel->setMaximumWidth(32767);
      }
      panelLayout->addWidget(m_panel, 2, 1);
      break;
   case StartMenu::West:
      m_panel->show();
      if (m_panelPos == StartMenu::Nowhere || m_panelPos == StartMenu::South || m_panelPos == StartMenu::North )
      {
//          m_panel->set2SizePolicies(TQSizePolicy::Fixed, TQSizePolicy::MinimumExpanding);
         m_panel->setSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Expanding);
         m_panel->setOrientation ( TQt::Vertical );
         m_panel->setFixedWidth(_BIGSIZE_(_size)+4);
         m_panel->setMaximumHeight(32767);
      }
      panelLayout->addWidget(m_panel, 1, 0);
      break;
   case StartMenu::East:
      m_panel->show();
      if (m_panelPos == StartMenu::Nowhere || m_panelPos == StartMenu::South || m_panelPos == StartMenu::North )
      {
//          m_panel->set2SizePolicies(TQSizePolicy::Fixed, TQSizePolicy::MinimumExpanding);
         m_panel->setSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Expanding);
         m_panel->setOrientation ( TQt::Vertical );
         m_panel->setFixedWidth(_BIGSIZE_(_size)+4);
         m_panel->setMaximumHeight(32767);
      }
      panelLayout->addWidget(m_panel, 1, 2);
      break;
   case StartMenu::North:
      m_panel->show();
      if (m_panelPos == StartMenu::Nowhere || m_panelPos == StartMenu::West || m_panelPos == StartMenu::East )
      {
//          m_panel->set2SizePolicies(TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed);
         m_panel->setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed);
         m_panel->setOrientation ( TQt::Horizontal );
         m_panel->setFixedHeight(_BIGSIZE_(_size)+4);
         m_panel->setMaximumWidth(32767);
      }
      panelLayout->addWidget(m_panel, 0, 1);
      break;
   default:
      m_panel->hide();
//       break;
   }
   m_panelPos = p;
}

void StartMenu::execute(const TQString& command)
// adapted from kicker run applet - far more convenient ;)
{
   history.remove(command);
   history.append(command); // all - the list is not stored and the user may want to easily correct mistypes of LOOOOOOOOOOOOOOOOOOOONG commands with a lot of "\ "... ;P
   TQString exec;
   
   kapp->propagateSessionManager();
   
   _filterData->setData( command.stripWhiteSpace() );
   TQStringList filters;
   filters << "kurisearchfilter" << "tdeshorturifilter";
   KURIFilter::self()->filterURI( *(_filterData), filters );
   
   TQString cmd = (_filterData->uri().isLocalFile() ? _filterData->uri().path():_filterData->uri().url());
   
    // Nothing interesting. Quit!
   if ( cmd.isEmpty() )
   {
      return;
   }
   else if (cmd == "logout")
   {
      close();
      kapp->requestShutDown();
   }
   else
   {
      switch( _filterData->uriType() )
      {
      case KURIFilterData::LOCAL_FILE:
      case KURIFilterData::LOCAL_DIR:
      case KURIFilterData::NET_PROTOCOL:
      case KURIFilterData::HELP:
         {
            (void) new KRun( _filterData->uri() );
            return;
         }
      case KURIFilterData::EXECUTABLE:
      case KURIFilterData::SHELL:
         {
            exec = cmd;
            if( _filterData->hasArgsAndOptions() )
               cmd += _filterData->argsAndOptions();
            break;
         }
      case KURIFilterData::UNKNOWN:
      case KURIFilterData::ERROR:
      default:
         return;
      }
   }
   KRun::runCommand( cmd, exec, "" );
   return;
}


void StartMenu::endHistory()
{
   currentHistoryItem = history.end();
}

void StartMenu::search(const TQString & string)
{
   disconnect (searchLine, SIGNAL(textChanged ( const TQString & )), this, SLOT(search(const TQString &)));
   appList->search(string);
}

bool StartMenu::eventFilter ( TQObject * o, TQEvent * e )
{
   if (o == header)
   {
      if (e->type() == TQEvent::MouseButtonPress && ((TQMouseEvent*)e)->button() ==  TQt::LeftButton)
      {
         inMove = true;
         movePoint = ((TQMouseEvent*)e)->pos();
         header->grabMouse(TQt::SizeAllCursor);
         return true;
      }
      else if (e->type() == TQEvent::MouseButtonRelease && ((TQMouseEvent*)e)->button() ==  TQt::LeftButton)
      {
         inMove = false;
         header->releaseMouse();
         return true;
      }
      else if (e->type() == TQEvent::MouseMove && inMove)
      {
         move(((TQMouseEvent*)e)->globalPos() - movePoint);
         return true;
      }
      else if (e->type() == TQEvent::Resize && ((TQResizeEvent*)e)->size().height() != ((TQResizeEvent*)e)->oldSize().height())
      {
         int height = ((TQResizeEvent*)e)->size().height();
         KPixmap bgPix = TQPixmap(32, height);
         KPixmap bgPix1 = TQPixmap(32, height/2);
         KPixmap bgPix2 = TQPixmap(32, height - bgPix1.height());
         TQColor buttonColor = ((TQWidget*)o)->palette().color(TQPalette::Active, TQColorGroup::Button);
         KPixmapEffect::gradient( bgPix1, buttonColor.light(130), buttonColor, KPixmapEffect::VerticalGradient, 0);
         KPixmapEffect::gradient( bgPix2, buttonColor.dark(120), buttonColor.light(110), KPixmapEffect::VerticalGradient, 0);
         TQPainter p(&bgPix);
         p.drawPixmap(0,0,bgPix1);
         p.drawPixmap(0,bgPix1.height(),bgPix2);
         p.end();
         ((TQWidget*)o)->setPaletteBackgroundPixmap( bgPix );
      }
      return false;
   }
   if (o != searchLine)
      return false;
   TQLineEdit *le = (TQLineEdit *)o;
   if (e->type() == TQEvent::KeyPress)
   {
      TQKeyEvent *ke = (TQKeyEvent*)e;
      if (ke->state() & TQt::ControlButton || ke->state() & TQt::AltButton)
         // might be shortcut, set category
      {
         if (ke->state() == TQt::ControlButton)
         {
            if (ke->key() == TQt::Key_Up)
            {
               if (categoryCombo->currentItem() > 0)
               {
                  categoryCombo->setCurrentItem ( categoryCombo->currentItem() - 1 );
                  appList->showCategory(categoryCombo->currentText());
               }
               return true;
            }
            if (ke->key() == TQt::Key_Down)
            {
               if (categoryCombo->currentItem() < categoryCombo->count())
               {
                  categoryCombo->setCurrentItem ( categoryCombo->currentItem() + 1 );
                  appList->showCategory(categoryCombo->currentText());
               }
               return true;
            }
         }
         if (ke->key() == TQt::Key_Shift || ke->key() == TQt::Key_Control || ke->key() == TQt::Key_Alt)
            return false;
         ShortcutList::Iterator it;
         for ( it = shortcutList.begin(); it != shortcutList.end(); ++it )
         {
            if (it.key().modFlags() == ke->state() && it.key().key() == ke->key())
               setCategory( it.data() );
         }
         return true; //fire event to prevent lienedit action like ctrl+d -> del etc.
      }
      switch(ke->key())
      {
      case TQt::Key_Return:
      case TQt::Key_Enter:
      {
         execute(le->text());
         if (!(ke->state() & TQt::ControlButton))
            close(); //bye
         return true; //don't do anything else
      }
      case TQt::Key_Backspace:
      case TQt::Key_Delete:
      {
         connect (le, SIGNAL(textChanged ( const TQString & )), this, SLOT(search(const TQString &)));
         break;
      }
      case TQt::Key_Down:
      {
         if (history.isEmpty() || currentHistoryItem == history.end())
         {
            appList->appDown();
         }
         else // navigate history
         {
            le->blockSignals(true);
            le->setText(*currentHistoryItem);
            le->blockSignals(false);
            currentHistoryItem++;
            if (currentHistoryItem == history.end())
               le->selectAll();
         }
         return true; //don't scroll the categories
      }
      case TQt::Key_Up:
      {
         if (!(history.isEmpty() || currentHistoryItem == history.begin()))
         {
            currentHistoryItem--;
            le->blockSignals(true);
            le->setText(*currentHistoryItem);
            le->blockSignals(false);
         }
         return true; //don't scroll the categories
      }
      case TQt::Key_Escape:
      {
         close(); //bye
         break;
      }
      default:
         break;
      }
      return false;
   }
   else if (isVisible() && useKTTS && e->type() == TQEvent::FocusIn)
   {
      TQString text = i18n("TTS output", "The searchline has now the focus.");
      sayText(text);
   }
   return false;
}

#include "menu.moc"
