/*****************************************************************

   Copyright (c) 1996-2000 the kicker authors. See file AUTHORS.
   Copyright (c) 2006 Debajyoti Bera <dbera.web@gmail.com>
   Copyright (c) 2006 Dirk Mueller <mueller@kde.org>

   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; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.

******************************************************************/

#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <dmctl.h>
#include <inttypes.h>

#include <qimage.h>
#include <qpainter.h>
#include <qstyle.h>
#include <qwidgetstack.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qregexp.h>
#include <qfile.h>
#include <qstylesheet.h>
#include <qaccel.h>
#include <qcursor.h>
#include <qdir.h>
#include <qsimplerichtext.h>
#include <qtooltip.h>

#include <dcopclient.h>
#include <kapplication.h>
#include <kaboutkde.h>
#include <kaction.h>
#include <kbookmarkmenu.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kglobal.h>
#include <kglobalsettings.h>
#include <kiconloader.h>
#include <klineedit.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kcombobox.h>
#include <kwin.h>
#include <kdebug.h>
#include <kuser.h>
#include <kurllabel.h>
#include <krun.h>
#include <kmimetype.h>
#include <krecentdocument.h>
#include <kcompletionbox.h>
#include <kurifilter.h>
#include <kbookmarkmanager.h>
#include <kbookmark.h>
#include <kprocess.h>
#include <kio/jobclasses.h>
#include <kio/job.h>
#include <dcopref.h>
#include <konq_popupmenu.h>
#include <konqbookmarkmanager.h>
#include <kparts/componentfactory.h>

#include "client_mnu.h"
#include "container_base.h"
#include "global.h"
#include "knewbutton.h"
#include "kicker.h"
#include "kickerSettings.h"
#include "konqbookmarkmanager.h"
#include "menuinfo.h"
#include "menumanager.h"
#include "popupmenutitle.h"
#include "quickbrowser_mnu.h"
#include "recentapps.h"
#include "flipscrollview.h"
#include "itemview.h"
#include <dmctl.h>
#include <sys/vfs.h>
#include <mykickoffsearchinterface.h>

#include "media_watcher.h"
#include "k_mnu.h"
#include "k_new_mnu.h"
#include "k_new_mnu.moc"

#define WAIT_BEFORE_QUERYING 700

#define IDS_PER_CATEGORY 20
#define ACTIONS_ID_BASE 10
#define APP_ID_BASE 10 + IDS_PER_CATEGORY
#define BOOKMARKS_ID_BASE 10 + (IDS_PER_CATEGORY * 2)
#define NOTES_ID_BASE 10 + (IDS_PER_CATEGORY * 3)
#define MAIL_ID_BASE 10 + (IDS_PER_CATEGORY * 4)
#define FILE_ID_BASE 10 + (IDS_PER_CATEGORY * 5)
#define MUSIC_ID_BASE 10 + (IDS_PER_CATEGORY * 6)
#define WEBHIST_ID_BASE 10 + (IDS_PER_CATEGORY * 7)
#define CHAT_ID_BASE 10 + (IDS_PER_CATEGORY * 8)
#define FEED_ID_BASE 10 + (IDS_PER_CATEGORY * 9)
#define PIC_ID_BASE 10 + (IDS_PER_CATEGORY * 10)
#define VIDEO_ID_BASE 10 + (IDS_PER_CATEGORY * 11)
#define DOC_ID_BASE 10 + (IDS_PER_CATEGORY * 12)
#define OTHER_ID_BASE 10 + (IDS_PER_CATEGORY * 13)

static QString calculate(const QString &exp)
{
   QString result, cmd;
   const QString bc = KStandardDirs::findExe("bc");
   if ( !bc.isEmpty() )
      cmd = QString("echo %1 | %2").arg(KProcess::quote(exp), KProcess::quote(bc));
   else
      cmd = QString("echo $((%1))").arg(exp);
   FILE *fs = popen(QFile::encodeName(cmd).data(), "r");
   if (fs)
   {
      QTextStream ts(fs, IO_ReadOnly);
      result = ts.read().stripWhiteSpace();
      pclose(fs);
   }
   return result;
}

int base_category_id[] = {ACTIONS_ID_BASE, APP_ID_BASE, BOOKMARKS_ID_BASE, NOTES_ID_BASE, MAIL_ID_BASE,
                          FILE_ID_BASE, MUSIC_ID_BASE, WEBHIST_ID_BASE, CHAT_ID_BASE, FEED_ID_BASE,
                          PIC_ID_BASE, VIDEO_ID_BASE, DOC_ID_BASE, OTHER_ID_BASE};

#include <assert.h>

static int used_size( QLabel *label, int oldsize )
{
    QSimpleRichText st( label->text(), KGlobalSettings::toolBarFont() );
    st.setWidth( oldsize );
    return QMAX( st.widthUsed(), oldsize );
}

KMenu::KMenu()
  : KMenuBase(0)
  , m_sloppyTimer(0, "KNewMenu::sloppyTimer"), m_mediaFreeTimer(0, "KNewMenu::mediaFreeTimer"),
    m_iconName(QString::null), m_activeTab( 0 ), m_orientation( Uninitialized ), m_search_plugin( 0 )
{
    setMouseTracking(true);
    connect(&m_sloppyTimer, SIGNAL(timeout()), SLOT(slotSloppyTimeout()));

    // set the first client id to some arbitrarily large value.
    client_id = 10000;
    // Don't automatically clear the main menu.
    actionCollection = new KActionCollection(this);

    connect(Kicker::the(), SIGNAL(configurationChanged()),
            this, SLOT(configChanged()));

    KUser * user = new KUser();

    char hostname[256];
    hostname[0] = '\0';
    if (!gethostname( hostname, sizeof(hostname) ))
      hostname[sizeof(hostname)-1] = '\0';

    m_userInfo->setText( i18n( "User&nbsp;<b>%1</b>&nbsp;on&nbsp;<b>%2</b>" )
                         .arg( user->loginName() ).arg( hostname ) );
    setupUi();

    m_userInfo->setBackgroundMode( PaletteBase );
    QColor userInfoColor = QApplication::palette().color( QPalette::Normal, QColorGroup::Mid );
    if ( qGray( userInfoColor.rgb() ) > 120 )
        userInfoColor = userInfoColor.dark( 200 );
    else
        userInfoColor = userInfoColor.light( 200 );
    m_userInfo->setPaletteForegroundColor( userInfoColor );

    m_favoriteView = new FavoritesItemView (m_stacker, "m_favoriteView");
    m_favoriteView->setAcceptDrops(true);
    m_favoriteView->setItemsMovable(true);
    m_stacker->addWidget(m_favoriteView, 1);

    m_recentlyView = new ItemView (m_stacker, "m_recentlyView");
    m_stacker->addWidget(m_recentlyView, 2);

    m_systemView = new ItemView(m_stacker, "m_systemView");
    m_stacker->addWidget(m_systemView, 3);

    m_browserView = new FlipScrollView(m_stacker, "m_browserView");
    m_stacker->addWidget(m_browserView, 4);
    connect( m_browserView, SIGNAL( backButtonClicked() ), SLOT( slotGoBack() ) );

    m_exitView = new FlipScrollView(m_stacker, "m_exitView");
    m_stacker->addWidget(m_exitView, 5);
    connect( m_exitView, SIGNAL( backButtonClicked() ), SLOT( slotGoExitMainMenu() ) );

    m_searchWidget = new QVBox (m_stacker, "m_searchWidget");
    m_searchWidget->setSpacing(0);
    m_stacker->addWidget(m_searchWidget, 6);

    // search provider icon
    QPixmap icon;
    KURIFilterData data;
    QStringList list;
    data.setData( QString("some keyword") );
    list << "kurisearchfilter" << "kuriikwsfilter";

    if ( KURIFilter::self()->filterURI(data, list) ) {
      QString iconPath = locate("cache", KMimeType::favIconForURL(data.uri()) + ".png");
      if ( iconPath.isEmpty() )
        icon = SmallIcon("enhanced_browsing");
      else
        icon = QPixmap( iconPath );
    }
    else
      icon = SmallIcon("enhanced_browsing");

    m_searchResultsWidget = new ItemView (m_searchWidget, "m_searchResultsWidget");
    m_searchResultsWidget->setItemMargin(4);
    m_searchResultsWidget->setIconSize(16);
    m_searchActions = new ItemView (m_searchWidget, "m_searchActions");
    m_searchActions->setFocusPolicy(QWidget::NoFocus);
    m_searchActions->setItemMargin(4);
    m_searchInternet = new QListViewItem(m_searchActions, i18n("Search Internet"));
    m_searchInternet->setPixmap(0,icon);
    setTabOrder(m_kcommand, m_searchResultsWidget);

    m_kerryInstalled = !KStandardDirs::findExe(QString::fromLatin1("kerry")).isEmpty();
    m_isShowing = false;

    if (!m_kerryInstalled) {
       m_searchIndex = 0;
       m_searchActions->setMaximumHeight(5+m_searchInternet->height());
    }
    else {
       m_searchIndex = new QListViewItem(m_searchActions, i18n("Search Index"));
       m_searchIndex->setPixmap(0,SmallIcon("kerry"));
       m_searchActions->setMaximumHeight(5+m_searchIndex->height()*2);
    }
    connect(m_searchActions, SIGNAL(clicked(QListViewItem*)), SLOT(searchActionClicked(QListViewItem*)));
    connect(m_searchActions, SIGNAL(returnPressed(QListViewItem*)), SLOT(searchActionClicked(QListViewItem*)));
    connect(m_searchActions, SIGNAL(spacePressed(QListViewItem*)), SLOT(searchActionClicked(QListViewItem*)));

    connect(m_searchResultsWidget, SIGNAL(startService(KService::Ptr)), SLOT(slotStartService(KService::Ptr)));
    connect(m_searchResultsWidget, SIGNAL(startURL(const QString&)), SLOT(slotStartURL(const QString&)));
    connect(m_searchResultsWidget, SIGNAL(rightButtonPressed( QListViewItem*, const QPoint &, int )), SLOT(slotContextMenuRequested( QListViewItem*, const QPoint &, int )));

    connect(m_recentlyView, SIGNAL(startService(KService::Ptr)), SLOT(slotStartService(KService::Ptr)));
    connect(m_recentlyView, SIGNAL(startURL(const QString&)), SLOT(slotStartURL(const QString&)));
    connect(m_recentlyView, SIGNAL(rightButtonPressed( QListViewItem*, const QPoint &, int  )), SLOT(slotContextMenuRequested( QListViewItem*, const QPoint &, int )));

    connect(m_favoriteView, SIGNAL(startService(KService::Ptr)), SLOT(slotStartService(KService::Ptr)));
    connect(m_favoriteView, SIGNAL(startURL(const QString&)), SLOT(slotStartURL(const QString&)));
    connect(m_favoriteView, SIGNAL(rightButtonPressed( QListViewItem*, const QPoint &, int  )), SLOT(slotContextMenuRequested( QListViewItem*, const QPoint &, int )));
    connect(m_favoriteView, SIGNAL(moved(QListViewItem*, QListViewItem*, QListViewItem*)), SLOT(slotFavoritesMoved( QListViewItem*, QListViewItem*, QListViewItem* )));

    connect(m_systemView, SIGNAL(startURL(const QString&)), SLOT(slotStartURL(const QString&)));
    connect(m_systemView, SIGNAL(startService(KService::Ptr)), SLOT(slotStartService(KService::Ptr)));
    connect(m_systemView, SIGNAL(rightButtonPressed( QListViewItem*, const QPoint &, int )), SLOT(slotContextMenuRequested( QListViewItem*, const QPoint &, int )));

    connect(m_browserView, SIGNAL(startURL(const QString&)), SLOT(slotGoSubMenu(const QString&)));
    connect(m_browserView, SIGNAL(startService(KService::Ptr)), SLOT(slotStartService(KService::Ptr)));
    connect(m_browserView, SIGNAL(rightButtonPressed( QListViewItem*, const QPoint &, int )), SLOT(slotContextMenuRequested( QListViewItem*, const QPoint &, int )));

    connect(m_exitView, SIGNAL(startURL(const QString&)), SLOT(slotStartURL(const QString&)));
    connect(m_exitView, SIGNAL(rightButtonPressed( QListViewItem*, const QPoint &, int )), SLOT(slotContextMenuRequested( QListViewItem*, const QPoint &, int )));

    m_kcommand->setDuplicatesEnabled( false );
    m_kcommand->setLineEdit(new KLineEdit(m_kcommand, "m_kcommand-lineedit"));
    m_kcommand->setCompletionMode( KGlobalSettings::CompletionAuto );
    connect(m_kcommand, SIGNAL(cleared()), SLOT(clearedHistory()));
    connect(m_kcommand->lineEdit(), SIGNAL(returnPressed()), SLOT(searchAccept()));
    connect(m_kcommand->lineEdit(), SIGNAL(textChanged(const QString &)), SLOT(searchChanged(const QString &)));

    // URI Filter meta object...
    m_filterData = new KURIFilterData();

    max_category_id = new int [num_categories];
    categorised_hit_total = new int [num_categories];

    input_timer = new QTimer (this, "input_timer");
    connect( input_timer, SIGNAL(timeout()), this, SLOT(doQuery()) );

    init_search_timer = new QTimer (this, "init_search_timer");
    connect( input_timer, SIGNAL(timeout()), this, SLOT(initSearch()) );
    init_search_timer->start(2000, true);

    connect( m_favoriteView, SIGNAL( dropped (QDropEvent *, QListViewItem * ) ),
             SLOT( slotFavDropped( QDropEvent *, QListViewItem * ) ) );

    this->installEventFilter(this);
    m_btnFavorites->installEventFilter(this);
    m_btnFavorites->setAcceptDrops(true);
    m_favoriteView->installEventFilter(this);
    m_btnRecently->installEventFilter(this);
    m_recentlyView->installEventFilter(this);
    m_browser->installEventFilter(this);
    m_browserView->leftView()->installEventFilter(this);
    m_browserView->rightView()->installEventFilter(this);
    m_system->installEventFilter(this);
    m_systemView->installEventFilter(this);
    m_exit->installEventFilter(this);
    m_exitView->leftView()->installEventFilter(this);
    m_exitView->rightView()->installEventFilter(this);
    m_kcommand->lineEdit()->installEventFilter(this);
    m_searchLabel->installEventFilter(this);
    m_searchPixmap->installEventFilter(this);
    m_stacker->installEventFilter(this);

    connect(this, SIGNAL(clickedFavoritesButton()), SLOT(slotFavoritesButton()));
    connect(this, SIGNAL(clickedRecentlyButton()), SLOT(slotRecentlyButton()));
    connect(this, SIGNAL(clickedBrowserButton()), SLOT(slotBrowserButton()));
    connect(this, SIGNAL(clickedSystemButton()), SLOT(slotSystemButton()));
    connect(this, SIGNAL(clickedExitButton()), SLOT(slotExitButton()));

    QAccel *a = new QAccel( this );
    a->connectItem( a->insertItem( QAccel::shortcutKey("&"+m_btnFavorites_text->text().mid(m_btnFavorites_text->text().find("<u>")+3,1))) , this, SLOT(slotFavoritesButton()) );
    a->connectItem( a->insertItem( QAccel::shortcutKey("&"+m_btnRecently_text->text().mid( m_btnRecently_text->text().find("<u>")+3,1))), this, SLOT(slotRecentlyButton()) );
    a->connectItem( a->insertItem( QAccel::shortcutKey("&"+m_system_text->text().mid( m_system_text->text().find("<u>")+3,1))), this, SLOT(slotSystemButton()) );
    a->connectItem( a->insertItem( QAccel::shortcutKey("&"+m_browser_text->text().mid( m_browser_text->text().find("<u>")+3,1))), this, SLOT(slotBrowserButton()) );
    a->connectItem( a->insertItem( QAccel::shortcutKey("&"+m_exit_text->text().mid( m_exit_text->text().find("<u>")+3,1))), this, SLOT(slotExitButton()) );
//    a->connectItem( a->insertItem( QAccel::shortcutKey(m_lock->textLabel())), this, SLOT(slotLock())) ;

    m_btnFavorites->setPaletteForegroundColor(Qt::black);
    m_btnRecently->setPaletteForegroundColor(Qt::black);
    m_browser->setPaletteForegroundColor(Qt::black);
    m_system->setPaletteForegroundColor(Qt::black);
    m_exit->setPaletteForegroundColor(Qt::black);
    m_searchFrame->setPaletteForegroundColor(Qt::black);

    emailRegExp = QRegExp("^([\\w\\-]+\\.)*[\\w\\-]+@([\\w\\-]+\\.)*[\\w\\-]+$");
    authRegExp = QRegExp("^[a-zA-Z]+://\\w+(:\\w+)?@([\\w\\-]+\\.)*[\\w\\-]+(:\\d+)?(/.*)?$");
    uriRegExp = QRegExp("^[a-zA-Z]+://([\\w\\-]+\\.)*[\\w\\-]+(:\\d+)?(/.*)?$");
    uri2RegExp = QRegExp("^([\\w\\-]+\\.)+[\\w\\-]+(:\\d+)?(/.*)?$");

    m_resizeHandle = new QLabel(this);
    m_resizeHandle->setBackgroundOrigin( QLabel::ParentOrigin );
    m_resizeHandle->setScaledContents(true);
    m_resizeHandle->setFixedSize( 16, 16 );
    m_searchFrame->stackUnder( m_resizeHandle );
    m_isresizing = false;

    m_searchPixmap->setPixmap( BarIcon( "find", 32 ) );

//    m_lock->setPixmap( BarIcon( "lock", 32 ) );
    const int tab_icon_size = 32;
    pixmapLabel_fav->setPixmap( BarIcon( "bookmark", tab_icon_size ) );
    pixmapLabel_fav->setFixedSize( tab_icon_size, tab_icon_size );
    pixmapLabel_recent->setPixmap( BarIcon( "recently_used", tab_icon_size ) );
    pixmapLabel_recent->setFixedSize( tab_icon_size, tab_icon_size );
    pixmapLabel_system->setPixmap( BarIcon( "system", tab_icon_size ) );
    pixmapLabel_system->setFixedSize( tab_icon_size, tab_icon_size );
    pixmapLabel_browser->setPixmap( BarIcon( "player_playlist", tab_icon_size ) );
    pixmapLabel_browser->setFixedSize( tab_icon_size, tab_icon_size );
    pixmapLabel_exit->setPixmap( BarIcon( "leave", tab_icon_size ) );
    pixmapLabel_exit->setFixedSize( tab_icon_size, tab_icon_size );

    QFont f = font();
    f.setPointSize( QMAX( 7, qRound( f.pointSize() * 0.80 ) ) );
    m_exit_text->setFont( f );
    m_btnFavorites_text->setFont( f );
    m_btnRecently_text->setFont( f );
    m_btnRecently_text->setFont( f );
    m_browser_text->setFont( f );
    m_system_text->setFont( f );
    m_exit_text->setFont( f );

    f.setPointSize( QMAX( 7, qRound( f.pointSize() * 1.50 ) ) );
    m_searchLabel->setFont( f );

    int minwidth = 32;
    minwidth = used_size( m_btnFavorites_text, minwidth );
    minwidth = used_size( m_btnRecently_text, minwidth );
    minwidth = used_size( m_btnRecently_text, minwidth );
    minwidth = used_size( m_browser_text, minwidth );
    minwidth = used_size( m_system_text, minwidth );
    minwidth = used_size( m_exit_text, minwidth );

    m_btnFavorites_text->setMinimumWidth( minwidth );
    m_btnRecently_text->setMinimumWidth( minwidth );
    m_browser_text->setMinimumWidth( minwidth );
    m_system_text->setMinimumWidth( minwidth );
    m_exit_text->setMinimumWidth( minwidth );

    const int bottom_margin = qRound(3. * QPaintDevice::x11AppDpiY( x11Screen () ) / 72.);

    m_btnFavorites_spacer->changeSize( 1, bottom_margin, QSizePolicy::Fixed,  QSizePolicy::Fixed);
    m_btnRecently_spacer->changeSize( 1, bottom_margin, QSizePolicy::Fixed,  QSizePolicy::Fixed );
    m_browser_spacer->changeSize( 1, bottom_margin, QSizePolicy::Fixed,  QSizePolicy::Fixed );
    m_system_spacer->changeSize( 1, bottom_margin, QSizePolicy::Fixed,  QSizePolicy::Fixed );
    m_exit_spacer->changeSize( 1, bottom_margin, QSizePolicy::Fixed,  QSizePolicy::Fixed );

#if KDE_IS_VERSION( 3, 5, 4 )
    static_cast<KLineEdit*>(m_kcommand->lineEdit())->setClickMessage(i18n( "Applications, Contacts and Documents" ) );
#endif

    bookmarkManager = 0;
    m_addressBook = 0;
    m_popupMenu = 0;

    main_border_lc.load( locate("appdata", "pics/main_border_lc.png" ) );
    main_border_rc.load( locate("appdata", "pics/main_border_rc.png" ) );

    main_border_tl.load( locate("appdata", "pics/main_corner_tl.png" ) );
    main_border_tr.load( locate("appdata", "pics/main_corner_tr.png" ) );

    search_tab_left.load( locate("appdata", "pics/search-tab-left.png" ) );
    search_tab_right.load( locate("appdata", "pics/search-tab-right.png" ) );
    search_tab_center.load( locate("appdata", "pics/search-tab-center.png" ) );

    QToolTip::add( m_btnRecently, "<qt>" + i18n( "Recently used applications and documents" ) + "</qt>" );
    QToolTip::add( m_btnFavorites, "<qt>" + i18n( "Most commonly used applications and documents" )  + "</qt>" );
    QToolTip::add( m_browser, "<qt>" + i18n( "List of installed applications" ) + "</qt>" );
    QToolTip::add( m_system, "<qt>" + i18n( "Information and configuration of your system, access to "
                                            "personal files, network resources and connected disk drives" ) + "</qt>" );
#warning FIXME after test
//    QToolTip::add( m_exit, "<qt>" + i18n( "Logout, switch user, switch off or reset, suspend of the system" ) + "</qt>" );
    QToolTip::add( m_exit, i18n( "<qt>Logout, switch user, switch off or reset, suspend of the system" ) + "</qt>" );
    QToolTip::add( m_searchFrame, "<qt>" + i18n( "Search for personal files and applications" ) + "</qt>" );
//    QToolTip::add( m_lock, "<qt>" + i18n( "Locks screen for the current session" ) + "</qt>" );
//    QToolTip::add( m_helpLabel, "<qt>" + i18n( "Shows Help" ) + "</qt>" );

    setOrientation(BottomUp);
}

void KMenu::setupUi()
{
    m_stacker = new QWidgetStack( this, "m_stacker" );
    m_stacker->setGeometry( QRect( 90, 260, 320, 220 ) );
    m_stacker->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)3, (QSizePolicy::SizeType)3, 1, 1, m_stacker->sizePolicy().hasHeightForWidth() ) );
    m_stacker->setPaletteBackgroundColor( QColor( 255, 255, 255 ) );
   // m_stacker->setFocusPolicy( QWidget::StrongFocus );
    m_stacker->setLineWidth( 0 );
    m_stacker->setFocusPolicy(QWidget::NoFocus);
    connect(m_stacker, SIGNAL(aboutToShow(QWidget*)), SLOT(stackWidgetRaised(QWidget*)));

    m_kcommand->setName("m_kcommand");

}

KMenu::~KMenu()
{
    saveConfig();

    clearSubmenus();
    delete m_filterData;
}

bool KMenu::loadSidePixmap()
{
    if (!KickerSettings::useSidePixmap())
    {
        return false;
    }

    QString sideName = KickerSettings::sidePixmapName();
    QString sideTileName = KickerSettings::sideTileName();

    QImage image;
    image.load(locate("data", "kicker/pics/" + sideName));

    if (image.isNull())
    {
        kdDebug(1210) << "Can't find a side pixmap" << endl;
        return false;
    }

    KickerLib::colorize(image);
    sidePixmap.convertFromImage(image);

    image.load(locate("data", "kicker/pics/" + sideTileName));

    if (image.isNull())
    {
        kdDebug(1210) << "Can't find a side tile pixmap" << endl;
        return false;
    }

    KickerLib::colorize(image);
    sideTilePixmap.convertFromImage(image);

    if (sidePixmap.width() != sideTilePixmap.width())
    {
        kdDebug(1210) << "Pixmaps have to be the same size" << endl;
        return false;
    }

    // pretile the pixmap to a height of at least 100 pixels
    if (sideTilePixmap.height() < 100)
    {
        int tiles = (int)(100 / sideTilePixmap.height()) + 1;
        QPixmap preTiledPixmap(sideTilePixmap.width(), sideTilePixmap.height() * tiles);
        QPainter p(&preTiledPixmap);
        p.drawTiledPixmap(preTiledPixmap.rect(), sideTilePixmap);
        sideTilePixmap = preTiledPixmap;
    }

    return true;
}

bool KMenu::eventFilter ( QObject * receiver, QEvent* e)
{
//kdDebug() << "eventFilter receiver=" << receiver->name() << "  type=" << e->type() << endl;
    QWidget* raiseWidget = 0;
    QFrame *raiseFrame = 0;

    if (e->type() ==  QEvent::KeyPress ||
        e->type() == QEvent::MouseMove
        || e->type() == QEvent::FocusIn
        || e->type() == QEvent::Wheel) {
        QPoint p;

        if (e->type() == QEvent::MouseMove) {
            QMouseEvent* me = static_cast<QMouseEvent*>(e);
            p = me->globalPos();
        }
        else if (e->type() == QEvent::Wheel) {
            QWheelEvent* we = static_cast<QWheelEvent*>(e);
            p = we->globalPos();
        }

	while (receiver) {
	    if (receiver == m_browser)
		raiseWidget = m_browserView;
	    if (receiver == m_btnFavorites)
		raiseWidget = m_favoriteView;
	    if (receiver == m_btnRecently)
		raiseWidget = m_recentlyView;
	    if (receiver == m_system)
		raiseWidget = m_systemView;
	    if (receiver == m_exit)
		raiseWidget = m_exitView;
	    if ( raiseWidget )
		raiseFrame = dynamic_cast<QFrame*>(receiver);
            /* we do not want hover activation for the search line edit as this can be
             * pretty disturbing */
	    if ( (receiver == m_searchPixmap ||
                  ( ( receiver == m_searchLabel || receiver==m_kcommand->lineEdit() ) &&
                  ( e->type() == QEvent::KeyPress || e->type() == QEvent::Wheel ) ) ) &&
                  !m_isShowing) {
		 raiseWidget = m_searchWidget;
		 raiseFrame = m_searchFrame;
	    }

	    if(raiseWidget)
		break;
	    if(receiver->isWidgetType())
		receiver = static_cast<QWidget*>(receiver)->parentWidget(true);
	    else
		break;
	}

        if (e->type() == QEvent::FocusIn && receiver && raiseWidget) {
            m_searchResultsWidget->setFocusPolicy(QWidget::StrongFocus);
            m_searchActions->setFocusPolicy(raiseWidget == m_searchWidget ?
                    QWidget::StrongFocus : QWidget::NoFocus);
            setTabOrder(raiseWidget, m_searchResultsWidget);
            if (raiseWidget != m_stacker->visibleWidget()
                && static_cast<QWidget*>(receiver)->focusPolicy() == QWidget::NoFocus
                && m_stacker->id(raiseWidget) >= 0) {

                raiseStackWidget(raiseWidget);
                return true;
            }

            if (raiseWidget->focusPolicy() != QWidget::NoFocus)
                return false;
        }

	if (m_sloppyRegion.contains(p)) {
            if (!m_sloppyTimer.isActive() || m_sloppyFrame != raiseFrame)
                m_sloppyTimer.start(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay)/2);
	    m_sloppyWidget = raiseWidget;
	    m_sloppyFrame = raiseFrame;
	    return false;
	}
    }

    if(e->type() == QEvent::Enter && receiver->isWidgetType()) {
	static_cast<QWidget*>(receiver)->setMouseTracking(true);
        QToolTip::hide();
    }

    if ( ( e->type() == QEvent::DragEnter || e->type() == QEvent::DragMove ) &&
         receiver == m_btnFavorites )
    {
        if ( m_activeTab != m_btnFavorites )
            QTimer::singleShot( 0, this, SLOT( slotFavoritesButton() ) );

	return false;
    }

    // This is a nightmare of a hack, look away. Logic needs
    // to be moved to the stacker and all widgets in the stacker
    // must have focusNextPrevChild() overwritten to do nothing
    if (e->type() == QEvent::KeyPress && raiseFrame) {
        ItemView* view;
        if (m_browserView==m_stacker->visibleWidget())
            view = m_browserView->currentView();
        else if (m_exitView==m_stacker->visibleWidget())
            view = m_exitView->currentView();
        else
            view = dynamic_cast<ItemView*>(m_stacker->visibleWidget());

        if (view)
        {
            bool handled = true;
            switch (static_cast<QKeyEvent*>(e)->key()) {
                case Key_Up:
                    if (view->selectedItem()) {
                        view->setSelected(view->selectedItem()->itemAbove(),true);
                    }
                    else {
                        view->setSelected(view->lastItem(),true);
                    }
                    break;
                case Key_Down:
                    if (view->selectedItem()) {
                        view->setSelected(view->selectedItem()->itemBelow(),true);
                    }
                    else {
                        view->setSelected(view->firstChild(),true);
                    }
                    break;
                case Key_Right:
                    if (view->selectedItem() && !static_cast<KMenuItem*>(view->selectedItem())->hasChildren())
                        break;
                    // nobreak
                case Key_Enter:
                case Key_Return:
                    if (view->selectedItem())
                        view->slotItemClicked(view->selectedItem());

                    break;
                case Key_Left:
                    if (view->selectedItem() && !static_cast<KMenuItem*>(view->selectedItem())->hasChildren())
                        break;
                    // nobreak
                case Key_Backspace:
                    if ((m_browserView == m_stacker->visibleWidget() || m_exitView == m_stacker->visibleWidget())
                            && view->firstChild())
                        view->slotItemClicked(view->firstChild());

                    break;
                default:
                    handled = false;
            }

            if (handled)
                view->ensureItemVisible(view->selectedItem());

            return handled;
        }
    }

    bool r = KMenuBase::eventFilter(receiver, e);

    if (!r && raiseWidget)
	raiseStackWidget(raiseWidget);

    if (e->type() == QEvent::Wheel && raiseWidget )
    {
        // due to an ugly Qt bug we have to kill wheel events
        // that cause focus switches
        r = true;
    }

    if (e->type() == QEvent::Enter && receiver == m_stacker)
    {
        QRect r(m_stacker->mapToGlobal(QPoint(-8,-64)), m_stacker->size());
        r.setSize(r.size()+QSize(16,128));

        m_sloppyRegion = QRegion(r);
    }

    // redo the sloppy region
    if (e->type() == QEvent::MouseMove && !r && raiseWidget)
    {
        QPointArray points(4);

        points.setPoint(0, m_stacker->mapToGlobal(m_stacker->rect().bottomLeft()));
        points.setPoint(1, m_stacker->mapToGlobal(m_stacker->rect().bottomRight()));

        // hmm, eventually this should be mouse position + 10px, not
        // just worst case. but worst case seems to work fine enough.
        QPoint edge(raiseFrame->mapToGlobal(QPoint(0,0)));
        edge.setY(edge.y()+raiseFrame->rect().height());
        edge.setX(edge.x()+raiseFrame->rect().center().x());

        points.setPoint(2, edge+QPoint(-16,0));
        points.setPoint(3, edge+QPoint(16,0));

        m_sloppyRegion = QRegion(points);
    }

    return r;
}

void KMenu::slotBrowserButton()
{
    if ( m_activeTab == m_browser)
       slotGoSubMenu(QString::null);
    else
       raiseStackWidget( m_browserView );
}

void KMenu::slotSystemButton()
{
    raiseStackWidget( m_systemView );
}

void KMenu::slotExitButton()
{
    if ( m_activeTab == m_exit)
       slotGoExitMainMenu();
    else
       raiseStackWidget( m_exitView );
}

void KMenu::slotRecentlyButton()
{
    raiseStackWidget( m_recentlyView );
}

void KMenu::slotFavoritesButton()
{
    raiseStackWidget( m_favoriteView );
}

void KMenu::slotSloppyTimeout()
{
    if (m_sloppyRegion.contains(QCursor::pos())
        && m_sloppyFrame)
    {
        QRect sloppyFrameRect(m_sloppyFrame->mapToGlobal(QPoint(0,0)),
                m_sloppyFrame->size());

        if ( sloppyFrameRect.contains(QCursor::pos()))
        {
            raiseStackWidget(m_sloppyWidget);

            m_sloppyWidget = 0;
            m_sloppyFrame = 0;
            m_sloppyRegion = QRegion();
        }
    }
    m_sloppyTimer.stop();
}

void KMenu::paintSearchTab( bool active )
{
    QPixmap canvas( m_searchFrame->size() );
    QPainter p( &canvas );

    QPixmap pix;

    if ( m_orientation == BottomUp )
        pix.load( locate("appdata", "pics/search-gradient.png" ) );
    else
        pix.load( locate("appdata", "pics/search-gradient-topdown.png" ) );

    pix.convertFromImage( pix.convertToImage().scale(pix.width(), m_searchFrame->height()));
    p.drawTiledPixmap( 0, 0, m_searchFrame->width(), m_searchFrame->height(), pix );

    if ( active ) {

        p.setBrush( Qt::white );
        p.setPen( Qt::NoPen );

        search_tab_center.convertFromImage( search_tab_center.convertToImage().scale(search_tab_center.width(), m_searchFrame->height()));
        p.drawTiledPixmap( search_tab_left.width(), 0, m_searchFrame->width()-search_tab_left.width()-search_tab_right.width(), m_searchFrame->height(), search_tab_center );

        search_tab_left.convertFromImage( search_tab_left.convertToImage().scale(search_tab_left.width(), m_searchFrame->height()));
        p.drawPixmap( 0, 0, search_tab_left );

        search_tab_right.convertFromImage( search_tab_right.convertToImage().scale(search_tab_right.width(), m_searchFrame->height()));
        p.drawPixmap( m_searchFrame->width()-search_tab_right.width(), 0, search_tab_right );
    }

    p.end();
    m_searchFrame->setPaletteBackgroundPixmap( canvas );
}

void KMenu::paintTab( QFrame *tab, bool active )
{
    if ( !tab )
        return;

    if ( tab==m_searchFrame ) {
        paintSearchTab( active );
        return;
    }

    QPixmap canvas( tab->size() );
    QPainter p( &canvas );

    QPixmap pix;

    if ( m_orientation == BottomUp )
        pix.load( locate( "appdata", "pics/button-box-gradient.png" ) );
    else
        pix.load( locate( "appdata", "pics/button-box-gradient-topdown.png" ) );

    pix.convertFromImage( pix.convertToImage().scale(1, tab->height()));
    p.drawTiledPixmap( 0, 0, tab->width(), tab->height(), pix );

    pix.load( locate( "appdata", "pics/button-box-top.png" ) );
    p.drawTiledPixmap( 0, 0, tab->width(), pix.height(), pix );
    int xpos = pix.width();

    pix.load( locate( "appdata", "pics/button-box-left-corner.png" ) );
    if ( tab->x() < pix.width() )
        p.drawPixmap( xpos, 0, pix );

    pix.load( locate( "appdata", "pics/button-box-right-corner.png" ) );
    if ( tab->geometry().right() + pix.width() >= buttonBox->mainWidget()->width() )
    {
        p.drawPixmap( tab->width() - pix.width(), 0, pix );
    }

    const int bottom_margin = m_btnFavorites_spacer->sizeHint().height();

    if ( active ) {

        p.setBrush( Qt::white );
        p.setPen( Qt::NoPen );
        const int left = tab_bottom_left.width();
        const int right = tab_bottom_right.width();
        if ( m_orientation == BottomUp )
        {
            p.drawRect( tab_left_center.width(), 0,
                    tab->width() - tab_left_center.width() - tab_right_center.width(),
                    tab->height() - tab_center.height() - bottom_margin );

            p.drawTiledPixmap( left, tab->height() - tab_center.height() - bottom_margin,
                    tab->width() - left - right,
                    tab_center.height(), tab_center );
        }
        else {
            p.drawRect( tab_left_center.width(), tab_center.height() + bottom_margin,
                    tab->width() - tab_left_center.width() - tab_right_center.width(),
                    tab->height() - tab_center.height() );

            p.drawTiledPixmap( left, bottom_margin,
                    tab->width() - left - right,
                    tab_center.height(), tab_center );
        }


        /* spills out tons of warnings ;(
         * QPixmap tab_center_rest = tab_center;
         * tab_center_rest.resize( tab_center.width(), tab_center.height() - tab_bottom_left.height() );
         */


        if ( m_orientation == BottomUp )
        {
            QPixmap tab_center_rest( tab_center.width(), tab_center.height() - tab_bottom_left.height() );
            QPainter pixp( &tab_center_rest );
            pixp.drawPixmap( 0, 0, tab_center );
            pixp.end();

            p.drawTiledPixmap( tab_left_center.width(),
                    tab->height() - tab_center.height() - bottom_margin,
                    left - tab_left_center.width(),
                    tab_center.height() - tab_bottom_left.height(),
                    tab_center_rest );
            p.drawTiledPixmap( tab->width() - right,
                    tab->height() - tab_center.height() - bottom_margin,
                    right - tab_left_center.width(),
                    tab_center.height() - tab_bottom_left.height(),
                    tab_center_rest );

            // top left corner
            p.drawPixmap( 0, 0, tab_top_left );
            // top right corner
            p.drawPixmap( tab->width() - tab_bottom_right.width(), 0,
                    tab_top_right );
            // bottom left corner
            p.drawPixmap( 0,
                    tab->height() - tab_bottom_left.height() - bottom_margin,
                    tab_bottom_left );
            // left edge, between corners
            p.drawTiledPixmap( 0, tab_top_left.height(),
                    tab_left_center.width(),
                    tab->height() - tab_bottom_left.height() - tab_top_left.height() - bottom_margin,
                    tab_left_center );
            // right edge, between corners
            p.drawTiledPixmap( tab->width() - tab_right_center.width(),
                    0,
                    tab_left_center.width(),
                    tab->height() - tab_bottom_right.height() - bottom_margin,
                    tab_right_center );
            // bottom right corner
            p.drawPixmap( tab->width() - tab_bottom_right.width(),
                    tab->height() - tab_bottom_right.height() - bottom_margin,
                    tab_bottom_right );
        }
        else
        {
            QPixmap tab_center_rest( tab_center.width(), tab_center.height() - tab_bottom_left.height() );
            QPainter pixp( &tab_center_rest );
            pixp.drawPixmap( 0, -( tab_bottom_left.height() ), tab_center );
            pixp.end();

            p.drawTiledPixmap( tab_left_center.width(),
                    bottom_margin + tab_top_left.height(),
                    left - tab_left_center.width(),
                    tab_center_rest.height()/* - tab_bottom_left.height()*/,
                    tab_center_rest );

            p.drawTiledPixmap( tab->width() - right,
                    bottom_margin + tab_top_left.height(),
                    /*tab->height() - tab_center.height() - bottom_margin,*/
                    right - tab_right_center.width(),

                    tab_center_rest.height(),
                    tab_center_rest );

            // top left corner
            p.drawPixmap( 0, bottom_margin, tab_top_left );
            // bottom left corner
            p.drawPixmap( 0, tab->height() - tab_bottom_left.height(),
                    tab_bottom_left );
            // left edge, between corners
              p.drawTiledPixmap( 0, tab_top_left.height() + bottom_margin,
              tab_left_center.width(),
              tab->height() - tab_bottom_left.height() - tab_top_left.height() - bottom_margin,
              tab_left_center );
            // right edge, between corners
            p.drawTiledPixmap( tab->width() - tab_right_center.width(), tab_top_right.height() + bottom_margin,
            tab_bottom_right.width(),
            tab->height() - tab_top_right.height() - tab_bottom_right.height() - bottom_margin,
            tab_right_center );

            // top right corner
            p.drawPixmap( tab->width() - tab_bottom_right.width(),
                    bottom_margin, tab_top_right );
            // bottom right corner
            p.drawPixmap( tab->width() - tab_bottom_right.width(),
                    tab->height() - tab_bottom_right.height(), tab_bottom_right );
        }

    }

    p.end();
    tab->setPaletteBackgroundPixmap( canvas );
}

void KMenu::stackWidgetRaised(QWidget* raiseWidget)
{
    QFrame * frame = 0;

    if (raiseWidget == m_searchWidget)
	frame = m_searchFrame;
    else if (raiseWidget == m_browserView) {
        if ( m_activeTab == m_browser)
          return;

	frame = m_browser;
        if (m_browserDirty ) {
          createNewProgramList();
          m_browserView->prepareRightMove();
          m_browserView->currentView()->clear();
          fillSubMenu(QString::null, m_browserView->currentView());
          m_browserDirty = false;
        }
    }
    else if (raiseWidget == m_systemView)
	frame = m_system;
    else if (raiseWidget==m_recentlyView) {
	frame = m_btnRecently;
        if (m_recentDirty)
            updateRecent();
    }
    else if (raiseWidget == m_favoriteView)
	frame = m_btnFavorites;
    else if (raiseWidget == m_exitView) {
        if ( m_activeTab == m_exit )
          return;

	frame = m_exit;
        slotGoExitMainMenu();
    }

    if (!frame)
      return;

    if ( m_activeTab == frame )
        return;

    paintTab( m_activeTab, false );
    paintTab( frame, true );

   // if (dynamic_cast<QScrollView*>(raiseWidget))
   //     m_activeTab->setFocusProxy(static_cast<QScrollView*>(raiseWidget)->viewport());

    if (0 && /*raiseWidget == m_stacker->visibleWidget() &&*/ !raiseWidget->hasFocus()) {

        if (dynamic_cast<QScrollView*>(raiseWidget))
            static_cast<QScrollView*>(raiseWidget)->viewport()->setFocus();
        else
            raiseWidget->setFocus();
    }

    m_activeTab = frame;

    m_sloppyRegion = QRegion();
    m_sloppyTimer.stop();

    ItemView* view;
    if (raiseWidget == m_browserView)
        view = m_browserView->currentView();
    else if (raiseWidget == m_exitView)
        view = m_exitView->currentView();
    else
        view = dynamic_cast<ItemView*>(m_stacker->visibleWidget());
    if (view && !view->selectedItem()) {
	if (view->firstChild() && view->firstChild()->isSelectable()) {
     	    view->setSelected(view->firstChild(),true);
        }
        else if (view->childCount()>1) {
       	    view->setSelected(view->firstChild()->itemBelow(),true);
        }
    }
}

void KMenu::raiseStackWidget( QWidget *raiseWidget )
{
    if (!raiseWidget)
      return;


    m_stacker->raiseWidget(raiseWidget);
}

void KMenu::paletteChanged()
{
    if (!loadSidePixmap())
    {
        sidePixmap = sideTilePixmap = QPixmap();
        setMinimumSize( sizeHint() );
    }
}

void KMenu::slotGoBack()
{
    goSubMenu( m_browserView->currentView()->backPath() );
}

void KMenu::slotGoExitMainMenu()
{
    if (m_exitView->currentView()==m_exitView->rightView()) {
      m_exitView->prepareLeftMove(false);
      m_exitView->showBackButton(false);
      m_exitView->flipScroll(QString::null);
    }
}

void KMenu::slotGoExitSubMenu(const QString& url)
{
    m_exitView->prepareRightMove();
    m_exitView->showBackButton(true);

    int nId = serviceMenuEndId() + 1;
    int index = 1;

    if (url=="kicker:/restart/") {
      QStringList rebootOptions;
      int def, cur;
      if ( DM().bootOptions( rebootOptions, def, cur ) )
      {
        if ( cur == -1 )
            cur = def;

        int boot_index = rebootOptions.count() - 1;
        QStringList::ConstIterator it = rebootOptions.end();
        --it;
        for (; it != rebootOptions.end(); --it, --boot_index)
        {

            QString option = i18n( "Start '%1'" ).arg( *it );
            if (boot_index == cur)
                option = i18n("Start '%1' (current)").arg( *it );
            m_exitView->rightView()->insertItem( "reload", option,
                    i18n( "Restart and boot directly into '%1'").arg( *it ),
                    QString( "kicker:/restart_%1" ).arg( boot_index ), nId++, index++ );
        }
        m_exitView->rightView()->insertHeader( nId++, "kicker:/restart/" );
      }
    }
    else /*if (url=="kicker:/switchuser/") */{
        m_exitView->rightView()->insertItem( "switchuser", i18n( "Start New Session" ),
                     i18n( "Start a parallel session" ), "kicker:/switchuser", nId++, index++ );

        m_exitView->rightView()->insertItem( "lock", i18n( "Lock Current && Start New Session").replace("&&","&"),
                     i18n( "Lock screen and start a parallel session" ), "kicker:/switchuserafterlock", nId++, index++ );

       SessList sess;
       if (DM().localSessions( sess )) {
          if (sess.count()>1)
              m_exitView->rightView()->insertSeparator( nId++, QString::null, index++ );
          for (SessList::ConstIterator it = sess.begin(); it != sess.end(); ++it) {
              if ((*it).vt && !(*it).self) {
                  QString user, loc;
                  DM().sess2Str2( *it, user, loc );
                  QStringList list = QStringList::split(":", user);
                  m_exitView->rightView()->insertItem( "switchuser", i18n( "Switch to Session of User '%1'").arg(list[0]),
                     i18n("Session: %1").arg(list[1].mid(1)+", "+loc) , QString("kicker:/switchuser_%1").arg((*it).vt), nId++, index++ );
              }
          }
        }

        m_exitView->rightView()->insertHeader( nId++, "kicker:/switchuser/" );
    }
    m_exitView->flipScroll(QString::null);
}

void KMenu::slotGoSubMenu(const QString& relPath)
{
     goSubMenu(relPath);
}

void KMenu::goSubMenu(const QString& relPath, bool keyboard)
{
    kdDebug() << "KMenu::goSubMenu(" << relPath << ", " << keyboard <<")" << endl;
    if ( relPath.startsWith( "kicker:/goup/" ) )
    {
        QString rel = relPath.mid( strlen( "kicker:/goup/" ) );
        int index = rel.length() - 1;
        if ( rel.endsWith( "/" ) )
            index--;
        index = rel.findRev( '/', index );
        kdDebug() << "goup, rel '" << rel << "' " << index << endl;
        QString currel = rel;
        rel = rel.left( index + 1 );
        if ( rel == "/" )
            rel = QString::null;

        kdDebug() << "goup, rel '" << rel << "' " << rel.isEmpty() << endl;
        fillSubMenu( rel, m_browserView->prepareLeftMove() );
        m_browserView->flipScroll(keyboard ? currel : QString::null);
        return;
    } else if (relPath.isEmpty())
    {
	if (m_browserView->currentView()->path.isEmpty())
	    return;
	fillSubMenu( relPath, m_browserView->prepareLeftMove() );
    } else if ( relPath.startsWith( "kicker:/new/" ) )
    {
        ItemView* view = m_browserView->prepareRightMove();
        m_browserView->showBackButton( true );

        int nId = serviceMenuEndId() + 1;
        view->insertHeader( nId++, "new/" );
        int index = 2;
        for (QStringList::ConstIterator it = m_newInstalledPrograms.begin();
            it != m_newInstalledPrograms.end(); ++it) {
            KService::Ptr p = KService::serviceByStorageId((*it));
            view->insertMenuItem(p, nId++, index++);
         }
    } else
    {
        //m_browserView->clear();
        fillSubMenu(relPath, m_browserView->prepareRightMove());
    }
    m_browserView->flipScroll(keyboard ? "kicker:/goup/": QString::null);
}

void KMenu::fillSubMenu(const QString& relPath, ItemView *view)
{
    kdDebug() << "fillSubMenu() " << relPath << endl;
    KServiceGroup::Ptr root = KServiceGroup::group(relPath);
    Q_ASSERT( root );

    KServiceGroup::List list = root->entries(true, true, true, KickerSettings::
            menuEntryFormat() == KickerSettings::DescriptionAndName || KickerSettings::menuEntryFormat()
            == KickerSettings::DescriptionOnly);

    int nId = serviceMenuStartId();
    m_browserView->showBackButton( !relPath.isEmpty() );
    if ( !relPath.isEmpty() )
    {
        view->insertHeader( nId++, relPath );
    }
    else if ( m_newInstalledPrograms.count() ) {
        KMenuItem *item = view->insertItem( "clock", i18n( "New Applications" ),
                              QString::null, "kicker:/new/", nId++, -1 );
        item->setHasChildren( true );
        view->insertSeparator( nId++, QString::null, -1 );
    }

    view->path = relPath;

    fillMenu (root, list, relPath, view, nId);
}

void KMenu::fillMenu(KServiceGroup::Ptr&
#ifdef KDELIBS_SUSE
    _root
#endif
                     , KServiceGroup::List& _list,
                     const QString& _relPath,
                     ItemView* view,
                     int& id)
{
    bool separatorNeeded = false;
    KServiceGroup::List::ConstIterator it = _list.begin();
#ifdef KDELIBS_SUSE
    KSortableValueList<KSharedPtr<KSycocaEntry>,QCString> slist;
    KSortableValueList<KSharedPtr<KSycocaEntry>,QCString> glist;
    QMap<QString,QString> specialTitle;
    QMap<QString,QString> categoryIcon;
    QMap<QString,QString> shortenedMenuPath;
#endif

    for (; it != _list.end(); ++it)
    {
        KSycocaEntry * e = *it;

        if (e->isType(KST_KServiceGroup))
        {
           KServiceGroup::Ptr g(static_cast<KServiceGroup *>(e));
#ifdef KDELIBS_SUSE
           if ( true /*KickerSettings::reduceMenuDepth()*/ && g->SuSEshortMenu() ){
              KServiceGroup::List l = g->entries(true, true /*excludeNoDisplay_*/ );
              if ( l.count() == 1 ) {
                 // the special case, we want to short the menu.
                 // TOFIX? : this works only for one level
                 KServiceGroup::List::ConstIterator _it=l.begin();
                 KSycocaEntry *_e = *_it;
                 if (_e->isType(KST_KService)) {
                     KService::Ptr s(static_cast<KService *>(_e));
		     QString key;
                     if ( g->SuSEgeneralDescription() ) {
			// we use the application name
                        key = s->name();
                     }
		     else {
			// we use the normal menu description
			key = s->name();
                        if( !s->genericName().isEmpty() && g->caption()!=s->genericName()) {
                           if (KickerSettings::menuEntryFormat() == KickerSettings::NameAndDescription)
                               key = s->name() + " (" + g->caption() + ")";
			   else if (KickerSettings::menuEntryFormat() == KickerSettings::DescriptionAndName)
                               key = g->caption() + " (" + s->name() + ")";
			   else if (KickerSettings::menuEntryFormat() == KickerSettings::DescriptionOnly)
                             key = g->caption();
                        }
		     }
		     specialTitle.insert( _e->name(), key );
		     categoryIcon.insert( _e->name(), g->icon() );
                     slist.insert( key.local8Bit(), _e );
		     shortenedMenuPath.insert( _e->name(), g->relPath() );
                     // and escape from here
                     continue;
                  }
               }
            }
            glist.insert( g->caption().local8Bit(), e );
        }else if( e->isType(KST_KService)) {
            KService::Ptr s(static_cast<KService *>(e));
            slist.insert( s->name().local8Bit(), e );
        } else
            slist.insert( e->name().local8Bit(), e );
    }

    _list = _root->SuSEsortEntries( slist, glist, true /*excludeNoDisplay_*/, true );
    it = _list.begin();

    for (; it != _list.end(); ++it) {

        KSycocaEntry * e = *it;

        if (e->isType(KST_KServiceGroup)) {

           KServiceGroup::Ptr g(static_cast<KServiceGroup *>(e));
           if ( true /*KickerSettings::reduceMenuDepth()*/ && g->SuSEshortMenu() ){
              KServiceGroup::List l = g->entries(true, true /*excludeNoDisplay_*/ );
              if ( l.count() == 1 ) {
                    continue;
              }
           }
           // standard sub menu
#endif
            QString groupCaption = g->caption();

           // Avoid adding empty groups.
            KServiceGroup::Ptr subMenuRoot = KServiceGroup::group(g->relPath());

            int nbChildCount = subMenuRoot->childCount();
            if (nbChildCount == 0 && !g->showEmptyMenu())
            {
                continue;
            }

            bool is_description = KickerSettings::menuEntryFormat() == KickerSettings::DescriptionAndName ||
                                  KickerSettings::menuEntryFormat() == KickerSettings::DescriptionOnly;

            QString inlineHeaderName = g->showInlineHeader() ? groupCaption : "";

            if ( nbChildCount == 1 && g->allowInline() && g->inlineAlias())
            {
                KServiceGroup::Ptr element = KServiceGroup::group(g->relPath());
                if ( element )
                {
                    //just one element

                    KServiceGroup::List listElement = element->entries(true, true, true, is_description );
                    KSycocaEntry * e1 = *( listElement.begin() );
                    if ( e1->isType( KST_KService ) )
                    {
                        KService::Ptr s(static_cast<KService *>(e1));
                        view->insertMenuItem(s, id++, -1, 0);
                        continue;
                    }
                }
            }

            if (g->allowInline() && ((nbChildCount <= g->inlineValue() ) ||   (g->inlineValue() == 0)))
            {
                //inline all entries
                KServiceGroup::Ptr rootElement = KServiceGroup::group(g->relPath());

                if (!rootElement || !rootElement->isValid())
                {
                    break;
                }


                KServiceGroup::List listElement = rootElement->entries(true, true, true, is_description );

#if 0
                if ( !g->inlineAlias() && !inlineHeaderName.isEmpty() )
                {
                    int mid = view->insertItem(new PopupMenuTitle(inlineHeaderName, font()), id++, id, 0);
                    m_browserView->setItemEnabled( mid, false );
                }
#endif

                fillMenu( rootElement, listElement, g->relPath(), 0, id );
                continue;
            }

            // Ignore dotfiles.
            if ((g->name().at(0) == '.'))
            {
                continue;
            }

            KMenuItem *item = view->insertItem(g->icon(), groupCaption, QString::null, g->relPath(), id++, -1);
	    item->setMenuPath(g->relPath());
            item->setHasChildren( true );

#warning FIXME
#if 0
            PanelServiceMenu * m =
                newSubMenu(g->name(), g->relPath(), this, g->name().utf8(), inlineHeaderName);
            m->setCaption(groupCaption);

            QIconSet iconset = KickerLib::menuIconSet(g->icon());

            if (separatorNeeded)
            {
                insertSeparator();
                separatorNeeded = false;
            }

            int newId = insertItem(iconset, groupCaption, m, id++);
            entryMap_.insert(newId, static_cast<KSycocaEntry*>(g));
            // We have to delete the sub menu our selves! (See Qt docs.)
            subMenus.append(m);
#endif
        }
        if (e->isType(KST_KService))
        {
            KService::Ptr s(static_cast<KService *>(e));
            if (_relPath.isEmpty()) {
                QStringList favs = KickerSettings::favorites();
                if (favs.find(s->storageId())!=favs.end())
                  continue;
            }
#ifdef KDELIBS_SUSE
            KMenuItem *item = view->insertMenuItem(s, id++, -1, 0, QString::null, specialTitle[s->name()], categoryIcon[s->name()] );
            if (shortenedMenuPath[s->name()].isEmpty())
	       item->setMenuPath(_relPath+s->menuId());
            else
	       item->setMenuPath(shortenedMenuPath[s->name()]+s->menuId());
#else
            KMenuItem *item = view->insertMenuItem(s, id++, -1);
	    item->setMenuPath(_relPath+s->menuId());
#endif
        }
        else if (e->isType(KST_KServiceSeparator))
        {
            separatorNeeded = true;
        }
    }

    view->slotMoveContent();
}

void KMenu::initialize()
{
    static bool m_initialized=false;
    if (m_initialized)
        return;
    m_initialized = true;

    kdDebug(1210) << "KMenu::initialize()" << endl;

    if (loadSidePixmap())
    {
        // in case we've been through here before, let's disconnect
        disconnect(kapp, SIGNAL(kdisplayPaletteChanged()),
                this, SLOT(paletteChanged()));
        connect(kapp, SIGNAL(kdisplayPaletteChanged()),
                this, SLOT(paletteChanged()));
    }
    else
    {
        sidePixmap = sideTilePixmap = QPixmap();
    }

   /*
       If  the user configured ksmserver to
     */
    KConfig ksmserver("ksmserverrc", false, false);
    ksmserver.setGroup("General");
/*
    if (kapp->authorize("lock_screen"))
        connect( m_lock, SIGNAL(clicked()), SLOT(slotLock()));
    else
        m_lock->hide();
*/
    connect( m_lock, SIGNAL(clicked()), SLOT(slotOpenSUSE()));
    if (!kapp->authorize("logout"))
        m_exit->hide();

    // load search field history
    QStringList histList = KickerSettings::history();
    int maxHistory = KickerSettings::historyLength();

    bool block = m_kcommand->signalsBlocked();
    m_kcommand->blockSignals( true );
    m_kcommand->setMaxCount( maxHistory );
    m_kcommand->setHistoryItems( histList );
    m_kcommand->blockSignals( block );

    QStringList compList = KickerSettings::completionItems();
    if( compList.isEmpty() )
        m_kcommand->completionObject()->setItems( histList );
    else
        m_kcommand->completionObject()->setItems( compList );

    KCompletionBox* box = m_kcommand->completionBox();
    if (box)
        box->setActivateOnSelect( false );

    m_finalFilters = KURIFilter::self()->pluginNames();
    m_finalFilters.remove("kuriikwsfilter");

    m_middleFilters = m_finalFilters;
    m_middleFilters.remove("localdomainurifilter");

    QStringList favs = KickerSettings::favorites();
    if (favs.isEmpty()) {
      QFile f(locate("data", "kicker/default-favs"));
      if (f.open(IO_ReadOnly)) {
        QTextStream is(&f);

        while (!is.eof())
            favs << is.readLine();

        f.close();
      }
      KickerSettings::setFavorites(favs);
      KickerSettings::writeConfig();
    }

    int nId = serviceMenuEndId() + 1;
    int index = 1;
    for (QStringList::ConstIterator it = favs.begin(); it != favs.end(); ++it)
    {
       if ((*it)[0]=='/') {
          KDesktopFile df((*it),true);
          QString url = df.readURL();
          if (!KURL(url).isLocalFile() || QFile::exists(url.replace("file://",QString::null)))
            m_favoriteView->insertItem(df.readIcon(),df.readName(),df.readGenericName(), url, nId++, index++);
       }
       else {
          KService::Ptr p = KService::serviceByStorageId((*it));
          m_favoriteView->insertMenuItem(p, nId++, index++);
       }
    }

    //nId = m_favoriteView->insertSeparator( nId, QString::null, index++ );
//    m_favoriteView->insertDocument(KURL("help:/khelpcenter/userguide/index.html"), nId++);

    insertStaticItems();

    QWidget *footer = m_footer->mainWidget();
    QPixmap pix( 1, footer->height() );
    QPainter p( &pix );
    p.fillRect( 0, 0, 1, footer->height(), m_lock->colorGroup().brush( QColorGroup::Base ) );
    p.setPen( KNewButton::self()->borderColor() );
    p.drawPoint( 0, footer->height() - 1 );
    p.drawPoint( 0, footer->height() - 2 );
    p.end();

    footer->setPaletteBackgroundPixmap( pix );
    raiseStackWidget( m_favoriteView );
}

void KMenu::insertStaticItems()
{
    int nId = serviceMenuEndId() + 1;
    int index = 1;

    m_exitView->leftView()->insertSeparator( nId++, i18n("Session"), index++ );
    if (kapp->authorize("logout"))
       m_exitView->leftView()->insertItem( "undo", i18n( "Logout" ),
                                   i18n( "End session" ), "kicker:/logout", nId++, index++ );
    if (kapp->authorize("lock_screen"))
       m_exitView->leftView()->insertItem( "lock", i18n( "Lock" ),
                                   i18n( "Lock screen" ), "kicker:/lock", nId++, index++ );

    KConfig ksmserver("ksmserverrc", false, false);
    ksmserver.setGroup("General");
    if (ksmserver.readEntry( "loginMode" ) == "restoreSavedSession")
    {
        m_exitView->leftView()->insertItem("filesave", i18n("Save Session"),
                               i18n("Save current Session for next login"),
                               "kicker:/savesession", nId++, index++ );
    }
    if (DM().isSwitchable() && kapp->authorize("switch_user"))
    {
        KMenuItem *switchuser = m_exitView->leftView()->insertItem( "switchuser", i18n( "Switch User" ),
                                                                    i18n( "Manage parallel sessions" ), "kicker:/switchuser/", nId++, index++ );
        switchuser->setHasChildren(true);
    }

    bool maysd = false;
    if (ksmserver.readBoolEntry( "offerShutdown", true ) && DM().canShutdown())
        maysd = true;

    if ( maysd )
    {
        m_exitView->leftView()->insertSeparator( nId++, i18n("System"), index++ );
        m_exitView->leftView()->insertItem( "exit", i18n( "Shutdown Computer" ),
                                   i18n( "Turn off computer" ), "kicker:/shutdown", nId++, index++ );

        m_exitView->leftView()->insertItem( "reload", i18n( "Restart Computer" ),
                                            i18n( "Restart and boot this system" ),
                                            "kicker:/restart", nId++, index++ );

        insertSuspendOption(nId, index);

        int def, cur;
        QStringList dummy_opts;
        if ( DM().bootOptions( dummy_opts, def, cur ) )
        {

            KMenuItem *restart = m_exitView->leftView()->insertItem( "reload", i18n( "Start Operating System" ),
                                                                     i18n( "Restart and boot another operating system" ),
                                                                     "kicker:/restart/", nId++, index++ );
            restart->setHasChildren(true);
        }
    }

    index = 1;

    m_systemView->insertSeparator( nId++, i18n("Applications"), index++);

    m_systemView->insertItem( "yast", i18n( "System Settings" ),
                                     i18n( "YaST2" ), "/usr/share/applications/YaST.desktop", nId++, index++ );

    m_systemView->insertItem( "info", i18n( "System Information" ),
                              "sysinfo:/",  "sysinfo:/", nId++, index++ );

    m_systemView->insertSeparator( nId++, i18n("System Folders"), index++ );

    m_systemView->insertItem( "folder_red", i18n( "Home Folder" ),
                              QDir::homeDirPath(), "file://"+QDir::homeDirPath(), nId++, index++ );

    if ( KStandardDirs::exists( KGlobalSettings::documentPath() + "/" ) )
    {
        QString documentPath = KGlobalSettings::documentPath();
        if ( documentPath.endsWith( "/" ) )
            documentPath = documentPath.left( documentPath.length() - 1 );
        if (documentPath!=QDir::homeDirPath())
           m_systemView->insertItem( "folder_green", i18n( "My Documents" ), documentPath, documentPath, nId++, index++ );
    }

    m_systemView->insertItem( "remote", i18n( "Network Folders" ),
                              "remote:/", "remote:/", nId++, index++ );

    m_mediaWatcher = new MediaWatcher( this );
    connect( m_mediaWatcher, SIGNAL( mediumChanged() ), SLOT( updateMedia() ) );
    m_media_id = 0;

    connect(&m_mediaFreeTimer, SIGNAL(timeout()), SLOT( updateMedia()));
}

int KMenu::insertClientMenu(KickerClientMenu *)
{
#if 0
    int id = client_id;
    clients.insert(id, p);
    return id;
#endif
    return 0;
}

void KMenu::removeClientMenu(int)
{
#if 0
    clients.remove(id);
    slotClear();
#endif
}

extern int kicker_screen_number;

void KMenu::slotLock()
{
    kdDebug() << "slotLock " << endl;
    accept();
    QCString appname( "kdesktop" );
    if ( kicker_screen_number )
        appname.sprintf("kdesktop-screen-%d", kicker_screen_number);
    kapp->dcopClient()->send(appname, "KScreensaverIface", "lock()", "");
}

void KMenu::slotOpenSUSE()
{
    kapp->invokeBrowser("http://opensuse.org");
}

void KMenu::slotLogout()
{
    kapp->requestShutDown();
}

void KMenu::slotPopulateSessions()
{
    int p = 0;
    DM dm;

    sessionsMenu->clear();
    if (kapp->authorize("start_new_session") && (p = dm.numReserve()) >= 0)
    {
        if (kapp->authorize("lock_screen"))
	  sessionsMenu->insertItem(/*SmallIconSet("lockfork"),*/ i18n("Lock Current && Start New Session"), 100 );
        sessionsMenu->insertItem(SmallIconSet("fork"), i18n("Start New Session"), 101 );
        if (!p) {
            sessionsMenu->setItemEnabled( 100, false );
            sessionsMenu->setItemEnabled( 101, false );
        }
        sessionsMenu->insertSeparator();
    }
    SessList sess;
    if (dm.localSessions( sess ))
        for (SessList::ConstIterator it = sess.begin(); it != sess.end(); ++it) {
            int id = sessionsMenu->insertItem( DM::sess2Str( *it ), (*it).vt );
            if (!(*it).vt)
                sessionsMenu->setItemEnabled( id, false );
            if ((*it).self)
                sessionsMenu->setItemChecked( id, true );
        }
}

void KMenu::slotSessionActivated( int ent )
{
    if (ent == 100)
        doNewSession( true );
    else if (ent == 101)
        doNewSession( false );
    else if (!sessionsMenu->isItemChecked( ent ))
        DM().lockSwitchVT( ent );
}

void KMenu::doNewSession( bool lock )
{
    int result = KMessageBox::warningContinueCancel(
        kapp->desktop()->screen(kapp->desktop()->screenNumber(this)),
        i18n("<p>You have chosen to open another desktop session.<br>"
               "The current session will be hidden "
               "and a new login screen will be displayed.<br>"
               "An F-key is assigned to each session; "
               "F%1 is usually assigned to the first session, "
               "F%2 to the second session and so on. "
               "You can switch between sessions by pressing "
               "Ctrl, Alt and the appropriate F-key at the same time. "
               "Additionally, the KDE Panel and Desktop menus have "
               "actions for switching between sessions.</p>")
                           .arg(7).arg(8),
        i18n("Warning - New Session"),
        KGuiItem(i18n("&Start New Session"), "fork"),
        ":confirmNewSession",
        KMessageBox::PlainCaption | KMessageBox::Notify);

    if (result==KMessageBox::Cancel)
        return;

    if (lock)
        slotLock();

    DM().startReserve();
}

void KMenu::searchAccept()
{
    QString cmd = m_kcommand->currentText().stripWhiteSpace();

    bool logout = (cmd == "logout");
    bool lock = (cmd == "lock");

    addToHistory();

    if ( !logout && !lock )
    {
        // first try if we have any search action
        if (m_searchResultsWidget->currentItem())
            m_searchResultsWidget->slotItemClicked(m_searchResultsWidget->currentItem());
        // otherwise, try to run it like minicli does
        else if ( runCommand() )
            return;
    }

    saveConfig();
    hide();

    if ( logout )
    {
        kapp->propagateSessionManager();
        kapp->requestShutDown();
    }
    if ( lock )
    {
        QCString appname( "kdesktop" );
        int kicker_screen_number = qt_xscreen();
        if ( kicker_screen_number )
            appname.sprintf("kdesktop-screen-%d", kicker_screen_number);
        kapp->dcopClient()->send(appname, "KScreensaverIface", "lock()", "");
    }
}

bool KMenu::runCommand()
{
kdDebug() << "runCommand() " << m_kcommand->lineEdit()->text() << endl;
    // Ignore empty commands...
    if ( m_kcommand->lineEdit()->text().isEmpty() )
      return true;

    accept();

    if (input_timer->isActive ())
        input_timer->stop ();

    // Make sure we have an updated data
    parseLine( true );

    bool block = m_kcommand->signalsBlocked();
    m_kcommand->blockSignals( true );
    m_kcommand->clearEdit();
    m_kcommand->setFocus();
    m_kcommand->reset();
    m_kcommand->blockSignals( block );


    QString cmd;
    KURL uri = m_filterData->uri();
    if ( uri.isLocalFile() && !uri.hasRef() && uri.query().isEmpty() )
      cmd = uri.path();
    else
      cmd = uri.url();

    QString exec;

    switch( m_filterData->uriType() )
    {
      case KURIFilterData::LOCAL_FILE:
      case KURIFilterData::LOCAL_DIR:
      case KURIFilterData::NET_PROTOCOL:
      case KURIFilterData::HELP:
      {
        // No need for kfmclient, KRun does it all (David)
        (void) new KRun( m_filterData->uri(), parentWidget());
        return false;
      }
      case KURIFilterData::EXECUTABLE:
      {
        if( !m_filterData->hasArgsAndOptions() )
        {
          // Look for desktop file
          KService::Ptr service = KService::serviceByDesktopName(cmd);
          if (service && service->isValid() && service->type() == "Application")
          {
            notifyServiceStarted(service);
            KRun::run(*service, KURL::List());
            return false;
          }
        }
      }
      // fall-through to shell case
      case KURIFilterData::SHELL:
      {
        if (kapp->authorize("shell_access"))
        {
          exec = cmd;

          if( m_filterData->hasArgsAndOptions() )
            cmd += m_filterData->argsAndOptions();

          break;
        }
        else
        {
          KMessageBox::sorry( this, i18n("<center><b>%1</b></center>\n"
                                    "You do not have permission to execute "
                                    "this command.")
                                    .arg( QStyleSheet::convertFromPlainText(cmd) ));
          return true;
        }
      }
      case KURIFilterData::UNKNOWN:
      case KURIFilterData::ERROR:
      default:
      {
        // Look for desktop file
        KService::Ptr service = KService::serviceByDesktopName(cmd);
        if (service && service->isValid() && service->type() == "Application")
        {
          notifyServiceStarted(service);
          KRun::run(*service, KURL::List(), this);
          return false;
        }

        service = KService::serviceByName(cmd);
        if (service && service->isValid() && service->type() == "Application")
        {
          notifyServiceStarted(service);
          KRun::run(*service, KURL::List(), this);
          return false;
      }

        KMessageBox::sorry( this, i18n("<center><b>%1</b></center>\n"
                                  "Could not run the specified command.")
                                  .arg( QStyleSheet::convertFromPlainText(cmd) ));
        return true;
      }
    }

    if ( KRun::runCommand( cmd, exec, m_iconName ) )
      return false;

    KMessageBox::sorry( this, i18n("<center><b>%1</b></center>\n"
                                "The specified command does not exist.").arg(cmd) );
    return true; // Let the user try again...
}

void KMenu::show()
{
    m_isShowing = true;
    emit aboutToShow();

    initialize();

    PanelPopupButton *kButton = MenuManager::the()->findKButtonFor( this );
    if (kButton)
    {
        if (kButton->center().y()<QApplication::desktop()->height()/2)
            setOrientation(TopDown);
        else
            setOrientation(BottomUp);
    }

    m_browserDirty=true;
    m_recentDirty=true;

    m_activeTab = m_btnFavorites;
    paintTab( m_btnFavorites, true );
    paintTab( m_btnRecently, false );
    paintTab( m_browser, false );
    paintTab( m_system, false );
    paintTab( m_exit, false );
    paintTab( m_expandSpace, false );
    paintTab( m_searchFrame, false );

    updateMedia();
    m_mediaFreeTimer.start(10 * 1000); // refresh all 10s

    slotFavoritesButton();
    m_kcommand->clear();
    current_query.clear();
    m_kcommand->setFocus();

    // we need to reenable it
    m_toolTipsEnabled = QToolTip::isGloballyEnabled();
    QToolTip::setGloballyEnabled(KickerSettings::showToolTips());

    if ( KNewButton::self() )
        KNewButton::self()->setActive( true );

    KMenuBase::show();
    m_isShowing = false;
}

void KMenu::setOrientation(MenuOrientation orientation)
{
    if (m_orientation==orientation)
        return;

    m_orientation=orientation;

    m_resizeHandle->setCursor(m_orientation == BottomUp ? Qt::sizeBDiagCursor : Qt::sizeFDiagCursor);

    QPixmap pix;
    if ( m_orientation == BottomUp )
        pix.load( locate("appdata", "pics/search-gradient.png" ) );
    else
        pix.load( locate("appdata", "pics/search-gradient-topdown.png" ) );

    pix.convertFromImage( pix.convertToImage().scale(pix.width(), m_searchFrame->height()));
    m_search->mainWidget()->setPaletteBackgroundPixmap( pix );
    m_resizeHandle->setPaletteBackgroundPixmap( pix );

    if ( m_orientation == BottomUp )
    {
        tab_bottom_left.load( locate("appdata", "pics/tab-bottom-left.png" ) );
        tab_bottom_right.load( locate("appdata", "pics/tab-bottom-right.png" ) );
        tab_center.load( locate("appdata", "pics/tab-center.png" ) );
        tab_left_center.load( locate("appdata", "pics/tab-left_center.png" ) );
        tab_right_center.load( locate("appdata", "pics/tab-right_center.png" ) );
        tab_top_left.load( locate("appdata", "pics/tab-top-left.png" ) );
        tab_top_right.load( locate("appdata", "pics/tab-top-right.png" ) );
    }
    else
    {
        tab_bottom_left.load( locate("appdata", "pics/tab-bottom-left-topdown.png" ) );
        tab_bottom_right.load( locate("appdata", "pics/tab-bottom-right-topdown.png" ) );
        tab_center.load( locate("appdata", "pics/tab-center-topdown.png" ) );
        tab_left_center.load( locate("appdata", "pics/tab-left_center.png" ) );
        tab_right_center.load( locate("appdata", "pics/tab-right_center.png" ) );
        tab_top_left.load( locate("appdata", "pics/tab-top-left-topdown.png" ) );
        tab_top_right.load( locate("appdata", "pics/tab-top-right-topdown.png" ) );
    }

    QPixmap respix = QPixmap( locate("data", "kicker/pics/resize_handle.png" ) );
    if ( m_orientation == TopDown ) {
      QWMatrix m;
      m.rotate( 90.0 );
      respix=respix.xForm(m);
    }
    m_resizeHandle->setPixmap(respix);

    resizeEvent(new QResizeEvent(sizeHint(), sizeHint()));
}

void KMenu::showMenu()
{
    kdDebug() << "KMenu::showMenu()" << endl;
    PanelPopupButton *kButton = MenuManager::the()->findKButtonFor(this);
    if (kButton)
    {
	adjustSize();
        kButton->showMenu();
    }
    else
    {
        show();
    }
    kdDebug() << "end KMenu::showMenu()" << endl;
}

void KMenu::hide()
{
    //kdDebug() << "KMenu::hide() from " << kdBacktrace() << endl;

    // TODO: hide popups
    emit aboutToHide();

    if (m_popupMenu) {
        m_popupMenu->deleteLater();
        m_popupMenu=0;
    }
    m_mediaFreeTimer.stop();

    m_isresizing = false;

    if ( KNewButton::self() )
        KNewButton::self()->setActive( false );

    KickerSettings::setKMenuWidth(width());
    KickerSettings::setKMenuHeight(height());
    KickerSettings::writeConfig();

    QToolTip::setGloballyEnabled(m_toolTipsEnabled);

    // remove focus from lineedit again, otherwise it doesn't kill its timers
    slotFavoritesButton();

    QWidget::hide();
}

void KMenu::paintEvent(QPaintEvent * e)
{
    KMenuBase::paintEvent(e);

    QPainter p(this);
    p.setClipRegion(e->region());

    const BackgroundMode bgmode = backgroundMode();
    const QColorGroup::ColorRole crole = QPalette::backgroundRoleFromMode( bgmode );
    p.setBrush( colorGroup().brush( crole ) );

    p.drawRect( 0, 0, width(), height() );
    int ypos = m_search->mainWidget()->geometry().bottom();

    p.drawTiledPixmap( 0, ypos, main_border_lc.width(),
                       m_stacker->geometry().bottom() - ypos + 1,
                       main_border_lc );
    p.drawTiledPixmap( width() - main_border_rc.width(), ypos, main_border_rc.width(),
                       m_stacker->geometry().bottom() - ypos + 1,
                       main_border_rc );

    p.drawPixmap( 0, ypos, main_border_tl );
    p.drawPixmap( width() - main_border_tr.width(), ypos, main_border_tr );
    p.drawPixmap( 0, buttonBox->mainWidget()->y(), button_box_left );
}


void KMenu::configChanged()
{
    RecentlyLaunchedApps::the().m_bNeedToUpdate = false;
    RecentlyLaunchedApps::the().configChanged();
}

// create and fill "recent" section at first
void KMenu::createRecentMenuItems()
{
    RecentlyLaunchedApps::the().init();

    if (!KickerSettings::numVisibleEntries())
      KickerSettings::setNumVisibleEntries(5);

    int nId = serviceMenuEndId() + 1;
    m_recentlyView->insertSeparator( nId++, i18n( "Applications" ), -1 );

    QStringList RecentApps;
    RecentlyLaunchedApps::the().getRecentApps(RecentApps);

    if (RecentApps.count() > 0)
    {
//        bool bSeparator = KickerSettings::showMenuTitles();
        int nIndex = 0;

        for (QValueList<QString>::ConstIterator it =
             RecentApps.fromLast(); /*nop*/; --it)
        {
            KService::Ptr s = KService::serviceByDesktopPath(*it);
            if (!s)
            {
                RecentlyLaunchedApps::the().removeItem(*it);
            }
            else
                m_recentlyView->insertMenuItem(s, nIndex++);

            if (it == RecentApps.begin())
            {
                break;
            }
        }

    }

    m_recentlyView->insertSeparator( nId++, i18n( "Documents" ), -1 );

    QStringList fileList = KRecentDocument::recentDocuments();
    kdDebug() << "createRecentMenuItems=" << fileList << endl;
    for (QStringList::ConstIterator it = fileList.begin();
         it != fileList.end();
         ++it)
        m_recentlyView->insertRecentlyItem(*it, nId++);

}

void KMenu::clearSubmenus()
{
    // we don't need to delete these on the way out since the libloader
    // handles them for us
    if (QApplication::closingDown())
    {
        return;
    }

    for (PopupMenuList::const_iterator it = dynamicSubMenus.constBegin();
            it != dynamicSubMenus.constEnd();
            ++it)
    {
        delete *it;
    }
    dynamicSubMenus.clear();

#warning FIXME
    //QFrame::clearSubmenus();
}

void KMenu::updateRecent()
{
    m_recentlyView->clear();

    createRecentMenuItems();

    m_recentDirty = false;
}

void KMenu::popup(const QPoint&, int)
{
   qDebug("KMenu::popup called!");
}

void KMenu::clearRecentAppsItems()
{
    RecentlyLaunchedApps::the().clearRecentApps();
    RecentlyLaunchedApps::the().save();
    RecentlyLaunchedApps::the().m_bNeedToUpdate = true;
    updateRecent();
}

void KMenu::clearRecentDocsItems()
{
    KRecentDocument::clear();
    updateRecent();
}

void KMenu::searchChanged(const QString & text)
{
  if (!text.isEmpty()) {
    const QColor on = QColor( 244, 244, 244 );
    const QColor off = QColor( 181, 181, 181 );
    raiseStackWidget(m_searchWidget);
  }

  m_searchActions->clearSelection();
  m_searchResultsWidget->clearSelection();

  if (input_timer->isActive ())
    input_timer->stop ();
  input_timer->start (WAIT_BEFORE_QUERYING, TRUE);
}

bool KMenu::dontQueryNow (const QString& str)
{
    if (str.isEmpty ())
        return true;
    if (str == current_query.get())
	return true;
    int length = str.length ();
    int last_whitespace = str.findRev (' ', -1);
    if (last_whitespace == length-1)
        return false; // if the user typed a space, search
    if (last_whitespace >= length-2)
        return true; // dont search if the user only typed one character
    QChar lastchar = str[length-1];
    if (lastchar == ":" || lastchar == "=")
        return true;
    return false;
}

void KMenu::createNewProgramList()
{
    m_seenProgramsChanged = false;
    m_seenPrograms = KickerSettings::firstSeenApps();
    m_newInstalledPrograms.clear();

    m_currentDate = QDate::currentDate().toString(Qt::ISODate);

    bool initialize = (m_seenPrograms.count() == 0);

    createNewProgramList(QString::null);

    if (initialize) {
       for (QStringList::Iterator it = m_seenPrograms.begin(); it != m_seenPrograms.end(); ++it)
           *(++it)="-";

        m_newInstalledPrograms.clear();
    }

    if (m_seenProgramsChanged) {
      KickerSettings::setFirstSeenApps(m_seenPrograms);
      KickerSettings::writeConfig();
    }
}

void KMenu::createNewProgramList(QString 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())
				createNewProgramList(g->relPath());
		} else if(e->isType(KST_KService)) {
			KService::Ptr s(static_cast<KService *>(e));
			if(s->type() == "Application" && !s->noDisplay() ) {
                            QString shortStorageId = s->storageId().replace(".desktop",QString::null);
                            QStringList::Iterator it_find = m_seenPrograms.begin();
                            QStringList::Iterator it_end = m_seenPrograms.end();
 			    bool found = false;
			    for (; it_find != it_end; ++it_find) {
				if (*(it_find)==shortStorageId) {
				   found = true;
				   break;
                                }
                                ++it_find;
                            }
                            if (!found) {
                                m_seenProgramsChanged=true;
                                m_seenPrograms+=shortStorageId;
                                m_seenPrograms+=m_currentDate;
                                if (m_newInstalledPrograms.find(s->storageId())==m_newInstalledPrograms.end())
                                  m_newInstalledPrograms+=s->storageId();
                            }
                            else {
                                ++it_find;
                                if (*(it_find)!="-") {
                                   QDate date = QDate::fromString(*(it_find),Qt::ISODate);
                                   if (date.daysTo(QDate::currentDate())<3) {
                                      if (m_newInstalledPrograms.find(s->storageId())==m_newInstalledPrograms.end())
                                         m_newInstalledPrograms+=s->storageId();
                                   }
                                   else {
                                      m_seenProgramsChanged=true;
                                      (*it_find)="-";
                                   }
                                }
                            }
                        }
		}
	}
    }
}

void KMenu::searchProgramList(QString 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() && !checkUriInMenu(s->desktopEntryPath())) {
				if (!current_query.matches(s->name()+' '+s->genericName()+' '+s->exec()+' '+
				    s->keywords().join(",")+' '+s->comment()+' '+group->caption()+' '+
				    s->categories().join(",")) || !anotherHitMenuItemAllowed(APPS))
					continue;

				QString input = current_query.get();
				int score = 0;
				if (s->exec()==input)
					score = 100;
				else if (s->exec().find(input)==0)
					score = 50;
				else if (s->exec().find(input)!=-1)
					score = 10;
				else if (s->name().lower()==input)
					score = 100;
				else if (s->name().lower().find(input)==0)
					score = 50;
				else if (s->name().lower().find(input)!=-1)
					score = 10;

				if (s->exec().find(' ')==-1)
					score+=1;

				if (s->substituteUid())
					score-=1;

				QString firstLine, secondLine;
				if ((KickerSettings::DescriptionAndName || KickerSettings::menuEntryFormat() == KickerSettings::DescriptionOnly) && !s->genericName().isEmpty()) {
					firstLine = s->genericName();
					secondLine = s->name();
				}
				else {
					firstLine = s->name();;
					secondLine = s->genericName();
				}

        			HitMenuItem *hit_item = new HitMenuItem (firstLine, secondLine,
                                s->desktopEntryPath(), QString::null, 0, APPS, s->icon(), score);
        			if (hit_item == NULL)
					continue;

				hit_item->service = s;
        			insertSearchResult(hit_item);

                                QString exe = s->exec();
                                int pos = exe.find(' ');
                                if (pos>0)
                                  exe=exe.left(pos);
				m_programsInMenu+=KGlobal::dirs()->findExe(exe);
			}
		}
	}
    }
}

void KMenu::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()) || !anotherHitMenuItemAllowed(BOOKMARKS)) {
                                        bookmark = group.next(bookmark);
	    				continue;
                                }

        			HitMenuItem *hit_item = new HitMenuItem (bookmark.fullText(), bookmark.fullText(),
                                bookmark.url(), QString::null, 0, BOOKMARKS, bookmark.icon());

        			insertSearchResult(hit_item);
		}
		bookmark = group.next(bookmark);
	}
}

void KMenu::initSearch()
{
    if (!m_addressBook)
       m_addressBook = KABC::StdAddressBook::self( false );

    if (!bookmarkManager)
      bookmarkManager = KBookmarkManager::userBookmarksManager();

    if (!m_search_plugin) {
      m_search_plugin_interface = new QObject( this, "m_search_plugin_interface" );
      new MyKickoffSearchInterface( this, m_search_plugin_interface, "kickoffsearch interface" );
      KTrader::OfferList offers = KTrader::self()->query("KickoffSearch/Plugin");

      KService::Ptr service = *offers.begin();
      if (service) {
        int errCode = 0;
        m_search_plugin = KParts::ComponentFactory::createInstanceFromService<KickoffSearch::Plugin>
            ( service, m_search_plugin_interface, 0, QStringList(), &errCode);
      }
    }
}

void KMenu::searchAddressbook()
{
    if (!m_addressBook)
      m_addressBook = KABC::StdAddressBook::self( false );

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

        HitMenuItem *hit_item;
        QString realName = (*it).realName();
        if (realName.isEmpty())
            realName=(*it).preferredEmail();

        if (!(*it).preferredEmail().isEmpty()) {
	    if (!anotherHitMenuItemAllowed(ACTIONS)) {
               it++;
               continue;
            }

            hit_item = new HitMenuItem (i18n("Send Email to %1").arg(realName), (*it).preferredEmail(),
                                "mailto:"+(*it).preferredEmail(), QString::null, 0, ACTIONS, "mail_new");

        			insertSearchResult(hit_item);
        }

	if (!anotherHitMenuItemAllowed(ACTIONS)) {
               it++;
               continue;
        }

        hit_item = new HitMenuItem (i18n("Open Addressbook at %1").arg(realName), (*it).preferredEmail(),
                                "kaddressbook --uid "+(*it).uid(), QString::null, 0, ACTIONS, "kaddressbook");

        			insertSearchResult(hit_item);

       it++;
    }
}

void KMenu::clearSearchResults(bool showHelp)
{
    m_searchResultsWidget->clear();
    m_searchResultsWidget->setFocusPolicy(showHelp ? QWidget::NoFocus : QWidget::StrongFocus);
    setTabOrder(m_kcommand, m_searchResultsWidget);

    if (showHelp) {
        QListViewItem* item;
        item = new QListViewItem( m_searchResultsWidget, "   " + i18n("- Add ext:type to specify a file extension.") );
        item->setSelectable(false);
        item = new QListViewItem( m_searchResultsWidget, "   " + i18n("- When searching for a phrase, add quotes.") );
        item->setSelectable(false);
        item = new QListViewItem( m_searchResultsWidget, "   " + i18n("- To exclude search terms, use the minus symbol in front.") );
        item->setSelectable(false);
        item = new QListViewItem( m_searchResultsWidget, "   " + i18n("- To search for optional terms, use OR.") );
        item->setSelectable(false);
        item = new QListViewItem( m_searchResultsWidget, "   " + i18n("- You can use upper and lower case.") );
        item->setSelectable(false);
        item = new QListViewItem( m_searchResultsWidget, i18n("Search Quick Tips"));
        item->setSelectable(false);
    }

    for (int i=0; i<num_categories; ++i) {
	categorised_hit_total [i] = 0;
	max_category_id [i] = base_category_id [i];
    }
}

void KMenu::doQuery (bool return_pressed)
{
    QString query_str = m_kcommand->lineEdit()->text ().simplifyWhiteSpace ();
    if (! return_pressed && dontQueryNow (query_str)) {
        if (query_str.length()<3)
            clearSearchResults();
        else {
            if (m_searchResultsWidget->firstChild() && m_searchResultsWidget->firstChild()->isSelectable()) {
                m_searchResultsWidget->setSelected(m_searchResultsWidget->firstChild(),true);
            }
            else if (m_searchResultsWidget->childCount()>1) {
                m_searchResultsWidget->setSelected(m_searchResultsWidget->firstChild()->itemBelow(),true);
            }
        }
        return;
    }
    kdDebug() << "Querying for [" << query_str << "]" << endl;
    current_query.set(query_str);

    // reset search results
    HitMenuItem *hit_item;
    while ((hit_item = m_current_menu_items.take ()) != NULL) {
        //kndDebug () << " (" << hit_item->id << "," << hit_item->category << ")" << endl;
        delete hit_item;
    }

    clearSearchResults(false);
    m_searchPixmap->setMovie(QMovie(locate( "appdata", "pics/search-running.mng" )));

    resetOverflowCategory();

    initCategoryTitlesUpdate();

    // calculate ?
    QString cmd = query_str.stripWhiteSpace();
    if (!cmd.isEmpty() && (cmd[0].isNumber() || (cmd[0] == '(')) &&
            (QRegExp("[a-zA-Z\\]\\[]").search(cmd) == -1))
    {
        QString result = calculate(cmd);
        if (!result.isEmpty())
        {
            categorised_hit_total[ACTIONS] ++;
            HitMenuItem *hit_item = new HitMenuItem (i18n("%1 = %2").arg(query_str, result), QString::null,
                    "kcalc", QString::null, (++max_category_id [ACTIONS]), ACTIONS, "kcalc");
            int index = getHitMenuItemPosition (hit_item);
            m_searchResultsWidget->insertItem(iconForHitMenuItem(hit_item), hit_item->display_name,
                    hit_item->display_info, KGlobal::dirs()->findExe("kcalc"), max_category_id [ACTIONS], index);
        }
    }

    // detect email address
    if (emailRegExp.exactMatch(query_str)) {
      categorised_hit_total[ACTIONS] ++;
      HitMenuItem *hit_item = new HitMenuItem (i18n("Send Email to %1").arg(query_str), QString::null,
                                "mailto:"+query_str, QString::null, (++max_category_id [ACTIONS]), ACTIONS, "mail_new");
      int index = getHitMenuItemPosition (hit_item);
      m_searchResultsWidget->insertItem(iconForHitMenuItem(hit_item), hit_item->display_name, hit_item->display_info, "mailto:"+query_str, max_category_id [ACTIONS], index);
    }

    // quick own application search
    m_programsInMenu.clear();
    searchProgramList(QString::null);

    KURIFilterData filterData;
    filterData.setData(query_str);
    filterData.setCheckForExecutables(true);

    if (KURIFilter::self()->filterURI(filterData)) {

        QString description;
        QString exe;

        switch (filterData.uriType()) {
        case KURIFilterData::LOCAL_FILE:
            description = i18n("Open Local File: %1").arg(filterData.uri().url());
            break;
        case KURIFilterData::LOCAL_DIR:
            description = i18n("Open Local Dir: %1").arg(filterData.uri().url());
            break;
        case KURIFilterData::NET_PROTOCOL:
            description = i18n("Open Remote Location: %1").arg(filterData.uri().url());
            break;
        case KURIFilterData::SHELL:
        case KURIFilterData::EXECUTABLE:
                exe = KGlobal::dirs()->findExe(filterData.uri().url());

            if (m_programsInMenu.find(exe)!=m_programsInMenu.end())
                exe = QString::null;
            else {
                description = i18n("Run '%1'").arg(exe);
            }
        default:
            break;
        }

        if (!description.isEmpty()) {
            categorised_hit_total[ACTIONS] ++;
            HitMenuItem *hit_item = new HitMenuItem (description, QString::null,
                    exe.isEmpty() ? filterData.uri() : exe, QString::null,
                    (++max_category_id [ACTIONS]), ACTIONS, exe.isEmpty() ? "fileopen": "run");
            int index = getHitMenuItemPosition (hit_item);
            m_searchResultsWidget->insertItem(iconForHitMenuItem(hit_item), hit_item->display_name,
                    hit_item->display_info,
                    exe.isEmpty() ? filterData.uri().url() : exe, max_category_id [ACTIONS], index);
        }
    }

    // search Konqueror bookmarks;
    if (!bookmarkManager)
      bookmarkManager = KBookmarkManager::userBookmarksManager();

    if (query_str.length()>=3)
      searchBookmarks(bookmarkManager->root());

    // search KDE addressbook
    if (query_str.length()>=3)
      searchAddressbook();

    updateCategoryTitles();

    if (m_searchResultsWidget->childCount()>1)
      m_searchResultsWidget->setSelected(m_searchResultsWidget->firstChild()->itemBelow(),true);
    m_searchActions->clearSelection();

    if (!m_search_plugin)
      initSearch();

    // start search plugin only with at least 3 characters
    if (query_str.length()<3 || !m_search_plugin || (m_search_plugin && !m_search_plugin->daemonRunning()) ) {
      m_searchPixmap->setPixmap( BarIcon( "find", 32 ) );
      fillOverflowCategory();
      if (query_str.length()>2 && m_current_menu_items.isEmpty())
	  reportError (i18n("No matches found"));
      return;
    }

    if (m_search_plugin) {
      m_search_plugin->query(current_query.get(), KickerSettings::DescriptionAndName || KickerSettings::menuEntryFormat() == KickerSettings::DescriptionOnly);
    }
}

bool KMenu::anotherHitMenuItemAllowed(int cat, bool count)
{
	// update number of hits in this category
        if (count)
	    categorised_hit_total [cat] ++;

	// if number of hits in this category is more than allowed, dont process this
	if (max_category_id [cat] - base_category_id [cat] < max_items(cat))
            return true;

        if (m_overflowCategoryState==None || (m_overflowCategoryState==Filling && m_overflowCategory==cat &&
            max_category_id [cat] + m_overflowList.count() - base_category_id [cat] < max_items(cat) * 2.0))
            return true;

        return false;
}

void KMenu::addHitMenuItem(HitMenuItem* item)
{
        if (checkUriInMenu(item->uri.path()))
            return;

	// if number of hits in this category is more than allowed, dont process this
	if (!anotherHitMenuItemAllowed(item->category, false))
	    return;

        insertSearchResult(item);
}

void KMenu::insertSearchResult(HitMenuItem* item)
{
        if (m_overflowCategoryState==None) {
            m_overflowCategoryState = Filling;
            m_overflowCategory = item->category;
        }
        else if (m_overflowCategoryState==Filling && m_overflowCategory!=item->category)
            m_overflowCategoryState = NotNeeded;

        if (max_category_id [item->category] - base_category_id [item->category] < max_items(item->category)) {
	    max_category_id [item->category]++;
            item->id=max_category_id [item->category];

            int index = getHitMenuItemPosition (item);

            kdDebug () << "Adding " << item->uri
    		    << "(" << item->mimetype << ") with id="
    		    << max_category_id [item->category] << " at " << index << endl;

            KMenuItem *hit_item = m_searchResultsWidget->insertItem(iconForHitMenuItem(item), item->display_name, item->display_info, item->uri.url(), max_category_id [item->category], index);
            hit_item->setService(item->service);

            kdDebug () << "Done inserting ... " << endl;
        }
        else if (m_overflowCategoryState==Filling && m_overflowCategory==item->category &&
            max_category_id [item->category] - base_category_id [item->category] < max_items(item->category) * 2)
               m_overflowList.append(item);
}

void KMenu::searchOver()
{
    m_searchPixmap->setPixmap( BarIcon( "find", 32 ) );
    fillOverflowCategory();
    if (m_current_menu_items.isEmpty()) {
        kdDebug() << "No matches found" << endl;
	reportError (i18n("No matches found"));
    }
    if (!m_searchResultsWidget->selectedItem() && !m_searchActions->selectedItem() && m_searchResultsWidget->childCount()>1) {
            m_searchResultsWidget->setSelected(m_searchResultsWidget->firstChild()->itemBelow(),true);
        }
}

void KMenu::initCategoryTitlesUpdate()
{
    // Need to know if each category was updated with hits or had the first hit
    // That way we know if we need to changetitle or inserttitle
    already_added = new bool [num_categories];
    for (int i=0; i<num_categories; ++i)
	already_added [i] = (max_category_id [i] != base_category_id [i]);
}

void KMenu::updateCategoryTitles()
{
    // update category title
    for (int i=0; i<num_categories; ++i) {
	if (i == OTHER)
	    continue;
	// nothing is in this category
	if (max_category_id [i] == base_category_id [i])
	    continue;

        KMenuItemSeparator *sep = 0;

	// if nothing was in this category before but now there is
	if (! already_added [i]) {
	    // insert a new title for this category
	    int index = getHitMenuItemPosition (new HitMenuItem (
						    base_category_id[i],
						    i));
            QString title = QString ("%1").arg (i18n(categories [i].utf8()));
            sep = m_searchResultsWidget->insertSeparator(base_category_id [i], title, index);
	    kdDebug () << "Inserting heading with id=" << base_category_id[i] << " for " << categories[i] << " at " << index << endl;
	} else {
	    // something was already displayed in this category
	    // update the title to reflect the total
            sep = dynamic_cast<KMenuItemSeparator*>( m_searchResultsWidget->findItem(base_category_id [i]) );
            if ( !sep )
                continue;
            kdDebug () << "Changing heading of id=" << base_category_id[i] << " for " << categories[i] << endl;
	}

        int max = max_items(i);
        if (m_overflowCategoryState == Filling && m_overflowCategory == i)
            max *= 2;

        if ( categorised_hit_total [i] > max ) {
            if (m_kerryInstalled)
                sep->setLink( i18n( "top %1 of %2" ).arg( max ).arg( categorised_hit_total [i] ), QString( "kerry:/%1" ).arg( i ) );
            else
                sep->setText( 0, i18n( "%1 (top %2 of %3)" ).arg( i18n(categories [i].utf8()) ).arg( max ).arg( categorised_hit_total [i] ) );
        }
        else {
            sep->setLink( QString::null );
        }
    }
    delete[] already_added;
    already_added = 0;
}

QString KMenu::iconForHitMenuItem(HitMenuItem *hit_item)
{
    // get the icon
    if (!hit_item->icon.isEmpty())
        return hit_item->icon;

    if (hit_item->category == WEBHIST) {
        QString favicon = KMimeType::favIconForURL (hit_item->uri);
        if (! favicon.isEmpty ())
	    return favicon;
    }

    if (mimetype_iconstore.contains (hit_item->mimetype))
        return (mimetype_iconstore [hit_item->mimetype]);
    else {
        KMimeType::Ptr mimetype_ptr = KMimeType::mimeType (hit_item->mimetype);
        QString mimetype_icon = mimetype_ptr->icon(QString::null, FALSE);
        mimetype_iconstore [hit_item->mimetype] = mimetype_icon;
        return mimetype_icon;
    }
    return QString::null;
}

void KMenu::slotStartService(KService::Ptr ptr)
{
    accept();

    addToHistory();
    KApplication::startServiceByDesktopPath(ptr->desktopEntryPath(),
                                            QStringList(), 0, 0, 0, "", true);
    updateRecentlyUsedApps(ptr);
}


void KMenu::slotStartURL(const QString& u)
{
    if ( u == "kicker:/goup/" ) {
        // only m_exitView is connected to this slot, not m_browserView
        slotGoExitMainMenu();
        return;
    }

    if ( u == "kicker:/restart/" || u=="kicker:/switchuser/") {
        slotGoExitSubMenu(u);
        return;
    }

    accept();

    if ( u == "kicker:/lock" ) {
        slotLock();
    }
    else if ( u == "kicker:/logout" ) {
#ifdef KDELIBS_SUSE
        QByteArray params;
        QDataStream stream(params, IO_WriteOnly);
        stream << 0 << -1 << "";

        kapp->dcopClient()->send("ksmserver", "default", "logoutTimed(int,int,QString)", params);
#else
        DCOPRef mediamanager("ksmserver", "ksmserver");
        DCOPReply reply = mediamanager.call( "logoutTimed", (int)KApplication::ShutdownTypeNone, (int)KApplication::ShutdownModeDefault );
	if (!reply.isValid() && KMessageBox::Continue==KMessageBox::warningContinueCancel(this, i18n("Do you really want to end the session?"),
                                                                                          i18n("Logout Confirmation"), KGuiItem(i18n("Logout"),"undo")))
            kapp->requestShutDown( KApplication::ShutdownConfirmNo,
                                   KApplication::ShutdownTypeNone,
                                   KApplication::ShutdownModeDefault );

#endif
    }
    else if ( u == "kicker:/shutdown" ) {
#ifdef KDELIBS_SUSE
        QByteArray params;
        QDataStream stream(params, IO_WriteOnly);
        stream << 2 << -1 << "";

        kapp->dcopClient()->send("ksmserver", "default", "logoutTimed(int,int,QString)", params);
#else
        if (KMessageBox::Continue==KMessageBox::warningContinueCancel(this, i18n("Do you really want to turn off the computer?"),
                                                                      i18n("Shutdown Confirmation"), KGuiItem(i18n("Shutdown"),"exit")))
            kapp->requestShutDown( KApplication::ShutdownConfirmNo,
                                   KApplication::ShutdownTypeHalt,
                                   KApplication::ShutdownModeDefault );
#endif
    }
    else if ( u == "kicker:/restart" ) {
#ifdef KDELIBS_SUSE
        QByteArray params;
        QDataStream stream(params, IO_WriteOnly);
        stream << 1 << -1 << QString::null;

        kapp->dcopClient()->send("ksmserver", "default", "logoutTimed(int,int,QString)", params);
#else
        if (KMessageBox::Continue==KMessageBox::warningContinueCancel(this, i18n("Do you really want to reset the computer and boot (another operating system)?"),
                                                                      i18n("Restart Confirmation"), KGuiItem(i18n("Restart"),"reload")))
            kapp->requestShutDown( KApplication::ShutdownConfirmNo,
                                   KApplication::ShutdownTypeReboot,
                                   KApplication::ShutdownModeDefault );
#endif
    }
#ifdef KDELIBS_SUSE
    else if ( u == "kicker:/suspend_disk" ) {
        slotSuspend( 1 );
    }
    else if ( u == "kicker:/suspend_ram" ) {
        slotSuspend( 2 );
    }
    else if ( u == "kicker:/standby" ) {
        slotSuspend( 3 );
    }
#endif
    else if ( u == "kicker:/savesession" ) {
        QByteArray data;
        kapp->dcopClient()->send( "ksmserver", "default",
                "saveCurrentSession()", data );
    }
    else if ( u == "kicker:/switchuser" ) {
        DM().startReserve();
    }
    else if ( u == "kicker:/switchuserafterlock" ) {
        slotLock();
        DM().startReserve();
    }
    else if ( u.startsWith("kicker:/switchuser_") )
        DM().lockSwitchVT( u.mid(19).toInt() );
    else if ( u.startsWith("kicker:/restart_") ) {
#ifdef KDELIBS_SUSE
        QStringList rebootOptions;
        int def, cur;
        DM().bootOptions( rebootOptions, def, cur );

        QByteArray params;
        QDataStream stream(params, IO_WriteOnly);
        stream << 1 << -1 << rebootOptions[u.mid(16).toInt()];

        kapp->dcopClient()->send("ksmserver", "default", "logoutTimed(int,int,QString)", params);
#else
        KMessageBox::error( this, QString( "Sorry, only implemented already for openSUSE." ));
#endif
    }
#warning restart entry not supported
#if 0
    else if ( u == "kicker:/restart_windows" ) {
        if (KMessageBox::Continue==KMessageBox::warningContinueCancel(this, i18n("Do you really want to reset the computer and boot Microsoft Windows"), i18n("Start Windows Confirmation"), KGuiItem(i18n("Start Windows"),"reload")))
            KMessageBox::error( this, QString( "kicker:/restart_windows is not yet implemented " ) );
    }
#endif
    else if ( u.startsWith("kerry:/"))
    {
       QByteArray data;
       QDataStream arg(data, IO_WriteOnly);
       arg << m_kcommand->currentText() << kerry_categories[u.mid(7).toInt()];
       if (ensureServiceRunning("kerry"))
           kapp->dcopClient()->send("kerry","search","search(QString,QString)", data);
    }
    else {
        addToHistory();
        if (u.startsWith("kaddressbook --uid")) {
            KProcess *proc = new KProcess;
            *proc << "kaddressbook" << "--uid" << u.mid(19);
            proc->start();
            accept();
            return;
        } else if (u.startsWith("note:/")) {
            KProcess *proc = new KProcess;
            *proc << "tomboy";
            *proc << "--open-note" << u;
            if (!proc->start())
               KMessageBox::error(0,i18n("Could not start Tomboy."));
            return;
        }
        else if (u.startsWith("knotes:/") ) {
            if (ensureServiceRunning("knotes")) {
                QByteArray data;
                QDataStream arg(data, IO_WriteOnly);
                arg << u.mid(9,22);

                kapp->dcopClient()->send("knotes","KNotesIface","showNote(QString)", data);
            }
            return;
        }

        kapp->propagateSessionManager();
        (void) new KRun( u, parentWidget());
    }
}

void KMenu::slotContextMenuRequested( QListViewItem * item, const QPoint & pos, int /*col*/ )
{
    const QObject* source = sender();

    if (!item)
        return;

    KMenuItem* kitem = dynamic_cast<KMenuItem*>(item);
    if (!kitem)
        return;

    KFileItemList _items;
    _items.setAutoDelete(true);

    if (dynamic_cast<KMenuItemSeparator*>(item))
        return;

    m_popupService = kitem->service();
    m_popupPath.menuPath = kitem->menuPath();
    if (!m_popupService) {
        m_popupPath.title = kitem->title();
        m_popupPath.description = kitem->description();
        m_popupPath.path = kitem->path();
        m_popupPath.icon = kitem->icon();

        if (m_popupPath.path.startsWith(locateLocal("data", QString::fromLatin1("RecentDocuments/")))) {
               KDesktopFile df(m_popupPath.path,true);
               m_popupPath.path=df.readURL();
        }
    }

    m_popupMenu = new KPopupMenu(this);
    connect(m_popupMenu, SIGNAL(activated(int)), SLOT(slotContextMenu(int)));
    bool hasEntries = false;

    m_popupMenu->insertTitle(SmallIcon(kitem->icon()),kitem->title());

     if (source==m_favoriteView)
     {
         hasEntries = true;
         m_popupMenu->insertItem(SmallIconSet("remove"),
             i18n("Remove From Favorites"), RemoveFromFavorites);
     }
     else if (!kitem->hasChildren() && !m_popupPath.path.startsWith("system:/"))
     {
         hasEntries = true;
         int num = m_popupMenu->insertItem(SmallIconSet("bookmark_add"),
             i18n("Add to Favorites"), AddToFavorites);

         QStringList favs = KickerSettings::favorites();
         if (m_popupService && favs.find(m_popupService->storageId())!=favs.end())
            m_popupMenu->setItemEnabled(num, false);
         else {
            QStringList::Iterator it;
            for (it = favs.begin(); it != favs.end(); ++it)
            {
                if ((*it)[0]=='/')
                {
                    KDesktopFile df((*it),true);
                    if (df.readURL().replace("file://",QString::null)==m_popupPath.path)
                        break;
                }
            }
            if (it!=favs.end())
                m_popupMenu->setItemEnabled(num, false);
         }
     }

     if (source!=m_exitView) {
        if (m_popupService || (!m_popupPath.path.startsWith("kicker:/") && !m_popupPath.path.startsWith("system:/") && !m_popupPath.path.startsWith("kaddressbook --uid"))) {
            if (hasEntries)
                m_popupMenu->insertSeparator();

            if (kapp->authorize("editable_desktop_icons") )
            {
                hasEntries = true;
                if (m_popupPath.menuPath.endsWith("/"))
                  m_popupMenu->insertItem(SmallIconSet("desktop"),
                    i18n("Add Menu to Desktop"), AddMenuToDesktop);
                else
                  m_popupMenu->insertItem(SmallIconSet("desktop"),
                      i18n("Add Item to Desktop"), AddItemToDesktop);
            }
            if (kapp->authorizeKAction("kicker_rmb") && !Kicker::the()->isImmutable())
            {
                hasEntries = true;
                if (m_popupPath.menuPath.endsWith("/"))
                   m_popupMenu->insertItem(SmallIconSet("kicker"),
                      i18n("Add Menu to Main Panel"), AddMenuToPanel);
                else
                   m_popupMenu->insertItem(SmallIconSet("kicker"),
                      i18n("Add Item to Main Panel"), AddItemToPanel);
            }
            if (kapp->authorizeKAction("menuedit") && !kitem->menuPath().isEmpty())
            {
                hasEntries = true;
                if (kitem->menuPath().endsWith("/"))
                  m_popupMenu->insertItem(SmallIconSet("kmenuedit"), i18n("Edit Menu"), EditMenu);
                else
                  m_popupMenu->insertItem(SmallIconSet("kmenuedit"), i18n("Edit Item"), EditItem);
            }
            if (kapp->authorize("run_command") && (m_popupService || (!m_popupPath.menuPath.isEmpty() && !m_popupPath.menuPath.endsWith("/"))))
            {
               hasEntries = true;
               m_popupMenu->insertItem(SmallIconSet("run"),
               i18n("Put Into Run Dialog"), PutIntoRunDialog);
            }
        }
        if (source==m_searchResultsWidget || ((source==m_favoriteView || source==m_recentlyView || source == m_systemView) && !m_popupService && !m_popupPath.path.startsWith("kicker:/")) ) {
            QString uri;
            if (m_popupService)
                uri = locate("apps", m_popupService->desktopEntryPath());
            else
                uri = m_popupPath.path;

            QString mimetype = QString::null;
            if ( m_popupPath.path.startsWith( "system:/media/" ) )
                mimetype = media_mimetypes[m_popupPath.path];

            KFileItem* item = new KFileItem(uri, mimetype, KFileItem::Unknown);
            _items.append( item );

            const KURL kurl(uri);
            KActionCollection act(this);

            KonqPopupMenu * konqPopupMenu = new KonqPopupMenu( KonqBookmarkManager::self(), _items,
                                                   kurl, act, (KNewMenu*)NULL, this,
                                                   item->isLocalFile() ? KonqPopupMenu::ShowProperties : KonqPopupMenu::NoFlags,
                                                   KParts::BrowserExtension::DefaultPopupItems );

            if (konqPopupMenu->count()) {
                if (hasEntries) {
                    m_popupMenu->insertSeparator();
                    m_popupMenu->insertItem(SmallIconSet("add"),i18n("Advanced"), konqPopupMenu);
                }
                else {
                    delete m_popupMenu;
                    m_popupMenu = (KPopupMenu*)konqPopupMenu;
                    m_popupMenu->insertTitle(SmallIcon(kitem->icon()),kitem->title(),-1,0);
                }
                hasEntries = true;
            }
        }
    }

    if (source==m_recentlyView) {
       m_popupMenu->insertSeparator();
       if (m_popupService)
         m_popupMenu->insertItem(SmallIconSet("history_clear"),
                 i18n("Clear Recently Used Applications"), ClearRecentlyUsedApps);
       else
         m_popupMenu->insertItem(SmallIconSet("history_clear"),
                 i18n("Clear Recently Used Documents"), ClearRecentlyUsedDocs);
    }

    if (hasEntries) {
       m_isShowing = true;
       m_popupMenu->exec(pos);
       m_isShowing = false;
    }

    delete m_popupMenu;
    m_popupMenu = 0;
}

void KMenu::slotContextMenu(int selected)
{
    KServiceGroup::Ptr g;
    QByteArray ba;
    QDataStream ds(ba, IO_WriteOnly);

    KURL src,dest;
    KIO::CopyJob *job;

    KProcess *proc;

    QStringList favs = KickerSettings::favorites();

    switch (selected) {
        case AddItemToDesktop:
            accept();
	    if (m_popupService) {
	        src.setPath( KGlobal::dirs()->findResource( "apps", m_popupService->desktopEntryPath() ) );
	        dest.setPath( KGlobalSettings::desktopPath() );
	        dest.setFileName( src.fileName() );

                job = KIO::copyAs( src, dest );
                job->setDefaultPermissions( true );
 	    }
            else {
		KDesktopFile* df = new KDesktopFile( newDesktopFile(KURL(m_popupPath.path), KGlobalSettings::desktopPath() ) );
		df->writeEntry("GenericName", m_popupPath.description);
		df->writeEntry( "Icon", m_popupPath.icon );
		df->writePathEntry( "URL", m_popupPath.path );
		df->writeEntry( "Name", m_popupPath.title );
		df->writeEntry( "Type", "Link" );
		df->sync();
		delete df;
            }
            accept();
	    break;

	case AddItemToPanel:
            accept();
	    if (m_popupService)
	    	kapp->dcopClient()->send("kicker", "Panel", "addServiceButton(QString)", m_popupService->desktopEntryPath());
            else
#warning FIXME special RecentDocuments/foo.desktop handling
	    	kapp->dcopClient()->send("kicker", "Panel", "addURLButton(QString)", m_popupPath.path);
	    accept();
	    break;

	case EditItem:
        case EditMenu:
	    accept();
            proc = new KProcess(this);
            *proc << KStandardDirs::findExe(QString::fromLatin1("kmenuedit"));
            *proc << "/"+m_popupPath.menuPath.section('/',-200,-2) << m_popupPath.menuPath.section('/', -1);
            proc->start();
	    break;

	case PutIntoRunDialog:
	    accept();
	    if (m_popupService)
	      kapp->dcopClient()->send("kdesktop", "default", "popupExecuteCommand(QString)", m_popupService->exec());
	    else
#warning FIXME special RecentDocuments/foo.desktop handling
              kapp->dcopClient()->send("kdesktop", "default", "popupExecuteCommand(QString)", m_popupPath.path);
            accept();
	    break;

	case AddMenuToDesktop: {
	    accept();
	    KDesktopFile *df = new KDesktopFile( newDesktopFile(KURL("programs:/"+m_popupPath.menuPath),KGlobalSettings::desktopPath()));
            df->writeEntry( "Icon", m_popupPath.icon );
            df->writePathEntry( "URL", "programs:/"+m_popupPath.menuPath );
	    df->writeEntry( "Name", m_popupPath.title );
	    df->writeEntry( "Type", "Link" );
            df->sync();
	    delete df;

	    break;
            }
	case AddMenuToPanel:
	    accept();
            ds << "foo" << m_popupPath.menuPath;
	    kapp->dcopClient()->send("kicker", "Panel", "addServiceMenuButton(QString,QString)", ba);
	    break;

        case AddToFavorites:
	    if (m_popupService) {
              if (favs.find(m_popupService->storageId())==favs.end()) {
                KService::Ptr p = KService::serviceByStorageId(m_popupService->storageId());
                m_favoriteView->insertMenuItem(p, serviceMenuEndId()+favs.count()+1);
                favs+=m_popupService->storageId();
              }
            }
            else {
               QStringList::Iterator it;
               for (it = favs.begin(); it != favs.end(); ++it) {
                  if ((*it)[0]=='/') {
                     KDesktopFile df((*it),true);
                     if (df.readURL().replace("file://",QString::null)==m_popupPath.path)
                        break;
                  }
               }
               if (it==favs.end()) {
                 QString file = KickerLib::newDesktopFile(m_popupPath.path);
                 KDesktopFile df(file);
                 df.writeEntry("Encoding", "UTF-8");
                 df.writeEntry("Type","Link");
                 df.writeEntry("Name", m_popupPath.title);
                 df.writeEntry("GenericName", m_popupPath.description);
                 df.writeEntry("Icon", m_popupPath.icon);
                 df.writeEntry("URL", m_popupPath.path);

                 m_favoriteView->insertItem(m_popupPath.icon, m_popupPath.title, m_popupPath.description,
                    m_popupPath.path, serviceMenuEndId()+favs.count()+1, -1);

                 favs+=file;
               }
            }
            KickerSettings::setFavorites(favs);
            KickerSettings::writeConfig();
            slotFavoritesButton();
	    break;

        case RemoveFromFavorites:
	    if (m_popupService) {
              favs.erase(favs.find(m_popupService->storageId()));

              for (QListViewItemIterator it(m_favoriteView); it.current(); ++it) {
                 KMenuItem* kitem = static_cast<KMenuItem*>(it.current());
	         if (kitem->service() && kitem->service()->storageId() == m_popupService->storageId()) {
                   delete it.current();
                   break;
                 }
              }
            }
            else {
               for (QStringList::Iterator it = favs.begin(); it != favs.end(); ++it) {
                  if ((*it)[0]=='/') {
                     KDesktopFile df((*it),true);
                     if (df.readURL().replace("file://",QString::null)==m_popupPath.path) {
			QFile::remove((*it));
                        favs.erase(it);
                        break;
                     }
                  }
               }
               for (QListViewItemIterator it(m_favoriteView); it.current(); ++it) {
                  KMenuItem* kitem = static_cast<KMenuItem*>(it.current());
	          if (!kitem->service() && kitem->path() == m_popupPath.path) {
                     delete it.current();
                     break;
                  }
               }
            }
	    m_favoriteView->slotMoveContent();
            KickerSettings::setFavorites(favs);
            KickerSettings::writeConfig();
            m_browserDirty=true;
            slotFavoritesButton();
	    break;

        case ClearRecentlyUsedApps:
            clearRecentAppsItems();
	    break;

        case ClearRecentlyUsedDocs:
            clearRecentDocsItems();
	    break;

	default:
	    break;
	}
}

void KMenu::resizeEvent ( QResizeEvent * e )
{
    //kdDebug() << "resizeEvent " << size() << endl;
    KMenuBase::resizeEvent(e);
    int ypos = 0;
    // this is the height remaining to fill
    int left_height = height();

    if ( m_orientation == BottomUp )
    {
        m_resizeHandle->move( e->size().width() - 19, 3);

        // put the search widget at the top of the menu and give it its desired
        // height
        m_search->mainWidget()->setGeometry( 0, ypos, width(),
                m_search->minimumSize().height() );
        left_height -= m_search->minimumSize().height();
        ypos += m_search->minimumSize().height();

        // place the footer widget at the bottom of the menu and give it its desired
        // height
        m_footer->mainWidget()->setGeometry( 0, height() - m_footer->minimumSize().height(),
                width(), m_footer->minimumSize().height() );
        left_height -= m_footer->minimumSize().height();

        // place the button box above the footer widget, horizontal placement
        // has the width of the edge graphics subtracted
        buttonBox->mainWidget()->setGeometry( button_box_left.width(),
                height() - m_footer->minimumSize().height() -
                buttonBox->minimumSize().height(),
                width() - button_box_left.width(),
                buttonBox->minimumSize().height() );
        left_height -= buttonBox->minimumSize().height();

        // place the main (stacker) widget below the search widget,
        // in the remaining vertical space
        m_stacker->setGeometry(main_border_lc.width(), ypos,
                width() - main_border_lc.width() - main_border_rc.width(),
                left_height );

        button_box_left.load( locate( "appdata", "pics/button-box-left.png" ) );
        button_box_left.convertFromImage( button_box_left.convertToImage().
                scale(button_box_left.width(),
                    buttonBox->mainWidget()->height() ) );
    }
    else // TopDown orientation
    {
        // place the 'footer' widget at the top of the menu and give it
        // its desired height
        m_footer->mainWidget()->setGeometry( 0,
                ypos /*height() - m_footer->minimumSize().height()*/,
                width(),
                m_footer->minimumSize().height() );
        ypos += m_footer->minimumSize().height();
        left_height -= m_footer->minimumSize().height();

        // place the button box next at the top of the menu.
        // has the width of the edge graphics subtracted
        buttonBox->mainWidget()->setGeometry( button_box_left.width(),
                ypos,
                width() - button_box_left.width(),
                buttonBox->minimumSize().height() );
        ypos += buttonBox->minimumSize().height();
        left_height -= buttonBox->minimumSize().height();

        // put the search widget above the footer widget
        // height
        m_search->mainWidget()->setGeometry( 0,
                height() - m_search->minimumSize().height(),
                width(),
                m_search->minimumSize().height()
                );
        left_height -= m_search->minimumSize().height();

        // place the main (stacker) widget below the button box,
        // in the remaining vertical space
        m_stacker->setGeometry(main_border_lc.width(), ypos,
                width() - main_border_lc.width() - main_border_rc.width(),
                left_height );
        m_resizeHandle->move( e->size().width() - 19, e->size().height() - 19);
    }
    paintTab( m_btnRecently, false );
    paintTab( m_btnFavorites, false );
    paintTab( m_browser, false );
    paintTab( m_system, false );
    paintTab( m_exit, false );
    paintTab( m_expandSpace, false );
    paintTab( m_searchFrame, false );
    paintTab( m_activeTab, true );
}

void KMenu::mousePressEvent ( QMouseEvent * e )
{
    kdDebug() << "KMenu::mousePressEvent " << e->pos() << " " << m_resizeHandle->pos() << endl;
    if ( m_orientation == BottomUp ) {
       if (e->x() > width() - m_resizeHandle->width() &&
          e->y() < m_resizeHandle->height() )
       {
           kdDebug() << "hit!" << endl; // TODO: it's triangle
            m_isresizing = true;
       }
    }
    else {
       if (e->x() > width() - m_resizeHandle->width() &&
          e->y() > height() - m_resizeHandle->height() )
       {
           kdDebug() << "hit!" << endl; // TODO: it's triangle
            m_isresizing = true;
       }
    }

    QPoint pos = buttonBox->mainWidget()->mapFromGlobal(e->globalPos());
    if ( m_exit->geometry().contains( pos ) )
        emit clickedExitButton();
    else if ( m_system->geometry().contains( pos ) )
        emit clickedSystemButton();
    else if ( m_btnFavorites->geometry().contains( pos ) )
        emit clickedFavoritesButton();
    else if ( m_browser->geometry().contains( pos ) )
        emit clickedBrowserButton();
    else if ( m_btnRecently->geometry().contains( pos ) )
        emit clickedRecentlyButton();

    KMenuBase::mousePressEvent(e);
}

void KMenu::mouseReleaseEvent ( QMouseEvent * /*e*/ )
{
    m_isresizing = false;
}

void KMenu::mouseMoveEvent ( QMouseEvent * e )
{
    if ( hasMouseTracking() && m_isresizing ) {
        m_stacker->setMinimumSize( QSize(0, 0) );
        m_stacker->setMaximumSize( QSize(32000, 32000) );
        int newWidth = QMAX( e->x() - x(), minimumSizeHint().width() );
        if ( m_orientation == BottomUp ) {
          int newHeight = QMAX( height() - e->y(), minimumSizeHint().height() + 10 );
          int newY = y() + height() - newHeight;
          setGeometry( x(), newY, newWidth, newHeight);
        }
        else {
          setGeometry( x(), y(), newWidth, QMAX( e->y(), minimumSizeHint().height() + 10 ));
        }
    }
}

void KMenu::clearedHistory()
{
    saveConfig();
}

void KMenu::saveConfig()
{
    KickerSettings::setHistory( m_kcommand->historyItems() );
    KickerSettings::setCompletionItems( m_kcommand->completionObject()->items() );
    KickerSettings::writeConfig();
}

void KMenu::notifyServiceStarted(KService::Ptr service)
{
    // Inform other applications (like the quickstarter applet)
    // that an application was started
    QByteArray params;
    QDataStream stream(params, IO_WriteOnly);
    stream << "minicli" << service->storageId();
    kdDebug() << "minicli appLauncher dcop signal: " << service->storageId() << endl;
    KApplication::kApplication()->dcopClient()->emitDCOPSignal("appLauncher",
        "serviceStartedByStorageId(QString,QString)", params);
}

void KMenu::parseLine( bool final )
{
  QString cmd = m_kcommand->currentText().stripWhiteSpace();
  m_filterData->setData( cmd );

  if( final )
    KURIFilter::self()->filterURI( *(m_filterData), m_finalFilters );
  else
    KURIFilter::self()->filterURI( *(m_filterData), m_middleFilters );

  m_iconName = m_filterData->iconName();

  kdDebug (1207) << "Command: " << m_filterData->uri().url() << endl;
  kdDebug (1207) << "Arguments: " << m_filterData->argsAndOptions() << endl;
}

// report error as a title in the menu
void KMenu::reportError (QString error)
{
    int index = 1000; //getHitMenuItemPosition (new HitMenuItem (base_category_id[0], 0));
    kndDebug () << "Inserting error:" << error << " at position " << index << endl;
    m_searchResultsWidget->insertSeparator(OTHER_ID_BASE + 120, error, index);
}

int KMenu::getHitMenuItemPosition ( HitMenuItem *hit_item)
{
    QPtrListIterator<HitMenuItem> it (m_current_menu_items);
    const HitMenuItem *cur_item;
    int pos = 0;
    while ((cur_item = it.current ()) != NULL) {
	++it;
	if ((cur_item->category!=hit_item->category || !cur_item->display_name.isEmpty()) && (*hit_item) < (*cur_item))
	    break;
	pos++;
    }
    m_current_menu_items.insert (pos, hit_item);

    return pos + 1;
}

bool KMenu::checkUriInMenu( const QString &uri)
{
    QPtrListIterator<HitMenuItem> it (m_current_menu_items);
    const HitMenuItem *cur_item;
    while ((cur_item = it.current ()) != NULL) {
	++it;
	if (cur_item->uri == uri )
	    return true;
    }

    return false;
}

void KMenu::searchActionClicked(QListViewItem* item)
{
   accept();

   addToHistory();
   if (item==m_searchIndex) {
     QByteArray data;
     QDataStream arg(data, IO_WriteOnly);
     arg << m_kcommand->currentText();

     if (ensureServiceRunning("kerry"))
       kapp->dcopClient()->send("kerry","search","search(QString)", data);
   }
   else {
     KURIFilterData data;
     QStringList list;
     data.setData( m_kcommand->currentText() );
     list << "kurisearchfilter" << "kuriikwsfilter";

     if( !KURIFilter::self()->filterURI(data, list) ) {
         KDesktopFile file("searchproviders/google.desktop", true, "services");
         data.setData(file.readEntry("Query").replace("\\{@}", m_kcommand->currentText()));
     }

     (void) new KRun( data.uri(), parentWidget());
   }
}

void KMenu::addToHistory()
{
  QString search = m_kcommand->currentText().stripWhiteSpace();

  if (search.length()<4)
    return;

  m_kcommand->addToHistory( search );
}

QString KMenu::newDesktopFile(const KURL& url, const QString &directory)
{
   QString base = url.fileName();
   if (base.endsWith(".desktop"))
      base.truncate(base.length()-8);
   QRegExp r("(.*)(?=-\\d+)");
   if (r.search(base) > -1)
      base = r.cap(1);

   QString file = base + ".desktop";

   for(int n = 1; ++n; )
   {
      if (!QFile::exists(directory+file))
         break;

      file = QString("%2-%1.desktop").arg(n).arg(base);
   }
   return directory+file;
}

void KMenu::updateRecentlyUsedApps(KService::Ptr &service)
{
    QString strItem(service->desktopEntryPath());

    // don't add an item from root kmenu level
    if (!strItem.contains('/'))
    {
        return;
    }

    // add it into recent apps list
    RecentlyLaunchedApps::the().appLaunched(strItem);
    RecentlyLaunchedApps::the().save();
    RecentlyLaunchedApps::the().m_bNeedToUpdate = true;
}

QSize KMenu::sizeHint() const
{
#warning FIXME
    // this should be only for the inner area so layout changes do not break it
    const int width = QMIN(KickerSettings::kMenuWidth(), QApplication::desktop()->width()-50);
    const int height = QMIN(KickerSettings::kMenuHeight(), QApplication::desktop()->height()-50);
    QSize wanted(width, height);
    kdDebug() << "show " << minimumSizeHint() << " " << m_stacker->minimumSizeHint() << " "
              << m_searchFrame->minimumSizeHint() << " " << wanted << endl;
    bool isDefault = wanted.isNull();
    wanted = wanted.expandedTo(minimumSizeHint());
    if ( isDefault )
        wanted.setHeight( wanted.height() + ( m_favoriteView->goodHeight() - m_stacker->minimumSizeHint().height() ) );

    return wanted;
}

QSize KMenu::minimumSizeHint() const
{
    QSize minsize = buttonBox->minimumSize();
    minsize.setWidth( minsize.width() + button_box_left.width() );
    minsize.setWidth( QMAX( minsize.width(),
                            m_search->minimumSize().width() ) );
    minsize.setWidth( QMAX( minsize.width(),
                            m_search->minimumSize().width() ) );

    minsize.setHeight( minsize.height() +
                       m_search->minimumSize().height() +
                       m_footer->minimumSize().height() +
                       180 ); // 180 is a very rough guess for 32 icon size
    return minsize;
}

void KMenu::slotFavoritesMoved( QListViewItem* item, QListViewItem* /*afterFirst*/, QListViewItem* afterNow)
{
    KMenuItem* kitem = dynamic_cast<KMenuItem*>(item);
    KMenuItem* kafterNow = dynamic_cast<KMenuItem*>(afterNow);

    QStringList favs = KickerSettings::favorites();
    QStringList::Iterator it;
    QString addFav = QString::null;

    // remove at old position
    if (kitem->service())
    {
        favs.erase(favs.find(kitem->service()->storageId()));
        addFav = kitem->service()->storageId();
    }
    else
    {
        for (it = favs.begin(); it != favs.end(); ++it)
        {
            if ((*it)[0]=='/')
            {
                KDesktopFile df((*it),true);
                if (df.readURL().replace("file://",QString::null)==kitem->path())
                {
                    addFav = *it;
                    favs.erase(it);
                    break;
                }
            }
        }
    }

    if (addFav.isEmpty())
      return;

    if (!kafterNow || dynamic_cast<KMenuSpacer*>(afterNow))
    {
        favs.prepend(addFav);
    }
    else
    {
        // add at new position
        for (it = favs.begin(); it != favs.end(); ++it)
        {
            if ((*it)[0]=='/' && !kafterNow->service())
            {
                KDesktopFile df((*it),true);
                if (df.readURL().replace("file://",QString::null)==kafterNow->path())
                {
                    kdDebug() << "insert after " << kafterNow->path() << endl;
                    favs.insert(++it,addFav);
                    break;
                }
            }
            else if (kafterNow->service() && *it==kafterNow->service()->storageId())
            {
                kdDebug() << "insert after service " << kafterNow->service() << endl;
                favs.insert(++it,addFav);
                break;
            }
        }
    }
    kdDebug() << "favs " << favs << endl;

    KickerSettings::setFavorites(favs);
    KickerSettings::writeConfig();

    m_favoriteView->slotMoveContent();
}

void KMenu::updateMedia()
{
    QStringList devices = m_mediaWatcher->devices();
    if ( devices.isEmpty() )
        return;

    int nId = serviceMenuStartId();
    if ( m_media_id ) {
        for ( int i = m_media_id + 1 ;; ++i )
        {
            KMenuItem *item = m_systemView->findItem( i );
            if ( !item )
                break;
            if ( !item->path().startsWith( "system:/" ) )
                break;
            media_mimetypes.remove(item->path());
            delete item;
        }
        nId = m_media_id + 1;
    } else {
        m_media_id = nId;
        m_systemView->insertSeparator( nId++, i18n("Media"), -1);
    }

    for ( QStringList::ConstIterator it = devices.constBegin(); it != devices.constEnd(); ++it )
    {
        QString id = ( *it );
        QString name = *++it;
        QString label = *++it;
        QString userLabel = ( *++it );
        bool mountable = ( *++it == "true" ); // bool
        ( void )mountable;
        QString deviceNode = ( *++it );
        QString mountPoint = ( *++it );
        QString fsType = ( *++it );
        bool mounted = ( *++it == "true" ); // bool
        QString baseURL = ( *++it );
        QString mimeType = ( *++it );
        QString iconName = ( *++it );

        media_mimetypes["system:/media/"+name] = mimeType;

        if ( iconName.isEmpty() ) // no user icon, query the MIME type
        {
            KMimeType::Ptr mime = KMimeType::mimeType( mimeType );
            iconName = mime->icon( QString::null, false );
        }

        QString descr = deviceNode;
        if ( mounted )
        {
            descr = mountPoint;
            // calc the free/total space
            struct statfs sfs;
            if ( statfs( QFile::encodeName( mountPoint ), &sfs ) == 0 )
            {
                uint64_t total = ( uint64_t )sfs.f_blocks * sfs.f_bsize;
                uint64_t avail = ( uint64_t )( getuid() ? sfs.f_bavail : sfs.f_bfree ) * sfs.f_bsize;
                if ( avail < total && avail > 1024 ) {
                    label += " " + i18n( "(%1 available)" ).arg( KIO::convertSize(avail) );
                }
            }
        }
        m_systemView->insertItem( iconName, label,
                                  descr, "system:/media/" + name, nId++, -1 );

        ++it; // skip separator
    }
}

bool KMenu::ensureServiceRunning(const QString & service)
{
    QStringList URLs;
    QByteArray data, replyData;
    QCString replyType;
    QDataStream arg(data, IO_WriteOnly);
    arg << service << URLs;

    if ( !kapp->dcopClient()->call( "klauncher", "klauncher", "start_service_by_desktop_name(QString,QStringList)",
                      data, replyType, replyData) ) {
        qWarning( "call to klauncher failed.");
        return false;
    }
    QDataStream reply(replyData, IO_ReadOnly);

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

void KMenu::slotFavDropped(QDropEvent * ev, QListViewItem *after )
{
    QStringList favs = KickerSettings::favorites();
    KMenuItem *newItem = 0;

    if (KMenuItemDrag::canDecode(ev))
    {
        KMenuItemInfo item;
        KMenuItemDrag::decode(ev,item);

        if (item.m_s)
        {
            if (favs.find(item.m_s->storageId())==favs.end())
            {
                newItem = m_favoriteView->insertMenuItem(item.m_s, serviceMenuEndId()+favs.count()+1);
                favs += item.m_s->storageId();
            }
        }
        else
        {
            QString uri = item.m_path;
            if (uri.startsWith(locateLocal("data", QString::fromLatin1("RecentDocuments/")))) {
               KDesktopFile df(uri,true);
               uri=df.readURL();
            }

            QStringList::Iterator it;
            for (it = favs.begin(); it != favs.end(); ++it)
            {
                if ((*it)[0]=='/')
                {
                    KDesktopFile df((*it),true);
                    if (df.readURL().replace("file://",QString::null)==uri)
                        break;
                }
            }
            if (it==favs.end())
            {
                QString file = KickerLib::newDesktopFile(uri);
                KDesktopFile df(file);
                df.writeEntry("Encoding", "UTF-8");
                df.writeEntry("Type","Link");
                df.writeEntry("Name", item.m_title);
                df.writeEntry("GenericName", item.m_description);
                df.writeEntry("Icon", item.m_icon);
                df.writeEntry("URL", uri);

                newItem = m_favoriteView->insertItem(item.m_icon, item.m_title, item.m_description,
                                                     uri, serviceMenuEndId()+favs.count()+1, -1);
                favs += file;
            }
        }
    }
    else if (QTextDrag::canDecode(ev))
    {
        QString text;
        QTextDrag::decode(ev,text);

        if (text.endsWith(".desktop"))
        {
            KService::Ptr p = KService::serviceByDesktopPath(text.replace("file://",QString::null));
            if (p && favs.find(p->storageId())==favs.end()) {
                newItem = m_favoriteView->insertMenuItem(p, serviceMenuEndId()+favs.count()+1);
                favs+=p->storageId();
            }
        }
        else
        {
            QStringList::Iterator it;
            for (it = favs.begin(); it != favs.end(); ++it)
            {
                if ((*it)[0]=='/')
                {
                    KDesktopFile df((*it),true);
                    if (df.readURL().replace("file://",QString::null)==text)
                        break;
                }
            }
            if (it==favs.end())
            {
                KFileItem* item = new KFileItem(text, QString::null, KFileItem::Unknown);
                KURL kurl(text);

                QString file = KickerLib::newDesktopFile(text);
                KDesktopFile df(file);
                df.writeEntry("Encoding", "UTF-8");
                df.writeEntry("Type","Link");
                df.writeEntry("Name", item->name());
                df.writeEntry("GenericName", i18n("Directory: %1").arg(kurl.upURL().path()));
                df.writeEntry("Icon", item->iconName());
                df.writeEntry("URL", text);

                newItem = m_favoriteView->insertItem(item->iconName(), item->name(), i18n("Directory: %1").arg(kurl.upURL().path()), text, serviceMenuEndId()+favs.count()+1, -1);
                favs += file;
            }
        }
    }

    if ( newItem ) {
        if (!after && m_favoriteView->childCount()>0) {
            newItem->moveItem( m_favoriteView->firstChild() );
            m_favoriteView->firstChild()->moveItem( newItem );
        }
        else
            newItem->moveItem( after );
        KickerSettings::setFavorites(favs);
        slotFavoritesMoved( newItem, 0, after );
    }
    raiseStackWidget( m_favoriteView );

}

void KMenu::resetOverflowCategory()
{
   if (m_overflowCategoryState==NotNeeded)
      m_overflowList.setAutoDelete( true );

   m_overflowList.clear();
   m_overflowList.setAutoDelete( false );
   m_overflowCategoryState = None;
   m_overflowCategory = num_categories;
}

void KMenu::fillOverflowCategory()
{
   if (m_overflowCategoryState==Filling) {
      initCategoryTitlesUpdate();
      for (HitMenuItem * item = m_overflowList.first(); item; item = m_overflowList.next() ) {
	  max_category_id [item->category]++;
          item->id=max_category_id [item->category];

          KMenuItem *hit_item = m_searchResultsWidget->insertItem(iconForHitMenuItem(item), item->display_name, item->display_info, item->uri.url(), max_category_id [item->category], getHitMenuItemPosition (item));
          hit_item->setService(item->service);
      }
      updateCategoryTitles();
  }
}

int KMenu::max_items(int category) const
{
    if (category==ACTIONS)
      return 10;

    return 5;
}

#define DBUS_HAL_INTERFACE             "org.freedesktop.Hal"
#define DBUS_HAL_SYSTEM_POWER_INTERFACE        "org.freedesktop.Hal.Device.SystemPowerManagement"
#define HAL_UDI_COMPUTER               "/org/freedesktop/Hal/devices/computer"

#ifdef KDELIBS_SUSE
#include <liblazy.h>
#endif

void KMenu::insertSuspendOption( int &nId, int &index )
{
#ifdef KDELIBS_SUSE
    int supported = -1;
    bool suspend_ram, suspend_disk, standby;

    liblazy_hal_get_property_bool(HAL_UDI_COMPUTER, "power_management.can_suspend", &supported);
    if (supported == 1)
        suspend_ram = true;
    else
        suspend_ram = false;
	liblazy_hal_get_property_bool(HAL_UDI_COMPUTER, "power_management.can_standby", &supported);
	if (supported == 1)
	     standby = true;
	else
	     standby = false;
	liblazy_hal_get_property_bool(HAL_UDI_COMPUTER, "power_management.can_hibernate", &supported);
	if (supported == 1)
	     suspend_disk = true;
	else
	     suspend_disk = false;

	if (liblazy_polkit_is_user_allowed_by_uid(getuid(), "hal-power-hibernate", NULL) != 1)
	     suspend_disk = false;
	if (liblazy_polkit_is_user_allowed_by_uid(getuid(), "hal-power-suspend", NULL) != 1)
	     suspend_ram = false;
	if (liblazy_polkit_is_user_allowed_by_uid(getuid(), "hal-power-standby", NULL) != 1)
	    standby = false;

	if ( ! ( standby + suspend_ram + suspend_disk ) )
            return;

        i18n("Suspend Computer");

        if ( suspend_disk )
            m_exitView->leftView()->insertItem( "player_pause", i18n( "Suspend to Disk" ),
                                                i18n( "Pause without logging out" ), "kicker:/suspend_disk", nId++, index++ );

        if ( suspend_ram )
            m_exitView->leftView()->insertItem( "player_pause", i18n( "Suspend to RAM" ),
                                                i18n( "Pause without logging out" ), "kicker:/suspend_ram", nId++, index++ );

        if ( standby )
            m_exitView->leftView()->insertItem( "player_pause", i18n( "Standby" ),
                                                i18n( "Pause without logging out" ), "kicker:/standby", nId++, index++ );
#endif
}

void KMenu::slotSuspend(int id)
{
#ifdef KDELIBS_SUSE
    int error = 0;
    int wake = 0;
    DBusMessage *reply;

    if (id == 1) {
        error = liblazy_dbus_system_send_method_call(DBUS_HAL_INTERFACE,
                                                     HAL_UDI_COMPUTER,
                                                     DBUS_HAL_SYSTEM_POWER_INTERFACE,
                                                     "Hibernate",
                                                     &reply,
                                                     DBUS_TYPE_INVALID);
    } else if (id == 2)
        error = liblazy_dbus_system_send_method_call(DBUS_HAL_INTERFACE,
                                                     HAL_UDI_COMPUTER,
                                                     DBUS_HAL_SYSTEM_POWER_INTERFACE,
                                                     "Suspend",
                                                     &reply,
                                                     DBUS_TYPE_INT32,
                                                     &wake,
                                                     DBUS_TYPE_INVALID);
    else if (id == 3)
        error = liblazy_dbus_system_send_method_call(DBUS_HAL_INTERFACE,
                                                     HAL_UDI_COMPUTER,
                                                     DBUS_HAL_SYSTEM_POWER_INTERFACE,
                                                     "Standby",
                                                     &reply,
                                                     DBUS_TYPE_INVALID);
    else
        return;
    if (error)
#endif
        KMessageBox::error(this, i18n("Suspend failed"));

}

// vim:cindent:sw=4:
