diff options
| author | 2016-06-13 18:42:42 -0400 | |
|---|---|---|
| committer | 2016-06-13 18:42:42 -0400 | |
| commit | 5d9314f134ddd3dc4c853e398ac90ba247fb2e4f (patch) | |
| tree | 5c457fc188036988d7abd29a3eb09931e406510f /SQLiteStudio3/guiSQLiteStudio/dialogs | |
| parent | 8e640722c62692818ab840d50b3758f89a41a54e (diff) | |
Imported Upstream version 3.1.0upstream/3.1.0
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/dialogs')
14 files changed, 1458 insertions, 422 deletions
diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp index f4e48fe..14cb06b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp @@ -33,8 +33,8 @@ void ColumnDialog::init() limitDialogWidth(this); setWindowIcon(ICONS.COLUMN); - ui->scale->setStrict(true); - ui->precision->setStrict(true); + ui->scale->setStrict(true, true); + ui->precision->setStrict(true, true); ui->typeCombo->addItem(""); foreach (DataType::Enum type, DataType::getAllTypes()) @@ -52,8 +52,11 @@ void ColumnDialog::init() connect(ui->constraintsView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(updateConstraintsToolbarState())); connect(ui->constraintsView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(editConstraint(QModelIndex))); - connect(constraintsModel, SIGNAL(constraintsChanged()), this, SLOT(updateConstraints())); + connect(constraintsModel, SIGNAL(constraintsChanged()), this, SLOT(updateValidations())); connect(constraintsModel, SIGNAL(constraintsChanged()), this, SLOT(updateState())); + connect(ui->typeCombo, SIGNAL(currentTextChanged(const QString&)), this, SLOT(updateValidations())); + connect(ui->scale, SIGNAL(modified()), this, SLOT(updateValidations())); + connect(ui->precision, SIGNAL(modified()), this, SLOT(updateValidations())); connect(ui->pkButton, SIGNAL(clicked()), this, SLOT(configurePk())); connect(ui->fkButton, SIGNAL(clicked()), this, SLOT(configureFk())); @@ -214,7 +217,7 @@ void ColumnDialog::editConstraint(SqliteCreateTable::Column::Constraint* constra ui->constraintsView->resizeColumnToContents(0); ui->constraintsView->resizeColumnToContents(1); - updateConstraints(); + updateValidations(); } void ColumnDialog::delConstraint(const QModelIndex& idx) @@ -377,6 +380,44 @@ bool ColumnDialog::isUnofficialSqlite2Constraint(SqliteCreateTable::Column::Cons return false; } +void ColumnDialog::updateTypeValidations() +{ + QString scaleErrorMsg = tr("Scale is not allowed for INTEGER PRIMARY KEY columns."); + QString precisionErrorMsg = tr("Precision cannot be defined without the scale."); + + QVariant scale = ui->scale->getValue(); + QVariant precision = ui->precision->getValue(); + + bool scaleDefined = !scale.toString().isEmpty(); + bool precisionDefined = !precision.toString().isEmpty(); + + bool precisionOk = !(precisionDefined && !scaleDefined); + bool scaleOk = true; + + bool hasPk = column->getConstraint(SqliteCreateTable::Column::Constraint::PRIMARY_KEY) != nullptr; + bool isInteger = ui->typeCombo->currentText().toUpper() == "INTEGER"; + if (hasPk && isInteger) + { + if (scaleDefined) + scaleOk = false; + + if (precisionDefined) + { + precisionOk = false; + precisionErrorMsg = tr("Precision is not allowed for INTEGER PRIMARY KEY columns."); + } + } + + setValidState(ui->scale, scaleOk, scaleErrorMsg); + setValidState(ui->precision, precisionOk, precisionErrorMsg); + + if (!scaleOk || !precisionOk) + { + QPushButton* btn = ui->buttonBox->button(QDialogButtonBox::Ok); + btn->setEnabled(false); + } +} + void ColumnDialog::moveConstraintUp() { QModelIndex idx = ui->constraintsView->currentIndex(); @@ -505,7 +546,7 @@ void ColumnDialog::switchMode(bool advanced) ui->constraintModesWidget->setCurrentWidget(advanced ? ui->advancedPage : ui->simplePage); } -void ColumnDialog::updateConstraints() +void ColumnDialog::updateValidations() { QPushButton* btn = ui->buttonBox->button(QDialogButtonBox::Ok); btn->setEnabled(true); @@ -536,8 +577,10 @@ void ColumnDialog::updateConstraints() setValidState(tb, true); } - foreach (SqliteCreateTable::Column::Constraint* constr, column->constraints) + for (SqliteCreateTable::Column::Constraint* constr : column->constraints) updateConstraint(constr); + + updateTypeValidations(); } void ColumnDialog::updateConstraint(SqliteCreateTable::Column::Constraint* constraint) @@ -564,7 +607,7 @@ void ColumnDialog::setColumn(SqliteCreateTable::Column* value) ui->precision->setValue(value->type->precision, false); } - updateConstraints(); + updateValidations(); } SqliteCreateTable::Column* ColumnDialog::getModifiedColumn() @@ -602,9 +645,13 @@ void ColumnDialog::updateDataType() if (!scaleTxt.isEmpty()) column->type->scale = ui->scale->getValue(); + else + column->type->scale = QVariant(); if (!precisionTxt.isEmpty()) column->type->precision = ui->precision->getValue(); + else + column->type->precision = QVariant(); column->type->rebuildTokens(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h index c567e1a..596441c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h @@ -67,6 +67,7 @@ class GUI_API_EXPORT ColumnDialog : public QDialog, public ExtActionContainer QCheckBox* getCheckBoxForConstraint(SqliteCreateTable::Column::Constraint* constraint); QToolButton* getToolButtonForConstraint(SqliteCreateTable::Column::Constraint* constraint); bool isUnofficialSqlite2Constraint(SqliteCreateTable::Column::Constraint* constraint); + void updateTypeValidations(); Ui::ColumnDialog *ui = nullptr; SqliteCreateTable::ColumnPtr column; @@ -105,7 +106,7 @@ class GUI_API_EXPORT ColumnDialog : public QDialog, public ExtActionContainer void notNullToggled(bool enabled); void defaultToggled(bool enabled); void switchMode(bool advanced); - void updateConstraints(); + void updateValidations(); void updateConstraint(SqliteCreateTable::Column::Constraint* constraint); void updateDataType(); }; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp index 627e2c1..42c7099 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp @@ -168,13 +168,7 @@ void ConfigDialog::init() ui->categoriesTree->setCurrentItem(ui->categoriesTree->topLevelItem(0));
configMapper = new ConfigMapper(CfgMain::getPersistableInstances());
- connect(configMapper, SIGNAL(modified()), this, SLOT(markModified()));
- connect(configMapper, &ConfigMapper::notifyEnabledWidgetModified, [=](QWidget* widget, CfgEntry* key, const QVariant& value)
- {
- UNUSED(widget);
- for (ConfigNotifiablePlugin* plugin : notifiablePlugins)
- plugin->configModified(key, value);
- });
+ connectMapperSignals(configMapper);
ui->categoriesFilter->setClearButtonEnabled(true);
UserInputFilter* filter = new UserInputFilter(ui->categoriesFilter, this, SLOT(applyFilter(QString)));
@@ -575,6 +569,12 @@ void ConfigDialog::commitPluginConfigs() }
}
+void ConfigDialog::connectMapperSignals(ConfigMapper* mapper)
+{
+ connect(mapper, SIGNAL(modified()), this, SLOT(markModified()));
+ connect(mapper, SIGNAL(notifyEnabledWidgetModified(QWidget*, CfgEntry*, const QVariant&)), this, SLOT(notifyPluginsAboutModification(QWidget*, CfgEntry*, const QVariant&)));
+}
+
void ConfigDialog::updateDataTypeListState()
{
bool listEditingEnabled = ui->dataEditorsTypesList->selectedItems().size() > 0 && ui->dataEditorsTypesList->currentItem()->flags().testFlag(Qt::ItemIsEditable);
@@ -952,6 +952,12 @@ void ConfigDialog::markRequiresSchemasRefresh() requiresSchemasRefresh = true;
}
+void ConfigDialog::notifyPluginsAboutModification(QWidget*, CfgEntry* key, const QVariant& value)
+{
+ for (ConfigNotifiablePlugin* plugin : notifiablePlugins)
+ plugin->configModified(key, value);
+}
+
void ConfigDialog::updatePluginCategoriesVisibility(QTreeWidgetItem* categoryItem)
{
categoryItem->setHidden(categoryItem->childCount() == 0);
@@ -1325,7 +1331,12 @@ bool ConfigDialog::initPluginPage(Plugin* plugin, bool skipConfigLoading) {
pluginConfigMappers[cfgPlugin] = new ConfigMapper(mainConfig);
pluginConfigMappers[cfgPlugin]->bindToConfig(widget);
+ connectMapperSignals(pluginConfigMappers[cfgPlugin]);
mainConfig->begin();
+
+ // Remove this config from global mapper (if present there), so it's handled per plugin mapper
+ configMapper->removeMainCfgEntry(mainConfig);
+ configMapper->ignoreWidget(widget);
}
else if (!skipConfigLoading)
{
@@ -1342,7 +1353,7 @@ void ConfigDialog::deinitPluginPage(Plugin* plugin) if (!nameToPage.contains(pluginName))
return;
- if (!dynamic_cast<UiConfiguredPlugin*>(plugin))
+ if (dynamic_cast<UiConfiguredPlugin*>(plugin))
{
UiConfiguredPlugin* cfgPlugin = dynamic_cast<UiConfiguredPlugin*>(plugin);
CfgMain* mainCfg = cfgPlugin->getMainUiConfig();
@@ -1360,6 +1371,7 @@ void ConfigDialog::deinitPluginPage(Plugin* plugin) QWidget* widget = nameToPage[pluginName];
nameToPage.remove(pluginName);
+ configMapper->removeIgnoredWidget(widget);
ui->stackedWidget->removeWidget(widget);
delete widget;
}
diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h index ae2f3a7..f0ab1de 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h @@ -84,6 +84,7 @@ class GUI_API_EXPORT ConfigDialog : public QDialog void addDataType(const QString& typeStr);
void rollbackPluginConfigs();
void commitPluginConfigs();
+ void connectMapperSignals(ConfigMapper* mapper);
Ui::ConfigDialog *ui = nullptr;
QStyle* previewStyle = nullptr;
@@ -136,6 +137,7 @@ class GUI_API_EXPORT ConfigDialog : public QDialog void updateBuiltInPluginsVisibility();
void applyShortcutsFilter(const QString& filter);
void markRequiresSchemasRefresh();
+ void notifyPluginsAboutModification(QWidget*, CfgEntry* key, const QVariant& value);
public slots:
void accept();
diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui index 1c5515d..c69ad20 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui @@ -14,16 +14,6 @@ <string>Configuration</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
- <item row="1" column="0">
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
<item row="0" column="0">
<widget class="QWidget" name="mainWidget" native="true">
<property name="contextMenuPolicy">
@@ -173,23 +163,15 @@ </item>
<item>
<property name="text">
- <string>Plugins</string>
+ <string>Database list</string>
</property>
<property name="statusTip">
- <string notr="true">pluginsPage</string>
+ <string notr="true">databaseListPage</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
- <normaloff>:/icons/img/plugin.png</normaloff>:/icons/img/plugin.png</iconset>
+ <normaloff>:/icons/img/database_online.png</normaloff>:/icons/img/database_online.png</iconset>
</property>
- <item>
- <property name="text">
- <string>Code formatters</string>
- </property>
- <property name="statusTip">
- <string notr="true">formatterPluginsPage</string>
- </property>
- </item>
</item>
<item>
<property name="text">
@@ -215,6 +197,26 @@ </property>
</item>
</item>
+ <item>
+ <property name="text">
+ <string>Plugins</string>
+ </property>
+ <property name="statusTip">
+ <string notr="true">pluginsPage</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../icons.qrc">
+ <normaloff>:/icons/img/plugin.png</normaloff>:/icons/img/plugin.png</iconset>
+ </property>
+ <item>
+ <property name="text">
+ <string>Code formatters</string>
+ </property>
+ <property name="statusTip">
+ <string notr="true">formatterPluginsPage</string>
+ </property>
+ </item>
+ </item>
</widget>
</item>
</layout>
@@ -227,68 +229,116 @@ </sizepolicy>
</property>
<property name="currentIndex">
- <number>0</number>
+ <number>5</number>
</property>
- <widget class="QWidget" name="dataBrowsingPage">
- <layout class="QVBoxLayout" name="verticalLayout_21">
+ <widget class="QWidget" name="databaseListPage">
+ <layout class="QVBoxLayout" name="verticalLayout_36">
<item>
- <widget class="QGroupBox" name="dataBrowsingGroup">
+ <widget class="QGroupBox" name="dbListGroup">
<property name="title">
- <string>Data browsing and editing</string>
+ <string>Database list</string>
</property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="0">
- <widget class="QLabel" name="rowsPerPageLabel">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="sortColumns">
+ <property name="toolTip">
+ <string>If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement.</string>
+ </property>
<property name="text">
- <string>Number of data rows per page:</string>
+ <string>Sort table columns alphabetically</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.SortColumns</string>
</property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QSpinBox" name="rowsPerPageSpin">
- <property name="maximumSize">
- <size>
- <width>150</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="minimum">
- <number>1</number>
- </property>
- <property name="maximum">
- <number>99999</number>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="expandTablesCheck">
+ <property name="text">
+ <string>Expand tables node when connected to a database</string>
</property>
<property name="cfg" stdset="0">
- <string notr="true">General.NumberOfRowsPerPage</string>
+ <string notr="true">General.ExpandTables</string>
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="initColWidthLimitLabel">
+ <item row="6" column="0">
+ <widget class="QGroupBox" name="addLabelsGroup">
<property name="toolTip">
- <string><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p></string>
+ <string><p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p></string>
+ </property>
+ <property name="title">
+ <string>Display additional labels on the list</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.ShowDbTreeLabels</string>
</property>
+ <layout class="QVBoxLayout" name="verticalLayout_20">
+ <item>
+ <widget class="QCheckBox" name="regularTableLabelsCheck">
+ <property name="toolTip">
+ <string>For regular tables labels will show number of columns, indexes and triggers for each of tables.</string>
+ </property>
+ <property name="text">
+ <string>Display labels for regular tables</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.ShowRegularTableLabels</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="virtTableLabelsCheck">
+ <property name="toolTip">
+ <string>Virtual tables will be marked with a 'virtual' label.</string>
+ </property>
+ <property name="text">
+ <string>Display labels for virtual tables</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.ShowVirtualTableLabels</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="expandViewsCheck">
<property name="text">
- <string>Limit initial data column width to (in pixels):</string>
+ <string>Expand views node when connected to a database</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.ExpandViews</string>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QSpinBox" name="initColWidthLimitSpin">
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="sortObjects">
<property name="toolTip">
- <string><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p></string>
+ <string>If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created)</string>
</property>
- <property name="minimum">
- <number>10</number>
+ <property name="text">
+ <string>Sort objects (tables, indexes, triggers and views) alphabetically</string>
</property>
- <property name="maximum">
- <number>99999</number>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.SortObjects</string>
</property>
- <property name="value">
- <number>600</number>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QCheckBox" name="dispSysTableCheck">
+ <property name="text">
+ <string>Display system tables and indexes on the list</string>
</property>
<property name="cfg" stdset="0">
- <string notr="true">General.MaxInitialColumnWith</string>
+ <string notr="true">General.ShowSystemObjects</string>
</property>
</widget>
</item>
@@ -296,50 +346,34 @@ </widget>
</item>
<item>
- <widget class="QGroupBox" name="insertRowPlacementGroup">
+ <widget class="QGroupBox" name="dbDialogGroup">
<property name="title">
- <string>Inserting new row in data grid</string>
+ <string>Database dialog window</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_33">
+ <layout class="QVBoxLayout" name="verticalLayout_37">
<item>
- <widget class="ConfigRadioButton" name="insertRowBeforeRadio">
- <property name="text">
- <string>Before currently selected row</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- <property name="assignedValue" stdset="0">
- <number>0</number>
- </property>
- <property name="cfg" stdset="0">
- <string>General.InsertRowPlacement</string>
+ <widget class="QCheckBox" name="dontMarkDbAsDefaultCheck">
+ <property name="toolTip">
+ <string><p>When adding new database it is marked to be "permanent" (stored in configuration) by default. Checking this option makes every new database to NOT be "permanent" by default.</p></string>
</property>
- </widget>
- </item>
- <item>
- <widget class="ConfigRadioButton" name="insertRowAfterRadio">
<property name="text">
- <string>After currently selected row</string>
- </property>
- <property name="assignedValue" stdset="0">
- <number>1</number>
+ <string>Do not mark database to be "permanent" by default</string>
</property>
<property name="cfg" stdset="0">
- <string>General.InsertRowPlacement</string>
+ <string notr="true">General.NewDbNotPermanentByDefault</string>
</property>
</widget>
</item>
<item>
- <widget class="ConfigRadioButton" name="insertRowAtEndRadio">
- <property name="text">
- <string>At the end of data view</string>
+ <widget class="QCheckBox" name="bypassDbDialogCheck">
+ <property name="toolTip">
+ <string><p>When this option is enabled, then files dropped from file manager onto database list will be automatically added to the list, bypassing standard database dialog. If for various reasons automatic process fails, then standard dialog will be presented to the user.</p></string>
</property>
- <property name="assignedValue" stdset="0">
- <number>2</number>
+ <property name="text">
+ <string>Try to bypass dialog completly when dropping database file onto the list</string>
</property>
<property name="cfg" stdset="0">
- <string>General.InsertRowPlacement</string>
+ <string notr="true">General.BypassDbDialogWhenDropped</string>
</property>
</widget>
</item>
@@ -347,7 +381,7 @@ </widget>
</item>
<item>
- <spacer name="verticalSpacer_6">
+ <spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@@ -361,6 +395,240 @@ </item>
</layout>
</widget>
+ <widget class="QWidget" name="dataBrowsingPage">
+ <layout class="QVBoxLayout" name="verticalLayout_21">
+ <item>
+ <widget class="QScrollArea" name="dataBrowsingScrollArea">
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="dataBrowsingScrollAreaContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>564</width>
+ <height>504</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_34">
+ <item>
+ <widget class="QGroupBox" name="dataBrowsingGroup">
+ <property name="title">
+ <string>Data browsing and editing</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="rowsPerPageLabel">
+ <property name="text">
+ <string>Number of data rows per page:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="rowsPerPageSpin">
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>99999</number>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.NumberOfRowsPerPage</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="initColWidthLimitLabel">
+ <property name="toolTip">
+ <string><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p></string>
+ </property>
+ <property name="text">
+ <string>Limit initial data column width to (in pixels):</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="initColWidthLimitSpin">
+ <property name="toolTip">
+ <string><p>When the data is read into grid view columns width is automatically adjusted. This value limits the initial width for the adjustment, but user can still resize the column manually over this limit.</p></string>
+ </property>
+ <property name="minimum">
+ <number>10</number>
+ </property>
+ <property name="maximum">
+ <number>99999</number>
+ </property>
+ <property name="value">
+ <number>600</number>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.MaxInitialColumnWith</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QCheckBox" name="tolltipInDataViewCheck">
+ <property name="toolTip">
+ <string><p>When this is enabled and user holds mouse pointer over a cell in any data view (query results, a table data, a view data) a tooltip will appear with details about the cell - it includes details like column data type, constraints, ROWID and others.</p></string>
+ </property>
+ <property name="text">
+ <string>Show column and row details tooltip in data view</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.ShowDataViewTooltips</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="insertRowPlacementGroup">
+ <property name="title">
+ <string>Inserting new row in data grid</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_33">
+ <item>
+ <widget class="ConfigRadioButton" name="insertRowBeforeRadio">
+ <property name="text">
+ <string>Before currently selected row</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="assignedValue" stdset="0">
+ <number>0</number>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.InsertRowPlacement</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ConfigRadioButton" name="insertRowAfterRadio">
+ <property name="text">
+ <string>After currently selected row</string>
+ </property>
+ <property name="assignedValue" stdset="0">
+ <number>1</number>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.InsertRowPlacement</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ConfigRadioButton" name="insertRowAtEndRadio">
+ <property name="text">
+ <string>At the end of data view</string>
+ </property>
+ <property name="assignedValue" stdset="0">
+ <number>2</number>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.InsertRowPlacement</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="tableWinGroup">
+ <property name="title">
+ <string>Table windows</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_18">
+ <item>
+ <widget class="QCheckBox" name="openTablesOnDataCheck">
+ <property name="toolTip">
+ <string><p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p></string>
+ </property>
+ <property name="text">
+ <string>Open Table Windows with the data tab for start</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.OpenTablesOnData</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="dataTabInTableAsFirstCheck">
+ <property name="toolTip">
+ <string><p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p></string>
+ </property>
+ <property name="text">
+ <string>Place data tab as first tab in a Table Window</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.DataTabAsFirstInTables</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="viewWinGroup">
+ <property name="title">
+ <string>View windows</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_19">
+ <item>
+ <widget class="QCheckBox" name="openViewsOnDataCheck">
+ <property name="toolTip">
+ <string><p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p></string>
+ </property>
+ <property name="text">
+ <string>Open View Windows with the data tab for start</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.OpenViewsOnData</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="dataTabInViewAsFirstCheck">
+ <property name="toolTip">
+ <string><p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p></string>
+ </property>
+ <property name="text">
+ <string>Place data tab as first tab in a View Window</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.DataTabAsFirstInViews</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
<widget class="QWidget" name="dataEditorsPage">
<layout class="QVBoxLayout" name="verticalLayout_24">
<item>
@@ -598,6 +866,28 @@ </widget>
</item>
<item>
+ <widget class="QGroupBox" name="statusFieldGroup_2">
+ <property name="title">
+ <string>Status Field</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_35">
+ <item>
+ <widget class="QCheckBox" name="checkBox_2">
+ <property name="toolTip">
+ <string><p>When user manually closes the Status panel, this option makes sure that if any new message is printed in the Status panel it will be reopened. If it's disabled, then Status panel can only be open manually by the user from the "View" menu.</p></string>
+ </property>
+ <property name="text">
+ <string>Always open Status panel when new message is printed</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string notr="true">General.AutoOpenStatusField</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -711,8 +1001,8 @@ <rect>
<x>0</x>
<y>0</y>
- <width>456</width>
- <height>608</height>
+ <width>596</width>
+ <height>487</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_31">
@@ -757,163 +1047,7 @@ <bool>true</bool>
</property>
<property name="cfg" stdset="0">
- <string>General.CompactLayout</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="dbListGroup">
- <property name="title">
- <string>Database list</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="4" column="0">
- <widget class="QCheckBox" name="sortColumns">
- <property name="toolTip">
- <string>If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement.</string>
- </property>
- <property name="text">
- <string>Sort table columns alphabetically</string>
- </property>
- <property name="cfg" stdset="0">
- <string notr="true">General.SortColumns</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QCheckBox" name="expandTablesCheck">
- <property name="text">
- <string>Expand tables node when connected to a database</string>
- </property>
- <property name="cfg" stdset="0">
- <string notr="true">General.ExpandTables</string>
- </property>
- </widget>
- </item>
- <item row="6" column="0">
- <widget class="QGroupBox" name="addLabelsGroup">
- <property name="toolTip">
- <string><p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p></string>
- </property>
- <property name="title">
- <string>Display additional labels on the list</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="checked">
- <bool>false</bool>
- </property>
- <property name="cfg" stdset="0">
- <string notr="true">General.ShowDbTreeLabels</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_20">
- <item>
- <widget class="QCheckBox" name="regularTableLabelsCheck">
- <property name="toolTip">
- <string>For regular tables labels will show number of columns, indexes and triggers for each of tables.</string>
- </property>
- <property name="text">
- <string>Display labels for regular tables</string>
- </property>
- <property name="cfg" stdset="0">
- <string notr="true">General.ShowRegularTableLabels</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="virtTableLabelsCheck">
- <property name="toolTip">
- <string>Virtual tables will be marked with a 'virtual' label.</string>
- </property>
- <property name="text">
- <string>Display labels for virtual tables</string>
- </property>
- <property name="cfg" stdset="0">
- <string notr="true">General.ShowVirtualTableLabels</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QCheckBox" name="expandViewsCheck">
- <property name="text">
- <string>Expand views node when connected to a database</string>
- </property>
- <property name="cfg" stdset="0">
- <string notr="true">General.ExpandViews</string>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QCheckBox" name="sortObjects">
- <property name="toolTip">
- <string>If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created)</string>
- </property>
- <property name="text">
- <string>Sort objects (tables, indexes, triggers and views) alphabetically</string>
- </property>
- <property name="cfg" stdset="0">
- <string notr="true">General.SortObjects</string>
- </property>
- </widget>
- </item>
- <item row="5" column="0">
- <widget class="QCheckBox" name="dispSysTableCheck">
- <property name="text">
- <string>Display system tables and indexes on the list</string>
- </property>
- <property name="cfg" stdset="0">
- <string notr="true">General.ShowSystemObjects</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="tableWinGroup">
- <property name="title">
- <string>Table windows</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_18">
- <item>
- <widget class="QCheckBox" name="openTablesOnDataCheck">
- <property name="toolTip">
- <string>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</string>
- </property>
- <property name="text">
- <string>Open Table Windows with the data tab for start</string>
- </property>
- <property name="cfg" stdset="0">
- <string notr="true">General.OpenTablesOnData</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="viewWinGroup">
- <property name="title">
- <string>View windows</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_19">
- <item>
- <widget class="QCheckBox" name="openViewsOnDataCheck">
- <property name="toolTip">
- <string>When enabled, View Windows will show up with the data tab, instead of the structure tab.</string>
- </property>
- <property name="text">
- <string>Open View Windows with the data tab for start</string>
- </property>
- <property name="cfg" stdset="0">
- <string notr="true">General.OpenViewsOnData</string>
+ <string notr="true">General.CompactLayout</string>
</property>
</widget>
</item>
@@ -966,6 +1100,19 @@ </layout>
</widget>
</item>
+ <item>
+ <spacer name="verticalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
</widget>
@@ -1275,7 +1422,7 @@ <string notr="true"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'.Helvetica Neue DeskInterface'; font-size:13pt; font-weight:400; font-style:normal;">
+</style></head><body style=" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Abcdefgh</span></p></body></html></string>
</property>
</widget>
@@ -1366,8 +1513,8 @@ p, li { white-space: pre-wrap; } <rect>
<x>0</x>
<y>0</y>
- <width>249</width>
- <height>322</height>
+ <width>290</width>
+ <height>323</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_9">
@@ -1488,8 +1635,8 @@ p, li { white-space: pre-wrap; } <rect>
<x>0</x>
<y>0</y>
- <width>329</width>
- <height>813</height>
+ <width>352</width>
+ <height>806</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
@@ -2012,6 +2159,16 @@ p, li { white-space: pre-wrap; } </layout>
</widget>
</item>
+ <item row="4" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
</layout>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp index a3d0e65..6bdf41f 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp @@ -102,6 +102,9 @@ void DbDialog::showEvent(QShowEvent *e) updateOptions(); updateState(); + if (doAutoTest) + testConnectionClicked(); + QDialog::showEvent(e); } @@ -133,6 +136,9 @@ void DbDialog::init() generateNameSwitched(true); layout()->setSizeConstraint(QLayout::SetFixedSize); + + if (mode == Mode::ADD && CFG_UI.General.NewDbNotPermanentByDefault.get()) + ui->permamentCheckBox->setChecked(false); } void DbDialog::updateOptions() @@ -396,32 +402,11 @@ void DbDialog::updateType() if (disableTypeAutodetection) return; - QFileInfo file(ui->fileEdit->text()); - if (!file.exists() || file.isDir()) + DbPlugin* validPlugin = SQLITESTUDIO->getDbManager()->getPluginForDbFile(ui->fileEdit->text()); + if (!validPlugin || validPlugin->getLabel() == ui->typeCombo->currentText()) return; - QString currentPluginLabel = ui->typeCombo->currentText(); - QList<DbPlugin*> validPlugins; - QHash<QString,QVariant> options; - QString path = ui->fileEdit->text(); - Db* probeDb = nullptr; - for (DbPlugin* plugin : dbPlugins) - { - probeDb = plugin->getInstance("", path, options); - if (probeDb) - { - delete probeDb; - probeDb = nullptr; - - if (plugin->getLabel() == currentPluginLabel) - return; // current plugin is among valid plugins, no need to change anything - - validPlugins << plugin; - } - } - - if (validPlugins.size() > 0) - ui->typeCombo->setCurrentText(validPlugins.first()->getLabel()); + ui->typeCombo->setCurrentText(validPlugin->getLabel()); } QHash<QString, QVariant> DbDialog::collectOptions() @@ -486,10 +471,9 @@ bool DbDialog::validate() } } - Db* registeredDb = DBLIST->getByName(ui->nameEdit->text()); + Db* registeredDb = DBLIST->getByName(ui->nameEdit->text(), Qt::CaseInsensitive); if (registeredDb && (mode == Mode::ADD || registeredDb != db)) { - qDebug() << ui->generateCheckBox->isChecked(); setValidState(ui->nameEdit, false, tr("This name is already in use. Please enter unique name.")); return false; } @@ -534,6 +518,11 @@ void DbDialog::updateState() ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(validate()); } +void DbDialog::setDoAutoTest(bool value) +{ + doAutoTest = value; +} + void DbDialog::propertyChanged() { ui->testConnIcon->setVisible(false); @@ -543,7 +532,7 @@ void DbDialog::typeChanged(int index) { UNUSED(index); updateOptions(); - updateState(); + valueForNameGenerationChanged(); } void DbDialog::valueForNameGenerationChanged() @@ -552,13 +541,16 @@ void DbDialog::valueForNameGenerationChanged() if (!ui->generateCheckBox->isChecked()) return; + QString generatedName; if (dbPlugins.count() > 0) { DbPlugin* plugin = dbPlugins[ui->typeCombo->currentText()]; - QString generatedName = plugin->generateDbName(ui->fileEdit->text()); - generatedName = generateUniqueName(generatedName, existingDatabaseNames); - ui->nameEdit->setText(generatedName); + generatedName = DBLIST->generateUniqueDbName(plugin, ui->fileEdit->text()); } + else + generatedName = DBLIST->generateUniqueDbName(ui->fileEdit->text()); + + ui->nameEdit->setText(generatedName); } void DbDialog::browseForFile() diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h index ce73b11..2beea3e 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h @@ -39,6 +39,7 @@ class GUI_API_EXPORT DbDialog : public QDialog QString getName(); QHash<QString,QVariant> collectOptions(); bool isPermanent(); + void setDoAutoTest(bool value); protected: void changeEvent(QEvent *e); @@ -68,6 +69,7 @@ class GUI_API_EXPORT DbDialog : public QDialog QWidget* lastWidgetInTabOrder = nullptr; DbPluginOption::CustomBrowseHandler customBrowseHandler = nullptr; bool disableTypeAutodetection = false; + bool doAutoTest = false; static const constexpr int ADDITIONAL_ROWS_BEGIN_INDEX = 1; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.ui index 333d887..186114b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>515</width> - <height>414</height> + <width>614</width> + <height>493</height> </rect> </property> <property name="windowTitle"> @@ -289,8 +289,8 @@ <rect> <x>0</x> <y>0</y> - <width>483</width> - <height>318</height> + <width>308</width> + <height>267</height> </rect> </property> <property name="styleSheet"> diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp index cdae91d..c5da317 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp @@ -1,7 +1,6 @@ #include "indexdialog.h" #include "ui_indexdialog.h" #include "schemaresolver.h" -#include "parser/ast/sqliteindexedcolumn.h" #include "services/notifymanager.h" #include "common/utils_sql.h" #include "db/chainexecutor.h" @@ -11,6 +10,7 @@ #include "services/config.h" #include "uiutils.h" #include "sqlite3.h" +#include "indexexprcolumndialog.h" #include "windows/editorwindow.h" #include "services/codeformatter.h" #include <QDebug> @@ -40,6 +40,7 @@ IndexDialog::IndexDialog(Db* db, const QString& index, QWidget* parent) : IndexDialog::~IndexDialog() { + clearColumns(); delete ui; } @@ -67,20 +68,34 @@ void IndexDialog::init() return; } + ui->moveUpButton->setIcon(ICONS.MOVE_UP); + ui->moveDownButton->setIcon(ICONS.MOVE_DOWN); + ui->addExprColumnButton->setIcon(ICONS.INDEX_EXPR_ADD); + ui->editExprColumnButton->setIcon(ICONS.INDEX_EXPR_EDIT); + ui->delExprColumnButton->setIcon(ICONS.INDEX_EXPR_DEL); + connect(ui->moveUpButton, SIGNAL(clicked()), this, SLOT(moveColumnUp())); + connect(ui->moveDownButton, SIGNAL(clicked()), this, SLOT(moveColumnDown())); + connect(ui->addExprColumnButton, SIGNAL(clicked()), this, SLOT(addExprColumn())); + connect(ui->editExprColumnButton, SIGNAL(clicked()), this, SLOT(editExprColumn())); + connect(ui->delExprColumnButton, SIGNAL(clicked()), this, SLOT(delExprColumn())); + connect(ui->columnsTable, SIGNAL(cellDoubleClicked(int,int)), this, SLOT(editExprColumn(int))); + ui->columnsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + connect(ui->columnsTable->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(updateToolBarButtons(QModelIndex))); ui->partialIndexEdit->setDb(db); connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); columnStateSignalMapping = new QSignalMapper(this); - connect(columnStateSignalMapping, SIGNAL(mapped(int)), this, SLOT(updateColumnState(int))); + connect(columnStateSignalMapping, SIGNAL(mapped(QString)), this, SLOT(updateColumnState(QString))); SchemaResolver resolver(db); ui->tableCombo->addItem(QString::null); ui->tableCombo->addItems(resolver.getTables()); connect(ui->tableCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateTable(QString))); connect(ui->tableCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateValidation())); + connect(ui->uniqueCheck, SIGNAL(toggled(bool)), this, SLOT(updateValidation())); if (existingIndex) ui->tableCombo->setEnabled(false); @@ -98,6 +113,9 @@ void IndexDialog::init() ui->partialIndexCheck->setVisible(false); ui->partialIndexEdit->setVisible(false); ui->columnsTable->setColumnHidden(1, true); + ui->addExprColumnButton->setVisible(false); + ui->editExprColumnButton->setVisible(false); + ui->delExprColumnButton->setVisible(false); } readCollations(); @@ -139,17 +157,17 @@ void IndexDialog::readIndex() void IndexDialog::buildColumns() { // Clean up + clearColumns(); ui->columnsTable->setRowCount(0); - columnCheckBoxes.clear(); - sortComboBoxes.clear(); - collateComboBoxes.clear(); totalColumns = tableColumns.size(); ui->columnsTable->setRowCount(totalColumns); int row = 0; - foreach (const QString& column, tableColumns) + for (const QString& column : tableColumns) buildColumn(column, row++); + + updateToolBarButtons(); } void IndexDialog::updateTable(const QString& value) @@ -166,15 +184,16 @@ void IndexDialog::updateValidation() { bool tableOk = ui->tableCombo->currentIndex() > 0; bool colSelected = false; + bool hasExprColumn = false; if (tableOk) { - foreach (QCheckBox* cb, columnCheckBoxes) + for (Column* col : columns.values()) { - if (cb->isChecked()) + if (col->getCheck()->isChecked()) { colSelected = true; - break; + hasExprColumn |= col->isExpr(); } } } @@ -182,11 +201,14 @@ void IndexDialog::updateValidation() bool partialConditionOk = (!ui->partialIndexCheck->isChecked() || (ui->partialIndexEdit->isSyntaxChecked() && !ui->partialIndexEdit->haveErrors())); + bool uniqueAndExprOk = !(ui->uniqueCheck->isChecked() && hasExprColumn); + + setValidState(ui->uniqueCheck, uniqueAndExprOk, tr("Unique index cannot have indexed expressions. Either remove expressions from list below, or uncheck this option.")); setValidState(ui->tableCombo, tableOk, tr("Pick the table for the index.")); setValidState(ui->columnsTable, colSelected, tr("Select at least one column.")); setValidState(ui->partialIndexCheck, partialConditionOk, tr("Enter a valid condition.")); - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(colSelected && partialConditionOk); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(colSelected && partialConditionOk && uniqueAndExprOk); } void IndexDialog::setTable(const QString& value) @@ -207,9 +229,30 @@ void IndexDialog::readCollations() void IndexDialog::buildColumn(const QString& name, int row) { - int col = 0; + Column* column = new Column(name, ui->columnsTable); + buildColumn(column, row); +} + +IndexDialog::Column* IndexDialog::buildColumn(SqliteOrderBy* orderBy, int row) +{ + SqliteExpr* expr = dynamic_cast<SqliteExpr*>(orderBy->expr->clone()); + return buildColumn(expr, row); +} + +IndexDialog::Column* IndexDialog::buildColumn(SqliteExpr* expr, int row) +{ + Column* column = new Column(expr, ui->columnsTable); + buildColumn(column, row); + return column; +} - QWidget* checkParent = new QWidget(); +void IndexDialog::buildColumn(Column* column, int row) +{ + QString key = column->getKey(); + columns[key] = column; + columnsByRow << column; + + column->setCheckParent(new QWidget()); QHBoxLayout* layout = new QHBoxLayout(); QMargins margins = layout->contentsMargins(); margins.setTop(0); @@ -217,51 +260,45 @@ void IndexDialog::buildColumn(const QString& name, int row) margins.setLeft(4); margins.setRight(4); layout->setContentsMargins(margins); - checkParent->setLayout(layout); + column->getCheckParent()->setLayout(layout); - QCheckBox* check = new QCheckBox(name); - checkParent->layout()->addWidget(check); + column->setCheck(new QCheckBox(key)); + column->getCheckParent()->layout()->addWidget(column->getCheck()); - ui->columnsTable->setCellWidget(row, col++, checkParent); - columnStateSignalMapping->setMapping(check, row); - connect(check, SIGNAL(toggled(bool)), columnStateSignalMapping, SLOT(map())); - connect(check, SIGNAL(toggled(bool)), this, SLOT(updateValidation())); - columnCheckBoxes << check; + columnStateSignalMapping->setMapping(column->getCheck(), key); + connect(column->getCheck(), SIGNAL(toggled(bool)), columnStateSignalMapping, SLOT(map())); + connect(column->getCheck(), SIGNAL(toggled(bool)), this, SLOT(updateValidation())); - QComboBox* collation = nullptr; if (db->getDialect() == Dialect::Sqlite3) { - collation = new QComboBox(); - collation->setEditable(true); - collation->lineEdit()->setPlaceholderText(tr("default", "index dialog")); - collation->setModel(&collations); - ui->columnsTable->setCellWidget(row, col++, collation); - collateComboBoxes << collation; - } - else - { - col++; + column->setCollation(new QComboBox()); + column->getCollation()->setEditable(true); + column->getCollation()->lineEdit()->setPlaceholderText(tr("default", "index dialog")); + column->getCollation()->setModel(&collations); } - QComboBox* sortOrder = new QComboBox(); - sortOrder->setToolTip(tr("Sort order", "table constraints")); - ui->columnsTable->setCellWidget(row, col++, sortOrder); - sortComboBoxes << sortOrder; + column->setSort(new QComboBox()); + column->getSort()->setToolTip(tr("Sort order", "table constraints")); QStringList sortList = {"", sqliteSortOrder(SqliteSortOrder::ASC), sqliteSortOrder(SqliteSortOrder::DESC)}; - sortOrder->addItems(sortList); + column->getSort()->addItems(sortList); + + column->prepareForNewRow(); + column->assignToNewRow(row); totalColumns++; - updateColumnState(row); + updateColumnState(key); } -void IndexDialog::updateColumnState(int row) +void IndexDialog::updateColumnState(const QString& columnKey) { - bool enabled = columnCheckBoxes[row]->isChecked(); - sortComboBoxes[row]->setEnabled(enabled); - if (db->getDialect() == Dialect::Sqlite3) - collateComboBoxes[row]->setEnabled(enabled); + Column* col = columns[columnKey]; + + bool enabled = col->getCheck()->isChecked(); + col->getSort()->setEnabled(enabled); + if (col->hasCollation()) + col->getCollation()->setEnabled(enabled); } void IndexDialog::updatePartialConditionState() @@ -283,26 +320,204 @@ void IndexDialog::tabChanged(int tab) updateDdl(); } +void IndexDialog::moveColumnUp() +{ + QModelIndex idx = ui->columnsTable->selectionModel()->currentIndex(); + if (!idx.isValid()) + return; + + int row = idx.row(); + if (row <= 0) + return; + + columnsByRow.move(row, row - 1); + rebuildColumnsByNewOrder(); + + idx = ui->columnsTable->model()->index(row - 1, 0); + ui->columnsTable->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); +} + +void IndexDialog::moveColumnDown() +{ + QModelIndex idx = ui->columnsTable->selectionModel()->currentIndex(); + if (!idx.isValid()) + return; + + int row = idx.row(); + int cols = tableColumns.size(); + + if ((row + 1) >= cols) + return; + + columnsByRow.move(row, row + 1); + rebuildColumnsByNewOrder(); + + idx = ui->columnsTable->model()->index(row + 1, 0); + ui->columnsTable->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); +} + +void IndexDialog::updateToolBarButtons(const QModelIndex& idx) +{ + if (!idx.isValid()) + { + ui->editExprColumnButton->setEnabled(false); + ui->delExprColumnButton->setEnabled(false); + ui->moveUpButton->setEnabled(false); + ui->moveDownButton->setEnabled(false); + return; + } + + int row = idx.row(); + int cols = tableColumns.size(); + ui->moveUpButton->setEnabled(row > 0); + ui->moveDownButton->setEnabled((row + 1) < cols); + + bool isExpr = columnsByRow[row]->isExpr(); + ui->editExprColumnButton->setEnabled(isExpr); + ui->delExprColumnButton->setEnabled(isExpr); +} + +void IndexDialog::addExprColumn() +{ + IndexExprColumnDialog dialog(db, this); + dialog.setExistingExprColumnKeys(getExistingColumnExprs()); + dialog.setTableColumns(getTableColumns()); + if (!dialog.exec()) + return; + + SqliteExpr* expr = dialog.getColumn(); + if (!expr) + { + qCritical() << "Null expr in IndexDialog::addExprColumn(). Aborting."; + return; + } + + int row = columnsByRow.size(); + ui->columnsTable->insertRow(row); + + Column* col = buildColumn(expr, row); + col->getCheck()->setChecked(true); + rebuildColumnsByNewOrder(); + + ui->columnsTable->scrollToBottom(); + updateValidation(); +} + +void IndexDialog::editExprColumn(int row) +{ + if (row < 0) + row = ui->columnsTable->currentRow(); + + if (row < 0 || row >= columnsByRow.size()) + { + qWarning() << "IndexDialog::editExprColumn() called for row out of bounds:" << row << "while there are" << columnsByRow.size() << "rows."; + return; + } + + Column* col = columnsByRow[row]; + if (!col->isExpr()) + { + qWarning() << "IndexDialog::editExprColumn() called for non-expr index column."; + return; + } + + IndexExprColumnDialog dialog(db, col->getExpr(), this); + dialog.setExistingExprColumnKeys(getExistingColumnExprs(col->getKey())); + dialog.setTableColumns(getTableColumns()); + if (!dialog.exec()) + return; + + SqliteExpr* expr = dialog.getColumn(); + if (!expr) + { + qCritical() << "Null expr in IndexDialog::editExprColumn(). Aborting."; + return; + } + + QString oldKey = col->getKey(); + col->setExpr(expr); + QString newKey = col->getKey(); + + columns.remove(oldKey); + columns[newKey] = col; + + col->getCheck()->setText(newKey); + col->getCheck()->setChecked(true); + rebuildColumnsByNewOrder(); + updateValidation(); +} + +void IndexDialog::delExprColumn() +{ + int row = ui->columnsTable->currentRow(); + + if (row < 0 || row >= columnsByRow.size()) + { + qWarning() << "IndexDialog::delExprColumn() called for row out of bounds:" << row << "while there are" << columnsByRow.size() << "rows."; + return; + } + + Column* col = columnsByRow[row]; + if (!col->isExpr()) + { + qWarning() << "IndexDialog::delExprColumn() called for non-expr index column."; + return; + } + + // Removes all widgets in the row + ui->columnsTable->removeRow(row); + + columnsByRow.removeOne(col); + columns.remove(col->getKey()); + delete col; + + rebuildColumnsByNewOrder(); + updateValidation(); +} + void IndexDialog::applyColumnValues() { - Dialect dialect = db->getDialect(); - int row; - foreach (SqliteIndexedColumn* idxCol, createIndex->indexedColumns) + Column* column = nullptr; + QString key; + int row = 0; + int totalRows = tableColumns.size(); + bool orderChanged = false; + for (SqliteOrderBy* idxCol : createIndex->indexedColumns) { - row = indexOf(tableColumns, idxCol->name, Qt::CaseInsensitive); - if (row == -1) + key = getKey(idxCol); + + if (idxCol->isSimpleColumn()) { - qCritical() << "Cannot find column from index in the table columns! Indexed column:" << idxCol->name - << ", table columns:" << tableColumns << ", index name:" << index << ", table name:" << table; - continue; + column = columns[key]; + if (!column) + { + qCritical() << "Cannot find column by name or expression! Column name/expression:" << key + << ", available columns:" << columns.keys() << ", index name:" << index; + continue; + } + } + else + column = buildColumn(idxCol, totalRows++); + + column->getCheck()->setChecked(true); + updateColumnState(key); + column->getSort()->setCurrentText(sqliteSortOrder(idxCol->order)); + if (column->hasCollation()) + column->getCollation()->setCurrentText(idxCol->getCollation()); + + // Setting proper order + int currentRow = columnsByRow.indexOf(column); + if (currentRow != row) + { + columnsByRow.move(currentRow, row); + orderChanged = true; } - columnCheckBoxes[row]->setChecked(true); - updateColumnState(row); - sortComboBoxes[row]->setCurrentText(sqliteSortOrder(idxCol->sortOrder)); - if (dialect == Dialect::Sqlite3) - collateComboBoxes[row]->setCurrentText(idxCol->collate); + row++; } + + if (orderChanged) + rebuildColumnsByNewOrder(); } void IndexDialog::applyIndex() @@ -315,15 +530,43 @@ void IndexDialog::applyIndex() ui->partialIndexEdit->setPlainText(createIndex->where->detokenize()); } -SqliteIndexedColumn* IndexDialog::addIndexedColumn(const QString& name) +SqliteOrderBy* IndexDialog::addIndexedColumn(const QString& name) +{ + SqliteOrderBy* idxCol = new SqliteOrderBy(); + idxCol->setParent(createIndex.data()); + + SqliteExpr* expr = new SqliteExpr(); + expr->initId(name); + idxCol->expr = expr; + expr->setParent(idxCol); + + createIndex->indexedColumns << idxCol; + return idxCol; +} + +SqliteOrderBy* IndexDialog::addIndexedColumn(SqliteExpr* expr) { - SqliteIndexedColumn* idxCol = new SqliteIndexedColumn(); - idxCol->name = name; + SqliteOrderBy* idxCol = new SqliteOrderBy(); idxCol->setParent(createIndex.data()); + + SqliteExpr* clonedExpr = dynamic_cast<SqliteExpr*>(expr->clone()); + idxCol->expr = clonedExpr; + clonedExpr->setParent(idxCol); + createIndex->indexedColumns << idxCol; return idxCol; } +void IndexDialog::addCollation(SqliteOrderBy* col, const QString& name) +{ + SqliteExpr* expr = new SqliteExpr(); + col->expr->setParent(expr); + expr->initCollate(col->expr, name); + expr->setParent(col); + + col->expr = expr; +} + void IndexDialog::rebuildCreateIndex() { createIndex = SqliteCreateIndexPtr::create(); @@ -333,22 +576,22 @@ void IndexDialog::rebuildCreateIndex() createIndex->uniqueKw = ui->uniqueCheck->isChecked(); - Dialect dialect = db->getDialect(); - SqliteIndexedColumn* idxCol = nullptr; - int i = -1; - for (const QString& column : tableColumns) + SqliteOrderBy* idxCol = nullptr; + for (Column* column : columnsByRow) { - i++; - - if (!columnCheckBoxes[i]->isChecked()) + if (!column->getCheck()->isChecked()) continue; - idxCol = addIndexedColumn(column); - if (dialect == Dialect::Sqlite3 && !collateComboBoxes[i]->currentText().isEmpty()) - idxCol->collate = collateComboBoxes[i]->currentText(); + if (column->isExpr()) + idxCol = addIndexedColumn(column->getExpr()); + else + idxCol = addIndexedColumn(column->getName()); + + if (column->hasCollation() && !column->getCollation()->currentText().isEmpty()) + addCollation(idxCol, column->getCollation()->currentText()); - if (sortComboBoxes[i]->currentIndex() > 0) - idxCol->sortOrder = sqliteSortOrder(sortComboBoxes[i]->currentText()); + if (column->getSort()->currentIndex() > 0) + idxCol->order = sqliteSortOrder(column->getSort()->currentText()); } if (ui->partialIndexCheck->isChecked()) @@ -388,10 +631,9 @@ void IndexDialog::queryDuplicates() QStringList countCols; QString wrappedCol; QString countColName; - int i = 0; for (const QString& column : tableColumns) { - if (!columnCheckBoxes[i++]->isChecked()) + if (!columns[column]->getCheck()->isChecked()) continue; wrappedCol = wrapObjIfNeeded(column, dialect); @@ -413,6 +655,62 @@ void IndexDialog::queryDuplicates() editor->execute(); } +void IndexDialog::clearColumns() +{ + for (Column* c : columns.values()) + delete c; + + columns.clear(); + columnsByRow.clear(); +} + +void IndexDialog::rebuildColumnsByNewOrder() +{ + int row = 0; + for (Column* column : columnsByRow) + { + column->prepareForNewRow(); + column->assignToNewRow(row++); + } +} + +QString IndexDialog::getKey(SqliteOrderBy* col) const +{ + if (col->isSimpleColumn()) + return col->getColumnName(); + + return col->expr->tokens.filterWhiteSpaces(false).detokenize(); +} + +QStringList IndexDialog::getExistingColumnExprs(const QString& exceptThis) const +{ + QString key; + QStringList exprs; + for (Column* col : columnsByRow) + { + if (col->isExpr()) + { + key = col->getKey(); + if (!exceptThis.isNull() && key == exceptThis) + continue; + + exprs << key; + } + } + return exprs; +} + +QStringList IndexDialog::getTableColumns() const +{ + QStringList cols; + for (Column* col : columnsByRow) + { + if (!col->isExpr()) + cols << col->getKey(); + } + return cols; +} + void IndexDialog::accept() { rebuildCreateIndex(); @@ -468,3 +766,125 @@ void IndexDialog::accept() .arg(executor.getErrorsMessages().join(",\n")), QMessageBox::Ok); } } + +IndexDialog::Column::Column(const QString& name, QTableWidget* table) +{ + this->name = name; + this->table = table; +} + +IndexDialog::Column::Column(SqliteExpr* expr, QTableWidget* table) +{ + this->expr = expr; + this->table = table; +} + +IndexDialog::Column::~Column() +{ + safe_delete(expr); +} + +void IndexDialog::Column::assignToNewRow(int row) +{ + table->setCellWidget(row, 0, column1Contrainer); + table->setCellWidget(row, 1, column2Contrainer); + table->setCellWidget(row, 2, column3Contrainer); +} + +void IndexDialog::Column::prepareForNewRow() +{ + column1Contrainer = defineContainer(checkParent); + column2Contrainer = defineContainer(sort); + if (collation) + column3Contrainer = defineContainer(collation); +} + +QCheckBox* IndexDialog::Column::getCheck() const +{ + return check; +} + +void IndexDialog::Column::setCheck(QCheckBox* cb) +{ + check = cb; +} + +QWidget* IndexDialog::Column::getCheckParent() const +{ + return checkParent; +} + +void IndexDialog::Column::setCheckParent(QWidget* w) +{ + checkParent = w; +} + +QComboBox* IndexDialog::Column::getSort() const +{ + return sort; +} + +void IndexDialog::Column::setSort(QComboBox* cb) +{ + sort = cb; +} + +QComboBox* IndexDialog::Column::getCollation() const +{ + return collation; +} + +void IndexDialog::Column::setCollation(QComboBox* cb) +{ + collation = cb; +} + +bool IndexDialog::Column::hasCollation() const +{ + return collation != nullptr; +} + +QString IndexDialog::Column::getName() const +{ + return name; +} + +SqliteExpr* IndexDialog::Column::getExpr() const +{ + return expr; +} + +void IndexDialog::Column::setExpr(SqliteExpr* expr) +{ + safe_delete(this->expr); + this->expr = expr; +} + +bool IndexDialog::Column::isExpr() const +{ + return expr != nullptr; +} + +QString IndexDialog::Column::getKey() const +{ + if (expr) + return expr->tokens.filterWhiteSpaces(false).detokenize(); + else + return name; +} + +QWidget* IndexDialog::Column::defineContainer(QWidget* w) +{ + QHBoxLayout* layout = new QHBoxLayout(); + QMargins margins = layout->contentsMargins(); + margins.setTop(0); + margins.setBottom(0); + margins.setLeft(0); + margins.setRight(0); + layout->setContentsMargins(margins); + + QWidget* container = new QWidget(); + container->setLayout(layout); + container->layout()->addWidget(w); + return container; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h index 1f9d1f8..f29b651 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h @@ -4,6 +4,7 @@ #include "db/db.h" #include "guiSQLiteStudio_global.h" #include "parser/ast/sqlitecreateindex.h" +#include "common/strhash.h" #include <QDialog> #include <QStringListModel> @@ -15,6 +16,7 @@ class QGridLayout; class QSignalMapper; class QCheckBox; class QComboBox; +class QTableWidget; class GUI_API_EXPORT IndexDialog : public QDialog { @@ -31,15 +33,65 @@ class GUI_API_EXPORT IndexDialog : public QDialog void changeEvent(QEvent *e); private: + class Column + { + public: + Column(const QString& name, QTableWidget* table); + Column(SqliteExpr* expr, QTableWidget* table); + ~Column(); + + void assignToNewRow(int row); + void prepareForNewRow(); + QCheckBox* getCheck() const; + void setCheck(QCheckBox* cb); + QWidget* getCheckParent() const; + void setCheckParent(QWidget* w); + QComboBox* getSort() const; + void setSort(QComboBox* cb); + QComboBox* getCollation() const; + void setCollation(QComboBox* cb); + bool hasCollation() const; + + QString getName() const; + SqliteExpr* getExpr() const; + void setExpr(SqliteExpr* expr); + bool isExpr() const; + QString getKey() const; + + private: + QWidget* defineContainer(QWidget* w); + + QWidget* column1Contrainer = nullptr; + QWidget* column2Contrainer = nullptr; + QWidget* column3Contrainer = nullptr; + QWidget* checkParent = nullptr; + QCheckBox* check = nullptr; + QComboBox* sort = nullptr; + QComboBox* collation = nullptr; + QTableWidget* table = nullptr; + QString name; + SqliteExpr* expr = nullptr; + }; + void init(); void readIndex(); void readCollations(); void buildColumn(const QString& name, int row); + Column* buildColumn(SqliteOrderBy* orderBy, int row); + Column* buildColumn(SqliteExpr* expr, int row); + void buildColumn(Column* column, int row); void applyColumnValues(); void applyIndex(); - SqliteIndexedColumn* addIndexedColumn(const QString& name); + SqliteOrderBy* addIndexedColumn(const QString& name); + SqliteOrderBy* addIndexedColumn(SqliteExpr* expr); + void addCollation(SqliteOrderBy* col, const QString& name); void rebuildCreateIndex(); void queryDuplicates(); + void clearColumns(); + void rebuildColumnsByNewOrder(); + QString getKey(SqliteOrderBy* col) const; + QStringList getExistingColumnExprs(const QString& exceptThis = QString()) const; + QStringList getTableColumns() const; bool existingIndex = false; Db* db = nullptr; @@ -50,9 +102,8 @@ class GUI_API_EXPORT IndexDialog : public QDialog QStringList tableColumns; QSignalMapper* columnStateSignalMapping = nullptr; QStringListModel collations; - QList<QCheckBox*> columnCheckBoxes; - QList<QComboBox*> sortComboBoxes; - QList<QComboBox*> collateComboBoxes; + StrHash<Column*> columns; + QList<Column*> columnsByRow; int totalColumns = 0; Ui::IndexDialog *ui = nullptr; @@ -60,10 +111,16 @@ class GUI_API_EXPORT IndexDialog : public QDialog void updateValidation(); void buildColumns(); void updateTable(const QString& value); - void updateColumnState(int row); + void updateColumnState(const QString& columnKey); void updatePartialConditionState(); void updateDdl(); void tabChanged(int tab); + void moveColumnUp(); + void moveColumnDown(); + void updateToolBarButtons(const QModelIndex& idx = QModelIndex()); + void addExprColumn(); + void editExprColumn(int row = -1); + void delExprColumn(); public slots: void accept(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.ui index 4e2cbac..36066c5 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.ui @@ -30,45 +30,10 @@ <string>Index</string> </attribute> <layout class="QGridLayout" name="gridLayout_2"> - <item row="1" column="0"> - <widget class="QLabel" name="tableLabel"> - <property name="text"> - <string>On table:</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="nameLabel"> - <property name="text"> - <string>Index name:</string> - </property> - </widget> - </item> - <item row="4" column="0" colspan="2"> - <widget class="QCheckBox" name="partialIndexCheck"> - <property name="text"> - <string>Partial index condition</string> - </property> - </widget> - </item> - <item row="5" column="0" colspan="2"> - <widget class="SqlEditor" name="partialIndexEdit"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>1</verstretch> - </sizepolicy> - </property> - </widget> - </item> - <item row="2" column="0" colspan="2"> - <widget class="QCheckBox" name="uniqueCheck"> - <property name="text"> - <string>Unique index</string> - </property> - </widget> + <item row="0" column="1" colspan="6"> + <widget class="QLineEdit" name="nameEdit"/> </item> - <item row="3" column="0" colspan="2"> + <item row="3" column="0" colspan="7"> <widget class="QTableWidget" name="columnsTable"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> @@ -76,8 +41,23 @@ <verstretch>2</verstretch> </sizepolicy> </property> + <property name="dragEnabled"> + <bool>false</bool> + </property> + <property name="dragDropOverwriteMode"> + <bool>true</bool> + </property> + <property name="dragDropMode"> + <enum>QAbstractItemView::NoDragDrop</enum> + </property> + <property name="defaultDropAction"> + <enum>Qt::IgnoreAction</enum> + </property> <property name="selectionMode"> - <enum>QAbstractItemView::NoSelection</enum> + <enum>QAbstractItemView::SingleSelection</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> </property> <property name="verticalScrollMode"> <enum>QAbstractItemView::ScrollPerPixel</enum> @@ -102,12 +82,97 @@ </column> </widget> </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="nameEdit"/> + <item row="1" column="0"> + <widget class="QLabel" name="tableLabel"> + <property name="text"> + <string>On table:</string> + </property> + </widget> </item> - <item row="1" column="1"> + <item row="1" column="1" colspan="6"> <widget class="QComboBox" name="tableCombo"/> </item> + <item row="2" column="4"> + <widget class="QToolButton" name="delExprColumnButton"> + <property name="toolTip"> + <string>Delete selected indexed expression</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="5"> + <widget class="QToolButton" name="moveUpButton"> + <property name="toolTip"> + <string>Moves selected index column up in the order, making it more significant in the index.</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="6"> + <widget class="QToolButton" name="moveDownButton"> + <property name="toolTip"> + <string>Moves selected index column down in the order, making it less significant in the index.</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="4" column="0" colspan="7"> + <widget class="QCheckBox" name="partialIndexCheck"> + <property name="text"> + <string>Partial index condition</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QCheckBox" name="uniqueCheck"> + <property name="text"> + <string>Unique index</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="nameLabel"> + <property name="text"> + <string>Index name:</string> + </property> + </widget> + </item> + <item row="2" column="3"> + <widget class="QToolButton" name="editExprColumnButton"> + <property name="toolTip"> + <string>Edit selected indexed expression</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="5" column="0" colspan="7"> + <widget class="SqlEditor" name="partialIndexEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QToolButton" name="addExprColumnButton"> + <property name="toolTip"> + <string>Add indexed expression</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> </layout> </widget> <widget class="QWidget" name="ddlTab"> diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.cpp new file mode 100644 index 0000000..f041294 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.cpp @@ -0,0 +1,151 @@ +#include "indexexprcolumndialog.h" +#include "ui_indexexprcolumndialog.h" +#include "parser/ast/sqliteexpr.h" +#include "db/db.h" +#include "uiutils.h" +#include "parser/parser.h" +#include "parser/ast/sqliteselect.h" +#include <QPushButton> + +IndexExprColumnDialog::IndexExprColumnDialog(Db* db, QWidget* parent) : + QDialog(parent), + ui(new Ui::IndexExprColumnDialog) +{ + ui->setupUi(this); + + this->db = db; + ui->sqlEditor->setDb(db); + ui->sqlEditor->setVirtualSqlExpression("CREATE INDEX idx ON tab (%1 COLLATE NOCASE ASC)"); + + connect(ui->sqlEditor, SIGNAL(textChanged()), this, SLOT(validate())); + connect(ui->sqlEditor, SIGNAL(errorsChecked(bool)), this, SLOT(validate())); +} + +IndexExprColumnDialog::IndexExprColumnDialog(Db* db, SqliteExpr* col, QWidget *parent) : + IndexExprColumnDialog(db, parent) +{ + readColumn(col); +} + +IndexExprColumnDialog::~IndexExprColumnDialog() +{ + delete ui; +} + +void IndexExprColumnDialog::readColumn(SqliteExpr* col) +{ + ui->sqlEditor->setPlainText(col->tokens.detokenize()); +} + +void IndexExprColumnDialog::setOkEnabled(bool enabled) +{ + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled); +} + +SqliteExpr* IndexExprColumnDialog::parseExpr() +{ + Parser parser(db->getDialect()); + return parser.parseExpr(ui->sqlEditor->toPlainText()); +} + +bool IndexExprColumnDialog::checkRestrictions(QString& errorMsg) +{ + SqliteExprPtr expr = SqliteExprPtr(parseExpr()); + if (!expr) + return false; + + QString key = expr->tokens.filterWhiteSpaces(false).detokenize(); + if (existingExprColumnKeys.contains(key)) + { + errorMsg = tr("This expression is already indexed by the index."); + return false; + } + + if (tableColumns.contains(key)) + { + errorMsg = tr("Column should be indexed directly, not by expression. Either extend this expression to contain something more " + "than just column name, or abort and select this column in index dialog directly."); + return false; + } + + QStringList usedColumns = expr->getContextColumns(false, true); + for (const QString& col : usedColumns) + { + if (!tableColumns.contains(col)) + { + errorMsg = tr("Column '%1' does not belong to the table covered by this index. Indexed expressions can refer only to columns from the indexed table.").arg(col); + return false; + } + } + + QList<SqliteSelect*> selects = expr->getAllTypedStatements<SqliteSelect>(); + if (!selects.isEmpty()) + { + errorMsg = tr("It's forbidden to use 'SELECT' statements in indexed expressions."); + return false; + } + + return true; +} + +void IndexExprColumnDialog::setExistingExprColumnKeys(const QStringList& value) +{ + existingExprColumnKeys = value; +} + +void IndexExprColumnDialog::setTableColumns(const QStringList& value) +{ + tableColumns = value; +} + +void IndexExprColumnDialog::validate() +{ + if (!ui->sqlEditor->isSyntaxChecked()) + { + setValidState(ui->sqlEditor, false, tr("Enter an indexed expression.")); + setOkEnabled(false); + return; + } + + // First check if we already validated this text. + // This method is called twice, by both errorsChecked() and textChanged(). + QString text = ui->sqlEditor->toPlainText(); + if (!lastValidatedText.isNull() && lastValidatedText == text) + return; + + lastValidatedText = text; + + bool exprOk = !ui->sqlEditor->toPlainText().trimmed().isEmpty() && !ui->sqlEditor->haveErrors(); + QString errorMsg = tr("Invalid expression."); + if (exprOk) + exprOk = checkRestrictions(errorMsg); + + setValidState(ui->sqlEditor, exprOk, errorMsg); + setOkEnabled(exprOk); +} + +SqliteExpr* IndexExprColumnDialog::getColumn() const +{ + return theColumn; +} + +void IndexExprColumnDialog::accept() +{ + SqliteExpr* expr = parseExpr(); + if (expr) + { + expr->rebuildTokens(); + theColumn = expr; + } + else + qCritical() << "Accepted IndexExprColumnDialog with unparsable expr! This should not happen. IndexDialog will get null expr."; + + QDialog::accept(); +} + + +int IndexExprColumnDialog::exec() +{ + ui->sqlEditor->checkSyntaxNow(); + return QDialog::exec(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.h new file mode 100644 index 0000000..5e4fde5 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.h @@ -0,0 +1,47 @@ +#ifndef INDEXEXPRCOLUMNDIALOG_H +#define INDEXEXPRCOLUMNDIALOG_H + +#include <QDialog> + +class SqliteExpr; +class Db; + +namespace Ui { + class IndexExprColumnDialog; +} + +class IndexExprColumnDialog : public QDialog +{ + Q_OBJECT + + public: + explicit IndexExprColumnDialog(Db* db, QWidget *parent = 0); + IndexExprColumnDialog(Db* db, SqliteExpr* col, QWidget *parent = 0); + ~IndexExprColumnDialog(); + + SqliteExpr* getColumn() const; + void setTableColumns(const QStringList& value); + void setExistingExprColumnKeys(const QStringList& value); + + private: + void readColumn(SqliteExpr* col); + void setOkEnabled(bool enabled); + SqliteExpr* parseExpr(); + bool checkRestrictions(QString& errorMsg); + + Ui::IndexExprColumnDialog *ui; + SqliteExpr* theColumn = nullptr; + QString lastValidatedText; + Db* db = nullptr; + QStringList tableColumns; + QStringList existingExprColumnKeys; + + public slots: + void accept(); + int exec(); + + private slots: + void validate(); +}; + +#endif // INDEXEXPRCOLUMNDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.ui new file mode 100644 index 0000000..4df8486 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.ui @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>IndexExprColumnDialog</class> + <widget class="QDialog" name="IndexExprColumnDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Indexed expression</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QGroupBox" name="indexExprGroup"> + <property name="title"> + <string>Expression to index</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="SqlEditor" name="sqlEditor"/> + </item> + </layout> + </widget> + </item> + <item row="1" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>SqlEditor</class> + <extends>QPlainTextEdit</extends> + <header>sqleditor.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>IndexExprColumnDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>IndexExprColumnDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> |
