diff options
Diffstat (limited to 'src/dialogs/ingredientmatcherdialog.cpp')
-rw-r--r-- | src/dialogs/ingredientmatcherdialog.cpp | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/src/dialogs/ingredientmatcherdialog.cpp b/src/dialogs/ingredientmatcherdialog.cpp new file mode 100644 index 0000000..b994ec8 --- /dev/null +++ b/src/dialogs/ingredientmatcherdialog.cpp @@ -0,0 +1,353 @@ +/*************************************************************************** +* Copyright (C) 2003 by * +* 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 "ingredientmatcherdialog.h" + +#include "datablocks/recipelist.h" +#include "widgets/ingredientlistview.h" +#include "datablocks/elementlist.h" +#include "backends/recipedb.h" +#include "widgets/krelistview.h" +#include "widgets/unitcombobox.h" +#include "widgets/fractioninput.h" +#include "widgets/amountunitinput.h" +#include "datablocks/mixednumber.h" +#include "recipeactionshandler.h" + +#include <tqheader.h> +#include <tqpainter.h> +#include <tqstringlist.h> +#include <tqlayout.h> +#include <tqgroupbox.h> + +#include <tdeapplication.h> +#include <kcursor.h> +#include <kiconloader.h> +#include <tdelocale.h> +#include <knuminput.h> +#include <tdeconfig.h> +#include <tdeglobal.h> +#include <kdebug.h> +#include <kdialogbase.h> + +#include "profiling.h" + +IngredientMatcherDialog::IngredientMatcherDialog( TQWidget *parent, RecipeDB *db ) : TQWidget( parent ) +{ + // Initialize internal variables + database = db; + + //Design the dialog + + TQVBoxLayout *dialogLayout = new TQVBoxLayout( this, 11, 6 ); + + // Ingredient list + TQHBoxLayout *layout2 = new TQHBoxLayout( 0, 0, 6, "layout2" ); + + allIngListView = new KreListView( this, TQString::null, true, 0 ); + StdIngredientListView *list_view = new StdIngredientListView(allIngListView,database); + list_view->setSelectionMode( TQListView::Multi ); + allIngListView->setListView(list_view); + layout2->addWidget( allIngListView ); + + TQVBoxLayout *layout1 = new TQVBoxLayout( 0, 0, 6, "layout1" ); + + TDEIconLoader il; + + addButton = new TQPushButton( this, "addButton" ); + addButton->setIconSet( il.loadIconSet( "forward", TDEIcon::Small ) ); + addButton->setFixedSize( TQSize( 32, 32 ) ); + layout1->addWidget( addButton ); + + removeButton = new TQPushButton( this, "removeButton" ); + removeButton->setIconSet( il.loadIconSet( "back", TDEIcon::Small ) ); + removeButton->setFixedSize( TQSize( 32, 32 ) ); + layout1->addWidget( removeButton ); + TQSpacerItem *spacer1 = new TQSpacerItem( 51, 191, TQSizePolicy::Minimum, TQSizePolicy::Expanding ); + layout1->addItem( spacer1 ); + layout2->addLayout( layout1 ); + + ingListView = new KreListView( this, TQString::null, true ); + ingListView->listView() ->addColumn( i18n( "Ingredient (required?)" ) ); + ingListView->listView() ->addColumn( i18n( "Amount Available" ) ); + layout2->addWidget( ingListView ); + dialogLayout->addLayout( layout2 ); + + // Box to select allowed number of missing ingredients + missingBox = new TQHBox( this ); + missingNumberLabel = new TQLabel( missingBox ); + missingNumberLabel->setText( i18n( "Missing ingredients allowed:" ) ); + missingNumberSpinBox = new KIntSpinBox( missingBox ); + missingNumberSpinBox->setMinValue( -1 ); + missingNumberSpinBox->setSpecialValueText( i18n( "Any" ) ); + dialogLayout->addWidget(missingBox); + + // Found recipe list + recipeListView = new KreListView( this, i18n( "Matching Recipes" ), false, 1, missingBox ); + recipeListView->listView() ->setSizePolicy( TQSizePolicy::Ignored, TQSizePolicy::Ignored ); + recipeListView->listView() ->setAllColumnsShowFocus( true ); + + recipeListView->listView() ->addColumn( i18n( "Title" ) ); + + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "Advanced" ); + bool show_id = config->readBoolEntry( "ShowID", false ); + recipeListView->listView() ->addColumn( i18n( "Id" ), show_id ? -1 : 0 ); + + recipeListView->listView() ->addColumn( i18n( "Missing Ingredients" ) ); + + recipeListView->listView() ->setSorting( -1 ); + dialogLayout->addWidget(recipeListView); + + RecipeActionsHandler *actionHandler = new RecipeActionsHandler( recipeListView->listView(), database, RecipeActionsHandler::Open | RecipeActionsHandler::Edit | RecipeActionsHandler::Export ); + + TQHBox *buttonBox = new TQHBox( this ); + + okButton = new TQPushButton( buttonBox ); + okButton->setIconSet( il.loadIconSet( "button_ok", TDEIcon::Small ) ); + okButton->setText( i18n( "Find matching recipes" ) ); + + //buttonBox->layout()->addItem( new TQSpacerItem( 10,10, TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed ) ); + + clearButton = new TQPushButton( buttonBox ); + clearButton->setIconSet( il.loadIconSet( "edit-clear", TDEIcon::Small ) ); + clearButton->setText( i18n( "Clear" ) ); + dialogLayout->addWidget(buttonBox); + + // Connect signals & slots + connect ( okButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( findRecipes() ) ); + connect ( clearButton, TQ_SIGNAL( clicked() ), recipeListView->listView(), TQ_SLOT( clear() ) ); + connect ( clearButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( unselectIngredients() ) ); + connect ( actionHandler, TQ_SIGNAL( recipeSelected( int, int ) ), TQ_SIGNAL( recipeSelected( int, int ) ) ); + connect( addButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( addIngredient() ) ); + connect( removeButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( removeIngredient() ) ); + connect( ingListView->listView(), TQ_SIGNAL( doubleClicked( TQListViewItem*, const TQPoint &, int ) ), TQ_SLOT( itemRenamed( TQListViewItem*, const TQPoint &, int ) ) ); +} + +IngredientMatcherDialog::~IngredientMatcherDialog() +{ +} + +void IngredientMatcherDialog::itemRenamed( TQListViewItem* item, const TQPoint &, int col ) +{ + if ( col == 1 ) { + KDialogBase amountEditDialog(this,"IngredientMatcherAmountEdit", + false, i18n("Enter amount"), KDialogBase::Cancel | KDialogBase::Ok, KDialogBase::Ok); + + TQGroupBox *box = new TQGroupBox( 1, Horizontal, i18n("Amount"), &amountEditDialog ); + AmountUnitInput *amountEdit = new AmountUnitInput( box, database ); + // Set the values from the item + if ( !item->text(1).isEmpty() ) { + amountEdit->setAmount( MixedNumber::fromString(item->text(2)) ); + Unit u; + u.id = item->text(3).toInt(); + amountEdit->setUnit( u ); + } else { + amountEdit->setAmount( -1 ); + Unit u; + u.id = -1; + amountEdit->setUnit( u ); + } + + amountEditDialog.setMainWidget(box); + + if ( amountEditDialog.exec() == TQDialog::Accepted ) { + MixedNumber amount = amountEdit->amount(); + Unit unit = amountEdit->unit(); + + if ( amount.toDouble() <= 1e-5 ) { + amount = -1; + unit.id = -1; + + item->setText(1,TQString::null); + } else { + item->setText(1,amount.toString()+" "+((amount.toDouble()>1)?unit.plural:unit.name)); + } + + item->setText(2,amount.toString()); + item->setText(3,TQString::number(unit.id)); + + IngredientList::iterator ing_it = m_item_ing_map[item]; + (*ing_it).amount = amount.toDouble(); + (*ing_it).units = unit; + } + } +} + +void IngredientMatcherDialog::addIngredient() +{ + TQPtrList<TQListViewItem> items = allIngListView->listView()->selectedItems(); + if ( items.count() > 0 ) { + TQPtrListIterator<TQListViewItem> it(items); + TQListViewItem *item; + while ( (item = it.current()) != 0 ) { + bool dup = false; + for ( TQListViewItem *exists_it = ingListView->listView()->firstChild(); exists_it; exists_it = exists_it->nextSibling() ) { + if ( exists_it->text( 0 ) == item->text( 0 ) ) { + dup = true; + break; + } + } + + if ( !dup ) { + TQListViewItem * new_item = new TQCheckListItem( ingListView->listView(), item->text( 0 ), TQCheckListItem::CheckBox ); + + ingListView->listView() ->setSelected( new_item, true ); + ingListView->listView() ->ensureItemVisible( new_item ); + allIngListView->listView() ->setSelected( item, false ); + + m_item_ing_map.insert( new_item, m_ingredientList.append( Ingredient( item->text( 0 ), 0, Unit(), -1, item->text( 1 ).toInt() ) ) ); + } + ++it; + } + } +} + +void IngredientMatcherDialog::removeIngredient() +{ + TQListViewItem * item = ingListView->listView() ->selectedItem(); + if ( item ) { + for ( IngredientList::iterator it = m_ingredientList.begin(); it != m_ingredientList.end(); ++it ) { + if ( *m_item_ing_map.find( item ) == it ) { + m_ingredientList.remove( it ); + m_item_ing_map.remove( item ); + break; + } + } + delete item; + } +} + +void IngredientMatcherDialog::unselectIngredients() +{ + ingListView->listView()->clear(); + for ( TQListViewItem *it = allIngListView->listView()->firstChild(); it; it = it->nextSibling() ) + allIngListView->listView()->setSelected(it,false); +} + +void IngredientMatcherDialog::findRecipes( void ) +{ + TDEApplication::setOverrideCursor( KCursor::waitCursor() ); + + START_TIMER("Ingredient Matcher: loading database data"); + + RecipeList rlist; + database->loadRecipes( &rlist, RecipeDB::Title | RecipeDB::NamesOnly | RecipeDB::Ingredients | RecipeDB::IngredientAmounts ); + + END_TIMER(); + START_TIMER("Ingredient Matcher: analyzing data for matching recipes"); + + // Clear the list + recipeListView->listView() ->clear(); + + // Now show the recipes with ingredients that are contained in the previous set + // of ingredients + RecipeList incompleteRecipes; + TQValueList <int> missingNumbers; + TQValueList <IngredientList> missingIngredients; + + RecipeList::Iterator it; + for ( it = rlist.begin();it != rlist.end();++it ) { + IngredientList il = ( *it ).ingList; + if ( il.isEmpty() ) + continue; + + IngredientList missing; + if ( m_ingredientList.containsSubSet( il, missing, true, database ) ) { + new CustomRecipeListItem( recipeListView->listView(), *it ); + } + else { + incompleteRecipes.append( *it ); + missingIngredients.append( missing ); + missingNumbers.append( missing.count() ); + } + } + END_TIMER(); + + //Check if the user wants to show missing ingredients + + if ( missingNumberSpinBox->value() == 0 ) { + TDEApplication::restoreOverrideCursor(); + return ; + } //"None" + + + + START_TIMER("Ingredient Matcher: searching for and displaying partial matches"); + + IngredientList requiredIngredients; + for ( TQListViewItem *it = ingListView->listView()->firstChild(); it; it = it->nextSibling() ) { + if ( ((TQCheckListItem*)it)->isOn() ) + requiredIngredients << *m_item_ing_map[it]; + } + + // Classify recipes with missing ingredients in different lists by ammount + TQValueList<int>::Iterator nit; + TQValueList<IngredientList>::Iterator ilit; + int missingNoAllowed = missingNumberSpinBox->value(); + + if ( missingNoAllowed == -1 ) // "Any" + { + for ( nit = missingNumbers.begin();nit != missingNumbers.end();++nit ) + if ( ( *nit ) > missingNoAllowed ) + missingNoAllowed = ( *nit ); + } + + + for ( int missingNo = 1; missingNo <= missingNoAllowed; missingNo++ ) { + nit = missingNumbers.begin(); + ilit = missingIngredients.begin(); + + bool titleShownYet = false; + + for ( it = incompleteRecipes.begin();it != incompleteRecipes.end();++it, ++nit, ++ilit ) { + if ( !( *it ).ingList.containsAny( m_ingredientList ) ) + continue; + + if ( !( *it ).ingList.containsSubSet( requiredIngredients ) ) + continue; + + if ( ( *nit ) == missingNo ) { + if ( !titleShownYet ) { + new SectionItem( recipeListView->listView(), i18n( "You are missing 1 ingredient for:", "You are missing %n ingredients for:", missingNo ) ); + titleShownYet = true; + } + new CustomRecipeListItem( recipeListView->listView(), *it, *ilit ); + } + } + } + END_TIMER(); + + TDEApplication::restoreOverrideCursor(); +} + +void IngredientMatcherDialog::reload( ReloadFlags flag ) +{ + ( ( StdIngredientListView* ) allIngListView->listView() ) ->reload(flag); +} + +void SectionItem::paintCell ( TQPainter * p, const TQColorGroup & /*cg*/, int column, int width, int /*align*/ ) +{ + // Draw the section's deco + p->setPen( TDEGlobalSettings::activeTitleColor() ); + p->setBrush( TDEGlobalSettings::activeTitleColor() ); + p->drawRect( 0, 0, width, height() ); + + // Draw the section's text + if ( column == 0 ) { + TQFont titleFont = TDEGlobalSettings::windowTitleFont (); + p->setFont( titleFont ); + + p->setPen( TDEGlobalSettings::activeTextColor() ); + p->drawText( 0, 0, width, height(), TQt::AlignLeft | TQt::AlignVCenter, mText ); + } +} + +#include "ingredientmatcherdialog.moc" |