/* This file is part of the KDE project
   Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/


#include <assert.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <kcolordrag.h>

#include <qkeycode.h>
#include <qpalette.h>
#include <qdragobject.h>
#include <qclipboard.h>
#include <qevent.h>
#include <qrect.h>
#include <qdir.h>

#include <dcopclient.h>
#include <kapp.h>
#include <kcursor.h>
#include <kdebug.h>
#include <konq_dirlister.h>
#include <kstddirs.h>
#include <kfileivi.h>
#include <kimageio.h>
#include <kipc.h>
#include <klocale.h>
#include <kio/job.h>
#include <kio/netaccess.h>
#include <kio/paste.h>
#include <knewmenu.h>
#include <konq_defaults.h>
#include <konq_drag.h>
#include <konq_popupmenu.h>
#include <konq_settings.h>
#include <konq_operations.h>
#include <konq_undo.h>
#include <kprocess.h>
#include <kstdaccel.h>
#include <ksycoca.h>
#include <ktempfile.h>
#include <kurl.h>
#include <kurldrag.h>
#include <kmessagebox.h>
#include <kglobalaccel.h>
#include <kwinmodule.h>
#include <krun.h>
#include <ksimpleconfig.h>
#include <kwin.h>
#include <kstdaction.h>
#include <kglobalsettings.h>
#include <kpopupmenu.h>

// root window hack
#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
// ----

#include "desktop.h"
#include "krootwm.h"
#include "bgmanager.h"
#include "bgsettings.h"

// -----------------------------------------------------------------------------
#define DEFAULT_DELETEACTION 1


// for multihead - from main.cc
extern int kdesktop_screen_number;


KDesktop::KDesktop( bool x_root_hack, bool auto_start, bool wait_for_kded ) :
    KonqIconViewWidget( 0L, "desktop", WResizeNoErase | (x_root_hack ? (WStyle_Customize | WStyle_NoBorder) : 0), TRUE ),
    // those two WStyle_ break kdesktop when the root-hack isn't used (no Dnd)

    DCOPObject( "KDesktopIface" )
{

  m_bAutoStart = auto_start;
  m_bWaitForKded = wait_for_kded;
  KGlobal::locale()->insertCatalogue("kdesktop");
  KGlobal::locale()->insertCatalogue("libkonq"); // needed for apps using libkonq
  m_language = KGlobal::locale()->language();

  setCaption( "KDE Desktop");
  KWin::setType( winId(), NET::Desktop );


  m_lastIcon = 0;
  m_hasExistingPos = false;
  setFrameStyle(NoFrame);
  setAcceptDrops(true); // WStyle_Customize seems to disable that
  // we don't want a scrolling desktop
  setVScrollBarMode( AlwaysOff );
  setHScrollBarMode( AlwaysOff );
  setDragAutoScroll( false );
  viewport()->setBackgroundMode( NoBackground );
  m_pKwinmodule = new KWinModule( this );
  updateWorkAreaTimer = new QTimer( this );
  connect( updateWorkAreaTimer, SIGNAL( timeout() ),
           this, SLOT( updateWorkArea() ) );
  connect( m_pKwinmodule, SIGNAL( workAreaChanged() ),
           this, SLOT( workAreaChanged() ) );
  connect( QApplication::clipboard(), SIGNAL(dataChanged()),
           this, SLOT(slotClipboardDataChanged()) );

  setURL( desktopURL() ); // sets m_url and m_dotDirectoryPath
  assert( !m_url.isMalformed() );

  // Dont repaint on configuration changes during construction
  m_bInit = true;

  setFocusPolicy( StrongFocus );
  viewport()->setFocusPolicy( StrongFocus );

  if ( x_root_hack )
  {
    // this is a ugly hack to make Dnd work
    // Matthias told me that it won't be necessary with kwin
    // actually my first try with ICCCM (Dirk) :-)
    unsigned long data[2];
    data[0] = (unsigned long) 1;
    data[1] = (unsigned long) 0; // None; (Werner)
    Atom wm_state = XInternAtom(qt_xdisplay(), "WM_STATE", False);
    XChangeProperty(qt_xdisplay(), winId(), wm_state, wm_state, 32,
                    PropModeReplace, (unsigned char *)data, 2);

  }

  setGeometry( QApplication::desktop()->geometry() );

  // Geert Jansen: backgroundmanager belongs here
  bgMgr = new KBackgroundManager(this, m_pKwinmodule );
  connect( bgMgr, SIGNAL( initDone()), SLOT( backgroundInitDone()));

  lower();

  connect( this, SIGNAL( executed( QIconViewItem * ) ),
           SLOT( slotReturnPressed( QIconViewItem * ) ) );
  connect( this, SIGNAL( returnPressed( QIconViewItem * ) ),
           SLOT( slotReturnPressed( QIconViewItem * ) ) );
  connect( this, SIGNAL( mouseButtonPressed(int, QIconViewItem*, const QPoint&)),
           SLOT( slotMouseButtonPressed(int, QIconViewItem*, const QPoint&)) );
  connect( this, SIGNAL( mouseButtonClicked(int, QIconViewItem*, const QPoint&)),
           SLOT( slotMouseButtonClickedKDesktop(int, QIconViewItem*, const QPoint&)) );

  connect( this, SIGNAL( enableAction( const char * , bool ) ),
         SLOT( slotEnableAction( const char * , bool ) ) );
  connect( this, SIGNAL(itemRenamed(QIconViewItem*)),
           SLOT( slotItemRenamed(QIconViewItem*)) );
  connect( this, SIGNAL( dropped( QDropEvent *, const QValueList<QIconDragItem> & ) ),
           this, SLOT( slotSaveDropPosition( QDropEvent *, const QValueList<QIconDragItem> & ) ) );

  connect(KSycoca::self(), SIGNAL(databaseChanged()), this, SLOT(slotDatabaseChanged()));

  QTimer::singleShot(0, this, SLOT( slotStart()));

  m_dotDirectory = 0;

  connect( kapp, SIGNAL( shutDown() ),
           this, SLOT( slotShutdown() ) );

  connect(kapp, SIGNAL(settingsChanged(int)),
          this, SLOT(slotSettingsChanged(int)));
  kapp->addKipcEventMask(KIPC::SettingsChanged);

  kdDebug() << "KDesktop constructor -> workAreaChanged" << endl;
  workAreaChanged();
}

void
KDesktop::backgroundInitDone()
{
  //kdDebug() << "KDesktop::backgroundInitDone" << endl;
  show();
  const QPixmap *bg = backgroundPixmap();
  if ( bg )
  {
     // Set the root window background now too.
     // This is entirely so that when the window manager starts
     // background flashing is kept to a minimum.
     kapp->desktop()->setBackgroundPixmap( *bg );
  }
}

void
KDesktop::slotStart()
{
  kdDebug() << "KDesktop::slotStart" << endl;
  if (!m_bInit) return;

  kapp->dcopClient()->send( "ksplash", "", "upAndRunning(QString)", QString("kdesktop"));

  // In case we started without database
  KImageIO::registerFormats();

  initConfig();

  // We need to be visible in order to insert icons, even if the background isn't ready yet...
  show();

  // Now we may react to configuration changes
  m_bInit = false;

  // Create the directory lister
  m_dirLister = new KonqDirLister();

  connect( m_dirLister, SIGNAL( clear() ), this, SLOT( slotClear() ) );
  connect( m_dirLister, SIGNAL( started(const QString&) ), this, SLOT( slotStarted() ) );
  connect( m_dirLister, SIGNAL( completed() ), this, SLOT( slotCompleted() ) );
  connect( m_dirLister, SIGNAL( newItems( const KFileItemList & ) ),
           this, SLOT( slotNewItems( const KFileItemList & ) ) );
  connect( m_dirLister, SIGNAL( deleteItem( KFileItem * ) ),
           this, SLOT( slotDeleteItem( KFileItem * ) ) );
  connect( m_dirLister, SIGNAL( refreshItems( const KFileItemList & ) ),
           this, SLOT( slotRefreshItems( const KFileItemList & ) ) );

  // Start the directory lister !
  m_dirLister->openURL( m_url, m_bShowDot );

  setResizeMode( Fixed );

  // Global keys
  keys = new KGlobalAccel;
#include "kdesktopbindings.cpp"
  keys->connectItem("Execute command", this, SLOT(slotExecuteCommand()));
  keys->connectItem("Show taskmanager", this, SLOT(slotShowTaskManager()));
  keys->connectItem("Show window list", this, SLOT(slotShowWindowList()));

  KAction *undo = KStdAction::undo( KonqUndoManager::self(), SLOT( undo() ), &m_actionCollection, "undo" );
  connect( KonqUndoManager::self(), SIGNAL( undoAvailable( bool ) ),
           undo, SLOT( setEnabled( bool ) ) );
  connect( KonqUndoManager::self(), SIGNAL( undoTextChanged( const QString & ) ),
           undo, SLOT( setText( const QString & ) ) );
  undo->setEnabled( KonqUndoManager::self()->undoAvailable() );

  KStdAction::cut( this, SLOT( slotCut() ), &m_actionCollection, "cut" );
  KStdAction::copy( this, SLOT( slotCopy() ), &m_actionCollection, "copy" );
  KStdAction::paste( this, SLOT( slotPaste() ), &m_actionCollection, "paste" );

  (void) new KAction( i18n( "&Rename" ), /*"editrename",*/ Key_F2, this, SLOT( renameSelectedItem() ), &m_actionCollection, "rename" );
  (void) new KAction( i18n( "&Move to Trash" ), "edittrash", Key_Delete, this, SLOT( slotTrash() ), &m_actionCollection, "trash" );
  (void) new KAction( i18n( "&Delete" ), "editdelete", SHIFT+Key_Delete, this, SLOT( slotDelete() ), &m_actionCollection, "del" );

  (void) new KAction( i18n( "&Shred" ), "editshred", CTRL+SHIFT+Key_Delete, this, SLOT( slotShred() ), &m_actionCollection, "shred" );

  // Make the accels for all those actions available, through KAccel.
  m_accel = new KAccel( this );
  int count = actionCollection()->count();
  for ( int i = 0; i < count; i++ )
  {
    KAction *act = actionCollection()->action( i );
    if ( act->accel() )
      act->plugAccel( m_accel );
  }

  new KRootWm( this ); // handler for root menu (used by kdesktop on RMB click)

  keys->readSettings();

  if ( m_bAutoStart )
  {
     // now let's execute all the stuff in the autostart folder.
     // the stuff will actually be really executed when the event loop is
     // entered, since KRun internally uses a QTimer
     QDir dir( KGlobalSettings::autostartPath() );
     QStringList entries = dir.entryList( QDir::Files );
     QStringList::Iterator it = entries.begin();
     QStringList::Iterator end = entries.end();
     for (; it != end; ++it )
     {
            // Don't execute backup files
            if ( (*it).right(1) != "~" && (*it).right(4) != ".bak" &&
                 ( (*it)[0] != '%' || (*it).right(1) != "%" ) &&
                 ( (*it)[0] != '#' || (*it).right(1) != "#" ) )
            {
                KURL url;
                url.setPath( dir.absPath() + '/' + (*it) );
                (void) new KRun( url, 0, true );
            }
     }
   }

  connect(kapp, SIGNAL(appearanceChanged()), SLOT(slotConfigure()));

}

// -----------------------------------------------------------------------------

KDesktop::~KDesktop()
{
  delete m_dirLister;
  delete bgMgr;
  delete keys;
}

// -----------------------------------------------------------------------------

void KDesktop::initConfig()
{
    KConfig * config = KGlobal::config();
    config->setGroup( "Desktop Icons" );
    m_bShowDot = config->readBoolEntry("ShowHidden", DEFAULT_SHOW_HIDDEN_ROOT_ICONS);
    m_bVertAlign = config->readBoolEntry("VertAlign", DEFAULT_VERT_ALIGN);
    QStringList oldPreview = m_preview;
    m_preview = config->readListEntry( "Preview" );

    if ( !m_bInit ) { // only when called while running - not on first startup
        m_dirLister->setShowingDotFiles( m_bShowDot );
        keys->readSettings();
    }
    m_tAlign = m_bVertAlign ? TopToBottom : LeftToRight;
    setArrangement(m_tAlign);

    KonqIconViewWidget::initConfig( m_bInit );

    setAutoArrange( false );

    if ( m_preview.count() )
    {
        for ( QStringList::ConstIterator it = oldPreview.begin(); it != oldPreview.end(); ++it)
            if ( !m_preview.contains( *it ) ){
                kdDebug() << "Disabling preview for " << *it << endl;
                setIcons( iconSize(), (*it).latin1() /* revert no-longer wanted previews to icons */ );
}        startImagePreview( m_preview, true );
    }
    else
    {
        stopImagePreview();
        setIcons( iconSize(), "" /* stopImagePreview */ );
    }
}

// -----------------------------------------------------------------------------

void KDesktop::slotExecuteCommand()
{
    // this function needs to be duplicated since it appears that one
    // cannot have a 'slot' be a DCOP method.  if this changes in the
    // future, then 'slotExecuteCommand' and 'popupExecuteCommand' can
    // merge into one slot.
    popupExecuteCommand();
}

// -----------------------------------------------------------------------------

void KDesktop::popupExecuteCommand()
{
    if (m_bInit) return;
    KRootWm::getRootWm()->slotExec();
}

// -----------------------------------------------------------------------------

void KDesktop::slotShowWindowList()
{
     KWin::setActiveWindow( winId() );
     KRootWm::getRootWm()->slotWindowList();
}

void KDesktop::slotShowTaskManager()
{
	kdDebug() << "Launching KSysGuard..." << endl;
	KProcess* p = new KProcess;
	CHECK_PTR(p);

	*p << "ksysguard";
	*p << "--showprocesses";

	p->start(KProcess::DontCare);

	delete p;
}

void KDesktop::rearrangeIcons( bool bAsk )
{
  if ( bAsk )
    if ( KMessageBox::questionYesNo( 0L,
         i18n( "Do you really want to rearrange your icons?" )) == KMessageBox::No )
      return;

  arrangeItemsInGrid();
  slotSaveIconPositions();
}


void KDesktop::rearrangeIcons()
{
  rearrangeIcons( false );
}


// -----------------------------------------------------------------------------

void KDesktop::lineupIcons()
{
  KonqIconViewWidget::lineupIcons();
  slotSaveIconPositions();
}

// -----------------------------------------------------------------------------

QStringList KDesktop::selectedURLs()
{
    QStringList seq;

    QIconViewItem *it = firstItem();
    for (; it; it = it->nextItem() )
        if ( it->isSelected() ) {
            KFileItem *fItem = ((KFileIVI *)it)->item();
            seq.append( fItem->url().url() ); // copy the URL
        }

    return seq;
}

// -----------------------------------------------------------------------------

void KDesktop::slotConfigure()
{
    configure();
}

void KDesktop::configure()
{
    // re-read configuration and apply it
    KGlobal::config()->reparseConfiguration();
    KonqFMSettings::reparseConfiguration();

    if (!m_bInit)
    {
       initConfig();
       KRootWm::getRootWm()->initConfig();
       updateContents();
    }

    keys->readSettings();
}

void KDesktop::slotSettingsChanged(int category)
{
    kdDebug() << "KDesktop::slotSettingsChanged" << endl;
    if (category == KApplication::SETTINGS_PATHS)
    {
        // Did someone change the path to the desktop ?
        kdDebug() << "KDesktop::slotSettingsChanged SETTINGS_PATHS" << endl;
        kdDebug() << desktopURL().url() << endl;
        kdDebug() << m_url.url() << endl;
        if ( desktopURL() != m_url )
        {
            kdDebug(1204) << "Desktop path changed from " << m_url.url() <<
                " to " << desktopURL().url() << endl;
            setURL( desktopURL() ); // sets m_url and m_dotDirectoryPath
            delete m_dotDirectory;
            m_dotDirectory = 0L;
            m_dirLister->openURL( m_url, m_bShowDot );
        }
    }
}

KURL KDesktop::desktopURL()
{
    // Support both paths and URLs
    QString desktopPath = KGlobalSettings::desktopPath();
    QString dn = "Desktop";
    if (kdesktop_screen_number != 0) {
	QString dn = "Desktop";
	dn += QString::number(kdesktop_screen_number);
	desktopPath.replace(QRegExp("Desktop"), dn);
    }

    KURL desktopURL;
    if (desktopPath[0] == '/')
        desktopURL.setPath(desktopPath);
    else
        desktopURL = desktopPath;
    return desktopURL;
}

// -----------------------------------------------------------------------------
void KDesktop::slotDatabaseChanged()
{
  //kdDebug() << "KDesktop::slotDatabaseChanged" << endl;
  if (m_bInit)
     slotStart();
  refreshMimeTypes();
}

// -----------------------------------------------------------------------------

// This is only emited when you click on the scrollview, not the
// viewport. We only need that if you have docks on the desktop which don't
// stretch over a whole height or width, like the kasbar was. Else you will
// never be able to click on the scrollview itself but only in the viewport.
void KDesktop::mousePressEvent( QMouseEvent *e )
{
    kdDebug() << "KDesktop::mousePressEvent" << endl;
    slotMouseButtonPressed( e->button(), 0, e->globalPos() );
}

void KDesktop::contentsMousePressEvent( QMouseEvent *e )
{
    if (m_bInit) return;
    //kdDebug() << "KDesktop::contentsMousePressEvent" << endl;
    // QIconView, as of Qt 2.2, doesn't emit mouseButtonPressed for LMB on background
    if ( e->button() == LeftButton && KRootWm::getRootWm()->hasLeftButtonMenu() )
    {
        QIconViewItem *item = findItem( e->pos() );
        if ( !item )
        {
            // Left click menu
            KRootWm::getRootWm()->mousePressed( e->globalPos(), e->button() );
            return;
        }
    }
    KonqIconViewWidget::contentsMousePressEvent( e );
}

void KDesktop::slotMouseButtonPressed(int _button, QIconViewItem* _item, const QPoint& _global)
{
    //kdDebug() << "KDesktop::slotMouseButtonPressed" << endl;
    if (m_bInit) return;
    m_lastIcon = 0L; // user action -> not renaming an icon
    if(_item) {
        if ( _button == RightButton )
        {
            ((KFileIVI*)_item)->setSelected( true );
            popupMenu( _global, selectedFileItems() );
        }
    }
    else
        KRootWm::getRootWm()->mousePressed( _global, _button );
}

void KDesktop::slotMouseButtonClickedKDesktop(int _button, QIconViewItem* _item, const QPoint&)
{
    if (m_bInit) return;
    //kdDebug() << "KDesktop::slotMouseButtonClickedKDesktop" << endl;
    if ( _item && _button == MidButton )
        slotReturnPressed( _item );
}

// -----------------------------------------------------------------------------

void KDesktop::slotReturnPressed( QIconViewItem *item )
{
  kapp->propagateSessionManager();
  m_lastIcon = 0L; // user action -> not renaming an icon
  if (item) {
    visualActivate(item);
    ((KFileIVI*)item)->returnPressed();
  }
}

// -----------------------------------------------------------------------------

void KDesktop::slotCut()
{
  cutSelection();
}

// -----------------------------------------------------------------------------

void KDesktop::slotCopy()
{
  copySelection();
}

// -----------------------------------------------------------------------------

void KDesktop::slotPaste()
{
  pasteSelection();
}

void KDesktop::slotTrash()
{
  KonqOperations::del(this, KonqOperations::TRASH, selectedUrls());
}

void KDesktop::slotDelete()
{
  KonqOperations::del(this, KonqOperations::DEL, selectedUrls());
}

void KDesktop::slotShred()
{
  KonqOperations::del(this, KonqOperations::SHRED, selectedUrls());
}

// -----------------------------------------------------------------------------

void KDesktop::popupMenu( const QPoint &_global, KFileItemList _items )
{
   if (m_bInit) return;
   KonqPopupMenu * popupMenu = new KonqPopupMenu( _items,
                                                  m_url,
                                                  m_actionCollection,
                                                  KRootWm::getRootWm()->newMenu() );

   popupMenu->exec( _global );
   delete popupMenu;
}

// -----------------------------------------------------------------------------

void KDesktop::slotEnableAction( const char * name, bool enabled )
{
  QCString sName( name );
  // No such actions here... konqpopupmenu provides them.
  if ( sName == "properties" || sName == "editMimeType" )
    return;

  KAction * act = m_actionCollection.action( sName.data() );
  if (!act)
    kdWarning(1203) << "Unknown action " << sName.data() << " - can't enable" << endl;
  else
    act->setEnabled( enabled );
}

// -----------------------------------------------------------------------------

QString KDesktop::makeFriendlyText( const QString & text )
{
    if (text.right(7) == QString::fromLatin1(".kdelnk"))
      return text.left(text.length() - 7);
    else if (text.right(8) == QString::fromLatin1(".desktop"))
      return text.left(text.length() - 8);
    return text;
}

// -----------------------------------------------------------------------------

void KDesktop::slotClear()
{
    clear();
}

// -----------------------------------------------------------------------------

void KDesktop::slotNewItems( const KFileItemList & entries )
{
  // We have new items, so we'll need to repaint in slotCompleted
  m_bNeedRepaint = true;
  //kdDebug() << "KDesktop::slotNewItems count=" << entries.count() << endl;
  KFileItemListIterator it(entries);
  KFileIVI* fileIVI = 0L;
  for (; it.current(); ++it)
  {
    fileIVI = new KFileIVI( this, static_cast<KonqFileItem *>(it.current()),
                            iconSize() );
    //kdDebug() << fileIVI->text() << endl;
    fileIVI->setText( makeFriendlyText( fileIVI->text() ) );
    fileIVI->setRenameEnabled( false );

    if ( m_dotDirectory )
    {
      QString group = m_iconPositionGroupPrefix;
      group.append( it.current()->url().fileName() );
      //kdDebug() << "slotNewItems : looking for group " << group << endl;
      if ( m_dotDirectory->hasGroup( group ) )
      {
        m_dotDirectory->setGroup( group );
        m_hasExistingPos = true;
        int x = m_dotDirectory->readNumEntry( "X" );
        int y = m_dotDirectory->readNumEntry( "Y" );
        fileIVI->move( x, y );
      }
      else // Not found, we'll need to save the new pos
          m_bNeedSave = true;
    }
  }
  if (fileIVI)
    m_lastIcon = fileIVI;
}

// -----------------------------------------------------------------------------

// see also KonqKfmIconView::slotRefreshItems
void KDesktop::slotRefreshItems( const KFileItemList & entries )
{
    kdDebug() << "KDesktop::slotRefreshItems" << endl;
    KFileItemListIterator rit(entries);
    for (; rit.current(); ++rit)
    {
        QIconViewItem *it = firstItem();
        for ( ; it ; it = it->nextItem() )
        {
            KFileIVI * fileIVI = static_cast<KFileIVI *>(it);
            if ( fileIVI->item() == rit.current() ) // compare the pointers
            {
                kdDebug() << "KDesktop::slotRefreshItems refreshing icon " << fileIVI->item()->url().url() << endl;
                fileIVI->refreshIcon( true );
                fileIVI->setText( makeFriendlyText( rit.current()->text() ) );
                break;
            }
        }
    }
    // In case we replace a big icon with a small one, need to repaint.
    updateContents();
    // Can't do that with m_bNeedRepaint since slotCompleted isn't called
}


void KDesktop::refreshIcons()
{
    QIconViewItem *it = firstItem();
    for ( ; it ; it = it->nextItem() )
    {
        KFileIVI * fileIVI = static_cast<KFileIVI *>(it);
        fileIVI->refreshIcon( true );
        fileIVI->setText( makeFriendlyText( fileIVI->item()->text() ) );
    }
}

// -----------------------------------------------------------------------------

void KDesktop::slotDeleteItem( KFileItem * _fileitem )
{
//    kdDebug() << "KDesktop::slotDeleteItems" << endl;
    // we need to find out the KFileIVI containing the fileitem
    QIconViewItem *it = firstItem();
    while ( it ) {
      KFileIVI * fileIVI = static_cast<KFileIVI *>(it);
      if ( fileIVI->item() == _fileitem ) { // compare the pointers
        // Delete this item.
        //kdDebug() << fileIVI->text() << endl;
        // It may be that it has been renamed. In this case,
        // m_lastIcon should be moved to this icon's position.
        // (We rely on newItems being emitted before deleteItem)
        if ( m_lastIcon )
          // Problem is: I'd like to compare those two file's attributes
          // (size, creation time, modification time... etc.) but since renaming
          // is done by kpropsdlg, all of those can have changed (and creation time
          // is different since the new file is a copy!)
        {
          kdDebug() << "moving " << m_lastIcon->text() << endl;
          m_lastIcon->move( fileIVI->x(), fileIVI->y() );
          m_lastIcon = 0L;
        }

        if ( m_dotDirectory )
        {
            QString group = m_iconPositionGroupPrefix;
            group.append( fileIVI->item()->url().fileName() );
            if ( m_dotDirectory->hasGroup( group ) )
                m_dotDirectory->deleteGroup( group );
        }

        delete fileIVI;
        break;
      }
      it = it->nextItem();
    }
    m_bNeedRepaint = true;
}

// -----------------------------------------------------------------------------

void KDesktop::slotStarted()
{
    kdDebug() << "KDesktop::slotStarted" << endl;
    m_dotDirectory = new KSimpleConfig( m_dotDirectoryPath, true );
    m_bNeedSave = false;
    m_bNeedRepaint = false;
}

void KDesktop::slotCompleted()
{
    // Root item ? Store in konqiconviewwidget
    if ( m_dirLister->rootItem() )
      setRootItem( static_cast<KonqFileItem *>(m_dirLister->rootItem()) );

    if ( m_dotDirectory )
    {
      delete m_dotDirectory;
      m_dotDirectory = 0;
    }

    if ( m_preview.count() )
        startImagePreview( m_preview, true );
    else
    {
        stopImagePreview();
        setIcons( iconSize(), "" /* stopImagePreview */ );
    }

    kdDebug() << "KDesktop::slotCompleted " << m_bNeedSave << " " << m_bNeedRepaint << endl;
    if ( m_bNeedSave )
        slotSaveIconPositions();
    if ( m_bNeedRepaint )
        viewport()->repaint();
}

void KDesktop::slotClipboardDataChanged()
{
    KURL::List lst;
    QMimeSource *data = QApplication::clipboard()->data();
    if ( data->provides( "application/x-kde-cutselection" ) && data->provides( "text/uri-list" ) )
        if ( KonqDrag::decodeIsCutSelection( data ) )
            (void) KURLDrag::decode( data, lst );

    disableIcons( lst );
}

// -----------------------------------------------------------------------------

void KDesktop::slotItemRenamed(QIconViewItem* /*_item*/)
{
    kdDebug() << "KDesktop::slotItemRenamed()" << endl;
}

void KDesktop::slotSaveDropPosition( QDropEvent *ev, const QValueList<QIconDragItem> & )
{
    m_lastIcon = 0L;
    if (m_bInit) return; // too early
    if (m_dotDirectory) return; // we are listing the dir...
    m_dotDirectory = new KSimpleConfig( m_dotDirectoryPath );
    if (ev->provides( "text/uri-list" ))
    {
        KURL::List lst;
        if ( KURLDrag::decode( ev, lst ) ) // Are they urls ?
        {
            // For now, deal with only one icon
            // TODO: if we can decode as application/x-qiconlist then we
            // can even store the position of each icon (to keep their relative position)
            if ( lst.count() == 1 )
            {
                KURL u = lst.first();
                kdDebug() << "Saving drop position for " << u.fileName() << " at " << ev->pos().x() << "," << ev->pos().y() << endl;
                m_dotDirectory->setGroup( QString( m_iconPositionGroupPrefix ).append( u.fileName() ) );
                m_dotDirectory->writeEntry( "X", ev->pos().x() );
                m_dotDirectory->writeEntry( "Y", ev->pos().y() );
            }
        }
    }
    m_dotDirectory->sync();
    delete m_dotDirectory;
    m_dotDirectory = 0L;
}


void KDesktop::setVRoot()
{
    if (KWin::info(winId()).mappingState == NET::Withdrawn) {
        QTimer::singleShot(100, this, SLOT(setVRoot()));
        return;
    }

    unsigned long rw = RootWindowOfScreen(ScreenOfDisplay(qt_xdisplay(), qt_xscreen()));
    unsigned long vroot_data[1] = { viewport()->winId() };
    Atom vroot = XInternAtom(qt_xdisplay(), "__SWM_VROOT", False);

    Window rootReturn, parentReturn, *children;
    unsigned int numChildren;
    Window top = winId();
    while (1) {
        /*int ret = */XQueryTree(qt_xdisplay(), top , &rootReturn, &parentReturn,
                                 &children, &numChildren);
        if (children)
            XFree((char *)children);
        if (parentReturn == rw) {
            break;
        } else
            top = parentReturn;
    }
    XChangeProperty(qt_xdisplay(), top, vroot, XA_WINDOW, 32,
                    PropModeReplace, (unsigned char *)vroot_data, 1);
}

// -----------------------------------------------------------------------------

void KDesktop::showEvent( QShowEvent *e )
{
    QTimer::singleShot(100, this, SLOT(setVRoot()));

    //HACK to avoid QIconView calling arrangeItemsInGrid (Simon)
    //EVEN MORE HACK: unfortunately, QScrollView has no concept of
    //TopToBottom, therefore, it always adds LeftToRight.  So, if any of
    //the icons have a setting, we'll use QScrollView.. but otherwise,
    //we use the iconview
    if (m_hasExistingPos)
        QScrollView::showEvent( e );
    else
        KIconView::showEvent( e );
}

// -----------------------------------------------------------------------------

void KDesktop::slotShutdown()
{
    slotSaveIconPositions();
    KRootWm::getRootWm()->saveConfig();
}

// don't hide when someone presses Alt-F4 on us
void KDesktop::closeEvent(QCloseEvent *e)
{
    e->ignore();
}

// don't scroll when someone uses his nifty mouse wheel
void KDesktop::viewportWheelEvent( QWheelEvent * e )
{
    e->ignore();
}

void KDesktop::workAreaChanged()
{
    kdDebug() << "KDesktop::workAreaChanged() -> starting timer" << endl;
    updateWorkAreaTimer->stop();
    updateWorkAreaTimer->start( 100, TRUE );
}

void KDesktop::updateWorkArea()
{
    QRect wr( kwinModule()->workArea( kwinModule()->currentDesktop() ) );
    kdDebug() << "KDesktop::updateWorkArea wr: " << wr.x() << "," << wr.y()
              << " " << wr.width() << "x" << wr.height() << endl;
    setMargins( wr.left(), wr.top(),
                QApplication::desktop()->width() - wr.right() - 1,
                QApplication::desktop()->height() - wr.bottom() - 1 );
    resizeContents( viewport()->width(), viewport()->height() );
    kdDebug() << "resizeContents " << viewport()->width() << "x" << viewport()->height() << endl;

    for ( QIconViewItem *item = firstItem(); item; item = item->nextItem() ) {
        QRect r( item->rect() );
        int dx = 0, dy = 0;
        if ( r.bottom() > visibleHeight() )
            dy = visibleHeight() - r.bottom() - 1;
        if ( r.right() > visibleWidth() )
            dx = visibleWidth() - r.right() - 1;
        if ( dx != 0 || dy != 0 )
            item->moveBy( dx, dy );
    }

    viewport()->repaint( FALSE );
    repaint( FALSE );
}

void KDesktop::contentsDropEvent(QDropEvent * e)
{
  bool isColorDrag = KColorDrag::canDecode(e);
  bool isImageDrag = QImageDrag::canDecode(e);

  if (!isColorDrag && !isImageDrag)
  {
    KonqIconViewWidget::contentsDropEvent(e);
    return;
  }

  if (isColorDrag) {
    KPopupMenu popup;
    popup.insertItem(i18n("Set as primary background color"), 1);
    popup.insertItem(i18n("Set as secondary background color"), 2);
    int result = popup.exec(e->pos());

    QColor c;
    KColorDrag::decode(e, c);
    switch (result) {
      case 1: bgMgr->setColor(c, true); break;
      case 2: bgMgr->setColor(c, false); break;
      default: break;
    }
  } else if (isImageDrag) {
    KPopupMenu popup;
    popup.insertItem(i18n("Set as wallpaper"), 1);
    int result = popup.exec(e->pos());

    if (result == 1)
    {
      QImage i;
      QImageDrag::decode(e, i);
      KTempFile tmpFile(KGlobal::dirs()->saveLocation("wallpaper"), ".png");
      i.save(tmpFile.name(), "PNG");
      kdDebug() << "KDesktop::contentsDropEvent " << tmpFile.name() << endl;
      bgMgr->setWallpaper(tmpFile.name());
    }
  }
}

void KDesktop::setWallpaper(const KURL &url)
{
    QString tmpFile;
    KIO::NetAccess::download( url, tmpFile );
    bgMgr->setWallpaper(tmpFile);
}

void KDesktop::languageChanged( const QString & lang )
{
    kdDebug() << "KDesktop::languageChanged " << lang << endl;

    // Translate messages using the old locale first, because
    // we can't use two klocale objects at once (the internals aren't really OO...)
    KLocale locale("kdesktop");
    locale.setLanguage( m_language );
    QString oldHome = locale.translate("Home Directory", "Home");

    locale.setLanguage( lang );
    KURL desktop_URL = desktopURL();
    // Rename the Home link if we have it
    if ( desktop_URL.isLocalFile() ) // todo network transparency
    {
        struct stat statbuf;
        QCString oldHomeDesktop = QFile::encodeName( desktop_URL.path() + oldHome );
        if ( ::stat( oldHomeDesktop, &statbuf ) == 0 )
        {
            kdDebug() << "Found " << oldHomeDesktop << endl;
            QCString newHomeDesktop = QFile::encodeName( desktop_URL.path() + locale.translate("Home Directory", "Home") );
            kdDebug() << "newHomeDesktop=" << newHomeDesktop << endl;
            if ( oldHomeDesktop != newHomeDesktop && ::rename( oldHomeDesktop, newHomeDesktop ) == 0 )
            {
                kdDebug() << oldHomeDesktop << " renamed to " << newHomeDesktop << endl;
            }
        } else
            kdDebug() << oldHomeDesktop << " not found" << endl;
    }


    // Rename the Trash path
    KURL oldTrash;
    oldTrash.setPath( KGlobalSettings::trashPath() );
    KURL newTrash;
    newTrash.setPath( oldTrash.directory(false) );
    newTrash.addPath( locale.translate( "Trash" ) );
    kdDebug() << "oldTrash = " << oldTrash.prettyURL() << endl;
    kdDebug() << "newTrash = " << newTrash.prettyURL() << endl;
    if ( !oldTrash.cmp(newTrash, true /*ignore trailing slash*/) &&
        ::rename( QFile::encodeName(oldTrash.path()), QFile::encodeName(newTrash.path()) ) == 0 )
    {
        kdDebug() << oldTrash.path() << " renamed to " << newTrash.path() << endl;
        // Worked. Save new setting.
        KConfig *config = KGlobal::config();
        KConfigGroupSaver cgs( config, "Paths" );
        config->writeEntry( "Trash", newTrash.path(), true, true );
        config->sync();
        // Tell all applications
        KIPC::sendMessageAll(KIPC::SettingsChanged, KApplication::SETTINGS_PATHS);
    }
    m_language = lang;
}

void KDesktop::logout()
{
    if( !kapp->requestShutDown() )
        // this i18n string is also in kicker/applets/run/runapplet
        KMessageBox::error( this, i18n("Could not logout properly.  The session manager cannot\n"
                                        "be contacted.  You can try to force a shutdown by pressing\n"
                                        "the CTRL, ALT and BACKSPACE keys at the same time.  Note\n"
                                        "however that your current session will not be saved with a\n"
                                        "forced shutdown." ) );
}

#include "desktop.moc"
