src/MainWindow.cpp

Sat, 11 Feb 2023 15:48:02 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 11 Feb 2023 15:48:02 +0100
changeset 493
520306773450
parent 485
83b5c2b3c414
child 523
632591d9825e
permissions
-rw-r--r--

Monitor iSpindels: use global variables instead of repeated expensive MySQL calls. Use the yeast temperature ranges for the colors on the thermometer scale. Don't show SVG and ABV if the OG is not yet known. Turn statusfield red if offline. Extra mon_ispindles fields yeast_lo and yeast_hi. Need at least bmsd version 0.3.42. If a websocket message is received that cannot be parsed, show the whole received message.

/**
 * MainWindow.cpp is part of bmsapp.
 *
 * bmsapp 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 3 of the License, or
 * (at your option) any later version.
 *
 * bmsapp is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "MainWindow.h"
#include "AboutDialog.h"
#include "RecipesTree.h"
#include "InventorySuppliers.h"
#include "InventoryFermentables.h"
#include "InventoryHops.h"
#include "InventoryYeasts.h"
#include "InventoryYeastPacks.h"
#include "InventoryMiscs.h"
#include "InventoryWaters.h"
#include "InventoryEquipments.h"
#include "ProdInprod.h"
#include "ProdOnName.h"
#include "ProdOnCode.h"
#include "ProdOnDate.h"
#include "ProdOnTree.h"
#include "ProfileWaters.h"
#include "ProfileMashs.h"
#include "ProfileStyles.h"
#include "ProfileFerments.h"
#include "MonNodes.h"
#include "MonFermenters.h"
#include "MonCO2meters.h"
#include "MoniSpindels.h"
#include "ImportXML.h"
#include "ImportBrew.h"
#include "Setup.h"
#include "PrinterDialog.h"
#include "../ui/ui_MainWindow.h"
#include "database/database.h"
#include "config.h"
#include "global.h"

#include <QApplication>
#include <QCloseEvent>
#include <QDebug>
#include <QStandardItem>
#include <QWidget>
#include <QtWidgets/QTableWidget>
#include <QtWidgets/QGroupBox>
#include <QtWidgets/QPushButton>
#include <QUrl>


MainWindow::MainWindow(bool useDevelopOption, bool startConfigOption, QWidget *parent) : QMainWindow(parent),  ui(new Ui::MainWindow)
{
    qDebug() << Q_FUNC_INFO << useDevelopOption << startConfigOption;
    ui->setupUi(this);
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );

    readsettings();
    db = new DataBase();
    db->openDataBase(useDevelopOption);

    updateDataBase();
    loadSetup();
    maintDataBase();
    openWS(useDevelopOption);

    /*
     * AcidSG are the values at 100% strength.
     */
    Acid a;
    a.name_en = "Lactic";
    a.name_nl = "Melkzuur";
    a.pK1 = 3.86;
    a.pK2 = 20.0;
    a.pK3 = 20.0;
    a.MolWt = 90.08;
    a.AcidSG = 1238.0;
    a.AcidPrc = 80.0;
    my_acids.append(a);
    a.name_en = "Hydrochloric";
    a.name_nl = "Zoutzuur";
    a.pK1 = -7.0;
    a.pK2 = 20.0;
    a.pK3 = 20.0;
    a.MolWt = 36.46;
    a.AcidSG = 1497.0;
    a.AcidPrc = 28.0;
    my_acids.append(a);
    a.name_en = "Phosphoric";
    a.name_nl = "Fosforzuur";
    a.pK1 = 2.12;
    a.pK2 = 7.20;
    a.pK3 = 12.44;
    a.MolWt = 98.00;
    a.AcidSG = 1773.0;
    a.AcidPrc = 75.0;
    my_acids.append(a);
    a.name_en = "Sulfuric";
    a.name_nl = "Zwavelzuur";
    a.pK1 = -1.0;
    a.pK2 = 1.92;
    a.pK3 = 20.0;
    a.MolWt = 98.07;
    a.AcidSG = 1884.0;
    a.AcidPrc = 93.0;
    my_acids.append(a);
}


MainWindow::~MainWindow()
{
    writesettings();
    webSocket->close(QWebSocketProtocol::CloseCodeNormal, "");
    db->closeDataBase();
    delete ui;
}


void MainWindow::loadSetup()
{
    /*
     * Load dedaults from the setup.
     */
    QSqlQuery query("SELECT * FROM profile_setup WHERE record='1'");
    query.first();
    my_brewery_name = query.value("brewery_name").toString();
    my_logoByteArray = query.value("brewery_logo").toByteArray();
    my_factor_mashhop = query.value("factor_mashhop").toInt();
    my_factor_fwh = query.value("factor_fwh").toInt();
    my_ut_pellet = query.value("ut_pellet").toDouble();
    my_ut_plug = query.value("ut_plug").toDouble();
    my_ut_leaf = query.value("ut_leaf").toDouble();
    my_ut_wethop = query.value("ut_wethop").toDouble();
    my_ut_t45 = query.value("ut_t45").toDouble();
    my_ut_co2extract = query.value("ut_co2extract").toDouble();
    my_ha_pellet = query.value("ha_pellet").toDouble();
    my_ha_plug = query.value("ha_plug").toDouble();
    my_ha_leaf = query.value("ha_leaf").toDouble();
    my_ha_wethop = query.value("ha_wethop").toDouble();
    my_ha_t45 = query.value("ha_t45").toDouble();
    my_ibu_method = query.value("ibu_method").toInt();
    my_color_method = query.value("color_method").toInt();
    my_brix_correction = query.value("brix_correction").toDouble();
    my_grain_absorption = query.value("grain_absorption").toDouble();
    my_default_water = query.value("default_water").toInt();
    my_yeastlab = query.value("my_yeastlab").toString();
    my_height = query.value("brewery_height").toInt();

    qInfo() << "loadSetup" << my_brewery_name;
}


/*
 * Upgrade database. Check and do upgrades.
 */
void MainWindow::updateDataBase()
{
    QSqlQuery query1, query2, query4;
    int	count = 0;
    bool added_packs = false;

    qDebug() << "updateDatabase() start";

    /*
     * Version 0.4.0.
     * Make sure we have the inventory_yeastpack with initial records.
     */
    query1.exec("CREATE TABLE IF NOT EXISTS `inventory_yeastpack` ("
  		"`record` int(11) NOT NULL AUTO_INCREMENT,"
  		"`uuid` varchar(36) NOT NULL,"
  		"`laboratory` varchar(128) NOT NULL,"
  		"`form` tinyint(4) NOT NULL DEFAULT 0,"
  		"`package` varchar(128) NOT NULL,"
  		"`notes` text DEFAULT NULL,"
  		"`cells` double NOT NULL DEFAULT 0,"
  		"`viability` double NOT NULL DEFAULT 0.99,"
		"`max` tinyint(4) NOT NULL DEFAULT 100,"
  		"`size` float NOT NULL DEFAULT 0,"
		"`used` int(11) NOT NULL DEFAULT 0,"
		"`valid` tinyint(1) NOT NULL DEFAULT 0,"
  		"PRIMARY KEY (`record`),"
  		"UNIQUE KEY `uuid` (`uuid`),"
		"UNIQUE KEY `package` (`laboratory`,`form`,`package`),"
  		"KEY `lab_form` (`laboratory`,`form`) USING BTREE"
		") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Yeast packages data'");
    if (query1.lastError().isValid()) {
	qWarning() << "  create inventory_yeastpack" << query1.lastError();
    } else {
	query1.exec("SELECT DISTINCT laboratory,form FROM inventory_yeasts");
	while (query1.next()) {
	    query2.prepare("SELECT record FROM inventory_yeastpack WHERE laboratory=:laboratory AND form=:form");
	    query2.bindValue(":laboratory", query1.value("laboratory").toString());
	    query2.bindValue(":form", query1.value("form").toInt());
	    query2.exec();
	    if (! query2.first()) {
	    	qDebug() << "  add yeastpack" << query1.value("laboratory").toString() << query1.value("form").toInt();

	    	query4.prepare("INSERT INTO inventory_yeastpack SET uuid=:uuid, laboratory=:laboratory, "
			   "form=:form, package=:package, viability=:viability, max=:max");
	    	query4.bindValue(":uuid", QUuid::createUuid().toString().mid(1, 36));
	    	query4.bindValue(":laboratory", query1.value("laboratory").toString());
	    	query4.bindValue(":form", query1.value("form").toInt());
	    	query4.bindValue(":package", g_yeast_forms[query1.value("form").toInt()]);
		switch (query1.value("form").toInt()) {
		    case YEAST_FORMS_LIQUID:	query4.bindValue(":viability", 0.80);
						query4.bindValue(":max", 97);
						break;
		    case YEAST_FORMS_DRY:	query4.bindValue(":viability", 0.998);
						query4.bindValue(":max", 100);
						break;
		    case YEAST_FORMS_DRIED:	query4.bindValue(":viability", 0.92);
						query4.bindValue(":max", 100);
						break;
		    default:			query4.bindValue(":viability", 0.99);
						query4.bindValue(":max", 97);
		}
	    	query4.exec();
	    	if (query4.lastError().isValid()) {
		    qWarning() << "  add yeastpack" << query4.lastError();
	    	} else {
		    count++;
		    added_packs = true;
	    	}
	    }
    	}
    }

    qDebug() << "updateDatabase()" << count << "updates";
}


void MainWindow::maintDataBase()
{
    QSqlQuery query, query1;

    /*
     * On the server where bmsd runs, there is a crontask.php that does these checks
     * too. Here we do some of the same commands so that we have the results sooner.
     * Currently this takes 6 to 9 mSecs.
     */
    query.exec("UPDATE products SET package_volume = bottle_amount + keg_amount WHERE package_volume='0'");
    if (query.numRowsAffected())
	qInfo() << "Updated" << query.numRowsAffected() << "products to new package_volume value";
    query.exec("UPDATE products SET bottle_priming_water = bottle_amount * bottle_priming_amount / 500 WHERE bottle_priming_water = 0");
    if (query.numRowsAffected())
	qInfo() << "Updated" << query.numRowsAffected() << "products to new bottle_priming_water";
    query.exec("UPDATE products SET keg_priming_water = keg_amount * keg_priming_amount / 500 WHERE keg_priming_water = 0");
    if (query.numRowsAffected())
	qInfo() << "Updated" << query.numRowsAffected() << "products to new keg_priming_water";

    /*
     * Upgrade inventory_reduced value from old boolean to tiny integer value.
     */
    query.exec("UPDATE products SET inventory_reduced=stage WHERE inventory_reduced = 1");
    if (query.numRowsAffected())
	qInfo() << "Updated" << query.numRowsAffected() << "products to new inventory_reduced value";

    /*
     * Update stages after packaging depending on the age.
     */
    query.exec("UPDATE products SET stage=7 WHERE stage = 6 AND DATEDIFF(CURDATE(), package_date) > 0");
    if (query.numRowsAffected())
	qInfo() << "Updated" << query.numRowsAffected() << "products to stage 7 (Carbonation)";
    query.exec("UPDATE products SET stage=8 WHERE stage = 7 AND DATEDIFF(CURDATE(), package_date) > 13");
    if (query.numRowsAffected())
	qInfo() << "Updated" << query.numRowsAffected() << "products to stage 8 (Mature)";
    query.exec("UPDATE products SET stage=9 WHERE stage = 8 AND DATEDIFF(CURDATE(), package_date) > 41");
    if (query.numRowsAffected())
	qInfo() << "Updated" << query.numRowsAffected() << "products to stage 9 (Taste)";

    /*
     * Count and update references in inventory_yeastpack to inventory_yeasts
     */
    query.exec("SELECT laboratory,form FROM inventory_yeastpack");
    while (query.next()) {
	query1.prepare("SELECT COUNT(*) FROM inventory_yeasts WHERE laboratory=:laboratory AND form=:form");
	query1.bindValue(":laboratory", query.value("laboratory").toString());
        query1.bindValue(":form", query.value("form").toInt());
	query1.exec();
	if (! query1.first()) {
	    qWarning() << "SELECT COUNT(*) FROM inventory_yeasts";
	} else {
	    int count = query1.value("COUNT(*)").toInt();
	    query1.prepare("UPDATE inventory_yeastpack SET used=:used WHERE laboratory=:laboratory AND form=:form");
	    query1.bindValue(":used", count);
	    query1.bindValue(":laboratory", query.value("laboratory").toString());
            query1.bindValue(":form", query.value("form").toInt());
	    query1.exec();
	}
    }
}


bool MainWindow::openWS(bool develop)
{
    QString server;
    if (develop)
	server = wsDev.host;
    else
	server = wsProd.host;
    QUrl url(QString("ws://%1/ws").arg(server));
    qInfo() << "Open websocket:" << url;

    webSocket = new QWebSocket;
    QObject::connect(webSocket, &QWebSocket::connected, this, &MainWindow::wsConnected);
    QObject::connect(webSocket, &QWebSocket::disconnected, this, &MainWindow::wsClosed);

    webSocket->open(QUrl(url));
    return true;
}


void MainWindow::wsConnected()
{
    qDebug() << Q_FUNC_INFO;

    connect(webSocket, &QWebSocket::textMessageReceived, this, &MainWindow::wsTextMessageReceived);
}


void MainWindow::wsClosed()
{
    qWarning() << Q_FUNC_INFO << webSocket->closeReason();

    // Should triger a periodic timer to try to reconnect.
}


void MainWindow::wsTextMessageReceived(QString message)
{
    //qDebug() << "WS received:" << message;

    QJsonParseError parseError;
    QJsonDocument jsonMessage = QJsonDocument::fromJson(message.toUtf8(),  &parseError);

    if (parseError.error != QJsonParseError::NoError) {
	qWarning() << "wsTextMessageReceived error:" << parseError.errorString() << "at" << parseError.offset ;
	qWarning() << message;
	return;
    }

    /*
     * Two maingroups, nodes and devices.
     * Node message are detected by the group_id object.
     * Device messages are detected by the device object.
     *
     * Signals can be connected from child widgets so we can emit messages to them.
     */
    QString device = jsonMessage.object()["device"].toString();
    QString group_id = jsonMessage.object()["group_id"].toString();
    if (device != "") {
	if (device == "fermenters") {
	    //qDebug() << "found fermenter" << jsonMessage.object()["node"].toString()+"/"+jsonMessage.object()["unit"].toString();
	    emit updateFermenters(jsonMessage.object()["node"].toString()+"/"+jsonMessage.object()["unit"].toString());
	} else if (device == "co2meters") {
	    //qDebug() << "found co2meter" << jsonMessage.object()["node"].toString()+"/"+jsonMessage.object()["unit"].toString();
	    emit updateCO2meters(jsonMessage.object()["node"].toString()+"/"+jsonMessage.object()["unit"].toString());
	} else if (device == "ispindels") {
	    //qDebug() << "found iSpindel" << jsonMessage.object()["node"].toString()+"/"+jsonMessage.object()["unit"].toString();
	    emit updateiSpindels(jsonMessage.object()["node"].toString()+"/"+jsonMessage.object()["unit"].toString());
	} else {
	    qDebug() << "unknown device" << device;
	}
    } else if (group_id != "") {
	emit updateNodes(jsonMessage.object()["node"].toString());
    } else if (jsonMessage.object()["ping"].toInt() ) {
	/*
	 * Reply to ping message. Note that the bmsd ignore's
	 * our reply, but we send it anyway.
	 */
	webSocket->sendTextMessage(QString("{\"pong\":1}"));
    } else {
	qDebug() << "unknown WS message" << message << jsonMessage;
    }
}


void MainWindow::readsettings()
{
    QSettings settings(QSettings::IniFormat, QSettings::UserScope, "mbse", "bmsapp");

    settings.beginGroup("dbprod");
    dbProd.host = settings.value("host").toString();
    if (dbProd.host.isEmpty()) {
        dbProd.host = "localhost";
        dbProd.port = "3306";
        dbProd.name = "bms_prod";
        dbProd.user = "nobody";
        dbProd.pass = "secret";
        dbProd.charset = "utf8";
        settings.setValue("host", dbProd.host);
        settings.setValue("port", dbProd.port);
        settings.setValue("name", dbProd.name);
        settings.setValue("user", dbProd.user);
        settings.setValue("pass", dbProd.pass);
        settings.setValue("charset", dbProd.charset);
    } else {
        dbProd.port = settings.value("port").toString();
        dbProd.name = settings.value("name").toString();
        dbProd.user = settings.value("user").toString();
        dbProd.pass = settings.value("pass").toString();
        dbProd.charset = settings.value("charset").toString();
    }
    settings.endGroup();
    qDebug() << "MySQL prod" << dbProd.host << dbProd.port << dbProd.name << dbProd.pass;

    settings.beginGroup("dbdev");
    dbDev.host = settings.value("host").toString();
    if (dbDev.host.isEmpty()) {
        dbDev.host = "localhost";
        dbDev.port = "3306";
        dbDev.name = "bms_dev";
        dbDev.user = "nobody";
        dbDev.pass = "secret";
        dbDev.charset = "utf8";
        settings.setValue("host", dbDev.host);
        settings.setValue("port", dbDev.port);
        settings.setValue("name", dbDev.name);
        settings.setValue("user", dbDev.user);
        settings.setValue("pass", dbDev.pass);
        settings.setValue("charset", dbDev.charset);
    } else {
        dbDev.port = settings.value("port").toString();
        dbDev.name = settings.value("name").toString();
        dbDev.user = settings.value("user").toString();
        dbDev.pass = settings.value("pass").toString();
        dbDev.charset = settings.value("charset").toString();
    }
    settings.endGroup();
    qDebug() << "MySQL dev" << dbDev.host << dbDev.port << dbDev.name << dbDev.pass;

    settings.beginGroup("wsprod");
    wsProd.host = settings.value("host").toString();
    if (wsProd.host.isEmpty()) {
        wsProd.host = "localhost";
        settings.setValue("host", wsProd.host);
    }
    settings.endGroup();
    qDebug() << "WS prod" << wsProd.host;

    settings.beginGroup("wsdev");
    wsDev.host = settings.value("host").toString();
    if (wsDev.host.isEmpty()) {
        wsDev.host = "localhost";
        settings.setValue("host", wsDev.host);
    }
    settings.endGroup();
    qDebug() << "WS dev" << wsDev.host;

    settings.beginGroup("paths");
    if (settings.value("images").toString().isEmpty()) {
	settings.setValue("download", QDir::homePath());
	settings.setValue("images", QDir::homePath());
	settings.setValue("beerxml", QDir::homePath());
    }
    settings.endGroup();
}


void MainWindow::writesettings()
{
    QSettings settings(QSettings::IniFormat, QSettings::UserScope, "mbse", "bmsapp");

    settings.beginGroup("dbprod");
    settings.setValue("host", dbProd.host);
    settings.setValue("port", dbProd.port);
    settings.setValue("name", dbProd.name);
    settings.setValue("user", dbProd.user);
    settings.setValue("pass", dbProd.pass);
    settings.setValue("charset", dbProd.charset);
    settings.endGroup();

    settings.beginGroup("dbdev");
    settings.setValue("host", dbDev.host);
    settings.setValue("port", dbDev.port);
    settings.setValue("name", dbDev.name);
    settings.setValue("user", dbDev.user);
    settings.setValue("pass", dbDev.pass);
    settings.setValue("charset", dbDev.charset);
    settings.endGroup();

    settings.beginGroup("wsprod");
    settings.setValue("host", wsProd.host);
    settings.endGroup();

    settings.beginGroup("wsdev");
    settings.setValue("host", wsDev.host);
    settings.endGroup();

    qDebug() << "writesettings() done.";
}


void MainWindow::on_actionExit_triggered()
{
    this->close();
}


void MainWindow::fromImportXML()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ImportXMLWindow);
    delete ImportXMLWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
}


void MainWindow::on_actionImport_XML_triggered()
{
    ImportXMLWindow = new ImportXML(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ImportXMLWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle( QString("BMSapp - %1 - Import XML").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromImportBrew()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ImportBrewWindow);
    delete ImportBrewWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
}


void MainWindow::on_actionImport_Brew_triggered()
{
    ImportBrewWindow = new ImportBrew(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ImportBrewWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle( QString("BMSapp - %1 - Import Brewlog").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromMonNodes()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(MonNodesWindow);
    delete MonNodesWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
}


void MainWindow::on_actionMon_Nodes_triggered()
{
    MonNodesWindow = new MonNodes(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(MonNodesWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle( QString("BMSapp - %1 - Monitor Nodes").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromMonFermenters()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(MonFermentersWindow);
    delete MonFermentersWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
}


void MainWindow::on_actionMon_Fermenters_triggered()
{
    MonFermentersWindow = new MonFermenters(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(MonFermentersWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle( QString("BMSapp - %1 - Monitor Fermenters").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromMonCO2meters()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(MonCO2metersWindow);
    delete MonCO2metersWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
}


void MainWindow::on_actionMon_CO2meters_triggered()
{
    MonCO2metersWindow = new MonCO2meters(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(MonCO2metersWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle( QString("BMSapp - %1 - Monitor CO2meters").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromMoniSpindels()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(MoniSpindelsWindow);
    delete MoniSpindelsWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
}


void MainWindow::on_actionMon_iSpindels_triggered()
{
    MoniSpindelsWindow = new MoniSpindels(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(MoniSpindelsWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle( QString("BMSapp - %1 - Monitor iSpindels").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromRecipesTree()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(RecipesTreeWindow);
    delete RecipesTreeWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
}


void MainWindow::on_actionRecipes_triggered()
{
    RecipesTreeWindow = new RecipesTree(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(RecipesTreeWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle( QString("BMSapp - %1 - Recipes").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromInventorySuppliers()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(InventorySuppliersWindow);
    delete InventorySuppliersWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
}


void MainWindow::on_actionSuppliers_triggered()
{
    InventorySuppliersWindow = new InventorySuppliers(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(InventorySuppliersWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle( QString("BMSapp - %1 - Inventory Suppliers").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromInventoryFermentables()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(InventoryFermentablesWindow);
    delete InventoryFermentablesWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionFermentables_triggered()
{
    InventoryFermentablesWindow = new InventoryFermentables(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(InventoryFermentablesWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Inventory Fermentables").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromInventoryHops()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(InventoryHopsWindow);
    delete InventoryHopsWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionHops_triggered()
{
    InventoryHopsWindow = new InventoryHops(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(InventoryHopsWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Inventory Hops").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromInventoryYeasts()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(InventoryYeastsWindow);
    delete InventoryYeastsWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionYeasts_triggered()
{
    InventoryYeastsWindow = new InventoryYeasts(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(InventoryYeastsWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Inventory Yeasts").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromInventoryYeastPacks()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(InventoryYeastPacksWindow);
    delete InventoryYeastPacksWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionYeastPacks_triggered()
{
    InventoryYeastPacksWindow = new InventoryYeastPacks(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(InventoryYeastPacksWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Inventory Yeasts").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromInventoryMiscs()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(InventoryMiscsWindow);
    delete InventoryMiscsWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionMiscs_triggered()
{
    InventoryMiscsWindow = new InventoryMiscs(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(InventoryMiscsWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Inventory Miscs").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromInventoryWaters()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(InventoryWatersWindow);
    delete InventoryWatersWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionWaters_triggered()
{
    InventoryWatersWindow = new InventoryWaters(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(InventoryWatersWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Inventory Waters").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromInventoryEquipments()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(InventoryEquipmentsWindow);
    delete InventoryEquipmentsWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionEquipments_triggered()
{
    InventoryEquipmentsWindow = new InventoryEquipments(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(InventoryEquipmentsWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Inventory Equipments").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::on_actionSupplies_list_triggered()
{
    PrinterDialog(PR_SUPPLIES, -1, this);
}


void MainWindow::on_actionYeast_bank_triggered()
{
    PrinterDialog(PR_YEASTBANK, -1, this);
}


void MainWindow::fromProdInprod()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ProdInprodWindow);
    delete ProdInprodWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionProd_inprod_triggered()
{
    ProdInprodWindow = new ProdInprod(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ProdInprodWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Products in production").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromProdOnName()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ProdOnNameWindow);
    delete ProdOnNameWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionOn_Name_triggered()
{
    ProdOnNameWindow = new ProdOnName(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ProdOnNameWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Products archive on name").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromProdOnCode()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ProdOnCodeWindow);
    delete ProdOnCodeWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionOn_Code_triggered()
{
    ProdOnCodeWindow = new ProdOnCode(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ProdOnCodeWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Products archive on code").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromProdOnDate()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ProdOnDateWindow);
    delete ProdOnDateWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionOn_Date_triggered()
{
    ProdOnDateWindow = new ProdOnDate(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ProdOnDateWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Products archive on date").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromProdOnTree()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ProdOnTreeWindow);
    delete ProdOnTreeWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionOn_Tree_triggered()
{
    ProdOnTreeWindow = new ProdOnTree(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ProdOnTreeWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Products archive on beerstyle").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::on_actionRep_Production_triggered()
{
    PrinterDialog(PR_REP_TOTAL, -1, this);
}


void MainWindow::on_actionRep_Efficiency_triggered()
{
    PrinterDialog(PR_REP_EFF, -1, this);
}


void MainWindow::on_actionRep_Fermentation_triggered()
{
    PrinterDialog(PR_REP_SVG, -1, this);
}


void MainWindow::fromProfileWaters()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ProfileWatersWindow);
    delete ProfileWatersWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionWater_profiles_triggered()
{
    ProfileWatersWindow = new ProfileWaters(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ProfileWatersWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Water Profiles").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromProfileMashs()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ProfileMashsWindow);
    delete ProfileMashsWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionMash_profiles_triggered()
{
    ProfileMashsWindow = new ProfileMashs(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ProfileMashsWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Mash Profiles").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromProfileStyles()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ProfileStylesWindow);
    delete ProfileStylesWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionStyles_profiles_triggered()
{
    ProfileStylesWindow = new ProfileStyles(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ProfileStylesWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Styles Profiles").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromProfileFerments()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(ProfileFermentsWindow);
    delete ProfileFermentsWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    statusBar()->clearMessage();
}


void MainWindow::on_actionFerments_profiles_triggered()
{
    ProfileFermentsWindow = new ProfileFerments(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(ProfileFermentsWindow);
    ui->mainStack->setCurrentIndex(index);
    setWindowTitle(QString("BMSapp - %1 - Fermentation Profiles").arg(VERSIONSTRING));
    ui->menuBar->setVisible(false);
}


void MainWindow::fromSetup()
{
    ui->mainStack->setCurrentIndex(-1);
    ui->mainStack->removeWidget(SetupWindow);
    delete SetupWindow;
    setWindowTitle( QString("BMSapp - %1").arg(VERSIONSTRING) );
    ui->menuBar->setVisible(true);
    loadSetup();
}


void MainWindow::on_actionSetup_triggered()
{
    SetupWindow = new Setup(this);
    int index = ui->mainStack->count();
    ui->mainStack->addWidget(SetupWindow);
    ui->mainStack->setCurrentIndex(index);
    ui->menuBar->setVisible(false);
}


void MainWindow::on_actionAbout_triggered()
{
    AboutDialog dialog(this);
    dialog.setModal(true);
    dialog.exec();
}


void MainWindow::windowTitle(QString msg)
{
    setWindowTitle(QString("BMSapp - %1 - %2").arg(VERSIONSTRING).arg(msg));
}


void MainWindow::statusMsg(QString msg)
{
    statusBar()->showMessage(msg);
}

mercurial