/*************************************************************************** * 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 "recipeinputdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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::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::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::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 new_ids = IngredientInputWidget::createNewPrepIfNecessary( new_ing.prepMethodList, database ); TQValueList::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 prepIDs = IngredientInputWidget::createNewPrepIfNecessary((*it).prepMethodList,database); TQValueList::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::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::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("%1: No nutrient information available")).arg(ing.name)); } TQMap ratioCache; //unit->conversion possible IngredientPropertyList::const_iterator prop_it; for ( prop_it = ingPropertyList.begin(); prop_it != ingPropertyList.end(); ++prop_it ) { Ingredient result; TQMap::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("%3: 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 > 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 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("%1: 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("
  • "+missingConversions.join("
  • ")+"
") ); } break; } case RecipeDB::MissingIngredientWeight: propertyStatusMapRed.insert(ing.ingredientID,TQString(i18n("%1: No ingredient weight entries")).arg(ing.name)); break; case RecipeDB::MismatchedPrepMethod: if ( ing.prepMethodList.count() == 0 ) propertyStatusMapRed.insert(ing.ingredientID,TQString(i18n("%1: There is no ingredient weight entry for when no preparation method is specified")).arg(ing.name)); else propertyStatusMapRed.insert(ing.ingredientID,TQString(i18n("%1: There is no ingredient weight entry for when prepared in any of the following manners: %2")).arg(ing.name).arg("
  • "+ing.prepMethodList.join("
  • ")+"
")); break; case RecipeDB::MismatchedPrepMethodUsingApprox: propertyStatusMapYellow.insert(ing.ingredientID,TQString(i18n("%1: 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("
  • "+ing.prepMethodList.join("
  • ")+"
")); break; default: kdDebug()<<"Code error: Unhandled conversion status code "<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("
    "); for ( TQMap::const_iterator it = propertyStatusMapRed.begin(); it != propertyStatusMapRed.end(); ++it ) { statusMessage.append("
  • "); statusMessage.append(it.data()); statusMessage.append("
  • "); } statusMessage.append("
"); } if ( propertyStatusMapYellow.count() > 0 ) { statusMessage.append( i18n("The following approximations will be made when determining nutrient information:") ); statusMessage.append("
    "); for ( TQMap::const_iterator it = propertyStatusMapYellow.begin(); it != propertyStatusMapYellow.end(); ++it ) { statusMessage.append("
  • "); statusMessage.append(it.data()); statusMessage.append("
  • "); } statusMessage.append("
"); } 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(" => '"+toUnit+"'"); lastUnit = toUnit; } if ( lastUnit != fromUnit ) { path.append(" => '"+fromUnit+"'"); lastUnit = fromUnit; } if ( lastUnit != propUnit ) { path.append(" => '"+propUnit+"'"); lastUnit = propUnit; } return path; } #include "recipeinputdialog.moc"