summaryrefslogtreecommitdiffstats
path: root/src/widgets/categorylistview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/categorylistview.cpp')
-rw-r--r--src/widgets/categorylistview.cpp637
1 files changed, 637 insertions, 0 deletions
diff --git a/src/widgets/categorylistview.cpp b/src/widgets/categorylistview.cpp
new file mode 100644
index 0000000..2ebe6e0
--- /dev/null
+++ b/src/widgets/categorylistview.cpp
@@ -0,0 +1,637 @@
+
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn ([email protected]) *
+* Unai Garro ([email protected]) *
+* *
+* 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. *
+***************************************************************************/
+
+#include "categorylistview.h"
+
+#include <tdelocale.h>
+#include <tdemessagebox.h>
+#include <kiconloader.h>
+#include <tdepopupmenu.h>
+#include <tdeconfig.h>
+#include <tdeglobal.h>
+#include <kdebug.h>
+
+#include "backends/recipedb.h"
+#include "datablocks/categorytree.h"
+#include "dialogs/createcategorydialog.h"
+#include "dialogs/dependanciesdialog.h"
+
+CategoryCheckListItem::CategoryCheckListItem( CategoryCheckListView* klv, const Element &category, bool _exclusive ) : TQCheckListItem( klv, TQString::null, TQCheckListItem::CheckBox ), CategoryItemInfo( category ),
+ locked( false ),
+ exclusive( _exclusive ),
+ m_listview(klv)
+{
+ setOn( false ); // Set unchecked by default
+}
+
+CategoryCheckListItem::CategoryCheckListItem( TQListViewItem* it, const Element &category, bool _exclusive ) : TQCheckListItem( it, TQString::null, TQCheckListItem::CheckBox ), CategoryItemInfo( category ),
+ locked( false ),
+ exclusive( _exclusive ),
+ m_listview((CategoryCheckListView*)it->listView())
+{
+ setOn( false ); // Set unchecked by default
+}
+
+CategoryCheckListItem::CategoryCheckListItem( CategoryCheckListView* klv, TQListViewItem* it, const Element &category, bool _exclusive ) : TQCheckListItem( klv, it, TQString::null, TQCheckListItem::CheckBox ), CategoryItemInfo( category ),
+ locked( false ),
+ exclusive( _exclusive ),
+ m_listview(klv)
+{
+ setOn( false ); // Set unchecked by default
+}
+
+TQString CategoryCheckListItem::text( int column ) const
+{
+ if ( column == 1 )
+ return ( TQString::number( ctyStored.id ) );
+ else
+ return ( ctyStored.name );
+}
+
+void CategoryCheckListItem::setText( int column, const TQString &text )
+{
+ switch ( column ) {
+ case 0:
+ ctyStored.name = text;
+ break;
+ default:
+ break;
+ }
+}
+
+void CategoryCheckListItem::stateChange( bool on )
+{
+ m_listview->stateChange(this,on);
+
+ if ( locked )
+ return;
+
+ if ( on && exclusive ) {
+ setParentsState( false );
+ setChildrenState( false );
+ }
+}
+
+void CategoryCheckListItem::setChildrenState( bool on )
+{
+ if ( !isPopulated() )
+ return;
+
+ for ( CategoryCheckListItem * cat_it = ( CategoryCheckListItem* ) firstChild(); cat_it; cat_it = ( CategoryCheckListItem* ) cat_it->nextSibling() ) {
+ cat_it->locked = true;
+ cat_it->setOn( on );
+ cat_it->setChildrenState( on );
+ cat_it->locked = false;
+ }
+}
+
+void CategoryCheckListItem::setParentsState( bool on )
+{
+ locked = true;
+
+ CategoryCheckListItem *cat_it;
+ for ( cat_it = ( CategoryCheckListItem* ) parent(); cat_it; cat_it = ( CategoryCheckListItem* ) cat_it->parent() )
+ cat_it->setOn( on );
+
+ locked = false;
+}
+
+
+
+
+CategoryListItem::CategoryListItem( TQListView* klv, const Element &category ) : TQListViewItem( klv ),
+ CategoryItemInfo(category)
+{}
+
+CategoryListItem::CategoryListItem( TQListViewItem* it, const Element &category ) : TQListViewItem( it ),
+ CategoryItemInfo(category)
+{}
+
+CategoryListItem::CategoryListItem( TQListView* klv, TQListViewItem* it, const Element &category ) : TQListViewItem( klv, it ),
+ CategoryItemInfo(category)
+{}
+
+TQString CategoryListItem::text( int column ) const
+{
+ if ( column == 1 )
+ return ( TQString::number( ctyStored.id ) );
+ else
+ return ( ctyStored.name );
+}
+
+void CategoryListItem::setText( int column, const TQString &text )
+{
+ if ( column == 0 )
+ ctyStored.name = text;
+}
+
+
+
+CategoryListView::CategoryListView( TQWidget *parent, RecipeDB *db ) : DBListViewBase( parent, db, db->categoryTopLevelCount() ),
+ m_item_to_delete(0)
+{
+ //connect( this, TQ_SIGNAL( spacePressed(TQListViewItem*) ), TQ_SLOT( open(TQListViewItem*) ) );
+ //connect( this, TQ_SIGNAL( returnPressed(TQListViewItem*) ), TQ_SLOT( open(TQListViewItem*) ) );
+ //connect( this, TQ_SIGNAL( executed(TQListViewItem*) ), TQ_SLOT( open(TQListViewItem*) ) );
+
+ connect( this, TQ_SIGNAL( expanded(TQListViewItem*) ), TQ_SLOT( open(TQListViewItem*) ) );
+
+ setRootIsDecorated( true );
+ setAllColumnsShowFocus( true );
+ setDefaultRenameAction( TQListView::Reject );
+}
+
+void CategoryListView::init()
+{
+ connect( database, TQ_SIGNAL( categoryCreated( const Element &, int ) ), TQ_SLOT( checkCreateCategory( const Element &, int ) ) );
+ connect( database, TQ_SIGNAL( categoryRemoved( int ) ), TQ_SLOT( removeCategory( int ) ) );
+ connect( database, TQ_SIGNAL( categoryModified( const Element & ) ), TQ_SLOT( modifyCategory( const Element & ) ) );
+ connect( database, TQ_SIGNAL( categoryModified( int, int ) ), TQ_SLOT( modifyCategory( int, int ) ) );
+ connect( database, TQ_SIGNAL( categoriesMerged( int, int ) ), TQ_SLOT( mergeCategories( int, int ) ) );
+}
+
+// (Re)loads the data from the database
+void CategoryListView::load( int limit, int offset )
+{
+ items_map.clear();
+
+ CategoryTree list;
+ CategoryTree *p_list = &list;
+ database->loadCachedCategories( &p_list, limit, offset, -1, false );
+
+ setTotalItems(p_list->count());
+
+ for ( CategoryTree * child_it = p_list->firstChild(); child_it; child_it = child_it->nextSibling() ) {
+ createCategory( child_it->category, -1 );
+ }
+}
+
+void CategoryListView::populate( TQListViewItem *item )
+{
+ CategoryItemInfo *cat_item = dynamic_cast<CategoryItemInfo*>(item);
+ if ( !cat_item || cat_item->isPopulated() ) return;
+
+ if ( item->firstChild() && item->firstChild()->rtti() != PSEUDOLISTITEM_RTTI )
+ return;
+
+ delete item->firstChild(); //delete the "pseudo item"
+
+ int id = cat_item->categoryId();
+ cat_item->setPopulated(true);
+
+ CategoryTree categoryTree;
+ database->loadCategories( &categoryTree, -1, 0, id, false );
+
+ for ( CategoryTree * child_it = categoryTree.firstChild(); child_it; child_it = child_it->nextSibling() ) {
+ createCategory( child_it->category, id );
+ }
+}
+
+void CategoryListView::populateAll( TQListViewItem *parent )
+{
+ if ( !parent )
+ parent = firstChild();
+
+ for ( TQListViewItem *item = parent; item; item = item->nextSibling() ) {
+ populate( item );
+ if ( item->firstChild() )
+ populateAll( item->firstChild() );
+ }
+}
+
+void CategoryListView::open( TQListViewItem *item )
+{
+ Q_ASSERT( item );
+ if ( !item->firstChild() || item->firstChild()->rtti() != PSEUDOLISTITEM_RTTI ) return;
+
+ populate(item);
+
+ item->setOpen(true);
+}
+
+void CategoryListView::checkCreateCategory( const Element &el, int parent_id )
+{
+ if ( parent_id != -1 || handleElement(el.name) ) { //only create this category if the base class okays it; allow all non-top-level items
+ createCategory(el,parent_id);
+ }
+}
+
+void CategoryListView::modifyCategory( const Element &category )
+{
+ TQListViewItem * item = items_map[ category.id ];
+
+ if ( item )
+ item->setText( 0, category.name );
+}
+
+void CategoryListView::modifyCategory( int id, int parent_id )
+{
+ TQMap<int,TQListViewItem*>::iterator item_it = items_map.find(id);
+ if ( item_it != items_map.end() ) {
+ TQListViewItem *item = *item_it;
+ Q_ASSERT( item );
+
+ removeElement(item,false);
+ if ( !item->parent() )
+ takeItem( item );
+ else
+ item->parent() ->takeItem( item );
+
+ if ( parent_id == -1 ) {
+ insertItem(item);
+ createElement(item);
+ }
+ else {
+ TQMap<int,TQListViewItem*>::iterator parent_item_it = items_map.find(parent_id);
+ if ( parent_item_it != items_map.end() &&
+ dynamic_cast<CategoryItemInfo*>(*parent_item_it)->isPopulated() ) {
+ (*parent_item_it)->insertItem( item );
+ createElement(item);
+ }
+ else {
+ if ( !(*parent_item_it)->firstChild() )
+ new PseudoListItem( *parent_item_it );
+
+ //removeElement() was already called on this item, so we just delete it
+ //we can't delete it just yet because this function is called by a slot
+ delete m_item_to_delete;
+ m_item_to_delete = item;
+ }
+ }
+ }
+}
+
+void CategoryListView::mergeCategories( int id1, int id2 )
+{
+ TQListViewItem * to_item = items_map[ id1 ];
+ TQListViewItem *from_item = items_map[ id2 ];
+
+ CategoryItemInfo *info_item = dynamic_cast<CategoryItemInfo*>(to_item);
+
+ if ( to_item && info_item->isPopulated() && from_item ) {
+ //note that this takes care of any recipes that may be children as well
+ TQListViewItem *next_sibling;
+ for ( TQListViewItem * it = from_item->firstChild(); it; it = next_sibling ) {
+ next_sibling = it->nextSibling(); //get the sibling before we move the item
+
+ removeElement(it,false);
+ from_item->takeItem( it );
+
+ to_item->insertItem( it );
+ createElement(it);
+ }
+ }
+
+ removeCategory( id2 );
+}
+
+
+StdCategoryListView::StdCategoryListView( TQWidget *parent, RecipeDB *db, bool editable ) : CategoryListView( parent, db ),
+ clipboard_item( 0 ),
+ clipboard_parent( 0 )
+{
+ addColumn( i18n( "Category" ) );
+
+ TDEConfig *config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+
+ if ( editable ) {
+ setRenameable( 0, true );
+ setDragEnabled( true );
+ setAcceptDrops( true );
+
+ TDEIconLoader *il = new TDEIconLoader;
+
+ kpop = new TDEPopupMenu( this );
+ kpop->insertItem( il->loadIcon( "document-new", TDEIcon::NoGroup, 16 ), i18n( "&Create" ), this, TQ_SLOT( createNew() ), CTRL + Key_C );
+ kpop->insertItem( il->loadIcon( "edit-delete", TDEIcon::NoGroup, 16 ), i18n( "&Delete" ), this, TQ_SLOT( remove
+ () ), Key_Delete );
+ kpop->insertItem( il->loadIcon( "edit", TDEIcon::NoGroup, 16 ), i18n( "&Rename" ), this, TQ_SLOT( rename() ), CTRL + Key_R );
+ kpop->insertSeparator();
+ kpop->insertItem( il->loadIcon( "edit-cut", TDEIcon::NoGroup, 16 ), i18n( "Cu&t" ), this, TQ_SLOT( cut() ), CTRL + Key_X );
+ kpop->insertItem( il->loadIcon( "edit-paste", TDEIcon::NoGroup, 16 ), i18n( "&Paste" ), this, TQ_SLOT( paste() ), CTRL + Key_V );
+ kpop->insertItem( il->loadIcon( "edit-paste", TDEIcon::NoGroup, 16 ), i18n( "Paste as Subcategory" ), this, TQ_SLOT( pasteAsSub() ), CTRL + SHIFT + Key_V );
+ kpop->polish();
+
+ delete il;
+
+ connect( kpop, TQ_SIGNAL( aboutToShow() ), TQ_SLOT( preparePopup() ) );
+ connect( this, TQ_SIGNAL( contextMenu( TDEListView *, TQListViewItem *, const TQPoint & ) ), TQ_SLOT( showPopup( TDEListView *, TQListViewItem *, const TQPoint & ) ) );
+ connect( this, TQ_SIGNAL( doubleClicked( TQListViewItem*, const TQPoint &, int ) ), TQ_SLOT( modCategory( TQListViewItem* ) ) );
+ connect( this, TQ_SIGNAL( itemRenamed ( TQListViewItem* ) ), TQ_SLOT( saveCategory( TQListViewItem* ) ) );
+ connect( this, TQ_SIGNAL( moved( TQListViewItem *, TQListViewItem *, TQListViewItem * ) ), TQ_SLOT( changeCategoryParent( TQListViewItem *, TQListViewItem *, TQListViewItem * ) ) );
+ }
+}
+
+StdCategoryListView::~StdCategoryListView()
+{
+ delete clipboard_item;
+}
+
+void StdCategoryListView::setPixmap( const TQPixmap &icon )
+{
+ m_folder_icon = icon;
+}
+
+void StdCategoryListView::preparePopup()
+{
+ //only enable the paste items if clipboard_item isn't null
+ kpop->setItemEnabled( kpop->idAt( 5 ), clipboard_item );
+ kpop->setItemEnabled( kpop->idAt( 6 ), clipboard_item );
+}
+
+void StdCategoryListView::showPopup( TDEListView * /*l*/, TQListViewItem *i, const TQPoint &p )
+{
+ if ( i )
+ kpop->exec( p );
+}
+
+void StdCategoryListView::createNew()
+{
+ ElementList categories;
+ database->loadCategories( &categories );
+ CreateCategoryDialog* categoryDialog = new CreateCategoryDialog( this, categories );
+
+ if ( categoryDialog->exec() == TQDialog::Accepted ) {
+ TQString result = categoryDialog->newCategoryName();
+ int subcategory = categoryDialog->subcategory();
+
+ //check bounds first
+ if ( checkBounds( result ) )
+ database->createNewCategory( result, subcategory ); // Create the new category in the database
+ }
+ delete categoryDialog;
+}
+
+void StdCategoryListView::remove
+ ()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item ) {
+ int id = item->text( 1 ).toInt();
+
+ ElementList recipeDependancies;
+ database->findUseOfCategoryInRecipes( &recipeDependancies, id );
+
+ if ( recipeDependancies.isEmpty() ) {
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "Are you sure you want to delete this category and all its subcategories?" ) ) ) {
+ case KMessageBox::Continue:
+ database->removeCategory( id );
+ break;
+ }
+ return;
+ }
+ else { // need warning!
+ ListInfo info;
+ info.list = recipeDependancies;
+ info.name = i18n("Recipes");
+ DependanciesDialog warnDialog( this, info, false );
+
+ if ( warnDialog.exec() == TQDialog::Accepted )
+ database->removeCategory( id );
+ }
+ }
+}
+
+void StdCategoryListView::rename()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item )
+ CategoryListView::rename( item, 0 );
+}
+
+void StdCategoryListView::cut()
+{
+ //restore a never used cut
+ if ( clipboard_item ) {
+ if ( clipboard_parent )
+ clipboard_parent->insertItem( clipboard_item );
+ else
+ insertItem( clipboard_item );
+ clipboard_item = 0;
+ }
+
+ TQListViewItem *item = currentItem();
+
+ if ( item ) {
+ clipboard_item = item;
+ clipboard_parent = item->parent();
+
+ if ( item->parent() )
+ item->parent() ->takeItem( item );
+ else
+ takeItem( item );
+ }
+}
+
+void StdCategoryListView::paste()
+{
+ TQListViewItem * item = currentItem();
+ if ( item && clipboard_item ) {
+ if ( item->parent() )
+ item->parent() ->insertItem( clipboard_item );
+ else
+ insertItem( clipboard_item );
+
+ database->modCategory( clipboard_item->text( 1 ).toInt(), item->parent() ? item->parent() ->text( 1 ).toInt() : -1 );
+ clipboard_item = 0;
+ }
+}
+
+void StdCategoryListView::pasteAsSub()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item && clipboard_item ) {
+ item->insertItem( clipboard_item );
+ database->modCategory( clipboard_item->text( 1 ).toInt(), item->text( 1 ).toInt() );
+ clipboard_item = 0;
+ }
+}
+
+void StdCategoryListView::changeCategoryParent( TQListViewItem *item, TQListViewItem * /*afterFirst*/, TQListViewItem * /*afterNow*/ )
+{
+ int new_parent_id = -1;
+ if ( TQListViewItem * parent = item->parent() )
+ new_parent_id = parent->text( 1 ).toInt();
+
+ int cat_id = item->text( 1 ).toInt();
+
+ disconnect( TQ_SIGNAL( moved( TQListViewItem *, TQListViewItem *, TQListViewItem * ) ) );
+ database->modCategory( cat_id, new_parent_id );
+ connect( this, TQ_SIGNAL( moved( TQListViewItem *, TQListViewItem *, TQListViewItem * ) ), TQ_SLOT( changeCategoryParent( TQListViewItem *, TQListViewItem *, TQListViewItem * ) ) );
+}
+
+void StdCategoryListView::removeCategory( int id )
+{
+ TQListViewItem * item = items_map[ id ];
+
+ items_map.remove( id );
+ removeElement(item);
+}
+
+void StdCategoryListView::createCategory( const Element &category, int parent_id )
+{
+ CategoryListItem * new_item = 0;
+ if ( parent_id == -1 ) {
+ new_item = new CategoryListItem( this, category );
+ }
+ else {
+ CategoryListItem *parent = (CategoryListItem*)items_map[ parent_id ];
+
+ if ( parent ) {
+ if ( parent->isPopulated() )
+ new_item = new CategoryListItem( parent, category );
+ else if ( !parent->firstChild() ) {
+ new PseudoListItem( parent );
+ parent->setOpen(true);
+ }
+ }
+ }
+
+ if ( new_item ) {
+ items_map.insert( category.id, new_item );
+ new_item->setPixmap( 0, m_folder_icon );
+ createElement(new_item);//new TQListViewItem(new_item);
+
+ CategoryTree list;
+ CategoryTree *p_list = &list;
+ database->loadCachedCategories( &p_list, 1, 0, category.id, false );
+
+ if ( p_list->firstChild() )
+ new PseudoListItem( new_item );
+ }
+}
+
+void StdCategoryListView::modCategory( TQListViewItem* i )
+{
+ if ( i )
+ CategoryListView::rename( i, 0 );
+}
+
+void StdCategoryListView::saveCategory( TQListViewItem* i )
+{
+ CategoryListItem * cat_it = ( CategoryListItem* ) i;
+
+ if ( !checkBounds( cat_it->categoryName() ) ) {
+ reload(ForceReload); //reset the changed text
+ return ;
+ }
+
+ int existing_id = database->findExistingCategoryByName( cat_it->categoryName() );
+ int cat_id = cat_it->categoryId();
+ if ( existing_id != -1 && existing_id != cat_id ) //category already exists with this label... merge the two
+ {
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "This category already exists. Continuing will merge these two categories into one. Are you sure?" ) ) )
+ {
+ case KMessageBox::Continue: {
+ database->mergeCategories( existing_id, cat_id );
+ break;
+ }
+ default:
+ reload(ForceReload);
+ break;
+ }
+ }
+ else
+ database->modCategory( cat_id, cat_it->categoryName() );
+}
+
+bool StdCategoryListView::checkBounds( const TQString &name )
+{
+ if ( name.length() > uint(database->maxCategoryNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Category name cannot be longer than %1 characters." ) ).arg( database->maxCategoryNameLength() ) );
+ return false;
+ }
+
+ return true;
+}
+
+
+
+CategoryCheckListView::CategoryCheckListView( TQWidget *parent, RecipeDB *db, bool _exclusive, const ElementList &init_items_checked ) : CategoryListView( parent, db ),
+ exclusive(_exclusive)
+{
+ addColumn( i18n( "Category" ) );
+
+ TDEConfig *config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+
+ for ( ElementList::const_iterator it = init_items_checked.begin(); it != init_items_checked.end(); ++it )
+ m_selections.append(*it);
+}
+
+void CategoryCheckListView::removeCategory( int id )
+{
+ TQListViewItem * item = items_map[ id ];
+
+ items_map.remove( id );
+ removeElement(item);
+}
+
+void CategoryCheckListView::createCategory( const Element &category, int parent_id )
+{
+ CategoryCheckListItem * new_item = 0;
+ if ( parent_id == -1 ) {
+ new_item = new CategoryCheckListItem( this, category, exclusive );
+ }
+ else {
+ TQListViewItem *parent = items_map[ parent_id ];
+ if ( parent )
+ new_item = new CategoryCheckListItem( parent, category, exclusive );
+ }
+
+ if ( new_item ) {
+ items_map.insert( category.id, new_item );
+ createElement(new_item);
+
+ CategoryTree list;
+ CategoryTree *p_list = &list;
+ database->loadCachedCategories( &p_list, 1, 0, category.id, false );
+
+ if ( p_list->firstChild() )
+ new PseudoListItem( new_item );
+
+
+ new_item->setOpen( false );
+ }
+}
+
+void CategoryCheckListView::stateChange( CategoryCheckListItem* it, bool on )
+{
+ if ( !reloading() ) {
+ if ( on )
+ m_selections.append(it->element());
+ else
+ m_selections.remove(it->element());
+ }
+}
+
+void CategoryCheckListView::load( int limit, int offset )
+{
+ CategoryListView::load(limit,offset);
+
+ for ( TQValueList<Element>::const_iterator it = m_selections.begin(); it != m_selections.end(); ++it ) {
+ TQCheckListItem * item = ( TQCheckListItem* ) findItem( TQString::number( (*it).id ), 1 );
+ if ( item ) {
+ item->setOn(true);
+ }
+ }
+}
+
+#include "categorylistview.moc"