summaryrefslogtreecommitdiffstats
path: root/src/dialogs/recipeinputdialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dialogs/recipeinputdialog.cpp')
-rw-r--r--src/dialogs/recipeinputdialog.cpp1641
1 files changed, 1641 insertions, 0 deletions
diff --git a/src/dialogs/recipeinputdialog.cpp b/src/dialogs/recipeinputdialog.cpp
new file mode 100644
index 0000000..31ee38e
--- /dev/null
+++ b/src/dialogs/recipeinputdialog.cpp
@@ -0,0 +1,1641 @@
+/***************************************************************************
+* Copyright (C) 2003-2005 by *
+* Unai Garro ([email protected]) *
+* Cyril Bosselut ([email protected]) *
+* Jason Kivlighn ([email protected]) *
+* *
+* Copyright (C) 2006 Jason Kivlighn ([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 "recipeinputdialog.h"
+
+#include <tqstring.h>
+#include <tqlayout.h>
+#include <tqhbox.h>
+#include <tqvbox.h>
+#include <tqimage.h>
+#include <tqmessagebox.h>
+#include <tqtooltip.h>
+#include <tqdatetimeedit.h>
+#include <tqdragobject.h>
+#include <tqbuttongroup.h>
+#include <tqradiobutton.h>
+#include <tqwidgetstack.h>
+#include <tqpainter.h>
+
+#include <tdeapplication.h>
+#include <tdecompletionbox.h>
+#include <tdespell.h>
+#include <kurl.h>
+#include <tdefiledialog.h>
+#include <tdelocale.h>
+#include <tdemessagebox.h>
+#include <kdebug.h>
+#include <kled.h>
+#include <kdialogbase.h>
+
+#include "selectauthorsdialog.h"
+#include "resizerecipedialog.h"
+#include "ingredientparserdialog.h"
+#include "editratingdialog.h"
+#include "createunitdialog.h"
+#include "datablocks/recipe.h"
+#include "datablocks/categorytree.h"
+#include "datablocks/unit.h"
+#include "datablocks/weight.h"
+#include "backends/recipedb.h"
+#include "selectcategoriesdialog.h"
+#include "widgets/fractioninput.h"
+#include "widgets/kretextedit.h"
+#include "widgets/inglistviewitem.h"
+#include "../widgets/ratingdisplaywidget.h"
+#include "widgets/kwidgetlistbox.h"
+#include "widgets/ingredientinputwidget.h"
+#include "image.h" //Initializes default photo
+
+#include "profiling.h"
+
+enum ColorStatus { GreenStatus, RedStatus, YellowStatus };
+
+ClickableLed::ClickableLed( TQWidget *parent ) : KLed(parent)
+{
+}
+
+void ClickableLed::mouseReleaseEvent( TQMouseEvent* )
+{
+ emit clicked();
+}
+
+ImageDropLabel::ImageDropLabel( TQWidget *parent, TQPixmap &_sourcePhoto ) : TQLabel( parent ),
+ sourcePhoto( _sourcePhoto )
+{
+ setAcceptDrops( TRUE );
+}
+
+void ImageDropLabel::dragEnterEvent( TQDragEnterEvent* event )
+{
+ event->accept( TQImageDrag::canDecode( event ) );
+}
+
+void ImageDropLabel::dropEvent( TQDropEvent* event )
+{
+ TQImage image;
+
+ if ( TQImageDrag::decode( event, image ) ) {
+ if ( ( image.width() > width() || image.height() > height() ) || ( image.width() < width() && image.height() < height() ) ) {
+ TQPixmap pm_scaled;
+ pm_scaled.convertFromImage( image.smoothScale( width(), height(), TQImage::ScaleMin ) );
+ setPixmap( pm_scaled );
+
+ sourcePhoto = pm_scaled; // to save scaled later on
+ }
+ else {
+ setPixmap( image );
+ sourcePhoto = image;
+ }
+
+ emit changed();
+ }
+}
+
+
+RecipeInputDialog::RecipeInputDialog( TQWidget* parent, RecipeDB *db ) : TQVBox( parent )
+{
+
+ // Adjust internal parameters
+ loadedRecipe = new Recipe();
+ loadedRecipe->recipeID = -1; // No loaded recipe initially
+ loadedRecipe->title = TQString::null;
+ loadedRecipe->instructions = TQString::null;
+ database = db;
+
+ TDEIconLoader *il = new TDEIconLoader;
+
+ // Tabs
+ tabWidget = new TQTabWidget( this, "tabWidget" );
+ tabWidget->setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding ) );
+
+
+ //------- Recipe Tab -----------------
+ // Recipe Photo
+
+ recipeTab = new TQGroupBox( tabWidget );
+ recipeTab->setFrameStyle( TQFrame::NoFrame );
+ recipeTab->setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding ) );
+
+
+ // Design the Dialog
+ TQGridLayout* recipeLayout = new TQGridLayout( recipeTab, 1, 1, 0, 0 );
+
+ // Border
+ TQSpacerItem* spacer_left = new TQSpacerItem( 10, 10, TQSizePolicy::Fixed, TQSizePolicy::Minimum );
+ recipeLayout->addItem( spacer_left, 1, 0 );
+ TQSpacerItem* spacer_right = new TQSpacerItem( 10, 10, TQSizePolicy::Fixed, TQSizePolicy::Minimum );
+ recipeLayout->addItem( spacer_right, 1, 8 );
+ TQSpacerItem* spacer_top = new TQSpacerItem( 10, 10, TQSizePolicy::Minimum , TQSizePolicy::Fixed );
+ recipeLayout->addItem( spacer_top, 0, 1 );
+ TQSpacerItem* spacer_bottom = new TQSpacerItem( 10, 10, TQSizePolicy::Minimum , TQSizePolicy::MinimumExpanding );
+ recipeLayout->addItem( spacer_bottom, 8, 1 );
+
+
+ TQPixmap image1( defaultPhoto );
+
+ photoLabel = new ImageDropLabel( recipeTab, sourcePhoto );
+ photoLabel->setPixmap( image1 );
+ photoLabel->setFixedSize( TQSize( 221, 166 ) );
+ photoLabel->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed ) );
+ photoLabel->setAlignment( TQt::AlignHCenter | TQt::AlignVCenter );
+ recipeLayout->addMultiCellWidget( photoLabel, 3, 7, 1, 1 );
+
+ TQVBox *photoButtonsBox = new TQVBox( recipeTab );
+
+ changePhotoButton = new TQPushButton( photoButtonsBox );
+ changePhotoButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Ignored ) );
+ changePhotoButton->setText( "..." );
+ TQToolTip::add
+ ( changePhotoButton, i18n( "Select photo" ) );
+
+ TQPushButton *clearPhotoButton = new TQPushButton( photoButtonsBox );
+ clearPhotoButton->setPixmap( il->loadIcon( "clear_left", TDEIcon::NoGroup, 16 ) );
+ TQToolTip::add
+ ( clearPhotoButton, i18n( "Clear photo" ) );
+
+ recipeLayout->addMultiCellWidget( photoButtonsBox, 3, 7, 2, 2 );
+
+
+ //Title->photo spacer
+ TQSpacerItem* title_photo = new TQSpacerItem( 10, 10, TQSizePolicy::Minimum, TQSizePolicy::Fixed );
+ recipeLayout->addItem( title_photo, 2, 3 );
+
+
+ // Title
+ TQVBox *titleBox = new TQVBox( recipeTab );
+ titleBox->setSpacing( 5 );
+ titleLabel = new TQLabel( i18n( "Recipe Name" ), titleBox );
+ titleEdit = new KLineEdit( titleBox );
+ titleEdit->setMinimumSize( TQSize( 360, 30 ) );
+ titleEdit->setMaximumSize( TQSize( 10000, 30 ) );
+ titleEdit->setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed ) );
+ recipeLayout->addMultiCellWidget( titleBox, 1, 1, 1, 7 );
+
+
+ // Photo ->author spacer
+ TQSpacerItem* title_spacer = new TQSpacerItem( 10, 10, TQSizePolicy::Fixed, TQSizePolicy::Minimum );
+ recipeLayout->addItem( title_spacer, 2, 1 );
+
+ // Author(s) & Categories
+ TQVBox *authorBox = new TQVBox( recipeTab ); // contains label and authorInput (input widgets)
+ authorBox->setSpacing( 5 );
+ recipeLayout->addWidget( authorBox, 3, 4 );
+ authorLabel = new TQLabel( i18n( "Authors" ), authorBox );
+ TQHBox *authorInput = new TQHBox( authorBox ); // Contains input + button
+
+
+ authorShow = new KLineEdit( authorInput );
+ authorShow->setReadOnly( true );
+ authorShow->setMinimumSize( TQSize( 100, 20 ) );
+ authorShow->setMaximumSize( TQSize( 10000, 20 ) );
+ authorShow->setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed ) );
+
+
+ addAuthorButton = new TQPushButton( authorInput );
+ addAuthorButton->setText( "+" );
+ addAuthorButton->setFixedSize( TQSize( 20, 20 ) );
+ addAuthorButton->setFlat( true );
+
+
+ TQSpacerItem* author_category = new TQSpacerItem( 10, 10, TQSizePolicy::Fixed, TQSizePolicy::Minimum );
+ recipeLayout->addItem( author_category, 3, 5 );
+
+ TQVBox *categoryBox = new TQVBox( recipeTab ); // Contains the label and categoryInput (input widgets)
+ categoryBox->setSpacing( 5 );
+ categoryLabel = new TQLabel( i18n( "Categories" ), categoryBox );
+ TQHBox *categoryInput = new TQHBox( categoryBox ); // Contains the input widgets
+
+ categoryShow = new KLineEdit( categoryInput );
+ categoryShow->setReadOnly( true );
+ categoryShow->setMinimumSize( TQSize( 100, 20 ) );
+ categoryShow->setMaximumSize( TQSize( 10000, 20 ) );
+ categoryShow->setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed ) );
+ recipeLayout->addWidget( categoryBox, 4, 4 );
+
+ addCategoryButton = new TQPushButton( categoryInput );
+ addCategoryButton->setText( "+" );
+ addCategoryButton->setFixedSize( TQSize( 20, 20 ) );
+ addCategoryButton->setFlat( true );
+
+ //Category ->Servings spacer
+ TQSpacerItem* category_yield = new TQSpacerItem( 10, 10, TQSizePolicy::Minimum, TQSizePolicy::Fixed );
+ recipeLayout->addItem( category_yield, 5, 4 );
+
+ TQHBox *serv_prep_box = new TQHBox( recipeTab );
+ serv_prep_box->setSpacing( 5 );
+
+ // Backup options
+ TQGroupBox *yieldGBox = new TQGroupBox( serv_prep_box, "yieldGBox" );
+ yieldGBox->setTitle( i18n( "Yield" ) );
+ yieldGBox->setColumns( 2 );
+
+ yieldLabel = new TQLabel( i18n( "Amount" ), yieldGBox );
+ /*TQLabel *yieldTypeLabel = */new TQLabel( i18n( "Type" ), yieldGBox );
+ yieldNumInput = new FractionInput( yieldGBox );
+ yieldNumInput->setAllowRange(true);
+ yieldTypeEdit = new KLineEdit( yieldGBox );
+
+ TQVBox *prepTimeBox = new TQVBox( serv_prep_box );
+ prepTimeBox->setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ) );
+ prepTimeBox->setSpacing( 5 );
+
+ ( void ) new TQLabel( i18n( "Preparation Time" ), prepTimeBox );
+ prepTimeEdit = new TQTimeEdit( prepTimeBox );
+ prepTimeEdit->setMinValue( TQTime( 0, 0 ) );
+ prepTimeEdit->setDisplay( TQTimeEdit::Hours | TQTimeEdit::Minutes );
+
+ recipeLayout->addWidget( serv_prep_box, 6, 4 );
+
+ //------- END OF Recipe Tab ---------------
+
+ //------- Ingredients Tab -----------------
+
+ ingredientGBox = new TQGroupBox( recipeTab );
+ ingredientGBox->setFrameStyle( TQFrame::NoFrame );
+ ingredientGBox->setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding ) );
+ TQGridLayout* ingredientsLayout = new TQGridLayout( ingredientGBox );
+
+ // Border
+ TQSpacerItem* spacerBoxLeft = new TQSpacerItem( 10, 10, TQSizePolicy::Fixed, TQSizePolicy::Minimum );
+ ingredientsLayout->addItem( spacerBoxLeft, 1, 0 );
+ TQSpacerItem* spacerBoxTop = new TQSpacerItem( 10, 20, TQSizePolicy::Minimum, TQSizePolicy::Fixed );
+ ingredientsLayout->addItem( spacerBoxTop, 0, 1 );
+
+ //Input Widgets
+ ingInput = new IngredientInputWidget( database, ingredientGBox );
+ ingredientsLayout->addMultiCellWidget( ingInput, 1, 1, 1, 5 );
+
+ // Spacers to list and buttons
+ TQSpacerItem* spacerToList = new TQSpacerItem( 10, 10, TQSizePolicy::Minimum, TQSizePolicy::Fixed );
+ ingredientsLayout->addItem( spacerToList, 2, 1 );
+ TQSpacerItem* spacerToButtons = new TQSpacerItem( 10, 10, TQSizePolicy::Fixed, TQSizePolicy::Minimum );
+ ingredientsLayout->addItem( spacerToButtons, 3, 4 );
+
+ // Add, Up,down,... buttons
+
+ addButton = new KPushButton( ingredientGBox );
+ addButton->setFixedSize( TQSize( 31, 31 ) );
+ addButton->setFlat( true );
+ TQPixmap pm = il->loadIcon( "new", TDEIcon::NoGroup, 16 );
+ addButton->setPixmap( pm );
+ addButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed ) );
+ ingredientsLayout->addWidget( addButton, 3, 5 );
+
+ // Spacer to the rest of buttons
+ TQSpacerItem* spacerToOtherButtons = new TQSpacerItem( 10, 10, TQSizePolicy::Minimum, TQSizePolicy::Fixed );
+ ingredientsLayout->addItem( spacerToOtherButtons, 4, 5 );
+
+ upButton = new KPushButton( ingredientGBox );
+ upButton->setFixedSize( TQSize( 31, 31 ) );
+ upButton->setFlat( true );
+ pm = il->loadIcon( "go-up", TDEIcon::NoGroup, 16 );
+ upButton->setPixmap( pm );
+ upButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed ) );
+ ingredientsLayout->addWidget( upButton, 5, 5 );
+
+ downButton = new KPushButton( ingredientGBox );
+ downButton->setFixedSize( TQSize( 31, 31 ) );
+ downButton->setFlat( true );
+ pm = il->loadIcon( "go-down", TDEIcon::NoGroup, 16 );
+ downButton->setPixmap( pm );
+ downButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed ) );
+ ingredientsLayout->addWidget( downButton, 6, 5 );
+
+ removeButton = new KPushButton( ingredientGBox );
+ removeButton->setFixedSize( TQSize( 31, 31 ) );
+ removeButton->setFlat( true );
+ pm = il->loadIcon( "remove", TDEIcon::NoGroup, 16 );
+ removeButton->setPixmap( pm );
+ removeButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed ) );
+ ingredientsLayout->addWidget( removeButton, 7, 5 );
+
+ ingParserButton = new KPushButton( ingredientGBox );
+ ingParserButton->setFixedSize( TQSize( 31, 31 ) );
+ ingParserButton->setFlat( true );
+ pm = il->loadIcon( "edit-paste", TDEIcon::NoGroup, 16 );
+ ingParserButton->setPixmap( pm );
+ ingParserButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed ) );
+ ingredientsLayout->addWidget( ingParserButton, 8, 5 );
+
+ TQToolTip::add
+ ( addButton, i18n( "Add ingredient" ) );
+ TQToolTip::add
+ ( upButton, i18n( "Move ingredient up" ) );
+ TQToolTip::add
+ ( downButton, i18n( "Move ingredient down" ) );
+ TQToolTip::add
+ ( removeButton, i18n( "Remove ingredient" ) );
+ TQToolTip::add
+ ( ingParserButton, i18n( "Paste Ingredients" ) );
+
+ // Ingredient List
+ ingredientList = new TDEListView( ingredientGBox, "ingredientList" );
+ ingredientList->addColumn( i18n( "Ingredient" ) );
+ ingredientList->addColumn( i18n( "Amount" ) );
+ ingredientList->setColumnAlignment( 1, TQt::AlignHCenter );
+ ingredientList->addColumn( i18n( "Units" ) );
+ ingredientList->addColumn( i18n( "Preparation Method" ) );
+ ingredientList->setSorting( -1 ); // Do not sort
+ ingredientList->setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::MinimumExpanding ) );
+ ingredientList->setItemsRenameable( true );
+ ingredientList->setRenameable( 0, false ); //name
+ ingredientList->setRenameable( 1, true ); //amount
+ ingredientList->setRenameable( 2, true ); //units
+ ingredientList->setRenameable( 3, true ); //prep method
+ ingredientList->setDefaultRenameAction( TQListView::Reject );
+ ingredientsLayout->addMultiCellWidget( ingredientList, 3, 9, 1, 3 );
+
+ TQHBoxLayout *propertyStatusLayout = new TQHBoxLayout( NULL, 0, 5 );
+ TQLabel *propertyLabel = new TQLabel( i18n("Property Status:"), ingredientGBox );
+ propertyStatusLabel = new TQLabel( ingredientGBox );
+ propertyStatusLed = new ClickableLed( ingredientGBox );
+ propertyStatusLed->setFixedSize( TQSize(16,16) );
+ propertyStatusButton = new TQPushButton( i18n("Details..."), ingredientGBox );
+ //TQPushButton *propertyUpdateButton = new TQPushButton( i18n("Update"), ingredientGBox );
+ propertyStatusLayout->addWidget( propertyLabel );
+ propertyStatusLayout->addWidget( propertyStatusLabel );
+ propertyStatusLayout->addWidget( propertyStatusLed );
+ propertyStatusLayout->addWidget( propertyStatusButton );
+ //propertyStatusLayout->addWidget( propertyUpdateButton );
+ TQSpacerItem* propertySpacerRight = new TQSpacerItem( 10, 10, TQSizePolicy::MinimumExpanding, TQSizePolicy::Minimum );
+ propertyStatusLayout->addItem( propertySpacerRight );
+
+ KGuiItem updateGuiItem;
+ updateGuiItem.setText( i18n("Update") );
+ updateGuiItem.setIconSet( il->loadIconSet( "reload", TDEIcon::NoGroup ) );
+ propertyStatusDialog = new KDialogBase( KDialogBase::Swallow, i18n("Property details"),
+ KDialogBase::Close | KDialogBase::User1 | KDialogBase::Help,
+ KDialogBase::Close, this, "propertyStatusDialog", false, false,
+ updateGuiItem
+ );
+ propertyStatusDialog->setHelp("property-status");
+ statusTextView = new TQTextEdit(0);
+ statusTextView->setTextFormat( TQt::RichText );
+ statusTextView->setReadOnly(true);
+ propertyStatusDialog->setMainWidget( statusTextView );
+ propertyStatusDialog->resize( 400, 300 );
+
+ ingredientsLayout->addMultiCellLayout( propertyStatusLayout, 10, 10, 1, 4 );
+
+ // ------- Recipe Instructions Tab -----------
+
+ instructionsTab = new TQGroupBox( recipeTab );
+ instructionsTab->setFrameStyle( TQFrame::NoFrame );
+ instructionsTab->setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding ) );
+
+ TQVBoxLayout *instructionsLayout = new TQVBoxLayout( instructionsTab );
+
+ instructionsEdit = new KreTextEdit( instructionsTab );
+ instructionsEdit->setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding ) );
+ instructionsEdit->setTabChangesFocus ( true );
+ instructionsLayout->addWidget( instructionsEdit );
+
+ spellCheckButton = new TQToolButton( instructionsTab );
+ spellCheckButton->setIconSet( il->loadIconSet( "tools-check-spelling", TDEIcon::Small ) );
+ TQToolTip::add
+ ( spellCheckButton, i18n( "Check spelling" ) );
+ instructionsLayout->addWidget( spellCheckButton );
+
+ // ------- END OF Recipe Instructions Tab -----------
+
+
+ // ------- Recipe Ratings Tab -----------
+
+ TQVBox *ratingsTab = new TQVBox(recipeTab);
+ ratingListDisplayWidget = new KWidgetListbox(ratingsTab);
+ TQPushButton *addRatingButton = new TQPushButton(i18n("Add Rating..."),ratingsTab);
+
+ connect( addRatingButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotAddRating()) );
+
+ // ------- END OF Recipe Ratings Tab -----------
+
+
+ tabWidget->insertTab( recipeTab, i18n( "Recipe" ) );
+ tabWidget->insertTab( ingredientGBox, i18n( "Ingredients" ) );
+ tabWidget->insertTab( instructionsTab, i18n( "Instructions" ) );
+ tabWidget->insertTab( ratingsTab, i18n( "Ratings" ) );
+
+
+ // Functions Box
+ TQHBox* functionsLayout = new TQHBox( this );
+
+ functionsBox = new TQGroupBox( 1, TQt::Vertical, functionsLayout );
+ functionsBox->setFrameStyle( TQFrame::NoFrame );
+
+ saveButton = new TQToolButton( functionsBox );
+ saveButton->setIconSet( il->loadIconSet( "document-save", TDEIcon::Small ) );
+ saveButton->setEnabled( false );
+ showButton = new TQToolButton( functionsBox );
+ showButton->setIconSet( il->loadIconSet( "viewmag", TDEIcon::Small ) );
+ closeButton = new TQToolButton( functionsBox );
+ closeButton->setIconSet( il->loadIconSet( "window-close", TDEIcon::Small ) );
+ resizeButton = new TQToolButton( functionsBox );
+ resizeButton->setIconSet( il->loadIconSet( "2uparrow", TDEIcon::Small ) ); //TODO: give me an icon :)
+
+ saveButton->setTextLabel( i18n( "Save recipe" ), true );
+ saveButton->setUsesTextLabel( true );
+ showButton->setTextLabel( i18n( "Show recipe" ), true );
+ showButton->setUsesTextLabel( true );
+ closeButton->setTextLabel( i18n( "Close" ), true );
+ closeButton->setUsesTextLabel( true );
+ resizeButton->setTextLabel( i18n( "Resize recipe" ), true );
+ resizeButton->setUsesTextLabel( true );
+
+ functionsLayout->layout() ->addItem( new TQSpacerItem( 10, 10, TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed ) );
+
+ // Dialog design
+ tabWidget->resize( size().expandedTo( minimumSizeHint() ) );
+ clearWState( WState_Polished );
+
+ // Initialize internal data
+ unsavedChanges = false; // Indicates if there's something not saved yet.
+ enableChangedSignal(); // Enables the signal "changed()"
+
+ // Connect signals & Slots
+ connect( changePhotoButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( changePhoto() ) );
+ connect( clearPhotoButton, TQ_SIGNAL( clicked() ), TQ_SLOT( clearPhoto() ) );
+ connect( upButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( moveIngredientUp() ) );
+ connect( downButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( moveIngredientDown() ) );
+ connect( removeButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( removeIngredient() ) );
+ connect( addButton, TQ_SIGNAL( clicked() ), ingInput, TQ_SLOT( addIngredient() ) );
+ connect( ingParserButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotIngredientParser() ) );
+ connect( photoLabel, TQ_SIGNAL( changed() ), this, TQ_SIGNAL( changed() ) );
+ connect( this, TQ_SIGNAL( changed() ), this, TQ_SLOT( recipeChanged() ) );
+ connect( yieldNumInput, TQ_SIGNAL( textChanged( const TQString & ) ), this, TQ_SLOT( recipeChanged() ) );
+ connect( yieldTypeEdit, TQ_SIGNAL( textChanged( const TQString & ) ), this, TQ_SLOT( recipeChanged() ) );
+ connect( prepTimeEdit, TQ_SIGNAL( valueChanged( const TQTime & ) ), TQ_SLOT( recipeChanged() ) );
+ connect( titleEdit, TQ_SIGNAL( textChanged( const TQString& ) ), this, TQ_SLOT( recipeChanged( const TQString& ) ) );
+ connect( instructionsEdit, TQ_SIGNAL( textChanged() ), this, TQ_SLOT( recipeChanged() ) );
+ connect( addCategoryButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( addCategory() ) );
+ connect( addAuthorButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( addAuthor() ) );
+ connect( titleEdit, TQ_SIGNAL( textChanged( const TQString& ) ), this, TQ_SLOT( prepTitleChanged( const TQString& ) ) );
+ connect( ingredientList, TQ_SIGNAL( itemRenamed( TQListViewItem*, const TQString &, int ) ), TQ_SLOT( syncListView( TQListViewItem*, const TQString &, int ) ) );
+
+ connect ( ingInput, TQ_SIGNAL( ingredientEntered(const Ingredient&) ), this, TQ_SLOT( addIngredient(const Ingredient&) ) );
+ connect ( ingInput, TQ_SIGNAL( headerEntered(const Element&) ), this, TQ_SLOT( addIngredientHeader(const Element&) ) );
+
+ connect( propertyStatusLed, TQ_SIGNAL(clicked()), TQ_SLOT(updatePropertyStatus()) );
+ connect( propertyStatusDialog, TQ_SIGNAL(user1Clicked()), TQ_SLOT(updatePropertyStatus()) );
+ connect( propertyStatusButton, TQ_SIGNAL(clicked()), propertyStatusDialog, TQ_SLOT(show()) );
+
+ // Function buttons
+ connect ( saveButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( save() ) );
+ connect ( closeButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( closeOptions() ) );
+ connect ( showButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( showRecipe() ) );
+ connect ( resizeButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( resizeRecipe() ) );
+ connect ( spellCheckButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( spellCheck() ) );
+ connect ( this, TQ_SIGNAL( enableSaveOption( bool ) ), this, TQ_SLOT( enableSaveButton( bool ) ) );
+
+ connect ( database, TQ_SIGNAL( recipeRemoved(int) ), this, TQ_SLOT( recipeRemoved(int) ) );
+
+ delete il;
+
+ //FIXME: We've got some sort of build issue... we get undefined references to CreateUnitDialog without this dummy code here
+ CreateUnitDialog d( this, "" );
+}
+
+
+RecipeInputDialog::~RecipeInputDialog()
+{
+ delete loadedRecipe;
+}
+
+void RecipeInputDialog::recipeRemoved( int id )
+{
+ if ( loadedRecipe->recipeID == id ) {
+ loadedRecipe->recipeID = -1;
+ recipeChanged();
+ }
+}
+
+void RecipeInputDialog::prepTitleChanged( const TQString &title )
+{
+ //we don't want the menu to grow due to a long title
+ //### KStringHandler::rsqueeze does this but I can't remember when it was added (compatibility issue...)
+ TQString short_title = title.left( 20 );
+ if ( title.length() > 20 )
+ short_title.append( "..." );
+
+ emit titleChanged( short_title );
+}
+
+int RecipeInputDialog::loadedRecipeID() const
+{
+ return loadedRecipe->recipeID;
+}
+
+void RecipeInputDialog::loadRecipe( int recipeID )
+{
+ emit enableSaveOption( false );
+ unsavedChanges = false;
+
+ //Disable changed() signals
+ enableChangedSignal( false );
+
+ //Empty current recipe
+ loadedRecipe->empty();
+
+ //Set back to the first page
+ tabWidget->setCurrentPage( 0 );
+
+ // Load specified Recipe ID
+ database->loadRecipe( loadedRecipe, RecipeDB::All ^ RecipeDB::Meta ^ RecipeDB::Properties, recipeID );
+
+ reload();
+
+ propertyStatusDialog->hide();
+ updatePropertyStatus();
+
+ //Enable changed() signals
+ enableChangedSignal();
+
+}
+
+void RecipeInputDialog::reload( void )
+{
+ yieldNumInput->setValue( 1, 0 );
+ yieldTypeEdit->setText("");
+ ingredientList->clear();
+ ratingListDisplayWidget->clear();
+ ingInput->clear();
+
+ //Load Values in Interface
+ titleEdit->setText( loadedRecipe->title );
+ instructionsEdit->setText( loadedRecipe->instructions );
+ yieldNumInput->setValue( loadedRecipe->yield.amount, loadedRecipe->yield.amount_offset );
+ yieldTypeEdit->setText( loadedRecipe->yield.type );
+ prepTimeEdit->setTime( loadedRecipe->prepTime );
+
+ //show ingredient list
+ IngredientList list_copy = loadedRecipe->ingList;
+ for ( IngredientList group_list = list_copy.firstGroup(); group_list.count() != 0; group_list = list_copy.nextGroup() ) {
+ TQListViewItem * lastElement = ingredientList->lastItem();
+ TQListViewItem *ing_header = 0;
+
+ TQString group = group_list[ 0 ].group;
+ if ( !group.isEmpty() ) {
+ if ( lastElement && lastElement->parent() )
+ lastElement = lastElement->parent();
+
+ ing_header = new IngGrpListViewItem( ingredientList, lastElement, group_list[ 0 ].group, group_list[ 0 ].groupID );
+ ing_header->setOpen( true );
+ lastElement = ing_header;
+ }
+
+ for ( IngredientList::const_iterator ing_it = group_list.begin(); ing_it != group_list.end(); ++ing_it ) {
+ //Insert ingredient after last one
+ if ( ing_header ) {
+ lastElement = new IngListViewItem ( ing_header, lastElement, *ing_it );
+ }
+ else {
+ if ( lastElement && lastElement->parent() )
+ lastElement = lastElement->parent();
+ lastElement = new IngListViewItem ( ingredientList, lastElement, *ing_it );
+ }
+
+ for ( TQValueList<IngredientData>::const_iterator sub_it = (*ing_it).substitutes.begin(); sub_it != (*ing_it).substitutes.end(); ++sub_it ) {
+ new IngSubListViewItem ( lastElement, *sub_it );
+ lastElement->setOpen(true);
+ }
+
+ //update completion
+ instructionsEdit->addCompletionItem( ( *ing_it ).name );
+ }
+ }
+ //
+ //show photo
+ if ( !loadedRecipe->photo.isNull() ) {
+
+ // //get the photo
+ sourcePhoto = loadedRecipe->photo;
+
+ if ( ( sourcePhoto.width() > photoLabel->width() || sourcePhoto.height() > photoLabel->height() ) || ( sourcePhoto.width() < photoLabel->width() && sourcePhoto.height() < photoLabel->height() ) ) {
+ TQImage pm = sourcePhoto.convertToImage();
+ TQPixmap pm_scaled;
+ pm_scaled.convertFromImage( pm.smoothScale( photoLabel->width(), photoLabel->height(), TQImage::ScaleMin ) );
+ photoLabel->setPixmap( pm_scaled );
+
+ sourcePhoto = pm_scaled; // to save scaled later on
+ }
+ else {
+ photoLabel->setPixmap( sourcePhoto );
+ }
+ }
+ else {
+ TQPixmap photo = TQPixmap( defaultPhoto );
+ photoLabel->setPixmap( photo );
+ sourcePhoto = TQPixmap();
+ }
+
+
+ // Show categories
+ showCategories();
+
+ // Show authors
+ showAuthors();
+
+ // Show ratings
+ for ( RatingList::iterator rating_it = loadedRecipe->ratingList.begin(); rating_it != loadedRecipe->ratingList.end(); ++rating_it ) {
+ RatingDisplayWidget *item = new RatingDisplayWidget;
+ item->rating_it = rating_it;
+ addRating(*rating_it,item);
+ ratingListDisplayWidget->insertItem(item);
+ }
+ ratingListDisplayWidget->ensureCellVisible(0,0);
+
+ // Update yield type auto completion
+ TDECompletion *completion = yieldTypeEdit->completionObject();
+ completion->clear();
+ ElementList yieldList;
+ database->loadYieldTypes( &yieldList );
+ for ( ElementList::const_iterator it = yieldList.begin(); it != yieldList.end(); ++it ) {
+ completion->addItem( (*it).name );
+ }
+}
+
+void RecipeInputDialog::changePhoto( void )
+{
+ // standard filedialog
+ KURL filename = KFileDialog::getOpenURL( TQString::null, TQString( "*.png *.jpg *.jpeg *.xpm *.gif|%1 (*.png *.jpg *.jpeg *.xpm *.gif)" ).arg( i18n( "Images" ) ), this );
+ TQPixmap pixmap ( filename.path() );
+ if ( !( pixmap.isNull() ) ) {
+ // If photo is bigger than the label, or smaller in width, than photoLabel, scale it
+ sourcePhoto = pixmap;
+ if ( ( sourcePhoto.width() > photoLabel->width() || sourcePhoto.height() > photoLabel->height() ) || ( sourcePhoto.width() < photoLabel->width() && sourcePhoto.height() < photoLabel->height() ) ) {
+ TQImage pm = sourcePhoto.convertToImage();
+ TQPixmap pm_scaled;
+ pm_scaled.convertFromImage( pm.smoothScale( photoLabel->width(), photoLabel->height(), TQImage::ScaleMin ) );
+ photoLabel->setPixmap( pm_scaled );
+
+ sourcePhoto = pm_scaled; // to save scaled later on
+ photoLabel->setPixmap( pm_scaled );
+ }
+ else {
+ photoLabel->setPixmap( sourcePhoto );
+ }
+ emit changed();
+ }
+}
+
+void RecipeInputDialog::clearPhoto( void )
+{
+ sourcePhoto = TQPixmap();
+ photoLabel->setPixmap( TQPixmap( defaultPhoto ) );
+
+ emit changed();
+}
+
+void RecipeInputDialog::moveIngredientUp( void )
+{
+ TQListViewItem * it = ingredientList->selectedItem();
+ if ( !it || it->rtti() == INGSUBLISTVIEWITEM_RTTI )
+ return ;
+
+ TQListViewItem *iabove = it->itemAbove();
+ while ( iabove && iabove->rtti() == INGSUBLISTVIEWITEM_RTTI )
+ iabove = iabove->itemAbove();
+
+ if ( iabove ) {
+ if ( it->rtti() == INGGRPLISTVIEWITEM_RTTI ) {
+ if ( iabove->parent() )
+ iabove = iabove->parent();
+
+ int it_index = ingItemIndex( ingredientList, it );
+ int iabove_index = ingItemIndex( ingredientList, iabove );
+
+ iabove->moveItem( it ); //Move the Item
+
+ loadedRecipe->ingList.move( iabove_index, ( iabove->rtti() == INGGRPLISTVIEWITEM_RTTI ) ? iabove->childCount() : 1, it_index + it->childCount() - 1 );
+ }
+ else {
+ int it_index = ingItemIndex( ingredientList, it );
+ int iabove_index = ingItemIndex( ingredientList, iabove );
+ IngredientList::iterator ing = loadedRecipe->ingList.at( it_index );
+
+ if ( iabove->parent() != it->parent() ) {
+ if ( iabove->rtti() == INGGRPLISTVIEWITEM_RTTI && it->parent() ) { //move the item out of the group
+ it->parent() ->takeItem( it );
+ ingredientList->insertItem( it );
+ it->moveItem( ( iabove->itemAbove() ->parent() ) ? iabove->itemAbove() ->parent() : iabove->itemAbove() ); //Move the Item
+ }
+ else { //move the item into the group
+ ingredientList->takeItem( it );
+ iabove->parent() ->insertItem( it );
+ it->moveItem( iabove ); //Move the Item
+ }
+
+ ingredientList->setCurrentItem( it ); //Keep selected
+ }
+ else {
+ iabove->moveItem( it ); //Move the Item
+ loadedRecipe->ingList.move( it_index, iabove_index );
+ }
+
+ if ( it->parent() )
+ ( *ing ).groupID = ( ( IngGrpListViewItem* ) it->parent() ) ->id();
+ else
+ ( *ing ).groupID = -1;
+ }
+
+ emit changed();
+ }
+}
+
+void RecipeInputDialog::moveIngredientDown( void )
+{
+ TQListViewItem * it = ingredientList->selectedItem();
+ if ( !it || it->rtti() == INGSUBLISTVIEWITEM_RTTI )
+ return ;
+
+ TQListViewItem *ibelow = it->itemBelow();
+ while ( ibelow && ibelow->rtti() == INGSUBLISTVIEWITEM_RTTI )
+ ibelow = ibelow->itemBelow();
+
+ if ( ibelow ) {
+ if ( it->rtti() == INGGRPLISTVIEWITEM_RTTI ) {
+ TQListViewItem * next_sibling = it->nextSibling();
+
+ if ( next_sibling ) {
+ int it_index = ingItemIndex( ingredientList, it );
+ int ibelow_index = ingItemIndex( ingredientList, next_sibling );
+
+ it->moveItem( next_sibling ); //Move the Item
+
+ int skip = 0;
+ if ( next_sibling->childCount() > 0 )
+ skip = next_sibling->childCount() - 1;
+
+ loadedRecipe->ingList.move( it_index, it->childCount(), ibelow_index + skip );
+ }
+ }
+ else {
+ int it_index = ingItemIndex( ingredientList, it );
+ int ibelow_index = ingItemIndex( ingredientList, ibelow );
+ IngredientList::iterator ing = loadedRecipe->ingList.at( it_index );
+
+ if ( ibelow->rtti() == INGGRPLISTVIEWITEM_RTTI || ( ibelow->parent() != it->parent() ) ) {
+ if ( ibelow->rtti() == INGGRPLISTVIEWITEM_RTTI && !it->parent() ) { //move the item into the group
+ if ( !it->parent() )
+ ingredientList->takeItem( it );
+ else
+ it->parent() ->takeItem( it );
+
+ ibelow->insertItem( it );
+ }
+ else { //move the item out of the group
+ TQListViewItem *parent = it->parent(); //store this because we can't get it after we do it->takeItem()
+ parent->takeItem( it );
+ ingredientList->insertItem( it );
+ it->moveItem( parent ); //Move the Item
+ }
+
+ ingredientList->setCurrentItem( it ); //Keep selected
+ }
+ else {
+ it->moveItem( ibelow ); //Move the Item
+ loadedRecipe->ingList.move( it_index, ibelow_index );
+ }
+
+ if ( it->parent() )
+ ( *ing ).groupID = ( ( IngGrpListViewItem* ) it->parent() ) ->id();
+ else
+ ( *ing ).groupID = -1;
+ }
+
+ emit changed();
+ }
+ else if ( it->parent() ) {
+ it->parent() ->takeItem( it );
+ ingredientList->insertItem( it );
+ it->moveItem( ( ingredientList->lastItem() ->parent() ) ? ingredientList->lastItem() ->parent() : ingredientList->lastItem() ); //Move the Item
+ ingredientList->setCurrentItem( it ); //Keep selected
+
+ int it_index = ingItemIndex( ingredientList, it );
+ IngredientList::iterator ing = loadedRecipe->ingList.at( it_index );
+ ( *ing ).groupID = -1;
+
+ emit changed();
+ }
+}
+
+void RecipeInputDialog::removeIngredient( void )
+{
+ TQListViewItem * it = ingredientList->selectedItem();
+ if ( it && (it->rtti() == INGLISTVIEWITEM_RTTI || it->rtti() == INGSUBLISTVIEWITEM_RTTI) ) {
+ TQListViewItem *iselect = it->itemBelow();
+ while ( iselect && iselect->rtti() == INGSUBLISTVIEWITEM_RTTI )
+ iselect = iselect->itemBelow();
+
+ if ( !iselect ) {
+ iselect = it->itemAbove();
+ while ( iselect && iselect->rtti() == INGSUBLISTVIEWITEM_RTTI )
+ iselect = iselect->itemAbove();
+ }
+
+ IngListViewItem *ing_item = (IngListViewItem*)it; //we can cast IngSubListViewItem to this too, it's a subclass
+
+ IngredientData &ing = loadedRecipe->ingList.findSubstitute( ing_item->ingredient() );
+
+ //Remove it from the instruction's completion
+ instructionsEdit->removeCompletionItem( ing.name );
+
+ loadedRecipe->ingList.removeSubstitute( ing );
+
+ int ingID = ing_item->ingredient().ingredientID;
+ TQMap<int,TQString>::iterator map_it;
+ if ( (map_it = propertyStatusMapRed.find(ingID)) != propertyStatusMapRed.end() )
+ propertyStatusMapRed.remove( map_it );
+ else if ( (map_it = propertyStatusMapYellow.find(ingID)) != propertyStatusMapYellow.end() )
+ propertyStatusMapYellow.remove( map_it );
+ showStatusIndicator();
+
+ //Now remove the ingredient
+ it->setSelected( false );
+ delete it;
+ if ( iselect )
+ ingredientList->setSelected( iselect, true ); // be careful iselect->setSelected doesn't work this way.
+
+ emit changed();
+ }
+ else if ( it && it->rtti() == INGGRPLISTVIEWITEM_RTTI ) {
+ IngGrpListViewItem * header = ( IngGrpListViewItem* ) it;
+
+ for ( IngListViewItem * sub_item = (IngListViewItem*)header->firstChild(); sub_item; sub_item = (IngListViewItem*)sub_item->nextSibling() ) {
+ IngredientData &ing = loadedRecipe->ingList.findSubstitute( sub_item->ingredient() );
+
+ //Remove it from the instruction's completion
+ instructionsEdit->removeCompletionItem( ing.name );
+
+ loadedRecipe->ingList.removeSubstitute( ing );
+
+ int ingID = sub_item->ingredient().ingredientID;
+ TQMap<int,TQString>::iterator map_it;
+ if ( (map_it = propertyStatusMapRed.find(ingID)) != propertyStatusMapRed.end() )
+ propertyStatusMapRed.remove( map_it );
+ else if ( (map_it = propertyStatusMapYellow.find(ingID)) != propertyStatusMapYellow.end() )
+ propertyStatusMapYellow.remove( map_it );
+ showStatusIndicator();
+ }
+
+ delete header;
+
+ emit changed();
+ }
+
+}
+
+int RecipeInputDialog::createNewYieldIfNecessary( const TQString &yield )
+{
+ if ( yield.stripWhiteSpace().isEmpty() ) //no yield
+ return -1;
+ else
+ {
+ int id = database->findExistingYieldTypeByName( yield );
+ if ( id == -1 ) //creating new
+ {
+ database->createNewYieldType( yield );
+ id = database->lastInsertID();
+ }
+
+ return id;
+ }
+}
+
+void RecipeInputDialog::syncListView( TQListViewItem* it, const TQString &new_text, int col )
+{
+ if ( it->rtti() != INGLISTVIEWITEM_RTTI )
+ return ;
+
+ IngListViewItem *ing_item = ( IngListViewItem* ) it;
+
+ IngredientData &new_ing = loadedRecipe->ingList.findSubstitute( ing_item->ingredient() );
+
+ switch ( col ) {
+ case 1: //amount
+ {
+ bool ok;
+
+ Ingredient new_ing_amount;
+ new_ing_amount.setAmount(new_text,&ok);
+
+ if ( ok )
+ {
+ if ( new_ing.amount != new_ing_amount.amount ||
+ new_ing.amount_offset != new_ing_amount.amount_offset ) {
+ new_ing.amount = new_ing_amount.amount;
+ new_ing.amount_offset = new_ing_amount.amount_offset;
+ if ( !new_text.isEmpty() )
+ ing_item->setAmount( new_ing_amount.amount, new_ing_amount.amount_offset );
+
+ new_ing.amount = new_ing_amount.amount;
+ new_ing.amount_offset = new_ing_amount.amount_offset;
+ emit changed();
+ }
+ }
+ else
+ {
+ if ( !new_text.isEmpty() )
+ ing_item->setAmount( new_ing.amount, new_ing.amount_offset );
+ }
+
+ break;
+ }
+ case 2: //unit
+ {
+ Unit old_unit = new_ing.units;
+
+ if ( new_text.length() > uint(database->maxUnitNameLength()) )
+ {
+ KMessageBox::error( this, TQString( i18n( "Unit name cannot be longer than %1 characters." ) ).arg( database->maxUnitNameLength() ) );
+ ing_item->setUnit( old_unit );
+ break;
+ }
+
+ TQString approp_unit = new_ing.amount > 1 ? new_ing.units.plural : new_ing.units.name;
+ if ( approp_unit != new_text.stripWhiteSpace() )
+ {
+ Unit new_unit;
+ int new_id = IngredientInputWidget::createNewUnitIfNecessary( new_text.stripWhiteSpace(), new_ing.amount > 1, ing_item->ingredient().ingredientID, new_unit, database );
+
+ if ( new_id != -1 ) {
+ new_ing.units = new_unit;
+ new_ing.units.id = new_id;
+
+ ing_item->setUnit( new_ing.units );
+
+ updatePropertyStatus();
+ emit changed();
+ }
+ else {
+ ing_item->setUnit( old_unit );
+ }
+ }
+ break;
+ }
+ case 3: //prep method
+ {
+ TQString old_text = new_ing.prepMethodList.join(",");
+
+ TQStringList prepMethodList = TQStringList::split(",",new_text.stripWhiteSpace());
+
+ 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() ) );
+ ing_item->setPrepMethod( old_text );
+ break;
+ }
+ }
+
+ if ( old_text != new_text.stripWhiteSpace() )
+ {
+ new_ing.prepMethodList = ElementList::split(",",new_text.stripWhiteSpace());
+ TQValueList<int> new_ids = IngredientInputWidget::createNewPrepIfNecessary( new_ing.prepMethodList, database );
+
+ TQValueList<int>::const_iterator id_it = new_ids.begin();
+ for ( ElementList::iterator it = new_ing.prepMethodList.begin(); it != new_ing.prepMethodList.end(); ++it, ++id_it ) {
+ (*it).id = *id_it;
+ }
+
+ updatePropertyStatus();
+ emit changed();
+ }
+ break;
+ }
+ }
+}
+
+void RecipeInputDialog::recipeChanged( void )
+{
+ if ( changedSignalEnabled ) {
+ // Enable Save Button
+ emit enableSaveOption( true );
+ emit createButton( this, titleEdit->text() );
+ unsavedChanges = true;
+
+ }
+
+}
+
+void RecipeInputDialog::recipeChanged( const TQString & /*t*/ )
+{
+ recipeChanged(); // jumps to the real slot function
+}
+
+void RecipeInputDialog::enableChangedSignal( bool en )
+{
+ changedSignalEnabled = en;
+}
+
+bool RecipeInputDialog::save ( void )
+{
+ //check bounds first
+ if ( titleEdit->text().length() > uint(database->maxRecipeTitleLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Recipe title cannot be longer than %1 characters." ) ).arg( database->maxRecipeTitleLength() ), i18n( "Unable to save recipe" ) );
+ return false;
+ }
+
+ emit enableSaveOption( false );
+ saveRecipe();
+ unsavedChanges = false;
+
+ return true;
+}
+
+void RecipeInputDialog::saveRecipe( void )
+{
+ // Nothing except for the ingredient list (loadedRecipe->ingList)
+ // was stored before for performance. (recipeID is already there)
+
+ loadedRecipe->photo = sourcePhoto;
+ loadedRecipe->instructions = instructionsEdit->text();
+ loadedRecipe->title = titleEdit->text();
+ yieldNumInput->value(loadedRecipe->yield.amount,loadedRecipe->yield.amount_offset);
+ loadedRecipe->yield.type_id = createNewYieldIfNecessary(yieldTypeEdit->text());
+ loadedRecipe->prepTime = prepTimeEdit->time();
+
+ // Now save()
+ kdDebug() << "Saving..." << endl;
+ database->saveRecipe( loadedRecipe );
+
+
+}
+
+void RecipeInputDialog::newRecipe( void )
+{
+ loadedRecipe->empty();
+ TQPixmap image( defaultPhoto );
+ photoLabel->setPixmap( image );
+ sourcePhoto = TQPixmap();
+
+ instructionsEdit->setText( i18n( "Write the recipe instructions here" ) );
+ instructionsEdit->clearCompletionItems();
+ titleEdit->setText( i18n( "Write the recipe title here" ) );
+ ingredientList->clear();
+ authorShow->clear();
+ categoryShow->clear();
+ yieldNumInput->setValue( 1, 0 );
+ yieldTypeEdit->setText("");
+ prepTimeEdit->setTime( TQTime( 0, 0 ) );
+
+ instructionsEdit->selectAll();
+
+ //Set back to the first page
+ tabWidget->setCurrentPage( 0 );
+
+ ingInput->clear();
+
+ //Set focus to the title
+ titleEdit->setFocus();
+ titleEdit->selectAll();
+
+ //clear status info
+ propertyStatusMapRed.clear();
+ propertyStatusMapYellow.clear();
+ showStatusIndicator();
+}
+
+bool RecipeInputDialog::everythingSaved()
+{
+ return ( !( unsavedChanges ) );
+}
+
+void RecipeInputDialog::addCategory( void )
+{
+ SelectCategoriesDialog *editCategoriesDialog = new SelectCategoriesDialog( this, loadedRecipe->categoryList, database );
+
+ if ( editCategoriesDialog->exec() == TQDialog::Accepted ) { // user presses Ok
+ loadedRecipe->categoryList.clear();
+ editCategoriesDialog->getSelectedCategories( &( loadedRecipe->categoryList ) ); // get the category list chosen
+ emit( recipeChanged() ); //Indicate that the recipe changed
+
+ }
+
+ delete editCategoriesDialog;
+
+ // show category list
+ showCategories();
+
+
+}
+
+void RecipeInputDialog::showCategories( void )
+{
+ TQString categories;
+ for ( ElementList::const_iterator cat_it = loadedRecipe->categoryList.begin(); cat_it != loadedRecipe->categoryList.end(); ++cat_it ) {
+ if ( !categories.isEmpty() )
+ categories += ",";
+ categories += ( *cat_it ).name;
+ }
+ categoryShow->setText( categories );
+}
+
+void RecipeInputDialog::addAuthor( void )
+{
+ SelectAuthorsDialog * editAuthorsDialog = new SelectAuthorsDialog( this, loadedRecipe->authorList, database );
+
+
+ if ( editAuthorsDialog->exec() == TQDialog::Accepted ) { // user presses Ok
+ loadedRecipe->authorList.clear();
+ editAuthorsDialog->getSelectedAuthors( &( loadedRecipe->authorList ) ); // get the category list chosen
+ emit( recipeChanged() ); //Indicate that the recipe changed
+ }
+
+ delete editAuthorsDialog;
+
+ // show authors list
+ showAuthors();
+}
+
+void RecipeInputDialog::showAuthors( void )
+{
+ TQString authors;
+ for ( ElementList::const_iterator author_it = loadedRecipe->authorList.begin(); author_it != loadedRecipe->authorList.end(); ++author_it ) {
+ if ( !authors.isEmpty() )
+ authors += ",";
+ authors += ( *author_it ).name;
+ }
+ authorShow->setText( authors );
+}
+
+void RecipeInputDialog::enableSaveButton( bool enabled )
+{
+ saveButton->setEnabled( enabled );
+}
+
+void RecipeInputDialog::closeOptions( void )
+{
+
+ // First check if there's anything unsaved in the recipe
+ if ( unsavedChanges ) {
+
+ switch ( KMessageBox::questionYesNoCancel( this, i18n( "This recipe contains unsaved changes.\n" "Would you like to save it before closing?" ), i18n( "Unsaved changes" ) ) ) {
+ case KMessageBox::Yes:
+ save();
+ break;
+ case KMessageBox::No:
+ break;
+ case KMessageBox::Cancel:
+ return ;
+ }
+
+ }
+
+ emit enableSaveOption( false );
+ unsavedChanges = false;
+
+ // Now close really
+ emit closeRecipe();
+
+
+}
+
+void RecipeInputDialog::showRecipe( void )
+{
+ // First check if there's anything unsaved in the recipe
+
+ if ( loadedRecipe->recipeID == -1 ) {
+ switch ( KMessageBox::questionYesNo( this, i18n( "You need to save the recipe before displaying it. Would you like to save it now?" ), i18n( "Unsaved changes" ) ) ) {
+ case KMessageBox::Yes:
+ save();
+ break;
+ case KMessageBox::No:
+ return ;
+ }
+ }
+ else if ( unsavedChanges ) {
+
+ switch ( KMessageBox::questionYesNoCancel( this, i18n( "This recipe has changes that will not be displayed unless the recipe is saved. Would you like to save it now?" ), i18n( "Unsaved changes" ) ) ) {
+ case KMessageBox::Yes:
+ save();
+ break;
+ case KMessageBox::No:
+ break;
+ case KMessageBox::Cancel:
+ return ;
+ }
+
+ }
+
+ // Now open it really
+ emit showRecipe( loadedRecipe->recipeID );
+}
+
+void RecipeInputDialog::spellCheck( void )
+{
+ TQString text = instructionsEdit->text();
+ KSpellConfig default_cfg( this );
+ KSpell::modalCheck( text, &default_cfg );
+ KMessageBox::information( this, i18n( "Spell check complete." ) );
+
+ if ( text != instructionsEdit->text() ) //check if there were changes
+ instructionsEdit->setText( text );
+}
+
+void RecipeInputDialog::resizeRecipe( void )
+{
+ yieldNumInput->value( loadedRecipe->yield.amount, loadedRecipe->yield.amount_offset );
+ ResizeRecipeDialog dlg( this, loadedRecipe );
+
+ if ( dlg.exec() == TQDialog::Accepted )
+ reload();
+}
+
+int RecipeInputDialog::ingItemIndex( TQListView *listview, const TQListViewItem *item ) const
+{
+ if ( !item )
+ return -1;
+
+ if ( item == listview->firstChild() )
+ return 0;
+ else {
+ TQListViewItemIterator it( listview->firstChild() );
+ uint j = 0;
+ for ( ; it.current() && it.current() != item; ++it ) {
+ if ( it.current() ->rtti() == INGLISTVIEWITEM_RTTI ) {
+ if ( !it.current()->parent() || it.current()->parent()->rtti() == INGGRPLISTVIEWITEM_RTTI )
+ j++;
+ }
+ }
+
+ if ( !it.current() )
+ return -1;
+
+ return j;
+ }
+}
+
+void RecipeInputDialog::slotIngredientParser()
+{
+ UnitList units;
+ database->loadUnits(&units);
+ IngredientParserDialog dlg(units,this);
+ if ( dlg.exec() == TQDialog::Accepted ) {
+ IngredientList ings = dlg.ingredients();
+ TQStringList usedGroups;
+ bool haveHeader = false;
+ for ( IngredientList::iterator it = ings.begin(); it != ings.end(); ++it ) {
+ if ( !(*it).group.isEmpty() && usedGroups.find((*it).group) == usedGroups.end() ) {
+ int id = IngredientInputWidget::createNewGroupIfNecessary((*it).group,database);
+ addIngredientHeader( Element((*it).group, id) );
+ haveHeader = true;
+ usedGroups << (*it).group;
+ }
+ (*it).ingredientID = IngredientInputWidget::createNewIngredientIfNecessary((*it).name,database);
+ (*it).units.id = IngredientInputWidget::createNewUnitIfNecessary((*it).units.name,false,(*it).ingredientID,(*it).units,database);
+
+ TQValueList<int> prepIDs = IngredientInputWidget::createNewPrepIfNecessary((*it).prepMethodList,database);
+ TQValueList<int>::const_iterator prep_id_it = prepIDs.begin();
+ for ( ElementList::iterator prep_it = (*it).prepMethodList.begin(); prep_it != (*it).prepMethodList.end(); ++prep_it, ++prep_id_it ) {
+ (*prep_it).id = *prep_id_it;
+ }
+
+ addIngredient( *it, !haveHeader );
+
+ if ( usedGroups.count() > 0 && (*it).group.isEmpty() ) {
+ TQListViewItem *last_item = ingredientList->lastItem();
+ if ( last_item->parent() ) {
+ last_item->parent()->takeItem( last_item );
+ ingredientList->insertItem( last_item );
+ last_item->moveItem( ingredientList->lastItem()->parent() );
+ }
+ }
+ }
+ }
+}
+
+void RecipeInputDialog::slotAddRating()
+{
+ ElementList criteriaList;
+ database->loadRatingCriterion(&criteriaList);
+
+ EditRatingDialog ratingDlg(criteriaList,this);
+ if ( ratingDlg.exec() == TQDialog::Accepted ) {
+ Rating r = ratingDlg.rating();
+
+ for ( RatingCriteriaList::iterator rc_it = r.ratingCriteriaList.begin(); rc_it != r.ratingCriteriaList.end(); ++rc_it ) {
+ int criteria_id = database->findExistingRatingByName((*rc_it).name);
+ if ( criteria_id == -1 ) {
+ database->createNewRating((*rc_it).name);
+ criteria_id = database->lastInsertID();
+ }
+ (*rc_it).id = criteria_id;
+ }
+
+ RatingDisplayWidget *item = new RatingDisplayWidget;
+ item->rating_it = loadedRecipe->ratingList.append(r);
+ addRating(r,item);
+ ratingListDisplayWidget->insertItem(item,0);
+ emit( recipeChanged() ); //Indicate that the recipe changed
+ }
+}
+
+void RecipeInputDialog::addRating( const Rating &rating, RatingDisplayWidget *item )
+{
+ int average = tqRound(rating.average());
+ if ( average >= 0 )
+ item->icon->setPixmap( UserIcon(TQString("rating%1").arg(average) ) );
+ else //no rating criteria, therefore no average (we don't want to automatically assume a zero average)
+ item->icon->clear();
+
+ item->raterName->setText(rating.rater);
+ item->comment->setText(rating.comment);
+
+ item->criteriaListView->clear();
+ for ( RatingCriteriaList::const_iterator rc_it = rating.ratingCriteriaList.begin(); rc_it != rating.ratingCriteriaList.end(); ++rc_it ) {
+ TQListViewItem * it = new TQListViewItem(item->criteriaListView,(*rc_it).name);
+
+ int stars = int((*rc_it).stars * 2); //multiply by two to make it easier to work with half-stars
+
+ TQPixmap star = UserIcon(TQString::fromLatin1("star_on"));
+ int pixmapWidth = 18*(stars/2)+((stars%2==1)?9:0);
+ TQPixmap generatedPixmap(pixmapWidth,18);
+
+ if ( !generatedPixmap.isNull() ) { //there aren't zero stars
+ generatedPixmap.fill();
+ TQPainter painter( &generatedPixmap );
+ painter.drawTiledPixmap(0,0,pixmapWidth,18,star);
+ it->setPixmap(1,generatedPixmap);
+ }
+ }
+
+ item->buttonEdit->disconnect();
+ item->buttonRemove->disconnect();
+ connect(item->buttonEdit, TQ_SIGNAL(clicked()),
+ this, TQ_SLOT(slotEditRating()));
+ connect(item->buttonRemove, TQ_SIGNAL(clicked()),
+ this, TQ_SLOT(slotRemoveRating()));
+}
+
+void RecipeInputDialog::slotEditRating()
+{
+ RatingDisplayWidget *sender = (RatingDisplayWidget*)(TQObject::sender()->parent());
+
+ ElementList criteriaList;
+ database->loadRatingCriterion(&criteriaList);
+
+ EditRatingDialog ratingDlg(criteriaList,*sender->rating_it,this);
+ if ( ratingDlg.exec() == TQDialog::Accepted ) {
+ Rating r = ratingDlg.rating();
+
+ for ( RatingCriteriaList::iterator rc_it = r.ratingCriteriaList.begin(); rc_it != r.ratingCriteriaList.end(); ++rc_it ) {
+ int criteria_id = database->findExistingRatingByName((*rc_it).name);
+ if ( criteria_id == -1 ) {
+ database->createNewRating((*rc_it).name);
+ criteria_id = database->lastInsertID();
+ }
+ (*rc_it).id = criteria_id;
+ }
+
+ (*sender->rating_it) = r;
+ addRating(r,sender);
+ emit recipeChanged(); //Indicate that the recipe changed
+ }
+}
+
+void RecipeInputDialog::slotRemoveRating()
+{
+ RatingDisplayWidget *sender = (RatingDisplayWidget*)(TQObject::sender()->parent());
+ loadedRecipe->ratingList.remove(sender->rating_it);
+
+ //FIXME: sender is removed but never deleted (sender->deleteLater() doesn't work)
+ ratingListDisplayWidget->removeItem(sender);
+
+ emit recipeChanged(); //Indicate that the recipe changed
+}
+
+void RecipeInputDialog::addIngredient( const Ingredient &ing, bool noHeader )
+{
+ Ingredient ingCopy = ing;
+
+ //Append to the ListView
+ TQListViewItem* lastElement = ingredientList->lastItem();
+ while ( lastElement && lastElement->rtti() == INGSUBLISTVIEWITEM_RTTI )
+ lastElement = lastElement->itemAbove();
+
+ if ( noHeader && lastElement )
+ lastElement = (lastElement->parent())?lastElement->parent():lastElement;
+
+ if ( !noHeader && lastElement &&
+ ( lastElement->rtti() == INGGRPLISTVIEWITEM_RTTI || ( lastElement->parent() && lastElement->parent() ->rtti() == INGGRPLISTVIEWITEM_RTTI ) ) )
+ {
+ IngGrpListViewItem * header = ( lastElement->parent() ) ? ( IngGrpListViewItem* ) lastElement->parent() : ( IngGrpListViewItem* ) lastElement;
+
+ ingCopy.groupID = header->id();
+
+ lastElement = new IngListViewItem( header, lastElement, ingCopy );
+ for ( TQValueList<IngredientData>::const_iterator it = ingCopy.substitutes.begin(); it != ingCopy.substitutes.end(); ++it ) {
+ new IngSubListViewItem( lastElement, *it );
+ }
+ lastElement->setOpen(true);
+ }
+ else {
+ lastElement = new IngListViewItem( ingredientList, lastElement, ingCopy );
+ for ( TQValueList<IngredientData>::const_iterator it = ing.substitutes.begin(); it != ing.substitutes.end(); ++it ) {
+ new IngSubListViewItem( lastElement, *it );
+ }
+ lastElement->setOpen(true);
+ }
+
+ //append to recipe
+ loadedRecipe->ingList.append( ingCopy );
+
+ //update the completion in the instructions edit
+ instructionsEdit->addCompletionItem( ingCopy.name );
+
+ updatePropertyStatus( ingCopy, true );
+
+ emit changed();
+}
+
+void RecipeInputDialog::addIngredientHeader( const Element &header )
+{
+ TQListViewItem *last_item = ingredientList->lastItem();
+ if ( last_item && last_item->parent() )
+ last_item = last_item->parent();
+
+ IngGrpListViewItem *ing_header = new IngGrpListViewItem( ingredientList, last_item, header.name, header.id );
+ ing_header->setOpen( true );
+}
+
+void RecipeInputDialog::updatePropertyStatus()
+{
+ propertyStatusMapRed.clear();
+ propertyStatusMapYellow.clear();
+
+ for ( IngredientList::const_iterator ing_it = loadedRecipe->ingList.begin(); ing_it != loadedRecipe->ingList.end(); ++ing_it ) {
+ updatePropertyStatus( *ing_it, false );
+ }
+
+ showStatusIndicator();
+}
+
+void RecipeInputDialog::updatePropertyStatus( const Ingredient &ing, bool updateIndicator )
+{
+ IngredientPropertyList ingPropertyList;
+ database->loadProperties( &ingPropertyList, ing.ingredientID );
+
+ if ( ingPropertyList.count() == 0 ) {
+ propertyStatusMapRed.insert(ing.ingredientID,TQString(i18n("<b>%1:</b> No nutrient information available")).arg(ing.name));
+ }
+
+ TQMap<int,bool> ratioCache; //unit->conversion possible
+ IngredientPropertyList::const_iterator prop_it;
+ for ( prop_it = ingPropertyList.begin(); prop_it != ingPropertyList.end(); ++prop_it ) {
+ Ingredient result;
+
+ TQMap<int,bool>::const_iterator cache_it = ratioCache.find((*prop_it).perUnit.id);
+ if ( cache_it == ratioCache.end() ) {
+ RecipeDB::ConversionStatus status = database->convertIngredientUnits( ing, (*prop_it).perUnit, result );
+ ratioCache.insert((*prop_it).perUnit.id,status==RecipeDB::Success||status==RecipeDB::MismatchedPrepMethod);
+
+ switch ( status ) {
+ case RecipeDB::Success: break;
+ case RecipeDB::MissingUnitConversion: {
+ if ( ing.units.type != Unit::Other && ing.units.type == (*prop_it).perUnit.type ) {
+ propertyStatusMapRed.insert(ing.ingredientID,TQString(i18n("<b>%3:</b> Unit conversion missing for conversion from '%1' to '%2'"))
+ .arg(ing.units.name.isEmpty()?i18n("-No unit-"):ing.units.name)
+ .arg((*prop_it).perUnit.name)
+ .arg(ing.name));
+ } else {
+ WeightList weights = database->ingredientWeightUnits( ing.ingredientID );
+ TQValueList< TQPair<int,int> > usedIds;
+ TQStringList missingConversions;
+ for ( WeightList::const_iterator weight_it = weights.begin(); weight_it != weights.end(); ++weight_it ) {
+ //skip entries that only differ in how it's prepared
+ TQPair<int,int> usedPair((*weight_it).perAmountUnitID,(*weight_it).weightUnitID);
+ if ( usedIds.find(usedPair) != usedIds.end() )
+ continue;
+
+ TQString toUnit = database->unitName((*weight_it).perAmountUnitID).name;
+ if ( toUnit.isEmpty() ) toUnit = i18n("-No unit-");
+
+ TQString fromUnit = database->unitName((*weight_it).weightUnitID).name;
+ if ( fromUnit.isEmpty() ) fromUnit = i18n("-No unit-");
+
+ TQString ingUnit = ing.units.name;
+ if ( ingUnit.isEmpty() ) ingUnit = i18n("-No unit-");
+
+ TQString propUnit = (*prop_it).perUnit.name;
+ if ( propUnit.isEmpty() ) propUnit = i18n("-No unit-");
+
+ missingConversions << conversionPath( ingUnit, toUnit, fromUnit, propUnit);
+
+ usedIds << usedPair;
+ }
+ propertyStatusMapRed.insert(ing.ingredientID,TQString(i18n("<b>%1:</b> Either an appropriate ingredient weight entry is needed, or Krecipes needs conversion information to perform one of the following conversions: %2"))
+ .arg(ing.name)
+ .arg("<ul><li>"+missingConversions.join("</li><li>")+"</li></ul>")
+ );
+ }
+ break;
+ }
+ case RecipeDB::MissingIngredientWeight:
+ propertyStatusMapRed.insert(ing.ingredientID,TQString(i18n("<b>%1:</b> No ingredient weight entries")).arg(ing.name));
+ break;
+ case RecipeDB::MismatchedPrepMethod:
+ if ( ing.prepMethodList.count() == 0 )
+ propertyStatusMapRed.insert(ing.ingredientID,TQString(i18n("<b>%1:</b> There is no ingredient weight entry for when no preparation method is specified")).arg(ing.name));
+ else
+ propertyStatusMapRed.insert(ing.ingredientID,TQString(i18n("<b>%1:</b> There is no ingredient weight entry for when prepared in any of the following manners: %2")).arg(ing.name).arg("<ul><li>"+ing.prepMethodList.join("</li><li>")+"</li></ul>"));
+ break;
+ case RecipeDB::MismatchedPrepMethodUsingApprox:
+ propertyStatusMapYellow.insert(ing.ingredientID,TQString(i18n("<b>%1:</b> There is no ingredient weight entry for when prepared in any of the following manners (defaulting to a weight entry without a preparation method specified): %2")).arg(ing.name).arg("<ul><li>"+ing.prepMethodList.join("</li><li>")+"</li></ul>"));
+ break;
+ default: kdDebug()<<"Code error: Unhandled conversion status code "<<status<<endl; break;
+ }
+ }
+ }
+
+ if ( updateIndicator )
+ showStatusIndicator();
+}
+
+void RecipeInputDialog::showStatusIndicator()
+{
+ if ( propertyStatusMapRed.count() == 0 ) {
+ if ( propertyStatusMapYellow.count() == 0 ) {
+ propertyStatusLed->setColor( TQt::green );
+ propertyStatusLabel->setText( i18n("Complete") );
+ propertyStatusButton->setEnabled(false);
+ }
+ else {
+ propertyStatusLed->setColor( TQt::yellow );
+ propertyStatusLabel->setText( i18n("Complete, but approximations made") );
+ propertyStatusButton->setEnabled(true);
+ }
+ }
+ else {
+ propertyStatusLed->setColor( TQt::red );
+ propertyStatusLabel->setText( i18n("Incomplete") );
+ propertyStatusButton->setEnabled(true);
+ }
+
+ if ( propertyStatusMapRed.count() == 0 && propertyStatusMapYellow.count() == 0 )
+ propertyStatusDialog->hide();
+ else
+ statusTextView->setText(statusMessage());
+}
+
+TQString RecipeInputDialog::statusMessage() const
+{
+ TQString statusMessage;
+
+ if ( propertyStatusMapRed.count() > 0 ) {
+ statusMessage.append( i18n("The nutrient information for this recipe is incomplete because the following information is missing:") );
+ statusMessage.append("<ul>");
+ for ( TQMap<int,TQString>::const_iterator it = propertyStatusMapRed.begin(); it != propertyStatusMapRed.end(); ++it ) {
+ statusMessage.append("<li>");
+ statusMessage.append(it.data());
+ statusMessage.append("</li>");
+ }
+ statusMessage.append("</ul>");
+ }
+
+ if ( propertyStatusMapYellow.count() > 0 ) {
+ statusMessage.append( i18n("The following approximations will be made when determining nutrient information:") );
+ statusMessage.append("<ul>");
+ for ( TQMap<int,TQString>::const_iterator it = propertyStatusMapYellow.begin(); it != propertyStatusMapYellow.end(); ++it ) {
+ statusMessage.append("<li>");
+ statusMessage.append(it.data());
+ statusMessage.append("</li>");
+ }
+ statusMessage.append("</ul>");
+ }
+
+ return statusMessage;
+}
+
+TQString RecipeInputDialog::conversionPath( const TQString &ingUnit, const TQString &toUnit, const TQString &fromUnit, const TQString &propUnit ) const
+{
+ TQString path = "'"+ingUnit+"'";
+
+ TQString lastUnit = ingUnit;
+ if ( lastUnit != toUnit ) {
+ path.append(" =&gt; '"+toUnit+"'");
+ lastUnit = toUnit;
+ }
+ if ( lastUnit != fromUnit ) {
+ path.append(" =&gt; '"+fromUnit+"'");
+ lastUnit = fromUnit;
+ }
+ if ( lastUnit != propUnit ) {
+ path.append(" =&gt; '"+propUnit+"'");
+ lastUnit = propUnit;
+ }
+ return path;
+}
+
+#include "recipeinputdialog.moc"