Logo Search packages:      
Sourcecode: qbrew version File versions

controller.cpp

/***************************************************************************
  controller.cpp
  -------------------
  Control class for QBrew
  -------------------
  Copyright (c) 2004 David Johnson
  Please see the header file for copyright and license information.
***************************************************************************/

#include <qaction.h>
#include <qapplication.h>
#include <qdir.h>
#include <qfiledialog.h>
#include <qlabel.h>
#include <qmenubar.h>
#include <qmessagebox.h>
#include <qpaintdevicemetrics.h>
#include <qpainter.h>
#include <qprinter.h>
#include <qregexp.h>
#include <qsettings.h>
#include <qstatusbar.h>
#include <qstyle.h>
#include <qstylefactory.h>
#include <qtimer.h>
#include <qtoolbar.h>
#include <qtoolbutton.h>
#include <qwhatsthis.h>

#include "alcoholtool.h"
#include "configure.h"
#include "helpwindow.h"
#include "hydrometertool.h"
#include "images.h"
#include "model.h"
#include "view.h"

#include "controller.h"

using namespace AppResource;

Controller *Controller::instance_ = 0;

const QImage& Controller::findImage(const QString& name)
{
    return qembed_findImage(name);
}

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

//////////////////////////////////////////////////////////////////////////////
// Controller()
// ------------
// Private constructor

Controller::Controller()
    : QMainWindow(0, "QBrew", WType_TopLevel | WDestructiveClose),
      model_(0), view_(0), configure_(0), helpwin_(0), primerwin_(0),
      alcoholtool_(0), hydrometertool_(0), filename_(),
      newflag_(false), filemenu_(0), optionsmenu_(0), toolsmenu_(0),
      helpmenu_(0), toolbar_(0),  recentmenu_(0), autosave_(0), backed_(false)
{ ; }

void Controller::initialize(const QString &filename)
{
    // TODO: the following is a temporary conversion
    convertPreferences();

    restoreState();
    readConfig();

    // setup look and feel
    setIcon(qembed_findImage("qbrew"));
    QIconSet::setIconSize(QIconSet::Large, QSize(22, 22));
    setUsesBigPixmaps(state_.general.largeicons);

    if ((state_.general.lookfeel != QApplication::style().name()) &&
        (QStyleFactory::keys().contains(state_.general.lookfeel))) {
        QApplication::setStyle(QStyleFactory::create(state_.general.lookfeel));
    }

    // initialize frame elements
    initActions();
    initMenuBar();
    initToolBar();
    initStatusBar();

    // create and initialize model
    model_ = Model::instance();
    if (state_.calc.units == UNIT_METRIC) {
        model_->setDefaultSize(Volume(state_.recipe.batch, Volume::liter));
        model_->setDefaultGrainUnit(Weight::kilogram);
        model_->setDefaultHopsUnit(Weight::gram);
    } else if (state_.calc.units == UNIT_US) {
        model_->setDefaultSize(Volume(state_.recipe.batch, Volume::gallon));
        model_->setDefaultGrainUnit(Weight::pound);
        model_->setDefaultHopsUnit(Weight::ounce);
    }
    model_->setDefaultStyle(Style(state_.recipe.style));
    model_->setDefaultMash(state_.recipe.mash);
    model_->setDefaultHopform(state_.recipe.hopform);
    model_->setDefaultMiscUnit(Quantity::generic);
    connect(model_, SIGNAL(recipeModified()), this, SLOT(documentModified()));

    // create and initialize view
    view_ = new View(this, model_);

    // initialize calculations
    Calc::setEfficiency(state_.calc.efficiency);
    Calc::setTinseth(state_.calc.tinseth);
    Calc::setMorey(state_.calc.morey);

    // load or create a recipe
    if (filename.isEmpty() && state_.general.loadlast) {
        filename_ = *state_.general.recentfiles.begin();
    } else {
        filename_ = filename;
    }
    if ((!filename_.isEmpty()) && (filename_ != ID_DEFAULT_FILE)) {
        model_->loadRecipe(filename_);
        setCaption(ID_TITLE + " - " + filename_);
    } else {
        model_->newRecipe();
        newflag_ = true;
        setCaption(ID_TITLE + " " + VERSION);
    }

    // other initialization
    initAutoSave();
    setCentralWidget(view_);
}

//////////////////////////////////////////////////////////////////////////////
// ~Controller()
// -------------
// Private destructor

Controller::~Controller()
{
    saveState();

    if (autosave_) { autosave_->stop(); delete autosave_; }

    if (view_) delete view_;
    if (filemenu_) delete filemenu_;
    if (optionsmenu_) delete optionsmenu_;
    if (toolsmenu_) delete toolsmenu_;
    if (helpmenu_) delete helpmenu_;
    if (toolbar_) delete toolbar_;
}

//////////////////////////////////////////////////////////////////////////////
// instance()
// ----------
// Return pointer to the controller.

Controller *Controller::instance()
{
    if (!instance_)
        instance_ = new Controller();
    return instance_;
}

//////////////////////////////////////////////////////////////////////////////
// initActions()
// -------------
// Initialize the actions

void Controller::initActions()
{
    // file actions
    filenew_ = new QAction("New",
                           QIconSet(qembed_findImage("sm_filenew"),
                                    qembed_findImage("filenew")),
                           "&New", CTRL+Key_N, this);
    fileopen_ = new QAction("Open",
                            QIconSet(qembed_findImage("sm_fileopen"),
                                     qembed_findImage("fileopen")),
                            "&Open...", CTRL+Key_O, this);
    filesave_ = new QAction("Save", 
                            QIconSet(qembed_findImage("sm_filesave"),
                                     qembed_findImage("filesave")),
                            "&Save",  CTRL+Key_S, this);
    filesaveas_ = new QAction("Save As",
                              QIconSet(qembed_findImage("sm_filesaveas"),
                                       qembed_findImage("filesaveas")),
                              "Save &as...", 0, this);
    fileprint_ = new QAction("Print",
                             QIconSet(qembed_findImage("sm_fileprint"),
                                      qembed_findImage("fileprint")),
                              "&Print...", CTRL+Key_P, this);
    filequit_ = new QAction("Quit",
                            QIconSet(qembed_findImage("sm_exit"),
                                     qembed_findImage("exit")),
                            "&Quit", CTRL+Key_Q, this);

    // options actions
    optionstoolbar_ = new QAction("Toolbar",
                                  "&Toolbar", 0, this, 0, true);
    optionsstatusbar_ = new QAction("Statusbar",
                                    "&Statusbar", 0, this, 0, true);
    // TODO: Configure Key Bindings?
    optionssetup_ = new QAction("Configure",
                                    "&Configure...", 0, this);
    optionssavesetup_ = new QAction("Save Configuration",
                                        "Sa&ve Configuration", 0, this);

    // tools actions
    toolalcohol_ = new QAction("Alcohol Percentage",
                               "&Alcohol Percentage...", 0, this);
    toolhydrometer_ = new QAction("Hydrometer Correction",
                                  "&Hydrometer Correction...", 0, this);

    // help actions
    helpcontents_ = new QAction("Help",
                                QIconSet(qembed_findImage("sm_contents"),
                                         qembed_findImage("contents")),
                                "&Contents", Key_F1, this);
    helpprimer_ = new QAction("Primer",
                              QIconSet(qembed_findImage("sm_contents"),
                                       qembed_findImage("contents")),
                                "&Primer", 0, this);
    helpabout_ = new QAction("About", "&About...", 0, this);
    helpcontext_ = new QAction("Context",
                               QIconSet(qembed_findImage("sm_contexthelp"),
                                        qembed_findImage("contexthelp")),
                               "&What's This?", SHIFT+Key_F1, this);

    // create status tips
    filenew_->setStatusTip("Create a new recipe");
    fileopen_->setStatusTip("Open an existing recipe");
    filesave_->setStatusTip("Save the recipe");
    filesaveas_->setStatusTip("Save the recipe under a new name");
    fileprint_->setStatusTip("Print the recipe");
    filequit_->setStatusTip("Quit the application");
    optionstoolbar_->setStatusTip("Enable or disable the Toolbar");
    optionsstatusbar_->setStatusTip("Enable or disable the Statusbar");
    optionssetup_->setStatusTip("Display the configuration dialog");
    optionssavesetup_->setStatusTip("Save the configuration");
    toolhydrometer_->setStatusTip("Hydrometer correction utility");
    toolalcohol_->setStatusTip("Alcohol percentage calculator");
    helpcontents_->setStatusTip("Display the application handbook");
    helpprimer_->setStatusTip("Display the brewing primer");
    helpabout_->setStatusTip("Application information");
    helpcontext_->setStatusTip("Context sensitive help");

    // create what's this help
    filenew_->setWhatsThis("Click this button to create a new recipe.\n\n"
        "You can also select the New command from the File menu.");
    fileopen_->setWhatsThis("Click this button to open a recipe.\n\n"
        "You can also select the Open command from the File menu.");
    filesave_->setWhatsThis("Click this button to save the recipe you "
        "are editing.  You will be prompted for a file name.\n\n"
        "You can also select the Save command from the File menu.");
    fileprint_->setWhatsThis("Click this button to print the recipe.\n\n"
        "You can also select the Print command from the File menu.");

    // create connections
    connect(filenew_, SIGNAL(activated()), this,
            SLOT(fileNew()));
    connect(fileopen_, SIGNAL(activated()), this,
            SLOT(fileOpen()));
    connect(filesave_, SIGNAL(activated()), this,
            SLOT(fileSave()));
    connect(filesaveas_, SIGNAL(activated()), this,
            SLOT(fileSaveAs()));
    connect(fileprint_, SIGNAL(activated()), this,
            SLOT(filePrint()));
    connect(filequit_, SIGNAL(activated()), qApp,
            SLOT(closeAllWindows()));
    connect(optionstoolbar_, SIGNAL(activated()), this,
            SLOT(optionsToolbar()));
    connect(optionsstatusbar_, SIGNAL(activated()), this,
            SLOT(optionsStatusbar()));
    connect(optionssetup_, SIGNAL(activated()), this,
            SLOT(optionsConfigure()));
    connect(optionssavesetup_, SIGNAL(activated()), this,
            SLOT(optionsSaveConfigure()));
    connect(toolhydrometer_, SIGNAL(activated()), this,
            SLOT(toolsHydrometer()));
    connect(toolalcohol_, SIGNAL(activated()), this,
            SLOT(toolsAlcohol()));
    connect(helpcontents_, SIGNAL(activated()), this,
            SLOT(helpContents()));
    connect(helpprimer_, SIGNAL(activated()), this,
            SLOT(helpPrimer()));
    connect(helpabout_, SIGNAL(activated()), this,
            SLOT(helpAbout()));
    connect(helpcontext_, SIGNAL(activated()), this,
            SLOT(whatsThis()));

    // enable/disable appropriate items
    filesave_->setEnabled(false);
}

//////////////////////////////////////////////////////////////////////////////
// initMenuBar()
// -------------
// Initialize the menu bar

void Controller::initMenuBar()
{
    // file menu
    filemenu_ = new QPopupMenu();
    filenew_->addTo(filemenu_);
    fileopen_->addTo(filemenu_);

    // recent file submenu
    recentmenu_ = new QPopupMenu();
    connect(recentmenu_, SIGNAL(aboutToShow()),
            this, SLOT(recentMenuShow()));
    connect(recentmenu_, SIGNAL(activated(int)),
            this, SLOT(fileRecent(int)));
    filemenu_->insertItem(QIconSet(qembed_findImage("sm_fileopen"),
                                   qembed_findImage("fileopen")),
                          "Open &Recent", recentmenu_);

    filemenu_->insertSeparator();
    filesave_->addTo(filemenu_);
    filesaveas_->addTo(filemenu_);
    filemenu_->insertSeparator();
    fileprint_->addTo(filemenu_);
    filemenu_->insertSeparator();
    filequit_->addTo(filemenu_);

    // options menu
    optionsmenu_ = new QPopupMenu();
    optionsmenu_->setCheckable(true);
    optionstoolbar_->addTo(optionsmenu_);
    optionsstatusbar_->addTo(optionsmenu_);
    optionsmenu_->insertSeparator();
    optionssetup_->addTo(optionsmenu_);
    optionsmenu_->insertSeparator();
    optionssavesetup_->addTo(optionsmenu_);

    // tools menu
    toolsmenu_ = new QPopupMenu();
    toolalcohol_->addTo(toolsmenu_);
    toolhydrometer_->addTo(toolsmenu_);

    // help menu
    helpmenu_ = new QPopupMenu();
    helpcontents_->addTo(helpmenu_);
    helpprimer_->addTo(helpmenu_);
    helpcontext_->addTo(helpmenu_);
    helpmenu_->insertSeparator();
    helpabout_->addTo(helpmenu_);

    // insert submenus into main menu
    menuBar()->insertItem("&File", filemenu_);
    menuBar()->insertItem("&Options", optionsmenu_);
    menuBar()->insertItem("&Tools", toolsmenu_);
    menuBar()->insertSeparator();
    menuBar()->insertItem("&Help", helpmenu_);
}

//////////////////////////////////////////////////////////////////////////////
// initToolBar()
// -------------
// Initialize the toolbar

void Controller::initToolBar()
{
    setRightJustification(false);
    toolbar_ = new QToolBar("Main Toolbar", this);

    filenew_->addTo(toolbar_);
    fileopen_->addTo(toolbar_);
    filesave_->addTo(toolbar_);
    fileprint_->addTo(toolbar_);
    toolbar_->addSeparator();
    helpcontext_->addTo(toolbar_);

    // now show or hide toolbar depending on initial setting
    toolbar_->setShown(state_.window.toolbar);
    optionstoolbar_->setOn(state_.window.toolbar);
}

//////////////////////////////////////////////////////////////////////////////
// initStatusBar()
// ---------------
// Initialize the status bar

void Controller::initStatusBar()
{
    statusBar()->message(ID_READY, 2000);
    // now show or hide statusbar depending on initial setting
    statusBar()->setShown(state_.window.statusbar);
    optionsstatusbar_->setOn(state_.window.statusbar);
}

//////////////////////////////////////////////////////////////////////////////
// initAutoSave()
// ---------------
// Initialize the autosave timer

void Controller::initAutoSave()
{
    // destroy existing timer, if any
    if (autosave_) {
        autosave_->stop();
        disconnect(autosave_, SIGNAL(timeout()), this, SLOT(autoSave()));
        delete autosave_; autosave_ = 0;
    }

    if (state_.general.autosave) {
        autosave_ = new QTimer(this, CONF_GEN_AUTOSAVE);
        connect(autosave_, SIGNAL(timeout()), this, SLOT(autoSave()));
        autosave_->start(state_.general.saveinterval * 60000, false);
    }
}

//////////////////////////////////////////////////////////////////////////////
// File Menu Implementation                                                 //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// fileNew()
// ---------
// Create a new recipe

void Controller::fileNew()
{
    if (model_->modified()) {
        // file needs to be saved, what do we do
        switch (querySave()) {
          case QMessageBox::Yes: // yes, save the file
              fileSave();
              break;
          case QMessageBox::No: // no, don't save the file
              break;
          case QMessageBox::Cancel: // cancel creating new file
              statusBar()->message("Canceled...", 2000);
              // exit function
              return;
        }
    }

    // create a new file
    statusBar()->message("Creating new recipe...");
    model_->newRecipe();
    newflag_ = true;
    backed_ = false;

    filesave_->setEnabled(false);

    filename_ = ID_DEFAULT_FILE;
    setCaption(ID_TITLE + " - " + filename_);
    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// fileOpen()
// ----------
// Open a recipe

void Controller::fileOpen()
{
    if (model_->modified()) {
        // file needs to be saved, what do we do
        switch (querySave()) {
            case QMessageBox::Yes:
                fileSave();
                break;
            case QMessageBox::No: // no, don't save the file
                break;
            case QMessageBox::Cancel: //
                statusBar()->message("Canceled...", 2000);
                return;
        }
    }

    // open the file
    statusBar()->message("Opening recipe...");
    QString fname = QFileDialog::getOpenFileName(0, ID_FILE_FILTER, this);

    if (openFile(fname)) {
        statusBar()->message("Loaded recipe", 2000);
    } else {
        statusBar()->message("Load failed", 2000);
    }
}

//////////////////////////////////////////////////////////////////////////////
// fileRecent()
// -----------
// Selection has been made from recent file menu

void Controller::fileRecent(int item)
{
    if (item < 0) return;

    if (model_->modified()) {
        // file needs to be saved, what do we do
        switch (querySave()) {
          case QMessageBox::Yes:
              fileSave();
              break;
          case QMessageBox::No:
              break;
          case QMessageBox::Cancel:
              statusBar()->message("Canceled...", 2000);
              return;
        }
    }

    // open the file
    QString fname = recentmenu_->text(item);
    if (openFile(fname)) {
        statusBar()->message("Loaded recipe", 2000);
        addRecent(fname);
    }
}

//////////////////////////////////////////////////////////////////////////////
// fileSave()
// ----------
// Save a recipe

void Controller::fileSave()
{
    if (newflag_) {
        fileSaveAs();
    } else {
        // file exists so save it
        statusBar()->message("Saving recipe...");
        // TODO: backupFile() returns a bool, so use it...
        if (state_.general.autobackup) backupFile();
        if (model_->saveRecipe(filename_)) {
            // successful in saving file
            newflag_ = false;
            filesave_->setEnabled(false);
            statusBar()->message(ID_READY, 2000);
        } else {
            // error in saving file
            QMessageBox::warning(this, ID_TITLE, ID_TITLE +
                                 " was unable to save the recipe " +
                                 filename_);
            statusBar()->message("Error in saving recipe", 2000);
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
// fileSaveAs()
// ------------
// Save a recipe under a new name

void Controller::fileSaveAs()
{
    statusBar()->message("Saving recipe under new filename...");
    QString fname = QFileDialog::getSaveFileName(0, ID_FILE_FILTER, this);
    if (!fname.isEmpty()) { // we got a valid filename
        QFileInfo finfo(fname);
        if (state_.general.autobackup) backupFile();
        // append .qbrew if there's no suffix
        if (finfo.extension(false).isEmpty()) fname += "." + ID_FILE_EXT;
        if (finfo.exists()) {
            // overwrite?
            switch (queryOverwrite(fname)) {
              case QMessageBox::Yes:
                  break;
              case QMessageBox::Cancel:
                  statusBar()->message("Canceled...", 2000);
                  return;
            }
        }

        if (model_->saveRecipe(fname)) {
            // successfully saved
            newflag_ = false;
            filesave_->setEnabled(false);
            setCaption(ID_TITLE + " - " + finfo.fileName());
            statusBar()->message(ID_READY, 2000);
            // save name of file
            filename_ = fname;
        } else {
            // error in saving
            QMessageBox::warning(this, ID_TITLE, ID_TITLE +
                                 " was unable to save the recipe " +
                                 finfo.fileName());
            statusBar()->message("Error in saving recipe", 2000);
        }
    } else {
        // no file name chosen
        statusBar()->message("Saving aborted", 2000);
    }
}

//////////////////////////////////////////////////////////////////////////////
// filePrint()
// -----------
// Print the recipe. Much of this method derived from Qt example programs,
// including "helpviewer" copyright 1992-2000 Troll Tech AS.

void Controller::filePrint()
{
    // TODO: make printer a member, so settings will be saved
    // TODO: eventually will have an export function to export to html. At that time
    // switch this to print html/richtext.
    statusBar()->message("Printing...");

    QPrinter* printer = new QPrinter();
    printer->setFullPage(true);

    if (printer->setup())
    {
        QPainter painter(printer);
        QPaintDeviceMetrics metrics(painter.device());

        QString line;
        int tpos;
        int ypos = 12;              // y position for each line
        const int margin = 18;      // eighteen point margin

        // painter.begin(printer);
        QFontMetrics fm = painter.fontMetrics();
        QFont font("times", 10);    // serif font best for printouts
        font.setPointSize(10);
        painter.setFont(font);      // use fixed width font
        QString text = model_->recipeText();
        int pageno = 1;             // keep track of pages
        // for each line of text...
        while (text.length() > 0) {
            // get line of text
            tpos = text.find('\n');
            if (tpos > 0) {
                line = text.left(tpos);
                text.remove(0, tpos+1);
            } else {
                // get last line if text doesn't end in newline
                line = text; text = "";
            }
            // is there space for this line on page?
            if ((margin + ypos) > (metrics.height() - margin)) {
                statusBar()->message("Printing (page " +
                                     QString::number(++pageno) + ")...");
                printer->newPage();         // no more room on this page
                ypos = 12;                  // back to top of page
            }
            // print the line
            painter.drawText(margin, margin + ypos, metrics.width(),
                             fm.lineSpacing(), ExpandTabs | DontClip, line );
            // update paper position
            ypos += fm.lineSpacing();
        }
        painter.end();        // send job to printer
        statusBar()->message(ID_READY, 2000);
    } else {
        // user chose not to print
        statusBar()->message("Printing cancelled", 2000);
    }
    delete printer;
}

//////////////////////////////////////////////////////////////////////////////
// Options Menu Implementation                                              //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// optionsToolbar()
// ----------------
// Toggle toolbar status

void Controller::optionsToolbar()
{
    bool show = !toolbar_->isVisible();
    optionstoolbar_->setOn(show);
    toolbar_->setShown(show);
    state_.window.toolbar = show;
    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// optionsSatusbar()
// -----------------
// Toggle statusbar status

void Controller::optionsStatusbar()
{
    bool show = !statusBar()->isVisible();
    optionsstatusbar_->setOn(show);
    statusBar()->setShown(show);
    state_.window.statusbar = show;
    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// optionsConfigure()
// ------------------
// Display the setup dialog

void Controller::optionsConfigure()
{
    statusBar()->message("Configuring " + ID_TITLE + "...", 2000);

    if (!configure_) {
        configure_ = new Configure(this, "configure");

        connect(configure_, SIGNAL(generalApply(const GenConfigState&)),
                this, SLOT(applyGeneralState(const GenConfigState&)));
        connect(configure_, SIGNAL(recipeApply(const RecipeConfigState&)),
                this, SLOT(applyRecipeState(const RecipeConfigState&)));
        connect(configure_, SIGNAL(calcApply(const CalcConfigState&)),
                this, SLOT(applyCalcState(const CalcConfigState&)));
    }

    if (!configure_->isVisible()) {
        configure_->setState(state_);
    }
    configure_->show(); // non-modal
    configure_->raise();
    if (configure_->isMinimized()) configure_->showNormal();

}

//////////////////////////////////////////////////////////////////////////////
// optionsSaveConfigure()
// ----------------------
// Received to save the application settings

void Controller::optionsSaveConfigure()
{
    statusBar()->message("Saving configuration...");

    // TODO: save toolbar positions

    writeConfig();
    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// Tools Menu Implementation                                                //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// toolsAlcohol()
// --------------
// A utility dialog for hydrometer correction

void Controller::toolsAlcohol()
{
    if (!alcoholtool_) alcoholtool_
        = new AlcoholTool(this, "alcoholtool");
    alcoholtool_->show();
    alcoholtool_->raise();
    if (alcoholtool_->isMinimized()) alcoholtool_->showNormal();

    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// toolsHydrometer()
// -----------------
// A utility dialog for hydrometer correction

void Controller::toolsHydrometer()
{
    if (!hydrometertool_) hydrometertool_
        = new HydrometerTool(this, "hydrometertool");
    hydrometertool_->show();
    hydrometertool_->raise();
    if (hydrometertool_->isMinimized()) hydrometertool_->showNormal();

    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// Help Menu Implementation                                                 //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// helpContents()
// --------------
// Display the application manual

void Controller::helpContents()
{
    QString home = QDir(docBase() + ID_HELP_FILE).absPath();

    if (!helpwin_) helpwin_ = new HelpWindow(home, 0, "help");
    helpwin_->show();
    helpwin_->raise();
    if (helpwin_->isMinimized()) helpwin_->showNormal();

    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// helpPrimer()
// ------------
// Display the brewing primer

void Controller::helpPrimer()
{
    QString home = QDir(docBase() + ID_PRIMER_FILE).absPath();

    if (!primerwin_) primerwin_ = new HelpWindow(home, 0, "primer");
    primerwin_->show();
    primerwin_->raise();
    if (primerwin_->isMinimized()) primerwin_->showNormal();

    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// helpAbout()
// -----------
// Display the About dialog

void Controller::helpAbout()
{
    QString message;

    message = "<center><nobr><big><strong>" + ID_TITLE
        + " Version " + VERSION + "</strong></big></nobr></center>";
    message += "<p align=center><strong>" + ID_DESCRIPTION + "</strong>";
    message += "<p align=center>" + ID_COPYRIGHT + ' ' + ID_AUTHOR;
    message += "<p align=center>Contributions by " + ID_CONTRIBUTORS;

    QMessageBox::about(this, "About " + ID_TITLE, message);
}

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

//////////////////////////////////////////////////////////////////////////////
// openFile()
// ----------
// Open the named file

bool Controller::openFile(const QString &filename)
{
    if (!filename.isEmpty()) {
        QFileInfo finfo(filename);
        if (model_->loadRecipe(filename)) {
            // load was successful
            filename_ = filename;
            newflag_ = false;
            backed_ = false;
            addRecent(filename);
            filesave_->setEnabled(false);

            setCaption(ID_TITLE + " - " + finfo.fileName());
            return true;
        } else {
            // load was unsuccessful
            QMessageBox::warning(this, ID_TITLE, ID_TITLE +
                                 " was unable to load the recipe " +
                                 finfo.fileName());
            return false;
        }
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////////
// backupFile()
// ------------
// Backup the current file

bool Controller::backupFile()
{
    if (backed_) return true;

    bool status = false;
    if ((!filename_.isEmpty()) && (filename_ != ID_DEFAULT_FILE)) {
        QFile source(filename_);
        QFile dest(filename_ + '~');

        // copy the file
        if (source.open(IO_ReadOnly)) {
            if (dest.open(IO_WriteOnly)) {
                char *buffer = new char[2048];
                int result = 0;
                while ((result >= 0) && (!source.atEnd())) {
                    result = source.readBlock(buffer, 2048);
                    if (result >= 0) dest.writeBlock(buffer, result);
                }
                status = result >= 0;
            }
            dest.close();
            source.close();
        }

        if (status) {
            backed_ = true;
        } else {
            QMessageBox::information(this, ID_TITLE + " - Backup",
                                     "Unable to make a backup of the "
                                     "original file");
        }
    }
    return status;
}

//////////////////////////////////////////////////////////////////////////////
// recentMenuShow()
// ----------------
// Initialize the recent file menu

void Controller::recentMenuShow()
{
    recentmenu_->clear();
    int item = 0;
    QStringList::ConstIterator it;
    for (it = state_.general.recentfiles.begin();
         it!=state_.general.recentfiles.end(); ++it) {
      recentmenu_->insertItem(*it, item++);
    }
}

//////////////////////////////////////////////////////////////////////////////
// addRecent()
// -----------
// Add a file to the recent files list

void Controller::addRecent(const QString &filename)
{
    if (state_.general.recentnum == 0) return;

    QFileInfo finfo(filename);
    finfo.convertToAbs();
    QString filepath = finfo.filePath();
    if (state_.general.recentfiles.find(filepath)
        != state_.general.recentfiles.end())
      state_.general.recentfiles.remove(filepath);
    if (state_.general.recentfiles.count() >= state_.general.recentnum)
      state_.general.recentfiles.pop_back();
    state_.general.recentfiles.prepend(filepath);
}

//////////////////////////////////////////////////////////////////////////////
// applyGeneralState()
// -------------------
// Set the general configuration state

void Controller::applyGeneralState(const GenConfigState &state)
{
    GenConfigState oldstate = state_.general;
    state_.general = state;

    // apply state changes
    if (state.lookfeel != oldstate.lookfeel) {
        QApplication::setStyle(QStyleFactory::create(state.lookfeel));
    }
    if (state.largeicons != oldstate.largeicons) {
        setUsesBigPixmaps(state.largeicons);
    }
    if ((state.autosave != oldstate.autosave) ||
        (state.saveinterval != oldstate.saveinterval)) {
        initAutoSave();
    }
    if (state.recentnum < oldstate.recentnum) {
        while (state_.general.recentfiles.count() > state.recentnum) {
            state_.general.recentfiles.pop_back();
        }
    }
    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// applyRecipeState()
// ------------------
// Set the recipe configuration state

void Controller::applyRecipeState(const RecipeConfigState &state)
{
    RecipeConfigState oldstate = state_.recipe;
    state_.recipe = state;

    // apply state changes
    if (state.batch != oldstate.batch) {
        if (state_.calc.units == UNIT_METRIC) {
            model_->setDefaultSize(Volume(state.batch, Volume::liter));
        } else if (state_.calc.units == UNIT_US) {
            model_->setDefaultSize(Volume(state.batch, Volume::gallon));
        }
    }
    if (state.style != oldstate.style) {
        model_->setDefaultStyle(Style(state.style));
    }
    if (state.hopform != oldstate.hopform) {
        model_->setDefaultHopform(state.hopform);
    }
    if (state.mash != oldstate.mash) {
        model_->setDefaultMash(state.mash);
    }

    // recalculate
    model_->recalc();
    view_->flushView();

    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// applyCalcState()
// ----------------
// Set the calc configuration state

void Controller::applyCalcState(const CalcConfigState &state)
{
    CalcConfigState oldstate = state_.calc;
    state_.calc = state;

    // apply state changes
    if (state.efficiency != oldstate.efficiency) {
        Calc::setEfficiency(state.efficiency);
    }
    if (state.morey != oldstate.morey) {
        Calc::setMorey(state.morey);
    }
    if (state.tinseth != oldstate.tinseth) {
        Calc::setTinseth(state.tinseth);
    }
    if (state.units != oldstate.units) {
        if (state.units == UNIT_METRIC) {
            model_->setDefaultSize(Volume(state_.recipe.batch,
                                          Volume::liter));
            model_->setDefaultGrainUnit(Weight::kilogram);
            model_->setDefaultHopsUnit(Weight::gram);
        } else if (state.units == UNIT_US) {
            model_->setDefaultSize(Volume(state_.recipe.batch,
                                          Volume::gallon));
            model_->setDefaultGrainUnit(Weight::pound);
            model_->setDefaultHopsUnit(Weight::ounce);
        }
    }

    // recalculate
    model_->recalc();
    view_->flushView();

    statusBar()->message(ID_READY, 2000);
}

//////////////////////////////////////////////////////////////////////////////
// autoSave()
// ----------
// Time to autosave

void Controller::autoSave()
{
    if (!model_->modified()) return;
    if ((filename_.isEmpty()) || (filename_ == ID_DEFAULT_FILE)) {
        model_->saveRecipe(QDir::currentDirPath()
                           + QString("/autosave.%1").arg(ID_FILE_EXT));
    } else {
        model_->saveRecipe(filename_);
    }
}

//////////////////////////////////////////////////////////////////////////////
// documentModified()
// ----------------------
// Received when document is modified

void Controller::documentModified()
{
    filesave_->setEnabled(true);
    QFileInfo finfo(filename_);
    setCaption(ID_TITLE + " - " + finfo.fileName() + " [modified]");
    // TODO: anything else, like updating view?
}

//////////////////////////////////////////////////////////////////////////////
// querySave()
// -----------
// Ask the user if they want to save their work before going on

int Controller::querySave()
{
    return QMessageBox::information(this, ID_TITLE + " - Save?",
                                    "<p>Do you wish to save your work first?",
                                    QMessageBox::Yes,
                                    QMessageBox::No,
                                    QMessageBox::Cancel);
}

//////////////////////////////////////////////////////////////////////////////
// queryOverwrite()
// -----------
// Ask the user if they want to overwrite an existing file

// TODO: ain't using this yet...
int Controller::queryOverwrite(const QString filename)
{
    return QMessageBox::information(this, ID_TITLE + " - Overwrite?",
                                    QString("<p>Are you sure you want to "
                                            "overwrite the file \"%1\"")
                                    .arg(filename),
                                    QMessageBox::Yes,
                                    QMessageBox::Cancel);
}

//////////////////////////////////////////////////////////////////////////////
// readConfig()
// ------------
// Read configuration from settings file

void Controller::readConfig()
{
    QSettings config;
    config.setPath("usermode.org", "QBrew");
    config.beginGroup('/' + ID_TITLE);

    // read general config
    config.beginGroup(CONFGROUP_GENERAL);
    state_.general.appdir  =
        config.readEntry(CONF_GEN_APPDIR, state_.general.appdir);
    state_.general.lookfeel =
        config.readEntry(CONF_GEN_LOOK_FEEL, state_.general.lookfeel);
    state_.general.largeicons =
        config.readBoolEntry(CONF_GEN_LARGE_ICONS, state_.general.largeicons);
    state_.general.autosave =
        config.readBoolEntry(CONF_GEN_AUTOSAVE, state_.general.autosave);
    state_.general.saveinterval =
        config.readNumEntry(CONF_GEN_SAVEINTERVAL, state_.general.saveinterval);
    state_.general.autobackup =
        config.readBoolEntry(CONF_GEN_AUTOBACKUP, state_.general.autobackup);
    state_.general.loadlast =
        config.readBoolEntry(CONF_GEN_LOADLAST, state_.general.autobackup);
    state_.general.recentnum =
        config.readNumEntry(CONF_GEN_RECENTNUM, state_.general.recentnum);
    config.endGroup();

    // read recipe config
    config.beginGroup(CONFGROUP_RECIPE);
    state_.recipe.batch =
        config.readDoubleEntry(CONF_RECIPE_BATCH, state_.recipe.batch);
    state_.recipe.style =
        config.readEntry(CONF_RECIPE_STYLE, state_.recipe.style);
    state_.recipe.hopform =
        config.readEntry(CONF_RECIPE_HOPFORM, state_.recipe.hopform);
    state_.recipe.mash =
        config.readBoolEntry(CONF_RECIPE_MASH, state_.recipe.mash);
    config.endGroup();

    // read calc config
    config.beginGroup(CONFGROUP_CALC);
    state_.calc.efficiency =
        config.readDoubleEntry(CONF_CALC_EFFICIENCY, state_.calc.efficiency);
    state_.calc.morey =
        config.readBoolEntry(CONF_CALC_MOREY, state_.calc.morey);
    state_.calc.tinseth =
        config.readBoolEntry(CONF_CALC_TINSETH, state_.calc.tinseth);
    state_.calc.units =
        config.readEntry(CONF_CALC_UNITS, state_.calc.units);
    config.endGroup();

    config.endGroup();
}

//////////////////////////////////////////////////////////////////////////////
// writeConfig()
// -------------
// Write configuration to settings file

void Controller::writeConfig()
{
    QSettings config;
    config.setPath("usermode.org", "QBrew");
    config.beginGroup('/' + ID_TITLE);

    // write general config
    config.beginGroup(CONFGROUP_GENERAL);
    config.writeEntry(CONF_GEN_APPDIR, state_.general.appdir);
    config.writeEntry(CONF_GEN_LOOK_FEEL, state_.general.lookfeel);
    config.writeEntry(CONF_GEN_LARGE_ICONS, state_.general.largeicons);
    config.writeEntry(CONF_GEN_AUTOSAVE, state_.general.autosave);
    config.writeEntry(CONF_GEN_SAVEINTERVAL, (int)state_.general.saveinterval);
    config.writeEntry(CONF_GEN_AUTOBACKUP, state_.general.autobackup);
    config.writeEntry(CONF_GEN_LOADLAST, state_.general.loadlast);
    config.writeEntry(CONF_GEN_RECENTNUM, (int)state_.general.recentnum);
    config.endGroup();

    // write recipe config
    config.beginGroup(CONFGROUP_RECIPE);
    config.writeEntry(CONF_RECIPE_BATCH, (double)state_.recipe.batch);
    config.writeEntry(CONF_RECIPE_STYLE, state_.recipe.style);
    config.writeEntry(CONF_RECIPE_HOPFORM, state_.recipe.hopform);
    config.writeEntry(CONF_RECIPE_MASH, state_.recipe.mash);
    config.endGroup();

    // write calc config
    config.beginGroup(CONFGROUP_CALC);
    config.writeEntry(CONF_CALC_EFFICIENCY, (double)state_.calc.efficiency);
    config.writeEntry(CONF_CALC_MOREY, state_.calc.morey);
    config.writeEntry(CONF_CALC_TINSETH, state_.calc.tinseth);
    config.writeEntry(CONF_CALC_UNITS, state_.calc.units);
    config.endGroup();

    config.endGroup();
}

//////////////////////////////////////////////////////////////////////////////
// restoreState()
// ------------
// Restore application state

void Controller::restoreState()
{
    QSettings config;
    config.setPath("usermode.org", "QBrew");
    config.beginGroup('/' + ID_TITLE);

    // read window state
    config.beginGroup(CONFGROUP_WINDOW);
    state_.window.toolbar = config.readBoolEntry(CONF_WIN_TOOLBAR,
                                                 state_.window.toolbar);
    state_.window.statusbar = config.readBoolEntry(CONF_WIN_STATUSBAR,
                                                   state_.window.statusbar);

    QString mainwindow = config.readEntry(CONF_WIN_MAINWINDOW);
    QTextStream stream(&mainwindow, IO_ReadOnly);
    stream >> *this; // dockwindow layout // TODO: doesn't work right...
    config.endGroup();

    // read general state
    config.beginGroup(CONFGROUP_GENERAL);
    state_.general.recentfiles = config.readListEntry(CONF_GEN_RECENTFILES, ',');
    config.endGroup();

    config.endGroup();
}

//////////////////////////////////////////////////////////////////////////////
// saveSate()
// ----------
// Save application state

void Controller::saveState()
{
    // load config from settings
    QSettings config;
    config.setPath("usermode.org", "QBrew");
    config.beginGroup('/' + ID_TITLE);

    // write window state
    config.beginGroup(CONFGROUP_WINDOW);
    config.writeEntry(CONF_WIN_TOOLBAR, state_.window.toolbar);
    config.writeEntry(CONF_WIN_STATUSBAR, state_.window.statusbar);

    QString mainwindow;
    QTextStream stream(&mainwindow, IO_WriteOnly);
    stream << *this; // dockwindow layout
    config.writeEntry(CONF_WIN_MAINWINDOW, mainwindow);

    config.endGroup();

    // write general state
    config.beginGroup(CONFGROUP_GENERAL);
    config.writeEntry(CONF_GEN_RECENTFILES, state_.general.recentfiles, ',');
    config.endGroup();

    config.endGroup();
}

//////////////////////////////////////////////////////////////////////////////
// closeEvent()
// -----------
// Catch the close event

void Controller::closeEvent(QCloseEvent* e)
{
    if (!model_->modified()) {
        e->accept();
    } else {
        // file needs to be saved, what do we do
        switch (querySave()) {
          case QMessageBox::Yes:
              fileSave();
              e->accept();
              break;
          case QMessageBox::Cancel:
              statusBar()->message(ID_READY, 2000);
              e->ignore();
              break;
          case QMessageBox::No:
          default:
              e->accept();
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
// dataBase()
// ---------
// Figure out the base directory for the data

QString Controller::dataBase()
{
    QString base = state_.general.appdir;
    if (base.isEmpty()) {
        base = qApp->applicationDirPath();
    } else {
        base = QDir(base).absPath();
    }

#if defined(Q_WS_X11)
    if (QRegExp("qbrew/?$", false).match(base) != -1) {
        // we have our own application directory (it ends in 'qbrew')
        base += "/";
    } else if (QRegExp("bin/?$", false).match(base) != -1) {
        // we are in the bin dir of a filesystem hiearchy like '/usr/local/bin'
        base += "/../share/qbrew/";
    } else {
        // we are in a filesystem hierarchy like '/usr/local'
        base += "/share/qbrew/";
    }

#elif defined(Q_WS_MACX)
    if (QRegExp("Contents/MacOS/?$", false).match(base) != -1) {
        // we're pointing inside an application bundle
        base += "/../Resources/";
    } else {
        // otherwise punt
        base += "/";
    }

#else // Win32 and others
    base += "/";
#endif
    return base;
}

//////////////////////////////////////////////////////////////////////////////
// docBase()
// ---------
// Figure out the base directory for the documentation

QString Controller::docBase()
{
    QString base = state_.general.appdir;
    if (base.isEmpty()) {
        base = qApp->applicationDirPath();
    } else {
        base = QDir(base).absPath();
    }

#if defined(Q_WS_X11)
    if (QRegExp("qbrew/?$", false).match(base) != -1) {
        // we have our own application directory (it ends in 'qbrew')
        base += "/doc/en/";
    } else if (QRegExp("bin/?$", false).match(base) != -1) {
        // we are in the bin dir of a filesystem hiearchy like '/usr/local/bin'
        base += "/../share/doc/qbrew/en/";
    } else {
        // we are in a hierarchy like '/usr/local'
        base += "/share/doc/qbrew/en/";
    }

#elif defined(Q_WS_MACX)
    if (QRegExp("Contents/MacOS/?$", false).match(base) != -1) {
        // we're pointing inside an application bundle
        base += "/../Resources/en.lproj/";
    } else if (QFile::exists(base + "/en.lproj/")) {
        // some other hierarchy using Mac style l10n
        base += "/en.lproj/"
    } else {
        // otherwise punt
        base += "/doc/en";
    }

#else // Win32 and others
    base += "/doc/en/";
#endif
    return base;
}

//////////////////////////////////////////////////////////////////////////////
// TODO: the following is a temporary conversion
// It needs to remain for at least one year after the 0.3.4 release
// Get rid of QDIR_HOME and QPREFIX at that time as well

#include "preferences.h"

void Controller::convertPreferences()
{
    if (QFile::exists(QDIR_HOME + "/" + ID_PREFERENCES_FILE)) {
        QString prefname(QDIR_HOME + "/" + ID_PREFERENCES_FILE);
        int status = QMessageBox::question(this, ID_TITLE + " - Old format",
            "<strong>" + prefname + "</strong>"
            "<p>An old configuration file was found. Do you wish to convert "
            "this to the new configuration format? This will remove the file.",
            QMessageBox::Yes | QMessageBox::Default,
            QMessageBox::No | QMessageBox::Escape);
        if (status == QMessageBox::Yes) {
            // convert it
            Preferences *prefs = new Preferences(prefname, PACKAGE, VERSION);
            QSettings config;
            config.setPath("usermode.org", "QBrew");
            config.beginGroup('/' + ID_TITLE);

            // convert general config
            config.beginGroup(CONFGROUP_GENERAL);
            config.writeEntry(CONF_GEN_APPDIR,
                              prefs->getString(ID_PREF_APPDIR,
                                               ID_PREF_APPDIR_DEFAULT));
            config.writeEntry(CONF_GEN_LOOK_FEEL,
                              prefs->getString(ID_PREF_WIDGET_STYLE,
                                               ID_PREF_WIDGET_STYLE_DEFAULT));
            config.writeEntry(CONF_GEN_LARGE_ICONS,
                              prefs->getBool(ID_PREF_LARGE_ICONS,
                                             ID_PREF_LARGE_ICONS_DEFAULT));
            config.endGroup();

            // convert recipe config
            config.beginGroup(CONFGROUP_RECIPE);
            config.writeEntry(CONF_RECIPE_BATCH,
                              prefs->getDouble(ID_PREF_BATCH,
                                               ID_PREF_BATCH_DEFAULT));
            config.writeEntry(CONF_RECIPE_STYLE,
                              prefs->getString(ID_PREF_RECIPE_STYLE,
                                               ID_PREF_RECIPE_STYLE_DEFAULT));
            config.endGroup();

            // convert calc config
            config.beginGroup(CONFGROUP_CALC);
            config.writeEntry(CONF_CALC_EFFICIENCY,
                              prefs->getDouble(ID_PREF_EFFICIENCY,
                                               ID_PREF_EFFICIENCY_DEFAULT));
            config.writeEntry(CONF_CALC_TINSETH,
                              prefs->getBool(ID_PREF_TINSETH,
                                             ID_PREF_TINSETH_DEFAULT));
            config.writeEntry(CONF_CALC_UNITS,
                              prefs->getString(ID_PREF_UNITS,
                                               ID_PREF_UNITS_DEFAULT));
            config.endGroup();
            config.endGroup();
            delete prefs;

            // now remove the old file
            QFile::remove(prefname);
            if (QFile::exists(prefname)) {
                // couldn't remove it!
                QMessageBox::warning(this, ID_TITLE + " - Warning",
                                     "Could not remove " + prefname);
            }
        }
    }
}

Generated by  Doxygen 1.6.0   Back to index