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
57Browser::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
80Browser::~Browser()
81{
82}
83
84void 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
101QSqlError 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
121void 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
160void 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
177void 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
213void 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
227void 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
243void 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
267void 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
274void Browser::on_fieldStrategyAction_triggered()
275{
276 QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
277 if (tm)
278 tm->setEditStrategy(QSqlTableModel::OnFieldChange);
279}
280
281void Browser::on_rowStrategyAction_triggered()
282{
283 QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
284 if (tm)
285 tm->setEditStrategy(QSqlTableModel::OnRowChange);
286}
287
288void Browser::on_manualStrategyAction_triggered()
289{
290 QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
291 if (tm)
292 tm->setEditStrategy(QSqlTableModel::OnManualSubmit);
293}
294
295void Browser::on_submitAction_triggered()
296{
297 QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
298 if (tm)
299 tm->submitAll();
300}
301
302void Browser::on_revertAction_triggered()
303{
304 QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
305 if (tm)
306 tm->revertAll();
307}
308
309void Browser::on_selectAction_triggered()
310{
311 QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model());
312 if (tm)
313 tm->select();
314}
315
316