/* kastasker.cpp
**
** Copyright (C) 2001-2004 Richard Moore <rich@kde.org>
** Contributor: Mosfet
**     All rights reserved.
**
** KasBar is dual-licensed: you can choose the GPL or the BSD license.
** Short forms of both licenses are included below.
*/

/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program in a file called COPYING; if not, write to
** the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
** MA 02111-1307, USA.
*/

/*
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
**    notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
*/

/*
** Bug reports and questions can be sent to kde-devel@kde.org
*/
#include <qbitmap.h>
#include <qapplication.h>

#include <kconfig.h>
#include <kdebug.h>
//#include <kconfiggroupsaver.h>

#include <taskmanager.h>

#include "kasaboutdlg.h"
#include "kastaskitem.h"
#include "kasprefsdlg.h"
#include "kasstartupitem.h"
#include "kasgroupitem.h"

#include "kastasker.h"
#include "kastasker.moc"

//
// Bitmap data used for the window state indicators
//
static unsigned char min_bits[] = {
    0x00, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00};
static unsigned char max_bits[] = {
    0xff, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xff};
static unsigned char shade_bits[] = {
    0x06, 0x1e, 0x7e, 0xfe, 0xfe, 0x7e, 0x1e, 0x06};

static const char *micro_max[]={
"6 6 2 1",
". c None",
"# c #000000",
"######",
"######",
"##..##",
"##..##",
"######",
"######",
};

static const char *micro_min[]={
"6 6 2 1",
". c None",
"# c #000000",
"......",
"######",
"######",
".####.",
"..##..",
"......"
};

static const char *micro_shade[]={
"6 6 2 1",
". c None",
"# c #000000",
".##...",
".###..",
".####.",
".####.",
".###..",
".##..."
};

KasTasker::KasTasker( Orientation o, QWidget* parent, const char* name, WFlags f )
  : KasBar( o, parent, name, f ),
    master_( 0 ),
    manager( new TaskManager( this, "taskmanager" ) ),
    minPix( 0 ), maxPix( 0 ), shadePix( 0 ),
    microShadePix(0),
    microMaxPix(0),
    microMinPix(0),
    enableThumbs_( true ),
    thumbnailSize_( 0.2 ),
    enableNotifier_( true ),
    showModified_( true ),
    showProgress_( true ),
    showAllWindows_( true ),
    thumbUpdateDelay_( 10 ),
    groupWindows_( true ),
    conf( 0 )
{
   setAcceptDrops( true );
   connect( manager, SIGNAL( taskAdded(Task*) ), SLOT( addTask(Task*) ) );
   connect( manager, SIGNAL( taskRemoved(Task*) ), SLOT( removeTask(Task*) ) );
   connect( manager, SIGNAL( startupAdded(Startup*) ), SLOT( addStartup(Startup*) ) );
   connect( manager, SIGNAL( startupRemoved(Startup*) ), SLOT( removeStartup(Startup*) ) );
   connect( this, SIGNAL( itemSizeChanged( int ) ), SLOT( refreshAll() ) );
}

KasTasker::KasTasker( Orientation o, KasTasker *master, QWidget* parent, const char* name, WFlags f )
  : KasBar( o, parent, name, f ),
    master_( master ),
    manager( master->manager ),
    minPix( 0 ), maxPix( 0 ), shadePix( 0 ),
    microShadePix(0),
    microMaxPix(0),
    microMinPix(0),
    enableThumbs_( master->enableThumbs_ ),
    thumbnailSize_( master->thumbnailSize_ ),
    enableNotifier_( master->enableNotifier_ ),
    showModified_( master->showModified_ ),
    showProgress_( master->showProgress_ ),
    showAllWindows_( master->showAllWindows_ ),
    thumbUpdateDelay_( master->thumbUpdateDelay_ ),
    groupWindows_( false ),
    conf( 0 )
{
  setAcceptDrops( true );
  rereadMaster();
  connect( master, SIGNAL( configChanged() ), SLOT( readConfig() ) );
}

KasTasker::~KasTasker()
{
   delete minPix;
   delete maxPix;
   delete shadePix;
   delete microShadePix;
   delete microMaxPix;
   delete microMinPix;
}

void KasTasker::rereadMaster()
{
    if ( !master_ )
	return;

    setItemSize( master_->itemSize() );
    setTint( master_->hasTint() );
    setTintColor( master_->tintColor() );
    setTintAmount( master_->tintAmount() );
    setTransparent( master_->isTransparent() );

    setLabelPenColor( master_->labelPenColor() );
    setLabelBgColor( master_->labelBgColor() );
    setInactivePenColor( master_->inactivePenColor() );
    setInactiveBgColor( master_->inactiveBgColor() );
    setActivePenColor( master_->activePenColor() );
    setActiveBgColor( master_->activeBgColor() );
    setProgressColor( master_->progressColor() );
}

KasTaskItem *KasTasker::findItem( Task *t )
{
   KasTaskItem *result = 0;
   for ( uint i = 0; i < itemCount(); i++ ) {
      if ( itemAt(i)->inherits( "KasTaskItem" ) ) {
	 KasTaskItem *curr = static_cast<KasTaskItem *> (itemAt( i ));
	 if ( curr->task() == t ) {
	    result = curr;
	    break;
	 }
      }
   }
   return result;
}

KasStartupItem *KasTasker::findItem( Startup *s )
{
   KasStartupItem *result = 0;
   for ( uint i = 0; i < itemCount(); i++ ) {
      if ( itemAt(i)->inherits( "KasStartupItem" ) ) {
	 KasStartupItem *curr = static_cast<KasStartupItem *> (itemAt( i ));
	 if ( curr->startup() == s ) {
	    result = curr;
	    break;
	 }
      }
   }
   return result;
}

void KasTasker::addTask( Task *t )
{
   KasItem *item = 0;
   if ( showAllWindows_ || t->isOnCurrentDesktop() ) {
      if ( groupWindows_ ) {
	 item = maybeAddToGroup( t );
      }
      if ( !item ) {
	  item = new KasTaskItem( this, t );
	  append( item );
      }

      //
      // Ensure the window manager knows where we put the icon.
      //
      QPoint p = mapToGlobal( itemPos( item ) );
      QSize s( itemExtent(), itemExtent() );
      t->publishIconGeometry( QRect( p, s ) );
   }
}

KasItem *KasTasker::maybeAddToGroup( Task *t )
{
   KasItem *item = 0;

   QString taskClass = t->className().lower();

   for ( uint i = 0; (!item) && (i < itemCount()); i++ ) {
      KasItem *ei = itemAt( i );
      if ( ei->inherits( "KasTaskItem" ) ) {
	 KasTaskItem *eti = static_cast<KasTaskItem *> (ei);

	 // NB This calls Task::className() not QObject::className()
	 QString currClass = eti->task()->className().lower();

	 if ( Task::idMatch( currClass, taskClass ) ) {
	    KasGroupItem *egi = convertToGroup( eti->task() );
	    egi->addTask( t );
	    item = egi;
	    break;
	 }
      }
      else if ( ei->inherits( "KasGroupItem" ) ) {
	 KasGroupItem *egi = static_cast<KasGroupItem *> (ei);

	 for ( int i = 0; i < egi->taskCount(); i++ ) {
	    // NB This calls Task::className() not QObject::className()
	    QString currClass = egi->task( i )->className().lower();

	    if ( Task::idMatch( currClass, taskClass ) ) {
	       egi->addTask( t );
	       item = egi;
	       break;
	    }
	 }
      }
   }

   return item;
}

void KasTasker::removeTask( Task *t )
{
   KasTaskItem *i = findItem( t );
   if ( !i )
     return;

   remove( i );
   refreshIconGeometry();
}

KasGroupItem *KasTasker::convertToGroup( Task *t )
{
  KasTaskItem *ti = findItem( t );
  int i = indexOf( ti );
  KasGroupItem *gi = new KasGroupItem( this );
  gi->addTask( t );
  removeTask( t );
  insert( i, gi );

  connect( manager, SIGNAL( taskRemoved(Task *) ),
	   gi, SLOT( removeTask(Task *) ) );

  return gi;
}

void KasTasker::moveToMain( KasGroupItem *gi, Task *t )
{
  int i = indexOf( gi );
  if ( i != -1 ) {
    remove( gi );
    insert( i, new KasTaskItem( this, t ) );
  }
  else
    append( new KasTaskItem( this, t ) );

  refreshIconGeometry();
}

void KasTasker::addStartup( Startup *s )
{
   if ( enableNotifier_ )
      append( new KasStartupItem( this, s ) );
}

void KasTasker::removeStartup( Startup *s )
{
   KasStartupItem *i = findItem( s );
   remove( i );
}

void KasTasker::refreshAll()
{
   setUpdatesEnabled( false );

   clear();

   TaskList l = manager->tasks();
   for ( Task *t = l.first(); t != 0; t = l.next() ) {
      addTask( t );
   }

   setUpdatesEnabled( true );
   updateLayout();
}

void KasTasker::refreshIconGeometry()
{
   for ( uint i = 0; i < itemCount(); i++ ) {
      if ( itemAt(i)->inherits( "KasTaskItem" ) ) {
	 KasTaskItem *curr = static_cast<KasTaskItem *> (itemAt( i ));

	 QPoint p = mapToGlobal( itemPos( curr ) );
	 QSize s( itemExtent(), itemExtent() );
	 curr->task()->publishIconGeometry( QRect( p, s ) );
      }
   }
}

QBitmap *KasTasker::minIcon()
{
   if ( !minPix ) {
      minPix = new QBitmap(8, 8, min_bits, true);
      minPix->setMask(*minPix);
   }

   return minPix;
}

QBitmap *KasTasker::maxIcon()
{
   if ( !maxPix ) {
      maxPix = new QBitmap(8, 8, max_bits, true);
      maxPix->setMask(*maxPix);
   }

   return maxPix;
}

QBitmap *KasTasker::shadeIcon()
{
   if ( !shadePix ) {
      shadePix = new QBitmap(8, 8, shade_bits, true);
      shadePix->setMask(*shadePix);
   }

   return shadePix;
}

QPixmap *KasTasker::microShadeIcon()
{
  if ( !microShadePix )
    microShadePix = new QPixmap( micro_shade );

  return microShadePix;
}

QPixmap *KasTasker::microMaxIcon()
{
  if ( !microMaxPix )
    microMaxPix = new QPixmap( micro_max );

  return microMaxPix;
}

QPixmap *KasTasker::microMinIcon()
{
  if ( !microMinPix )
    microMinPix = new QPixmap( micro_min );

  return microMinPix;
}

void KasTasker::setNotifierEnabled( bool enable )
{
   enableNotifier_ = enable;
}

void KasTasker::setThumbnailSize( double size )
{
  thumbnailSize_ = size;
}

void KasTasker::setThumbnailSize( int percent )
{
   double amt = (double) percent / 100.0;
   setThumbnailSize( amt );
}

void KasTasker::setThumbnailsEnabled( bool enable )
{
   enableThumbs_ = enable;
}

void KasTasker::setShowModified( bool enable )
{
   showModified_ = enable;
   update();
}

void KasTasker::setShowProgress( bool enable )
{
   showProgress_ = enable;
   update();
}

void KasTasker::setShowAllWindows( bool enable )
{
   if ( showAllWindows_ != enable ) {
      showAllWindows_ = enable;
      refreshAll();
      if ( !showAllWindows_ ) {
	connect( manager, SIGNAL( desktopChanged( int ) ), SLOT( refreshAll() ) );
	connect( manager, SIGNAL( windowChanged( WId ) ), SLOT( refreshAll() ) );
      }
      else {
	disconnect( manager, SIGNAL( desktopChanged( int ) ), this, SLOT( refreshAll() ) );
	disconnect( manager, SIGNAL( windowChanged( WId ) ), this, SLOT( refreshAll() ) );
      }
   }
}

void KasTasker::setThumbnailUpdateDelay( int secs )
{
  thumbUpdateDelay_ = secs;
}

void KasTasker::setGroupWindows( bool enable )
{
   if ( groupWindows_ != enable ) {
      groupWindows_ = enable;
      refreshAll();
   }
}

//
// Configuration Loader
//

void KasTasker::setConfig( KConfig *conf )
{
    this->conf = conf;
}

void KasTasker::readConfig()
{
   readConfig(conf);
}

void KasTasker::readConfig( KConfig *conf )
{
    if ( !conf ) {
	kdWarning() << "KasTasker::readConfig() got a null KConfig" << endl;
	return;
    }

   //
   // Appearance Settings.
   //
   KConfigGroupSaver saver( conf, "Appearance" );

   setItemSize( conf->readNumEntry( "ItemSize", KasBar::Medium ) );
   setTint( conf->readBoolEntry( "EnableTint", false ) );
   setTintColor( conf->readColorEntry( "TintColor", &Qt::black ) );
   setTintAmount( conf->readDoubleNumEntry( "TintAmount", 0.1 ) );
   setTransparent( conf->readBoolEntry( "Transparent", true ) );

   //
   // Painting colors
   //
   conf->setGroup("Colors");
   setLabelPenColor( conf->readColorEntry( "LabelPenColor", &Qt::white ) );
   setLabelBgColor( conf->readColorEntry( "LabelBgColor", &Qt::black ) );
   setInactivePenColor( conf->readColorEntry( "InactivePenColor", &Qt::black ) );
   setInactiveBgColor( conf->readColorEntry( "InactiveBgColor", &Qt::white ) );
   setActivePenColor( conf->readColorEntry( "ActivePenColor", &Qt::black ) );
   setActiveBgColor( conf->readColorEntry( "ActiveBgColor", &Qt::white ) );
   setProgressColor( conf->readColorEntry( "ProgressColor", &Qt::green ) );

   //
   // Thumbnail Settings
   //
   conf->setGroup("Thumbnails");
   setThumbnailsEnabled( conf->readBoolEntry( "Thumbnails", true ) );
   setThumbnailSize( conf->readDoubleNumEntry( "ThumbnailSize", 0.2 ) );
   setThumbnailUpdateDelay( conf->readNumEntry( "ThumbnailUpdateDelay", 10 ) );

   //
   // Behaviour Settings
   //
   conf->setGroup("Behaviour");
   setNotifierEnabled( conf->readBoolEntry( "StartupNotifier", true ) );
   setShowModified( conf->readBoolEntry( "ModifiedIndicator", true ) );
   setShowProgress( conf->readBoolEntry( "ProgressIndicator", true ) );
   setShowAllWindows( conf->readBoolEntry( "ShowAllWindows", true ) );
   setGroupWindows( conf->readBoolEntry( "GroupWindows", true ) );

   //
   // Layout Settings
   //
   conf->setGroup("Layout");
   setMaxBoxes( conf->readUnsignedNumEntry( "MaxBoxes", 0 ) );

   //    fillBg = conf->readBoolEntry( "FillIconBackgrounds", /*true*/ false );
   //    fillActiveBg = conf->readBoolEntry( "FillActiveIconBackground", true );
   //    enablePopup = conf->readBoolEntry( "EnablePopup", true );
}

void KasTasker::showPreferences()
{
   KasPrefsDialog *dlg = new KasPrefsDialog( master_ ? master_ : this );
   dlg->exec();
   delete dlg;

   readConfig();
}

void KasTasker::showAbout()
{
  KasAboutDialog *dlg = new KasAboutDialog( 0L );
  dlg->exec();
  delete dlg;
}


