
#include <dcopclient.h>
#include <tqdragobject.h>
#include <kurl.h>
#include <tqstrlist.h>
#include <tqtimer.h>
#include <tdeapplication.h>
#include <tdelocale.h>
#include <tdefileitem.h>
#include <tdepopupmenu.h>
#include <kicondialog.h>
#include <kiconloader.h>
#include <klineedit.h>
#include <kprocess.h>
#include <kurlrequester.h>
#include <kstandarddirs.h>
#include <twin.h>
#include <tqclipboard.h>
#include "listboxlink.h"
#include "linkconfig.h"
#include "dndlistbox.h"
#include "baghiralinkdrag.h"
#include "dndlistbox.moc"

#define ID 0
#define NAME 1
#define LABEL 2
#define USER_LABEL 3
#define MOUNTABLE 4
#define DEVICE_NODE 5
#define MOUNT_POINT 6
#define FS_TYPE 7
#define MOUNTED 8
#define BASE_URL 9
#define MIME_TYPE 10
#define ICON_NAME 11
#define MEDIA_PROPS 12
#define MEDIALIST_PROPS 13

ResizingLinkBox::ResizingLinkBox( TQWidget * parent, const char * name, WFlags f ) : TDEListBox( parent, name, f)
{
   TDEConfig config(TQDir::homeDirPath() + "/.qt/baghirarc");
   config.setGroup("Sidebar");
   size_ = config.readNumEntry (TQString(name) + "_IconSize", 48);
   popupMenu = new TDEPopupMenu;
   popupMenu->insertTitle (i18n("Icon Size"),122,122);
   popupMenu->insertItem("16x16", this, SLOT(setIconSize(int)),0,16,123);
   popupMenu->insertItem("22x22", this, SLOT(setIconSize(int)),0,22,124);
   popupMenu->insertItem("32x32", this, SLOT(setIconSize(int)),0,32,125);
   popupMenu->insertItem("48x48", this, SLOT(setIconSize(int)),0,48,126);
   popupMenu->insertItem("64x64", this, SLOT(setIconSize(int)),0,64,127);
   popupMenu->insertItem("128x128", this, SLOT(setIconSize(int)),0,128,128);
}

void ResizingLinkBox::insertItem( const TQListBoxItem *lbi, int index )
{
   TDEListBox::insertItem( lbi, index );
   if (height() <= numRows()*itemHeight())
      emit itemNumberChanged(TRUE);
}

void ResizingLinkBox::insertItem( const TQListBoxItem *lbi, const TQListBoxItem *after )
{
   TDEListBox::insertItem( lbi, after );
   if (height() <= numRows()*itemHeight())
      emit itemNumberChanged(TRUE);
}


void ResizingLinkBox::insertItem( const TQString & icon, const TQString & title, const TQString & url )
{
   insertItem( new ListBoxLink(icon, size_, title, url ) );
}

void ResizingLinkBox::removeItem( int index )
{
   blockSignals ( true );
   TDEListBox::removeItem(index);
   blockSignals ( false );
   emit itemNumberChanged(FALSE);
}

void ResizingLinkBox::setIconSize(int size)
{
   size_ = size;
   TDEConfig *config = new TDEConfig(TQDir::homeDirPath() + "/.qt/baghirarc");
   config->setGroup("Sidebar");
   config->writeEntry (TQString(name()) + "_IconSize", size);
   config->sync();
   ListBoxLink *runner;
   ListBoxDevice *current;
   blockSignals ( true );
   for (uint i = 0; i < count(); i++)
   {
      runner = (ListBoxLink*)item(i);
      if (dynamic_cast<ListBoxDevice*>(runner))
      {
         current = (ListBoxDevice*)runner;
         insertItem( new ListBoxDevice(current->icon(), size, current->text(), current->URL(), current->name(), current->mountPoint(), current->mounted(), current->ejectable(), current->removable(), current->id()), i );
      }
      else
      {
         insertItem( new ListBoxLink(runner->icon(), size, runner->text(), runner->URL()), i );
      }
      TDEListBox::removeItem(i+1);
   }
   blockSignals ( false );
}

void ResizingLinkBox::mousePressEvent ( TQMouseEvent *mpe )
{
   TDEListBox::mousePressEvent( mpe );
}

void ResizingLinkBox::mouseReleaseEvent ( TQMouseEvent *mpe )
{
   if (mpe->button() == TQt::LeftButton)
   {
      ListBoxLink *link = (ListBoxLink*)itemAt(mpe->pos());
      if (isSelected(link)) emit clicked(link);
      TDEListBox::mousePressEvent( mpe );
      return;
   }
   TDEListBox::mouseReleaseEvent( mpe );
}

void ResizingLinkBox::contentsWheelEvent ( TQWheelEvent * we )
{
   if (we->state() == TQt::ControlButton)
      TDEListBox::contentsWheelEvent ( we );
   else
      emit scrolled(0, -we->delta());
}

MediaListBox::MediaListBox( TQWidget * parent, const char * name, WFlags f ) : ResizingLinkBox(parent, name, f), DCOPObject("BaghiraSidebarIface")
{
   TDEConfig config(TQDir::homeDirPath() + "/.qt/baghirarc");
   config.setGroup("Sidebar");
   hiddenDevices = config.readListEntry("HiddenDevices");
   currentFloppy = 0L;
   devicePopup = new TDEPopupMenu(this);
   devicePopup->setCheckable ( true );
   popupMenu->insertItem(i18n("Device List"), devicePopup, 1, 0);
   popupMenu->insertSeparator( 0 );

   insertItem(new ListBoxDevice("system", size_, i18n("My Computer"), "system:/", "", "", TRUE, FALSE, FALSE));
   insertItem(new ListBoxDevice("network", size_, i18n("Network"), "remote:/", "", "", TRUE, FALSE, FALSE));
   insertItem(new ListBoxDevice("hdd-mounted", size_, i18n("Startvolume"), TQDir::rootDirPath(), "", "", TRUE, FALSE, FALSE));
   client = TDEApplication::dcopClient();
   client->connectDCOPSignal("kded", "mediamanager", "mediumAdded(TQString)", "BaghiraSidebarIface", "mediumAdded(TQString)", FALSE);
   client->connectDCOPSignal("kded", "mediamanager", "mediumRemoved(TQString)", "BaghiraSidebarIface", "mediumRemoved(const TQString)", FALSE);
   client->connectDCOPSignal("kded", "mediamanager", "mediumChanged(TQString)", "BaghiraSidebarIface", "mediumChanged(TQString)", FALSE);
   /* Get the media info - huhhh ;) */
   TQByteArray data, replyData;
   TQCString replyType;
   TQDataStream arg(data, IO_WriteOnly);
   arg << ""; // ask for the full list
   if (!client->call("kded", "mediamanager", "fullList()", data, replyType, replyData))
      tqDebug("there was some error using DCOP.");
   else
   {
      TQDataStream reply(replyData, IO_ReadOnly);
      if (replyType == "TQStringList")
      {
         TQStringList result;
         reply >> result;
         blockSignals ( true );
         for (uint i = 0; i < result.size()/MEDIALIST_PROPS; i++)
         {
            if (result[MEDIALIST_PROPS*i+MIME_TYPE] != "media/hdd_mounted" &&
                  result[MEDIALIST_PROPS*i+MIME_TYPE] != "media/hdd_unmounted" &&
                  result[MEDIALIST_PROPS*i+MIME_TYPE] != "media/nfs_mounted" &&
                  result[MEDIALIST_PROPS*i+MIME_TYPE] != "media/nfs_unmounted" &&
                  result[MEDIALIST_PROPS*i+MIME_TYPE] != "media/smb_mounted" &&
                  result[MEDIALIST_PROPS*i+MIME_TYPE] != "media/smb_unmounted")
            {
               ListBoxDevice *dev = createListBoxDevice(result, i);
               if (hiddenDevices.contains(dev->name()))
               {
                  deviceList.append(dev);
                  devicePopup->setItemChecked(dev->id(),false);
               }
               else
               {
                  insertItem(dev);
                  devicePopup->setItemChecked(dev->id(),true);
               }
            }
         }
         blockSignals ( false );
      }
      else
         tqWarning("fullList() returned an unexpected type of reply!");
   }
//    setCurrentItem( 0 );
}

MediaListBox::~MediaListBox()
{
   hiddenDevices.clear();
   ListBoxDevice *runner;
   for ( runner = deviceList.first(); runner; runner = deviceList.next() )
      hiddenDevices.append(runner->name());
   TDEConfig config(TQDir::homeDirPath() + "/.qt/baghirarc");
   config.setGroup("Sidebar");
   config.writeEntry("HiddenDevices", hiddenDevices);
}

void MediaListBox::removeItem( int index )
{
   devicePopup->removeItem(((ListBoxDevice*)item( index ))->id());
   ResizingLinkBox::removeItem(index);
}

void MediaListBox::kfloppy()
{
   if (currentFloppy)
   {
      TDEProcess proc;
      proc << "kfloppy" << currentFloppy->mountPoint();
      proc.start(TDEProcess::DontCare);
      proc.detach();
      currentFloppy = 0L;
   }
   return;
}

void MediaListBox::toggleDevice(int id)
{
   ListBoxDevice *runner;
   if (devicePopup->isItemChecked(id)) // remove the device!
   {
      devicePopup->setItemChecked(id, false);
      for (uint i = 0; i < count(); i++)
      {
         runner = (ListBoxDevice*)item(i);
         if (runner->id() == id)
         {
            deviceList.append(runner);
            blockSignals ( true );
            takeItem(runner);
            blockSignals ( false );
            break;
         }
      }
   }
   else  // add the device!
   {
      devicePopup->setItemChecked(id, true);
      for ( runner = deviceList.first(); runner; runner = deviceList.next() )
         if (runner->id() == id)
         {
            insertItem(deviceList.take());
            break;
         }
   }
}

ListBoxDevice *MediaListBox::createListBoxDevice(TQStringList & deviceProperties, uint n)
{
   TQString icon;
   icon = deviceProperties[MEDIALIST_PROPS*n+ICON_NAME];
   if (icon.isNull())
   {
      icon = deviceProperties[MEDIALIST_PROPS*n+MIME_TYPE];
      icon = icon.section( '/', -1 );
      icon.truncate( icon.length()-2 );
      if (icon.contains("floppy")) icon.prepend("3");
   }
   TQString label;
   label = deviceProperties[MEDIALIST_PROPS*n+USER_LABEL];
   if (label.isNull())
   {
      label = deviceProperties[MEDIALIST_PROPS*n+LABEL];
      label = i18n(label.section( " (", 0, 0 ).utf8());
   }
   return new ListBoxDevice( icon, size_, label, "system:/media/"+deviceProperties[MEDIALIST_PROPS*n+NAME], deviceProperties[MEDIALIST_PROPS*n+NAME], deviceProperties[MEDIALIST_PROPS*n+DEVICE_NODE], deviceProperties[MEDIALIST_PROPS*n+MOUNTED] == "true", icon.contains("dvd") || icon.contains("cdrom") || icon.contains("cdwriter"),icon.contains("floppy"), devicePopup->insertItem(deviceProperties[MEDIALIST_PROPS*n+NAME], this, SLOT(toggleDevice(int))));
}

int MediaListBox::index (const TQString & name )
{
   ListBoxDevice *device;
   for (uint i = 0; i < count(); i++)
      {
         device = (ListBoxDevice*)item(i);
         if (device && device->name() == name) return i;
      }
   return -1;
}

void MediaListBox::mediumAdded(const TQString &name)
{
   TQByteArray data, replyData;
   TQCString replyType;
   TQDataStream arg(data, IO_WriteOnly);
   arg << name; // ask for this item only
   if (!client->call("kded", "mediamanager", "properties(TQString)", data, replyType, replyData))
      tqDebug("there was some error using DCOP.");
   else
   {
      TQDataStream reply(replyData, IO_ReadOnly);
      if (replyType == "TQStringList")
      {
         TQStringList result;
         reply >> result;
         ListBoxDevice *dev = createListBoxDevice(result);
         if (hiddenDevices.contains(dev->name()))
         {
            deviceList.append(dev);
            devicePopup->setItemChecked(dev->id(),false);
         }
         else
         {
            insertItem(dev);
            devicePopup->setItemChecked(dev->id(),true);
         }
      }
      else
         tqWarning("properties() returned an unexpected type of reply!");
   }
}

void MediaListBox::mediumRemoved(const TQString &name)
{
   TQByteArray data, replyData;
   TQCString replyType;
   TQDataStream arg(data, IO_WriteOnly);
   arg << name; // ask for this item only
   if (!client->call("kded", "mediamanager", "properties(TQString)", data, replyType, replyData))
      tqDebug("there was some error using DCOP.");
   else
   {
      TQDataStream reply(replyData, IO_ReadOnly);
      if (replyType == "TQStringList")
      {
         TQStringList result;
         reply >> result;
         int i = index(name);
         if (i<0)
            return;
         if (i == currentItem()) setCurrentItem(0);
         removeItem(i);
      }
      else
         tqWarning("properties() returned an unexpected type of reply!");
   }
}

void MediaListBox::mediumChanged(const TQString &name)
{
   TQByteArray data, replyData;
   TQCString replyType;
   TQDataStream arg(data, IO_WriteOnly);
   arg << name; // ask for this item only
   if (!client->call("kded", "mediamanager", "properties(TQString)", data, replyType, replyData))
      tqDebug("there was some error using DCOP.");
   else
   {
      TQDataStream reply(replyData, IO_ReadOnly);
      if (replyType == "TQStringList")
      {
         TQStringList result;
         reply >> result;
         int i = index(name);
         if (i<0)
            return;
         
         ListBoxDevice *device = createListBoxDevice(result);
         if (hiddenDevices.contains(device->name()))
         {
            deviceList.append(device);
            devicePopup->setItemChecked(device->id(),false);
            return;
         }
         devicePopup->setItemChecked(device->id(),true);
         blockSignals(true);
         if (i == currentItem()) // changing current item - take some care of updating stuff
         {
            if (((ListBoxDevice*)item(i))->mounted() && !device->mounted()) // unmounted the device - we certainly do not wanna select it anymore
            {
               setCurrentItem(0);
               removeItem(i);
               insertItem(device, i);
            }
            else // we're selected and wanna keep selection
            {
               removeItem(i);
               insertItem(device, i);
               setSelected( i, true );
            }
         }
         else // ordinary change
         {
            removeItem(i);
            insertItem(device, i);
         }
         blockSignals(false);
      }
      else
         tqWarning("properties() returned an unexpected type of reply!");
   }
}

#define _FLOPPYID_ 0
#define _FLOPPYINDEX_ 0

void MediaListBox::mousePressEvent ( TQMouseEvent *mpe )
{
   if (mpe->button() == TQt::RightButton)
   {
      popupMenu->removeItem(_FLOPPYID_);
      ListBoxDevice *device = (ListBoxDevice*)itemAt(mpe->pos());
      if (device && device->name().contains("fd"))
      {
         currentFloppy = device;
         popupMenu->insertItem(i18n("Format disk..."), this, SLOT(kfloppy()),0,_FLOPPYID_,_FLOPPYINDEX_);
      }
      popupMenu->popup(mpe->globalPos());
      return;
   }
   if (mpe->button() == TQt::LeftButton && mpe->x() > width()-22)
   {
      ListBoxDevice *device = (ListBoxDevice*)itemAt(mpe->pos());
      int dy = itemRect(device).y();
      if ((device->removable() || device->ejectable()) && device->mounted() && mpe->y() > dy+11 && mpe->y() < dy+33)
      {
         TDEProcess proc;
         device->ejectable()?
            proc << "kdeeject" /*<< "-q"*/ << device->mountPoint():
            proc << "umount" << device->mountPoint(); // umount?
         proc.start(TDEProcess::DontCare);
         proc.detach();
         return;
      }
   }
   ResizingLinkBox::mousePressEvent( mpe );
}

void MediaListBox::resizeEvent ( TQResizeEvent * re)
{
   if (width() != re->oldSize().width())
   {
      for (uint i = 0; i < count(); i++)
         if (((ListBoxDevice*)item(i))->ejectable() && ((ListBoxDevice*)item(i))->mounted() && !isSelected(i)) updateItem(i);
   }
   ResizingLinkBox::resizeEvent(re);
}

DnDListBox::DnDListBox( TQWidget * parent, const char * name, WFlags f ):
ResizingLinkBox( parent, name, f), _poof(0), _poofIndex(0), _poofAnimPix(0), _poofPix(0)
{
   setAcceptDrops(true);
   dialog = new LinkConfig();
   connect(dialog->buttonOk, SIGNAL(clicked()), this, SLOT(updateLink()));
   setCursor(TQt::PointingHandCursor);
}

DnDListBox::~DnDListBox()
{
}

void DnDListBox::poof(ListBoxLink *link)
{
   _poofIndex = 0;
   _poofPix = new TQPixmap(locateLocal("data", "baghira/poof.png"), "png");
   _poofAnimPix = new TQPixmap(_poofPix->width(), _poofPix->width());
   if (!_poof)
      _poof = new TQWidget(0,0, TQt::WType_TopLevel | TQt::WStyle_NoBorder | TQt::WStyle_StaysOnTop | TQt::WX11BypassWM);
   KWin::setShadowSize(_poof->winId(), 0);
   _poof->setFixedSize(_poofPix->width(), _poofPix->width());
   int x = TQCursor::pos().x() - _poof->width()/2;
   int y = TQCursor::pos().y() - _poof->height()/2;
   TQPixmap bgPix = TQPixmap::grabWindow( tqt_xrootwin(), x, y, _poofPix->width(), _poofPix->width());
   _poof->move(x,y);
   _poof->show();
   _poof->setBackgroundOrigin(TQWidget::WidgetOrigin);
   _poof->setPaletteBackgroundPixmap( bgPix );
   runPoof();
   removeItem ( index(link) );
}

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

void DnDListBox::dragEnterEvent ( TQDragEnterEvent *dee )
{
//    dragging_ = true;
   if (TQUriDrag::canDecode(dee) || BaghiraLinkDrag::canDecode(dee) || TQTextDrag::canDecode(dee))
      dee->accept(true);
}

void DnDListBox::dropEvent ( TQDropEvent *de )
{
   TQStrList list;
   TQString title;
   TQString command;
   TQString icon;
   int oldIndex;
   TQCString subtype;
   if (BaghiraLinkDrag::decode(de, &title, &command, &icon, &oldIndex)) // internal move
   {
      BaghiraLinkDrag::setAccepted();
      TQListBoxItem *after = itemAt(de->pos());
      int newIndex = index(after);
      if (!dragging_ || oldIndex < 0 || oldIndex > count()-2)
         insertItem (new ListBoxLink(icon, size_, title, command), after?newIndex:count());
      else if (oldIndex != newIndex)
      {
         insertItem (new ListBoxLink(*((ListBoxLink*)item(oldIndex))), after?newIndex:count());
         removeItem ( (newIndex < 0 || oldIndex < newIndex) ? oldIndex : oldIndex + 1 );
      }
   }
   else if ( TQUriDrag::decode(de, list) )
   {
      char *uri;
      KURL url;
      TQListBoxItem *after = itemAt(de->pos());
      for ( uri = list.first(); uri; uri = list.next() )
      {
         url = KURL(uri);
         if (url.protocol() == "http")
            insertItem(new ListBoxLink("html", size_, url.host()+(url.path()=="/"?TQString(""):url.path()), uri), after?index(after):count());
         else
         {
            KFileItem item = KFileItem(KFileItem::Unknown, KFileItem::Unknown, url, true);
               insertItem(new ListBoxLink(item.iconName(), size_, url.fileName().isEmpty()?url.prettyURL():url.fileName(), uri), after?index(after):count());
         }
      }
   }
   else if (TQTextDrag::decode(de, command, subtype))
   {
      KURL url(command);
      if (url.isValid())
      {
         TQListBoxItem *after = itemAt(de->pos());
         if (url.protocol() == "http")
            insertItem(new ListBoxLink("html", size_, url.host()+(url.path()=="/"?TQString(""):url.path()), command), after?index(after):count());
         else
         {
            KFileItem item = KFileItem(KFileItem::Unknown, KFileItem::Unknown, url, true);
            insertItem(new ListBoxLink(item.iconName(), size_, url.fileName().isEmpty()?url.prettyURL():url.fileName(), command), after?index(after):count());
         }
      }
      else if (command.contains('@'))
      {
         TQListBoxItem *after = itemAt(de->pos());
         command.replace(" ","");
         insertItem(new ListBoxLink("kmail", size_, command, "mailto:"+command), after?index(after):count());
      }
      else if (command.contains("'at'")) //last chance for anti-spam addy
      {
         TQListBoxItem *after = itemAt(de->pos());
         command.replace(" ","");
         command.replace("'at'","@");
         insertItem(new ListBoxLink("kmail", size_, command, "mailto:"+command), after?index(after):count());
      }
   }
}

void DnDListBox::mousePressEvent ( TQMouseEvent *mpe )
{
   if (mpe->button() == TQt::RightButton)
   {
      currentItem = 0;
      popupMenu->removeItem(0);
      ListBoxDevice *device = (ListBoxDevice*)itemAt(mpe->pos());
      if (device)
      {
         currentItem = device;
         popupMenu->insertItem("Edit link...", this, SLOT(configureLink()),0,0,0);
      }
      popupMenu->popup(mpe->globalPos());
      return;
   }
   if (mpe->button() == TQt::MidButton)
   {
      pasteURL(TQClipboard::Selection, itemAt(mpe->pos()));
      return;
   }
   ResizingLinkBox::mousePressEvent( mpe );
}

void DnDListBox::mouseReleaseEvent ( TQMouseEvent *mre )
{
   if (!dragging_) ResizingLinkBox::mouseReleaseEvent( mre );
}

void DnDListBox::mouseMoveEvent ( TQMouseEvent * mme )
{
   if (mme->state() & TQt::LeftButton)
   {
      if (!dragging_)
      {
         ListBoxLink *link = (ListBoxLink*)itemAt(mme->pos());
         if (link)
         {
            dragging_ = true;
            BaghiraLinkDrag *d = new BaghiraLinkDrag( link->text(), link->URL(), link->icon(), index(link), this );
            d->setPixmap(*link->pixmap(),TQPoint(22,22));
            d->drag();
            if (mme->state() & TQt::ControlButton || BaghiraLinkDrag::accepted())
               return;
            poof(link);
            // do NOT delete d.
         }
      }
   }
   else // ensure to release from drag
      dragging_ = false;
}

void DnDListBox::pasteURL(int mode, TQListBoxItem *after)
{
   TQString string = tqApp->clipboard()->text( (TQClipboard::Mode)mode );
   KURL url(string);
   if (url.isValid())
   {
      if (url.protocol() == "http")
         insertItem(new ListBoxLink("html", size_, url.host()+(url.path()=="/"?TQString(""):url.path()), string), after?index(after):count());
      else
      {
         KFileItem item = KFileItem(KFileItem::Unknown, KFileItem::Unknown, url, true);
         insertItem(new ListBoxLink(item.iconName(),size_, url.fileName().isEmpty()?url.prettyURL():url.fileName(), string), after?index(after):count());
      }
   }
   else if (string.contains('@'))
   {
      string.replace(" ","");
      insertItem(new ListBoxLink("kmail", size_, string, "mailto:"+string), after?index(after):count());
   }
   else if (string.contains("'at'")) //last chance for anti-spam addy
   {
      string.replace(" ","");
      string.replace("'at'","@");
      insertItem(new ListBoxLink("kmail", size_, string, "mailto:"+string), after?index(after):count());
   }
}

void DnDListBox::configureLink()
{
   if (currentItem == 0L)
      return;
   dialog->title->setText(currentItem->text());
   dialog->url->setURL(currentItem->URL());
   dialog->icon->setIcon(currentItem->icon());
   dialog->show();
}

void DnDListBox::updateLink()
{
   if (currentItem)
   {
      int index_ = index(currentItem);
      bool wasSelected = isSelected(index_);
      insertItem( new ListBoxLink(dialog->icon->icon(), size_, dialog->title->text(),dialog->url->url()), index_ +1);
      removeItem( index_ );
      setSelected(index_, wasSelected);
   }
}
