mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-20 19:11:39 -06:00
* Issue #530: constraints on table prevents new record being added This adds a new dialog for adding records to a table. The current approach is broken for several cases where foreign keys and check constraints are impeding the insertion of an empty record. With the dialog, the user can inspect the constraints (tooltip) and type of fields and add values consistent with the requirements. The data is only inserted when the user presses the Save button. The dialog is modelled after the Edit Table or Edit Index dialog. An upper frame allows entering the data using widgets. The lower frame previews the SQL statement that will be used. The old approach for adding records is still accessible pressing Tab on the last cell of the table. * Fix build problem introduced in previous commit on this branch * Dialog as fallback for failure after empty row insertion and read only text The insertion of an empty row is always tried. When it fails due to constraints and foreign keys, the Add Record Dialog is open so the user can enter values for the new record considering the constraints. When the table has not constraints, or the row insertion provides valid values, the user is still able to insert rows using the simple approach. SQL preview in dialog is now read-only. * Visual improvements for the Add Record dialog QLineEdit as item delegate for the value, so it is more visible that we are supposed to edit the value. Remove last end-of-line in tool-tip. * Improvements in the "Add Record" dialog Display of NULL values using DisplayRole (no focus) or place holder text (when focus). Set value to NULL through a context menu and shortcut in the value line edit. Take text type affinity into account for quoting or not entered numbers. New isType and affinity functions in sqlitetypes. Clarify wording of constraints in tooltip for value. Added the same tooltip for the type. Escape quotes inside string values. Removed unused parameters warnings. Other wording or code improvements based on the pull-request review: #1477. * User access to the Add Record dialog The Add Record dialog is now accessible for the user. The New Record button is converted to a QToolButton and a new pop-up menu is added to it for invoking the in-line table insertion (New Record) or the Add Record dialog (Insert Values...). What's This information for the button updated.
This commit is contained in:
335
src/AddRecordDialog.cpp
Normal file
335
src/AddRecordDialog.cpp
Normal file
@@ -0,0 +1,335 @@
|
||||
#include "AddRecordDialog.h"
|
||||
#include "ui_AddRecordDialog.h"
|
||||
#include "sqlitedb.h"
|
||||
#include "Settings.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QKeyEvent>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QWhatsThis>
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
|
||||
class NullLineEdit: public QLineEdit {
|
||||
private:
|
||||
bool m_isNull;
|
||||
|
||||
public:
|
||||
NullLineEdit(QWidget* parent=nullptr): QLineEdit(parent), m_isNull (true) {}
|
||||
|
||||
bool isNull() {return m_isNull;}
|
||||
void setNull(bool value) {
|
||||
if (value) {
|
||||
clear();
|
||||
setPlaceholderText(Settings::getValue("databrowser", "null_text").toString());
|
||||
setModified(false);
|
||||
} else
|
||||
setPlaceholderText("");
|
||||
m_isNull = value;
|
||||
}
|
||||
protected:
|
||||
void contextMenuEvent(QContextMenuEvent *event)
|
||||
{
|
||||
QMenu* editContextMenu = createStandardContextMenu();
|
||||
|
||||
QAction* nullAction = new QAction(tr("Set to NULL"), editContextMenu);
|
||||
connect(nullAction, &QAction::triggered, [&]() {
|
||||
setNull(true);
|
||||
});
|
||||
nullAction->setShortcut(QKeySequence(tr("Alt+Del")));
|
||||
editContextMenu->addSeparator();
|
||||
editContextMenu->addAction(nullAction);
|
||||
|
||||
editContextMenu->exec(event->globalPos());
|
||||
delete editContextMenu;
|
||||
}
|
||||
|
||||
void keyPressEvent(QKeyEvent *evt) {
|
||||
// Alt+Del sets field to NULL
|
||||
if((evt->modifiers() & Qt::AltModifier) && (evt->key() == Qt::Key_Delete))
|
||||
setNull(true);
|
||||
else {
|
||||
// Remove any possible NULL mark when user starts typing
|
||||
setPlaceholderText("");
|
||||
QLineEdit::keyPressEvent(evt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Styled Item Delegate for non-editable columns (all except Value)
|
||||
class NoEditDelegate: public QStyledItemDelegate {
|
||||
public:
|
||||
NoEditDelegate(QObject* parent=nullptr): QStyledItemDelegate(parent) {}
|
||||
virtual QWidget* createEditor(QWidget* /* parent */, const QStyleOptionViewItem& /* option */, const QModelIndex& /* index */) const {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// Styled Item Delegate for editable columns (Value)
|
||||
class EditDelegate: public QStyledItemDelegate {
|
||||
|
||||
public:
|
||||
EditDelegate(QObject* parent=nullptr): QStyledItemDelegate(parent) {}
|
||||
virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem& /* option */, const QModelIndex& /* index */) const {
|
||||
return new NullLineEdit(parent);
|
||||
}
|
||||
|
||||
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const {
|
||||
|
||||
NullLineEdit* lineEditor = dynamic_cast<NullLineEdit*>(editor);
|
||||
// Set the editor in the null state (unless the user has actually written NULL)
|
||||
if (index.model()->data(index, Qt::UserRole).isNull() &&
|
||||
index.model()->data(index, Qt::DisplayRole) == Settings::getValue("databrowser", "null_text"))
|
||||
lineEditor->setNull(true);
|
||||
else {
|
||||
QStyledItemDelegate::setEditorData(editor, index);
|
||||
lineEditor->setNull(false);
|
||||
}
|
||||
}
|
||||
virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
|
||||
|
||||
NullLineEdit* lineEditor = dynamic_cast<NullLineEdit*>(editor);
|
||||
// Restore NULL text (unless the user has already modified the value)
|
||||
if (lineEditor->isNull() && !lineEditor->isModified()) {
|
||||
model->setData(index, Settings::getValue("databrowser", "null_text"), Qt::DisplayRole);
|
||||
model->setData(index, QVariant(), Qt::UserRole);
|
||||
} else {
|
||||
// Get isModified flag before calling setModelData
|
||||
bool modified = lineEditor->isModified();
|
||||
QStyledItemDelegate::setModelData(editor, model, index);
|
||||
// Copy the just edited data to the user role, so it can be later used in the SQL insert statement.
|
||||
if (modified) {
|
||||
lineEditor->setNull(false);
|
||||
model->setData(index, model->data(index, Qt::EditRole), Qt::UserRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddRecordDialog::AddRecordDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& tableName, QWidget* parent)
|
||||
: QDialog(parent),
|
||||
ui(new Ui::AddRecordDialog),
|
||||
pdb(db),
|
||||
curTable(tableName),
|
||||
m_table(*(pdb.getObjectByName(curTable).dynamicCast<sqlb::Table>()))
|
||||
{
|
||||
// Create UI
|
||||
ui->setupUi(this);
|
||||
connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)),this,SLOT(itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
populateFields();
|
||||
|
||||
ui->sqlTextEdit->setReadOnly(true);
|
||||
|
||||
// Update UI
|
||||
ui->treeWidget->resizeColumnToContents(kName);
|
||||
ui->treeWidget->resizeColumnToContents(kType);
|
||||
ui->treeWidget->setFrameShape(QFrame::Box);
|
||||
}
|
||||
|
||||
AddRecordDialog::~AddRecordDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AddRecordDialog::keyPressEvent(QKeyEvent *evt)
|
||||
{
|
||||
if((evt->modifiers() & Qt::ControlModifier)
|
||||
&& (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return))
|
||||
{
|
||||
accept();
|
||||
return;
|
||||
}
|
||||
if(evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return)
|
||||
return;
|
||||
QDialog::keyPressEvent(evt);
|
||||
}
|
||||
|
||||
void AddRecordDialog::setDefaultsStyle(QTreeWidgetItem* item)
|
||||
{
|
||||
// Default values are displayed with the style configured for NULL values in the Data Browser.
|
||||
QFont font;
|
||||
font.setItalic(true);
|
||||
item->setData(kValue, Qt::FontRole, font);
|
||||
item->setData(kValue, Qt::BackgroundRole, QColor(Settings::getValue("databrowser", "null_bg_colour").toString()));
|
||||
item->setData(kValue, Qt::ForegroundRole, QColor(Settings::getValue("databrowser", "null_fg_colour").toString()));
|
||||
}
|
||||
|
||||
void AddRecordDialog::populateFields()
|
||||
{
|
||||
// disconnect the itemChanged signal or the SQL text will
|
||||
// be updated while filling the treewidget.
|
||||
disconnect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this,SLOT(itemChanged(QTreeWidgetItem*,int)));
|
||||
|
||||
ui->treeWidget->clear();
|
||||
|
||||
// Allow all Edit Triggers, but they will only apply to the columns with
|
||||
// editors (Value)
|
||||
ui->treeWidget->setEditTriggers(QAbstractItemView::AllEditTriggers);
|
||||
|
||||
// Disallow edition of columns except Value
|
||||
ui->treeWidget->setItemDelegateForColumn(kName, new NoEditDelegate(this));
|
||||
ui->treeWidget->setItemDelegateForColumn(kType, new NoEditDelegate(this));
|
||||
ui->treeWidget->setItemDelegateForColumn(kValue, new EditDelegate(this));
|
||||
|
||||
const sqlb::FieldVector& fields = m_table.fields();
|
||||
const sqlb::FieldVector& pk = m_table.primaryKey();
|
||||
for(const sqlb::FieldPtr& f : fields)
|
||||
{
|
||||
QTreeWidgetItem *tbitem = new QTreeWidgetItem(ui->treeWidget);
|
||||
|
||||
tbitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable);
|
||||
|
||||
tbitem->setText(kName, f->name());
|
||||
tbitem->setText(kType, f->type());
|
||||
tbitem->setData(kType, Qt::UserRole, f->affinity());
|
||||
|
||||
// NOT NULL fields are indicated in bold.
|
||||
if (f->notnull()) {
|
||||
QFont font;
|
||||
font.setBold(true);
|
||||
tbitem->setData(kName, Qt::FontRole, font);
|
||||
}
|
||||
if (pk.contains(f))
|
||||
tbitem->setIcon(kName, QIcon(":/icons/field_key"));
|
||||
else
|
||||
tbitem->setIcon(kName, QIcon(":/icons/field"));
|
||||
|
||||
QString defaultValue = f->defaultValue();
|
||||
QString toolTip;
|
||||
|
||||
if (f->autoIncrement())
|
||||
toolTip.append(tr("Auto-increment\n"));
|
||||
|
||||
if (f->unique())
|
||||
toolTip.append(tr("Unique constraint\n"));
|
||||
|
||||
if (!f->check().isEmpty())
|
||||
toolTip.append(tr("Check constraint:\t %1\n").arg (f->check()));
|
||||
|
||||
QSharedPointer<sqlb::ForeignKeyClause> fk =
|
||||
m_table.constraint({f}, sqlb::Constraint::ForeignKeyConstraintType).dynamicCast<sqlb::ForeignKeyClause>();
|
||||
if(fk)
|
||||
toolTip.append(tr("Foreign key:\t %1\n").arg(fk->toString()));
|
||||
|
||||
setDefaultsStyle(tbitem);
|
||||
|
||||
// Display Role is used for displaying the default values.
|
||||
// Only when they are changed, the User Role is updated and then used in the INSERT query.
|
||||
if (!defaultValue.isEmpty()) {
|
||||
tbitem->setData(kValue, Qt::DisplayRole, f->defaultValue());
|
||||
toolTip.append(tr("Default value:\t %1\n").arg (defaultValue));
|
||||
} else
|
||||
tbitem->setData(kValue, Qt::DisplayRole, Settings::getValue("databrowser", "null_text"));
|
||||
|
||||
|
||||
if (!toolTip.isEmpty()) {
|
||||
// Chop last end-of-line
|
||||
toolTip.chop(1);
|
||||
tbitem->setToolTip(kValue, toolTip);
|
||||
tbitem->setToolTip(kType, toolTip);
|
||||
}
|
||||
}
|
||||
|
||||
updateSqlText();
|
||||
|
||||
// and reconnect
|
||||
connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)),this,SLOT(itemChanged(QTreeWidgetItem*,int)));
|
||||
}
|
||||
|
||||
void AddRecordDialog::accept()
|
||||
{
|
||||
if(!pdb.executeSQL(ui->sqlTextEdit->text()))
|
||||
{
|
||||
QMessageBox::warning(
|
||||
this,
|
||||
QApplication::applicationName(),
|
||||
tr("Error adding record. Message from database engine:\n\n%1").arg(pdb.lastError()));
|
||||
return;
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void AddRecordDialog::updateSqlText()
|
||||
{
|
||||
QString stmt = QString("INSERT INTO %1").arg(curTable.toString());
|
||||
|
||||
QStringList vals;
|
||||
QStringList fields;
|
||||
|
||||
// If the User Role of the Value column is not null, the entered value is used
|
||||
// in the INSERT statement. Otherwise, SQLite just uses the default value for the field.
|
||||
for(int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem *item = ui->treeWidget->topLevelItem(i);
|
||||
// User role contains now values entered by the user, that we actually need to insert.
|
||||
QVariant value = item->data(kValue, Qt::UserRole);
|
||||
|
||||
if (!value.isNull()) {
|
||||
bool isNumeric;
|
||||
fields << sqlb::escapeIdentifier(item->text(kName));
|
||||
value.toDouble(&isNumeric);
|
||||
// If it has a numeric format and has no text affinity, do not quote it.
|
||||
if (isNumeric && item->data(kType, Qt::UserRole).toString() != "TEXT")
|
||||
vals << value.toString();
|
||||
else
|
||||
vals << QString("'%1'").arg(value.toString().replace("'", "''"));
|
||||
}
|
||||
}
|
||||
|
||||
if(fields.empty())
|
||||
{
|
||||
stmt.append(" DEFAULT VALUES;");
|
||||
} else {
|
||||
stmt.append("\n(");
|
||||
stmt.append(fields.join(", "));
|
||||
stmt.append(")\nVALUES (");
|
||||
stmt.append(vals.join(", "));
|
||||
stmt.append(");");
|
||||
}
|
||||
|
||||
ui->sqlTextEdit->setText(stmt);
|
||||
}
|
||||
|
||||
void AddRecordDialog::itemChanged(QTreeWidgetItem *item, int column)
|
||||
{
|
||||
if (item->data(column, Qt::UserRole).isNull())
|
||||
setDefaultsStyle(item);
|
||||
else {
|
||||
// Restore default fore/background for the value column,
|
||||
// since the value has changed away from the default.
|
||||
QFont font;
|
||||
font.setItalic(false);
|
||||
item->setData(column, Qt::FontRole, font);
|
||||
item->setData(column, Qt::BackgroundRole, item->data(kName, Qt::BackgroundRole));
|
||||
item->setData(column, Qt::ForegroundRole, item->data(kName, Qt::ForegroundRole));
|
||||
}
|
||||
|
||||
updateSqlText();
|
||||
}
|
||||
|
||||
void AddRecordDialog::help()
|
||||
{
|
||||
QWhatsThis::enterWhatsThisMode();
|
||||
}
|
||||
|
||||
void AddRecordDialog::on_buttonBox_clicked(QAbstractButton* button)
|
||||
{
|
||||
if (button == ui->buttonBox->button(QDialogButtonBox::Cancel))
|
||||
reject();
|
||||
else if (button == ui->buttonBox->button(QDialogButtonBox::Save))
|
||||
accept();
|
||||
else if (button == ui->buttonBox->button(QDialogButtonBox::Help))
|
||||
help();
|
||||
else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) {
|
||||
if (QMessageBox::warning(this,
|
||||
QApplication::applicationName(),
|
||||
tr("Are you sure you want to restore all the entered values to their defaults?"),
|
||||
QMessageBox::RestoreDefaults | QMessageBox::Cancel,
|
||||
QMessageBox::Cancel) == QMessageBox::RestoreDefaults)
|
||||
populateFields();
|
||||
}
|
||||
}
|
||||
52
src/AddRecordDialog.h
Normal file
52
src/AddRecordDialog.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef ADDRECORDDIALOG_H
|
||||
#define ADDRECORDDIALOG_H
|
||||
|
||||
#include "sqlitetypes.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class DBBrowserDB;
|
||||
class QTreeWidgetItem;
|
||||
|
||||
namespace Ui {
|
||||
class AddRecordDialog;
|
||||
}
|
||||
class QAbstractButton;
|
||||
|
||||
|
||||
class AddRecordDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AddRecordDialog(DBBrowserDB& pdb, const sqlb::ObjectIdentifier& tableName, QWidget* parent = nullptr);
|
||||
~AddRecordDialog();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *evt);
|
||||
|
||||
private:
|
||||
enum Columns {
|
||||
kName = 0,
|
||||
kType = 1,
|
||||
kValue = 2,
|
||||
};
|
||||
|
||||
void updateSqlText();
|
||||
void populateFields();
|
||||
void setDefaultsStyle(QTreeWidgetItem* item);
|
||||
|
||||
private slots:
|
||||
virtual void accept();
|
||||
void itemChanged(QTreeWidgetItem* item, int column);
|
||||
void help();
|
||||
void on_buttonBox_clicked(QAbstractButton* button);
|
||||
|
||||
private:
|
||||
Ui::AddRecordDialog* ui;
|
||||
DBBrowserDB& pdb;
|
||||
sqlb::ObjectIdentifier curTable;
|
||||
sqlb::Table m_table;
|
||||
};
|
||||
|
||||
#endif
|
||||
158
src/AddRecordDialog.ui
Normal file
158
src/AddRecordDialog.ui
Normal file
@@ -0,0 +1,158 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AddRecordDialog</class>
|
||||
<widget class="QDialog" name="AddRecordDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>650</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Add New Record</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/table</normaloff>:/icons/table</iconset>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupFields">
|
||||
<property name="title">
|
||||
<string>Enter values for the new record considering constraints. Fields in bold are mandatory.</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QTreeWidget" name="treeWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>140</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>In the Value column you can specify the value for the field identified in the Name column. The Type column indicates the type of the field. Default values are displayed in the same style as NULL values.</string>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Values to insert. Pre-filled default values are inserted automatically unless they are changed.</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="SqlTextEdit" name="sqlTextEdit" native="true">
|
||||
<property name="whatsThis">
|
||||
<string>When you edit the values in the upper frame, the SQL query for inserting this new record is shown here. You can edit manually the query before saving.</string>
|
||||
</property>
|
||||
<property name="readOnly" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p><span style=" font-weight:600;">Save</span> will submit the shown SQL statement to the database for inserting the new record.</p><p><span style=" font-weight:600;">Restore Defaults</span> will restore the initial values in the <span style=" font-weight:600;">Value</span> column.</p><p><span style=" font-weight:600;">Cancel</span> will close this dialog without executing the query.</p></body></html></string>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>SqlTextEdit</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>sqltextedit.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>treeWidget</tabstop>
|
||||
<tabstop>sqlTextEdit</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="icons/icons.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>clicked(QAbstractButton*)</signal>
|
||||
<receiver>AddRecordDialog</receiver>
|
||||
<slot>on_buttonBox_clicked(QAbstractButton*)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>324</x>
|
||||
<y>477</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>324</x>
|
||||
<y>249</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>itemChanged()</slot>
|
||||
</slots>
|
||||
</ui>
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "EditIndexDialog.h"
|
||||
#include "AboutDialog.h"
|
||||
#include "EditTableDialog.h"
|
||||
#include "AddRecordDialog.h"
|
||||
#include "ImportCsvDialog.h"
|
||||
#include "ExportDataDialog.h"
|
||||
#include "Settings.h"
|
||||
@@ -180,6 +181,11 @@ void MainWindow::init()
|
||||
popupOpenDbMenu->addAction(ui->fileOpenReadOnlyAction);
|
||||
ui->fileOpenActionPopup->setMenu(popupOpenDbMenu);
|
||||
|
||||
popupNewRecordMenu = new QMenu(this);
|
||||
popupNewRecordMenu->addAction(ui->newRecordAction);
|
||||
popupNewRecordMenu->addAction(ui->insertValuesAction);
|
||||
ui->buttonNewRecord->setMenu(popupNewRecordMenu);
|
||||
|
||||
popupSaveSqlFileMenu = new QMenu(this);
|
||||
popupSaveSqlFileMenu->addAction(ui->actionSqlSaveFile);
|
||||
popupSaveSqlFileMenu->addAction(ui->actionSqlSaveFileAs);
|
||||
@@ -720,10 +726,19 @@ void MainWindow::addRecord()
|
||||
{
|
||||
selectTableLine(row);
|
||||
} else {
|
||||
QMessageBox::warning(this, QApplication::applicationName(), tr("Error adding record:\n") + db.lastError());
|
||||
// Error inserting empty row.
|
||||
// User has to provide values acomplishing the constraints. Open Add Record Dialog.
|
||||
insertValues();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::insertValues()
|
||||
{
|
||||
AddRecordDialog dialog(db, currentlyBrowsedTableName(), this);
|
||||
if (dialog.exec())
|
||||
populateTable();
|
||||
}
|
||||
|
||||
void MainWindow::deleteRecord()
|
||||
{
|
||||
if(ui->dataTable->selectionModel()->hasSelection())
|
||||
|
||||
@@ -147,6 +147,7 @@ private:
|
||||
QMenu* popupTableMenu;
|
||||
QMenu* recentFilesMenu;
|
||||
QMenu* popupOpenDbMenu;
|
||||
QMenu* popupNewRecordMenu;
|
||||
QMenu* popupSaveSqlFileMenu;
|
||||
QMenu* popupSaveSqlResultsMenu;
|
||||
QMenu* popupSaveFilterAsMenu;
|
||||
@@ -220,6 +221,7 @@ private slots:
|
||||
void clearTableBrowser();
|
||||
bool fileClose();
|
||||
void addRecord();
|
||||
void insertValues();
|
||||
void deleteRecord();
|
||||
void navigatePrevious();
|
||||
void navigateNext();
|
||||
|
||||
@@ -197,12 +197,12 @@ You can drag SQL statements from an object row and drop them into other applicat
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonNewRecord">
|
||||
<widget class="QToolButton" name="buttonNewRecord">
|
||||
<property name="toolTip">
|
||||
<string>Insert a new record in the current table</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>This button creates a new, empty record in the database</string>
|
||||
<string><html><head/><body><p>This button creates a new record in the database. Hold the mouse button to open a pop-up menu of different options:</p><ul><li><span style=" font-weight:600;">New Record</span>: insert a new record with default values in the database.</li><li><span style=" font-weight:600;">Insert Values...</span>: open a dialog for entering values before they are inserted in the database. This allows to enter values acomplishing the different constraints. This dialog is also open if the <span style=" font-weight:600;">New Record</span> option fails due to these constraints.</li></ul></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New Record</string>
|
||||
@@ -210,7 +210,7 @@ You can drag SQL statements from an object row and drop them into other applicat
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonDeleteRecord">
|
||||
<widget class="QToolButton" name="buttonDeleteRecord">
|
||||
<property name="toolTip">
|
||||
<string>Delete the current record</string>
|
||||
</property>
|
||||
@@ -2170,6 +2170,28 @@ You can drag SQL statements from the Schema column and drop them into the SQL ed
|
||||
<enum>QAction::TextHeuristicRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="insertValuesAction">
|
||||
<property name="text">
|
||||
<string>Insert Values...</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open a dialog for inserting values in a new record</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Open a dialog for inserting values in a new record</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="newRecordAction">
|
||||
<property name="text">
|
||||
<string>New Record</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Insert new record using default values in browsed table</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Insert new record using default values in browsed table</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="fileNewInMemoryDatabaseAction">
|
||||
<property name="text">
|
||||
<string>New In-&Memory Database</string>
|
||||
@@ -3484,6 +3506,34 @@ You can drag SQL statements from the Schema column and drop them into the SQL ed
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>newRecordAction</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>addRecord()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>518</x>
|
||||
<y>314</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>insertValuesAction</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>insertValues()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>fileNewInMemoryDatabaseAction</sender>
|
||||
<signal>triggered()</signal>
|
||||
|
||||
@@ -304,6 +304,48 @@ bool Field::isInteger() const
|
||||
|| norm == "int8";
|
||||
}
|
||||
|
||||
bool Field::isReal() const
|
||||
{
|
||||
QString norm = m_type.trimmed().toLower();
|
||||
|
||||
return norm == "real"
|
||||
|| norm == "double"
|
||||
|| norm == "double precision"
|
||||
|| norm == "float";
|
||||
}
|
||||
|
||||
bool Field::isNumeric() const
|
||||
{
|
||||
QString norm = m_type.trimmed().toLower();
|
||||
|
||||
return norm.startsWith("decimal")
|
||||
|| norm == "numeric"
|
||||
|| norm == "boolean"
|
||||
|| norm == "date"
|
||||
|| norm == "datetime";
|
||||
}
|
||||
|
||||
bool Field::isBlob() const
|
||||
{
|
||||
QString norm = m_type.trimmed().toLower();
|
||||
|
||||
return norm.isEmpty()
|
||||
|| norm == "blob";
|
||||
}
|
||||
|
||||
QString Field::affinity() const
|
||||
{
|
||||
if (isInteger()) return "INTEGER";
|
||||
|
||||
if (isText()) return "TEXT";
|
||||
|
||||
if (isBlob()) return "BLOB";
|
||||
|
||||
if (isReal()) return "REAL";
|
||||
|
||||
return "NUMERIC";
|
||||
}
|
||||
|
||||
void Table::clear()
|
||||
{
|
||||
m_rowidColumn = "_rowid_";
|
||||
|
||||
@@ -359,6 +359,12 @@ public:
|
||||
|
||||
bool isText() const;
|
||||
bool isInteger() const;
|
||||
bool isBlob() const;
|
||||
bool isReal() const;
|
||||
bool isNumeric() const;
|
||||
|
||||
// Type affinity of the column according to SQLite3 rules
|
||||
QString affinity() const;
|
||||
|
||||
const QString& name() const { return m_name; }
|
||||
const QString& type() const { return m_type; }
|
||||
|
||||
@@ -27,6 +27,7 @@ HEADERS += \
|
||||
EditIndexDialog.h \
|
||||
AboutDialog.h \
|
||||
EditTableDialog.h \
|
||||
AddRecordDialog.h \
|
||||
Settings.h \
|
||||
PreferencesDialog.h \
|
||||
EditDialog.h \
|
||||
@@ -74,6 +75,7 @@ SOURCES += \
|
||||
MainWindow.cpp \
|
||||
EditIndexDialog.cpp \
|
||||
EditTableDialog.cpp \
|
||||
AddRecordDialog.cpp \
|
||||
Settings.cpp \
|
||||
PreferencesDialog.cpp \
|
||||
AboutDialog.cpp \
|
||||
@@ -123,6 +125,7 @@ FORMS += \
|
||||
EditIndexDialog.ui \
|
||||
AboutDialog.ui \
|
||||
EditTableDialog.ui \
|
||||
AddRecordDialog.ui \
|
||||
PreferencesDialog.ui \
|
||||
EditDialog.ui \
|
||||
ExportDataDialog.ui \
|
||||
|
||||
Reference in New Issue
Block a user