diff --git a/RecipeDB.pro b/RecipeDB.pro index 92ac7d6..4727193 100644 --- a/RecipeDB.pro +++ b/RecipeDB.pro @@ -34,7 +34,9 @@ SOURCES += model/recipe/instruction.cpp \ utils/stringutils.cpp \ gui/openrecipedialog.cpp \ model/recipe/recipetablemodel.cpp \ - gui/mainwindow.cpp + gui/mainwindow.cpp \ + gui/newDialogs/newfoodgroupdialog.cpp \ + model/recipe/ingredients/recipeingredientlistmodel.cpp HEADERS += model/recipe/instruction.h \ model/recipe/recipe.h \ @@ -58,7 +60,9 @@ HEADERS += model/recipe/instruction.h \ utils/stringutils.h \ gui/openrecipedialog.h \ model/recipe/recipetablemodel.h \ - gui/mainwindow.h + gui/mainwindow.h \ + gui/newDialogs/newfoodgroupdialog.h \ + model/recipe/ingredients/recipeingredientlistmodel.h LIBS += -ldl \ @@ -68,7 +72,8 @@ FORMS += gui/mainwindow.ui \ gui/newDialogs/newtagdialog.ui \ gui/newDialogs/newunitdialog.ui \ gui/openrecipedialog.ui \ - gui/mainwindow.ui + gui/mainwindow.ui \ + gui/newDialogs/newfoodgroupdialog.ui DISTFILES += \ .gitignore diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 2b62cc3..03e802a 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -6,7 +6,7 @@ #include #include "model/recipe/recipe.h" -#include "model/recipe/ingredients/ingredientlistmodel.h" +#include "model/recipe/ingredients/recipeingredientlistmodel.h" #include "gui/newrecipedialog.h" #include "gui/openrecipedialog.h" #include "utils/stringutils.h" @@ -39,7 +39,7 @@ public: private: Ui::MainWindow *ui; RecipeDatabase *recipeDB; - IngredientListModel ingredientModel; + RecipeIngredientListModel ingredientModel; TagListModel tagsListModel; //Hidden manipulation methods. diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index 0925f75..4ac1f88 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -329,10 +329,10 @@ font: "Noto Sans CJK KR"; 0 - + - + 0 0 @@ -361,6 +361,26 @@ font: "Noto Sans CJK KR"; + + + + + 0 + 0 + + + + + Noto Sans CJK KR Light + 14 + PreferAntialias + + + + By Unknown Author + + + @@ -584,6 +604,9 @@ font: "Noto Sans CJK KR"; Qt::ScrollBarAlwaysOff + + QAbstractItemView::NoEditTriggers + QAbstractItemView::ExtendedSelection diff --git a/gui/newDialogs/newfoodgroupdialog.cpp b/gui/newDialogs/newfoodgroupdialog.cpp new file mode 100644 index 0000000..8e0277a --- /dev/null +++ b/gui/newDialogs/newfoodgroupdialog.cpp @@ -0,0 +1,18 @@ +#include "newfoodgroupdialog.h" +#include "ui_newfoodgroupdialog.h" + +newFoodGroupDialog::newFoodGroupDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::newFoodGroupDialog) +{ + ui->setupUi(this); +} + +newFoodGroupDialog::~newFoodGroupDialog() +{ + delete ui; +} + +string newFoodGroupDialog::getFoodGroup() const{ + return ui->lineEdit->text().toStdString(); +} diff --git a/gui/newDialogs/newfoodgroupdialog.h b/gui/newDialogs/newfoodgroupdialog.h new file mode 100644 index 0000000..20c3451 --- /dev/null +++ b/gui/newDialogs/newfoodgroupdialog.h @@ -0,0 +1,27 @@ +#ifndef NEWFOODGROUPDIALOG_H +#define NEWFOODGROUPDIALOG_H + +#include +#include + +using namespace std; + +namespace Ui { +class newFoodGroupDialog; +} + +class newFoodGroupDialog : public QDialog +{ + Q_OBJECT + + public: + explicit newFoodGroupDialog(QWidget *parent = 0); + ~newFoodGroupDialog(); + + string getFoodGroup() const; + + private: + Ui::newFoodGroupDialog *ui; +}; + +#endif // NEWFOODGROUPDIALOG_H diff --git a/gui/newDialogs/newfoodgroupdialog.ui b/gui/newDialogs/newfoodgroupdialog.ui new file mode 100644 index 0000000..bbd72d3 --- /dev/null +++ b/gui/newDialogs/newfoodgroupdialog.ui @@ -0,0 +1,101 @@ + + + newFoodGroupDialog + + + + 0 + 0 + 240 + 114 + + + + + 11 + + + + New Food Group + + + + :/images/images/icon.png:/images/images/icon.png + + + + + + + Noto Sans CJK KR Light + 12 + + + + Add New Food Group + + + Qt::AlignCenter + + + + + + + + Noto Sans CJK KR Light + 12 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + newFoodGroupDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + newFoodGroupDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/gui/newDialogs/newingredientdialog.cpp b/gui/newDialogs/newingredientdialog.cpp index 5b7597a..693dc1e 100644 --- a/gui/newDialogs/newingredientdialog.cpp +++ b/gui/newDialogs/newingredientdialog.cpp @@ -8,11 +8,42 @@ NewIngredientDialog::NewIngredientDialog(QWidget *parent) : ui->setupUi(this); } +NewIngredientDialog::NewIngredientDialog(RecipeDatabase *recipeDB, QWidget *parent) : NewIngredientDialog(parent){ + this->recipeDB = recipeDB; + this->populateFoodGroupBox(); +} + NewIngredientDialog::~NewIngredientDialog() { delete ui; } Ingredient NewIngredientDialog::getIngredient(){ - return Ingredient(ui->nameEdit->text().toLower().toStdString(), ui->foodGroupEdit->text().toLower().toStdString()); + return Ingredient(ui->nameEdit->text().toLower().toStdString(), ui->foodGroupBox->currentText().toStdString()); +} + +void NewIngredientDialog::populateFoodGroupBox(){ + vector foodGroups = this->recipeDB->retrieveAllFoodGroups(); + ui->foodGroupBox->clear(); + for (unsigned int i = 0; i < foodGroups.size(); i++){ + QString s = QString::fromStdString(foodGroups[i]); + ui->foodGroupBox->insertItem(i, s); + } +} + +void NewIngredientDialog::on_addFoodGroupButton_clicked(){ + newFoodGroupDialog d(this); + if (d.exec() == QDialog::Accepted){ + string s = d.getFoodGroup(); + if (!s.empty()){ + ui->foodGroupBox->addItem(QString::fromStdString(s)); + ui->foodGroupBox->setCurrentText(QString::fromStdString(s)); + } else { + QMessageBox::warning(this, "Empty Food Group", "The food group you entered is empty!"); + } + } +} + +void NewIngredientDialog::on_deleteFoodGroupButton_clicked(){ + ui->foodGroupBox->removeItem(ui->foodGroupBox->currentIndex()); } diff --git a/gui/newDialogs/newingredientdialog.h b/gui/newDialogs/newingredientdialog.h index 4c94275..e922167 100644 --- a/gui/newDialogs/newingredientdialog.h +++ b/gui/newDialogs/newingredientdialog.h @@ -2,7 +2,11 @@ #define NEWINGREDIENTDIALOG_H #include +#include + #include "model/recipe/ingredients/ingredient.h" +#include "model/database/recipedatabase.h" +#include "gui/newDialogs/newfoodgroupdialog.h" namespace Ui { class NewIngredientDialog; @@ -14,13 +18,22 @@ class NewIngredientDialog : public QDialog public: explicit NewIngredientDialog(QWidget *parent = 0); + NewIngredientDialog(RecipeDatabase *recipeDB, QWidget *parent = 0); ~NewIngredientDialog(); //Access values. Ingredient getIngredient(); + private slots: + void on_addFoodGroupButton_clicked(); + + void on_deleteFoodGroupButton_clicked(); + private: Ui::NewIngredientDialog *ui; + RecipeDatabase *recipeDB; + + void populateFoodGroupBox(); }; #endif // NEWINGREDIENTDIALOG_H diff --git a/gui/newDialogs/newingredientdialog.ui b/gui/newDialogs/newingredientdialog.ui index 8f4f268..792e239 100644 --- a/gui/newDialogs/newingredientdialog.ui +++ b/gui/newDialogs/newingredientdialog.ui @@ -6,8 +6,8 @@ 0 0 - 240 - 320 + 367 + 228 @@ -60,7 +60,45 @@ - + + + + + + + 0 + 0 + + + + QComboBox::InsertAlphabetically + + + + + + + + + + + :/images/images/plus_icon.png:/images/images/plus_icon.png + + + + + + + + + + + :/images/images/minus_icon.png:/images/images/minus_icon.png + + + + + diff --git a/gui/newDialogs/newtagdialog.ui b/gui/newDialogs/newtagdialog.ui index fd77812..72ea9f2 100644 --- a/gui/newDialogs/newtagdialog.ui +++ b/gui/newDialogs/newtagdialog.ui @@ -7,16 +7,19 @@ 0 0 240 - 320 + 121 - Dialog + New Tag :/images/images/icon.png:/images/images/icon.png + + font: 25 "Noto Sans CJK KR Light"; + true @@ -26,6 +29,14 @@ + + + 13 + 3 + false + false + + New Tag @@ -35,7 +46,16 @@ - + + + + 12 + 3 + false + false + + + diff --git a/gui/newDialogs/newunitdialog.ui b/gui/newDialogs/newunitdialog.ui index 953be58..92fc161 100644 --- a/gui/newDialogs/newunitdialog.ui +++ b/gui/newDialogs/newunitdialog.ui @@ -6,8 +6,8 @@ 0 0 - 240 - 350 + 195 + 340 @@ -17,6 +17,9 @@ :/images/images/icon.png:/images/images/icon.png + + font: 25 "Noto Sans CJK KR Light"; + true @@ -26,6 +29,14 @@ + + + 12 + 3 + false + false + + Unit Name @@ -39,6 +50,14 @@ + + + 12 + 3 + false + false + + Plural Name @@ -52,6 +71,14 @@ + + + 12 + 3 + false + false + + Abbreviation @@ -86,6 +113,14 @@ 0 + + + 12 + 3 + false + false + + Type: @@ -109,6 +144,14 @@ + + + 12 + 3 + false + false + + Metric Coefficient diff --git a/gui/newrecipedialog.cpp b/gui/newrecipedialog.cpp index a0681f2..c105333 100644 --- a/gui/newrecipedialog.cpp +++ b/gui/newrecipedialog.cpp @@ -75,6 +75,7 @@ void NewRecipeDialog::on_addIngredientButton_clicked(){ UnitOfMeasure u = this->units[ui->unitComboBox->currentIndex()]; RecipeIngredient ri(i, ui->quantitySpinBox->value(), u, ui->commentsLineEdit->text().toStdString()); this->ingredientListModel.addIngredient(ri); + ui->commentsLineEdit->clear(); } void NewRecipeDialog::on_italicsButton_clicked(){ @@ -144,12 +145,17 @@ void NewRecipeDialog::on_deleteIngredientButton_clicked(){ } void NewRecipeDialog::on_newIngredientButton_clicked(){ - NewIngredientDialog d(this); + NewIngredientDialog d(this->recipeDB, this); d.show(); if (d.exec() == QDialog::Accepted){ Ingredient i = d.getIngredient(); - this->recipeDB->storeIngredient(i); - this->populateIngredientsBox(); + if (!i.getName().empty() && !i.getFoodGroup().empty() && this->recipeDB->storeIngredient(i)){ + this->populateIngredientsBox(); + ui->ingredientNameBox->setCurrentText(QString::fromStdString(i.getName())); + } else { + QMessageBox::critical(this, "Error", "Unable to add ingredient."); + } + } } @@ -159,12 +165,16 @@ void NewRecipeDialog::on_newTagButton_clicked(){ if (d.exec() == QDialog::Accepted){ RecipeTag tag = d.getTag(); //Temporarily add this to the tags list, and it will be saved if the recipe is saved. - this->tags.push_back(tag); - this->tagsListModel.addTag(tag); - ui->tagsComboBox->clear(); - for (unsigned int i = 0; i < this->tags.size(); i++){ - QString s = QString::fromStdString(this->tags[i].getValue()); - ui->tagsComboBox->insertItem(i, s); + if (!tag.getValue().empty()){ + this->tags.push_back(tag); + this->tagsListModel.addTag(tag); + ui->tagsComboBox->clear(); + for (unsigned int i = 0; i < this->tags.size(); i++){ + QString s = QString::fromStdString(this->tags[i].getValue()); + ui->tagsComboBox->insertItem(i, s); + } + } else { + QMessageBox::warning(this, "Empty Tag", "The tag you entered is blank!"); } } @@ -189,10 +199,11 @@ void NewRecipeDialog::on_newUnitButton_clicked(){ d.show(); if (d.exec() == QDialog::Accepted){ UnitOfMeasure u = d.getUnit(); - if (!this->recipeDB->storeUnitOfMeasure(u) || u.getName().empty() || u.getNamePlural().empty() || u.getAbbreviation().empty()){ - QMessageBox::critical(this, "Error", "Unable to store new unit."); + if (u.getName().empty() || u.getNamePlural().empty() || u.getAbbreviation().empty() || !this->recipeDB->storeUnitOfMeasure(u)){ + QMessageBox::critical(this, "Error", "Unable to store new unit. Make sure all the information is filled in!"); } else { this->populateUnitsBox(); + ui->unitComboBox->setCurrentText(QString::fromStdString(u.getName())); } } } diff --git a/gui/newrecipedialog.h b/gui/newrecipedialog.h index bfeae1f..181cea2 100644 --- a/gui/newrecipedialog.h +++ b/gui/newrecipedialog.h @@ -8,7 +8,7 @@ #include #include "model/database/recipedatabase.h" -#include "model/recipe/ingredients/ingredientlistmodel.h" +#include "model/recipe/ingredients/recipeingredientlistmodel.h" #include "model/recipe/tags/taglistmodel.h" #include "gui/newDialogs/newingredientdialog.h" @@ -67,7 +67,7 @@ class NewRecipeDialog : public QDialog vector ingredients; vector units; vector tags; - IngredientListModel ingredientListModel; + RecipeIngredientListModel ingredientListModel; TagListModel tagsListModel; QImage img; bool accepted = false; diff --git a/gui/newrecipedialog.ui b/gui/newrecipedialog.ui index 7a57690..937ee33 100644 --- a/gui/newrecipedialog.ui +++ b/gui/newrecipedialog.ui @@ -96,27 +96,11 @@ - - - - - 3 - false - false - - - - Edit Recipe - - - Qt::AlignCenter - - - + 16 3 false false @@ -158,7 +142,7 @@ 0 1999 12 - 25 + 24 @@ -289,6 +273,12 @@ 0 + + background-color: rgb(113, 119, 255); + + + QComboBox::InsertAlphabetically + @@ -418,16 +408,6 @@ 0 - - - - Ingredients - - - Qt::AlignCenter - - - @@ -485,12 +465,21 @@ 0 + + background-color: rgb(113, 119, 255); + false + + QComboBox::InsertAlphabetically + + + true + @@ -580,6 +569,12 @@ 0 + + background-color: rgb(113, 119, 255); + + + QComboBox::InsertAlphabetically + @@ -832,7 +827,10 @@ Qt::LeftToRight - false + true + + + background-color: rgb(255, 255, 255); QDialogButtonBox::Cancel|QDialogButtonBox::Ok diff --git a/gui/openrecipedialog.cpp b/gui/openrecipedialog.cpp index fb34703..648beb9 100644 --- a/gui/openrecipedialog.cpp +++ b/gui/openrecipedialog.cpp @@ -8,10 +8,19 @@ OpenRecipeDialog::OpenRecipeDialog(QWidget *parent) : ui->setupUi(this); ui->recipeTableView->setModel(&this->recipeTableModel); + ui->ingredientsListView->setModel(&this->ingredientsModel); + ui->tagsListView->setModel(&this->tagsModel); + + connect(ui->ingredientsListView->selectionModel(), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, + SLOT(on_ingredientsListView_selectionChanged(QItemSelection))); } OpenRecipeDialog::OpenRecipeDialog(RecipeDatabase *recipeDB, QWidget *parent) : OpenRecipeDialog(parent){ this->recipeDB = recipeDB; + this->populateIngredientsList(); + this->populateTagsList(); this->populateRecipesTable(); } @@ -32,13 +41,21 @@ void OpenRecipeDialog::populateRecipesTable(){ ui->recipeTableView->show(); } +void OpenRecipeDialog::populateIngredientsList(){ + this->ingredientsModel.setIngredients(this->recipeDB->retrieveAllIngredients()); +} + +void OpenRecipeDialog::populateTagsList(){ + this->tagsModel.setTags(this->recipeDB->retrieveAllTags()); +} + void OpenRecipeDialog::on_deleteRecipeButton_clicked(){ QItemSelectionModel *selectModel = ui->recipeTableView->selectionModel(); if (!selectModel->hasSelection()){ return; } vector rows; - QModelIndexList indexes = selectModel->selectedIndexes(); + QModelIndexList indexes = selectModel->selectedRows(); for (int i = 0; i < indexes.count(); i++){ rows.push_back(indexes.at(i).row()); } @@ -62,3 +79,14 @@ void OpenRecipeDialog::on_recipeTableView_doubleClicked(const QModelIndex &index this->selectedRecipe = this->recipeTableModel.getRecipeAt(index.row()); this->close(); } + +void OpenRecipeDialog::on_ingredientsListView_selectionChanged(const QItemSelection &selection){ + printf("Selection changed!\n"); + vector ingredients; + for (QModelIndex index : selection.indexes()){ + Ingredient i = this->ingredientsModel.getIngredients().at(index.row()); + ingredients.push_back(i); + printf("Selected: %s\n", i.getName().c_str()); + } + +} diff --git a/gui/openrecipedialog.h b/gui/openrecipedialog.h index 974b6cf..44c56f9 100644 --- a/gui/openrecipedialog.h +++ b/gui/openrecipedialog.h @@ -3,9 +3,12 @@ #include #include +#include #include "model/database/recipedatabase.h" #include "model/recipe/recipetablemodel.h" +#include "model/recipe/ingredients/ingredientlistmodel.h" +#include "model/recipe/tags/taglistmodel.h" namespace Ui { class OpenRecipeDialog; @@ -27,13 +30,20 @@ class OpenRecipeDialog : public QDialog void on_recipeTableView_doubleClicked(const QModelIndex &index); + void on_ingredientsListView_selectionChanged(const QItemSelection &selection); + private: Ui::OpenRecipeDialog *ui; RecipeDatabase *recipeDB; RecipeTableModel recipeTableModel; Recipe selectedRecipe; + IngredientListModel ingredientsModel; + TagListModel tagsModel; + void populateRecipesTable(); + void populateIngredientsList(); + void populateTagsList(); }; #endif // OPENRECIPEDIALOG_H diff --git a/gui/openrecipedialog.ui b/gui/openrecipedialog.ui index d689a16..0e51715 100644 --- a/gui/openrecipedialog.ui +++ b/gui/openrecipedialog.ui @@ -6,7 +6,7 @@ 0 0 - 640 + 1000 480 @@ -20,10 +20,80 @@ true - - + + - + + + + + + + + Tag + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QAbstractItemView::ExtendedSelection + + + false + + + false + + + + + + + + + + + + + Ingredient + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + + + + @@ -40,38 +110,6 @@ - - - - - - - Tag - - - - - - - - - - - - - - - - Ingredient - - - - - - - - - @@ -86,26 +124,26 @@ - - - - - - - - - - - :/images/images/trash.png:/images/images/trash.png - - - - - - + + + + + + + + + + + :/images/images/trash.png:/images/images/trash.png + + + + + + diff --git a/main.cpp b/main.cpp index 3d4bfdb..2a70cec 100644 --- a/main.cpp +++ b/main.cpp @@ -4,22 +4,39 @@ #include "model/database/database.h" #include "model/database/recipedatabase.h" +#include "utils/fileutils.h" + +void test(RecipeDatabase *recipeDB); int main(int argc, char *argv[]) { - RecipeDatabase recipeDB("recipes"); + RecipeDatabase recipeDB(QString(FileUtils::appDataPath+"recipes.db").toStdString()); QApplication a(argc, argv); MainWindow w(&recipeDB); w.show(); //TESTING CODE + test(&recipeDB); + + //END TESTING CODE. + + w.loadFromRecipe(recipeDB.retrieveRandomRecipe()); + + a.exec(); + recipeDB.closeConnection(); + printf("Total queries: %lu\n", recipeDB.getQueryCount()); + + return 0; +} + +void test(RecipeDatabase *recipeDB){ vector ri; ri.push_back(RecipeIngredient("flour", "grains", 3.0f, UnitOfMeasure("cup", "cups", "c", UnitOfMeasure::VOLUME, 1.0), "")); ri.push_back(RecipeIngredient("baking powder", "additives", 1.0f, UnitOfMeasure("teaspoon", "teaspoons", "tsp", UnitOfMeasure::VOLUME, 1.0), "")); Recipe rec("Example", ri, - Instruction("BOLDiTaLiCs"), + Instruction("Placeholder Text"), QImage(), vector({RecipeTag("testing"), RecipeTag("fake")}), @@ -28,13 +45,21 @@ int main(int argc, char *argv[]) QTime(0, 25), 10.0f); - bool success = recipeDB.storeRecipe(rec); + bool success = recipeDB->storeRecipe(rec); printf("Storage successful: %d\n", success); - //recipeDB.selectFrom("recipe", "recipeId, name", "").printData(); - w.loadFromRecipe(recipeDB.retrieveRandomRecipe()); + vector foodGroups = recipeDB->retrieveAllFoodGroups(); + printf("Food Groups:\n"); + for (string s : foodGroups){ + printf("\t%s\n", s.c_str()); + } + + //Get food groups from recipe. + Recipe r = recipeDB->retrieveRecipe("Pannenkoeken"); + vector foodGroupsR = r.getFoodGroups(); + printf("Pannenkoeken Food Groups:\n"); + for (string s : foodGroupsR){ + printf("\t%s\n", s.c_str()); + } - a.exec(); - recipeDB.closeConnection(); - return 0; } diff --git a/model/database/database.cpp b/model/database/database.cpp index 0d3b9a4..db53c3f 100644 --- a/model/database/database.cpp +++ b/model/database/database.cpp @@ -2,6 +2,7 @@ Database::Database(string filename){ this->filename = filename; + this->queryCount = 0; openConnection(); } @@ -22,6 +23,7 @@ ResultTable Database::executeSQL(string statement){ t.extractData(stmt); this->returnCode = sqlite3_finalize(stmt); + this->queryCount++; return t; } @@ -99,6 +101,10 @@ bool Database::tableExists(string tableName){ return !t.isEmpty(); } -int Database::getLastInsertedRowId(){ +int Database::getLastInsertedRowId() const{ return sqlite3_last_insert_rowid(this->db); } + +unsigned long Database::getQueryCount() const{ + return this->queryCount; +} diff --git a/model/database/database.h b/model/database/database.h index ab149d5..d0bd435 100644 --- a/model/database/database.h +++ b/model/database/database.h @@ -29,7 +29,9 @@ public: bool deleteFrom(string tableName, string conditions); bool tableExists(string tableName); - int getLastInsertedRowId(); + int getLastInsertedRowId() const; + + unsigned long getQueryCount() const; void closeConnection(); @@ -45,6 +47,9 @@ private: string sql; char* errorMsg; + //Data tracking. + unsigned long queryCount; + void openConnection(); std::string combineVector(std::vector strings, std::string mid); }; diff --git a/model/database/recipedatabase.cpp b/model/database/recipedatabase.cpp index 1650579..1eeebd3 100644 --- a/model/database/recipedatabase.cpp +++ b/model/database/recipedatabase.cpp @@ -88,7 +88,7 @@ int RecipeDatabase::storeIngredient(Ingredient ingredient){ return -1; } } else { - return std::stoi(t.valueAt(0, 0)); + return std::stoi(t.at(0, 0)); } } @@ -162,12 +162,21 @@ Recipe RecipeDatabase::retrieveRandomRecipe(){ vector RecipeDatabase::retrieveAllRecipes(){ ResultTable t = this->selectFrom("recipe", "name", "ORDER BY name"); vector recipes; - for (unsigned int row = 0; row < t.rowCount(); row++){ - recipes.push_back(this->retrieveRecipe(t.valueAt(row, 0))); + for (TableRow row : t.rows()){ + recipes.push_back(this->retrieveRecipe(row.at(0))); } return recipes; } +vector RecipeDatabase::retrieveAllFoodGroups(){ + ResultTable t = this->executeSQL("SELECT DISTINCT foodGroup FROM ingredient ORDER BY foodGroup;"); + vector foodGroups; + for (TableRow row : t.rows()){ + foodGroups.push_back(row.at(0)); + } + return foodGroups; +} + vector RecipeDatabase::retrieveRecipeIngredients(int recipeId){ ResultTable t = this->executeSQL("SELECT ingredient.name, ingredient.foodGroup, "//0, 1 "recipeIngredient.quantity, recipeIngredient.unitName, recipeIngredient.comment,"//2, 3, 4 @@ -179,33 +188,33 @@ vector RecipeDatabase::retrieveRecipeIngredients(int recipeId) "ON recipeIngredient.unitName = unitOfMeasure.name " "WHERE recipeIngredient.recipeId = "+std::to_string(recipeId)+";"); vector ings; - for (unsigned int row = 0; row < t.rowCount(); row++){ - RecipeIngredient r(t.valueAt(row, 0), - t.valueAt(row, 1), - std::stof(t.valueAt(row, 2)), - UnitOfMeasure(t.valueAt(row, 5), t.valueAt(row, 6), t.valueAt(row, 7), std::stoi(t.valueAt(row, 8)), std::stod(t.valueAt(row, 9))), - t.valueAt(row, 4)); + for (TableRow row : t.rows()){ + RecipeIngredient r(row.at(0), + row.at(1), + std::stof(row.at(2)), + UnitOfMeasure(row.at(5), row.at(6), row.at(7), std::stoi(row.at(8)), std::stod(row.at(9))), + row.at(4)); ings.push_back(r); } return ings; } vector RecipeDatabase::retrieveAllIngredients(){ - ResultTable t = this->selectFrom("ingredient", "*", "ORDER BY name"); + ResultTable t = this->selectFrom("ingredient", "name, foodGroup", "ORDER BY name"); vector ings; - for (unsigned int row = 0; row < t.rowCount(); row++){ - Ingredient i(t.valueAt(row, 2), t.valueAt(row, 1)); + for (TableRow row : t.rows()){ + Ingredient i(row.at(0), row.at(1)); ings.push_back(i); } return ings; } vector RecipeDatabase::retrieveAllUnitsOfMeasure(){ - ResultTable t = this->selectFrom("unitOfMeasure", "*", "ORDER BY name"); + ResultTable t = this->selectFrom("unitOfMeasure", "name, plural, abbreviation, type, metricCoefficient", "ORDER BY name"); vector units; if (!t.isEmpty()){ - for (unsigned int row = 0; row < t.rowCount(); row++){ - UnitOfMeasure u(t.valueAt(row, 0), t.valueAt(row, 1), t.valueAt(row, 2), std::stoi(t.valueAt(row, 3)), std::stod(t.valueAt(row, 4))); + for (TableRow row : t.rows()){ + UnitOfMeasure u(row.at(0), row.at(1), row.at(2), std::stoi(row.at(3)), std::stod(row.at(4))); units.push_back(u); } } @@ -213,11 +222,11 @@ vector RecipeDatabase::retrieveAllUnitsOfMeasure(){ } vector RecipeDatabase::retrieveTags(int recipeId){ - ResultTable t = this->selectFrom("recipeTag", "tagName", "WHERE recipeId="+std::to_string(recipeId)); + ResultTable t = this->selectFrom("recipeTag", "tagName", "WHERE recipeId="+std::to_string(recipeId)+" ORDER BY tagName"); vector tags; if (!t.isEmpty()){ - for (unsigned int row = 0; row < t.rowCount(); row++){ - RecipeTag tag(t.valueAt(row, 0)); + for (TableRow row : t.rows()){ + RecipeTag tag(row.at(0)); tags.push_back(tag); } } @@ -225,11 +234,11 @@ vector RecipeDatabase::retrieveTags(int recipeId){ } vector RecipeDatabase::retrieveAllTags(){ - ResultTable t = this->selectFrom("recipeTag", "tagName", "ORDER BY tagName"); + ResultTable t = this->executeSQL("SELECT DISTINCT tagName FROM recipeTag ORDER BY tagName;"); vector tags; if (!t.isEmpty()){ - for (unsigned int row = 0; row < t.rowCount(); row++){ - RecipeTag tag(t.valueAt(row, 0)); + for (TableRow row : t.rows()){ + RecipeTag tag(row.at(0)); tags.push_back(tag); } } @@ -241,7 +250,7 @@ bool RecipeDatabase::deleteRecipe(string name){ if (t.rowCount() != 1){ return false; } - string recipeId = t.valueAt(0, 0); + string recipeId = t.at(0, 0); return this->deleteRecipe(std::stoi(recipeId)); } @@ -255,6 +264,10 @@ bool RecipeDatabase::deleteRecipe(int recipeId){ bool tagsDeleted = this->deleteFrom("recipeTag", "WHERE recipeId="+idString); bool recipeIngredientDeleted = this->deleteFrom("recipeIngredient", "WHERE recipeId="+idString); bool recipeDeleted = this->deleteFrom("recipe", "WHERE recipeId="+idString); + bool instructionDeleted = FileUtils::deleteInstruction(recipeId); + bool imageDeleted = FileUtils::deleteImage(recipeId); + Q_UNUSED(instructionDeleted); + Q_UNUSED(imageDeleted); if (tagsDeleted && recipeIngredientDeleted && recipeDeleted){ this->executeSQL("COMMIT;"); return true; @@ -332,14 +345,15 @@ void RecipeDatabase::ensureTablesExist(){ this->executeSQL("COMMIT;"); } -Recipe RecipeDatabase::readFromResultTable(ResultTable t, int row){ +Recipe RecipeDatabase::readFromResultTable(ResultTable t, int tRow){ Recipe r; - int id = std::stoi(t.valueAt(row, 0)); - r.setName(t.valueAt(row, 1)); - r.setCreatedDate(QDate::fromString(QString::fromStdString(t.valueAt(row, 2)))); - r.setPrepTime(QTime::fromString(QString::fromStdString(t.valueAt(row, 3)))); - r.setCookTime(QTime::fromString(QString::fromStdString(t.valueAt(row, 4)))); - r.setServings(std::stof(t.valueAt(row, 5))); + TableRow row = t.rows().at(tRow); + int id = std::stoi(row.at(0)); + r.setName(row.at(1)); + r.setCreatedDate(QDate::fromString(QString::fromStdString(row.at(2)))); + r.setPrepTime(QTime::fromString(QString::fromStdString(row.at(3)))); + r.setCookTime(QTime::fromString(QString::fromStdString(row.at(4)))); + r.setServings(std::stof(row.at(5))); r.setInstruction(FileUtils::loadInstruction(id)); r.setImage(FileUtils::loadImage(id)); r.setIngredients(this->retrieveRecipeIngredients(id)); diff --git a/model/database/recipedatabase.h b/model/database/recipedatabase.h index c5466b7..79ff7bd 100644 --- a/model/database/recipedatabase.h +++ b/model/database/recipedatabase.h @@ -1,7 +1,7 @@ #ifndef RECIPEDATABASE_H #define RECIPEDATABASE_H - +#include #include "database.h" #include "model/recipe/recipe.h" @@ -34,6 +34,7 @@ class RecipeDatabase : public Database Recipe retrieveRecipe(string name); Recipe retrieveRandomRecipe(); vector retrieveAllRecipes(); + vector retrieveAllFoodGroups(); vector retrieveRecipeIngredients(int recipeId); vector retrieveAllIngredients(); vector retrieveAllUnitsOfMeasure(); diff --git a/model/database/resulttable.cpp b/model/database/resulttable.cpp index 9811d64..ac96d7b 100644 --- a/model/database/resulttable.cpp +++ b/model/database/resulttable.cpp @@ -47,7 +47,7 @@ bool ResultTable::isEmpty(){ return this->values.empty(); } -string ResultTable::valueAt(unsigned int row, unsigned int col){ +string ResultTable::at(unsigned int row, unsigned int col){ if (isIndexValid(row, col)){ return this->values[row][col]; } else { @@ -78,6 +78,10 @@ unsigned int ResultTable::rowCount(){ return this->values.size(); } +vector > ResultTable::rows(){ + return this->values; +} + string ResultTable::convertToString(sqlite3_value *val){ const unsigned char* raw_text = sqlite3_value_text(val); if (raw_text == 0){ diff --git a/model/database/resulttable.h b/model/database/resulttable.h index a138ea3..a4dcd84 100644 --- a/model/database/resulttable.h +++ b/model/database/resulttable.h @@ -12,6 +12,8 @@ using namespace std; * @brief The ResultTable class is an object that contains the results of an SQL query, in string form. */ +typedef vector TableRow; + class ResultTable { public: @@ -28,11 +30,12 @@ class ResultTable void printData(); bool isEmpty(); - string valueAt(unsigned int row, unsigned int col); + string at(unsigned int row, unsigned int col); int getReturnCode(); string getOriginalQuery(); unsigned int columnCount(); unsigned int rowCount(); + vector> rows(); private: vector> values; int queryCode; diff --git a/model/recipe/ingredients/ingredient.cpp b/model/recipe/ingredients/ingredient.cpp index d0a4664..8942e11 100644 --- a/model/recipe/ingredients/ingredient.cpp +++ b/model/recipe/ingredients/ingredient.cpp @@ -23,5 +23,9 @@ void Ingredient::setName(string newName){ } void Ingredient::setFoodGroup(string newFoodGroup){ - this->foodGroup = newFoodGroup; + this->foodGroup = newFoodGroup; +} + +string Ingredient::toString(){ + return this->getName(); } diff --git a/model/recipe/ingredients/ingredient.h b/model/recipe/ingredients/ingredient.h index 5dbe08d..88a7ec7 100644 --- a/model/recipe/ingredients/ingredient.h +++ b/model/recipe/ingredients/ingredient.h @@ -23,6 +23,8 @@ public: //Setters void setName(string newName); void setFoodGroup(string newFoodGroup); + + string toString(); protected: string name; string foodGroup; diff --git a/model/recipe/ingredients/ingredientlistmodel.cpp b/model/recipe/ingredients/ingredientlistmodel.cpp index 916bbb0..054a4d4 100644 --- a/model/recipe/ingredients/ingredientlistmodel.cpp +++ b/model/recipe/ingredients/ingredientlistmodel.cpp @@ -1,7 +1,7 @@ #include "model/recipe/ingredients/ingredientlistmodel.h" IngredientListModel::IngredientListModel(){ - this->ingredients = vector(); + this->ingredients = vector(); } int IngredientListModel::rowCount(const QModelIndex &parent) const{ @@ -10,7 +10,7 @@ int IngredientListModel::rowCount(const QModelIndex &parent) const{ QVariant IngredientListModel::data(const QModelIndex &index, int role) const{ int row = index.row(); - RecipeIngredient i = this->ingredients[row]; + Ingredient i = this->ingredients[row]; string displayStr = i.toString(); @@ -22,14 +22,14 @@ QVariant IngredientListModel::data(const QModelIndex &index, int role) const{ return QVariant(); } -void IngredientListModel::setIngredients(vector ingredients){ +void IngredientListModel::setIngredients(vector ingredients){ this->ingredients = ingredients; QModelIndex index = createIndex(0, 0); QModelIndex bottomIndex = createIndex(ingredients.size()-1, 0); emit dataChanged(index, bottomIndex); } -bool IngredientListModel::addIngredient(RecipeIngredient ri){ +bool IngredientListModel::addIngredient(Ingredient ri){ //Add only if it doesn't exist already. for (unsigned int i = 0; i < this->ingredients.size(); i++){ if (!this->ingredients[i].getName().compare(ri.getName())){ @@ -48,6 +48,6 @@ void IngredientListModel::deleteIngredient(int index){ emit dataChanged(createIndex(0, 0), createIndex(this->ingredients.size()-1, 0)); } -vector IngredientListModel::getIngredients(){ +vector IngredientListModel::getIngredients(){ return this->ingredients; } diff --git a/model/recipe/ingredients/ingredientlistmodel.h b/model/recipe/ingredients/ingredientlistmodel.h index 738e570..31dd94d 100644 --- a/model/recipe/ingredients/ingredientlistmodel.h +++ b/model/recipe/ingredients/ingredientlistmodel.h @@ -16,13 +16,13 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; //Custom methods to handle ingredient data. - void setIngredients(vector ingredients); - bool addIngredient(RecipeIngredient ri); + void setIngredients(vector ingredients); + bool addIngredient(Ingredient ri); void deleteIngredient(int index); - vector getIngredients(); + vector getIngredients(); private: - vector ingredients; + vector ingredients; //Helper for printing. diff --git a/model/recipe/ingredients/recipeingredient.cpp b/model/recipe/ingredients/recipeingredient.cpp index 34de9d1..6e7d775 100644 --- a/model/recipe/ingredients/recipeingredient.cpp +++ b/model/recipe/ingredients/recipeingredient.cpp @@ -14,6 +14,10 @@ RecipeIngredient::RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure u setComment(comment); } +RecipeIngredient::RecipeIngredient(Ingredient &i) : RecipeIngredient(i, 0.0f, UnitOfMeasure("bleh"), "Fuck"){ + //Constructs recipe ingredient from ingredient which is a hidden recipe ingredient. +} + RecipeIngredient::RecipeIngredient(){ } diff --git a/model/recipe/ingredients/recipeingredient.h b/model/recipe/ingredients/recipeingredient.h index cd044a5..8c1dd45 100644 --- a/model/recipe/ingredients/recipeingredient.h +++ b/model/recipe/ingredients/recipeingredient.h @@ -21,6 +21,7 @@ public: RecipeIngredient(string name, string foodGroup, float quantity, UnitOfMeasure unit, string comment); //Constructor using data from a child ingredient. RecipeIngredient(Ingredient i, float quantity, UnitOfMeasure unit, string comment); + RecipeIngredient(Ingredient &i); RecipeIngredient(); //Getters diff --git a/model/recipe/ingredients/recipeingredientlistmodel.cpp b/model/recipe/ingredients/recipeingredientlistmodel.cpp new file mode 100644 index 0000000..26e98fd --- /dev/null +++ b/model/recipe/ingredients/recipeingredientlistmodel.cpp @@ -0,0 +1,53 @@ +#include "recipeingredientlistmodel.h" + +RecipeIngredientListModel::RecipeIngredientListModel(){ + this->ingredients = vector(); +} + +int RecipeIngredientListModel::rowCount(const QModelIndex &parent) const{ + return this->ingredients.size(); +} + +QVariant RecipeIngredientListModel::data(const QModelIndex &index, int role) const{ + int row = index.row(); + RecipeIngredient i = this->ingredients[row]; + + string displayStr = i.toString(); + + switch(role){ + case Qt::DisplayRole: + return QString::fromStdString(displayStr); + } + + return QVariant(); +} + +void RecipeIngredientListModel::setIngredients(vector ingredients){ + this->ingredients = ingredients; + QModelIndex index = createIndex(0, 0); + QModelIndex bottomIndex = createIndex(ingredients.size()-1, 0); + emit dataChanged(index, bottomIndex); +} + +bool RecipeIngredientListModel::addIngredient(RecipeIngredient ri){ + //Add only if it doesn't exist already. + for (unsigned int i = 0; i < this->ingredients.size(); i++){ + if (!this->ingredients[i].getName().compare(ri.getName())){ + return false; + } + } + this->ingredients.push_back(ri); + QModelIndex index = createIndex(this->ingredients.size()-1, 0); + QModelIndex bottomIndex = createIndex(this->ingredients.size()-1, 0); + emit dataChanged(index, bottomIndex); + return true; +} + +void RecipeIngredientListModel::deleteIngredient(int index){ + this->ingredients.erase(this->ingredients.begin() + index); + emit dataChanged(createIndex(0, 0), createIndex(this->ingredients.size()-1, 0)); +} + +vector RecipeIngredientListModel::getIngredients(){ + return this->ingredients; +} diff --git a/model/recipe/ingredients/recipeingredientlistmodel.h b/model/recipe/ingredients/recipeingredientlistmodel.h new file mode 100644 index 0000000..5ba9da0 --- /dev/null +++ b/model/recipe/ingredients/recipeingredientlistmodel.h @@ -0,0 +1,28 @@ +#ifndef RECIPEINGREDIENTLISTMODEL_H +#define RECIPEINGREDIENTLISTMODEL_H + +#include + +#include "model/recipe/ingredients/recipeingredient.h" + +class RecipeIngredientListModel : public QAbstractListModel +{ + public: + RecipeIngredientListModel(); + + //Overridden methods. + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + //Custom methods to handle ingredient data. + void setIngredients(vector ingredients); + bool addIngredient(RecipeIngredient ri); + void deleteIngredient(int index); + vector getIngredients(); + + private: + vector ingredients; + +}; + +#endif // RECIPEINGREDIENTLISTMODEL_H diff --git a/model/recipe/recipe.cpp b/model/recipe/recipe.cpp index 0859bd8..a899c1f 100644 --- a/model/recipe/recipe.cpp +++ b/model/recipe/recipe.cpp @@ -21,7 +21,17 @@ string Recipe::getName() const{ } vector Recipe::getIngredients() const{ - return this->ingredients; + return this->ingredients; +} + +vector Recipe::getFoodGroups() const{ + vector foodGroups; + for (RecipeIngredient ri : this->ingredients){ + if (find(foodGroups.begin(), foodGroups.end(), ri.getFoodGroup()) == foodGroups.end()){ + foodGroups.push_back(ri.getFoodGroup()); + } + } + return foodGroups; } Instruction Recipe::getInstruction() const{ diff --git a/model/recipe/recipe.h b/model/recipe/recipe.h index 048b09a..3bdb92d 100644 --- a/model/recipe/recipe.h +++ b/model/recipe/recipe.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "model/recipe/ingredients/recipeingredient.h" #include "model/recipe/instruction.h" @@ -38,6 +39,7 @@ public: //Getters string getName() const; vector getIngredients() const; + vector getFoodGroups() const; Instruction getInstruction() const; QImage getImage() const; vector getTags() const; diff --git a/model/recipe/recipetablemodel.cpp b/model/recipe/recipetablemodel.cpp index 521fa51..aab90e4 100644 --- a/model/recipe/recipetablemodel.cpp +++ b/model/recipe/recipetablemodel.cpp @@ -72,8 +72,8 @@ void RecipeTableModel::setRecipes(vector recipes){ endInsertRows(); } -Recipe RecipeTableModel::getRecipeAt(int index){ - if (index < 0 || index >= this->recipes.size()){ +Recipe RecipeTableModel::getRecipeAt(unsigned int index){ + if (index >= this->recipes.size()){ return Recipe(); } return this->recipes[index]; diff --git a/model/recipe/recipetablemodel.h b/model/recipe/recipetablemodel.h index 7449619..74645d7 100644 --- a/model/recipe/recipetablemodel.h +++ b/model/recipe/recipetablemodel.h @@ -20,7 +20,7 @@ class RecipeTableModel : public QAbstractTableModel //Normal methods. void setRecipes(vector recipes); - Recipe getRecipeAt(int index); + Recipe getRecipeAt(unsigned int index); void clear(); private: vector recipes; diff --git a/utils/fileutils.cpp b/utils/fileutils.cpp index a453c57..1e428b0 100644 --- a/utils/fileutils.cpp +++ b/utils/fileutils.cpp @@ -61,4 +61,18 @@ QImage loadImage(int nr){ return img; } +bool deleteInstruction(int nr){ + ensureAppDataFolderExists(); + QString filename = appDataPath + QString::fromStdString(std::to_string(nr)) +".html"; + QFile file(filename); + return file.remove(); +} + +bool deleteImage(int nr){ + ensureAppDataFolderExists(); + QString filename = appDataPath + QString::fromStdString(std::to_string(nr)) +".png"; + QFile file(filename); + return file.remove(); +} + } diff --git a/utils/fileutils.h b/utils/fileutils.h index b975215..322dd84 100644 --- a/utils/fileutils.h +++ b/utils/fileutils.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "model/recipe/instruction.h" @@ -19,9 +20,13 @@ namespace FileUtils{ Instruction loadInstruction(int nr); + bool deleteInstruction(int nr); + bool saveImage(int nr, QImage image); QImage loadImage(int nr); + + bool deleteImage(int nr); } #endif // FILEUTILS_H diff --git a/utils/stringutils.cpp b/utils/stringutils.cpp index f6115e8..86cafc3 100644 --- a/utils/stringutils.cpp +++ b/utils/stringutils.cpp @@ -29,4 +29,13 @@ std::string toString(float val){ return s; } +void printVector(std::vector &vect){ + std::printf("Vector of %ld elements:\n", vect.size()); + int c = 0; + for (std::string s : vect){ + std::printf("\t[%d] = %s\n", c, s.c_str()); + c++; + } +} + } diff --git a/utils/stringutils.h b/utils/stringutils.h index c678e46..5646704 100644 --- a/utils/stringutils.h +++ b/utils/stringutils.h @@ -2,6 +2,7 @@ #define STRINGUTILS_H #include +#include #include namespace StringUtils{