diff options
Diffstat (limited to 'src/widgets/recipelistview.cpp')
-rw-r--r-- | src/widgets/recipelistview.cpp | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/src/widgets/recipelistview.cpp b/src/widgets/recipelistview.cpp new file mode 100644 index 0000000..536d06c --- /dev/null +++ b/src/widgets/recipelistview.cpp @@ -0,0 +1,447 @@ +/*************************************************************************** +* 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 "recipelistview.h" + +#include <tqintdict.h> +#include <tqdatastream.h> +#include <tqtooltip.h> + +#include <tdeapplication.h> +#include <kdebug.h> +#include <tdeconfig.h> +#include <tdeglobal.h> +#include <tdelocale.h> +#include <kiconloader.h> +#include <kprogress.h> + +#include "backends/recipedb.h" + +class UncategorizedItem : public TQListViewItem +{ +public: + UncategorizedItem( TQListView *lv ) : TQListViewItem( lv, i18n("Uncategorized") ){} + int rtti() const { return 1006; } +}; + +RecipeItemDrag::RecipeItemDrag( RecipeListItem *recipeItem, TQWidget *dragSource, const char *name ) + : TQStoredDrag( RECIPEITEMMIMETYPE, dragSource, name ) +{ + if ( recipeItem ) { + TQByteArray data; + TQDataStream out( data, IO_WriteOnly ); + out << recipeItem->recipeID(); + out << recipeItem->title(); + setEncodedData( data ); + } +} + +bool RecipeItemDrag::canDecode( TQMimeSource* e ) +{ + return e->provides( RECIPEITEMMIMETYPE ); +} + +bool RecipeItemDrag::decode( const TQMimeSource* e, RecipeListItem& item ) +{ + if ( !e ) + return false; + + TQByteArray data = e->encodedData( RECIPEITEMMIMETYPE ); + if ( data.isEmpty() ) + return false; + + TQString title; + int recipeID; + TQDataStream in( data, IO_ReadOnly ); + in >> recipeID; + in >> title; + + item.setTitle( title ); + item.setRecipeID( recipeID ); + + return true; +} + +class RecipeListToolTip : public TQToolTip +{ +public: + RecipeListToolTip( RecipeListView *view ) : TQToolTip(view->viewport()), m_view(view) + {} + + void maybeTip( const TQPoint &point ) + { + TQListViewItem *item = m_view->itemAt( point ); + if ( item ) { + TQString text = m_view->tooltip(item,0); + if ( !text.isEmpty() ) + tip( m_view->itemRect( item ), text ); + } + } + +private: + RecipeListView *m_view; + +}; + + +RecipeListView::RecipeListView( TQWidget *parent, RecipeDB *db ) : StdCategoryListView( parent, db ), + flat_list( false ), + m_uncat_item(0), + m_progress_dlg(0) +{ + setColumnText( 0, i18n( "Recipe" ) ); + + TDEConfig *config = TDEGlobal::config(); config->setGroup( "Performance" ); + curr_limit = config->readNumEntry("CategoryLimit",-1); + + TDEIconLoader il; + setPixmap( il.loadIcon( "categories", TDEIcon::NoGroup, 16 ) ); + + setSelectionMode( TQListView::Extended ); + + (void)new RecipeListToolTip(this); +} + +void RecipeListView::init() +{ + connect( database, TQ_SIGNAL( recipeCreated( const Element &, const ElementList & ) ), TQ_SLOT( createRecipe( const Element &, const ElementList & ) ) ); + connect( database, TQ_SIGNAL( recipeRemoved( int ) ), TQ_SLOT( removeRecipe( int ) ) ); + connect( database, TQ_SIGNAL( recipeRemoved( int, int ) ), TQ_SLOT( removeRecipe( int, int ) ) ); + connect( database, TQ_SIGNAL( recipeModified( const Element &, const ElementList & ) ), TQ_SLOT( modifyRecipe( const Element &, const ElementList & ) ) ); + + StdCategoryListView::init(); +} + +TQDragObject *RecipeListView::dragObject() +{ + RecipeListItem * item = dynamic_cast<RecipeListItem*>( currentItem() ); + if ( item != 0 ) { + RecipeItemDrag * obj = new RecipeItemDrag( item, this, "Recipe drag item" ); + /*const TQPixmap *pm = item->pixmap(0); + if( pm ) + obj->setPixmap( *pm );*/ + return obj; + } + return 0; +} + +bool RecipeListView::acceptDrag( TQDropEvent *event ) const +{ + return RecipeItemDrag::canDecode( event ); +} + +TQString RecipeListView::tooltip(TQListViewItem *item, int /*column*/) const +{ + if ( item->rtti() == RECIPELISTITEM_RTTI ) { + RecipeListItem *recipe_it = (RecipeListItem*)item; + + Recipe r; + database->loadRecipe(&r,RecipeDB::Meta|RecipeDB::Noatime,recipe_it->recipeID() ); + + TDELocale *locale = TDEGlobal::locale(); + + return TQString("<center><b>%7</b></center><center>__________</center>%1 %2<br />%3 %4<br />%5 %6") + .arg(i18n("Created:")).arg(locale->formatDateTime(r.ctime)) + .arg(i18n("Modified:")).arg(locale->formatDateTime(r.mtime)) + .arg(i18n("Last Accessed:")).arg(locale->formatDateTime(r.atime)) + .arg(recipe_it->title()); + }/* Maybe this would be handy + else if ( item->rtti() == CATEGORYLISTITEM_RTTI ) { + CategoryListItem *cat_it = (CategoryListItem*)item; + + return TQString("<b>%1</b><hr />%2: %3") + .arg(cat_it->categoryName()) + .arg(i18n("Recipes")) + .arg(TQString::number(WHATEVER THE CHILD COUNT IS)); + }*/ + + return TQString::null; +} + +void RecipeListView::load(int limit, int offset) +{ + m_uncat_item = 0; + + if ( flat_list ) { + ElementList recipeList; + database->loadRecipeList( &recipeList ); + + ElementList::const_iterator recipe_it; + for ( recipe_it = recipeList.begin();recipe_it != recipeList.end();++recipe_it ) { + Recipe recipe; + recipe.recipeID = ( *recipe_it ).id; + recipe.title = ( *recipe_it ).name; + createRecipe( recipe, -1 ); + } + } + else { + StdCategoryListView::load(limit,offset); + + if ( offset == 0 ) { + ElementList recipeList; + database->loadUncategorizedRecipes( &recipeList ); + + ElementList::const_iterator recipe_it; + for ( recipe_it = recipeList.begin();recipe_it != recipeList.end();++recipe_it ) { + Recipe recipe; + recipe.recipeID = ( *recipe_it ).id; + recipe.title = ( *recipe_it ).name; + createRecipe( recipe, -1 ); + } + } + } +} + +void RecipeListView::populate( TQListViewItem *item ) +{ + CategoryItemInfo *cat_item = dynamic_cast<CategoryItemInfo*>(item); + if ( !cat_item || cat_item->isPopulated() ) return; + + delete item->firstChild(); //delete the "pseudo item" + + if ( m_progress_dlg ){ + m_progress_dlg->progressBar()->advance(1); + kapp->processEvents(); + } + + StdCategoryListView::populate(item); + + if ( !flat_list ) { + int id = cat_item->categoryId(); + + // Now show the recipes + ElementList recipeList; + database->loadRecipeList( &recipeList, id ); + + ElementList::const_iterator recipe_it; + for ( recipe_it = recipeList.begin(); recipe_it != recipeList.end(); ++recipe_it ) { + Recipe recipe; + recipe.recipeID = ( *recipe_it ).id; + recipe.title = ( *recipe_it ).name; + createRecipe( recipe, id ); + } + } +} + +void RecipeListView::populateAll( TQListViewItem *parent ) +{ + bool first = false; + if ( !parent ) { + first = true; + m_progress_dlg = new KProgressDialog(this,"populate_all_prog_dlg",TQString::null,i18n("Loading recipes"),true); + m_progress_dlg->setAllowCancel(false); + m_progress_dlg->progressBar()->setTotalSteps(0); + m_progress_dlg->progressBar()->setPercentageVisible(false); + + m_progress_dlg->grabKeyboard(); //don't let the user keep hitting keys + + parent = firstChild(); + } + else { + populate( parent ); + parent = parent->firstChild(); + } + + for ( TQListViewItem *item = parent; item; item = item->nextSibling() ) { + if ( m_progress_dlg && m_progress_dlg->wasCancelled() ) + break; + + populateAll( item ); + } + + if ( first ) { + delete m_progress_dlg; + m_progress_dlg = 0; + } +} + +void RecipeListView::createRecipe( const Recipe &recipe, int parent_id ) +{ + if ( parent_id == -1 ) { + if ( !m_uncat_item && curr_offset == 0 ) { + m_uncat_item = new UncategorizedItem(this); + if ( childCount() == 1 ) //only call createElement if this is the only item in the list + createElement(m_uncat_item); //otherwise, this item won't stay at the top + } + + if ( m_uncat_item ) + new RecipeListItem( m_uncat_item, recipe ); + } + else { + CategoryListItem *parent = (CategoryListItem*)items_map[ parent_id ]; + if ( parent && parent->isPopulated() ) + createElement(new RecipeListItem( parent, recipe )); + } +} + +void RecipeListView::createRecipe( const Element &recipe_el, const ElementList &categories ) +{ + Recipe recipe; + recipe.recipeID = recipe_el.id; + recipe.title = recipe_el.name; + + if ( categories.count() == 0 ) { + createRecipe( recipe, -1 ); + } + else { + for ( ElementList::const_iterator cat_it = categories.begin(); cat_it != categories.end(); ++cat_it ) { + int cur_cat_id = ( *cat_it ).id; + + TQListViewItemIterator iterator( this ); + while ( iterator.current() ) { + if ( iterator.current() ->rtti() == 1001 ) { + CategoryListItem * cat_item = ( CategoryListItem* ) iterator.current(); + if ( cat_item->categoryId() == cur_cat_id ) { + createRecipe( recipe, cur_cat_id ); + } + } + ++iterator; + } + } + } +} + +void RecipeListView::createElement( TQListViewItem *item ) +{ + CategoryItemInfo *cat_item = dynamic_cast<CategoryItemInfo*>(item); + if ( cat_item && !cat_item->isPopulated() ) { + new PseudoListItem( item ); + } + + //if ( cat_item && !cat_item->isPopulated() && item->rtti() == RECIPELISTITEM_RTTI ) + // return; + + #if 0 + ElementList list; + database->loadRecipeList( &list, cat_item->categoryId() ); + if ( list.count() > 0 ) + #endif + + CategoryListView::createElement(item); +} + +void RecipeListView::modifyRecipe( const Element &recipe, const ElementList &categories ) +{ + removeRecipe( recipe.id ); + createRecipe( recipe, categories ); +} + +void RecipeListView::removeRecipe( int id ) +{ + TQListViewItemIterator iterator( this ); + while ( iterator.current() ) { + if ( iterator.current() ->rtti() == 1000 ) { + RecipeListItem * recipe_it = ( RecipeListItem* ) iterator.current(); + if ( recipe_it->recipeID() == id ) { + removeElement(recipe_it); + + //delete the "Uncategorized" item if we removed the last recipe that was under it + if ( m_uncat_item && m_uncat_item->childCount() == 0 ) { + delete m_uncat_item; + m_uncat_item = 0; + } + } + } + ++iterator; + } +} + +void RecipeListView::removeRecipe( int recipe_id, int cat_id ) +{ + TQListViewItem * item = items_map[ cat_id ]; + + //find out if this is the only category the recipe belongs to + int finds = 0; + TQListViewItemIterator iterator( this ); + while ( iterator.current() ) { + if ( iterator.current() ->rtti() == 1000 ) { + RecipeListItem * recipe_it = ( RecipeListItem* ) iterator.current(); + + if ( recipe_it->recipeID() == recipe_id ) { + if ( finds > 1 ) + break; + finds++; + } + } + ++iterator; + } + + //do this to only iterate over children of 'item' + TQListViewItem *pEndItem = NULL; + TQListViewItem *pStartItem = item; + do { + if ( pStartItem->nextSibling() ) + pEndItem = pStartItem->nextSibling(); + else + pStartItem = pStartItem->parent(); + } + while ( pStartItem && !pEndItem ); + + iterator = TQListViewItemIterator( item ); + while ( iterator.current() != pEndItem ) { + if ( iterator.current() ->rtti() == 1000 ) { + RecipeListItem * recipe_it = ( RecipeListItem* ) iterator.current(); + + if ( recipe_it->recipeID() == recipe_id ) { + + if ( finds == 1 ) { + //the item is now uncategorized + if ( !m_uncat_item && curr_offset == 0 ) + m_uncat_item = new UncategorizedItem(this); + if ( m_uncat_item ) { + Recipe r; + r.title = recipe_it->title(); r.recipeID = recipe_id; + new RecipeListItem(m_uncat_item,r); + } + } + removeElement(recipe_it); + break; + } + } + ++iterator; + } +} + +void RecipeListView::removeCategory( int id ) +{ + TQListViewItem * item = items_map[ id ]; + if ( !item ) + return ; //this may have been deleted already by its parent being deleted + + moveChildrenToRoot( item ); + + StdCategoryListView::removeCategory( id ); +} + +void RecipeListView::moveChildrenToRoot( TQListViewItem *item ) +{ + TQListViewItem * next_sibling; + for ( TQListViewItem * it = item->firstChild(); it; it = next_sibling ) { + next_sibling = it->nextSibling(); + if ( it->rtti() == 1000 ) { + RecipeListItem *recipe_it = (RecipeListItem*) it; + Recipe r; + r.title = recipe_it->title(); r.recipeID = recipe_it->recipeID(); + + //the item is now uncategorized + removeElement(it,false); + it->parent() ->takeItem( it ); + if ( !m_uncat_item && curr_offset == 0 ) + m_uncat_item = new UncategorizedItem(this); + if ( m_uncat_item ) + new RecipeListItem(m_uncat_item,r); + } + moveChildrenToRoot( it ); + delete it; + } +} + +#include "recipelistview.moc" |