Chris@225: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@225: 
Chris@225: /*
Chris@225:     Sonic Visualiser
Chris@225:     An audio file viewer and annotation editor.
Chris@225:     Centre for Digital Music, Queen Mary, University of London.
Chris@225:     This file copyright 2007 QMUL.
Chris@225:     
Chris@225:     This program is free software; you can redistribute it and/or
Chris@225:     modify it under the terms of the GNU General Public License as
Chris@225:     published by the Free Software Foundation; either version 2 of the
Chris@225:     License, or (at your option) any later version.  See the file
Chris@225:     COPYING included with this distribution for more information.
Chris@225: */
Chris@225: 
Chris@225: #include "TipDialog.h"
Chris@225: 
Chris@225: #include <QLabel>
Chris@225: #include <QPushButton>
Chris@225: #include <QSettings>
Chris@225: #include <QLabel>
Chris@225: #include <QLocale>
Chris@225: #include <QXmlInputSource>
Chris@225: #include <QFileInfo>
Chris@225: #include <QGridLayout>
Chris@225: #include <QGroupBox>
Chris@228: #include <QCheckBox>
Chris@225: 
Chris@225: #include <iostream>
Chris@225: 
Chris@616: TipDialog::TipDialog(QWidget *parent) :
Chris@616:     QDialog(parent),
Chris@225:     m_tipNumber(0),
Chris@225:     m_label(0),
Chris@225:     m_caption(tr("Tip of the Day"))
Chris@225: {
Chris@225:     readTips();
Chris@225: 
Chris@225:     QSettings settings;
Chris@225:     settings.beginGroup("TipOfTheDay");
Chris@225: 
Chris@225:     if (!settings.value("showonstartup", true).toBool()) return;
Chris@225: 
Chris@225:     m_tipNumber = settings.value("nexttip", 0).toInt();
Chris@225: 
Chris@225:     setWindowTitle(m_caption);
Chris@225: 
Chris@225:     QGridLayout *grid = new QGridLayout;
Chris@225:     setLayout(grid);
Chris@225:     
Chris@225:     QGroupBox *groupBox = new QGroupBox;
Chris@228: //    groupBox->setTitle(m_caption);
Chris@225:     grid->addWidget(groupBox, 0, 0);
Chris@225:     
Chris@225:     QGridLayout *subgrid = new QGridLayout;
Chris@225:     groupBox->setLayout(subgrid);
Chris@225: 
Chris@225:     m_label = new QLabel;
Chris@225:     subgrid->addWidget(m_label, 0, 0);
Chris@228:     m_label->setWordWrap(true);
Chris@225: 
Chris@225:     QHBoxLayout *hbox = new QHBoxLayout;
Chris@225:     grid->addLayout(hbox, 1, 0);
Chris@228: 
Chris@229:     QCheckBox *show = new QCheckBox(tr("Show tip on startup"));
Chris@228:     hbox->addWidget(show);
Chris@228: 
Chris@229:     hbox->addSpacing(20);
Chris@228:     hbox->addStretch(10);
Chris@228:     
Chris@228:     QPushButton *prev = new QPushButton(tr("<< Previous"));
Chris@228:     hbox->addWidget(prev);
Chris@228:     connect(prev, SIGNAL(clicked()), this, SLOT(previous()));
Chris@228:     
Chris@228:     QPushButton *next = new QPushButton(tr("Next >>"));
Chris@228:     hbox->addWidget(next);
Chris@228:     connect(next, SIGNAL(clicked()), this, SLOT(next()));
Chris@225:     
Chris@225:     QPushButton *close = new QPushButton(tr("Close"));
Chris@225:     hbox->addWidget(close);
Chris@225:     connect(close, SIGNAL(clicked()), this, SLOT(accept()));
Chris@225:     
Chris@228:     close->setDefault(true);
Chris@228: 
Chris@225:     showTip();
Chris@225: }
Chris@225: 
Chris@225: TipDialog::~TipDialog()
Chris@225: {
Chris@225: }
Chris@225: 
Chris@225: void
Chris@225: TipDialog::next()
Chris@225: {
Chris@225:     if (++m_tipNumber >= int(m_tips.size())) {
Chris@225:         //!!! The tips file should define where we loop back to -- the
Chris@225:         // first one at least is likely to be a generic welcome message
Chris@225:         m_tipNumber = 0;
Chris@225:     }
Chris@225: 
Chris@225:     showTip();
Chris@225: }
Chris@225: 
Chris@225: void
Chris@225: TipDialog::previous()
Chris@225: {
Chris@225:     if (--m_tipNumber < 0) {
Chris@908:         m_tipNumber = int(m_tips.size()) - 1;
Chris@225:     }
Chris@225: 
Chris@225:     showTip();
Chris@225: }
Chris@225: 
Chris@225: void
Chris@225: TipDialog::readTips()
Chris@225: {
Chris@587:     SVDEBUG << "TipDialog::readTips" << endl;
Chris@225: 
Chris@225:     QString language = QLocale::system().name();
Chris@225:     QString filename = QString(":i18n/tips_%1.xml").arg(language);
Chris@225: 
Chris@225:     if (!QFileInfo(filename).exists()) {
Chris@225: 
Chris@225:         QString base = language.section('_', 0, 0);
Chris@225:         filename = QString(":i18n/tips_%1.xml").arg(base);
Chris@225: 
Chris@225:         if (!QFileInfo(filename).exists()) {
Chris@225: 
Chris@225:             filename = QString(":i18n/tips.xml");
Chris@225: 
Chris@225:             if (!QFileInfo(filename).exists()) return;
Chris@225:         }
Chris@225:     }
Chris@225: 
Chris@225:     QFile file(filename);
Chris@225: 
Chris@587:     SVDEBUG << "TipDialog::readTips from " << filename << endl;
Chris@225: 
Chris@225:     QXmlInputSource source(&file);
Chris@225:     
Chris@225:     TipFileParser parser(this);
Chris@225:     parser.parse(source);
Chris@225: }
Chris@225: 
Chris@225: void
Chris@225: TipDialog::showTip()
Chris@225: {
Chris@225:     if (m_tipNumber < int(m_tips.size())) {
Chris@682:         cerr << "Tip " << m_tipNumber << " is: " << m_tips[m_tipNumber] << endl;
Chris@225:         m_label->setText(m_tips[m_tipNumber]);
Chris@225:     } else {
Chris@225:         accept();
Chris@225:     }
Chris@225: 
Chris@225:     int tn = m_tipNumber;
Chris@225:     if (++tn >= int(m_tips.size())) tn = 0; //!!! as above
Chris@225: 
Chris@225:     QSettings settings;
Chris@225:     settings.beginGroup("TipOfTheDay");
Chris@225:     settings.setValue("nexttip", tn);
Chris@225: }
Chris@225: 
Chris@225: TipDialog::TipFileParser::TipFileParser(TipDialog *dialog) :
Chris@225:     m_dialog(dialog),
Chris@225:     m_inTip(false),
Chris@228:     m_inText(false),
Chris@228:     m_inHtml(false)
Chris@225: {
Chris@225: }
Chris@225: 
Chris@225: TipDialog::TipFileParser::~TipFileParser()
Chris@225: {
Chris@225: }
Chris@225: 
Chris@225: void
Chris@225: TipDialog::TipFileParser::parse(QXmlInputSource &source)
Chris@225: {
Chris@225:     QXmlSimpleReader reader;
Chris@225:     reader.setContentHandler(this);
Chris@225:     reader.setErrorHandler(this);
Chris@225:     reader.parse(source);
Chris@225: }
Chris@225: 
Chris@225: bool
Chris@225: TipDialog::TipFileParser::startElement(const QString &, const QString &,
Chris@225:                                        const QString &qName,
Chris@225:                                        const QXmlAttributes &attributes)
Chris@225: {
Chris@225:     QString name = qName.toLower();
Chris@225: 
Chris@587:     SVDEBUG << "TipFileParser::startElement(" << name << ")" << endl;
Chris@225: 
Chris@225:     if (name == "tips") {
Chris@225:         QString caption = attributes.value("caption");
Chris@587:         SVDEBUG << "TipFileParser::caption = " << caption << endl;
Chris@225:         if (caption != "") m_dialog->m_caption = caption;
Chris@225:     } else if (name == "tip") {
Chris@225:         if (m_inTip) {
Chris@682:             cerr << "WARNING: TipFileParser: nested <tip> elements" << endl;
Chris@225:         }
Chris@225:         m_inTip = true;
Chris@225:     } else if (name == "text") {
Chris@225:         if (m_inTip) {
Chris@225:             m_inText = true;
Chris@682:             cerr << "TipFileParser: adding new tip" << endl;
Chris@225:             m_dialog->m_tips.push_back("");
Chris@225:         } else {
Chris@682:             cerr << "WARNING: TipFileParser: <text> outside <tip> element" << endl;
Chris@225:         }
Chris@228:     } else if (name == "html") {
Chris@228:         if (m_inTip) {
Chris@228:             m_inHtml = true;
Chris@682:             cerr << "TipFileParser: adding new tip" << endl;
Chris@228:             m_dialog->m_tips.push_back("");
Chris@228:         } else {
Chris@682:             cerr << "WARNING: TipFileParser: <html> outside <tip> element" << endl;
Chris@228:         }
Chris@228:     } else if (m_inHtml) {
Chris@228:         m_dialog->m_tips[m_dialog->m_tips.size()-1] += "<" + qName;
Chris@228:         for (int i = 0; i < attributes.count(); ++i) {
Chris@228:             m_dialog->m_tips[m_dialog->m_tips.size()-1] += 
Chris@228:                 " " + attributes.qName(i) + "=\"" + attributes.value(i) + "\"";
Chris@228:         }
Chris@228:         m_dialog->m_tips[m_dialog->m_tips.size()-1] += ">";
Chris@225:     }
Chris@225:     
Chris@587:     SVDEBUG << "TipFileParser::startElement done" << endl;
Chris@225:     return true;
Chris@225: }
Chris@225: 
Chris@225: bool
Chris@225: TipDialog::TipFileParser::endElement(const QString &, const QString &,
Chris@225:                                      const QString &qName)
Chris@225: {
Chris@225:     QString name = qName.toLower();
Chris@225: 
Chris@225:     if (name == "text") {
Chris@228:         if (!m_inText) {
Chris@682:             cerr << "WARNING: TipFileParser: </text> without <text>" << endl;
Chris@225:         }
Chris@225:         m_inText = false;
Chris@228:     } else if (name == "html") {
Chris@228:         if (!m_inHtml) {
Chris@682:             cerr << "WARNING: TipFileParser: </html> without <html>" << endl;
Chris@228:         }
Chris@228:         m_inHtml = false;
Chris@225:     } else if (name == "tip") {
Chris@225:         if (m_inText) {
Chris@682:             cerr << "WARNING: TipFileParser: <text> without </text>" << endl;
Chris@228:         } else if (m_inHtml) {
Chris@682:             cerr << "WARNING: TipFileParser: <html> without </html>" << endl;
Chris@225:         } else if (!m_inTip) {
Chris@682:             cerr << "WARNING: TipFileParser: </tip> without <tip>" << endl;
Chris@225:         }
Chris@225:         m_inTip = false;
Chris@228:     } else if (m_inHtml) {
Chris@228:         m_dialog->m_tips[m_dialog->m_tips.size()-1] += "</" + qName + ">";
Chris@225:     }
Chris@225: 
Chris@225:     return true;
Chris@225: }
Chris@225: 
Chris@225: bool
Chris@225: TipDialog::TipFileParser::characters(const QString &text)
Chris@225: {
Chris@587:     SVDEBUG << "TipFileParser::characters(" << text << ")" << endl;
Chris@225: 
Chris@228:     if (m_inText || m_inHtml) {
Chris@225:         m_dialog->m_tips[m_dialog->m_tips.size()-1] += text;
Chris@225:     }
Chris@225: 
Chris@225:     return true;
Chris@225: }
Chris@225: 
Chris@225: bool
Chris@225: TipDialog::TipFileParser::error(const QXmlParseException &exception)
Chris@225: {
Chris@225:     QString errorString =
Chris@225: 	QString("ERROR: TipFileParser: %1 at line %2, column %3")
Chris@225: 	.arg(exception.message())
Chris@225: 	.arg(exception.lineNumber())
Chris@225: 	.arg(exception.columnNumber());
Chris@682:     cerr << errorString << endl;
Chris@225:     return QXmlDefaultHandler::error(exception);
Chris@225: }
Chris@225: 
Chris@225: bool
Chris@225: TipDialog::TipFileParser::fatalError(const QXmlParseException &exception)
Chris@225: {
Chris@225:     QString errorString =
Chris@225: 	QString("FATAL ERROR: TipFileParser: %1 at line %2, column %3")
Chris@225: 	.arg(exception.message())
Chris@225: 	.arg(exception.lineNumber())
Chris@225: 	.arg(exception.columnNumber());
Chris@682:     cerr << errorString << endl;
Chris@225:     return QXmlDefaultHandler::fatalError(exception);
Chris@225: }