/*
 * krootwm.cc Part of the KDE project.
 *
 * Copyright (C) 1997 Matthias Ettrich
 *           (C) 1997 Torben Weis, weis@kde.org
 *           (C) 1998 S.u.S.E. weis@suse.de
 *
 */

#include <qdir.h>

#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

#include <qmessagebox.h>
#include <qtimer.h>
#include <qpopupmenu.h>
#include <kapp.h>
#include <kbookmark.h>
#include <kbookmarkmenu.h>
#include <kglobal.h>
#include <kio/job.h>
#include <kiconloader.h>
#include <klocale.h>
#include <knewmenu.h>
#include <kprocess.h>
#include <krun.h>
#include <ksimpleconfig.h>
#include <kwinmodule.h>
#include <kwin.h>
#include <kwm.h>
#include <kglobalsettings.h>
#include <kstddirs.h>
#include <dcopclient.h>
#include <khelpmenu.h>
#include <kstringhandler.h>
#include <kiconloader.h>

#include <X11/X.h>
#include <X11/Xlib.h>

#include "krootwm.h"
#include "minicli.h"
#include "desktop.h"

#include <kmenubar.h>

void execute(const char* cmd){
  kapp->propagateSessionManager();
  KShellProcess proc;
  proc << cmd;
  proc.start(KShellProcess::DontCare);
}

KRootWm * KRootWm::s_rootWm = 0L;

KRootWm::KRootWm(KDesktop* _desktop) : QObject()
{
  desktopMenu = 0L; // not yet built

  m_actionCollection = new KActionCollection();

  s_rootWm = this;
  wmakerMode = FALSE;

  miniCli = new Minicli;

  m_pDesktop = _desktop;

  myMenuBar = 0;
  myMenuBarContainer = 0;

  m_bInit = true;
  initConfig();
  m_bInit = false;

  // Stop the initialisation here.
  // We'll create the menus in 2 seconds (it's long to do and the user
  // doesn't need it RIGHT NOW)
  QTimer::singleShot( 2000, this, SLOT(slotTimerDone()) );
}

KRootWm::~KRootWm()
{
    delete miniCli;
    delete bookmarkMenu;
}

void KRootWm::initConfig()
{
  KConfig *kconfig = KGlobal::config();

  // parse the configuration
  kconfig->setGroup(QString::fromLatin1("KDE"));
  bool global_mac = kconfig->readBoolEntry(QString::fromLatin1("macStyle"), false);
  kconfig->setGroup(QString::fromLatin1("Menubar"));
  bool local_mac = kconfig->readBoolEntry(QString::fromLatin1("TopLevel"), true);
  macMode = (global_mac || local_mac);

  // read configuration for clicks on root window
  const char * s_choices[4] = { "", "WindowListMenu", "DesktopMenu", "AppMenu" };
  leftButtonChoice = middleButtonChoice = rightButtonChoice = NOTHING;
  kconfig->setGroup("Mouse Buttons");
  QString s = kconfig->readEntry("Left", "");
  for ( int c = 0 ; c < 4 ; c ++ )
    if (s == s_choices[c])
      { leftButtonChoice = (menuChoice) c; break; }
  s = kconfig->readEntry("Middle", "WindowListMenu");
  for ( int c = 0 ; c < 4 ; c ++ )
    if (s == s_choices[c])
      { middleButtonChoice = (menuChoice) c; break; }
  s = kconfig->readEntry("Right", "DesktopMenu");
  for ( int c = 0 ; c < 4 ; c ++ )
    if (s == s_choices[c])
      { rightButtonChoice = (menuChoice) c; break; }

  // if we aren't initting, then we rebuild the menubars
  if (m_bInit == false)
    buildMenubars();
}

void KRootWm::slotTimerDone()
{
  // Creates the new menu
  menuNew = new KNewMenu( m_actionCollection, "new_menu" );

  bookmarks = new KActionMenu( i18n("Bookmarks"), "www", m_actionCollection, "bookmarks" );
  bookmarkMenu = new KBookmarkMenu( new KBookmarkOwner(),
                                    (QPopupMenu*)bookmarks->popupMenu(),
                                    m_actionCollection,
                                    true, false );

  // The windowList and desktop menus can be part of a menubar (Mac style)
  // so we create them here
  desktopMenu = new QPopupMenu;
  desktopMenu->setFont(KGlobalSettings::menuFont());
  windowListMenu = new QPopupMenu;
  windowListMenu->setFont(KGlobalSettings::menuFont());
  windowListMenu->setCheckable(TRUE);

  // Create the actions

  // Can't use KStdAction, we don't want an accel nor a shortcut (IMHO)
  new KAction(i18n("Paste"), "editpaste", 0, this, SLOT( slotPaste() ), m_actionCollection, "paste" );
  new KAction(i18n("Help on desktop"), "contents", 0, this, SLOT( slotHelp() ), m_actionCollection, "help" );
  new KAction(i18n("Run Command..."), "run", 0, this, SLOT( slotExec() ), m_actionCollection, "exec" );
  new KAction(i18n("Configure Background..."), "background", 0, this, SLOT( slotConfigureBackground() ),
              m_actionCollection, "configbackground" );
  new KAction(i18n("Configure Desktop..."), "configure", 0, this, SLOT( slotConfigureDesktop() ),
              m_actionCollection, "configdesktop" );
  new KAction(i18n("Unclutter Windows"), 0, this, SLOT( slotUnclutterWindows() ),
              m_actionCollection, "unclutter" );
  new KAction(i18n("Cascade Windows"), 0, this, SLOT( slotCascadeWindows() ),
              m_actionCollection, "cascade" );
  new KAction(i18n("Arrange Icons"), 0, this, SLOT( slotArrangeIcons() ),
              m_actionCollection, "arrange" );
  new KAction(i18n("Line up Icons"), 0, this, SLOT( slotLineupIcons() ),
              m_actionCollection, "lineup" );
  // Icons in sync with kicker
  new KAction(i18n("Lock Screen"), "lock", 0, this, SLOT( slotLock() ),
              m_actionCollection, "lock" );
  new KAction(i18n("Logout"), "exit", 0, this, SLOT( slotLogout() ),
              m_actionCollection, "logout" );


  buildMenubars();
}

void KRootWm::buildMenubars()
{
    if (macMode) {
        myMenuBarContainer = new QWidget(0, 0, WStyle_Customize|WStyle_NoBorderEx);
        myMenuBarContainer->setCaption("MAC MENU");
        KWM::setSticky( myMenuBarContainer->winId(), TRUE );

        myMenuBar = new KMenuBar(myMenuBarContainer);
        myMenuBar->setTopLevelMenu(true);

        myMenuBarContainer->setGeometry(0, qApp->desktop()->width()+10,100,40);
        myMenuBarContainer->show();
        myMenuBarContainer->hide();
    }
    else {
        // always try to delete these just in case we are rebuilding
        // and used to be in top-level mode but aren't anymore
        delete myMenuBarContainer; myMenuBarContainer = 0;
        delete myMenuBar; myMenuBar = 0;
    }

    if (myMenuBar) {
        file = new QPopupMenu;
        file->setMouseTracking(TRUE);
        file->installEventFilter(this);

        m_actionCollection->action("exec")->plug( file );

        file->insertSeparator();
        m_actionCollection->action("lock")->plug( file );
        m_actionCollection->action("logout")->plug( file );

        desk = new QPopupMenu;
        desk->setMouseTracking(TRUE);
        desk->installEventFilter(this);

        m_actionCollection->action("unclutter")->plug( desk );
        m_actionCollection->action("cascade")->plug( desk );
        desk->insertSeparator();
        m_actionCollection->action("lineup")->plug( desk );
        m_actionCollection->action("arrange")->plug( desk );
        desk->insertSeparator();
        m_actionCollection->action("configbackground")->plug( desk );
        m_actionCollection->action("configdesktop")->plug( desk );

        connect(desk, SIGNAL( aboutToShow() ),
                this, SLOT( slotFileNewAboutToShow() ) );

        help = new KHelpMenu(0, 0, false);
    }

    desktopMenu->clear();
    desktopMenu->disconnect( this );
    windowListMenu->clear();
    windowListMenu->disconnect( this );

    menuNew->plug( desktopMenu );
    bookmarks->plug( desktopMenu );
    desktopMenu->insertSeparator();

    m_actionCollection->action("paste")->plug( desktopMenu );
    m_actionCollection->action("help")->plug( desktopMenu );
    m_actionCollection->action("exec")->plug( desktopMenu );
    desktopMenu->insertSeparator();
    m_actionCollection->action("configbackground")->plug( desktopMenu );
    m_actionCollection->action("configdesktop")->plug( desktopMenu );
    desktopMenu->insertSeparator();
    m_actionCollection->action("unclutter")->plug( desktopMenu );
    m_actionCollection->action("cascade")->plug( desktopMenu );
    desktopMenu->insertSeparator();
    m_actionCollection->action("lineup")->plug( desktopMenu );
    m_actionCollection->action("arrange")->plug( desktopMenu );

    if (wmakerMode) {
	desktopMenu->insertSeparator();
	desktopMenu->insertItem(i18n("Arrange Window Maker icons"), ITEM_WMAKER_ARRANGE_ICONS);
	desktopMenu->insertItem(i18n("Show all"), ITEM_WMAKER_SHOW_ALL);
	desktopMenu->insertItem(i18n("Hide other"), ITEM_WMAKER_HIDE_OTHER);
	QPopupMenu* wmaker = new QPopupMenu;
	wmaker->insertItem(i18n("Show info panel"), ITEM_WMAKER_INFO);
	wmaker->insertItem(i18n("Show legal panel"), ITEM_WMAKER_LEGAL);
	wmaker->insertItem(i18n("Restart"), ITEM_WMAKER_RESTART);
	wmaker->insertSeparator();
	wmaker->insertItem(i18n("Exit"), ITEM_WMAKER_EXIT);
	desktopMenu->insertItem(i18n("Window Maker"), wmaker);

	connect(desktopMenu, SIGNAL(activated(int)), this, SLOT(slotMenuItemActivated(int)));
    }

    desktopMenu->insertSeparator();
    m_actionCollection->action("lock")->plug( desktopMenu );
    m_actionCollection->action("logout")->plug( desktopMenu );
    connect( desktopMenu, SIGNAL( aboutToShow() ), this, SLOT( slotFileNewAboutToShow() ) );

    connect(windowListMenu, SIGNAL(activated(int)), this, SLOT(slotWindowItemActivated(int)));
    connect(windowListMenu, SIGNAL(aboutToShow()), this, SLOT(generateWindowlist()));

    if (myMenuBar) {
        myMenuBar->insertItem(i18n("File"), file);
        myMenuBar->insertItem(i18n("New"), (QPopupMenu*)menuNew->popupMenu());
        myMenuBar->insertItem(i18n("Bookmarks"), (QPopupMenu*)bookmarks->popupMenu());
        myMenuBar->insertItem(i18n("Desktop"), desk);
        myMenuBar->insertItem(i18n("Windows"), windowListMenu);
        myMenuBar->insertItem(i18n("Help"), help->menu());

        if (macMode) {
            connect(m_pDesktop->kwinModule(), SIGNAL(windowActivate(WId)), this,
                    SLOT(slotFocusChanged(WId)));
        }

        if (KWin::activeWindow() != None)
            myMenuBar->lower();
    }
}

void KRootWm::slotFileNewAboutToShow()
{
  // As requested by KNewMenu :
  menuNew->slotCheckUpToDate();
  // And set the files that the menu apply on :
  menuNew->setPopupFiles( m_pDesktop->url() );
}

/*
void KRootWm::kwmCommandReceived(QString com)
{
  //sent by KBookmarkManager::emitChanged()
  if (com == "krootwm:refreshBM")
    updateBookmarkMenu ();
  else if (com == "macStyleOn") {
    if (!macMode) {
      KGlobal::config()->reparseConfiguration();
      macMode = true;
      buildMenubars();
    }
  }
  else if (com == "macStyleOff") {
    if (macMode) {
      delete myMenuBar;
      myMenuBar = 0;
      delete myMenuBarContainer;
      myMenuBarContainer = 0;
      macMode = false;
      updateBookmarkMenu ();
      buildMenubars(); // will add Bookmark menus
    }
  }
  else if (com == "wm:wmaker") {
    wmakerMode = TRUE;
    buildMenubars();
  }
  else if (com == "wm:kwm") {
    wmakerMode = FALSE;
    buildMenubars();
  }
}
*/
void KRootWm::slotExec()
{
  if ( miniCli->isVisible() ) {
      miniCli->raise();
      KWin::setActiveWindow( miniCli->winId() );
  } else {
      miniCli->move(QApplication::desktop()->width()/2 - miniCli->width()/2,
		    QApplication::desktop()->height()/2 - miniCli->height()/2);
      miniCli->hide(); // withdraw
      miniCli->show();
  }
}

void KRootWm::activateMenu( menuChoice choice, const QPoint& global )
{
  switch ( choice )
  {
    case WINDOWLISTMENU:
      windowListMenu->popup(global);
      break;
    case DESKTOPMENU:
      desktopMenu->popup(global);
      break;
    case APPMENU:
    {
      // XAllowEvents(qt_xdisplay(), AsyncPointer, CurrentTime); // ?
      // This allows the menu to disappear when clicking on the background another time
      XUngrabPointer(qt_xdisplay(), CurrentTime);
      XSync(qt_xdisplay(), False);
      // Ask kicker to showup the menu
      QByteArray data;
      QDataStream stream( data, IO_WriteOnly );
      stream << global.x();
      stream << global.y();
      kapp->dcopClient()->send( "kicker", "kickerMenuManager", "popupKMenu(int,int)", data );
      break;
    }
    case NOTHING:
      break;
  }
}

void KRootWm::mousePressed( const QPoint& _global, int _button )
{
    if (!desktopMenu) return; // initialisation not yet done
    switch ( _button ) {
    case LeftButton:
	if ( macMode && myMenuBarContainer ) {
	    myMenuBarContainer->raise();
	}
	activateMenu( leftButtonChoice, _global );
	break;
    case MidButton:
	activateMenu( middleButtonChoice, _global );
	break;
    case RightButton:
	activateMenu( rightButtonChoice, _global );
	break;
    default:
	// nothing
	break;
    }
}


void KRootWm::slotArrangeIcons() {
  m_pDesktop->rearrangeIcons( true /* ask confirmation */);
}
void KRootWm::slotLineupIcons() {
  m_pDesktop->lineupIcons();
}
void KRootWm::slotConfigureBackground() {
  execute("kcmshell LookNFeel/Desktop/background");
}
void KRootWm::slotConfigureDesktop() {
  execute("kcmshell LookNFeel/Desktop/desktop");
}
void KRootWm::slotUnclutterWindows() {
  KWM::sendKWMCommand("deskUnclutter");
}
void KRootWm::slotCascadeWindows() {
  KWM::sendKWMCommand("deskCascade");
}
void KRootWm::slotHelp() {
  execute ("khelpcenter");
}
void KRootWm::slotPaste() {
  m_pDesktop->unselectAll(); // perhaps a hack. But required by pasteSelection() currently
  m_pDesktop->slotPaste();
}
void KRootWm::slotLock() {
  kapp->dcopClient()->send("kdesktop", "KScreensaverIface", "lock()", "");
}
void KRootWm::slotLogout() {
  kapp->requestShutDown();
}

void KRootWm::slotMenuItemActivated(int item)
{
  switch (item)
  {
    case ITEM_WMAKER_INFO :
      KWM::sendKWMCommand("wmaker:lega"); // _INFO calls lega ?? (David)
      break;
    case ITEM_WMAKER_LEGAL :
      KWM::sendKWMCommand("wmaker:info"); // _LEGA calls info ?? (David)
      break;
    case ITEM_WMAKER_ARRANGE_ICONS :
      KWM::sendKWMCommand("wmaker:arrangeIcons");
      break;
    case ITEM_WMAKER_SHOW_ALL :
      KWM::sendKWMCommand("wmaker:showAll");
      break;
    case ITEM_WMAKER_HIDE_OTHER :
      KWM::sendKWMCommand("wmaker:hideOther");
      break;
    case ITEM_WMAKER_RESTART :
      KWM::sendKWMCommand("wmaker:restart");
      break;
    case ITEM_WMAKER_EXIT :
      KWM::sendKWMCommand("wmaker:exit");
      break;
    default:
      // nothing
      break;
  }
}

void KRootWm::slotWindowItemActivated(int item){
  if (item>1000){
    KWin::setCurrentDesktop(item-1000);
  }
  else {
    Window w = m_pDesktop->kwinModule()->windows()[item];
    KWin::setActiveWindow(w);
  }
}


void KRootWm::generateWindowlist(){ //sven changed this to slot
  windowListMenu->clear();
  int i = 0;
  int d = 1;
  int nd = KWin::numberOfDesktops();
  int cd = KWin::currentDesktop();
  WId active_window = KWin::activeWindow();
  if (nd > 1){
    for (d=1; d<=nd; d++){
      windowListMenu->insertItem(QString("&")+QString::number(d), 1000+d);
      if (!active_window && d == cd)
        windowListMenu->setItemChecked(1000+d, TRUE);

      QValueList<WId>::ConstIterator it;
      i = 0;
      it = m_pDesktop->kwinModule()->windows().begin();
      for ( ; it != m_pDesktop->kwinModule()->windows().end(); ++it,++i) {

        if ( (KWM::desktop(*it) == d && !KWM::isSticky(*it) ) ||
             (d == cd && KWM::isSticky(*it)) ) {

          QString title(KWM::titleWithState(*it));

          if (KWin::avoid(*it))
            continue;

          QPixmap pm = KWM::miniIcon(*it, 16, 16);
          windowListMenu->insertItem(pm.isNull()?defaultPixmap:pm,
              QString("   ")+
              KStringHandler::csqueeze(title,25),i);
          if (*it == active_window)
            windowListMenu->setItemChecked(i, TRUE);
        }
      }
      if (d < nd)
        windowListMenu->insertSeparator();
    }
  }
  else {
    QValueList<WId>::ConstIterator it;
    for (it = m_pDesktop->kwinModule()->windows().begin();
        it != m_pDesktop->kwinModule()->windows().end(); ++it,++i) {
      if ( (KWM::desktop(*it) == d && !KWM::isSticky(*it)) ||
           (d == cd && KWM::isSticky(*it))){

        QString title(KWM::titleWithState(*it));

        if (KWin::avoid(*it))
          continue;

        QPixmap pm = KWM::miniIcon(*it, 16, 16);
        windowListMenu->insertItem(pm.isNull()?defaultPixmap:pm,
            KStringHandler::csqueeze(title,25),i);
        if (*it == active_window)
          windowListMenu->setItemChecked(i, TRUE);
      }
    }
  }
}

void KRootWm::slotFocusChanged(WId w)
{
  static Window oldFocus = None;
  if (myMenuBarContainer && macMode && w == None && w != oldFocus)
      myMenuBarContainer->raise();
  oldFocus = w;
}

#include "krootwm.moc"
