| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the examples of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:BSD$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** BSD License Usage |
| ** Alternatively, 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 The Qt Company Ltd 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 <QtWidgets> |
| #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, &QSqlRelationalTableModel::rowsInserted, |
| this, &MainWindow::updateHeader); |
| connect(model, &QSqlRelationalTableModel::rowsRemoved, |
| this, &MainWindow::updateHeader); |
| |
| QGridLayout *layout = new QGridLayout; |
| layout->addWidget(artists, 0, 0); |
| layout->addWidget(albums, 1, 0); |
| layout->addWidget(details, 0, 1, 2, 1); |
| layout->setColumnStretch(1, 1); |
| layout->setColumnMinimumWidth(0, 500); |
| |
| QWidget *widget = new QWidget; |
| widget->setLayout(layout); |
| setCentralWidget(widget); |
| createMenuBar(); |
| |
| showImageLabel(); |
| resize(850, 400); |
| 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 i = 0; i < tracks.count(); ++i) { |
| |
| track = tracks.item(i); |
| 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, QOverload<int>::of(&QComboBox::currentIndexChanged), |
| this, &MainWindow::changeArtist); |
| |
| 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, &QTableView::clicked, |
| this, &MainWindow::showAlbumDetails); |
| connect(albumView, &QTableView::activated, |
| this, &MainWindow::showAlbumDetails); |
| |
| QVBoxLayout *layout = new QVBoxLayout; |
| layout->addWidget(albumView, 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, &QAction::triggered, |
| this, &MainWindow::addAlbum); |
| connect(deleteAction, &QAction::triggered, |
| this, &MainWindow::deleteAlbum); |
| connect(quitAction, &QAction::triggered, |
| this, &MainWindow::close); |
| connect(aboutAction, &QAction::triggered, |
| this, &MainWindow::about); |
| connect(aboutQtAction, &QAction::triggered, |
| qApp, &QApplication::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()->setSectionResizeMode(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 Qt SQL and " |
| "Qt XML modules, respectively.</p>")); |
| } |