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

Copyright (c) 1996-2000 the kicker authors. See file AUTHORS.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

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

#include <math.h>

#include <qdragobject.h>
#include <qpixmap.h>
#include <qbitmap.h>

#include <kapp.h>
#include <kglobal.h>
#include <kstddirs.h>
#include <kurl.h>
#include <kdebug.h>
#include <kdesktopfile.h>
#include <kmimetype.h>
#include <kprocess.h>
#include <kpixmap.h>
#include <kpixmapeffect.h>
#include <dcopclient.h>

// X11/Qt conflict
#undef Unsorted

#include "appletarea.h"
#include "appletarea.moc"
#include "menus.h"
#include "dialogs.h"
#include "panel.h"
#include "internalapplet.h"

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

AppletArea::AppletArea( Orientation orient, QWidget* parent, const char* name)
  : Panner( orient, parent, name )
  , DCOPObject("appletArea")
  , _block_relayout(false)
  , _movingAC(false)
  , _moveAC(0)
  , _moveOffset(QPoint(0,0))
  , _mouseAccel(2)
  , _mouseThreshold(2)
{
  setAcceptDrops(true);
  _applets.setAutoDelete(false);

  // dummy widget used for visual feedback on move operations
  _dummyWidget = new QWidget(viewport(), "dummy_widget");
  _dummyWidget->hide();
  
  KConfig* config = KGlobal::config();
  config->setGroup("layout");
  if(!config->hasKey("appletpositions")) // default layout
    {
      // kmenu
      KMenuButtonContainer *kmenu = new KMenuButtonContainer(viewport());
      addApplet(kmenu);

	  // window list
	  WindowListButtonContainer *wlist = new WindowListButtonContainer(viewport());
      wlist->setFreeSpace(0.02);
	  addApplet(wlist);
      
      QString appPath = KGlobal::dirs()->resourceDirs("apps").last();
      
      // some url buttons
      URLButtonContainer *url;

      url = new URLButtonContainer(appPath + "/Home.desktop", viewport());
      url->setFreeSpace(0.04);
      addApplet(url);

      url = new URLButtonContainer(appPath + "/System/konsole.desktop", viewport());
      url->setFreeSpace(0.04);
      addApplet(url);

      url = new URLButtonContainer(appPath + "/KControl.desktop", viewport());
      url->setFreeSpace(0.04);
      addApplet(url);

      url = new URLButtonContainer(appPath + "/Help.desktop", viewport());
      url->setFreeSpace(0.04);
      addApplet(url);

      url = new URLButtonContainer(appPath + "/Editors/kwrite.desktop", viewport());
      url->setFreeSpace(0.04);
      addApplet(url);

      // pager applet
      InternalFrame *pager = new InternalFrame("kminipagerapplet",
                                               "kminipagerapplet.desktop", viewport());
      pager->setFreeSpace(0.2);
      addApplet(pager);

      // taskbar applet
      InternalFrame *taskbar = new InternalFrame("ktaskbarapplet",
                                                 "ktaskbarapplet.desktop", viewport());
      taskbar->setFreeSpace(0.3);
      addApplet(taskbar);

      // dock applet
      InternalFrame *dock = new InternalFrame("kdockapplet",
                                              "kdockapplet.desktop", viewport());
      dock->setFreeSpace(1);
      addApplet(dock);

      // date applet
      InternalFrame *date = new InternalFrame("kdateapplet",
                                               "kdateapplet.desktop", viewport());
      date->setFreeSpace(1);
      addApplet(date);
    }
  else // restore layout
    {
      // read layout stringlist
      QStringList appletLayout = config->readListEntry("appletpositions");

      // now restore the applets
      QStringList::Iterator it;
      it = appletLayout.begin();
      int freepos = 0;
      while(it != appletLayout.end())
        {
          config->setGroup("applets");
          AppletContainer *a = 0;
          
          // read applet id
          QString appletId(*it);

          // iterate...make sure not to read beyond the list end
          ++it;
          if(it == appletLayout.end())
            break;

          // read free space
          float fspace = (*it).toFloat();
          
          // external applet hack
          //fspace += freepos;
          freepos = 0;

          // iterate
          ++it;

          // is there a entry for the current appletId?
          if(!config->hasKey(appletId))
            continue;
          
          // read the entry for the current appletId
          QString data = config->readEntry(appletId);
          
          // create a matching applet container
          if (appletId.contains("KMenuButton") > 0)
            a = new KMenuButtonContainer(viewport());
          if (appletId.contains("WindowListButton") > 0)
            a = new WindowListButtonContainer(viewport());
          else if (appletId.contains("URLButton") > 0)
            a = new URLButtonContainer(data, viewport());
          else if (appletId.contains("BrowserButton") > 0)
            a = new BrowserButtonContainer(data, viewport());
          else if (appletId.contains("ExeButton") > 0)
            a = new ExeButtonContainer(data, viewport());
          else if (appletId.contains("InternalApplet") > 0)
            a = new InternalFrame(appletId, data, viewport());
          else if (appletId.contains("ExternalApplet") > 0)
            {
              int index = data.find("::seperator::");
              QString command = data;
              command.truncate(index);
              freepos = (data.mid(index + 13)).toInt();
              
              KProcess process;
              process << command << QString("--appletId") << appletId;
              process.start(KProcess::DontCare);
              
            }
     
          if (a) {
            a->setFreeSpace(fspace);
            addApplet(a);
          }
        }
    }
  layoutChildren();
  saveAppletConfig();

  configure();
}

AppletArea::~AppletArea()
{
  for (QListIterator<AppletContainer> it(_applets); it.current(); ++it )
    {
      AppletContainer *a = it.current();
      if (a) delete a;
    }
}

void AppletArea::configure()
{
  // set background pixmap
  KConfig* config = KGlobal::config();
  config->setGroup("panel");
  if (config->readBoolEntry("UseBackgroundTheme", false))
    {
      QString bgStr = config->readEntry("BackgroundTheme", "");
      if(!bgStr.isEmpty()){
        QPixmap bgPix(bgStr);
        if(!bgPix.isNull()){
          QBrush bgBrush(colorGroup().background(), bgPix);
          QPalette pal = kapp->palette();
          pal.setBrush(QColorGroup::Background, bgBrush);
          setPalette(pal);
        }
        else
          {
            QPalette pal = kapp->palette();
            setPalette(pal);
            warning("Kicker: Error loading background theme pixmap");
          }
      }
    }
  else
    {
      QPalette pal = kapp->palette();
      setPalette(pal);
    }

  for (QListIterator<AppletContainer> it(_applets); it.current(); ++it )
    {
      AppletContainer *a = it.current();
      if (a) a->configure();
    }
}

void AppletArea::addKMenuButton()
{
  KMenuButtonContainer *b = new KMenuButtonContainer(viewport());
  addApplet(b);
  moveToFirstFreePosition(b);
  saveAppletConfig();
}

void AppletArea::addWindowListButton()
{
  WindowListButtonContainer *b = new WindowListButtonContainer(viewport());
  addApplet(b);
  moveToFirstFreePosition(b);
  saveAppletConfig();
}

void AppletArea::addURLButton(const QString &url)
{
  URLButtonContainer *b = new URLButtonContainer(url, viewport());
  addApplet(b);
  moveToFirstFreePosition(b);
  saveAppletConfig();
}

void AppletArea::addBrowserButton(const QString &startDir)
{
  BrowserButtonContainer *b = new BrowserButtonContainer(startDir, viewport());
  addApplet(b);
  moveToFirstFreePosition(b);
  saveAppletConfig();
}

void AppletArea::addExeButton(const QString &filePath, const QString &icon,
                    const QString &cmdLine, bool inTerm)
{
  ExeButtonContainer *b = new ExeButtonContainer(filePath, icon, cmdLine, inTerm, viewport());
  addApplet(b);
  moveToFirstFreePosition(b);
  saveAppletConfig();
}

void AppletArea::addInternalFrame(const QString &name, const QString &desktopFile)
{
  InternalFrame *i = new InternalFrame(name, desktopFile, viewport());
  addApplet(i);
  moveToFirstFreePosition(i);
  saveAppletConfig();
}

void AppletArea::addApplet(AppletContainer* a)
{
  if (!a) return;

  setUniqueId(a);

  _applets.append(a);

  if (isTopLevel(a))
	  a->hide();
  else
	{
	  a->show();
	  connect(a, SIGNAL(moveme(AppletContainer*) ),
			  SLOT( startAppletMove(AppletContainer*)));
	}
  
  connect(a, SIGNAL(removeme(AppletContainer*) ),
		  SLOT( removeApplet(AppletContainer*)));
  connect(a, SIGNAL(requestSave()),
		  SLOT(saveAppletConfig()));

  if (a->inherits("ExternalFrame"))
      connect(a, SIGNAL(embeddedWindowDestroyed() ), this,
			  SLOT( embeddedWindowDestroyed()));
  if (a->inherits("InternalFrame"))
    connect(a, SIGNAL(sigUpdateLayout() ), this,
			  SLOT( slotLayoutChildren()));

  a->setOrientation(orientation());
  a->setPosition(PGlobal::panel->position());
  a->configure();
}

void AppletArea::setUniqueId(AppletContainer* a)
{
  QString idBase = a->appletType() + "_%1";
  QString newId;
  int i = 0;
  bool unique = false;

  while(!unique)
    {
      i++;
      newId = idBase.arg(i);

      unique = true;
      QListIterator<AppletContainer> it(_applets);
      for(; it.current() ; ++it)
        {
          AppletContainer* b = static_cast<AppletContainer*>(it.current());
          if (b->appletId() == newId)
            {
              unique = false;
              break;
            }
        }
    }
  a->setAppletId(newId);
}

void AppletArea::saveAppletConfig()
{
  //kdDebug() << "AppletArea::saveAppletConfig()" << endl;

  KConfig *config = KGlobal::config();
  config->setGroup("applets");

  QStringList layoutList;

  QListIterator<AppletContainer> it(_applets);
  for(; it.current() ; ++it)
    {
      AppletContainer* a = static_cast<AppletContainer*>(it.current());

      // append applet to layoutList
      layoutList.append(a->appletId());
      layoutList.append(QString("%1").arg(a->freeSpace()));

      // write applet config entry
      config->writeEntry(a->appletId(), a->configData()); 
    }
  
  config->setGroup("layout");
  config->writeEntry("appletpositions", layoutList);
  config->sync();
}

bool AppletArea::isStretch(AppletContainer* a)
{
  if (!a->inherits("AppletFrame"))
	return false;
  return (static_cast<AppletFrame*>(a)->flags() & InternalApplet::Stretch);
}

bool AppletArea::isTopLevel(AppletContainer* a)
{
  if (!a->inherits("AppletFrame"))
	return false;
  return (static_cast<AppletFrame*>(a)->flags() & InternalApplet::TopLevel);
}

void AppletArea::removeApplet(AppletContainer *a)
{
  if (a) {  
    delete a;
    _applets.removeRef(a);
  }

  updateAppletContainerList();
  layoutChildren();
  saveAppletConfig();
}

void AppletArea::startAppletMove(AppletContainer *a)
{
  if (!a) return;

  _moveAC = a;
  _movingAC = true;	
  setMouseTracking(true);
  QCursor::setPos(mapToGlobal(QPoint(a->x() + a->moveOffset().x(), a->y() + a->moveOffset().y())));
  grabMouse(sizeAllCursor);
  
  int accel_num, accel_den, threshold;
  XGetPointerControl( kapp->getDisplay(), 
                      &accel_num, &accel_den, &threshold );
  _mouseAccel = accel_num / accel_den;
  _mouseThreshold = threshold;
  
  XChangePointerControl( kapp->getDisplay(),
                         true, true, 1, 1, _mouseThreshold);
  
  _block_relayout = true;
  QListIterator<AppletContainer> it(_applets);
  for(; it.current() ; ++it)
	{
      AppletContainer* b = static_cast<AppletContainer*>(it.current());
     
      if (orientation() == Horizontal)
        b->resize(b->widthForHeight(height()), height());
      else
        b->resize(width(), b->heightForWidth(width()));
    }

  setupDummyWidget(a);
  a->raise();
}

void AppletArea::stopAppletMove(AppletContainer *b)
{
  if (_moveAC != b) return;

  _dummyWidget->hide();

  releaseMouse();
  setCursor(arrowCursor);
  _movingAC = false;
  setMouseTracking(false);
  
  // If the moving applet covers another applet it can be dropped on the
  // dummywidget(shadow).
  if ( coversAppletContainer(_moveAC, true) )
    _moveAC->move(_dummyWidget->pos());
 
  if(_moveAC->inherits("ButtonContainer"))
    static_cast<ButtonContainer*>(_moveAC)->completeMoveOperation();

  _moveAC = 0;
  
  XChangePointerControl( kapp->getDisplay(),
                         true, true, _mouseAccel, 1, _mouseThreshold);

  _block_relayout = false;

  // Not necessary anymore when the move algorithm is finished. 
  updateAppletContainerList();
  layoutChildren();

  saveAppletConfig();
}

void AppletArea::mouseReleaseEvent(QMouseEvent *)
{
  if (_movingAC && _moveAC)
    stopAppletMove(_moveAC);
}

void AppletArea::mouseMoveEvent(QMouseEvent *ev)
{
  if (!(_movingAC && _moveAC)) {
    Panner::mouseMoveEvent(ev);
    return;
  }
    
  // horizontal panel
  if (orientation() == Horizontal) {

    // calculate new position.
    int x = ev->pos().x()-_moveAC->moveOffset().x();
    
    // make sure the new position is inside the appletarea
    // scroll if needed
    if (x < 0) {
      x = 0;
      //scrollBy( -10, 0 );
    }
    else if (x > width() - _moveAC->width()) {
      x = width() - _moveAC->width();
      //scrollBy( 10, 0 );
    }

    // move the Applet to the calculated position, remember the old
    // position.
    int oldACx = _moveAC->x();
    _moveAC->move(x, _moveAC->y());

    if (oldACx < _moveAC->x()) { // left to right
     
      _applets.findRef(_moveAC);
      AppletContainer *a = _applets.next();
      AppletContainer *b = _moveAC;

      while (a) 
      {
        if (isTopLevel(a)) {
          a = _applets.next();
          continue;
        }
      
        // '_moveAC' has completely passed applet 'a'.
        if ( a->x() + a->width() <= _moveAC->x() ) {
          a->move(a->x() - _moveAC->width(), a->y());
          b = a;
          a = _applets.next();
          continue;
        }
        
        // 'a' has not been completely passed by '_moveAC', but still may be
        // covered by it.
        int switchMargin = 0;
          
        // calculate the position and width of the 'virtual' container 
        // containing moveAC and 'a'.
        int tx = a->x() - _moveAC->width();
        int twidth = _moveAC->width() + a->width();

        // determine the middle of the containers.
        int tmiddle = tx + twidth/2;
        int moveACmiddle = _moveAC->x() + _moveAC->width()/2;
        
        // move 'a' from the right side of the virtual container to the left
        // side if the middle of moveAC has moved far enough to the left, 
        // i.e. past the middle of the virtual container plus the 
        // switchMargin. The switchMargin prevents rapidly switching when 
        // '_moveAC' and 'a' have the same size.
        if (moveACmiddle >= tmiddle + switchMargin) {
          a->move(a->x() - _moveAC->width(), a->y());
          // store 'a', because it may become null in the next step.
          b = a; 
          a = _applets.next();
          continue;
        }

        // '_moveAC' doesn't cover 'a', and hasn't passed it. We now know that
        // this also yields for the rest of the applets, so break out of the
        // loop.
        break;
      }

      // visual feedback
      AppletContainer *c = coversAppletContainer(_moveAC, true);
      if (c)
        {
          int cxmiddle = c->x() + c->width()/2;
          
          if (_moveAC->x() < cxmiddle)
            _dummyWidget->move(QPoint(c->x() - _moveAC->width(), _moveAC->y()));
          else
            _dummyWidget->move(QPoint(c->x() + c->width(), _moveAC->y()));
         
          _dummyWidget->show();
        }
      else
        _dummyWidget->hide();

      // Finally move _moveAC to its new position in the applet list.
      if (b != _moveAC) {
        _applets.removeRef(_moveAC);
        _applets.insert( _applets.findRef(b) + 1, _moveAC );
      }
    }
    
    else if (oldACx > _moveAC->x()) { // right to left
      
      _applets.findRef(_moveAC);
      AppletContainer *a = _applets.prev();
      AppletContainer *b = _moveAC;

      while (a) 
      {
        if (isTopLevel(a)) {
          a = _applets.prev();
          continue;
        }
      
        if ( _moveAC->x() + _moveAC->width() <= a->x() ) {
          a->move(a->x() + _moveAC->width(), a->y());
          b = a;
          a = _applets.prev();
          continue;
        }
        
        int switchMargin = 0;
          
        // calculate the position and width of the 'virtual' container 
        // containing moveAC and a.
        int tx = a->x();
        int twidth = _moveAC->width() + a->width();

        // determine the middle of the containers.
        int tmiddle = tx + twidth/2;
        int moveACmiddle = _moveAC->x() + _moveAC->width()/2;

        // move a from the left side of the virtual container to the right
        // side if the middle of moveAC has moved past the middle of the
        // virtual container plus the switchMargin. The switchMargin 
        // prevents rapidly switching when moveAC and a have the same size.
        if (moveACmiddle <= tmiddle + switchMargin) {
          a->move(a->x() + _moveAC->width(), a->y());
          b = a;
          a = _applets.prev();
          continue;
        } 
        
        break;
      }

      // visual feedback
      AppletContainer *c = coversAppletContainer(_moveAC, true);
      if (c)
        {
          int cxmiddle = c->x() + c->width()/2;
          
          if (_moveAC->x() < cxmiddle)
            _dummyWidget->move(QPoint(c->x() - _moveAC->width(), _moveAC->y()));
          else
            _dummyWidget->move(QPoint(c->x() + c->width(), _moveAC->y()));
          
          _dummyWidget->show();
        }
      else
        _dummyWidget->hide();

      // Finally move _moveAC to its new position in the applet list.
      if (b != _moveAC) {
        _applets.removeRef(_moveAC);
        _applets.insert(_applets.findRef(b), _moveAC);
      }
    }
  }
  
  // vertical panel
  else if (orientation() == Vertical) {

    // calculate new position.
    int y = ev->pos().y()-_moveAC->moveOffset().y();
    
    // make sure the new position is inside the appletarea
    // scroll if needed
    if (y < 0) {
      y = 0;
      //scrollBy( -10, 0 );
    }
    else if (y > height() - _moveAC->height()) {
      y = height() - _moveAC->height();
      //scrollBy( 10, 0 );
    }

    // move the Applet to the calculated position, remember the old
    // position.
    int oldACy = _moveAC->y();
    _moveAC->move(_moveAC->x(), y);
   
    if (oldACy < _moveAC->y()) { // top to bottom
     
      _applets.findRef(_moveAC);
      AppletContainer *a = _applets.next();
      AppletContainer *b = _moveAC;

      while (a) 
      {
        if (isTopLevel(a)) {
          a = _applets.next();
          continue;
        }
      
        if ( a->y() + a->height() <= _moveAC->y() ) {
          a->move(a->x(), a->y() - _moveAC->height());
          b = a;
          a = _applets.next();
          continue;
        }
        
        int switchMargin = 0;
          
        // calculate the position and height of the 'virtual' container 
        // containing moveAC and a.
        int ty = a->y() - _moveAC->height();
        int theight = _moveAC->height() + a->height();

        // determine the middle of the containers.
        int tmiddle = ty + theight/2;
        int moveACmiddle = _moveAC->y() + _moveAC->height()/2;
        
        // move a from the bmottom of the virtual container to the top
        // side if the middle of moveAC has moved past the middle of the
        // virtual container plus the switchMargin. The switchMargin 
        // prevents rapidly switching when moveAC and a have the same size.
        if (moveACmiddle >= tmiddle + switchMargin) {
          a->move(a->x(), a->y() - _moveAC->height());
          b = a;
          a = _applets.next();
          continue;
        }

        break;
      }

      // visual feedback
      AppletContainer *c = coversAppletContainer(_moveAC, true);
      if (c)
        {
          int cymiddle = c->y() + c->height()/2;
          
          if (_moveAC->y() < cymiddle)
            _dummyWidget->move(QPoint(_moveAC->x(), c->y() - _moveAC->height()));
          else
            _dummyWidget->move(QPoint(_moveAC->x(), c->y() + c->height()));
          
          _dummyWidget->show();
        }
      else
        _dummyWidget->hide();
      
      // Finally move _moveAC to its new position in the applet list.
      if (b != _moveAC) {
        _applets.removeRef(_moveAC);
        _applets.insert( _applets.findRef(b) + 1, _moveAC );
      }
    }
    
    else if (oldACy > _moveAC->y()) { // bottom to top
      
      _applets.findRef(_moveAC);
      AppletContainer *a = _applets.prev();
      AppletContainer *b = _moveAC;

      while (a) 
      {
        if (isTopLevel(a)) {
          a = _applets.prev();
          continue;
        }
      
        if ( _moveAC->y() + _moveAC->height() <= a->y() ) {
          a->move(a->x(), a->y() + _moveAC->height());
          b = a;
          a = _applets.prev();
          continue;
        }
        
        int switchMargin = 0;
          
        // calculate the position and height of the 'virtual' container 
        // containing moveAC and a.
        int ty = a->y();
        int theight = _moveAC->height() + a->height();

        // determine the middle of the containers.
        int tmiddle = ty + theight/2;
        int moveACmiddle = _moveAC->y() + _moveAC->height()/2;

        // move a from the top of the virtual container to the bottom
        // if the middle of moveAC has moved past the middle of the
        // virtual container plus the switchMargin. The switchMargin 
        // prevents rapidly switching when moveAC and a have the same size.
        if (moveACmiddle <= tmiddle + switchMargin) {
          a->move(a->x(), a->y() + _moveAC->height());
          b = a;
          a = _applets.prev();
          continue;
        } 
        
        break;
      }
      
      // visual feedback
      AppletContainer *c = coversAppletContainer(_moveAC, true);
      if (c)
        {
          int cymiddle = c->y() + c->height()/2;
          
          if (_moveAC->y() < cymiddle)
            _dummyWidget->move(QPoint(_moveAC->x(), c->y() - _moveAC->height()));
          else
              _dummyWidget->move(QPoint(_moveAC->x(), c->y() + c->height()));
          
          _dummyWidget->show();
        }
      else
        _dummyWidget->hide();
      
      // Finally move _moveAC to its new position in the applet list.
      if (b != _moveAC) {
        _applets.removeRef(_moveAC);
        _applets.insert(_applets.findRef(b), _moveAC);
      }
    }
  }
}

bool AppletArea::process(const QCString &fun, const QByteArray &data,
	     QCString& replyType, QByteArray &replyData)
{
  if ( fun == "dockRequest(QCString,QCString)" )
    {
      QDataStream dataStream( data, IO_ReadOnly );
      QCString name, id;
      dataStream >> name >> id;
      dockMe( kapp->dcopClient()->senderId(), name );
      return true;
    }
   else if(fun == "position()")
    {
      QDataStream dataStream(replyData, IO_ReadWrite);
      replyType = "int";
      dataStream << static_cast<int>(PGlobal::panel->position());
      return(true);
    }
  else if(fun == "orientation()")
    {
      QDataStream dataStream(replyData, IO_ReadWrite);
      replyType = "int";
      dataStream << static_cast<int>(PGlobal::panel->orientation());
      return(true);
    }
  else if(fun == "updateLayout()")
    {
      layoutChildren();
      return(true);
    }
  return true;
}

void AppletArea::slotLayoutChildren()
{
  layoutChildren();
}

AppletContainer* AppletArea::findApplet( QCString id )
{
  for ( QListIterator<AppletContainer> it(_applets); it.current(); ++it ) {
    if ( it.current()->objId() == id )
      return it.current();
  }
  return 0;
}

void AppletArea::embeddedWindowDestroyed()
{
  if (sender() && sender()->inherits("ExternalFrame"))
    removeApplet((ExternalFrame*)sender());
}

void AppletArea::layoutChildren()
{
  if (_block_relayout) return;
  int pos = 0;

  int occupiedspace = 0;
  int freespace = totalFreeSpace();

  AppletContainer *a;
  for (a = _applets.first(); a != 0; a = _applets.next())
    {
	  if (isTopLevel(a))
		{
		  a->hide();
		  a->move(0,0);
		  continue;
		}

      // get pointer to the next non toplevel applet
	  AppletContainer *next = _applets.next();
	  
	  while(next && isTopLevel(next))
		next = _applets.next();

	  // reset current
	  _applets.find(a);
      
      double fspace = a->freeSpace()*freespace;
      if ((fspace - floor(fspace)) > 0.5)
        fspace += 1;
      pos = static_cast<int>(fspace) + occupiedspace;

      if (orientation() == Horizontal)
        {
          a->move( pos, 0 );
          int w = a->widthForHeight(height());
          if (isStretch(a))
            {
              if (next)
                a->resize(w + (next->freeSpace() - a->freeSpace())*freespace, 
                  height());
              else
                a->resize(realWidth() - a->x(), height());
            }
          else
            a->resize(w, height());
          occupiedspace += w;
        }
      else
        {
          a->move( 0, pos );
          int h = a->heightForWidth(width());
		  if (isStretch(a))
            {
              if (next)
                a->resize(width(), 
                  h + (next->freeSpace() - a->freeSpace())*freespace);
              else
                a->resize(width(), realHeight() - a->y());
            }
          else
            a->resize(width(), h);
          occupiedspace += h;
        }
    }

  resizeContents(viewport()->childrenRect().width(),
                 viewport()->childrenRect().height());
}

void AppletArea::dockMe( QCString application, QCString applet )
{
  ExternalFrame* frame = new ExternalFrame( application, applet, viewport() );
  addApplet(frame);
  layoutChildren();
  moveToFirstFreePosition(frame);
  saveAppletConfig();
}

void AppletArea::dragEnterEvent(QDragEnterEvent *ev)
{
  ev->accept(QUriDrag::canDecode(ev));
}

void AppletArea::dropEvent(QDropEvent *ev)
{
  QStrList fileList;
  
  if(QUriDrag::decode(ev, fileList)){
    QStrListIterator it(fileList);
    for(;it.current(); ++it)
      {
        warning("In dropEvent for %s", it.current());
        AppletContainer* a;
        KURL url(it.current());

        // see if it's a executable or directory
        if(url.isLocalFile() && !KDesktopFile::isDesktopFile(url.path()))
          {
            QFileInfo fi(url.path());
            if(fi.isDir())  // directory
              {
                PanelDirDropMenu mnu;
                switch(mnu.exec(mapToGlobal(ev->pos()))){
                case PanelDirDropMenu::Browser:
                  a = new BrowserButtonContainer(url.path(), viewport());
                  break;
                case PanelDirDropMenu::Url:
                default:
                  a = new URLButtonContainer(it.current(), viewport());
                  break;
                }
              }
            else if(fi.isExecutable())  // non-KDE executable
              {
                warning("Dropped exe");
                QString pixmapFile;
                KMimeType::pixmapForURL(url.path(), 0, KIcon::Desktop, 0,
                                        KIcon::DefaultState, &pixmapFile);
                PanelExeDialog dlg(url.path(), pixmapFile,
                                   QString::null, false, viewport());
                if(dlg.exec() == QDialog::Accepted){
                  // KIconloader returns a full path, we only want name
                  QFileInfo iconfi(dlg.icon());
                  a = new ExeButtonContainer(url.path(), iconfi.fileName(),
                                             dlg.commandLine(),
                                             dlg.useTerminal(), viewport());
                }
                else
                  break;
            }
            else // some unknown local file
              a = new URLButtonContainer(it.current(), viewport());
          }
        else // a internet URL or desktop file.
          a= new URLButtonContainer(it.current(), viewport());
        addApplet(a);
        layoutChildren();
        moveToFirstFreePosition(a);
        saveAppletConfig();
      }
  }
}

void AppletArea::moveToFirstFreePosition(AppletContainer* a)
{
  if (isTopLevel(a)) return;

  int w = a->widthForHeight(height());
  int h = a->heightForWidth(width());

  Orientation orient = orientation();

  QListIterator<AppletContainer> it(_applets);
  for(; it.current() ; ++it)
	{
	  AppletContainer* b = static_cast<AppletContainer*>(it.current());
	  if(isTopLevel(b)) continue;

      int space = relativeAppletContainerPos(b);

      if (orient == Horizontal) {
        if (space >= w) {
          a->move(b->pos().x() - space, a->y());
          break;
        }
      }
      else {
        if (space >= h) {
          a->move(a->x(), b->pos().y() - space);
          break;
        }
      }
    }
  updateAppletContainerList();
  layoutChildren();
}

AppletContainer* AppletArea::coversAppletContainer(AppletContainer *a, bool strict)
{
  if (isTopLevel(a)) return 0;

  AppletContainer *b;
  QListIterator<AppletContainer> it(_applets);

  for(; it.current() ; ++it)
	{
	  b = (AppletContainer*)it.current();
	  if (isTopLevel(b)) continue;

	  if (b == a) continue;

	  if ( orientation() == Horizontal ) {
		int bl, br;
		if (strict) {
		  bl = b->x();
		  br = b->x() + b->width();
		}
		else {
		  bl = b->x() + 10;
		  br = b->x() + b->width() - 10;
		}

		int btnl = a->x();
		int btnr = btnl + a->width();

		if ((btnl >= bl) && (btnl <= br))
		  return b;
		else if ((btnr >= bl) && (btnr <= br))
		  return b;
	  }
	  else {
		int bt, bb;
		if (strict) {
		  bt = b->y();
		  bb = b->y() + b->height();
		}
		else {
		  bt = b->y() + 10;
		  bb = b->y() + b->height() - 10;
		}
		int btnt = a->y();
		int btnb = btnt + a->height();

		if ((btnt >= bt) && (btnt <= bb))
		  return b;
		else if ((btnb >= bt) && (btnb <= bb))
		  return b;
	  }
	}
  return 0;
}

void AppletArea::updateAppletContainerList()
{
  QList<AppletContainer> sorted;
  
  while(!_applets.isEmpty())
	{
	  AppletContainer *b = 0;
	  int pos = 9999;
      
	  QListIterator<AppletContainer> it(_applets);
      
	  for(; it.current() ; ++it)
		{
		  AppletContainer* a = static_cast<AppletContainer*>(it.current());

		  if (isTopLevel(a))
			{
			  b = a;
			}
		  else if(orientation() == Horizontal)
			{
			  if (a->x() < pos) {
                b = a;
                pos = a->x();
              }
			}
		  else
			{
			  if (a->y() < pos) {
                b = a;
                pos = a->y();
              }
			}
		}
      
	  if (b) {
        sorted.append(b);
        _applets.remove(b);
      }
	}
  _applets = sorted;
  
  float freespace = totalFreeSpace();
  float fspace = 0;

  QListIterator<AppletContainer> it(_applets);
  for(; it.current() ; ++it)
    {
      fspace += relativeAppletContainerPos(it.current());
      if (fspace < 0) fspace = 0;
      it.current()->setFreeSpace(fspace/freespace);
    }
}

int AppletArea::totalFreeSpace()
{
  int availablespace;
  int usedspace = 0;

  if(orientation() == Horizontal)
    availablespace = width();
  else
    availablespace = height();

  // calculate used space
  QListIterator<AppletContainer> it(_applets);
  for(; it.current() ; ++it)
	{
	  AppletContainer* a = static_cast<AppletContainer*>(it.current());

      if (isTopLevel(a))
        continue;

      int space;
      if(orientation() == Horizontal)
        space = a->widthForHeight(height());
      else
        space = a->heightForWidth(width());

      if (space > 0)
        usedspace += space;
    }

  int freespace = availablespace - usedspace;
  if (freespace < 0) freespace = 0;

  return freespace;
}

int AppletArea::relativeAppletContainerPos(AppletContainer* b)
{
  if (!b) return 0;
  if (!_applets.contains(b)) return 0;
  if (isTopLevel(b)) return 0;
  
  uint pos = 0;
  
  QListIterator<AppletContainer> it(_applets);
  for(; it.current() ; ++it)
	{
	  AppletContainer* a = static_cast<AppletContainer*>(it.current());

	  if (isTopLevel(a)) continue;

	  if(orientation() == Horizontal)
		{
		  if (a == b)
            {
              int p = b->x() - pos;
              if (b < 0) return 0;
              return p;
            }
		  else
			pos = a->x() + a->widthForHeight(height());
		}
	  else
		{
		  if (a == b)
            {
              int p = b->y() - pos;
              if (b < 0) return 0;
              return p;
            }
		  else
			pos = a->y() + a->heightForWidth(width());
		}
	}
  return 0;
}

void AppletArea::setupDummyWidget(AppletContainer* a)
{
  if (!a) return;

  // resize to match the AppletConatiner's size
  _dummyWidget->resize(a->size());

  // draw the AppletConatiner into a pixmap
  KPixmap pix = QPixmap::grabWidget(a);
  
  // decrease pixmap intensity to create a poor shadow effect
  pix = KPixmapEffect::intensity(pix, -0.4);

  // set the dummy widgets background to our pixmap
  _dummyWidget->setBackgroundPixmap(pix);
}
