summaryrefslogtreecommitdiffstats
path: root/src/widgets/ingredientinputwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/ingredientinputwidget.cpp')
-rw-r--r--src/widgets/ingredientinputwidget.cpp542
1 files changed, 542 insertions, 0 deletions
diff --git a/src/widgets/ingredientinputwidget.cpp b/src/widgets/ingredientinputwidget.cpp
new file mode 100644
index 0000000..3c0aa7e
--- /dev/null
+++ b/src/widgets/ingredientinputwidget.cpp
@@ -0,0 +1,542 @@
+/***************************************************************************
+* Copyright (C) 2003-2005 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* Copyright (C) 2006 Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* 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 "ingredientinputwidget.h"
+
+#include <tqlabel.h>
+#include <tqwidgetstack.h>
+#include <tqhbox.h>
+#include <tqvbox.h>
+#include <tqgroupbox.h>
+#include <tqbuttongroup.h>
+#include <tqradiobutton.h>
+#include <tqcheckbox.h>
+
+#include <kcombobox.h>
+#include <tdelocale.h>
+#include <tdemessagebox.h>
+#include <kdebug.h>
+
+#include "backends/recipedb.h"
+#include "datablocks/unit.h"
+#include "widgets/fractioninput.h"
+#include "widgets/ingredientcombobox.h"
+#include "widgets/headercombobox.h"
+#include "widgets/prepmethodcombobox.h"
+#include "dialogs/createunitdialog.h"
+
+#include "profiling.h"
+
+IngredientInput::IngredientInput( RecipeDB *db, TQWidget *parent, bool allowHeader ) : TQHBox(parent), database(db)
+{
+ TQVBox *ingredientVBox = new TQVBox( this );
+ TQHBox *typeHBox = new TQHBox( ingredientVBox );
+
+ if ( allowHeader ) {
+ typeButtonGrp = new TQButtonGroup();
+ TQRadioButton *ingredientRadioButton = new TQRadioButton( i18n( "Ingredient:" ), typeHBox );
+ typeButtonGrp->insert( ingredientRadioButton, 0 );
+
+ TQRadioButton *headerRadioButton = new TQRadioButton( i18n( "Ingredient grouping name", "Header:" ), typeHBox );
+ typeButtonGrp->insert( headerRadioButton, 1 );
+
+ typeButtonGrp->setButton( 0 );
+ connect( typeButtonGrp, TQ_SIGNAL( clicked( int ) ), TQ_SLOT( typeButtonClicked( int ) ) );
+ }
+ else {
+ (void) new TQLabel( i18n( "Ingredient:" ), typeHBox );
+ typeButtonGrp = 0;
+ }
+
+ header_ing_stack = new TQWidgetStack(ingredientVBox);
+ ingredientBox = new IngredientComboBox( TRUE, header_ing_stack, database );
+ ingredientBox->setAutoCompletion( TRUE );
+ ingredientBox->lineEdit() ->disconnect( ingredientBox ); //so hitting enter doesn't enter the item into the box
+ ingredientBox->setSizePolicy( TQSizePolicy( TQSizePolicy::Ignored, TQSizePolicy::Fixed ) );
+ header_ing_stack->addWidget( ingredientBox );
+ headerBox = new HeaderComboBox( TRUE, header_ing_stack, database );
+ headerBox->setAutoCompletion( TRUE );
+ headerBox->lineEdit() ->disconnect( ingredientBox ); //so hitting enter doesn't enter the item into the box
+ headerBox->setSizePolicy( TQSizePolicy( TQSizePolicy::Ignored, TQSizePolicy::Fixed ) );
+ header_ing_stack->addWidget( headerBox );
+
+ TQVBox *amountVBox = new TQVBox( this );
+ amountLabel = new TQLabel( i18n( "Amount:" ), amountVBox );
+ amountEdit = new FractionInput( amountVBox );
+ amountEdit->setAllowRange(true);
+ amountEdit->setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ) );
+
+ TQVBox *unitVBox = new TQVBox( this );
+ unitLabel = new TQLabel( i18n( "Unit:" ), unitVBox );
+ unitBox = new KComboBox( TRUE, unitVBox );
+ unitBox->setAutoCompletion( TRUE );
+ unitBox->lineEdit() ->disconnect( unitBox ); //so hitting enter doesn't enter the item into the box
+ unitBox->setSizePolicy( TQSizePolicy( TQSizePolicy::Ignored, TQSizePolicy::Fixed ) );
+
+ TQVBox *prepMethodVBox = new TQVBox( this );
+ prepMethodLabel = new TQLabel( i18n( "Preparation Method:" ), prepMethodVBox );
+ prepMethodBox = new PrepMethodComboBox( TRUE, prepMethodVBox, database );
+ prepMethodBox->setAutoCompletion( TRUE );
+ prepMethodBox->lineEdit() ->disconnect( prepMethodBox ); //so hitting enter doesn't enter the item into the box
+ prepMethodBox->setSizePolicy( TQSizePolicy( TQSizePolicy::Ignored, TQSizePolicy::Fixed ) );
+
+ orButton = new TQCheckBox( i18n( "OR" ), this );
+
+ setStretchFactor( ingredientVBox, 5 );
+ setStretchFactor( amountVBox, 1 );
+ setStretchFactor( unitVBox, 2 );
+ setStretchFactor( prepMethodVBox, 3 );
+
+ connect( ingredientBox, TQ_SIGNAL( activated( int ) ), this, TQ_SLOT( loadUnitListCombo() ) );
+ connect( ingredientBox->lineEdit(), TQ_SIGNAL( lostFocus() ), this, TQ_SLOT( slotIngredientBoxLostFocus() ) );
+ connect( unitBox->lineEdit(), TQ_SIGNAL( lostFocus() ), this, TQ_SLOT( slotUnitBoxLostFocus() ) );
+ connect( prepMethodBox->lineEdit(), TQ_SIGNAL( lostFocus() ), this, TQ_SLOT( slotPrepMethodBoxLostFocus() ) );
+ connect( orButton, TQ_SIGNAL( toggled(bool) ), this, TQ_SLOT( orToggled(bool) ) );
+
+ connect( unitBox->lineEdit(), TQ_SIGNAL( returnPressed() ), this, TQ_SLOT( signalIngredient() ) );
+ connect( ingredientBox->lineEdit(), TQ_SIGNAL( returnPressed() ), this, TQ_SLOT( signalIngredient() ) );
+ connect( headerBox->lineEdit(), TQ_SIGNAL( returnPressed() ), this, TQ_SLOT( signalIngredient() ) );
+ connect( prepMethodBox->lineEdit(), TQ_SIGNAL( returnPressed() ), this, TQ_SLOT( signalIngredient() ) );
+ connect( amountEdit, TQ_SIGNAL( returnPressed( const TQString & ) ), this, TQ_SLOT( signalIngredient() ) );
+
+ unitComboList = new UnitList;
+
+ setFocusProxy( ingredientBox );
+}
+
+IngredientInput::~IngredientInput()
+{
+ delete unitComboList;
+ delete typeButtonGrp;
+}
+
+void IngredientInput::clear()
+{
+ unitComboList->clear();
+
+ orButton->setChecked(false);
+ typeButtonGrp->setButton( 0 ); //put back to ingredient input
+ typeButtonClicked( 0 );
+
+ amountEdit->clear();
+ ingredientBox->lineEdit()->setText("");
+ prepMethodBox->lineEdit()->setText("");
+ headerBox->lineEdit()->setText("");
+ unitBox->lineEdit()->setText("");
+}
+
+void IngredientInput::orToggled(bool b)
+{
+ emit orToggled(b,this);
+}
+
+void IngredientInput::reloadCombos()
+{
+ //these only needed to be loaded once
+ if ( ingredientBox->count() == 0 ) {
+ START_TIMER("Loading ingredient input auto-completion");
+ ingredientBox->reload();
+ END_TIMER();
+ }
+ if ( headerBox->count() == 0 ) {
+ START_TIMER("Loading ingredient header input auto-completion");
+ headerBox->reload();
+ END_TIMER();
+ }
+ if ( prepMethodBox->count() == 0 ) {
+ START_TIMER("Loading prep method input auto-completion");
+ prepMethodBox->reload();
+ END_TIMER();
+ }
+
+ loadUnitListCombo();
+}
+
+void IngredientInput::slotIngredientBoxLostFocus( void )
+{
+ if ( ingredientBox->contains( ingredientBox->currentText() ) ) {
+ ingredientBox->setCurrentItem( ingredientBox->currentText() );
+ loadUnitListCombo();
+ }
+ else {
+ unitBox->clear();
+ unitBox->completionObject() ->clear();
+ unitComboList->clear();
+ }
+}
+
+void IngredientInput::slotUnitBoxLostFocus()
+{
+ if ( unitBox->contains( unitBox->currentText() ) )
+ unitBox->setCurrentItem( unitBox->currentText() );
+}
+
+void IngredientInput::slotPrepMethodBoxLostFocus()
+{
+ if ( prepMethodBox->contains( prepMethodBox->currentText() ) )
+ prepMethodBox->setCurrentItem( prepMethodBox->currentText() );
+}
+
+void IngredientInput::typeButtonClicked( int button_id )
+{
+ if ( amountEdit->isEnabled() == !bool( button_id ) ) //it is already set (the same button was clicked more than once)
+ return ;
+
+ amountEdit->setEnabled( !bool( button_id ) );
+ unitBox->setEnabled( !bool( button_id ) );
+ prepMethodBox->setEnabled( !bool( button_id ) );
+
+ if ( button_id == 1 ) { //Header
+ header_ing_stack->raiseWidget( headerBox );
+ }
+ else {
+ header_ing_stack->raiseWidget( ingredientBox );
+ }
+}
+
+void IngredientInput::enableHeader( bool enable )
+{
+ if ( !enable ) {
+ typeButtonGrp->setButton( 0 ); //put back to ingredient input
+ typeButtonClicked( 0 );
+ }
+ typeButtonGrp->find(1)->setEnabled(enable);
+}
+
+void IngredientInput::signalIngredient()
+{
+ //validate input; if successful, emit signal
+ if ( isHeader() ) {
+ if ( header().isEmpty() )
+ return;
+ }
+ else {
+ if ( !isInputValid() )
+ return;
+ }
+
+ emit addIngredient();
+}
+
+bool IngredientInput::isInputValid()
+{
+ if ( ingredientBox->currentText().stripWhiteSpace().isEmpty() ) {
+ KMessageBox::error( this, i18n( "Please enter an ingredient" ), TQString::null );
+ ingredientBox->setFocus();
+ return false;
+ }
+ return checkAmountEdit() && checkBounds();
+}
+
+bool IngredientInput::checkBounds()
+{
+ if ( ingredientBox->currentText().length() > uint(database->maxIngredientNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Ingredient name cannot be longer than %1 characters." ) ).arg( database->maxIngredientNameLength() ) );
+ ingredientBox->lineEdit() ->setFocus();
+ ingredientBox->lineEdit() ->selectAll();
+ return false;
+ }
+
+ if ( unitBox->currentText().length() > uint(database->maxUnitNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Unit name cannot be longer than %1 characters." ) ).arg( database->maxUnitNameLength() ) );
+ unitBox->lineEdit() ->setFocus();
+ unitBox->lineEdit() ->selectAll();
+ return false;
+ }
+
+ TQStringList prepMethodList = TQStringList::split(",",prepMethodBox->currentText());
+ for ( TQStringList::const_iterator it = prepMethodList.begin(); it != prepMethodList.end(); ++it ) {
+ if ( (*it).stripWhiteSpace().length() > uint(database->maxPrepMethodNameLength()) )
+ {
+ KMessageBox::error( this, TQString( i18n( "Preparation method cannot be longer than %1 characters." ) ).arg( database->maxPrepMethodNameLength() ) );
+ prepMethodBox->lineEdit() ->setFocus();
+ prepMethodBox->lineEdit() ->selectAll();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool IngredientInput::checkAmountEdit()
+{
+ if ( amountEdit->isInputValid() )
+ return true;
+ else {
+ KMessageBox::error( this, i18n( "Amount field contains invalid input." ),
+ i18n( "Invalid input" ) );
+ amountEdit->setFocus();
+ amountEdit->selectAll();
+ return false;
+ }
+}
+
+void IngredientInput::loadUnitListCombo()
+{
+ TQString store_unit = unitBox->currentText();
+ unitBox->clear(); // Empty the combo first
+ unitBox->completionObject() ->clear();
+
+ int comboIndex = ingredientBox->currentItem();
+ int comboCount = ingredientBox->count();
+
+ if ( comboCount > 0 ) { // If not, the list may be empty (no ingredient list defined) and crashes while reading
+ int selectedIngredient = ingredientBox->id( comboIndex );
+ database->loadPossibleUnits( selectedIngredient, unitComboList );
+
+ //Populate this data into the ComboBox
+ for ( UnitList::const_iterator unit_it = unitComboList->begin(); unit_it != unitComboList->end(); ++unit_it ) {
+ unitBox->insertItem( ( *unit_it ).name );
+ unitBox->completionObject() ->addItem( ( *unit_it ).name );
+ if ( ( *unit_it ).name != (*unit_it ).plural ) {
+ unitBox->insertItem( ( *unit_it ).plural );
+ unitBox->completionObject() ->addItem( ( *unit_it ).plural );
+ }
+
+ if ( !( *unit_it ).name_abbrev.isEmpty() ) {
+ unitBox->insertItem( ( *unit_it ).name_abbrev );
+ unitBox->completionObject() ->addItem( ( *unit_it ).name_abbrev );
+ }
+ if ( !(*unit_it ).plural_abbrev.isEmpty() &&
+ ( *unit_it ).name_abbrev != (*unit_it ).plural_abbrev ) {
+ unitBox->insertItem( ( *unit_it ).plural_abbrev );
+ unitBox->completionObject() ->addItem( ( *unit_it ).plural_abbrev );
+ }
+
+ }
+ }
+ unitBox->lineEdit() ->setText( store_unit );
+}
+
+bool IngredientInput::isHeader() const
+{
+ return typeButtonGrp && (typeButtonGrp->id( typeButtonGrp->selected() ) == 1);
+}
+
+Ingredient IngredientInput::ingredient() const
+{
+ Ingredient ing;
+
+ ing.prepMethodList = ElementList::split(",",prepMethodBox->currentText());
+ ing.name = ingredientBox->currentText();
+ amountEdit->value(ing.amount,ing.amount_offset);
+ ing.units = Unit(unitBox->currentText().stripWhiteSpace(),ing.amount+ing.amount_offset);
+ ing.ingredientID = ingredientBox->id( ingredientBox->currentItem() );
+
+ return ing;
+}
+
+TQString IngredientInput::header() const
+{
+ return headerBox->currentText().stripWhiteSpace();
+}
+
+void IngredientInput::updateTabOrder()
+{
+ TQWidget::setTabOrder( ingredientBox, amountEdit );
+ TQWidget::setTabOrder( amountEdit, unitBox );
+ TQWidget::setTabOrder( unitBox, prepMethodBox );
+ TQWidget::setTabOrder( prepMethodBox, orButton );
+}
+
+
+IngredientInputWidget::IngredientInputWidget( RecipeDB *db, TQWidget *parent ) : TQVBox(parent), database(db)
+{
+ setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed ) );
+
+ m_ingInputs.append(new IngredientInput(database,this));
+
+ // Connect signals & Slots
+ connect( m_ingInputs[0], TQ_SIGNAL(addIngredient()), this, TQ_SLOT(addIngredient()) );
+ connect( m_ingInputs[0], TQ_SIGNAL(orToggled(bool,IngredientInput*)), this, TQ_SLOT(updateInputs(bool,IngredientInput*)) );
+
+ reloadCombos();
+}
+
+IngredientInputWidget::~IngredientInputWidget()
+{
+}
+
+void IngredientInputWidget::clear()
+{
+ //clearing the first input deletes all substitute inputs
+ m_ingInputs[0]->clear();
+}
+
+void IngredientInputWidget::updateInputs(bool on, IngredientInput* input)
+{
+ TQValueList<IngredientInput*>::iterator curr = m_ingInputs.find(input);
+ IngredientInput *prev_input = *curr;
+ ++curr;
+
+ if ( on ) {
+ IngredientInput *new_input = new IngredientInput(database,this,false);
+ new_input->reloadCombos();
+
+ TQWidget::setTabOrder( prev_input, new_input );
+ new_input->updateTabOrder();
+
+ connect( new_input, TQ_SIGNAL(addIngredient()), this, TQ_SLOT(addIngredient()) );
+ connect( new_input, TQ_SIGNAL(orToggled(bool,IngredientInput*)), this, TQ_SLOT(updateInputs(bool,IngredientInput*)) );
+
+ new_input->show();
+ m_ingInputs.insert(curr,new_input);
+
+ m_ingInputs[0]->enableHeader(false);
+
+ }
+ else {
+ while ( curr != m_ingInputs.end() ) {
+ (*curr)->deleteLater();
+ curr = m_ingInputs.remove(curr);
+ }
+ if ( m_ingInputs.count() == 1 )
+ m_ingInputs[0]->enableHeader(true);
+ }
+}
+
+void IngredientInputWidget::addIngredient()
+{
+ if ( m_ingInputs[0]->isHeader() ) {
+ TQString header = m_ingInputs[0]->header();
+ if ( header.isEmpty() )
+ return;
+
+ int group_id = createNewGroupIfNecessary( header,database );
+ emit headerEntered( Element(header,group_id) );
+ }
+ else {
+ for ( TQValueList<IngredientInput*>::iterator it = m_ingInputs.begin(); it != m_ingInputs.end(); ++it ) {
+ if ( !(*it)->isInputValid() )
+ return;
+ }
+
+ TQValueList<IngredientData> list;
+ for ( TQValueList<IngredientInput*>::const_iterator it = m_ingInputs.begin(); it != m_ingInputs.end(); ++it ) {
+ Ingredient ing = (*it)->ingredient();
+ ing.ingredientID = createNewIngredientIfNecessary(ing.name,database);
+
+ bool plural = ing.amount+ing.amount_offset > 1;
+ ing.units.id = createNewUnitIfNecessary( (plural)?ing.units.plural:ing.units.name, plural, ing.ingredientID, ing.units,database );
+ if ( ing.units.id == -1 ) // this will happen if the dialog to create a unit was cancelled
+ return ;
+
+ TQValueList<int> prepIDs = createNewPrepIfNecessary( ing.prepMethodList,database );
+ TQValueList<int>::const_iterator id_it = prepIDs.begin();
+ for ( ElementList::iterator it = ing.prepMethodList.begin(); it != ing.prepMethodList.end(); ++it, ++id_it ) {
+ (*it).id = *id_it;
+ }
+
+ list.append(ing);
+ }
+
+ Ingredient ing = list.first();
+ list.pop_front();
+ ing.substitutes = list;
+ emit ingredientEntered( ing );
+ }
+ clear();
+
+ m_ingInputs[0]->setFocus(); //put cursor back to the ingredient name so user can begin next ingredient
+}
+
+int IngredientInputWidget::createNewIngredientIfNecessary( const TQString &ing, RecipeDB *database )
+{
+ int id = -1;
+ if ( ing.isEmpty() )
+ return -1;
+
+ id = database->findExistingIngredientByName( ing );
+ if ( id == -1 ) {
+ database->createNewIngredient( ing );
+ id = database->lastInsertID();
+ }
+ return id;
+}
+
+int IngredientInputWidget::createNewUnitIfNecessary( const TQString &unit, bool plural, int ingredientID, Unit &new_unit, RecipeDB *database )
+{
+ int id = database->findExistingUnitByName( unit );
+ if ( -1 == id )
+ {
+ CreateUnitDialog getUnit( 0, ( plural ) ? TQString::null : unit, ( !plural ) ? TQString::null : unit );
+ if ( getUnit.exec() == TQDialog::Accepted ) {
+ new_unit = getUnit.newUnit();
+ database->createNewUnit( new_unit );
+
+ id = database->lastInsertID();
+ }
+ }
+
+ if ( !database->ingredientContainsUnit(
+ ingredientID,
+ id ) )
+ {
+ database->addUnitToIngredient(
+ ingredientID,
+ id );
+ }
+
+ new_unit = database->unitName( id );
+
+ //loadUnitListCombo();
+ return id;
+}
+
+TQValueList<int> IngredientInputWidget::createNewPrepIfNecessary( const ElementList &prepMethods, RecipeDB *database )
+{
+ TQValueList<int> ids;
+
+ if ( prepMethods.isEmpty() ) //no prep methods
+ return ids;
+ else
+ {
+ for ( ElementList::const_iterator it = prepMethods.begin(); it != prepMethods.end(); ++it ) {
+ int id = database->findExistingPrepByName( (*it).name.stripWhiteSpace() );
+ if ( id == -1 )
+ {
+ database->createNewPrepMethod( (*it).name.stripWhiteSpace() );
+ id = database->lastInsertID();
+ }
+ ids << id;
+ }
+
+ return ids;
+ }
+}
+
+int IngredientInputWidget::createNewGroupIfNecessary( const TQString &group, RecipeDB *database )
+{
+ if ( group.stripWhiteSpace().isEmpty() ) //no group
+ return -1;
+ else
+ {
+ int id = database->findExistingIngredientGroupByName( group );
+ if ( id == -1 ) //creating new
+ {
+ database->createNewIngGroup( group );
+ id = database->lastInsertID();
+ }
+
+ return id;
+ }
+}
+
+void IngredientInputWidget::reloadCombos()
+{
+ for ( TQValueList<IngredientInput*>::iterator it = m_ingInputs.begin(); it != m_ingInputs.end(); ++it )
+ (*it)->reloadCombos();
+}
+
+#include "ingredientinputwidget.moc"