| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the demonstration applications of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:BSD$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** BSD License Usage |
| 18 | ** Alternatively, you may use this file under the terms of the BSD license |
| 19 | ** as follows: |
| 20 | ** |
| 21 | ** "Redistribution and use in source and binary forms, with or without |
| 22 | ** modification, are permitted provided that the following conditions are |
| 23 | ** met: |
| 24 | ** * Redistributions of source code must retain the above copyright |
| 25 | ** notice, this list of conditions and the following disclaimer. |
| 26 | ** * Redistributions in binary form must reproduce the above copyright |
| 27 | ** notice, this list of conditions and the following disclaimer in |
| 28 | ** the documentation and/or other materials provided with the |
| 29 | ** distribution. |
| 30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
| 31 | ** contributors may be used to endorse or promote products derived |
| 32 | ** from this software without specific prior written permission. |
| 33 | ** |
| 34 | ** |
| 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
| 46 | ** |
| 47 | ** $QT_END_LICENSE$ |
| 48 | ** |
| 49 | ****************************************************************************/ |
| 50 | |
| 51 | #include "browser.h" |
| 52 | #include "qsqlconnectiondialog.h" |
| 53 | |
| 54 | #include <QtWidgets> |
| 55 | #include <QtSql> |
| 56 | |
| 57 | Browser::Browser(QWidget *parent) |
| 58 | : QWidget(parent) |
| 59 | { |
| 60 | setupUi(this); |
| 61 | |
| 62 | table->addAction(insertRowAction); |
| 63 | table->addAction(deleteRowAction); |
| 64 | table->addAction(fieldStrategyAction); |
| 65 | table->addAction(rowStrategyAction); |
| 66 | table->addAction(manualStrategyAction); |
| 67 | table->addAction(submitAction); |
| 68 | table->addAction(revertAction); |
| 69 | table->addAction(selectAction); |
| 70 | |
| 71 | if (QSqlDatabase::drivers().isEmpty()) |
| 72 | QMessageBox::information(this, tr("No database drivers found" ), |
| 73 | tr("This demo requires at least one Qt database driver. " |
| 74 | "Please check the documentation how to build the " |
| 75 | "Qt SQL plugins." )); |
| 76 | |
| 77 | emit statusMessage(tr("Ready." )); |
| 78 | } |
| 79 | |
| 80 | Browser::~Browser() |
| 81 | { |
| 82 | } |
| 83 | |
| 84 | void Browser::exec() |
| 85 | { |
| 86 | QSqlQueryModel *model = new QSqlQueryModel(table); |
| 87 | model->setQuery(QSqlQuery(sqlEdit->toPlainText(), connectionWidget->currentDatabase())); |
| 88 | table->setModel(model); |
| 89 | |
| 90 | if (model->lastError().type() != QSqlError::NoError) |
| 91 | emit statusMessage(model->lastError().text()); |
| 92 | else if (model->query().isSelect()) |
| 93 | emit statusMessage(tr("Query OK." )); |
| 94 | else |
| 95 | emit statusMessage(tr("Query OK, number of affected rows: %1" ).arg( |
| 96 | model->query().numRowsAffected())); |
| 97 | |
| 98 | updateActions(); |
| 99 | } |
| 100 | |
| 101 | QSqlError Browser::addConnection(const QString &driver, const QString &dbName, const QString &host, |
| 102 | const QString &user, const QString &passwd, int port) |
| 103 | { |
| 104 | static int cCount = 0; |
| 105 | |
| 106 | QSqlError err; |
| 107 | QSqlDatabase db = QSqlDatabase::addDatabase(driver, QString("Browser%1" ).arg(++cCount)); |
| 108 | db.setDatabaseName(dbName); |
| 109 | db.setHostName(host); |
| 110 | db.setPort(port); |
| 111 | if (!db.open(user, passwd)) { |
| 112 | err = db.lastError(); |
| 113 | db = QSqlDatabase(); |
| 114 | QSqlDatabase::removeDatabase(QString("Browser%1" ).arg(cCount)); |
| 115 | } |
| 116 | connectionWidget->refresh(); |
| 117 | |
| 118 | return err; |
| 119 | } |
| 120 | |
| 121 | void Browser::addConnection() |
| 122 | { |
| 123 | QSqlConnectionDialog dialog(this); |
| 124 | if (dialog.exec() != QDialog::Accepted) |
| 125 | return; |
| 126 | |
| 127 | if (dialog.useInMemoryDatabase()) { |
| 128 | QSqlDatabase::database("in_mem_db" , false).close(); |
| 129 | QSqlDatabase::removeDatabase("in_mem_db" ); |
| 130 | QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE" , "in_mem_db" ); |
| 131 | db.setDatabaseName(":memory:" ); |
| 132 | if (!db.open()) |
| 133 | QMessageBox::warning(this, tr("Unable to open database" ), tr("An error occurred while " |
| 134 | "opening the connection: " ) + db.lastError().text()); |
| 135 | QSqlQuery q("" , db); |
| 136 | q.exec("drop table Movies" ); |
| 137 | q.exec("drop table Names" ); |
| 138 | q.exec("create table Movies (id integer primary key, Title varchar, Director varchar, Rating number)" ); |
| 139 | q.exec("insert into Movies values (0, 'Metropolis', 'Fritz Lang', '8.4')" ); |
| 140 | q.exec("insert into Movies values (1, 'Nosferatu, eine Symphonie des Grauens', 'F.W. Murnau', '8.1')" ); |
| 141 | q.exec("insert into Movies values (2, 'Bis ans Ende der Welt', 'Wim Wenders', '6.5')" ); |
| 142 | q.exec("insert into Movies values (3, 'Hardware', 'Richard Stanley', '5.2')" ); |
| 143 | q.exec("insert into Movies values (4, 'Mitchell', 'Andrew V. McLaglen', '2.1')" ); |
| 144 | q.exec("create table Names (id integer primary key, Firstname varchar, Lastname varchar, City varchar)" ); |
| 145 | q.exec("insert into Names values (0, 'Sala', 'Palmer', 'Morristown')" ); |
| 146 | q.exec("insert into Names values (1, 'Christopher', 'Walker', 'Morristown')" ); |
| 147 | q.exec("insert into Names values (2, 'Donald', 'Duck', 'Andeby')" ); |
| 148 | q.exec("insert into Names values (3, 'Buck', 'Rogers', 'Paris')" ); |
| 149 | q.exec("insert into Names values (4, 'Sherlock', 'Holmes', 'London')" ); |
| 150 | connectionWidget->refresh(); |
| 151 | } else { |
| 152 | QSqlError err = addConnection(dialog.driverName(), dialog.databaseName(), dialog.hostName(), |
| 153 | dialog.userName(), dialog.password(), dialog.port()); |
| 154 | if (err.type() != QSqlError::NoError) |
| 155 | QMessageBox::warning(this, tr("Unable to open database" ), tr("An error occurred while " |
| 156 | "opening the connection: " ) + err.text()); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | void Browser::showTable(const QString &t) |
| 161 | { |
| 162 | QSqlTableModel *model = new CustomModel(table, connectionWidget->currentDatabase()); |
| 163 | model->setEditStrategy(QSqlTableModel::OnRowChange); |
| 164 | model->setTable(connectionWidget->currentDatabase().driver()->escapeIdentifier(t, QSqlDriver::TableName)); |
| 165 | model->select(); |
| 166 | if (model->lastError().type() != QSqlError::NoError) |
| 167 | emit statusMessage(model->lastError().text()); |
| 168 | |
| 169 | table->setModel(model); |
| 170 | table->setEditTriggers(QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed); |
| 171 | connect(table->selectionModel(), &QItemSelectionModel::currentRowChanged, |
| 172 | this, &Browser::currentChanged); |
| 173 | |
| 174 | updateActions(); |
| 175 | } |
| 176 | |
| 177 | void Browser::showMetaData(const QString &t) |
| 178 | { |
| 179 | QSqlRecord rec = connectionWidget->currentDatabase().record(t); |
| 180 | QStandardItemModel *model = new QStandardItemModel(table); |
| 181 | |
| 182 | model->insertRows(0, rec.count()); |
| 183 | model->insertColumns(0, 7); |
| 184 | |
| 185 | model->setHeaderData(0, Qt::Horizontal, "Fieldname" ); |
| 186 | model->setHeaderData(1, Qt::Horizontal, "Type" ); |
| 187 | model->setHeaderData(2, Qt::Horizontal, "Length" ); |
| 188 | model->setHeaderData(3, Qt::Horizontal, "Precision" ); |
| 189 | model->setHeaderData(4, Qt::Horizontal, "Required" ); |
| 190 | model->setHeaderData(5, Qt::Horizontal, "AutoValue" ); |
| 191 | model->setHeaderData(6, Qt::Horizontal, "DefaultValue" ); |
| 192 | |
| 193 | for (int i = 0; i < rec.count(); ++i) { |
| 194 | QSqlField fld = rec.field(i); |
| 195 | model->setData(model->index(i, 0), fld.name()); |
| 196 | model->setData(model->index(i, 1), fld.typeID() == -1 |
| 197 | ? QString(fld.metaType().name()) |
| 198 | : QString("%1 (%2)" ).arg(fld.metaType().name()).arg(fld.typeID())); |
| 199 | model->setData(model->index(i, 2), fld.length()); |
| 200 | model->setData(model->index(i, 3), fld.precision()); |
| 201 | model->setData(model->index(i, 4), fld.requiredStatus() == -1 ? QVariant("?" ) |
| 202 | : QVariant(bool(fld.requiredStatus()))); |
| 203 | model->setData(model->index(i, 5), fld.isAutoValue()); |
| 204 | model->setData(model->index(i, 6), fld.defaultValue()); |
| 205 | } |
| 206 | |
| 207 | table->setModel(model); |
| 208 | table->setEditTriggers(QAbstractItemView::NoEditTriggers); |
| 209 | |
| 210 | updateActions(); |
| 211 | } |
| 212 | |
| 213 | void Browser::insertRow() |
| 214 | { |
| 215 | QSqlTableModel *model = qobject_cast<QSqlTableModel *>(table->model()); |
| 216 | if (!model) |
| 217 | return; |
| 218 | |
| 219 | QModelIndex insertIndex = table->currentIndex(); |
| 220 | int row = insertIndex.row() == -1 ? 0 : insertIndex.row(); |
| 221 | model->insertRow(row); |
| 222 | insertIndex = model->index(row, 0); |
| 223 | table->setCurrentIndex(insertIndex); |
| 224 | table->edit(insertIndex); |
| 225 | } |
| 226 | |
| 227 | void Browser::deleteRow() |
| 228 | { |
| 229 | QSqlTableModel *model = qobject_cast<QSqlTableModel *>(table->model()); |
| 230 | if (!model) |
| 231 | return; |
| 232 | |
| 233 | QModelIndexList currentSelection = table->selectionModel()->selectedIndexes(); |
| 234 | for (int i = 0; i < currentSelection.count(); ++i) { |
| 235 | if (currentSelection.at(i).column() != 0) |
| 236 | continue; |
| 237 | model->removeRow(currentSelection.at(i).row()); |
| 238 | } |
| 239 | |
| 240 | updateActions(); |
| 241 | } |
| 242 | |
| 243 | void Browser::updateActions() |
| 244 | { |
| 245 | QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); |
| 246 | bool enableIns = tm; |
| 247 | bool enableDel = enableIns && table->currentIndex().isValid(); |
| 248 | |
| 249 | insertRowAction->setEnabled(enableIns); |
| 250 | deleteRowAction->setEnabled(enableDel); |
| 251 | |
| 252 | fieldStrategyAction->setEnabled(tm); |
| 253 | rowStrategyAction->setEnabled(tm); |
| 254 | manualStrategyAction->setEnabled(tm); |
| 255 | submitAction->setEnabled(tm); |
| 256 | revertAction->setEnabled(tm); |
| 257 | selectAction->setEnabled(tm); |
| 258 | |
| 259 | if (tm) { |
| 260 | QSqlTableModel::EditStrategy es = tm->editStrategy(); |
| 261 | fieldStrategyAction->setChecked(es == QSqlTableModel::OnFieldChange); |
| 262 | rowStrategyAction->setChecked(es == QSqlTableModel::OnRowChange); |
| 263 | manualStrategyAction->setChecked(es == QSqlTableModel::OnManualSubmit); |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | void Browser::about() |
| 268 | { |
| 269 | QMessageBox::about(this, tr("About" ), tr("The SQL Browser demonstration " |
| 270 | "shows how a data browser can be used to visualize the results of SQL" |
| 271 | "statements on a live database" )); |
| 272 | } |
| 273 | |
| 274 | void Browser::on_fieldStrategyAction_triggered() |
| 275 | { |
| 276 | QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); |
| 277 | if (tm) |
| 278 | tm->setEditStrategy(QSqlTableModel::OnFieldChange); |
| 279 | } |
| 280 | |
| 281 | void Browser::on_rowStrategyAction_triggered() |
| 282 | { |
| 283 | QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); |
| 284 | if (tm) |
| 285 | tm->setEditStrategy(QSqlTableModel::OnRowChange); |
| 286 | } |
| 287 | |
| 288 | void Browser::on_manualStrategyAction_triggered() |
| 289 | { |
| 290 | QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); |
| 291 | if (tm) |
| 292 | tm->setEditStrategy(QSqlTableModel::OnManualSubmit); |
| 293 | } |
| 294 | |
| 295 | void Browser::on_submitAction_triggered() |
| 296 | { |
| 297 | QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); |
| 298 | if (tm) |
| 299 | tm->submitAll(); |
| 300 | } |
| 301 | |
| 302 | void Browser::on_revertAction_triggered() |
| 303 | { |
| 304 | QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); |
| 305 | if (tm) |
| 306 | tm->revertAll(); |
| 307 | } |
| 308 | |
| 309 | void Browser::on_selectAction_triggered() |
| 310 | { |
| 311 | QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); |
| 312 | if (tm) |
| 313 | tm->select(); |
| 314 | } |
| 315 | |
| 316 | |