/*
 * iconwidget.cpp - misc. Iconset- and Icon-aware widgets
 * Copyright (C) 2003  Michail Pishchagin
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "iconwidget.h"

#include <qapplication.h>
#include <qpainter.h>

#ifndef WIDGET_PLUGIN
#	include "iconset.h"
#	include <qstyle.h>
#	include <qbitmap.h>
#	include <qmap.h>
#else
#	include <qimage.h>

static const char *cancel_xpm[] = {
"22 22 60 1",
" 	c None",
".	c #E84300",
"+	c #E63F00",
"@	c #D11E00",
"#	c #D11B00",
"$	c #F69D50",
"%	c #F59A4D",
"&	c #E23800",
"*	c #EE5F1F",
"=	c #ED5A1D",
"-	c #CD1700",
";	c #FECBA2",
">	c #FEC69A",
",	c #F39045",
"'	c #DE3200",
")	c #FE7B3C",
"!	c #FE7234",
"~	c #EC4C15",
"{	c #CC1100",
"]	c #FEC091",
"^	c #FEBA89",
"/	c #F2873D",
"(	c #DA2C00",
"_	c #FE692C",
":	c #EB4712",
"<	c #CA0F00",
"[	c #FEB480",
"}	c #FEAE78",
"|	c #F07D35",
"1	c #D62600",
"2	c #FEA870",
"3	c #FEA166",
"4	c #EF722D",
"5	c #D32100",
"6	c #FE9B5F",
"7	c #FE9356",
"8	c #F16C2A",
"9	c #F16525",
"0	c #FE8B4D",
"a	c #FE8445",
"b	c #EE4B15",
"c	c #FE6025",
"d	c #EE4310",
"e	c #C90E00",
"f	c #FE561D",
"g	c #FE4B16",
"h	c #EA2F08",
"i	c #C70900",
"j	c #FE4010",
"k	c #FE350B",
"l	c #EA1D03",
"m	c #C60700",
"n	c #FE2906",
"o	c #FE1A02",
"p	c #E90900",
"q	c #C50300",
"r	c #FE0A00",
"s	c #FE0000",
"t	c #E90000",
"u	c #C40000",
"                      ",
"                      ",
"    .+          @#    ",
"   .$%&        @*=-   ",
"  .$;>,'      @*)!~{  ",
"  +%>]^/(    @*)!_:<  ",
"   &,^[}|1  @*)!_:<   ",
"    '/}2345@*)!_:<    ",
"     (|36789)!_:<     ",
"      1470a)!_:<      ",
"       58a)!_b<       ",
"       @9)!_cde       ",
"      @*)!_cfghi      ",
"     @*)!_bdgjklm     ",
"    @*)!_:<ehknopq    ",
"   @*)!_:<  ilorstu   ",
"  @*)!_:<    mpssstu  ",
"  #=!_:<      qtsstu  ",
"   -~:<        uttu   ",
"    {<          uu    ",
"                      ",
"                      "};

// dammit MSVC6
class Iconset {
public:
	Iconset() { }
};
#endif

//----------------------------------------------------------------------------
// IconsetSelect
//----------------------------------------------------------------------------

class IconsetSelectItem : public IconWidgetItem
{
	Q_OBJECT
private:
	static const int margin;
	static const int displayNumIcons;
#ifndef WIDGET_PLUGIN
	Iconset iss;
	QMap<Icon*, QRect> iconRects;
#endif
	int w, h;
	mutable int fullW, fullH;

public:
	IconsetSelectItem(QListBox *parent, IconsetSelectItem *after, const Iconset &_iconset)
	: IconWidgetItem(parent, after)
	{
#ifndef WIDGET_PLUGIN
		iss = _iconset;
		setText( iss.name() );

		w = margin;
		h = 2*margin;

		int count;

		QPtrListIterator<Icon> it = iss.iterator();
		for (count = 0; it.current(); ++it) {
			if ( count++ >= displayNumIcons )
				break; // display only first displayNumIcons icons

			QPixmap pix = it.current()->pixmap();

			iconRects[it.current()] = QRect( w, margin, pix.width(), pix.height() );

			w += pix.width() + margin;
			h = QMAX( h, pix.height() + 2*margin );

			connect (it.current(), SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
			it.current()->activated(false); // start animation
		}

		QMap<Icon*, QRect>::Iterator it2;
		for (it2 = iconRects.begin(); it2 != iconRects.end(); it2++) {
			QRect r = it2.data();
			it2.data() = QRect( r.x(), (h - r.height())/2, r.width(), r.height() );
		}
#else
		Q_UNUSED( _iconset );
#endif
	}

	~IconsetSelectItem()
	{
#ifndef WIDGET_PLUGIN
		QMap<Icon*, QRect>::Iterator it;
		for (it = iconRects.begin(); it != iconRects.end(); it++)
			it.key()->stop();
#endif
	}

	const Iconset *iconset() const
	{
#ifndef WIDGET_PLUGIN
		return &iss;
#else
		return 0;
#endif
	}

	int height( const QListBox *lb ) const
	{
		fullH = lb->fontMetrics().lineSpacing() + 2 + h;
		return QMAX( fullH, QApplication::globalStrut().height() );
	}

	int width( const QListBox *lb ) const
	{
		fullW = QMAX(lb->fontMetrics().width( text() ) + 6, w + 10);
		return QMAX( fullW, QApplication::globalStrut().width() );
	}

	void paint(QPainter *painter)
	{
#ifndef WIDGET_PLUGIN
		QFontMetrics fm = painter->fontMetrics();
		painter->drawText( 3, fm.ascent() + (fm.leading()+1)/2 + 1, text() );

		QMap<Icon*, QRect>::Iterator it;
		for (it = iconRects.begin(); it != iconRects.end(); it++) {
			Icon *icon = it.key();
			QRect r = it.data();
			painter->drawPixmap(QPoint(10 + r.left(), fm.lineSpacing() + 2 + r.top()), icon->pixmap());
		}
#else
		Q_UNUSED(painter);
#endif
	}

private slots:
	void iconUpdated(const QPixmap &)
	{
#ifndef WIDGET_PLUGIN
		IconsetSelect *issel = (IconsetSelect *)listBox();
		issel->updateItem (this);
#endif
	}
};
const int IconsetSelectItem::margin = 3;
const int IconsetSelectItem::displayNumIcons = 10;

class IconsetSelect::Private
{
public:
	Private()
	{
		lastItem = 0;
	}

	IconsetSelectItem *lastItem;
};

IconsetSelect::IconsetSelect(QWidget *parent, const char *name)
: QListBox(parent, name)
{
	d = new Private;
}

IconsetSelect::~IconsetSelect()
{
	delete d;
}

void IconsetSelect::insert(const Iconset &iconset)
{
#ifndef WIDGET_PLUGIN
	IconsetSelectItem *item = new IconsetSelectItem(this, d->lastItem, iconset);
	d->lastItem = item;
#else
	Q_UNUSED(iconset);
#endif
}

void IconsetSelect::moveItemUp()
{
	if ( currentItem() < 1 )
		return;

	IconsetSelectItem *i = (IconsetSelectItem *)item(currentItem());
	if ( !i )
		return;
	QListBoxItem *prev = i->prev()->prev();
	takeItem (i);
	insertItem (i, prev);
	setSelected (i, true);
	setCurrentItem (i);
}

void IconsetSelect::moveItemDown()
{
	if ( currentItem() == -1 || currentItem() > (int)count() - 2 )
		return;

	IconsetSelectItem *i = (IconsetSelectItem *)item(currentItem());
	if ( !i )
		return;
	QListBoxItem *next = i->next();
	takeItem (i);
	insertItem (i, next);
	setCurrentItem (i);
}

const Iconset *IconsetSelect::iconset() const
{
	IconsetSelectItem *i = (IconsetSelectItem *)selectedItem();
	if ( !i )
		i = (IconsetSelectItem *)item(currentItem());
	if ( i )
		return i->iconset();
	return 0;
}

void IconsetSelect::paintCell(QPainter *p, int row, int col)
{
	// we'll do some caching to avoid flicker
	QListBoxItem *item = QListBox::item(row);

	if ( !item ) {
		QListBox::paintCell(p, row, col);
		return;
	}

	int w = contentsWidth();
	int h = item->height(this);
	QPixmap pix(w, h);
	QPainter p2;
	p2.begin (&pix);
	QListBox::paintCell(&p2, row, col);
	p2.end ();

	p->drawPixmap(QPoint(0, 0), pix);
}


//----------------------------------------------------------------------------
// IconsetDisplay
//----------------------------------------------------------------------------

class IconsetDisplayItem : public IconWidgetItem
{
	Q_OBJECT
private:
	static const int margin;
	Icon *icon;
	int w, h;

public:
	IconsetDisplayItem(QListBox *parent, IconsetDisplayItem *after, Icon *i, int iconW)
	: IconWidgetItem(parent, after)
	{
#ifndef WIDGET_PLUGIN
		icon = i;
		w = iconW;

		connect (icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
		icon->activated(false);

		h = icon->pixmap().height();

		QString str;
		QDictIterator<QString> it ( icon->text() );
		for ( ; it.current(); ++it) {
			if ( !str.isEmpty() )
				str += ", ";
			str += **it;
		}
		if ( !str.isEmpty() )
			setText(str);
		else
			setText(tr("Name: '%1'").arg(icon->name()));
#else
		Q_UNUSED( i );
		Q_UNUSED( iconW );
#endif
	}

	~IconsetDisplayItem()
	{
#ifndef WIDGET_PLUGIN
		icon->stop();
#endif
	}

	int height( const QListBox *lb ) const
	{
		int hh = QMAX(h + 2*margin, lb->fontMetrics().lineSpacing() + 2);
		return QMAX( hh, QApplication::globalStrut().height() );
	}

	int width( const QListBox *lb ) const
	{
		int ww = lb->fontMetrics().width( text() ) + 6 + w + 2*margin;
		return QMAX( ww, QApplication::globalStrut().width() );
	}

	void paint(QPainter *painter)
	{
#ifndef WIDGET_PLUGIN
		painter->drawPixmap(QPoint((2*margin+w - icon->pixmap().width())/2, margin), icon->pixmap());
		QFontMetrics fm = painter->fontMetrics();
		//int hh = QMAX(h + 2*margin, fm.lineSpacing() + 2);
		painter->drawText( w + 2*margin + 3, fm.ascent() + (fm.leading()+1)/2 + 1, text() );
		//painter->drawText( w + 2*margin + 3, (hh - fm.lineSpacing())/2, text() );
#else
		Q_UNUSED(painter);
#endif
	}

private slots:
	void iconUpdated(const QPixmap &)
	{
		IconsetDisplay *issel = (IconsetDisplay *)listBox();
		issel->updateItem (this);
	}
};
const int IconsetDisplayItem::margin = 3;

class IconsetDisplay::Private
{
public:
	Private()
	{
		lastItem = 0;
	}

	IconsetDisplayItem *lastItem;
};

IconsetDisplay::IconsetDisplay(QWidget *parent, const char *name)
: QListBox(parent, name, WStaticContents | WResizeNoErase | WRepaintNoErase)
{
	d = new Private;
}

IconsetDisplay::~IconsetDisplay()
{
	delete d;
}

void IconsetDisplay::setIconset(const Iconset &iconset)
{
#ifndef WIDGET_PLUGIN
	int w = 0;
	QPtrListIterator<Icon> it = iconset.iterator();
	for ( ; it.current(); ++it) {
		w = QMAX(w, it.current()->pixmap().width());
	}

	it = iconset.iterator();
	for ( ; it.current(); ++it) {
		IconsetDisplayItem *item = new IconsetDisplayItem(this, d->lastItem, it.current(), w);
		d->lastItem = item;
	}
#else
	Q_UNUSED(iconset);
#endif
}

void IconsetDisplay::paintCell(QPainter *p, int row, int col)
{
	// we'll do some caching to avoid flicker
	QListBoxItem *item = QListBox::item(row);

	if ( !item ) {
		QListBox::paintCell(p, row, col);
		return;
	}

	int w = contentsWidth();
	int h = item->height(this);
	QPixmap pix(w, h);
	QPainter p2;
	p2.begin (&pix);
	QListBox::paintCell(&p2, row, col);
	p2.end ();

	p->drawPixmap(QPoint(0, 0), pix);
}


//----------------------------------------------------------------------------
// IconButton
//----------------------------------------------------------------------------

class IconButton::Private : public QObject
{
	Q_OBJECT
public:
	Icon *icon;
	IconButton *button;
	bool textVisible;
	bool activate, forced;
#ifdef WIDGET_PLUGIN
	QString iconName;
#endif

public:
	Private(IconButton *b)
	{
		icon = 0;
		button = b;
		textVisible = true;
		forced = false;
	}

	~Private()
	{
		iconStop();
	}

	void setIcon(Icon *i)
	{
#ifndef WIDGET_PLUGIN
		iconStop();
		if ( i )
			icon = new Icon(*i);
		iconStart();
#else
		Q_UNUSED(i);
#endif
	}

	void iconStart()
	{
#ifndef WIDGET_PLUGIN
		if ( icon ) {
			connect(icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
			if ( activate )
				icon->activated(true); // FIXME: should icon play sound when it's activated on button?
		}

		updateIcon();
#endif
	}

	void iconStop()
	{
#ifndef WIDGET_PLUGIN
		if ( icon ) {
			disconnect(icon, 0, this, 0 );
			if ( activate )
				icon->stop();

			delete icon;
			icon = 0;
		}
#endif
	}

	void update()
	{
#ifndef WIDGET_PLUGIN
		if ( icon )
			iconUpdated( icon->pixmap() );
#endif
	}

	void updateIcon()
	{
#ifndef WIDGET_PLUGIN
		if ( icon )
			iconUpdated( icon->pixmap() );
		else
			iconUpdated( QPixmap() );
#endif
	}

public slots:
	void iconUpdated(const QPixmap &pix)
	{
		button->setUpdatesEnabled(FALSE);
		if ( textVisible || button->text().isEmpty() )
			button->setIconSet(pix);
		else
			button->setPixmap(pix);
		button->setUpdatesEnabled(TRUE);
		button->update();
	}
};

IconButton::IconButton(QWidget *parent, const char *name)
: QPushButton(parent, name)
{
	setWFlags(getWFlags() | WRepaintNoErase); // no nasty flicker anymore :)
	d = new Private(this);
}

IconButton::~IconButton()
{
	delete d;
}

void IconButton::setIcon(const QPixmap &p)
{
	QPushButton::setIcon(p);
}

void IconButton::forceSetIcon(const Icon *i, bool activate)
{
	d->activate = activate;
	d->setIcon ((Icon *)i);
	d->forced = true;
}

void IconButton::setIcon(const Icon *i, bool activate)
{
#ifndef Q_WS_X11
	if ( !text().isEmpty() )
		return;
#endif

	forceSetIcon(i, activate);
	d->forced = false;
}

void IconButton::setIcon(const QString &name)
{
#ifndef WIDGET_PLUGIN
	setIcon( IconsetFactory::iconPtr(name) );
#else
	d->iconName = name;

	if ( !name.isEmpty() ) {
		QPixmap pix((const char **)cancel_xpm);
		d->iconUpdated(QPixmap( pix ));
	}
	else
		d->iconUpdated(QPixmap());
#endif
}

const QString &IconButton::iconName() const
{
#ifndef WIDGET_PLUGIN
	if ( d->icon )
		return d->icon->name();
	return QString::null;
#else
	return d->iconName;
#endif
}

void IconButton::setText(const QString &text)
{
#ifndef Q_WS_X11
	if ( !d->forced )
		setIcon(0);
#endif

	QPushButton::setText( text );
	d->updateIcon();
}

bool IconButton::textVisible() const
{
	return d->textVisible;
}

void IconButton::setTextVisible(bool v)
{
	d->textVisible = v;
	d->updateIcon();
}

void IconButton::drawButtonLabel(QPainter *p)
{
	QPushButton::drawButtonLabel(p);
}

//----------------------------------------------------------------------------
// IconToolButton
//----------------------------------------------------------------------------

class IconToolButton::Private : public QObject
{
	Q_OBJECT
public:
	Icon *icon;
	IconToolButton *button;
	bool activate;
#ifdef WIDGET_PLUGIN
	QString iconName;
#endif

public:
	Private(IconToolButton *b)
	{
		icon = 0;
		button = b;
	}

	~Private()
	{
		iconStop();
	}

	void setIcon(Icon *i)
	{
#ifndef WIDGET_PLUGIN
		iconStop();
		if ( i )
			icon = new Icon(*i);
		iconStart();
#else
		Q_UNUSED(i);
#endif
	}

	void iconStart()
	{
#ifndef WIDGET_PLUGIN
		if ( icon ) {
			connect(icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
			if ( activate )
				icon->activated(true); // FIXME: should icon play sound when it's activated on button?
			iconUpdated( icon->pixmap() );
		}
		else
			iconUpdated( QPixmap() );
#endif
	}

	void iconStop()
	{
#ifndef WIDGET_PLUGIN
		if ( icon ) {
			disconnect(icon, 0, this, 0 );
			if ( activate )
				icon->stop();

			delete icon;
			icon = 0;
		}
#endif
	}

	void update()
	{
#ifndef WIDGET_PLUGIN
		if ( icon )
			iconUpdated( icon->pixmap() );
#endif
	}

private slots:
	void iconUpdated(const QPixmap &pix)
	{
		button->setUpdatesEnabled(FALSE);
		//if ( textVisible )
			button->setIconSet(pix);
		//else
		//	button->setPixmap(pix);
		button->setUpdatesEnabled(TRUE);
		button->update();
	}
};

IconToolButton::IconToolButton(QWidget *parent, const char *name)
: QToolButton(parent, name)
{
	setWFlags(getWFlags() | WRepaintNoErase);
	d = new Private(this);
}

IconToolButton::~IconToolButton()
{
	delete d;
}

void IconToolButton::setIcon(const QPixmap &p)
{
	QToolButton::setIcon(p);
}

void IconToolButton::setIcon(const Icon *i, bool activate)
{
	d->activate = activate;
	d->setIcon ((Icon *)i);
}

void IconToolButton::setIcon(const QString &name)
{
#ifndef WIDGET_PLUGIN
	setIcon( IconsetFactory::iconPtr(name) );
#else
	d->iconName = name;
#endif
}

const QString &IconToolButton::iconName() const
{
#ifndef WIDGET_PLUGIN
	if ( d->icon )
		return d->icon->name();
	return QString::null;
#else
	return d->iconName;
#endif
}

void IconToolButton::drawButtonLabel(QPainter *p)
{
	QToolButton::drawButtonLabel(p);
}

#include "iconwidget.moc"
