Qt Reference Documentation

mainwindow.cpp Example File

sql/masterdetail/mainwindow.cpp
 /****************************************************************************
 **
 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
 ** All rights reserved.
 ** Contact: Nokia Corporation (qt-info@nokia.com)
 **
 ** This file is part of the examples of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:BSD$
 ** You may use this file under the terms of the BSD license as follows:
 **
 ** "Redistribution and use in source and binary forms, with or without
 ** modification, are permitted provided that the following conditions are
 ** met:
 **   * Redistributions of source code must retain the above copyright
 **     notice, this list of conditions and the following disclaimer.
 **   * Redistributions in binary form must reproduce the above copyright
 **     notice, this list of conditions and the following disclaimer in
 **     the documentation and/or other materials provided with the
 **     distribution.
 **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
 **     the names of its contributors may be used to endorse or promote
 **     products derived from this software without specific prior written
 **     permission.
 **
 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/

 #include "mainwindow.h"
 #include "dialog.h"

 #include <QtGui>
 #include <QtSql>
 #include <QtXml>

 extern int uniqueAlbumId;
 extern int uniqueArtistId;

 MainWindow::MainWindow(const QString &artistTable, const QString &albumTable,
                        QFile *albumDetails, QWidget *parent)
      : QMainWindow(parent)
 {
     file = albumDetails;
     readAlbumData();

     model = new QSqlRelationalTableModel(this);
     model->setTable(albumTable);
     model->setRelation(2, QSqlRelation(artistTable, "id", "artist"));
     model->select();

     QGroupBox *artists = createArtistGroupBox();
     QGroupBox *albums = createAlbumGroupBox();
     QGroupBox *details = createDetailsGroupBox();

     artistView->setCurrentIndex(0);
     uniqueAlbumId = model->rowCount();
     uniqueArtistId = artistView->count();

     connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
             this, SLOT(updateHeader(QModelIndex,int,int)));
     connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
             this, SLOT(updateHeader(QModelIndex,int,int)));

     QGridLayout *layout = new QGridLayout;
     layout->addWidget(artists, 0, 0);
     layout->addWidget(albums, 1, 0);
     layout->addWidget(details, 0, 1, 2, 1);
 #if !defined(Q_OS_SYMBIAN) && !defined(Q_WS_MAEMO_5)
     layout->setColumnStretch(1, 1);
     layout->setColumnMinimumWidth(0, 500);
 #endif

     QWidget *widget = new QWidget;
     widget->setLayout(layout);
     setCentralWidget(widget);
     createMenuBar();

     showImageLabel();
 #if !defined(Q_OS_SYMBIAN) && !defined(Q_WS_MAEMO_5)
     resize(850, 400);
 #endif
     setWindowTitle(tr("Music Archive"));
 }

 void MainWindow::changeArtist(int row)
 {
     if (row > 0) {
         QModelIndex index = model->relationModel(2)->index(row, 1);
         model->setFilter("artist = '" + index.data().toString() + '\'') ;
         showArtistProfile(index);
     } else if (row == 0) {
         model->setFilter(QString());
         showImageLabel();
     } else {
         return;
     }
 }

 void MainWindow::showArtistProfile(QModelIndex index)
 {
     QSqlRecord record = model->relationModel(2)->record(index.row());

     QString name = record.value("artist").toString();
     QString count = record.value("albumcount").toString();
     profileLabel->setText(tr("Artist : %1 \n" \
                              "Number of Albums: %2").arg(name).arg(count));

     profileLabel->show();
     iconLabel->show();

     titleLabel->hide();
     trackList->hide();
     imageLabel->hide();
 }

 void MainWindow::showAlbumDetails(QModelIndex index)
 {
     QSqlRecord record = model->record(index.row());

     QString artist = record.value("artist").toString();
     QString title = record.value("title").toString();
     QString year = record.value("year").toString();
     QString albumId = record.value("albumid").toString();

     showArtistProfile(indexOfArtist(artist));
     titleLabel->setText(tr("Title: %1 (%2)").arg(title).arg(year));
     titleLabel->show();

     QDomNodeList albums = albumData.elementsByTagName("album");
     for (int i = 0; i < albums.count(); i++) {
         QDomNode album = albums.item(i);
         if (album.toElement().attribute("id") == albumId) {
             getTrackList(album.toElement());
             break;
         }
     }
     if (!trackList->count() == 0)
         trackList->show();
 }

 void MainWindow::getTrackList(QDomNode album)
 {
     trackList->clear();

     QDomNodeList tracks = album.childNodes();
     QDomNode track;
     QString trackNumber;

     for (int j = 0; j < tracks.count(); j++) {

         track = tracks.item(j);
         trackNumber = track.toElement().attribute("number");

         QListWidgetItem *item = new QListWidgetItem(trackList);
         item->setText(trackNumber + ": " + track.toElement().text());
     }
 }

 void MainWindow::addAlbum()
 {
     Dialog *dialog = new Dialog(model, albumData, file, this);
     int accepted = dialog->exec();

     if (accepted == 1) {
         int lastRow = model->rowCount() - 1;
         albumView->selectRow(lastRow);
         albumView->scrollToBottom();
         showAlbumDetails(model->index(lastRow, 0));
     }
 }

 void MainWindow::deleteAlbum()
 {
     QModelIndexList selection = albumView->selectionModel()->selectedRows(0);

     if (!selection.empty()) {
         QModelIndex idIndex = selection.at(0);
         int id = idIndex.data().toInt();
         QString title = idIndex.sibling(idIndex.row(), 1).data().toString();
         QString artist = idIndex.sibling(idIndex.row(), 2).data().toString();

         QMessageBox::StandardButton button;
         button = QMessageBox::question(this, tr("Delete Album"),
                                        tr("Are you sure you want to "
                                           "delete '%1' by '%2'?")
                                        .arg(title, artist),
                                        QMessageBox::Yes | QMessageBox::No);

         if (button == QMessageBox::Yes) {
             removeAlbumFromFile(id);
             removeAlbumFromDatabase(idIndex);
             decreaseAlbumCount(indexOfArtist(artist));

             showImageLabel();
         }
     } else {
         QMessageBox::information(this, tr("Delete Album"),
                                  tr("Select the album you want to delete."));
     }
 }

 void MainWindow::removeAlbumFromFile(int id)
 {

     QDomNodeList albums = albumData.elementsByTagName("album");

     for (int i = 0; i < albums.count(); i++) {
         QDomNode node = albums.item(i);
         if (node.toElement().attribute("id").toInt() == id) {
             albumData.elementsByTagName("archive").item(0).removeChild(node);
             break;
         }
     }
 /*
     The following code is commented out since the example uses an in
     memory database, i.e., altering the XML file will bring the data
     out of sync.

     if (!file->open(QIODevice::WriteOnly)) {
         return;
     } else {
         QTextStream stream(file);
         albumData.elementsByTagName("archive").item(0).save(stream, 4);
         file->close();
     }
 */
 }

 void MainWindow::removeAlbumFromDatabase(QModelIndex index)
 {
     model->removeRow(index.row());
 }

 void MainWindow::decreaseAlbumCount(QModelIndex artistIndex)
 {
     int row = artistIndex.row();
     QModelIndex albumCountIndex = artistIndex.sibling(row, 2);
     int albumCount = albumCountIndex.data().toInt();

     QSqlTableModel *artists = model->relationModel(2);

     if (albumCount == 1) {
         artists->removeRow(row);
         showImageLabel();
     } else {
         artists->setData(albumCountIndex, QVariant(albumCount - 1));
     }
 }

 void MainWindow::readAlbumData()
 {
     if (!file->open(QIODevice::ReadOnly))
         return;

     if (!albumData.setContent(file)) {
         file->close();
         return;
     }
     file->close();
 }

 QGroupBox* MainWindow::createArtistGroupBox()
 {
     artistView = new QComboBox;
     artistView->setModel(model->relationModel(2));
     artistView->setModelColumn(1);

     connect(artistView, SIGNAL(currentIndexChanged(int)),
             this, SLOT(changeArtist(int)));

     QGroupBox *box = new QGroupBox(tr("Artist"));

     QGridLayout *layout = new QGridLayout;
     layout->addWidget(artistView, 0, 0);
     box->setLayout(layout);

     return box;
 }

 QGroupBox* MainWindow::createAlbumGroupBox()
 {
     QGroupBox *box = new QGroupBox(tr("Album"));

     albumView = new QTableView;
     albumView->setEditTriggers(QAbstractItemView::NoEditTriggers);
     albumView->setSortingEnabled(true);
     albumView->setSelectionBehavior(QAbstractItemView::SelectRows);
     albumView->setSelectionMode(QAbstractItemView::SingleSelection);
     albumView->setShowGrid(false);
     albumView->verticalHeader()->hide();
     albumView->setAlternatingRowColors(true);
     albumView->setModel(model);
     adjustHeader();

     QLocale locale = albumView->locale();
     locale.setNumberOptions(QLocale::OmitGroupSeparator);
     albumView->setLocale(locale);

     connect(albumView, SIGNAL(clicked(QModelIndex)),
             this, SLOT(showAlbumDetails(QModelIndex)));
     connect(albumView, SIGNAL(activated(QModelIndex)),
             this, SLOT(showAlbumDetails(QModelIndex)));

     QVBoxLayout *layout = new QVBoxLayout;
     layout->addWidget(albumView, 0, 0);
     box->setLayout(layout);

     return box;
 }

 QGroupBox* MainWindow::createDetailsGroupBox()
 {
     QGroupBox *box = new QGroupBox(tr("Details"));

     profileLabel = new QLabel;
     profileLabel->setWordWrap(true);
     profileLabel->setAlignment(Qt::AlignBottom);

     titleLabel = new QLabel;
     titleLabel->setWordWrap(true);
     titleLabel->setAlignment(Qt::AlignBottom);

     iconLabel = new QLabel();
     iconLabel->setAlignment(Qt::AlignBottom | Qt::AlignRight);
     iconLabel->setPixmap(QPixmap(":/images/icon.png"));

     imageLabel = new QLabel;
     imageLabel->setWordWrap(true);
     imageLabel->setAlignment(Qt::AlignCenter);
     imageLabel->setPixmap(QPixmap(":/images/image.png"));

     trackList = new QListWidget;

     QGridLayout *layout = new QGridLayout;
     layout->addWidget(imageLabel, 0, 0, 3, 2);
     layout->addWidget(profileLabel, 0, 0);
     layout->addWidget(iconLabel, 0, 1);
     layout->addWidget(titleLabel, 1, 0, 1, 2);
     layout->addWidget(trackList, 2, 0, 1, 2);
     layout->setRowStretch(2, 1);
     box->setLayout(layout);

     return box;
 }

 void MainWindow::createMenuBar()
 {
     QAction *addAction = new QAction(tr("&Add album..."), this);
     QAction *deleteAction = new QAction(tr("&Delete album..."), this);
     QAction *quitAction = new QAction(tr("&Quit"), this);
     QAction *aboutAction = new QAction(tr("&About"), this);
     QAction *aboutQtAction = new QAction(tr("About &Qt"), this);

     addAction->setShortcut(tr("Ctrl+A"));
     deleteAction->setShortcut(tr("Ctrl+D"));
     quitAction->setShortcuts(QKeySequence::Quit);

     QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
     fileMenu->addAction(addAction);
     fileMenu->addAction(deleteAction);
     fileMenu->addSeparator();
     fileMenu->addAction(quitAction);

     QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
     helpMenu->addAction(aboutAction);
     helpMenu->addAction(aboutQtAction);

     connect(addAction, SIGNAL(triggered(bool)), this, SLOT(addAlbum()));
     connect(deleteAction, SIGNAL(triggered(bool)), this, SLOT(deleteAlbum()));
     connect(quitAction, SIGNAL(triggered(bool)), this, SLOT(close()));
     connect(aboutAction, SIGNAL(triggered(bool)), this, SLOT(about()));
     connect(aboutQtAction, SIGNAL(triggered(bool)), qApp, SLOT(aboutQt()));
 }

 void MainWindow::showImageLabel()
 {
     profileLabel->hide();
     titleLabel->hide();
     iconLabel->hide();
     trackList->hide();

     imageLabel->show();
 }

 QModelIndex MainWindow::indexOfArtist(const QString &artist)
 {
     QSqlTableModel *artistModel = model->relationModel(2);

     for (int i = 0; i < artistModel->rowCount(); i++) {
         QSqlRecord record =  artistModel->record(i);
         if (record.value("artist") == artist)
             return artistModel->index(i, 1);
     }
     return QModelIndex();
 }

 void MainWindow::updateHeader(QModelIndex, int, int)
 {
     adjustHeader();
 }

 void MainWindow::adjustHeader()
 {
     albumView->hideColumn(0);
     albumView->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch);
     albumView->resizeColumnToContents(2);
     albumView->resizeColumnToContents(3);
 }

 void MainWindow::about()
 {
     QMessageBox::about(this, tr("About Music Archive"),
             tr("<p>The <b>Music Archive</b> example shows how to present "
                "data from different data sources in the same application. "
                "The album titles, and the corresponding artists and release dates, "
                "are kept in a database, while each album's tracks are stored "
                "in an XML file. </p><p>The example also shows how to add as "
                "well as remove data from both the database and the "
                "associated XML file using the API provided by the QtSql and "
                "QtXml modules, respectively.</p>"));
 }