/***************************************************************************
*   Copyright (C) 2004 by                                                 *
*   Jason Kivlighn (jkivlighn@gmail.com)                                  *
*   Unai Garro (ugarro@users.sourceforge.net)                             *
*                                                                         *
*   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.                                   *
***************************************************************************/

#ifndef CATEGORYLISTVIEW_H
#define CATEGORYLISTVIEW_H

#include <tqmap.h>
#include <tqpixmap.h>

#include "dblistviewbase.h"

#include "datablocks/elementlist.h"

class TDEPopupMenu;

class RecipeDB;
class CategoryTree;
class CategoryCheckListView;

#define CATEGORYCHECKLISTITEM_RTTI 1005
#define CATEGORYLISTITEM_RTTI 1001
#define PSEUDOLISTITEM_RTTI 1008

/** Category listitems inherit this class to provide a common interface for accessing this information.
 */
class CategoryItemInfo
{
public:
	CategoryItemInfo( const Element &category ) : ctyStored( category ), populated(false){}
	bool isPopulated() const { return populated; }
	void setPopulated( bool b ){ populated = b; }

	Element element() const
	{
		return ctyStored;
	}

	int categoryId() const
	{
		return ctyStored.id;
	}
	TQString categoryName() const
	{
		return ctyStored.name;
	}

protected:
	Element ctyStored;

private:
	bool populated;
};

class CategoryCheckListItem : public TQCheckListItem, public CategoryItemInfo
{
public:
	CategoryCheckListItem( CategoryCheckListView* klv, const Element &category, bool exclusive = true );
	CategoryCheckListItem( TQListViewItem* it, const Element &category, bool exclusive = true );
	CategoryCheckListItem( CategoryCheckListView* klv, TQListViewItem* it, const Element &category, bool exclusive = true );

	virtual TQString text( int column ) const;
	virtual void setText( int column, const TQString &text );

	int rtti() const
	{
		return CATEGORYCHECKLISTITEM_RTTI;
	}

protected:
	virtual void stateChange( bool );
	void setChildrenState( bool );
	void setParentsState( bool );

	bool locked;
	bool exclusive;

private:
	CategoryCheckListView *m_listview;
};


class CategoryListItem : public TQListViewItem, public CategoryItemInfo
{
public:
	CategoryListItem( TQListView* klv, const Element &category );
	CategoryListItem( TQListViewItem* it, const Element &category );
	CategoryListItem( TQListView* klv, TQListViewItem* it, const Element &category );

	virtual TQString text( int column ) const;
	virtual void setText( int column, const TQString &text );
	
	int rtti() const
	{
		return CATEGORYLISTITEM_RTTI;
	}
};



class CategoryListView : public DBListViewBase
{
	TQ_OBJECT

public:
	CategoryListView( TQWidget *parent, RecipeDB * );

	void populateAll( TQListViewItem *parent = 0 );

public slots:
	void open( TQListViewItem *item );

protected:
	virtual void init();

	virtual void load( int limit, int offset );

	/** so that it allows dropping into
	         * subchildren that aren't expandable.  The code is taken from TDE's TDEListView with
	         * one line commented out.
	  */
	void findDrop( const TQPoint &pos, TQListViewItem *&parent, TQListViewItem *&after )
	{
		TQPoint p ( contentsToViewport( pos ) );

		// Get the position to put it in
		TQListViewItem *atpos = itemAt( p );

		TQListViewItem *above;
		if ( !atpos )  // put it at the end
			above = lastItem();
		else {
			// Get the closest item before us ('atpos' or the one above, if any)
			if ( p.y() - itemRect( atpos ).topLeft().y() < ( atpos->height() / 2 ) )
				above = atpos->itemAbove();
			else
				above = atpos;
		}

		if ( above ) {
			// if above has children, I might need to drop it as the first item there

			if ( above->firstChild() && above->isOpen() ) {
				parent = above;
				after = 0;
				return ;
			}

			// Now, we know we want to go after "above". But as a child or as a sibling ?
			// We have to ask the "above" item if it accepts children.
			// ### NOTE: Here is the one line commented out so that "above" always accepts children
			//if (above->isExpandable())
			{
				// The mouse is sufficiently on the right ? - doesn't matter if 'above' has visible children
				if ( p.x() >= depthToPixels( above->depth() + 1 ) ||
				        ( above->isOpen() && above->childCount() > 0 ) )
				{
					parent = above;
					after = 0L;
					return ;
				}
			}

			// Ok, there's one more level of complexity. We may want to become a new
			// sibling, but of an upper-level group, rather than the "above" item
			TQListViewItem * betterAbove = above->parent();
			TQListViewItem * last = above;
			while ( betterAbove ) {
				// We are allowed to become a sibling of "betterAbove" only if we are
				// after its last child
				if ( last->nextSibling() == 0 ) {
					if ( p.x() < depthToPixels ( betterAbove->depth() + 1 ) )
						above = betterAbove; // store this one, but don't stop yet, there may be a better one
					else
						break; // not enough on the left, so stop
					last = betterAbove;
					betterAbove = betterAbove->parent(); // up one level
				}
				else
					break; // we're among the child of betterAbove, not after the last one
			}
		}
		// set as sibling
		after = above;
		parent = after ? after->parent() : 0L ;
	}

protected slots:
	virtual void removeCategory( int id ) = 0;
	virtual void createCategory( const Element &category, int parent_id ) = 0;
	virtual void modifyCategory( const Element &category );
	virtual void modifyCategory( int id, int parent_id );
	virtual void mergeCategories( int id1, int id2 );

	virtual void checkCreateCategory( const Element &, int parent_id );
	virtual void populate( TQListViewItem *item );

	TQMap<int, TQListViewItem*> items_map;

private:
	TQListViewItem *m_item_to_delete;
};


class StdCategoryListView : public CategoryListView
{
	TQ_OBJECT

public:
	StdCategoryListView( TQWidget *parent, RecipeDB *, bool editable = false );
	~StdCategoryListView();

protected:
	virtual void removeCategory( int id );
	virtual void createCategory( const Element &category, int parent_id );

	void setPixmap( const TQPixmap &pixmap );

private slots:
	void preparePopup();
	void showPopup( TDEListView *, TQListViewItem *, const TQPoint & );

	void createNew();
	void remove
		();
	void rename();
	void cut();
	void paste();
	void pasteAsSub();

	void changeCategoryParent( TQListViewItem *item, TQListViewItem * /*afterFirst*/, TQListViewItem * /*afterNow*/ );

	void modCategory( TQListViewItem* i );
	void saveCategory( TQListViewItem* i );

private:
	bool checkBounds( const TQString &name );

	TDEPopupMenu *kpop;
	TQListViewItem *clipboard_item;
	TQListViewItem *clipboard_parent;
	
	TQPixmap m_folder_icon;
};


class CategoryCheckListView : public CategoryListView
{
	TQ_OBJECT

public:
	CategoryCheckListView( TQWidget *parent, RecipeDB *, bool exclusive=true, const ElementList &init_items_checked = ElementList() );

	virtual void stateChange( CategoryCheckListItem*, bool );

	ElementList selections() const{ return m_selections; }

protected:
	virtual void removeCategory( int id );
	virtual void createCategory( const Element &category, int parent_id );

	virtual void load( int limit, int offset );

	bool exclusive;

private:
	ElementList m_selections;
};


class PseudoListItem : public TQListViewItem
{
public:
	PseudoListItem( TQListView* lv ) : TQListViewItem(lv){}
	PseudoListItem( TQListViewItem* it ) : TQListViewItem(it){}

protected:
	int rtti() const { return PSEUDOLISTITEM_RTTI; }
};

#endif //CATEGORYLISTVIEW_H
