Logo Search packages:      
Sourcecode: qbrew version File versions

model.cpp

/***************************************************************************
  model.cpp
  -------------------
  Model class for brewing
  -------------------
  Copyright (c) 2001-2004 David Johnson
  Please see the header file for copyright and license information.
 ***************************************************************************/

#include <qdir.h>
#include <qfile.h>
#include <qdom.h>
#include <qstringlist.h>
#include <qtextstream.h>

#include "controller.h"
#include "resource.h"

#include "model.h"

using namespace AppResource;
using namespace CalcResource;

Model *Model::instance_ = 0;

// Construction, Destruction /////////////////////////////////////////////////

// Private constructor
Model::Model()
    : QObject(0, "model"), defaultsize_(Volume(5.0, Volume::gallon)),
      defaultmash_(false), defaulthopform_(HOP_PELLET),
      defaultgrainunit_(&Weight::pound), defaulthopsunit_(&Weight::ounce),
      defaultmiscunit_(&Quantity::generic), graindb_(), hopdb_(), miscdb_(),
      styledb_(), recipe_(0), datadir_("")
{
    // load data file - try user home directory first, quietly...
    if (!loadData(QDIR_HOME + "/." + ID_CALC_FILE, true)) {
        // then the default data file
        if (!loadData(Controller::instance()->dataBase() + ID_CALC_FILE)) {
            // else load in some defaults
            styledb_.append(Style());
            graindb_.append(Grain());
            hopdb_.append(Hops());
            miscdb_.append(MiscIngredient());
            UEntry u = {0, 20};
            Calc::addUEntry(u);
        }
    }
    recipe_ = new Recipe();
}

// Private destructor
Model::~Model()
{ ; }

// Return pointer to the model
Model *Model::instance()
{
    if (!instance_)
        instance_ = new Model();
    return instance_;
}

//////////////////////////////////////////////////////////////////////////////
// Data Access                                                              //
//////////////////////////////////////////////////////////////////////////////

void Model::setDefaultSize(const Volume &v)
{
    defaultsize_ = v;
    // convert recipe size
    recipe_->setSize(defaultsize_);
}

void Model::setDefaultStyle(const Style &s)
{
    defaultstyle_ = s;
    recipe_->setStyle(defaultstyle_);
}

void Model::setDefaultGrainUnit(Unit &u)
{
    defaultgrainunit_ = &u;
    // convert recipe grain units
    GrainIterator it;
    for (it = recipe_->grains()->begin(); it != recipe_->grains()->end(); ++it)
        (*it).weight().convert(u);    
    // convert graindb units
    for (it = graindb_.begin(); it != graindb_.end(); ++it )
        (*it).setWeight(Weight(1.0, u));    
}

void Model::setDefaultHopsUnit(Unit &u)
{
    defaulthopsunit_ = &u;
    // convert recipe hop units
    HopIterator it;
    for (it = recipe_->hops()->begin(); it != recipe_->hops()->end(); ++it)
        (*it).weight().convert(u);
    // convert hopsdb units
    for (it = hopdb_.begin(); it != hopdb_.end(); ++it )
        (*it).setWeight(Weight(1.0, u));
}

void Model::setDefaultMiscUnit(Unit &u)
{
    defaultmiscunit_ = &u;
    // convert recipe misc units
    MiscIngredientIterator it;
    for (it = recipe_->miscs()->begin(); it != recipe_->miscs()->end(); ++it)
        (*it).quantity().convert(u);
    // convert miscdb units
    for (it = miscdb_.begin(); it != miscdb_.end(); ++it )
        (*it).setQuantity(Quantity(1.0, u));
}

//////////////////////////////////////////////////////////////////////////////
// stylesList()
// ------------
// Return a string list of available styles

QStringList Model::stylesList()
{
    QStringList list;
    StyleIterator it;
    for (it=styledb_.begin(); it!=styledb_.end(); ++it) {
        list += (*it).name();
    }
    list.sort();
    return list;
}

//////////////////////////////////////////////////////////////////////////////
// style()
// -------
// Return a style given its name

Style &Model::style(const QString &name)
{
    static Style defaultstyle;

    StyleIterator it;
    for (it=styledb_.begin(); it!=styledb_.end(); ++it) {
        if (name == (*it).name()) return (*it);
    }
    return defaultstyle;
}

//////////////////////////////////////////////////////////////////////////////
// checkGrain()
// ----------
// Check existance of ingredient, adding it to db if not found

void Model::checkGrain(const Grain &g)
{
    bool found = false;
    GrainIterator it;
    for (it=graindb_.begin(); it!=graindb_.end(); ++it) {
        if (g.name() == (*it).name()) found = true;
    }

    if (!found) graindb_.append(g);
}

//////////////////////////////////////////////////////////////////////////////
// grainsList()
// ------------
// Return string list of available grains

QStringList Model::grainsList()
{
    QStringList list;
    GrainIterator it;
    for (it=graindb_.begin(); it!=graindb_.end(); ++it) {
        list += (*it).name();
    }
    list.sort();
    return list;
}

//////////////////////////////////////////////////////////////////////////////
// grain()
// -------
// Return grain given its name

Grain* Model::grain(const QString &name)
{
    GrainIterator it;
    for (it=graindb_.begin(); it!=graindb_.end(); ++it) {
        if (name == (*it).name()) return &(*it);
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////
// checkHop()
// ----------
// Check existance of ingredient, adding it to db if not found

void Model::checkHops(const Hops &h)
{
    bool found = false;
    HopIterator it;
    for (it=hopdb_.begin(); it!=hopdb_.end(); ++it) {
        if (h.name() == (*it).name()) found = true;
    }

    if (!found) hopdb_.append(h);
}

//////////////////////////////////////////////////////////////////////////////
// hopsList()
// ----------
// Return string list of available hops

QStringList Model::hopsList()
{
    QStringList list;
    HopIterator it;
    for (it=hopdb_.begin(); it != hopdb_.end(); ++it) {
        list += (*it).name();
    }
    list.sort();
    return list;
}

//////////////////////////////////////////////////////////////////////////////
// formsList()
// ----------
// Return string list of available hop forms

QStringList Model::formsList()
{
    QStringList list;
    list << HOP_PELLET << HOP_PLUG << HOP_WHOLE;
    // search through existing hopdb for other forms
    HopIterator it;
    for (it=hopdb_.begin(); it!=hopdb_.end(); ++it) {
        if ((!(*it).form().isEmpty())
            && (list.contains((*it).form()) == 0)) list += (*it).form();
    }
    list.sort();
    return list;
}

//////////////////////////////////////////////////////////////////////////////
// hop()
// -----
// Return hop given its name

Hops* Model::hop(const QString &name)
{
    HopIterator it;
    for (it=hopdb_.begin(); it!=hopdb_.end(); ++it) {
        if (name == (*it).name()) return &(*it);
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////
// checkMisc()
// ----------
// Check existance of ingredient, adding it to db if not found

void Model::checkMisc(const MiscIngredient &m)
{
    bool found = false;
    MiscIngredientIterator it;
    for (it=miscdb_.begin(); it!=miscdb_.end(); ++it) {
        if (m.name() == (*it).name()) found = true;
    }

    if (!found) miscdb_.append(m);
}

//////////////////////////////////////////////////////////////////////////////
// miscList()
// ----------
// Return string list of available misc ingredients

QStringList Model::miscList()
{
    QStringList list;
    MiscIngredientIterator it;
    for (it=miscdb_.begin(); it!=miscdb_.end(); ++it) {
        list += (*it).name();
    }
    list.sort();
    return list;
}

//////////////////////////////////////////////////////////////////////////////
// misc()
// ------
// Return misc ingredient given its name

MiscIngredient* Model::misc(const QString &name)
{
    MiscIngredientIterator it;
    for (it=miscdb_.begin(); it!=miscdb_.end(); ++it) {
        if (name == (*it).name()) return &(*it);
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////
// Serialization                                                            //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// loadData()
// ----------
// Load the data

bool Model::loadData(const QString &filename, bool quiet)
{
    // TODO: need more error checking on tags and elements
    // open file
    QFile* datafile = new QFile(filename);
    if (!datafile->open(IO_ReadOnly)) {
        // error opening file
        if (!quiet) qWarning("Error: Cannot open " + filename);
        datafile->close();
        delete (datafile);
        return false;
    }

    // open dom document
    QDomDocument doc;
    doc.setContent(datafile);
    datafile->close();
    delete (datafile);

    // check the doc type and stuff
    if (doc.doctype().name() != tagDoc) {
        // wrong file type
        if (!quiet) qWarning("Error: Wrong file type " + filename);
        return false;
    }
    QDomElement root = doc.documentElement();

    // check file version
    if (root.attribute(attrVersion) < CALC_PREVIOUS) {
        // too old of a version
        if (!quiet) qWarning("Error: Unsupported version " + filename);
        return false;
    }

    // get all styles tags
    styledb_.clear();
    QDomNodeList nodes = root.elementsByTagName(tagStyles);
    QDomNodeList subnodes;
    QDomElement element;
    QDomElement subelement;
    unsigned n, m;
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            // get all style tags
            subnodes = element.elementsByTagName(tagStyle);
            for (m=0; m<subnodes.count(); m++) {
                if (subnodes.item(m).isElement()) {
                    subelement = subnodes.item(m).toElement();
                    styledb_.append(Style(subelement.text(),
                        subelement.attribute(attrOGLow).toDouble(),
                        subelement.attribute(attrOGHigh).toDouble(),
                        subelement.attribute(attrIBULow).toDouble(),
                        subelement.attribute(attrIBUHigh).toDouble(),
                        subelement.attribute(attrSRMLow).toDouble(),
                        subelement.attribute(attrSRMHigh).toDouble()));
                }
            }
        }
    }

   // get all grains tags
    graindb_.clear();
    nodes = root.elementsByTagName(tagGrains);
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            // get all grain tags
            subnodes = element.elementsByTagName(tagGrain);
            for (m=0; m<subnodes.count(); m++) {
                if (subnodes.item(m).isElement()) {
                    subelement = subnodes.item(m).toElement();
                    graindb_.append(Grain(subelement.text(),
                        Weight(1.0, *defaultgrainunit_),
                        subelement.attribute(attrExtract).toDouble(),
                        subelement.attribute(attrColor).toDouble(),
                        subelement.attribute(attrUse)));
                }
            }
        }
    }

    // get all hops tags
    hopdb_.clear();
    nodes = root.elementsByTagName(tagHops);
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            // get all hop tags
            subnodes = element.elementsByTagName(tagHop);
            for (m=0; m<subnodes.count(); m++) {
                if (subnodes.item(m).isElement()) {
                    subelement = subnodes.item(m).toElement();
                    hopdb_.append(Hops(subelement.text(),
                        Weight(1.0, *defaulthopsunit_), QString::null,
                        subelement.attribute(attrAlpha).toDouble(), 60));
                }
            }
        }
    }

    // get all miscingredients tags
    miscdb_.clear();
    nodes = root.elementsByTagName(tagMiscIngredients);
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            // get all miscingredient tags
            subnodes = element.elementsByTagName(tagMiscIngredient);
            for (m=0; m<subnodes.count(); m++) {
                if (subnodes.item(m).isElement()) {
                    subelement = subnodes.item(m).toElement();
                    miscdb_.append(MiscIngredient(subelement.text(),
                        Quantity(1.0, *defaultmiscunit_),
                        subelement.attribute(attrNotes)));
                }
            }
        }
    }

    // get all utilization tags
    nodes = root.elementsByTagName(tagUtilization);
    UEntry entry;
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            // get all entry tags
            subnodes = element.elementsByTagName(tagEntry);
            for (m=0; m<subnodes.count(); m++) {
                if (subnodes.item(m).isElement()) {
                    subelement = subnodes.item(m).toElement();
                    entry.time = subelement.attribute(attrTime).toUInt();
                    entry.utilization = subelement.attribute(attrUtil).toUInt();
                    Calc::addUEntry(entry);
                }
            }
        }
    }
    return true;
}

//////////////////////////////////////////////////////////////////////////////
// saveData()
// ------------
// Save info to data file

void Model::saveData(const QString &filename)
{
    QDomDocument doc(tagDoc);

    // create the root element
    QDomElement root = doc.createElement(doc.doctype().name());
    root.setAttribute(attrVersion, VERSION);
    doc.appendChild(root);

    // styles elements
    QDomElement element = doc.createElement(tagStyles);
    StyleIterator sit;
    QDomElement subelement;
    // iterate through styles list
    for (sit=styledb_.begin(); sit!=styledb_.end(); ++sit) {
        subelement = doc.createElement(tagStyle);
        subelement.appendChild(doc.createTextNode((*sit).name()));
        subelement.setAttribute(attrOGLow, (*sit).OGLow());
        subelement.setAttribute(attrOGHigh, (*sit).OGHi());
        subelement.setAttribute(attrIBULow, (*sit).IBULow());
        subelement.setAttribute(attrIBUHigh, (*sit).IBUHi());
        subelement.setAttribute(attrSRMLow, (*sit).SRMLow());
        subelement.setAttribute(attrSRMHigh, (*sit).SRMHi());
        element.appendChild(subelement);
    }
    root.appendChild(element);

    // grains elements
    element = doc.createElement(tagGrains);
    GrainIterator git;
    // iterate through grains_ list
    for (git=graindb_.begin(); git!=graindb_.end(); ++git) {
        subelement = doc.createElement(tagGrain);
        subelement.appendChild(doc.createTextNode((*git).name()));
        subelement.setAttribute(attrExtract, (*git).extract());
        subelement.setAttribute(attrColor, (*git).color());
        subelement.setAttribute(attrUse, (*git).useString());
        element.appendChild(subelement);
    }
    root.appendChild(element);

    // hops elements
    element = doc.createElement(tagHops);
    HopIterator hit;
    // iterate through hops_ list
    for (hit=hopdb_.begin(); hit!=hopdb_.end(); ++hit) {
        subelement = doc.createElement(tagHop);
        subelement.appendChild(doc.createTextNode((*hit).name()));
        subelement.setAttribute(attrAlpha, (*hit).alpha());
        element.appendChild(subelement);
    }
    root.appendChild(element);

    // miscingredients elements
    element = doc.createElement(tagMiscIngredients);
    MiscIngredientIterator mit;
    // iterate through misc_ list
    for (mit=miscdb_.begin(); mit!=miscdb_.end(); ++mit) {
        subelement = doc.createElement(tagMiscIngredient);
        subelement.appendChild(doc.createTextNode((*mit).name()));
        subelement.setAttribute(attrNotes, (*mit).notes());
        element.appendChild(subelement);
    }
    root.appendChild(element);

    // utilization elements
    element = doc.createElement(tagUtilization);
    QValueList<UEntry>::Iterator uit;
    QValueList<UEntry> ulist = Calc::getUEntryList();
    // iterate through uentry list
    for (uit=ulist.begin(); uit!=ulist.end(); ++uit) {
        subelement = doc.createElement(tagEntry);
        subelement.setAttribute(attrTime, (*uit).time);
        subelement.setAttribute(attrUtil, (*uit).utilization);
        element.appendChild(subelement);
    }
    root.appendChild(element);

    // open file
    QFile* datafile = new QFile(filename);
    if (!datafile->open(IO_WriteOnly)) {
        // error opening file
        qWarning("Error: Cannot open file " + filename);
        datafile->close();
    }

    // write it out
    QTextStream textstream(datafile);
    doc.save(textstream, 0);
    datafile->close();
    delete (datafile);
}

//////////////////////////////////////////////////////////////////////////////
// newRecipe()
// -----------
// Creates a new recipe

void Model::newRecipe()
{
    if (recipe_) delete recipe_;
    recipe_ = new Recipe(QString::null, QString::null, defaultmash_,
                         defaultsize_, defaultstyle_, GrainList(), HopsList(),
                         MiscIngredientList(), QString::null, QString::null);
    setModified(false);
    emit (recipeChanged());
}

//////////////////////////////////////////////////////////////////////////////
// loadRecipe()
// ------
// load the recipe from file

bool Model::loadRecipe(const QString &filename)
{
    // TODO: need more error checking on tags and elements

    // open file
    QFile* datafile = new QFile(filename);
    if (!datafile->open(IO_ReadOnly)) {
        // error opening file
        qWarning("Error: Cannot open " + filename);
        datafile->close();
        delete (datafile);
        return false;
    }

    // open dom document
    QDomDocument doc;
    doc.setContent(datafile);
    datafile->close();
    delete (datafile);

    // check the doc type and stuff
    if (doc.doctype().name() != tagRecipe) {
        // wrong file type
        qWarning("Error: Wrong file type " + filename);
        return false;
    }
    QDomElement root = doc.documentElement();

    // check generator
    if (root.attribute(attrGenerator) != PACKAGE) {
        // right file type, wrong generator
        qWarning("Not a recipe file for " + QString(PACKAGE));
        return false;
    }

    // check file version
    if (root.attribute(attrVersion) < QBREW_PREVIOUS) {
        // too old of a version
        qWarning("Error: Unsupported version " + filename);
        return false;
    }

    // new recipe
    if (recipe_) delete recipe_;
    recipe_ = new Recipe();

    // Note: for some of these tags, only one in a document makes sense.
    // But if there is more than one, process them in order, with later
    // ones overwriting the earlier

    // get title
    QDomNodeList nodes = root.elementsByTagName(tagTitle);
    QDomElement element;
      unsigned n, m;
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            recipe_->setTitle(element.text().latin1());
        }
    }
    // get brewer
    nodes = root.elementsByTagName(tagBrewer);
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            recipe_->setBrewer(element.text().latin1());
        }
    }
    // get style
    nodes = root.elementsByTagName(tagStyle);
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            recipe_->setStyle(style(element.text()));
        }
    }
    // get batch settings
    nodes = root.elementsByTagName(tagBatch);
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            if (element.hasAttribute(attrSize)) {
                // backwards compatibility
                recipe_->setSize(Volume(element.attribute(attrSize).toDouble(),
                                        Volume::gallon));
            } else {
                recipe_->size().fromQString(element.attribute(attrQuantity),
                                           Volume::gallon);
            }
            recipe_->setMashed(element.attribute(attrMash).lower() == "true");
        }
    }
    // get notes
    nodes = root.elementsByTagName(tagNotes);
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            if (element.hasAttribute(attrClass)) {
                if (element.attribute(attrClass) == classRecipe) {
                    recipe_->setRecipeNotes(element.text().latin1());
                } else if (element.attribute(attrClass) == classBatch) {
                    recipe_->setBatchNotes(element.text().latin1());
                }
            }
        }
    }

    // get all grains tags
    nodes = root.elementsByTagName(tagGrains);
    QDomNodeList subnodes;
    QDomElement subelement;
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            // get all grain tags
            subnodes = element.elementsByTagName(tagGrain);
            for (m=0; m<subnodes.count(); m++) {
                if (subnodes.item(m).isElement()) {
                    subelement = subnodes.item(m).toElement();
                    recipe_->addGrain(Grain(subelement.text(),
                        Weight(subelement.attribute(attrQuantity), Weight::pound),
                        subelement.attribute(attrExtract).toDouble(),
                        subelement.attribute(attrColor).toDouble(),
                        subelement.attribute(attrUse)));
                }
            }
        }
    }

    // get all hops tags
    nodes = root.elementsByTagName(tagHops);
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            // get all hop tags
            subnodes = element.elementsByTagName(tagHop);
            for (m=0; m<subnodes.count(); m++) {
                if (subnodes.item(m).isElement()) {
                    subelement = subnodes.item(m).toElement();
                    recipe_->addHop(Hops(subelement.text(),
                        Weight(subelement.attribute(attrQuantity), Weight::ounce),
                        subelement.attribute(attrForm),
                        subelement.attribute(attrAlpha).toDouble(),
                        subelement.attribute(attrTime).toUInt()));
                }
            }
        }
    }

    // get all miscingredients tags
    nodes = root.elementsByTagName(tagMiscIngredients);
    for (n=0; n<nodes.count(); ++n) {
        if (nodes.item(n).isElement()) {
            element = nodes.item(n).toElement();
            // get all miscingredient tags
            subnodes = element.elementsByTagName(tagMiscIngredient);
            for (m=0; m<subnodes.count(); m++) {
                if (subnodes.item(m).isElement()) {
                    subelement = subnodes.item(m).toElement();
                    recipe_->addMisc(MiscIngredient(subelement.text(),
                        Quantity(subelement.attribute(attrQuantity),
                                 Quantity::generic),
                        subelement.attribute(attrNotes)));
                }
            }
        }
    }
    // calculate the numbers
    Calc::recalc(recipe_);
    // just loaded recipes are not modified
    setModified(false);
    emit (recipeChanged());
    return true;
}

//////////////////////////////////////////////////////////////////////////////
// saveRecipe()
// ------------
// Save the recipe out to file

bool Model::saveRecipe(const QString &filename)
{
    QDomDocument doc(tagRecipe);

    // create the root element
    QDomElement root = doc.createElement(doc.doctype().name());
    root.setAttribute(attrGenerator, PACKAGE);
    root.setAttribute(attrVersion, VERSION);
    doc.appendChild(root);

    // title
    QDomElement element = doc.createElement(tagTitle);
    element.appendChild(doc.createTextNode(recipe_->title()));
    root.appendChild(element);
    // brewer
    element = doc.createElement(tagBrewer);
    element.appendChild(doc.createTextNode(recipe_->brewer()));
    root.appendChild(element);
    // style
    element = doc.createElement(tagStyle);
    element.appendChild(doc.createTextNode(recipe_->style().name()));
    root.appendChild(element);
    // batch settings
    element = doc.createElement(tagBatch);
    element.setAttribute(attrQuantity, recipe_->size().toQString());
    element.setAttribute(attrMash, recipe_->mashed() ? "true" : "false");
    root.appendChild(element);
    // notes
    if (!recipe_->recipeNotes().isEmpty()) {
        element = doc.createElement(tagNotes);
        element.setAttribute(attrClass, classRecipe);
        element.appendChild(doc.createTextNode(recipe_->recipeNotes()));
        root.appendChild(element);
    }
    if (!recipe_->batchNotes().isEmpty()) {
        element = doc.createElement(tagNotes);
        element.setAttribute(attrClass, classBatch);
        element.appendChild(doc.createTextNode(recipe_->batchNotes()));
        root.appendChild(element);
    }

    // grains elements
    element = doc.createElement(tagGrains);
    GrainIterator git;
    QDomElement subelement;
    // iterate through _grains list
    for (git=recipe_->grains()->begin(); git!=recipe_->grains()->end(); ++git) {
        subelement = doc.createElement(tagGrain);
        subelement.appendChild(doc.createTextNode((*git).name()));
        subelement.setAttribute(attrQuantity, (*git).weight().toQString());
        subelement.setAttribute(attrExtract, (*git).extract());
        subelement.setAttribute(attrColor, (*git).color());
        subelement.setAttribute(attrUse, (*git).useString());
        element.appendChild(subelement);
    }
    root.appendChild(element);

    // hops elements
    element = doc.createElement(tagHops);
    HopIterator hit;
    // iterate through _hops list
    for (hit=recipe_->hops()->begin(); hit!=recipe_->hops()->end(); ++hit) {
        subelement = doc.createElement(tagHop);
        subelement.appendChild(doc.createTextNode((*hit).name()));
        subelement.setAttribute(attrQuantity, (*hit).weight().toQString());
        subelement.setAttribute(attrForm, (*hit).form());
        subelement.setAttribute(attrAlpha, (*hit).alpha());
        subelement.setAttribute(attrTime, (*hit).time());
        element.appendChild(subelement);
    }
    root.appendChild(element);

    // miscingredients elements
    element = doc.createElement(tagMiscIngredients);
    MiscIngredientIterator mit;
    // iterate through _misc list
    for (mit=recipe_->miscs()->begin(); mit!=recipe_->miscs()->end(); ++mit) {
        subelement = doc.createElement(tagMiscIngredient);
        subelement.appendChild(doc.createTextNode((*mit).name()));
        subelement.setAttribute(attrQuantity, (*mit).quantity().toQString());
        subelement.setAttribute(attrNotes, (*mit).notes());
        element.appendChild(subelement);
    }
    root.appendChild(element);

    // open file
    QFile* datafile = new QFile(filename);
    if (!datafile->open(IO_WriteOnly)) {
        // error opening file
        qWarning("Error: Cannot open file " + filename);
        datafile->close();
        return false;
    }

    // write it out
    QTextStream textstream(datafile);
    doc.save(textstream, 0);
    datafile->close();
    delete (datafile);

    // document is saved, so set flags accordingly
    recipe_->setModified(false);
    return true;
}

//////////////////////////////////////////////////////////////////////////////
// recipeText()
// ------------
// Get the ascii text of the recipe, for printing and exporting

QString Model::recipeText()
{
    QString buffer = "\n";

    // title stuff
    buffer += "Recipe: " + recipe_->title() + '\n';
    buffer += "Brewer: " + recipe_->brewer() + '\n';
    buffer += "Style: " +
        recipe_->style().name() + '\n';
    buffer += "Batch: " + recipe_->size().toQString();
    if (recipe_->mashed()) buffer += ", Mashed";
    buffer += "\n\n";

    // style stuff
    buffer += "Recipe Gravity: " +
        QString::number(Calc::OG(recipe_), 'f', 3) + " OG\n";
    buffer += "Recipe Bitterness: " +
        QString::number(Calc::IBU(recipe_), 'f', 0) + "IBU\n";
    buffer += "Recipe Color: " +
        QString::number(Calc::SRM(recipe_), 'f', 0) + CHAR_LATIN_DEGREE + "SRM\n";
    buffer += "Estimated FG: " +
        QString::number(Calc::FGEstimate(recipe_), 'f', 3) + '\n';
    buffer += "Alcohol by Volume: " +
        QString::number(Calc::ABV(recipe_) * 100.0, 'f', 1) + "%\n";
    buffer += "Alcohol by Weight: " +
        QString::number(Calc::ABW(recipe_) * 100.0, 'f', 1) + "%\n\n";

    // grains
    GrainList *grainlist = recipe_->grains();
    GrainList::Iterator itg;
    for (itg=grainlist->begin(); itg != grainlist->end(); ++itg) {
        buffer += (*itg).name().leftJustify(30, ' ');
        buffer += (*itg).weight().toQString() + ", ";
        buffer += (*itg).useString() + '\n';
    }
    buffer += '\n';

    // hops
    HopsList *hopslist = recipe_->hops();
    HopsList::Iterator ith;
    for (ith=hopslist->begin(); ith != hopslist->end(); ++ith) {
        buffer += (*ith).name().leftJustify(30, ' ');
        buffer += (*ith).weight().toQString() + ", ";
        buffer += (*ith).form() + ", ";
        buffer += QString::number((*ith).time()) + " minutes\n";
    }
    buffer += '\n';

    // misc ingredients
    MiscIngredientList *misclist = recipe_->miscs();
    MiscIngredientList::Iterator itm;
    for (itm=misclist->begin(); itm != misclist->end(); ++itm) {
        buffer += (*itm).name().leftJustify(30, ' ');
        buffer += (*itm).quantity().toQString() + ", ";
        buffer += (*itm).notes() + '\n';
    }

    // TODO: long notes don't wrap...
    buffer += "Recipe Notes:\n" + recipe_->recipeNotes() + "\n\n";
    buffer += "Batch Notes:\n" + recipe_->batchNotes() + "\n\n";

    buffer += ID_TITLE + " " + VERSION;
    return buffer;
}

///////////////////////////////////////////////////////////////////////////////
// Miscellaneous                                                             //
///////////////////////////////////////////////////////////////////////////////

void Model::setModified(bool mod)
{
    recipe_->setModified(mod);
    if (mod) emit recipeModified();
}


Generated by  Doxygen 1.6.0   Back to index