mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-20 02:50:46 -06:00
Merge branch 'master' into browser_formats
Necessary changes: - Added function `ExtendedTableWidget::colsInSelection` to emulate former behaviour of `ExtendedTableWidget::selectedCols`, which is needed by the format toolbar. - Added call to `ui->dataTable->scrollTo(match);` in TableBrowser::find, otherwise the scroll was not advancing to the current index. # Conflicts: # src/TableBrowser.cpp # src/TableBrowser.ui # src/icons/icons.qrc # src/sqlitetablemodel.cpp
This commit is contained in:
16
src/Data.cpp
16
src/Data.cpp
@@ -1,6 +1,9 @@
|
||||
#include "Data.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QImageReader>
|
||||
#include <QTextCodec>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// Note that these aren't all possible BOMs. But they are probably the most common ones.
|
||||
@@ -88,6 +91,19 @@ QByteArray removeBom(QByteArray& data)
|
||||
}
|
||||
}
|
||||
|
||||
QString isImageData(const QByteArray& data)
|
||||
{
|
||||
// Check if it's an image. First do a quick test by calling canRead() which only checks the first couple of bytes or so. Only if
|
||||
// that returned true, do a more sophisticated test of the data. This way we get both, good performance and proper data checking.
|
||||
QBuffer imageBuffer(const_cast<QByteArray*>(&data));
|
||||
QImageReader readerBuffer(&imageBuffer);
|
||||
QString imageFormat = readerBuffer.format();
|
||||
if(readerBuffer.canRead() && !readerBuffer.read().isNull())
|
||||
return imageFormat;
|
||||
else
|
||||
return QString();
|
||||
}
|
||||
|
||||
QStringList toStringList(const QList<QByteArray>& list) {
|
||||
QStringList strings;
|
||||
for (const QByteArray &item : list) {
|
||||
|
||||
@@ -21,6 +21,9 @@ bool startsWithBom(const QByteArray& data);
|
||||
// with a BOM an empty byte array is returned and the original data is not modified.
|
||||
QByteArray removeBom(QByteArray& data);
|
||||
|
||||
// Check if a byte array contains an image. Returns the name of the image format for images or a null string for non-image data.
|
||||
QString isImageData(const QByteArray& data);
|
||||
|
||||
QStringList toStringList(const QList<QByteArray>& list);
|
||||
|
||||
QByteArray encodeString(const QByteArray& str, const QString& encoding);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <QKeySequence>
|
||||
#include <QShortcut>
|
||||
#include <QImageReader>
|
||||
#include <QBuffer>
|
||||
#include <QModelIndex>
|
||||
#include <QtXml/QDomDocument>
|
||||
#include <QMessageBox>
|
||||
@@ -856,12 +855,9 @@ int EditDialog::checkDataType(const QByteArray& bArrdata)
|
||||
return Null;
|
||||
}
|
||||
|
||||
// Check if it's an image. First do a quick test by calling canRead() which only checks the first couple of bytes or so. Only if
|
||||
// that returned true, do a more sophisticated test of the data. This way we get both, good performance and proper data checking.
|
||||
QBuffer imageBuffer(&cellData);
|
||||
QImageReader readerBuffer(&imageBuffer);
|
||||
QString imageFormat = readerBuffer.format();
|
||||
if(readerBuffer.canRead() && !readerBuffer.read().isNull())
|
||||
// Check if it's an image
|
||||
QString imageFormat = isImageData(cellData);
|
||||
if(!imageFormat.isNull())
|
||||
return imageFormat == "svg" ? SVG : Image;
|
||||
|
||||
// Check if it's text only
|
||||
|
||||
@@ -374,7 +374,6 @@ void EditTableDialog::checkInput()
|
||||
if (normTableName != m_table.name()) {
|
||||
const std::string oldTableName = m_table.name();
|
||||
m_table.setName(normTableName);
|
||||
m_fkEditorDelegate->updateTablesList(oldTableName);
|
||||
|
||||
// update fk's that refer to table itself recursively
|
||||
const auto& fields = m_table.fields;
|
||||
@@ -505,6 +504,7 @@ void EditTableDialog::fieldItemChanged(QTreeWidgetItem *item, int column)
|
||||
field.setName(item->text(column).toStdString());
|
||||
m_table.renameKeyInAllConstraints(oldFieldName.toStdString(), item->text(column).toStdString());
|
||||
qobject_cast<QComboBox*>(ui->treeWidget->itemWidget(item, kType))->setProperty("column", item->text(column));
|
||||
qobject_cast<QComboBox*>(ui->treeWidget->itemWidget(item, kCollation))->setProperty("column", item->text(column));
|
||||
|
||||
// Update the field name in the map of old column names to new column names
|
||||
if(!m_bNewTable)
|
||||
@@ -845,24 +845,46 @@ void EditTableDialog::fieldSelectionChanged()
|
||||
if(hasSelection)
|
||||
{
|
||||
ui->buttonMoveUp->setEnabled(ui->treeWidget->selectionModel()->currentIndex().row() != 0);
|
||||
ui->buttonMoveTop->setEnabled(ui->buttonMoveUp->isEnabled());
|
||||
ui->buttonMoveDown->setEnabled(ui->treeWidget->selectionModel()->currentIndex().row() != ui->treeWidget->topLevelItemCount() - 1);
|
||||
ui->buttonMoveBottom->setEnabled(ui->buttonMoveDown->isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
void EditTableDialog::moveUp()
|
||||
{
|
||||
moveCurrentField(false);
|
||||
moveCurrentField(MoveUp);
|
||||
}
|
||||
|
||||
void EditTableDialog::moveDown()
|
||||
{
|
||||
moveCurrentField(true);
|
||||
moveCurrentField(MoveDown);
|
||||
}
|
||||
|
||||
void EditTableDialog::moveCurrentField(bool down)
|
||||
void EditTableDialog::moveTop()
|
||||
{
|
||||
moveCurrentField(MoveTop);
|
||||
}
|
||||
|
||||
void EditTableDialog::moveBottom()
|
||||
{
|
||||
moveCurrentField(MoveBottom);
|
||||
}
|
||||
|
||||
void EditTableDialog::moveCurrentField(MoveFieldDirection dir)
|
||||
{
|
||||
int currentRow = ui->treeWidget->currentIndex().row();
|
||||
int newRow = currentRow + (down ? 1 : -1);
|
||||
int newRow;
|
||||
if(dir == MoveUp)
|
||||
newRow = currentRow - 1;
|
||||
else if(dir == MoveDown)
|
||||
newRow = currentRow + 1;
|
||||
else if(dir == MoveTop)
|
||||
newRow = 0;
|
||||
else if(dir == MoveBottom)
|
||||
newRow = ui->treeWidget->topLevelItemCount() - 1;
|
||||
else
|
||||
return;
|
||||
|
||||
// Save the comboboxes first by making copies
|
||||
QComboBox* newCombo[2];
|
||||
@@ -891,7 +913,9 @@ void EditTableDialog::moveCurrentField(bool down)
|
||||
ui->treeWidget->setCurrentIndex(ui->treeWidget->currentIndex().sibling(newRow, 0));
|
||||
|
||||
// Finally update the table SQL
|
||||
std::swap(m_table.fields[static_cast<size_t>(newRow)], m_table.fields[static_cast<size_t>(currentRow)]);
|
||||
sqlb::Field temp = m_table.fields[static_cast<size_t>(currentRow)];
|
||||
m_table.fields.erase(m_table.fields.begin() + currentRow);
|
||||
m_table.fields.insert(m_table.fields.begin() + newRow, temp);
|
||||
|
||||
// Update the SQL preview
|
||||
updateSqlText();
|
||||
|
||||
@@ -50,10 +50,18 @@ private:
|
||||
kConstraintSql = 3
|
||||
};
|
||||
|
||||
enum MoveFieldDirection
|
||||
{
|
||||
MoveUp,
|
||||
MoveDown,
|
||||
MoveTop,
|
||||
MoveBottom
|
||||
};
|
||||
|
||||
void updateColumnWidth();
|
||||
void updateSqlText();
|
||||
|
||||
void moveCurrentField(bool down);
|
||||
void moveCurrentField(MoveFieldDirection dir);
|
||||
|
||||
private slots:
|
||||
void populateFields();
|
||||
@@ -71,6 +79,8 @@ private slots:
|
||||
void updateTypeAndCollation();
|
||||
void moveUp();
|
||||
void moveDown();
|
||||
void moveTop();
|
||||
void moveBottom();
|
||||
void setWithoutRowid(bool without_rowid);
|
||||
void changeSchema(const QString& schema);
|
||||
void removeConstraint();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>650</width>
|
||||
<width>652</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -111,7 +111,7 @@
|
||||
<item>
|
||||
<widget class="QToolButton" name="addFieldButton">
|
||||
<property name="text">
|
||||
<string>Add field</string>
|
||||
<string>Add</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
@@ -131,7 +131,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove field</string>
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
@@ -145,13 +145,36 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="buttonMoveTop">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move to top</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/arrow_top</normaloff>:/icons/arrow_top</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="arrowType">
|
||||
<enum>Qt::NoArrow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="buttonMoveUp">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move field up</string>
|
||||
<string>Move up</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
@@ -171,7 +194,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move field down</string>
|
||||
<string>Move down</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
@@ -185,6 +208,26 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="buttonMoveBottom">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move to bottom</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/arrow_bottom</normaloff>:/icons/arrow_bottom</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@@ -275,7 +318,7 @@
|
||||
<string>Check constraint</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Collation</string>
|
||||
</property>
|
||||
@@ -463,12 +506,18 @@
|
||||
<tabstop>buttonMore</tabstop>
|
||||
<tabstop>comboSchema</tabstop>
|
||||
<tabstop>checkWithoutRowid</tabstop>
|
||||
<tabstop>groupDefinition</tabstop>
|
||||
<tabstop>addFieldButton</tabstop>
|
||||
<tabstop>removeFieldButton</tabstop>
|
||||
<tabstop>buttonMoveTop</tabstop>
|
||||
<tabstop>buttonMoveUp</tabstop>
|
||||
<tabstop>buttonMoveDown</tabstop>
|
||||
<tabstop>buttonMoveBottom</tabstop>
|
||||
<tabstop>treeWidget</tabstop>
|
||||
<tabstop>sqlTextEdit</tabstop>
|
||||
<tabstop>buttonAddConstraint</tabstop>
|
||||
<tabstop>buttonRemoveConstraint</tabstop>
|
||||
<tabstop>tableConstraints</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="icons/icons.qrc"/>
|
||||
@@ -529,8 +578,8 @@
|
||||
<slot>addField()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>94</x>
|
||||
<y>253</y>
|
||||
<x>78</x>
|
||||
<y>255</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>79</x>
|
||||
@@ -545,8 +594,8 @@
|
||||
<slot>removeField()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>222</x>
|
||||
<y>253</y>
|
||||
<x>167</x>
|
||||
<y>255</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>249</x>
|
||||
@@ -613,8 +662,8 @@
|
||||
<y>78</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>117</x>
|
||||
<y>176</y>
|
||||
<x>138</x>
|
||||
<y>172</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
@@ -625,8 +674,8 @@
|
||||
<slot>setWithoutRowid(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>344</x>
|
||||
<y>143</y>
|
||||
<x>485</x>
|
||||
<y>160</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>324</x>
|
||||
@@ -657,8 +706,8 @@
|
||||
<slot>changeSchema(QString)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>186</x>
|
||||
<y>155</y>
|
||||
<x>327</x>
|
||||
<y>139</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>647</x>
|
||||
@@ -673,8 +722,8 @@
|
||||
<slot>removeConstraint()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>186</x>
|
||||
<y>155</y>
|
||||
<x>295</x>
|
||||
<y>255</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>647</x>
|
||||
@@ -682,6 +731,38 @@
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonMoveTop</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>EditTableDialog</receiver>
|
||||
<slot>moveTop()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>207</x>
|
||||
<y>240</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>202</x>
|
||||
<y>190</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonMoveBottom</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>EditTableDialog</receiver>
|
||||
<slot>moveBottom()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>530</x>
|
||||
<y>246</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>400</x>
|
||||
<y>186</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>fieldSelectionChanged()</slot>
|
||||
@@ -695,5 +776,7 @@
|
||||
<slot>setWithoutRowid(bool)</slot>
|
||||
<slot>changeSchema(QString)</slot>
|
||||
<slot>removeConstraint()</slot>
|
||||
<slot>moveTop()</slot>
|
||||
<slot>moveBottom()</slot>
|
||||
</slots>
|
||||
</ui>
|
||||
|
||||
@@ -844,11 +844,19 @@ int ExtendedTableWidget::numVisibleRows() const
|
||||
std::unordered_set<int> ExtendedTableWidget::selectedCols() const
|
||||
{
|
||||
std::unordered_set<int> selectedCols;
|
||||
for(const QModelIndex & idx : selectedIndexes())
|
||||
for(const auto& idx : selectionModel()->selectedColumns())
|
||||
selectedCols.insert(idx.column());
|
||||
return selectedCols;
|
||||
}
|
||||
|
||||
std::unordered_set<int> ExtendedTableWidget::colsInSelection() const
|
||||
{
|
||||
std::unordered_set<int> colsInSelection;
|
||||
for(const QModelIndex & idx : selectedIndexes())
|
||||
colsInSelection.insert(idx.column());
|
||||
return colsInSelection;
|
||||
}
|
||||
|
||||
void ExtendedTableWidget::cellClicked(const QModelIndex& index)
|
||||
{
|
||||
// If Ctrl-Shift is pressed try to jump to the row referenced by the foreign key of the clicked cell
|
||||
|
||||
@@ -52,7 +52,11 @@ public:
|
||||
FilterTableHeader* filterHeader() { return m_tableHeader; }
|
||||
|
||||
public:
|
||||
// Get set of selected columns (all cells in column has to be selected)
|
||||
std::unordered_set<int> selectedCols() const;
|
||||
// Get set of columns traversed by selection (only some cells in column has to be selected)
|
||||
std::unordered_set<int> colsInSelection() const;
|
||||
|
||||
int numVisibleRows() const;
|
||||
|
||||
void sortByColumns(const std::vector<sqlb::SortedColumn>& columns);
|
||||
|
||||
@@ -85,7 +85,8 @@ ForeignKeyEditorDelegate::ForeignKeyEditorDelegate(const DBBrowserDB& db, sqlb::
|
||||
{
|
||||
for(const auto& jt : it.second)
|
||||
{
|
||||
if(jt.second->type() == sqlb::Object::Types::Table)
|
||||
// Don't insert the current table into the list. The name and fields of the current table are always taken from the m_table reference
|
||||
if(jt.second->type() == sqlb::Object::Types::Table && jt.second->name() != m_table.name())
|
||||
m_tablesIds.insert({jt.second->name(), std::dynamic_pointer_cast<sqlb::Table>(jt.second)->fieldNames()});
|
||||
}
|
||||
}
|
||||
@@ -105,14 +106,25 @@ QWidget* ForeignKeyEditorDelegate::createEditor(QWidget* parent, const QStyleOpt
|
||||
QComboBox* box = editor->idsComboBox;
|
||||
box->clear();
|
||||
box->addItem(QString()); // for those heroes who don't like to specify key explicitly
|
||||
for(const auto& n : m_tablesIds[tableName.toStdString()])
|
||||
box->addItem(QString::fromStdString(n));
|
||||
|
||||
// For recursive foreign keys get the field list from the m_table reference. For other foreign keys from the prepared field lists.
|
||||
if(tableName.toStdString() == m_table.name())
|
||||
{
|
||||
for(const auto& n : m_table.fieldNames())
|
||||
box->addItem(QString::fromStdString(n));
|
||||
} else {
|
||||
for(const auto& n : m_tablesIds[tableName.toStdString()])
|
||||
box->addItem(QString::fromStdString(n));
|
||||
}
|
||||
|
||||
|
||||
box->setCurrentIndex(0);
|
||||
});
|
||||
|
||||
editor->tablesComboBox->clear();
|
||||
for(const auto& i : m_tablesIds)
|
||||
editor->tablesComboBox->addItem(QString::fromStdString(i.first));
|
||||
editor->tablesComboBox->addItem(QString::fromStdString(m_table.name())); // For recursive foreign keys
|
||||
|
||||
return editor;
|
||||
}
|
||||
@@ -175,12 +187,4 @@ void ForeignKeyEditorDelegate::updateEditorGeometry(QWidget* editor, const QStyl
|
||||
editor->setGeometry(option.rect);
|
||||
}
|
||||
|
||||
void ForeignKeyEditorDelegate::updateTablesList(const std::string& oldTableName)
|
||||
{
|
||||
// this is used for recursive table constraints when
|
||||
// table column references column within same table
|
||||
m_tablesIds.erase(oldTableName);
|
||||
m_tablesIds.insert({m_table.name(), m_table.fieldNames()});
|
||||
}
|
||||
|
||||
#include "ForeignKeyEditorDelegate.moc"
|
||||
|
||||
@@ -25,8 +25,6 @@ public:
|
||||
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
|
||||
void updateTablesList(const std::string& oldTableName);
|
||||
|
||||
private:
|
||||
const DBBrowserDB& m_db;
|
||||
sqlb::Table& m_table;
|
||||
|
||||
@@ -109,6 +109,7 @@ void PreferencesDialog::loadSettings()
|
||||
|
||||
ui->spinSymbolLimit->setValue(Settings::getValue("databrowser", "symbol_limit").toInt());
|
||||
ui->spinCompleteThreshold->setValue(Settings::getValue("databrowser", "complete_threshold").toInt());
|
||||
ui->checkShowImagesInline->setChecked(Settings::getValue("databrowser", "image_preview").toBool());
|
||||
ui->txtNull->setText(Settings::getValue("databrowser", "null_text").toString());
|
||||
ui->txtBlob->setText(Settings::getValue("databrowser", "blob_text").toString());
|
||||
ui->editFilterEscape->setText(Settings::getValue("databrowser", "filter_escape").toString());
|
||||
@@ -217,6 +218,7 @@ void PreferencesDialog::saveSettings()
|
||||
|
||||
Settings::setValue("databrowser", "font", ui->comboDataBrowserFont->currentText());
|
||||
Settings::setValue("databrowser", "fontsize", ui->spinDataBrowserFontSize->value());
|
||||
Settings::setValue("databrowser", "image_preview", ui->checkShowImagesInline->isChecked());
|
||||
saveColorSetting(ui->fr_null_fg, "null_fg");
|
||||
saveColorSetting(ui->fr_null_bg, "null_bg");
|
||||
saveColorSetting(ui->fr_reg_fg, "reg_fg");
|
||||
|
||||
@@ -821,6 +821,23 @@ Can be set to 0 for disabling completion.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_22">
|
||||
<property name="text">
|
||||
<string>Show images in cell</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>checkShowImagesInline</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="checkShowImagesInline">
|
||||
<property name="toolTip">
|
||||
<string>Enable this option to show a preview of BLOBs containing image data in the cells. This can affect the performance of the data browser, however.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -1866,10 +1883,12 @@ Can be set to 0 for disabling completion.</string>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>tabWidget</tabstop>
|
||||
<tabstop>comboDefaultLocation</tabstop>
|
||||
<tabstop>locationEdit</tabstop>
|
||||
<tabstop>setLocationButton</tabstop>
|
||||
<tabstop>languageComboBox</tabstop>
|
||||
<tabstop>appStyleCombo</tabstop>
|
||||
<tabstop>toolbarStyleComboMain</tabstop>
|
||||
<tabstop>toolbarStyleComboStructure</tabstop>
|
||||
<tabstop>toolbarStyleComboBrowse</tabstop>
|
||||
@@ -1877,6 +1896,7 @@ Can be set to 0 for disabling completion.</string>
|
||||
<tabstop>toolbarStyleComboEditCell</tabstop>
|
||||
<tabstop>checkUseRemotes</tabstop>
|
||||
<tabstop>checkUpdates</tabstop>
|
||||
<tabstop>buttonManageFileExtension</tabstop>
|
||||
<tabstop>encodingComboBox</tabstop>
|
||||
<tabstop>foreignKeysCheckBox</tabstop>
|
||||
<tabstop>checkHideSchemaLinebreaks</tabstop>
|
||||
@@ -1886,6 +1906,9 @@ Can be set to 0 for disabling completion.</string>
|
||||
<tabstop>editDatabaseDefaultSqlText</tabstop>
|
||||
<tabstop>comboDataBrowserFont</tabstop>
|
||||
<tabstop>spinDataBrowserFontSize</tabstop>
|
||||
<tabstop>spinSymbolLimit</tabstop>
|
||||
<tabstop>spinCompleteThreshold</tabstop>
|
||||
<tabstop>checkShowImagesInline</tabstop>
|
||||
<tabstop>fr_null_fg</tabstop>
|
||||
<tabstop>fr_null_bg</tabstop>
|
||||
<tabstop>txtNull</tabstop>
|
||||
@@ -1902,20 +1925,25 @@ Can be set to 0 for disabling completion.</string>
|
||||
<tabstop>spinEditorFontSize</tabstop>
|
||||
<tabstop>spinLogFontSize</tabstop>
|
||||
<tabstop>spinTabSize</tabstop>
|
||||
<tabstop>wrapComboBox</tabstop>
|
||||
<tabstop>quoteComboBox</tabstop>
|
||||
<tabstop>checkAutoCompletion</tabstop>
|
||||
<tabstop>checkCompleteUpper</tabstop>
|
||||
<tabstop>checkErrorIndicators</tabstop>
|
||||
<tabstop>checkHorizontalTiling</tabstop>
|
||||
<tabstop>listExtensions</tabstop>
|
||||
<tabstop>buttonAddExtension</tabstop>
|
||||
<tabstop>buttonRemoveExtension</tabstop>
|
||||
<tabstop>checkRegexDisabled</tabstop>
|
||||
<tabstop>editRemoteCloneDirectory</tabstop>
|
||||
<tabstop>buttonRemoteBrowseCloneDirectory</tabstop>
|
||||
<tabstop>tableCaCerts</tabstop>
|
||||
<tabstop>checkAllowLoadExtension</tabstop>
|
||||
<tabstop>tabCertificates</tabstop>
|
||||
<tabstop>tableClientCerts</tabstop>
|
||||
<tabstop>buttonClientCertAdd</tabstop>
|
||||
<tabstop>buttonClientCertRemove</tabstop>
|
||||
<tabstop>tabWidget</tabstop>
|
||||
<tabstop>buttonProxy</tabstop>
|
||||
<tabstop>editRemoteCloneDirectory</tabstop>
|
||||
<tabstop>buttonRemoteBrowseCloneDirectory</tabstop>
|
||||
<tabstop>tableCaCerts</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="icons/icons.qrc"/>
|
||||
@@ -1992,12 +2020,12 @@ Can be set to 0 for disabling completion.</string>
|
||||
<slot>setVisible(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>365</x>
|
||||
<y>207</y>
|
||||
<x>344</x>
|
||||
<y>230</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>108</x>
|
||||
<y>280</y>
|
||||
<x>119</x>
|
||||
<y>273</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
@@ -2008,8 +2036,8 @@ Can be set to 0 for disabling completion.</string>
|
||||
<slot>setVisible(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>365</x>
|
||||
<y>207</y>
|
||||
<x>344</x>
|
||||
<y>230</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>365</x>
|
||||
@@ -2024,8 +2052,8 @@ Can be set to 0 for disabling completion.</string>
|
||||
<slot>activateRemoteTab(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>161</x>
|
||||
<y>172</y>
|
||||
<x>231</x>
|
||||
<y>393</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>382</x>
|
||||
@@ -2040,8 +2068,8 @@ Can be set to 0 for disabling completion.</string>
|
||||
<slot>addClientCertificate()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>578</x>
|
||||
<y>315</y>
|
||||
<x>722</x>
|
||||
<y>110</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>596</x>
|
||||
@@ -2056,8 +2084,8 @@ Can be set to 0 for disabling completion.</string>
|
||||
<slot>removeClientCertificate()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>578</x>
|
||||
<y>353</y>
|
||||
<x>722</x>
|
||||
<y>139</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>597</x>
|
||||
@@ -2072,8 +2100,8 @@ Can be set to 0 for disabling completion.</string>
|
||||
<slot>chooseRemoteCloneDirectory()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>567</x>
|
||||
<y>54</y>
|
||||
<x>732</x>
|
||||
<y>538</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>595</x>
|
||||
@@ -2088,12 +2116,12 @@ Can be set to 0 for disabling completion.</string>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>474</x>
|
||||
<y>464</y>
|
||||
<x>642</x>
|
||||
<y>450</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>474</x>
|
||||
<y>492</y>
|
||||
<x>642</x>
|
||||
<y>480</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
@@ -2104,8 +2132,8 @@ Can be set to 0 for disabling completion.</string>
|
||||
<slot>on_buttonBox_clicked(QAbstractButton*)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>385</x>
|
||||
<y>591</y>
|
||||
<x>394</x>
|
||||
<y>584</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>385</x>
|
||||
@@ -2120,8 +2148,8 @@ Can be set to 0 for disabling completion.</string>
|
||||
<slot>configureProxy()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>385</x>
|
||||
<y>591</y>
|
||||
<x>225</x>
|
||||
<y>506</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>385</x>
|
||||
|
||||
@@ -208,6 +208,8 @@ QVariant Settings::getDefaultValue(const std::string& group, const std::string&
|
||||
return 5000;
|
||||
if(name == "complete_threshold")
|
||||
return 1000;
|
||||
if(name == "image_preview")
|
||||
return false;
|
||||
if(name == "indent_compact")
|
||||
return false;
|
||||
if(name == "auto_switch_mode")
|
||||
|
||||
@@ -130,31 +130,31 @@ TableBrowser::TableBrowser(QWidget* parent) :
|
||||
connect(ui->dataTable, &ExtendedTableWidget::selectedRowsToBeDeleted, this, &TableBrowser::deleteRecord);
|
||||
|
||||
connect(ui->fontComboBox, &QFontComboBox::currentFontChanged, this, [this](const QFont &font) {
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [font](CondFormat& format) { format.setFontFamily(font.family()); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [font](CondFormat& format) { format.setFontFamily(font.family()); });
|
||||
});
|
||||
connect(ui->fontSizeBox, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int pointSize) {
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [pointSize](CondFormat& format) { format.setFontPointSize(pointSize); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [pointSize](CondFormat& format) { format.setFontPointSize(pointSize); });
|
||||
});
|
||||
|
||||
connect(ui->actionFontColor, &QAction::triggered, this, [this]() {
|
||||
QColor color = QColorDialog::getColor(QColor(m_browseTableModel->data(currentIndex(), Qt::ForegroundRole).toString()));
|
||||
if(color.isValid())
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [color](CondFormat& format) { format.setForegroundColor(color); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [color](CondFormat& format) { format.setForegroundColor(color); });
|
||||
});
|
||||
connect(ui->actionBackgroundColor, &QAction::triggered, this, [this]() {
|
||||
QColor color = QColorDialog::getColor(QColor(m_browseTableModel->data(currentIndex(), Qt::BackgroundRole).toString()));
|
||||
if(color.isValid())
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [color](CondFormat& format) { format.setBackgroundColor(color); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [color](CondFormat& format) { format.setBackgroundColor(color); });
|
||||
});
|
||||
|
||||
connect(ui->actionBold, &QAction::toggled, this, [this](bool checked) {
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [checked](CondFormat& format) { format.setBold(checked); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [checked](CondFormat& format) { format.setBold(checked); });
|
||||
});
|
||||
connect(ui->actionItalic, &QAction::toggled, this, [this](bool checked) {
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [checked](CondFormat& format) { format.setItalic(checked); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [checked](CondFormat& format) { format.setItalic(checked); });
|
||||
});
|
||||
connect(ui->actionUnderline, &QAction::toggled, this, [this](bool checked) {
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [checked](CondFormat& format) { format.setUnderline(checked); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [checked](CondFormat& format) { format.setUnderline(checked); });
|
||||
});
|
||||
|
||||
connect(ui->actionLeftAlign, &QAction::triggered, this, [this]() {
|
||||
@@ -162,33 +162,33 @@ TableBrowser::TableBrowser(QWidget* parent) :
|
||||
ui->actionRightAlign->setChecked(false);
|
||||
ui->actionCenter->setChecked(false);
|
||||
ui->actionJustify->setChecked(false);
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignLeft); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignLeft); });
|
||||
});
|
||||
connect(ui->actionRightAlign, &QAction::triggered, this, [this]() {
|
||||
ui->actionLeftAlign->setChecked(false);
|
||||
ui->actionRightAlign->setChecked(true);
|
||||
ui->actionCenter->setChecked(false);
|
||||
ui->actionJustify->setChecked(false);
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignRight); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignRight); });
|
||||
});
|
||||
connect(ui->actionCenter, &QAction::triggered, this, [this]() {
|
||||
ui->actionLeftAlign->setChecked(false);
|
||||
ui->actionRightAlign->setChecked(false);
|
||||
ui->actionCenter->setChecked(true);
|
||||
ui->actionJustify->setChecked(false);
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignCenter); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignCenter); });
|
||||
});
|
||||
connect(ui->actionJustify, &QAction::triggered, this, [this]() {
|
||||
ui->actionLeftAlign->setChecked(false);
|
||||
ui->actionRightAlign->setChecked(false);
|
||||
ui->actionCenter->setChecked(false);
|
||||
ui->actionJustify->setChecked(true);
|
||||
modifyColumnFormat(ui->dataTable->selectedCols(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignJustify); });
|
||||
modifyColumnFormat(ui->dataTable->colsInSelection(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignJustify); });
|
||||
});
|
||||
|
||||
connect(ui->actionEditCondFormats, &QAction::triggered, this, [this]() { editCondFormats(currentIndex().column()); });
|
||||
connect(ui->actionClearFormat, &QAction::triggered, this, [this]() {
|
||||
for (int column : ui->dataTable->selectedCols())
|
||||
for (int column : ui->dataTable->colsInSelection())
|
||||
clearAllCondFormats(column);
|
||||
});
|
||||
|
||||
@@ -236,6 +236,41 @@ TableBrowser::TableBrowser(QWidget* parent) :
|
||||
connect(ui->actionToggleFormatToolbar, &QAction::toggled, ui->formatFrame, &QFrame::setVisible);
|
||||
ui->actionToggleFormatToolbar->setChecked(false);
|
||||
ui->formatFrame->setVisible(false);
|
||||
|
||||
// Set up find frame
|
||||
ui->frameFind->hide();
|
||||
|
||||
QShortcut* shortcutHideFindFrame = new QShortcut(QKeySequence("ESC"), ui->editFindExpression);
|
||||
connect(shortcutHideFindFrame, &QShortcut::activated, ui->buttonFindClose, &QToolButton::click);
|
||||
|
||||
connect(ui->actionFind, &QAction::triggered, [this](bool checked) {
|
||||
if(checked)
|
||||
{
|
||||
ui->frameFind->show();
|
||||
ui->editFindExpression->setFocus();
|
||||
} else {
|
||||
ui->buttonFindClose->click();
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->editFindExpression, &QLineEdit::returnPressed, ui->buttonFindNext, &QToolButton::click);
|
||||
connect(ui->editFindExpression, &QLineEdit::textChanged, this, [this]() {
|
||||
// When the text has changed but neither Return nor F3 or similar nor any buttons were pressed, we want to include the current
|
||||
// cell in the search as well. This makes sure the selected cell does not jump around every time the text is changed but only
|
||||
// when the current cell does not match the search expression anymore.
|
||||
find(ui->editFindExpression->text(), true, true);
|
||||
});
|
||||
connect(ui->buttonFindClose, &QToolButton::clicked, this, [this](){
|
||||
ui->dataTable->setFocus();
|
||||
ui->frameFind->hide();
|
||||
ui->actionFind->setChecked(false);
|
||||
});
|
||||
connect(ui->buttonFindPrevious, &QToolButton::clicked, this, [this](){
|
||||
find(ui->editFindExpression->text(), false);
|
||||
});
|
||||
connect(ui->buttonFindNext, &QToolButton::clicked, this, [this](){
|
||||
find(ui->editFindExpression->text(), true);
|
||||
});
|
||||
}
|
||||
|
||||
TableBrowser::~TableBrowser()
|
||||
@@ -328,6 +363,7 @@ void TableBrowser::setEnabled(bool enable)
|
||||
ui->actionRefresh->setEnabled(enable);
|
||||
ui->actionPrintTable->setEnabled(enable);
|
||||
ui->editGlobalFilter->setEnabled(enable);
|
||||
ui->actionFind->setEnabled(enable);
|
||||
|
||||
updateInsertDeleteRecordButton();
|
||||
}
|
||||
@@ -379,7 +415,7 @@ void TableBrowser::updateTable()
|
||||
}
|
||||
statusMessage += tr(". Sum: %1; Average: %2; Min: %3; Max: %4").arg(sum).arg(sum/sel.count()).arg(min).arg(max);
|
||||
}
|
||||
};
|
||||
}
|
||||
emit statusMessageRequested(statusMessage);
|
||||
});
|
||||
}
|
||||
@@ -1310,3 +1346,54 @@ void TableBrowser::jumpToRow(const sqlb::ObjectIdentifier& table, QString column
|
||||
ui->dataTable->filterHeader()->setFilter(static_cast<size_t>(column_index-obj->fields.begin()+1), QString("=") + value);
|
||||
updateTable();
|
||||
}
|
||||
|
||||
void TableBrowser::find(const QString& expr, bool forward, bool include_first)
|
||||
{
|
||||
// Get the cell from which the search should be started. If there is a selected cell, use that. If there is no selected cell, start at the first cell.
|
||||
QModelIndex start;
|
||||
if(ui->dataTable->selectionModel()->hasSelection())
|
||||
start = ui->dataTable->selectionModel()->selectedIndexes().front();
|
||||
else
|
||||
start = m_browseTableModel->index(0, 0);
|
||||
|
||||
// Prepare the match flags with all the search settings
|
||||
Qt::MatchFlags flags = Qt::MatchWrap;
|
||||
|
||||
if(ui->checkFindCaseSensitive->isChecked())
|
||||
flags |= Qt::MatchCaseSensitive;
|
||||
|
||||
if(ui->checkFindWholeCell->isChecked())
|
||||
flags |= Qt::MatchFixedString;
|
||||
else
|
||||
flags |= Qt::MatchContains;
|
||||
|
||||
if(ui->checkFindRegEx->isChecked())
|
||||
flags |= Qt::MatchRegExp;
|
||||
|
||||
// Prepare list of columns to search in. We only search in non-hidden rows
|
||||
std::vector<int> column_list;
|
||||
sqlb::ObjectIdentifier tableName = currentlyBrowsedTableName();
|
||||
if(browseTableSettings[tableName].showRowid)
|
||||
column_list.push_back(0);
|
||||
for(int i=1;i<m_browseTableModel->columnCount();i++)
|
||||
{
|
||||
if(browseTableSettings[tableName].hiddenColumns.contains(i) == false)
|
||||
column_list.push_back(i);
|
||||
else if(browseTableSettings[tableName].hiddenColumns[i] == false)
|
||||
column_list.push_back(i);
|
||||
}
|
||||
|
||||
// Perform the actual search using the model class
|
||||
const auto match = m_browseTableModel->nextMatch(start, column_list, expr, flags, !forward, include_first);
|
||||
|
||||
// Select the next match if we found one
|
||||
if(match.isValid()) {
|
||||
ui->dataTable->setCurrentIndex(match);
|
||||
ui->dataTable->scrollTo(match);
|
||||
}
|
||||
// Make the expression control red if no results were found
|
||||
if(match.isValid() || expr == "")
|
||||
ui->editFindExpression->setStyleSheet("");
|
||||
else
|
||||
ui->editFindExpression->setStyleSheet("QLineEdit {color: white; background-color: rgb(255, 102, 102)}");
|
||||
}
|
||||
|
||||
@@ -152,6 +152,9 @@ private slots:
|
||||
void browseDataSetTableEncoding(bool forAllTables = false);
|
||||
void browseDataSetDefaultTableEncoding();
|
||||
|
||||
private:
|
||||
void find(const QString& expr, bool forward, bool include_first = false);
|
||||
|
||||
private:
|
||||
Ui::TableBrowser* ui;
|
||||
QIntValidator* gotoValidator;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>651</width>
|
||||
<height>362</height>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -85,6 +85,7 @@
|
||||
<addaction name="actionDeleteRecord"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionToggleFormatToolbar"/>
|
||||
<addaction name="actionFind"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -194,6 +195,152 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frameFind">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="0" column="2">
|
||||
<widget class="QToolButton" name="buttonFindNext">
|
||||
<property name="toolTip">
|
||||
<string>Find next match [Enter, F3]</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Find next match with wrapping</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/down</normaloff>:/icons/down</iconset>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F3</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QToolButton" name="buttonFindPrevious">
|
||||
<property name="toolTip">
|
||||
<string>Find previous match [Shift+F3]</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Find previous match with mapping</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/up</normaloff>:/icons/up</iconset>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Shift+F3</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="5">
|
||||
<widget class="QCheckBox" name="checkFindRegEx">
|
||||
<property name="toolTip">
|
||||
<string>Interpret search pattern as a regular expression</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>When checked, the pattern to find is interpreted as a UNIX regular expression. See <a href="https://en.wikibooks.org/wiki/Regular_Expressions">Regular Expression in Wikibooks</a>.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Regular Expression</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QCheckBox" name="checkFindWholeCell">
|
||||
<property name="whatsThis">
|
||||
<string>The found pattern must be a whole word</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Whole Cell</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLineEdit" name="editFindExpression">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Text pattern to find considering the checks in this frame</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Find in table</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="8">
|
||||
<widget class="QToolButton" name="buttonFindClose">
|
||||
<property name="toolTip">
|
||||
<string>Close Find Bar</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close Find Bar</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/close</normaloff>:/icons/close</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="6">
|
||||
<spacer name="horizontalSpacer_1">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="checkFindCaseSensitive">
|
||||
<property name="whatsThis">
|
||||
<string>The found pattern must match in letter case</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Case Sensitive</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
@@ -588,6 +735,24 @@
|
||||
<enum>Qt::WidgetShortcut</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFind">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/find</normaloff>:/icons/find</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Find in cells</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open the find tool bar which allows you to search for values in the table view below.</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionBold">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
@@ -788,6 +953,14 @@
|
||||
<tabstops>
|
||||
<tabstop>comboBrowseTable</tabstop>
|
||||
<tabstop>dataTable</tabstop>
|
||||
<tabstop>editGlobalFilter</tabstop>
|
||||
<tabstop>editFindExpression</tabstop>
|
||||
<tabstop>buttonFindPrevious</tabstop>
|
||||
<tabstop>buttonFindNext</tabstop>
|
||||
<tabstop>checkFindCaseSensitive</tabstop>
|
||||
<tabstop>checkFindWholeCell</tabstop>
|
||||
<tabstop>checkFindRegEx</tabstop>
|
||||
<tabstop>buttonFindClose</tabstop>
|
||||
<tabstop>buttonBegin</tabstop>
|
||||
<tabstop>buttonPrevious</tabstop>
|
||||
<tabstop>buttonNext</tabstop>
|
||||
@@ -806,8 +979,8 @@
|
||||
<slot>updateTable()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>118</x>
|
||||
<y>141</y>
|
||||
<x>159</x>
|
||||
<y>31</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
@@ -822,8 +995,8 @@
|
||||
<slot>navigatePrevious()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>86</x>
|
||||
<y>539</y>
|
||||
<x>54</x>
|
||||
<y>358</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
@@ -838,8 +1011,8 @@
|
||||
<slot>navigateNext()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>183</x>
|
||||
<y>539</y>
|
||||
<x>139</x>
|
||||
<y>358</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
@@ -854,8 +1027,8 @@
|
||||
<slot>navigateGoto()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>365</x>
|
||||
<y>539</y>
|
||||
<x>403</x>
|
||||
<y>360</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
@@ -870,8 +1043,8 @@
|
||||
<slot>navigateGoto()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>506</x>
|
||||
<y>538</y>
|
||||
<x>550</x>
|
||||
<y>360</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
@@ -886,8 +1059,8 @@
|
||||
<slot>navigateEnd()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>223</x>
|
||||
<y>539</y>
|
||||
<x>169</x>
|
||||
<y>358</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>499</x>
|
||||
@@ -902,8 +1075,8 @@
|
||||
<slot>navigateBegin()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>50</x>
|
||||
<y>539</y>
|
||||
<x>24</x>
|
||||
<y>358</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>499</x>
|
||||
@@ -1147,7 +1320,7 @@
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>326</x>
|
||||
<y>347</y>
|
||||
<y>291</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
|
||||
BIN
src/icons/bullet_arrow_bottom.png
Normal file
BIN
src/icons/bullet_arrow_bottom.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 229 B |
BIN
src/icons/bullet_arrow_top.png
Normal file
BIN
src/icons/bullet_arrow_top.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 230 B |
@@ -83,6 +83,8 @@
|
||||
<file>color_swatch.png</file>
|
||||
<file>edit_cond_formats.png</file>
|
||||
<file alias="clear_sorting">clear_sorting.png</file>
|
||||
<file alias="arrow_bottom">bullet_arrow_bottom.png</file>
|
||||
<file alias="arrow_top">bullet_arrow_top.png</file>
|
||||
<file>text_bold.png</file>
|
||||
<file>text_italic.png</file>
|
||||
<file>text_underline.png</file>
|
||||
|
||||
@@ -53,6 +53,11 @@ std::string escapeIdentifier(const std::string& id)
|
||||
}
|
||||
}
|
||||
|
||||
std::string escapeString(const std::string& literal)
|
||||
{
|
||||
return '\'' + duplicate_char(literal, '\'') + '\'';
|
||||
}
|
||||
|
||||
bool ObjectIdentifier::fromSerialised(const std::string& serialised)
|
||||
{
|
||||
auto pos_comma = serialised.find(",");
|
||||
|
||||
@@ -20,6 +20,9 @@ char getIdentifierQuoteChar();
|
||||
// Add quotes to an identifier
|
||||
std::string escapeIdentifier(const std::string& id);
|
||||
|
||||
// Add SQL quotes to a string literal and escape any single quote character
|
||||
std::string escapeString(const std::string& literal);
|
||||
|
||||
// Object identifier consisting of schema name and object name
|
||||
class ObjectIdentifier
|
||||
{
|
||||
|
||||
@@ -45,6 +45,10 @@ QString escapeIdentifier(const QString& id)
|
||||
{
|
||||
return QString::fromStdString(escapeIdentifier(id.toStdString()));
|
||||
}
|
||||
QString escapeString(const QString& literal)
|
||||
{
|
||||
return QString::fromStdString(escapeString(literal.toStdString()));
|
||||
}
|
||||
}
|
||||
|
||||
// collation callbacks
|
||||
@@ -324,7 +328,7 @@ bool DBBrowserDB::attach(const QString& filePath, QString attach_as)
|
||||
}
|
||||
}
|
||||
|
||||
if(!executeSQL(QString("ATTACH '%1' AS %2 %3").arg(filePath).arg(sqlb::escapeIdentifier(attach_as)).arg(key), false))
|
||||
if(!executeSQL(QString("ATTACH %1 AS %2 %3").arg(sqlb::escapeString(filePath)).arg(sqlb::escapeIdentifier(attach_as)).arg(key), false))
|
||||
{
|
||||
QMessageBox::warning(nullptr, qApp->applicationName(), lastErrorMessage);
|
||||
return false;
|
||||
@@ -334,7 +338,7 @@ bool DBBrowserDB::attach(const QString& filePath, QString attach_as)
|
||||
delete cipherSettings;
|
||||
#else
|
||||
// Attach database
|
||||
if(!executeSQL(QString("ATTACH '%1' AS %2").arg(filePath).arg(sqlb::escapeIdentifier(attach_as)), false))
|
||||
if(!executeSQL(QString("ATTACH %1 AS %2").arg(sqlb::escapeString(filePath)).arg(sqlb::escapeIdentifier(attach_as)), false))
|
||||
{
|
||||
QMessageBox::warning(nullptr, qApp->applicationName(), lastErrorMessage);
|
||||
return false;
|
||||
|
||||
@@ -33,6 +33,7 @@ int collCompare(void* pArg, int sizeA, const void* sA, int sizeB, const void* sB
|
||||
namespace sqlb
|
||||
{
|
||||
QString escapeIdentifier(const QString& id);
|
||||
QString escapeString(const QString& literal);
|
||||
}
|
||||
|
||||
/// represents a single SQLite database. except when noted otherwise,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <QUrl>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <QProgressDialog>
|
||||
#include <QRegularExpression>
|
||||
#include <json.hpp>
|
||||
|
||||
#include "RowLoader.h"
|
||||
@@ -422,6 +423,16 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const
|
||||
bool isNumber;
|
||||
value.toDouble(&isNumber);
|
||||
return static_cast<int>((isNumber ? Qt::AlignRight : Qt::AlignLeft) | Qt::AlignVCenter);
|
||||
} else if(role == Qt::DecorationRole) {
|
||||
if(!row_available)
|
||||
return QVariant();
|
||||
|
||||
if(Settings::getValue("databrowser", "image_preview").toBool() && !isImageData(cached_row->at(column)).isNull())
|
||||
{
|
||||
QImage img;
|
||||
img.loadFromData(cached_row->at(column));
|
||||
return QPixmap::fromImage(img);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1035,3 +1046,108 @@ void SqliteTableModel::waitUntilIdle () const
|
||||
{
|
||||
worker->waitUntilIdle();
|
||||
}
|
||||
|
||||
QModelIndex SqliteTableModel::nextMatch(const QModelIndex& start, const std::vector<int>& column_list, const QString& value, Qt::MatchFlags flags, bool reverse, bool dont_skip_to_next_field) const
|
||||
{
|
||||
// Extract flags
|
||||
bool whole_cell = !(flags & Qt::MatchContains);
|
||||
bool regex = flags & Qt::MatchRegExp;
|
||||
Qt::CaseSensitivity case_sensitive = ((flags & Qt::MatchCaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
||||
bool wrap = flags & Qt::MatchWrap;
|
||||
int increment = (reverse ? -1 : 1);
|
||||
|
||||
// Prepare the regular expression for regex mode
|
||||
QRegularExpression reg_exp;
|
||||
if(regex)
|
||||
{
|
||||
reg_exp = QRegularExpression(value, (case_sensitive ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption));
|
||||
if(!reg_exp.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
if(whole_cell)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0)
|
||||
reg_exp.setPattern("\\A(" + reg_exp.pattern() + ")\\Z");
|
||||
#else
|
||||
reg_exp.setPattern(QRegularExpression::anchoredPattern(reg_exp.pattern()));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until the row count is there
|
||||
waitUntilIdle();
|
||||
|
||||
// Make sure the start position starts in a column from the list of columns to search in
|
||||
QModelIndex pos = start;
|
||||
if(std::find(column_list.begin(), column_list.end(), pos.column()) == column_list.end())
|
||||
{
|
||||
// If for some weird reason the start index is not in the column list, we simply use the first column of the column list instead
|
||||
pos = pos.sibling(pos.row(), reverse ? column_list.back() : column_list.front());
|
||||
}
|
||||
|
||||
// Get the last cell to search in. If wrapping is enabled, we search until we hit the start cell again. If wrapping is not enabled, we start at the last
|
||||
// cell of the table.
|
||||
QModelIndex end = (wrap ? pos : index(rowCount(), column_list.back()));
|
||||
|
||||
// Loop through all cells for the search
|
||||
while(true)
|
||||
{
|
||||
// Go to the next cell and skip all columns in between which we do not care about. This is done as the first step in order
|
||||
// to skip the start index when matching the first cell is disabled.
|
||||
if(dont_skip_to_next_field == false)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
// Next cell position
|
||||
int next_row = pos.row();
|
||||
int next_column = pos.column() + increment;
|
||||
|
||||
// Have we reached the end of the row? Then go to the next one
|
||||
if(next_column < 0 || next_column >= static_cast<int>(m_headers.size()))
|
||||
{
|
||||
next_row += increment;
|
||||
next_column = (reverse ? column_list.back() : column_list.front());
|
||||
}
|
||||
|
||||
// Have we reached the last row? Then wrap around to the first one
|
||||
if(wrap && (next_row < 0 || next_row >= rowCount()))
|
||||
next_row = (reverse ? rowCount()-1 : 0);
|
||||
|
||||
// Set next index for search
|
||||
pos = pos.sibling(next_row, next_column);
|
||||
|
||||
// Have we hit the last column? We have not found anything then
|
||||
if(pos == end)
|
||||
return QModelIndex();
|
||||
|
||||
// Is this a column which we are supposed to search in? If so, stop looking for the next cell and start comparing
|
||||
if(std::find(column_list.begin(), column_list.end(), next_column) != column_list.end())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the next time we hit the above check, we actuall move on to the next cell and do not skip the loop again.
|
||||
dont_skip_to_next_field = false;
|
||||
|
||||
// Get row from cache. If it is not in the cache, load the next chunk from the database
|
||||
const size_t row = static_cast<size_t>(pos.row());
|
||||
if(!m_cache.count(row))
|
||||
{
|
||||
triggerCacheLoad(static_cast<int>(row));
|
||||
waitUntilIdle();
|
||||
}
|
||||
const Row* row_data = &m_cache.at(row);
|
||||
|
||||
// Get cell data
|
||||
const size_t column = static_cast<size_t>(pos.column());
|
||||
QString data = row_data->at(column);
|
||||
|
||||
// Perform comparison
|
||||
if(whole_cell && !regex && data.compare(value, case_sensitive) == 0)
|
||||
return pos;
|
||||
else if(!whole_cell && !regex && data.contains(value, case_sensitive))
|
||||
return pos;
|
||||
else if(regex && reg_exp.match(data).hasMatch())
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +117,19 @@ public:
|
||||
void addCondFormat(int column, const CondFormat& condFormat);
|
||||
void setCondFormats(int column, const std::vector<CondFormat>& condFormats);
|
||||
|
||||
// Search for the specified expression in the given cells. This intended as a replacement for QAbstractItemModel::match() even though
|
||||
// it does not override it, which - because of the different parameters - is not possible.
|
||||
// start contains the index to start with, column_list contains the ordered list of the columns to look in, value is the value to search for,
|
||||
// flags allows to modify the search process (Qt::MatchContains, Qt::MatchRegExp, Qt::MatchCaseSensitive, and Qt::MatchWrap are understood),
|
||||
// reverse can be set to true to progress through the cells in backwards direction, and dont_skip_to_next_field can be set to true if the current
|
||||
// cell can be matched as well.
|
||||
QModelIndex nextMatch(const QModelIndex& start,
|
||||
const std::vector<int>& column_list,
|
||||
const QString& value,
|
||||
Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchContains),
|
||||
bool reverse = false,
|
||||
bool dont_skip_to_next_field = false) const;
|
||||
|
||||
DBBrowserDB& db() { return m_db; }
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -182,7 +182,7 @@ void TestTable::parseSQL()
|
||||
"\tinfo VARCHAR(255) CHECK (info == 'x')\n"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.name(), "hero");
|
||||
QCOMPARE(tab.rowidColumns(), {"_rowid_"});
|
||||
@@ -212,7 +212,7 @@ void TestTable::parseSQLdefaultexpr()
|
||||
"date datetime default CURRENT_TIMESTAMP,"
|
||||
"zoi integer)";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.name(), "chtest");
|
||||
QCOMPARE(tab.fields.at(0).name(), "id");
|
||||
@@ -246,7 +246,7 @@ void TestTable::parseSQLMultiPk()
|
||||
"PRIMARY KEY(\"id1\",\"id2\")\n"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.name(), "hero");
|
||||
QCOMPARE(tab.fields.at(0).name(), "id1");
|
||||
@@ -265,7 +265,7 @@ void TestTable::parseSQLForeignKey()
|
||||
{
|
||||
std::string sSQL = "CREATE TABLE grammar_test(id, test, FOREIGN KEY(test) REFERENCES other_table);";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.name(), "grammar_test");
|
||||
QCOMPARE(tab.fields.at(0).name(), "id");
|
||||
@@ -276,7 +276,7 @@ void TestTable::parseSQLSingleQuotes()
|
||||
{
|
||||
std::string sSQL = "CREATE TABLE 'test'('id','test');";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.name(), "test");
|
||||
QCOMPARE(tab.fields.at(0).name(), "id");
|
||||
@@ -287,7 +287,7 @@ void TestTable::parseSQLSquareBrackets()
|
||||
{
|
||||
std::string sSQL = "CREATE TABLE [test]([id],[test]);";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.name(), "test");
|
||||
QCOMPARE(tab.fields.at(0).name(), "id");
|
||||
@@ -299,7 +299,7 @@ void TestTable::parseSQLKeywordInIdentifier()
|
||||
{
|
||||
std::string sSQL = "CREATE TABLE deffered(key integer primary key, if text);";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.name(), "deffered");
|
||||
QCOMPARE(tab.fields.at(0).name(), "key");
|
||||
@@ -313,7 +313,7 @@ void TestTable::parseSQLSomeKeywordsInIdentifier()
|
||||
"`Area of Work` TEXT,"
|
||||
"`Average Number of Volunteers` INTEGER);";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.name(), "Average Number of Volunteers by Area of Work");
|
||||
QCOMPARE(tab.fields.at(0).name(), "Area of Work");
|
||||
@@ -324,7 +324,7 @@ void TestTable::parseSQLWithoutRowid()
|
||||
{
|
||||
std::string sSQL = "CREATE TABLE test(a integer primary key, b integer) WITHOUT ROWID;";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.primaryKey()->columnList(), {"a"});
|
||||
QCOMPARE(tab.rowidColumns(), {"a"});
|
||||
@@ -337,7 +337,7 @@ void TestTable::parseNonASCIIChars()
|
||||
"PRIMARY KEY(`Fieldöäüß`)"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.name(), "lösung");
|
||||
QCOMPARE(tab.fields.at(0).name(), "Fieldöäüß");
|
||||
@@ -350,7 +350,7 @@ void TestTable::parseNonASCIICharsEs()
|
||||
"PRIMARY KEY(\"Field áéíóúÁÉÍÓÚñÑçÇ\")"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
|
||||
QCOMPARE(tab.name(), "Cigüeñas de Alcalá");
|
||||
QCOMPARE(tab.fields.at(0).name(), "Field áéíóúÁÉÍÓÚñÑçÇ");
|
||||
@@ -360,7 +360,7 @@ void TestTable::parseSQLEscapedQuotes()
|
||||
{
|
||||
std::string sSql = "CREATE TABLE double_quotes(a text default 'a''a');";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSql))));
|
||||
Table tab(*Table::parseSQL(sSql));
|
||||
|
||||
QCOMPARE(tab.name(), "double_quotes");
|
||||
QCOMPARE(tab.fields.at(0).name(), "a");
|
||||
@@ -371,7 +371,7 @@ void TestTable::parseSQLForeignKeys()
|
||||
{
|
||||
std::string sql = "CREATE TABLE foreign_key_test(a int, b int, foreign key (a) references x, foreign key (b) references w(y,z) on delete set null);";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sql))));
|
||||
Table tab(*Table::parseSQL(sql));
|
||||
|
||||
QCOMPARE(tab.name(), "foreign_key_test");
|
||||
QCOMPARE(tab.fields.at(0).name(), "a");
|
||||
@@ -386,7 +386,7 @@ void TestTable::parseSQLCheckConstraint()
|
||||
{
|
||||
std::string sql = "CREATE TABLE a (\"b\" text CHECK(\"b\"='A' or \"b\"='B'));";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sql))));
|
||||
Table tab(*Table::parseSQL(sql));
|
||||
|
||||
QCOMPARE(tab.name(), "a");
|
||||
QCOMPARE(tab.fields.at(0).name(), "b");
|
||||
@@ -398,7 +398,7 @@ void TestTable::parseDefaultValues()
|
||||
{
|
||||
std::string sql = "CREATE TABLE test(a int DEFAULT 0, b int DEFAULT -1, c text DEFAULT 'hello', d text DEFAULT '0');";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sql))));
|
||||
Table tab(*Table::parseSQL(sql));
|
||||
|
||||
QCOMPARE(tab.name(), "test");
|
||||
QCOMPARE(tab.fields.at(0).name(), "a");
|
||||
@@ -422,7 +422,7 @@ void TestTable::createTableWithIn()
|
||||
"value NVARCHAR(5) CHECK (value IN ('a', 'b', 'c'))"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
QCOMPARE(tab.name(), "not_working");
|
||||
|
||||
QCOMPARE(tab.fields.at(1).check(), "\"value\" IN ('a', 'b', 'c')");
|
||||
@@ -439,7 +439,7 @@ void TestTable::createTableWithNotLikeConstraint()
|
||||
"value6 INTEGER CHECK(value6 NOT BETWEEN 1 AND 100)\n"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sSQL))));
|
||||
Table tab(*Table::parseSQL(sSQL));
|
||||
QCOMPARE(tab.name(), "hopefully_working");
|
||||
|
||||
QCOMPARE(tab.fields.at(0).check(), "\"value\" NOT LIKE 'prefix%'");
|
||||
@@ -458,7 +458,7 @@ void TestTable::rowValues()
|
||||
"CHECK((a, b) = (1, 2))\n"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sql))));
|
||||
Table tab(*Table::parseSQL(sql));
|
||||
QCOMPARE(tab.name(), "test");
|
||||
|
||||
QCOMPARE(std::dynamic_pointer_cast<sqlb::CheckConstraint>(tab.constraint({}, sqlb::Constraint::CheckConstraintType))->expression(), "(\"a\", \"b\") = (1, 2)");
|
||||
@@ -473,7 +473,7 @@ void TestTable::complexExpressions()
|
||||
"d INTEGER CHECK((((d > 0))))\n"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sql))));
|
||||
Table tab(*Table::parseSQL(sql));
|
||||
QCOMPARE(tab.name(), "test");
|
||||
|
||||
QCOMPARE(tab.fields.at(0).check(), "(\"a\" > 0)");
|
||||
@@ -488,7 +488,7 @@ void TestTable::datetimeExpression()
|
||||
"entry INTEGER DEFAULT (DATETIME(CURRENT_TIMESTAMP, 'LOCALTIME'))\n"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sql))));
|
||||
Table tab(*Table::parseSQL(sql));
|
||||
QCOMPARE(tab.name(), "test");
|
||||
|
||||
QCOMPARE(tab.fields.at(0).name(), "entry");
|
||||
@@ -502,7 +502,7 @@ void TestTable::extraParentheses()
|
||||
"xy INTEGER DEFAULT (1 + (5) - 4)\n"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sql))));
|
||||
Table tab(*Table::parseSQL(sql));
|
||||
QCOMPARE(tab.name(), "test");
|
||||
|
||||
QCOMPARE(tab.fields.at(0).name(), "xy");
|
||||
@@ -516,7 +516,7 @@ void TestTable::moduloOperator()
|
||||
"xy INTEGER DEFAULT (7 % 2)\n"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sql))));
|
||||
Table tab(*Table::parseSQL(sql));
|
||||
QCOMPARE(tab.name(), "test");
|
||||
|
||||
QCOMPARE(tab.fields.at(0).name(), "xy");
|
||||
@@ -533,7 +533,7 @@ void TestTable::complexExpression()
|
||||
"CHECK((a = 'S' AND b IS NOT NULL) OR (a IN ('A', 'P')))"
|
||||
");";
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sql))));
|
||||
Table tab(*Table::parseSQL(sql));
|
||||
QCOMPARE(tab.name(), "test");
|
||||
|
||||
QCOMPARE(tab.fields.at(0).name(), "uuid");
|
||||
@@ -549,7 +549,7 @@ void TestTable::parseTest()
|
||||
{
|
||||
QFETCH(std::string, sql);
|
||||
|
||||
Table tab(*(std::dynamic_pointer_cast<sqlb::Table>(Table::parseSQL(sql))));
|
||||
Table tab(*Table::parseSQL(sql));
|
||||
QVERIFY(tab.fullyParsed());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user