/*
 * iconaction.cpp - the QAction subclass that uses Icons and supports animation
 * Copyright (C) 2003-2004  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 "iconaction.h"

#include "iconwidget.h"
#include "iconset.h"

#include <qlayout.h>
#include <qpopupmenu.h>
#include <qtooltip.h>
#include <qobjectlist.h>
#include <qtimer.h>

//----------------------------------------------------------------------------
// IconAction
//----------------------------------------------------------------------------

class IconAction::Private : public QObject
{
	Q_OBJECT
public:
	QPtrList<IconToolButton> buttons;
	QMap<QPopupMenu *, int> popups;
	Icon *icon;
	IconAction *action;
	QPopupMenu *popup;

	Private(IconAction *act) {
		icon = 0;
		action = act;
		popup = 0;
	}

	QString menuText() {
		QString txt = action->menuText();

		if ( !action->accel().isEmpty() )
			txt += '\t' + (QString)action->accel();

		return txt;
	}

public slots:
	void menuClicked() {
		action->setOn( !action->isOn() );
		emit action->setOn( action->isOn() );
		emit action->activated(); // required for our lovely status change actions
	}

	void popupDestroyed( QObject *obj ) {
		bool dirty = true;
		while ( dirty ) {
			dirty = false;

			QMap<QPopupMenu *, int>::Iterator it = popups.begin();
			for ( ; it != popups.end(); ++it ) {
				if ( it.key() == obj ) {
					dirty = true;
					popups.remove( it );
					break;
				}
			}
		}
	}
};

IconAction::IconAction(QObject *parent, const char *name)
: QAction(parent, name)
{
	d = new Private(this);
}

IconAction::IconAction(const QString &text, const QString &icon, const QString &menuText, QKeySequence accel, QObject *parent, const char *name, bool toggle)
: QAction(text, menuText, accel, parent, name, toggle)
{
	d = new Private(this);

	setIcon(icon);
}

IconAction::IconAction(const QString &text, const QString &menuText, QKeySequence accel, QObject *parent, const char *name, bool toggle)
: QAction(text, menuText, accel, parent, name, toggle)
{
	d = new Private(this);
}

IconAction::~IconAction()
{
	// delete the buttons list before our own destruction
	d->buttons.setAutoDelete(true);
	d->buttons.clear();

	delete d;
}

const Icon *IconAction::icon() const
{
	return d->icon;
}

void IconAction::setIcon(const Icon *i)
{
	if ( d->icon ) {
		disconnect(d->icon, 0, this, 0 );
		d->icon->stop();
		delete d->icon;
		d->icon = 0;
	}

	QIconSet is;
	if ( i ) {
		d->icon = new Icon(*i);
		connect(d->icon, SIGNAL(iconModified(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
		d->icon->activated(true);

		is = d->icon->iconSet();
	}

	QAction::setIconSet( is );

	for ( QPtrListIterator<IconToolButton> it(d->buttons); it.current(); ++it ) {
		IconToolButton *btn = it.current();
		btn->setIcon ( d->icon );
	}

#ifndef Q_WS_MAC
	is = QIconSet();
#endif

	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
	for ( ; it2 != d->popups.end(); ++it2 ) {
		it2.key()->changeItem( it2.data(), iconSet(), d->menuText() );
	}
}

void IconAction::setIcon(const QString &name)
{
	setIcon( IconsetFactory::iconPtr(name) );
}

const QString &IconAction::iconName() const
{
	if ( d->icon )
		return d->icon->name();
	return QString::null;
}

bool IconAction::addTo(QWidget *w)
{
	if ( w->inherits("QToolBar") || w->isA("QWidget") ) {
		QCString bname = name() + QCString("_action_button");
		IconToolButton *btn = new IconToolButton ( w, bname );
		d->buttons.append ( btn );

		btn->setToggleButton ( isToggleAction() );
		btn->setOn( isOn() );
		btn->setTextLabel ( text() );
		btn->setIcon ( d->icon, false );
		btn->setEnabled ( isEnabled() );

		if ( d->popup )
			btn->setPopup( d->popup );
		btn->setPopupDelay (1); // the popup will be displayed immediately

		QToolTip::add (btn, toolTipFromMenuText());

		if ( w->isA("QWidget") )
			if ( w->layout() )
				w->layout()->add( btn );

		connect(btn, SIGNAL(clicked()), this, SIGNAL(activated()));
		connect(btn, SIGNAL(toggled(bool)), this, SLOT(setOn(bool)));

		connect(btn, SIGNAL(destroyed()), SLOT(objectDestroyed()));

		addingToolButton(btn);
	}
	else if ( w->inherits("QPopupMenu") ) {
		//if ( !isVisible() )
		//	return true;

		QPopupMenu *popup = (QPopupMenu *)w;
		int id;
		QIconSet iconset;
#ifndef Q_WS_MAC
		iconset = iconSet();
#endif
		if ( d->popup )
			id = popup->insertItem (iconset, d->menuText(), d->popup);
		else
			id = popup->insertItem (iconset, d->menuText());

		d->popups.insert( popup, id );
		connect( popup, SIGNAL( destroyed( QObject * ) ), d, SLOT( popupDestroyed( QObject * ) ) );

		popup->setItemEnabled(id, isEnabled());
		popup->setWhatsThis(id, whatsThis());

		popup->setItemVisible(id, isVisible());

		// will not work for actions with d->popup :-/
		if ( isToggleAction() ) {
			popup->setCheckable( true );
			popup->setItemChecked(id, isOn());
			popup->connectItem(id, d, SLOT(menuClicked()));
		}
		else
			popup->connectItem(id, this, SIGNAL(activated()));

		addingMenuItem( popup, id );
	}
	else
		return QAction::addTo(w);

	return true;
}

void IconAction::objectDestroyed()
{
	const QObject *obj = sender();
	d->buttons.removeRef((IconToolButton *)obj);
}

void IconAction::setOn(bool b)
{
	QAction::setOn(b);
	for ( QPtrListIterator<IconToolButton> it(d->buttons); it.current(); ++it ) {
		IconToolButton *btn = it.current();
		btn->setOn(b);
	}

	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
	for ( ; it2 != d->popups.end(); ++it2 ) {
		it2.key()->setItemChecked( it2.data(), b );
	}
}

void IconAction::toolButtonToggled(bool b)
{
	setOn(b);
}

void IconAction::setEnabled(bool e)
{
	QAction::setEnabled(e);
	for ( QPtrListIterator<IconToolButton> it(d->buttons); it.current(); ++it ) {
		IconToolButton *btn = it.current();
		btn->setEnabled (e);
	}

	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
	for ( ; it2 != d->popups.end(); ++it2 ) {
		it2.key()->setItemEnabled( it2.data(), e );
	}
}

void IconAction::setText(const QString &t)
{
	QAction::setText(t);
	for ( QPtrListIterator<IconToolButton> it(d->buttons); it.current(); ++it ) {
		IconToolButton *btn = it.current();
		btn->setTextLabel(t);
	}

#ifndef Q_WS_MAC
	QIconSet is = iconSet();
#else
	QIconSet is;
#endif

	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
	for ( ; it2 != d->popups.end(); ++it2 ) {
		it2.key()->changeItem( it2.data(), is, d->menuText() );
	}
}

QPtrList<IconToolButton> IconAction::buttonList()
{
	return d->buttons;
}

void IconAction::iconUpdated(const QPixmap &pix)
{
	QAction::setIconSet( d->icon->iconSet() );

#ifndef Q_WS_MAC
	QPixmap p = pix;
#else
	Q_UNUSED( pix );
	QPixmap p;
#endif

	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
	for ( ; it2 != d->popups.end(); ++it2 ) {
		it2.key()->changeItem( it2.data(), p, d->menuText() );
	}
}

QString IconAction::toolTipFromMenuText() const
{
	QString tt, str = menuText();
	for (int i = 0; i < (int)str.length(); i++)
		if ( str[i] == '&' && str[i+1] != '&' )
			continue;
		else
			tt += str[i];

	return tt;
}

QPopupMenu *IconAction::popup() const
{
	return d->popup;
}

void IconAction::setPopup( QPopupMenu *p )
{
	d->popup = p;

	QPtrList<IconToolButton> btns = buttonList();
	for ( QPtrListIterator<IconToolButton> it(btns); it.current(); ++it ) {
		QToolButton *btn = it.current();

		btn->setPopup (0);

		if ( p )
			btn->setPopup ( p );
	}
}

void IconAction::setIconSet( const QIconSet &ic )
{
	QAction::setIconSet( ic );

	QPtrList<IconToolButton> btns = buttonList();
	for ( QPtrListIterator<IconToolButton> it(btns); it.current(); ++it ) {
		QToolButton *btn = it.current();

		btn->setIconSet( ic );
	}

#ifndef Q_WS_MAC
	QIconSet is = ic;
#else
	QIconSet is;
#endif

	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
	for ( ; it2 != d->popups.end(); ++it2 ) {
		it2.key()->changeItem( it2.data(), is, d->menuText() );
	}
}

void IconAction::setVisible( bool b )
{
	QAction::setVisible( b );

	QPtrList<IconToolButton> btns = buttonList();
	for ( QPtrListIterator<IconToolButton> it(btns); it.current(); ++it ) {
		QToolButton *btn = it.current();

		if ( b )
			btn->show();
		else
			btn->hide();
	}

	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
	for ( ; it2 != d->popups.end(); ++it2 ) {
		it2.key()->setItemVisible( it2.data(), b );
	}
}

IconAction *IconAction::copy() const
{
	IconAction *act = new IconAction(text(), iconName(), menuText(), accel(), 0, name(), isToggleAction());

	*act = *this;

	return act;
}

IconAction &IconAction::operator=( const IconAction &from )
{
	setText( from.text() );
	setIcon( from.iconName() );
	setMenuText( from.menuText() );
	setAccel( from.accel() );
	setName( from.name() );
	setToggleAction( from.isToggleAction() );
	setWhatsThis( whatsThis() );

	// TODO: add more

	return *this;
}

//----------------------------------------------------------------------------
// IconActionGroup
//----------------------------------------------------------------------------

class IconActionGroup::Private : public QObject
{
	Q_OBJECT
public:
	Private(IconActionGroup *_group) {
		group = _group;
		dirty = false;
	}

	IconActionGroup *group;

	QPopupMenu *popup;

	bool exclusive;
	bool usesDropDown;

	bool dirty;

public slots:
	void updatePopup();
};

void IconActionGroup::Private::updatePopup()
{
	if ( !dirty )
		return;

	if ( !usesDropDown )
		qWarning("IconActionGroup does not support !usesDropDown yet");

	popup->clear();

	QObjectList *l = group->queryList("QAction");
	QObjectListIt it( *l );
	QObject *obj;
	for ( ; (obj = it.current()); ++it) {
		QAction *action = (QAction *)obj;

		if ( !group->icon() && action->inherits( "IconAction" ) )
			group->setIcon( ((IconAction * )action)->icon() );

		action->addTo( popup );
	}
	delete l;

	group->setPopup( popup );
	dirty = false;
}

IconActionGroup::IconActionGroup(QObject *parent, const char *name, bool exclusive)
	: IconAction( parent, name )
{
	d = new Private(this);
	d->popup = new QPopupMenu();

	d->exclusive = exclusive;
}

IconActionGroup::~IconActionGroup()
{
	delete d->popup;
	delete d;
}

void IconActionGroup::insertChild( QObject *obj )
{
	IconAction::insertChild( obj );

	d->dirty = true;
	QTimer::singleShot( 0, d, SLOT( updatePopup() ) );
}

void IconActionGroup::removeChild( QObject *obj )
{
	IconAction::removeChild( obj );

	d->dirty = true;
	QTimer::singleShot( 0, d, SLOT( updatePopup() ) );
}

void IconActionGroup::add( QAction * )
{
	qWarning("IconActionGroup::add(): not implemented");
}

void IconActionGroup::addSeparator()
{
       QAction *separatorAction = new QAction(this, "qt_separator_action");
       Q_UNUSED( separatorAction );
}

bool IconActionGroup::addTo( QWidget *w )
{
	if ( w->inherits("QPopupMenu") ) {
		QPopupMenu *popup = (QPopupMenu *)w;

		QObjectList *l = queryList("QAction");
		QObjectListIt it( *l );
		QObject *obj;
		for ( ; (obj = it.current()); ++it) {
			QAction *action = (QAction *)obj;

			action->addTo( popup );
		}
		delete l;

		return true;
	}

	return IconAction::addTo( w );
}

IconAction *IconActionGroup::copy() const
{
	qWarning("IconActionGroup::copy() doesn't work!");
	return (IconAction *)this;
}

void IconActionGroup::setExclusive( bool e )
{
	d->exclusive = e;
}

bool IconActionGroup::isExclusive() const
{
	return d->exclusive;
}

void IconActionGroup::setUsesDropDown( bool u )
{
	d->usesDropDown = u;
}

bool IconActionGroup::usesDropDown() const
{
	return d->usesDropDown;
}

void IconActionGroup::addingToolButton(IconToolButton *btn)
{
	btn->setPopupDelay( 0 );
}

#include "iconaction.moc"
