This commit is contained in:
Navdeep Singh Sidhu
2018-11-03 23:30:11 -05:00
46 changed files with 1768 additions and 1134 deletions
+4
View File
@@ -144,6 +144,8 @@ set(SQLB_MOC_HDR
src/FileExtensionManager.h
src/CipherSettings.h
src/DotenvFormat.h
src/Palette.h
src/CondFormat.h
)
set(SQLB_SRC
@@ -191,6 +193,8 @@ set(SQLB_SRC
src/Data.cpp
src/CipherSettings.cpp
src/DotenvFormat.cpp
src/Palette.cpp
src/CondFormat.cpp
)
set(SQLB_FORMS
+17
View File
@@ -90,6 +90,7 @@ Application::Application(int& argc, char** argv) :
qWarning() << qPrintable(tr(" -s, --sql [file]\tExecute this SQL file after opening the DB"));
qWarning() << qPrintable(tr(" -t, --table [table]\tBrowse this table after opening the DB"));
qWarning() << qPrintable(tr(" -R, --read-only\tOpen database in read-only mode"));
qWarning() << qPrintable(tr(" -o, --option [group/setting=value]\tRun application with this setting temporarily set to value"));
qWarning() << qPrintable(tr(" -v, --version\t\tDisplay the current version"));
qWarning() << qPrintable(tr(" [file]\t\tOpen this SQLite database"));
m_dontShowMainWindow = true;
@@ -113,6 +114,22 @@ Application::Application(int& argc, char** argv) :
m_dontShowMainWindow = true;
} else if(arguments().at(i) == "-R" || arguments().at(i) == "--read-only") {
readOnly = true;
} else if(arguments().at(i) == "-o" || arguments().at(i) == "--option") {
const QString optionWarning = tr("The -o/--option option requires an argument in the form group/setting=value");
if(++i >= arguments().size())
qWarning() << qPrintable(optionWarning);
else {
QStringList option = arguments().at(i).split("=");
if (option.size() != 2)
qWarning() << qPrintable(optionWarning);
else {
QStringList setting = option.at(0).split("/");
if (setting.size() != 2)
qWarning() << qPrintable(optionWarning);
else
Settings::setValue(setting.at(0), setting.at(1), option.at(1), /* dont_save_to_disk */ true);
}
}
} else {
// Other: Check if it's a valid file name
if(QFile::exists(arguments().at(i)))
+111
View File
@@ -0,0 +1,111 @@
#include "CondFormat.h"
#include "Settings.h"
#include "Data.h"
CondFormat::CondFormat(const QString& filter, const QColor& color, const QString& encoding)
: m_filter(filter),
m_color(color)
{
m_sqlCondition = filterToSqlCondition(filter, encoding);
}
QString CondFormat::filterToSqlCondition(const QString& value, const QString& encoding)
{
// Check for any special comparison operators at the beginning of the value string. If there are none default to LIKE.
QString op = "LIKE";
QString val, val2;
QString escape;
bool numeric = false, ok = false;
// range/BETWEEN operator
if (value.contains("~")) {
int sepIdx = value.indexOf('~');
val = value.mid(0, sepIdx);
val2 = value.mid(sepIdx+1);
val.toFloat(&ok);
if (ok) {
val2.toFloat(&ok);
ok = ok && (val.toFloat() < val2.toFloat());
}
}
if (ok) {
op = "BETWEEN";
numeric = true;
} else {
val.clear();
val2.clear();
if(value.left(2) == ">=" || value.left(2) == "<=" || value.left(2) == "<>")
{
// Check if we're filtering for '<> NULL'. In this case we need a special comparison operator.
if(value.left(2) == "<>" && value.mid(2) == "NULL")
{
// We are filtering for '<>NULL'. Override the comparison operator to search for NULL values in this column. Also treat search value (NULL) as number,
// in order to avoid putting quotes around it.
op = "IS NOT";
numeric = true;
val = "NULL";
} else if(value.left(2) == "<>" && value.mid(2) == "''") {
// We are filtering for "<>''", i.e. for everything which is not an empty string
op = "<>";
numeric = true;
val = "''";
} else {
value.mid(2).toFloat(&numeric);
op = value.left(2);
val = value.mid(2);
}
} else if(value.left(1) == ">" || value.left(1) == "<") {
value.mid(1).toFloat(&numeric);
op = value.left(1);
val = value.mid(1);
} else if(value.left(1) == "=") {
val = value.mid(1);
// Check if value to compare with is 'NULL'
if(val != "NULL")
{
// It's not, so just compare normally to the value, whatever it is.
op = "=";
} else {
// It is NULL. Override the comparison operator to search for NULL values in this column. Also treat search value (NULL) as number,
// in order to avoid putting quotes around it.
op = "IS";
numeric = true;
}
} else {
// Keep the default LIKE operator
// Set the escape character if one has been specified in the settings dialog
QString escape_character = Settings::getValue("databrowser", "filter_escape").toString();
if(escape_character == "'") escape_character = "''";
if(escape_character.length())
escape = QString("ESCAPE '%1'").arg(escape_character);
// Add % wildcards at the start and at the beginning of the filter query, but only if there weren't set any
// wildcards manually. The idea is to assume that a user who's just typing characters expects the wildcards to
// be added but a user who adds them herself knows what she's doing and doesn't want us to mess up her query.
if(!value.contains("%"))
{
val = value;
val.prepend('%');
val.append('%');
}
}
}
if(val.isEmpty())
val = value;
if(val == "" || val == "%" || val == "%%")
return QString();
else {
// Quote and escape value, but only if it's not numeric and not the empty string sequence
if(!numeric && val != "''")
val = QString("'%1'").arg(val.replace("'", "''"));
QString whereClause(op + " " + QString(encodeString(val.toUtf8(), encoding)));
if (!val2.isEmpty())
whereClause += " AND " + QString(encodeString(val2.toUtf8(), encoding));
whereClause += " " + escape;
return whereClause;
}
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef CONDFORMAT_H
#define CONDFORMAT_H
#include <QString>
#include <QColor>
// Conditional formatting for given format to table cells based on a specified condition.
class CondFormat
{
public:
CondFormat() {};
explicit CondFormat(const QString& filter, const QColor& color, const QString& encoding = QString());
static QString filterToSqlCondition(const QString& value, const QString& encoding = QString());
private:
QString m_sqlCondition;
QString m_filter;
QColor m_color;
public:
QString sqlCondition() const { return m_sqlCondition; };
QString filter() const { return m_filter; };
QColor color() const { return m_color; };
};
#endif // CONDFORMAT_H
+17 -2
View File
@@ -21,8 +21,7 @@ bool isTextOnly(QByteArray data, const QString& encoding, bool quickTest)
data = data.left(512);
// Convert to Unicode if necessary
if(!encoding.isEmpty())
data = QTextCodec::codecForName(encoding.toUtf8())->toUnicode(data).toUtf8();
data = decodeString(data, encoding);
// Perform check
return QString(data).toUtf8() == data;
@@ -65,3 +64,19 @@ QStringList toStringList(const QList<QByteArray> list) {
}
return strings;
}
QByteArray encodeString(const QByteArray& str, const QString& encoding)
{
if(encoding.isEmpty())
return str;
else
return QTextCodec::codecForName(encoding.toUtf8())->fromUnicode(str);
}
QByteArray decodeString(const QByteArray& str, const QString& encoding)
{
if(encoding.isEmpty())
return str;
else
return QTextCodec::codecForName(encoding.toUtf8())->toUnicode(str).toUtf8();
}
+5
View File
@@ -2,6 +2,7 @@
#define DATA_H
#include <QString>
#include <QByteArray>
// This returns false if the data in the data parameter contains binary data. If it is text only, the function returns
// true. If the second parameter is specified, it will be used to convert the data from the given encoding to Unicode
@@ -19,4 +20,8 @@ QByteArray removeBom(QByteArray& data);
QStringList toStringList(const QList<QByteArray> list);
QByteArray encodeString(const QByteArray& str, const QString& encoding);
QByteArray decodeString(const QByteArray& str, const QString& encoding);
#endif
+2
View File
@@ -339,6 +339,7 @@ void EditDialog::importData()
break;
}
QString fileName = FileDialog::getOpenFileName(
OpenDataFile,
this,
tr("Choose a file to import")
#ifndef Q_OS_MAC // Filters on OS X are buggy
@@ -403,6 +404,7 @@ void EditDialog::exportData()
QString selectedFilter = filters.first();
QString fileName = FileDialog::getSaveFileName(
CreateDataFile,
this,
tr("Choose a filename to export data"),
filters.join(";;"),
+3
View File
@@ -307,6 +307,7 @@ void ExportDataDialog::accept()
{
// called from sqlexecute query tab
QString sFilename = FileDialog::getSaveFileName(
CreateDataFile,
this,
tr("Choose a filename to export data"),
file_dialog_filter);
@@ -333,6 +334,7 @@ void ExportDataDialog::accept()
if(selectedItems.size() == 1)
{
QString fileName = FileDialog::getSaveFileName(
CreateDataFile,
this,
tr("Choose a filename to export data"),
file_dialog_filter,
@@ -347,6 +349,7 @@ void ExportDataDialog::accept()
} else {
// ask for folder
QString exportfolder = FileDialog::getExistingDirectory(
CreateDataFile,
this,
tr("Choose a directory"),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
+1
View File
@@ -83,6 +83,7 @@ void ExportSqlDialog::accept()
defaultFileName = pdb->currentFile() + ".sql";;
QString fileName = FileDialog::getSaveFileName(
CreateSQLFile,
this,
tr("Choose a filename to export"),
tr("Text files(*.sql *.txt)"),
+24 -18
View File
@@ -1,52 +1,55 @@
#include "FileDialog.h"
#include "Settings.h"
QString FileDialog::getOpenFileName(QWidget* parent, const QString& caption, const QString &filter, QString *selectedFilter, Options options)
QString FileDialog::getOpenFileName(const FileDialogTypes dialogType, QWidget* parent, const QString& caption, const QString &filter, QString *selectedFilter, Options options)
{
QString result = QFileDialog::getOpenFileName(parent, caption, getFileDialogPath(), filter, selectedFilter, options);
QString result = QFileDialog::getOpenFileName(parent, caption, getFileDialogPath(dialogType), filter, selectedFilter, options);
if(!result.isEmpty())
setFileDialogPath(result);
setFileDialogPath(dialogType, result);
return result;
}
QStringList FileDialog::getOpenFileNames(QWidget *parent, const QString &caption, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
QStringList FileDialog::getOpenFileNames(const FileDialogTypes dialogType, QWidget *parent, const QString &caption, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
{
QStringList result = QFileDialog::getOpenFileNames(parent, caption, getFileDialogPath(), filter, selectedFilter, options);
QStringList result = QFileDialog::getOpenFileNames(parent, caption, getFileDialogPath(dialogType), filter, selectedFilter, options);
if(!result.isEmpty())
{
QFileInfo path = QFileInfo(result.first());
setFileDialogPath(path.absolutePath());
setFileDialogPath(dialogType, path.absolutePath());
}
return result;
}
QString FileDialog::getSaveFileName(QWidget* parent, const QString& caption, const QString& filter, const QString& defaultFileName, QString* selectedFilter, Options options)
QString FileDialog::getSaveFileName(const FileDialogTypes dialogType, QWidget* parent, const QString& caption, const QString& filter, const QString& defaultFileName, QString* selectedFilter, Options options)
{
QString dir = getFileDialogPath();
QString dir = getFileDialogPath(dialogType);
if(!defaultFileName.isEmpty())
dir += "/" + defaultFileName;
QString result = QFileDialog::getSaveFileName(parent, caption, dir, filter, selectedFilter, options);
if(!result.isEmpty())
setFileDialogPath(result);
setFileDialogPath(dialogType, result);
return result;
}
QString FileDialog::getExistingDirectory(QWidget* parent, const QString& caption, Options options)
QString FileDialog::getExistingDirectory(const FileDialogTypes dialogType, QWidget* parent, const QString& caption, Options options)
{
QString result = QFileDialog::getExistingDirectory(parent, caption, getFileDialogPath(), options);
QString result = QFileDialog::getExistingDirectory(parent, caption, getFileDialogPath(dialogType), options);
if(!result.isEmpty())
setFileDialogPath(result);
setFileDialogPath(dialogType, result);
return result;
}
QString FileDialog::getFileDialogPath()
QString FileDialog::getFileDialogPath(const FileDialogTypes dialogType)
{
switch(Settings::getValue("db", "savedefaultlocation").toInt())
{
case 0: // Remember last location
case 2: // Remember last location for current session only
return Settings::getValue("db", "lastlocation").toString();
case 2: { // Remember last location for current session only
QHash<QString, QVariant> lastLocations = Settings::getValue("db", "lastlocations").toHash();
return lastLocations[QString(dialogType)].toString();
}
case 1: // Always use this locations
return Settings::getValue("db", "defaultlocation").toString();
default:
@@ -54,17 +57,20 @@ QString FileDialog::getFileDialogPath()
}
}
void FileDialog::setFileDialogPath(const QString& new_path)
void FileDialog::setFileDialogPath(const FileDialogTypes dialogType, const QString& new_path)
{
QString dir = QFileInfo(new_path).absolutePath();
QHash<QString, QVariant> lastLocations = Settings::getValue("db", "lastlocations").toHash();
lastLocations[QString(dialogType)] = dir;
switch(Settings::getValue("db", "savedefaultlocation").toInt())
{
case 0: // Remember last location
Settings::setValue("db", "lastlocation", dir);
Settings::setValue("db", "lastlocations", lastLocations);
break;
case 2: // Remember last location for current session only
Settings::setValue("db", "lastlocation", dir, true);
Settings::setValue("db", "lastlocations", lastLocations, true);
break;
case 1: // Always use this locations
break; // Do nothing
+30 -6
View File
@@ -3,28 +3,52 @@
#include <QFileDialog>
enum FileDialogTypes {
NoSpecificType,
CreateProjectFile,
OpenProjectFile,
CreateDatabaseFile,
OpenDatabaseFile,
CreateSQLFile,
OpenSQLFile,
OpenCSVFile,
CreateDataFile,
OpenDataFile,
OpenExtensionFile,
OpenCertificateFile,
// ImportTable,
// ExportTable,
};
class FileDialog : public QFileDialog
{
Q_OBJECT
public:
static QString getOpenFileName(QWidget* parent = nullptr, const QString& caption = QString(),
static QString getOpenFileName(const FileDialogTypes dialogType, QWidget* parent = nullptr, const QString& caption = QString(),
const QString& filter = QString(), QString* selectedFilter = nullptr,
Options options = 0);
static QStringList getOpenFileNames(QWidget* parent = nullptr, const QString& caption = QString(),
static QStringList getOpenFileNames(const FileDialogTypes dialogType, QWidget* parent = nullptr, const QString& caption = QString(),
const QString& filter = QString(), QString* selectedFilter = nullptr,
Options options = 0);
static QString getSaveFileName(QWidget* parent = nullptr, const QString& caption = QString(),
static QString getSaveFileName(const FileDialogTypes dialogType, QWidget* parent = nullptr, const QString& caption = QString(),
const QString& filter = QString(), const QString& defaultFileName = QString(), QString* selectedFilter = nullptr,
Options options = 0);
static QString getExistingDirectory(QWidget* parent = nullptr, const QString& caption = QString(),
static QString getExistingDirectory(const FileDialogTypes dialogType, QWidget* parent = nullptr, const QString& caption = QString(),
Options options = 0);
static QString getSqlDatabaseFileFilter();
private:
static QString getFileDialogPath();
static void setFileDialogPath(const QString& new_path);
static QString getFileDialogPath(const FileDialogTypes dialogType);
static void setFileDialogPath(const FileDialogTypes dialogType, const QString& new_path);
};
#endif
+11
View File
@@ -107,6 +107,15 @@ void FilterLineEdit::showContextMenu(const QPoint &pos)
// This has to be created here, otherwise the set of enabled options would not update accordingly.
QMenu* editContextMenu = createStandardContextMenu();
editContextMenu->addSeparator();
QString conditionalFormatLabel = text().isEmpty() ? tr("Clear All Conditional Formats") : tr("Use for Conditional Format");
QAction* conditionalFormatAction = new QAction(conditionalFormatLabel, editContextMenu);
connect(conditionalFormatAction, &QAction::triggered, [&]() {
if (text().isEmpty())
emit clearAllCondFormats();
else
emit addFilterAsCondFormat(text());
});
editContextMenu->addSeparator();
QMenu* filterMenu = editContextMenu->addMenu(tr("Set Filter Expression"));
@@ -164,6 +173,8 @@ void FilterLineEdit::showContextMenu(const QPoint &pos)
setFilterHelper(QString ("?~"));
});
editContextMenu->addAction(conditionalFormatAction);
filterMenu->addAction(whatsThisAction);
filterMenu->addSeparator();
filterMenu->addAction(isNullAction);
+2
View File
@@ -23,6 +23,8 @@ private slots:
signals:
void delayedTextChanged(QString text);
void addFilterAsCondFormat(QString text);
void clearAllCondFormats();
protected:
void keyReleaseEvent(QKeyEvent* event) override;
+14
View File
@@ -36,6 +36,8 @@ void FilterTableHeader::generateFilters(int number, bool showFirst)
else
l->setVisible(true);
connect(l, SIGNAL(delayedTextChanged(QString)), this, SLOT(inputChanged(QString)));
connect(l, SIGNAL(addFilterAsCondFormat(QString)), this, SLOT(addFilterAsCondFormat(QString)));
connect(l, SIGNAL(clearAllCondFormats()), this, SLOT(clearAllCondFormats()));
filterWidgets.push_back(l);
}
@@ -86,6 +88,18 @@ void FilterTableHeader::inputChanged(const QString& new_value)
emit filterChanged(sender()->property("column").toInt(), new_value);
}
void FilterTableHeader::addFilterAsCondFormat(const QString& filter)
{
// Just get the column number and the new value and send them to anybody interested in new conditional formatting
emit addCondFormat(sender()->property("column").toInt(), filter);
}
void FilterTableHeader::clearAllCondFormats()
{
// Just get the column number and send it to anybody responsible or interested in clearing conditional formatting
emit clearAllCondFormats(sender()->property("column").toInt());
}
void FilterTableHeader::clearFilters()
{
for(FilterLineEdit* filterLineEdit : filterWidgets)
+4
View File
@@ -25,12 +25,16 @@ public slots:
signals:
void filterChanged(int column, QString value);
void addCondFormat(int column, QString filter);
void clearAllCondFormats(int column);
protected:
void updateGeometries() override;
private slots:
void inputChanged(const QString& new_value);
void addFilterAsCondFormat(const QString& filter);
void clearAllCondFormats();
private:
QList<FilterLineEdit*> filterWidgets;
+28 -6
View File
@@ -100,10 +100,15 @@ namespace {
void rollback(
ImportCsvDialog* dialog,
DBBrowserDB* pdb,
DBBrowserDB::db_pointer_type* db_ptr,
const QString& savepointName,
size_t nRecord,
const QString& message)
{
// Release DB handle. This needs to be done before calling revertToSavepoint as that function needs to be able to acquire its own handle.
if(db_ptr)
*db_ptr = nullptr;
QApplication::restoreOverrideCursor(); // restore original cursor
if(!message.isEmpty())
{
@@ -533,7 +538,7 @@ bool ImportCsvDialog::importCsv(const QString& fileName, const QString& name)
QString restorepointName = pdb->generateSavepointName("csvimport");
if(!pdb->setSavepoint(restorepointName))
{
rollback(this, pdb, restorepointName, 0, tr("Creating restore point failed: %1").arg(pdb->lastError()));
rollback(this, pdb, nullptr, restorepointName, 0, tr("Creating restore point failed: %1").arg(pdb->lastError()));
return false;
}
@@ -546,7 +551,7 @@ bool ImportCsvDialog::importCsv(const QString& fileName, const QString& name)
{
if(!pdb->createTable(sqlb::ObjectIdentifier("main", tableName), fieldList))
{
rollback(this, pdb, restorepointName, 0, tr("Creating the table failed: %1").arg(pdb->lastError()));
rollback(this, pdb, nullptr, restorepointName, 0, tr("Creating the table failed: %1").arg(pdb->lastError()));
return false;
}
@@ -601,7 +606,7 @@ bool ImportCsvDialog::importCsv(const QString& fileName, const QString& name)
}
// Prepare the INSERT statement. The prepared statement can then be reused for each row to insert
QString sQuery = QString("INSERT INTO %1 VALUES(").arg(sqlb::escapeIdentifier(tableName));
QString sQuery = QString("INSERT %1 INTO %2 VALUES(").arg(currentOnConflictStrategy()).arg(sqlb::escapeIdentifier(tableName));
for(size_t i=1;i<=fieldList.size();i++)
sQuery.append(QString("?%1,").arg(i));
sQuery.chop(1); // Remove last comma
@@ -672,13 +677,15 @@ bool ImportCsvDialog::importCsv(const QString& fileName, const QString& name)
// Some error occurred or the user cancelled the action
// Rollback the entire import. If the action was cancelled, don't show an error message. If it errored, show an error message.
sqlite3_finalize(stmt);
if(result == CSVParser::ParserResult::ParserResultCancelled)
{
rollback(this, pdb, restorepointName, 0, QString());
sqlite3_finalize(stmt);
rollback(this, pdb, &pDb, restorepointName, 0, QString());
return false;
} else {
rollback(this, pdb, restorepointName, lastRowNum, tr("Inserting row failed: %1").arg(pdb->lastError()));
QString error(sqlite3_errmsg(pDb.get()));
sqlite3_finalize(stmt);
rollback(this, pdb, &pDb, restorepointName, lastRowNum, tr("Inserting row failed: %1").arg(error));
return false;
}
}
@@ -773,6 +780,19 @@ QString ImportCsvDialog::currentEncoding() const
return ui->comboEncoding->currentText();
}
QString ImportCsvDialog::currentOnConflictStrategy() const
{
switch(ui->comboOnConflictStrategy->currentIndex())
{
case 1:
return "OR IGNORE";
case 2:
return "OR REPLACE";
default:
return QString();
}
}
void ImportCsvDialog::toggleAdvancedSection(bool show)
{
ui->labelNoTypeDetection->setVisible(show);
@@ -781,4 +801,6 @@ void ImportCsvDialog::toggleAdvancedSection(bool show)
ui->checkFailOnMissing->setVisible(show);
ui->labelIgnoreDefaults->setVisible(show);
ui->checkIgnoreDefaults->setVisible(show);
ui->labelOnConflictStrategy->setVisible(show);
ui->comboOnConflictStrategy->setVisible(show);
}
+2
View File
@@ -54,6 +54,8 @@ private:
void setEncoding(const QString& sEnc);
QString currentEncoding() const;
QString currentOnConflictStrategy() const;
};
#endif
+67 -30
View File
@@ -292,14 +292,14 @@
</property>
</widget>
</item>
<item row="10" column="1">
<item row="11" column="1">
<widget class="QCheckBox" name="checkFailOnMissing">
<property name="toolTip">
<string>Activate this option to stop the import when trying to import an empty value into a NOT NULL column without a default value.</string>
</property>
</widget>
</item>
<item row="10" column="0">
<item row="11" column="0">
<widget class="QLabel" name="labelFailOnMissing">
<property name="text">
<string>Fail on missing values </string>
@@ -314,6 +314,9 @@
<property name="text">
<string>Disable data type detection</string>
</property>
<property name="buddy">
<cstring>checkNoTypeDetection</cstring>
</property>
</widget>
</item>
<item row="8" column="1">
@@ -323,6 +326,38 @@
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QComboBox" name="comboOnConflictStrategy">
<property name="toolTip">
<string>When importing into an existing table with a primary key, unique constraints or a unique index there is a chance for a conflict. This option allows you to select a strategy for that case: By default the import is aborted and rolled back but you can also choose to ignore and not import conflicting rows or to replace the existing row in the table.</string>
</property>
<item>
<property name="text">
<string>Abort import</string>
</property>
</item>
<item>
<property name="text">
<string>Ignore row</string>
</property>
</item>
<item>
<property name="text">
<string>Replace existing row</string>
</property>
</item>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="labelOnConflictStrategy">
<property name="text">
<string>Conflict strategy</string>
</property>
<property name="buddy">
<cstring>comboOnConflictStrategy</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -432,7 +467,9 @@
<tabstop>checkBoxSeparateTables</tabstop>
<tabstop>buttonAdvanced</tabstop>
<tabstop>checkIgnoreDefaults</tabstop>
<tabstop>checkNoTypeDetection</tabstop>
<tabstop>checkFailOnMissing</tabstop>
<tabstop>comboOnConflictStrategy</tabstop>
<tabstop>filePicker</tabstop>
<tabstop>toggleSelected</tabstop>
<tabstop>matchSimilar</tabstop>
@@ -449,8 +486,8 @@
<slot>updatePreview()</slot>
<hints>
<hint type="sourcelabel">
<x>232</x>
<y>99</y>
<x>245</x>
<y>92</y>
</hint>
<hint type="destinationlabel">
<x>445</x>
@@ -481,8 +518,8 @@
<slot>updatePreview()</slot>
<hints>
<hint type="sourcelabel">
<x>478</x>
<y>99</y>
<x>511</x>
<y>92</y>
</hint>
<hint type="destinationlabel">
<x>577</x>
@@ -497,8 +534,8 @@
<slot>updatePreview()</slot>
<hints>
<hint type="sourcelabel">
<x>478</x>
<y>132</y>
<x>511</x>
<y>126</y>
</hint>
<hint type="destinationlabel">
<x>530</x>
@@ -513,8 +550,8 @@
<slot>updatePreview()</slot>
<hints>
<hint type="sourcelabel">
<x>495</x>
<y>165</y>
<x>524</x>
<y>160</y>
</hint>
<hint type="destinationlabel">
<x>540</x>
@@ -529,8 +566,8 @@
<slot>updatePreview()</slot>
<hints>
<hint type="sourcelabel">
<x>184</x>
<y>191</y>
<x>192</x>
<y>182</y>
</hint>
<hint type="destinationlabel">
<x>368</x>
@@ -545,8 +582,8 @@
<slot>updatePreview()</slot>
<hints>
<hint type="sourcelabel">
<x>263</x>
<y>183</y>
<x>271</x>
<y>160</y>
</hint>
<hint type="destinationlabel">
<x>572</x>
@@ -561,8 +598,8 @@
<slot>updatePreview()</slot>
<hints>
<hint type="sourcelabel">
<x>184</x>
<y>60</y>
<x>192</x>
<y>56</y>
</hint>
<hint type="destinationlabel">
<x>354</x>
@@ -577,8 +614,8 @@
<slot>updateSelection(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>780</x>
<y>337</y>
<x>777</x>
<y>385</y>
</hint>
<hint type="destinationlabel">
<x>368</x>
@@ -593,8 +630,8 @@
<slot>updatePreview()</slot>
<hints>
<hint type="sourcelabel">
<x>232</x>
<y>132</y>
<x>245</x>
<y>126</y>
</hint>
<hint type="destinationlabel">
<x>350</x>
@@ -609,8 +646,8 @@
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>272</x>
<y>677</y>
<x>281</x>
<y>707</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
@@ -625,8 +662,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>340</x>
<y>677</y>
<x>349</x>
<y>707</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
@@ -641,8 +678,8 @@
<slot>checkInput()</slot>
<hints>
<hint type="sourcelabel">
<x>194</x>
<y>236</y>
<x>192</x>
<y>206</y>
</hint>
<hint type="destinationlabel">
<x>368</x>
@@ -657,8 +694,8 @@
<slot>matchSimilar()</slot>
<hints>
<hint type="sourcelabel">
<x>780</x>
<y>378</y>
<x>777</x>
<y>418</y>
</hint>
<hint type="destinationlabel">
<x>368</x>
@@ -673,8 +710,8 @@
<slot>toggleAdvancedSection(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>214</x>
<y>259</y>
<x>265</x>
<y>241</y>
</hint>
<hint type="destinationlabel">
<x>393</x>
+160 -27
View File
@@ -27,6 +27,7 @@
#include "RemoteDatabase.h"
#include "FindReplaceDialog.h"
#include "Data.h"
#include "CondFormat.h"
#include <QFile>
#include <QApplication>
@@ -76,6 +77,28 @@ QDataStream& operator>>(QDataStream& ds, sqlb::ObjectIdentifier& objid)
return ds;
}
// These are temporary helper functions to turn a vector of sorted columns into a single column to sort and vice verse. This is done by just taking the
// first sort column there is and ignoring all the others or creating a single item vector respectively. These functions can be removed once all parts
// of the application have been converted to deal with vectors of sorted columns.
void fromSortOrderVector(const QVector<BrowseDataTableSettings::SortedColumn>& vector, int& index, Qt::SortOrder& mode)
{
if(vector.size())
{
index = vector.at(0).index;
mode = vector.at(0).mode;
} else {
index = 0;
mode = Qt::AscendingOrder;
}
}
QVector<BrowseDataTableSettings::SortedColumn> toSortOrderVector(int index, Qt::SortOrder mode)
{
QVector<BrowseDataTableSettings::SortedColumn> vector;
vector.push_back(BrowseDataTableSettings::SortedColumn(index, mode));
return vector;
}
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent),
ui(new Ui::MainWindow),
@@ -127,6 +150,8 @@ void MainWindow::init()
// Set up filters
connect(ui->dataTable->filterHeader(), SIGNAL(filterChanged(int,QString)), this, SLOT(updateFilter(int,QString)));
connect(ui->dataTable->filterHeader(), SIGNAL(addCondFormat(int,QString)), this, SLOT(addCondFormat(int,QString)));
connect(ui->dataTable->filterHeader(), SIGNAL(clearAllCondFormats(int)), this, SLOT(clearAllCondFormats(int)));
connect(m_browseTableModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataTableSelectionChanged(QModelIndex)));
// Select in table the rows correspoding to the selected points in plot
@@ -410,6 +435,7 @@ bool MainWindow::fileOpen(const QString& fileName, bool dontAddToRecentFiles, bo
if (!QFile::exists(wFile))
{
wFile = FileDialog::getOpenFileName(
OpenDatabaseFile,
this,
tr("Choose a database file")
#ifndef Q_OS_MAC // Filters on OS X are buggy
@@ -463,9 +489,11 @@ bool MainWindow::fileOpen(const QString& fileName, bool dontAddToRecentFiles, bo
void MainWindow::fileNew()
{
QString fileName = FileDialog::getSaveFileName(this,
tr("Choose a filename to save under"),
FileDialog::getSqlDatabaseFileFilter());
QString fileName = FileDialog::getSaveFileName(
CreateDatabaseFile,
this,
tr("Choose a filename to save under"),
FileDialog::getSqlDatabaseFileFilter());
if(!fileName.isEmpty())
{
if(QFile::exists(fileName))
@@ -648,10 +676,14 @@ void MainWindow::populateTable()
}
}
}
int sortOrderIndex;
Qt::SortOrder sortOrderMode;
fromSortOrderVector(storedData.sortOrder, sortOrderIndex, sortOrderMode);
if(only_defaults)
m_browseTableModel->setTable(tablename, storedData.sortOrderIndex, storedData.sortOrderMode, storedData.filterValues);
m_browseTableModel->setTable(tablename, sortOrderIndex, sortOrderMode, storedData.filterValues);
else
m_browseTableModel->setTable(tablename, storedData.sortOrderIndex, storedData.sortOrderMode, storedData.filterValues, v);
m_browseTableModel->setTable(tablename, sortOrderIndex, sortOrderMode, storedData.filterValues, v);
// There is information stored for this table, so extract it and apply it
applyBrowseTableSettings(storedData);
@@ -703,7 +735,10 @@ void MainWindow::applyBrowseTableSettings(BrowseDataTableSettings storedData, bo
ui->dataTable->setColumnWidth(widthIt.key(), widthIt.value());
// Sorting
ui->dataTable->filterHeader()->setSortIndicator(storedData.sortOrderIndex, storedData.sortOrderMode);
int sortOrderIndex;
Qt::SortOrder sortOrderMode;
fromSortOrderVector(storedData.sortOrder, sortOrderIndex, sortOrderMode);
ui->dataTable->filterHeader()->setSortIndicator(sortOrderIndex, sortOrderMode);
// Filters
if(!skipFilters)
@@ -713,7 +748,12 @@ void MainWindow::applyBrowseTableSettings(BrowseDataTableSettings storedData, bo
bool oldState = filterHeader->blockSignals(true);
for(auto filterIt=storedData.filterValues.constBegin();filterIt!=storedData.filterValues.constEnd();++filterIt)
filterHeader->setFilter(filterIt.key(), filterIt.value());
filterHeader->blockSignals(oldState);
// Conditional formats
for(auto formatIt=storedData.condFormats.constBegin(); formatIt!=storedData.condFormats.constEnd(); ++formatIt)
m_browseTableModel->setCondFormats(formatIt.key(), formatIt.value());
filterHeader->blockSignals(oldState);
}
// Encoding
@@ -1298,6 +1338,13 @@ void MainWindow::executeQuery()
{
// What type of query is this?
QString qtail = QString(tail).trimmed();
// Remove trailing comments so we don't get fooled by some trailing text at the end of the stream.
// Otherwise we'll pass them to SQLite and its execution will trigger a savepoint that wouldn't be
// reverted.
SqliteTableModel::removeCommentsFromQuery(qtail);
if (qtail.isEmpty())
break;
StatementType query_type = getQueryType(qtail);
// Check whether the DB structure is changed by this statement
@@ -1521,9 +1568,10 @@ void MainWindow::mainTabSelected(int tabindex)
void MainWindow::importTableFromCSV()
{
QStringList wFiles = FileDialog::getOpenFileNames(
this,
tr("Choose text files"),
tr("Text files(*.csv *.txt);;All files(*)"));
OpenCSVFile,
this,
tr("Choose text files"),
tr("Text files(*.csv *.txt);;All files(*)"));
QStringList validFiles;
for(const auto& file : wFiles) {
@@ -1629,6 +1677,7 @@ void MainWindow::importDatabaseFromSQL()
{
// Get file name to import
QString fileName = FileDialog::getOpenFileName(
OpenSQLFile,
this,
tr("Choose a file to import"),
tr("Text files(*.sql *.txt);;All files(*)"));
@@ -1646,6 +1695,7 @@ void MainWindow::importDatabaseFromSQL()
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) || !db.isOpen())
{
newDbFile = FileDialog::getSaveFileName(
CreateDatabaseFile,
this,
tr("Choose a filename to save under"),
FileDialog::getSqlDatabaseFileFilter());
@@ -1932,9 +1982,12 @@ void MainWindow::browseTableHeaderClicked(int logicalindex)
// instead of the column name we just use the column index, +2 because 'rowid, *' is the projection
BrowseDataTableSettings& settings = browseTableSettings[currentlyBrowsedTableName()];
settings.sortOrderIndex = logicalindex;
settings.sortOrderMode = settings.sortOrderMode == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder;
ui->dataTable->sortByColumn(settings.sortOrderIndex, settings.sortOrderMode);
int dummy;
Qt::SortOrder order;
fromSortOrderVector(settings.sortOrder, dummy, order);
order = order == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder;
settings.sortOrder = toSortOrderVector(logicalindex, order);
ui->dataTable->sortByColumn(logicalindex, order);
// select the first item in the column so the header is bold
// we might try to select the last selected item
@@ -2109,6 +2162,7 @@ void MainWindow::changeSqlTab(int /*index*/)
void MainWindow::openSqlFile()
{
QString file = FileDialog::getOpenFileName(
OpenSQLFile,
this,
tr("Select SQL file to open"),
tr("Text files(*.sql *.txt);;All files(*)"));
@@ -2169,6 +2223,7 @@ void MainWindow::saveSqlFileAs()
return;
QString file = FileDialog::getSaveFileName(
CreateSQLFile,
this,
tr("Select file name"),
tr("Text files(*.sql *.txt);;All files(*)"));
@@ -2194,6 +2249,7 @@ void MainWindow::saveSqlResultsAsView()
void MainWindow::loadExtension()
{
QString file = FileDialog::getOpenFileName(
OpenExtensionFile,
this,
tr("Select extension file"),
tr("Extensions(*.so *.dll);;All files(*)"));
@@ -2405,15 +2461,33 @@ void MainWindow::updateBrowseDataColumnWidth(int section, int /*old_size*/, int
static void loadBrowseDataTableSettings(BrowseDataTableSettings& settings, QXmlStreamReader& xml)
{
settings.sortOrderIndex = xml.attributes().value("sort_order_index").toInt();
settings.sortOrderMode = static_cast<Qt::SortOrder>(xml.attributes().value("sort_order_mode").toInt());
// TODO Remove this in the near future. This file format was only created temporarily by the nightlies from the late 3.11 development period.
if(xml.attributes().hasAttribute("sort_order_index"))
{
int sortOrderIndex = xml.attributes().value("sort_order_index").toInt();
Qt::SortOrder sortOrderMode = static_cast<Qt::SortOrder>(xml.attributes().value("sort_order_mode").toInt());
settings.sortOrder = toSortOrderVector(sortOrderIndex, sortOrderMode);
}
settings.showRowid = xml.attributes().value("show_row_id").toInt();
settings.encoding = xml.attributes().value("encoding").toString();
settings.plotXAxis = xml.attributes().value("plot_x_axis").toString();
settings.unlockViewPk = xml.attributes().value("unlock_view_pk").toString();
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "table") {
if(xml.name() == "column_widths") {
if(xml.name() == "sort")
{
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "sort")
{
if(xml.name() == "column")
{
int index = xml.attributes().value("index").toInt();
int mode = xml.attributes().value("mode").toInt();
settings.sortOrder.push_back(BrowseDataTableSettings::SortedColumn(index, mode));
xml.skipCurrentElement();
}
}
} else if(xml.name() == "column_widths") {
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "column_widths") {
if (xml.name() == "column") {
int index = xml.attributes().value("index").toInt();
@@ -2429,6 +2503,21 @@ static void loadBrowseDataTableSettings(BrowseDataTableSettings& settings, QXmlS
xml.skipCurrentElement();
}
}
} else if(xml.name() == "conditional_formats") {
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "conditional_formats") {
if (xml.name() == "column") {
int index = xml.attributes().value("index").toInt();
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "column") {
if(xml.name() == "format") {
CondFormat newCondFormat(xml.attributes().value("condition").toString(),
QColor(xml.attributes().value("color").toString()),
settings.encoding);
settings.condFormats[index].append(newCondFormat);
xml.skipCurrentElement();
}
}
}
}
} else if(xml.name() == "display_formats") {
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "display_formats") {
if (xml.name() == "column") {
@@ -2467,9 +2556,11 @@ bool MainWindow::loadProject(QString filename, bool readOnly)
// Show the open file dialog when no filename was passed as parameter
if(filename.isEmpty())
{
filename = FileDialog::getOpenFileName(this,
tr("Choose a project file to open"),
tr("DB Browser for SQLite project file (*.sqbpro)"));
filename = FileDialog::getOpenFileName(
OpenProjectFile,
this,
tr("Choose a project file to open"),
tr("DB Browser for SQLite project file (*.sqbpro)"));
}
if(!filename.isEmpty())
@@ -2610,8 +2701,11 @@ bool MainWindow::loadProject(QString filename, bool readOnly)
{
populateTable(); // Refresh view
sqlb::ObjectIdentifier current_table = currentlyBrowsedTableName();
ui->dataTable->sortByColumn(browseTableSettings[current_table].sortOrderIndex,
browseTableSettings[current_table].sortOrderMode);
int sortIndex;
Qt::SortOrder sortMode;
fromSortOrderVector(browseTableSettings[current_table].sortOrder, sortIndex, sortMode);
ui->dataTable->sortByColumn(sortIndex, sortMode);
showRowidColumn(browseTableSettings[current_table].showRowid);
unlockViewEditing(!browseTableSettings[current_table].unlockViewPk.isEmpty(), browseTableSettings[current_table].unlockViewPk);
}
@@ -2667,12 +2761,21 @@ static void saveDbTreeState(const QTreeView* tree, QXmlStreamWriter& xml, QModel
static void saveBrowseDataTableSettings(const BrowseDataTableSettings& object, QXmlStreamWriter& xml)
{
xml.writeAttribute("sort_order_index", QString::number(object.sortOrderIndex));
xml.writeAttribute("sort_order_mode", QString::number(object.sortOrderMode));
xml.writeAttribute("show_row_id", QString::number(object.showRowid));
xml.writeAttribute("encoding", object.encoding);
xml.writeAttribute("plot_x_axis", object.plotXAxis);
xml.writeAttribute("unlock_view_pk", object.unlockViewPk);
xml.writeStartElement("sort");
for(const auto& column : object.sortOrder)
{
xml.writeStartElement("column");
xml.writeAttribute("index", QString::number(column.index));
xml.writeAttribute("mode", QString::number(column.mode));
xml.writeEndElement();
}
xml.writeEndElement();
xml.writeStartElement("column_widths");
for(auto iter=object.columnWidths.constBegin(); iter!=object.columnWidths.constEnd(); ++iter) {
xml.writeStartElement("column");
@@ -2689,6 +2792,19 @@ static void saveBrowseDataTableSettings(const BrowseDataTableSettings& object, Q
xml.writeEndElement();
}
xml.writeEndElement();
xml.writeStartElement("conditional_formats");
for(auto iter=object.condFormats.constBegin(); iter!=object.condFormats.constEnd(); ++iter) {
xml.writeStartElement("column");
xml.writeAttribute("index", QString::number(iter.key()));
for(auto format : iter.value()) {
xml.writeStartElement("format");
xml.writeAttribute("condition", format.filter());
xml.writeAttribute("color", format.color().name());
xml.writeEndElement();
}
xml.writeEndElement();
}
xml.writeEndElement();
xml.writeStartElement("display_formats");
for(auto iter=object.displayFormats.constBegin(); iter!=object.displayFormats.constEnd(); ++iter) {
xml.writeStartElement("column");
@@ -2721,10 +2837,12 @@ static void saveBrowseDataTableSettings(const BrowseDataTableSettings& object, Q
void MainWindow::saveProject()
{
QString filename = FileDialog::getSaveFileName(this,
tr("Choose a filename to save under"),
tr("DB Browser for SQLite project file (*.sqbpro)"),
db.currentFile());
QString filename = FileDialog::getSaveFileName(
CreateProjectFile,
this,
tr("Choose a filename to save under"),
tr("DB Browser for SQLite project file (*.sqbpro)"),
db.currentFile());
if(!filename.isEmpty())
{
// Make sure the file has got a .sqbpro ending
@@ -2837,6 +2955,7 @@ void MainWindow::fileAttach()
{
// Get file name of database to attach
QString file = FileDialog::getOpenFileName(
OpenDatabaseFile,
this,
tr("Choose a database file"),
FileDialog::getSqlDatabaseFileFilter());
@@ -2858,6 +2977,20 @@ void MainWindow::updateFilter(int column, const QString& value)
applyBrowseTableSettings(settings, true);
}
void MainWindow::addCondFormat(int column, const QString& value)
{
CondFormat newCondFormat(value, m_condFormatPalette.nextSerialColor(Palette::appHasDarkTheme()), m_browseTableModel->encoding());
m_browseTableModel->addCondFormat(column, newCondFormat);
browseTableSettings[currentlyBrowsedTableName()].condFormats[column].append(newCondFormat);
}
void MainWindow::clearAllCondFormats(int column)
{
QVector<CondFormat> emptyCondFormatVector = QVector<CondFormat>();
m_browseTableModel->setCondFormats(column, emptyCondFormatVector);
browseTableSettings[currentlyBrowsedTableName()].condFormats[column].clear();
}
void MainWindow::editEncryption()
{
#ifdef ENABLE_SQLCIPHER
+30 -8
View File
@@ -3,6 +3,8 @@
#include "sqlitedb.h"
#include "PlotDock.h"
#include "Palette.h"
#include "CondFormat.h"
#include <QMainWindow>
#include <QMap>
@@ -26,10 +28,28 @@ class MainWindow;
struct BrowseDataTableSettings
{
int sortOrderIndex;
Qt::SortOrder sortOrderMode;
struct SortedColumn
{
SortedColumn() :
index(0),
mode(Qt::AscendingOrder)
{}
SortedColumn(int index_, Qt::SortOrder mode_) :
index(index_),
mode(mode_)
{}
SortedColumn(int index_, int mode_) :
index(index_),
mode(static_cast<Qt::SortOrder>(mode_))
{}
int index;
Qt::SortOrder mode;
};
QVector<SortedColumn> sortOrder;
QMap<int, int> columnWidths;
QMap<int, QString> filterValues;
QMap<int, QVector<CondFormat>> condFormats;
QMap<int, QString> displayFormats;
bool showRowid;
QString encoding;
@@ -39,18 +59,16 @@ struct BrowseDataTableSettings
QMap<int, bool> hiddenColumns;
BrowseDataTableSettings() :
sortOrderIndex(0),
sortOrderMode(Qt::AscendingOrder),
showRowid(false)
{
}
friend QDataStream& operator>>(QDataStream& stream, BrowseDataTableSettings& object)
{
stream >> object.sortOrderIndex;
int sortordermode;
stream >> sortordermode;
object.sortOrderMode = static_cast<Qt::SortOrder>(sortordermode);
int sortOrderIndex, sortOrderMode;
stream >> sortOrderIndex;
stream >> sortOrderMode;
object.sortOrder.push_back(SortedColumn(sortOrderIndex, sortOrderMode));
stream >> object.columnWidths;
stream >> object.filterValues;
stream >> object.displayFormats;
@@ -177,6 +195,8 @@ private:
QString defaultBrowseTableEncoding;
Palette m_condFormatPalette;
void init();
void clearCompleterModelsFields();
@@ -278,6 +298,8 @@ private slots:
void saveProject();
void fileAttach();
void updateFilter(int column, const QString& value);
void addCondFormat(int column, const QString& value);
void clearAllCondFormats(int column);
void editEncryption();
void on_buttonClearFilters_clicked();
void copyCurrentCreateStatement();
+2 -2
View File
@@ -194,10 +194,10 @@ You can drag SQL statements from an object row and drop them into other applicat
<item>
<widget class="QToolButton" name="buttonPrintTable">
<property name="toolTip">
<string>Print currrently browsed table data [Ctrl+P]</string>
<string>Print currently browsed table data [Ctrl+P]</string>
</property>
<property name="whatsThis">
<string>Print currrently browsed table data. Print selection if more than one cell is selected.</string>
<string>Print currently browsed table data. Print selection if more than one cell is selected.</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
+120
View File
@@ -0,0 +1,120 @@
#include "Palette.h"
#include <QPalette>
Palette::Palette()
: m_lastColourIndex(0)
{
}
QColor Palette::nextSerialColor(bool dark)
{
if (dark) {
switch(m_lastColourIndex++)
{
case 0:
return QColor(0, 69, 134);
break;
case 1:
return QColor(255, 66, 14);
break;
case 2:
return QColor(255, 211, 32);
break;
case 3:
return QColor(87, 157, 28);
break;
case 4:
return QColor(126, 0, 33);
break;
case 5:
return QColor(131, 202, 255);
break;
case 6:
return QColor(49, 64, 4);
break;
case 7:
return QColor(174, 207, 0);
break;
case 8:
return QColor(75, 31, 111);
break;
case 9:
return QColor(255, 149, 14);
break;
case 10:
return QColor(197, 00, 11);
break;
case 11:
// Since this is the last colour in our table, reset the counter back
// to the first colour
m_lastColourIndex = 0;
return QColor(0, 132, 209);
break;
default:
// NOTE: This shouldn't happen!
m_lastColourIndex = 0;
return QColor(0, 0, 0);
break;
}
} else {
// TODO: review the bright colours
switch(m_lastColourIndex++)
{
case 0:
return QColor(Qt::yellow);
break;
case 1:
return QColor(Qt::cyan);
break;
case 2:
return QColor(Qt::green);
break;
case 3:
return QColor(Qt::magenta);
break;
case 4:
return QColor(Qt::lightGray);
break;
case 5:
return QColor("beige");
break;
case 6:
return QColor("lightblue");
break;
case 7:
return QColor("turquoise");
break;
case 8:
return QColor("mediumspringgreen");
break;
case 9:
return QColor("lightskyblue");
break;
case 10:
return QColor("moccasin");
break;
case 11:
// Since this is the last colour in our table, reset the counter back
// to the first colour
m_lastColourIndex = 0;
return QColor("pink");
break;
default:
// NOTE: This shouldn't happen!
return QColor(0, 0, 0);
break;
}
}
}
bool Palette::appHasDarkTheme()
{
QColor backgroundColour = QPalette().color(QPalette::Active, QPalette::Base);
QColor foregroundColour = QPalette().color(QPalette::Active, QPalette::Text);
return backgroundColour.value() < foregroundColour.value();
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef PALETTE_H
#define PALETTE_H
#include <QColor>
// Class providing series of colours in a given dark or light side of
// the spectrum.
class Palette
{
public:
Palette();
QColor nextSerialColor(bool dark = true);
static bool appHasDarkTheme();
private:
int m_lastColourIndex;
};
#endif
+6 -54
View File
@@ -492,56 +492,7 @@ void PlotDock::on_treePlotColumns_itemChanged(QTreeWidgetItem* changeitem, int c
// Generate a default colour if none isn't set yet
QColor colour = changeitem->backgroundColor(column);
if(!colour.isValid())
{
static int last_colour_index = 0;
switch(last_colour_index++)
{
case 0:
colour = QColor(0, 69, 134);
break;
case 1:
colour = QColor(255, 66, 14);
break;
case 2:
colour = QColor(255, 211, 32);
break;
case 3:
colour = QColor(87, 157, 28);
break;
case 4:
colour = QColor(126, 0, 33);
break;
case 5:
colour = QColor(131, 202, 255);
break;
case 6:
colour = QColor(49, 64, 4);
break;
case 7:
colour = QColor(174, 207, 0);
break;
case 8:
colour = QColor(75, 31, 111);
break;
case 9:
colour = QColor(255, 149, 14);
break;
case 10:
colour = QColor(197, 00, 11);
break;
case 11:
colour = QColor(0, 132, 209);
// Since this is the last colour in our table, reset the counter back
// to the first colour
last_colour_index = 0;
break;
default:
// NOTE: This shouldn't happen!
colour = QColor(0, 0, 0);
break;
}
}
colour = m_graphPalette.nextSerialColor(true);
// Set colour
changeitem->setBackgroundColor(column, colour);
@@ -606,10 +557,11 @@ void PlotDock::on_treePlotColumns_itemDoubleClicked(QTreeWidgetItem* item, int c
void PlotDock::on_butSavePlot_clicked()
{
QString fileName = FileDialog::getSaveFileName(this,
tr("Choose a filename to save under"),
tr("PNG(*.png);;JPG(*.jpg);;PDF(*.pdf);;BMP(*.bmp);;All Files(*)")
);
QString fileName = FileDialog::getSaveFileName(
CreateDataFile,
this,
tr("Choose a filename to save under"),
tr("PNG(*.png);;JPG(*.jpg);;PDF(*.pdf);;BMP(*.bmp);;All Files(*)"));
if(!fileName.isEmpty())
{
if(fileName.endsWith(".png", Qt::CaseInsensitive))
+3
View File
@@ -1,6 +1,8 @@
#ifndef PLOTDOCK_H
#define PLOTDOCK_H
#include "Palette.h"
#include <QDialog>
#include <QVariant>
#include <QMenu>
@@ -89,6 +91,7 @@ private:
QMenu* m_contextMenu;
bool m_showLegend;
bool m_stackedBars;
Palette m_graphPalette;
/*!
* \brief guessdatatype try to parse the first 10 rows and decide the datatype
+4 -1
View File
@@ -56,6 +56,7 @@ PreferencesDialog::~PreferencesDialog()
void PreferencesDialog::chooseLocation()
{
QString s = FileDialog::getExistingDirectory(
NoSpecificType,
this,
tr("Choose a directory"),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
@@ -368,6 +369,7 @@ bool PreferencesDialog::eventFilter(QObject *obj, QEvent *event)
void PreferencesDialog::addExtension()
{
QString file = FileDialog::getOpenFileName(
OpenExtensionFile,
this,
tr("Select extension file"),
tr("Extensions(*.so *.dll);;All files(*)"));
@@ -503,7 +505,7 @@ void PreferencesDialog::addClientCertificate()
{
// Get certificate file to import and abort here if no file gets selected
// NOTE: We assume here that this file contains both, certificate and private key!
QString path = FileDialog::getOpenFileName(this, tr("Import certificate file"), "*.pem");
QString path = FileDialog::getOpenFileName(OpenCertificateFile, this, tr("Import certificate file"), "*.pem");
if(path.isEmpty())
return;
@@ -575,6 +577,7 @@ void PreferencesDialog::addClientCertToTable(const QString& path, const QSslCert
void PreferencesDialog::chooseRemoteCloneDirectory()
{
QString s = FileDialog::getExistingDirectory(
NoSpecificType,
this,
tr("Choose a directory"),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
+67 -46
View File
@@ -986,16 +986,18 @@ bool DBBrowserDB::executeMultiSQL(const QString& statement, bool dirty, bool log
return true;
}
QVariant DBBrowserDB::querySingleValueFromDb(const QString& statement, bool log)
QByteArray DBBrowserDB::querySingleValueFromDb(const QString& sql, bool log)
{
waitForDbRelease();
if(!_db)
return QVariant();
return QByteArray();
if(log)
logSQL(statement, kLogMsg_App);
logSQL(sql, kLogMsg_App);
QByteArray utf8Query = statement.toUtf8();
QByteArray retval;
QByteArray utf8Query = sql.toUtf8();
sqlite3_stmt* stmt;
if(sqlite3_prepare_v2(_db, utf8Query, utf8Query.size(), &stmt, nullptr) == SQLITE_OK)
{
@@ -1005,16 +1007,22 @@ QVariant DBBrowserDB::querySingleValueFromDb(const QString& statement, bool log)
{
int bytes = sqlite3_column_bytes(stmt, 0);
if(bytes)
return QByteArray(static_cast<const char*>(sqlite3_column_blob(stmt, 0)), bytes);
retval = QByteArray(static_cast<const char*>(sqlite3_column_blob(stmt, 0)), bytes);
else
return "";
retval = "";
}
sqlite3_finalize(stmt);
} else {
lastErrorMessage = tr("didn't receive any output from %1").arg(sql);
qWarning() << lastErrorMessage;
}
} else {
lastErrorMessage = tr("could not execute command: %1").arg(sqlite3_errmsg(_db));
qWarning() << lastErrorMessage;
}
return QVariant();
return retval;
}
bool DBBrowserDB::getRow(const sqlb::ObjectIdentifier& table, const QString& rowid, QVector<QByteArray>& rowdata)
@@ -1296,7 +1304,7 @@ bool DBBrowserDB::addColumn(const sqlb::ObjectIdentifier& tablename, const sqlb:
return executeSQL(sql);
}
bool DBBrowserDB::alterTable(const sqlb::ObjectIdentifier& tablename, const sqlb::Table& table, const QString& name, const sqlb::Field* to, int move, QString newSchemaName)
bool DBBrowserDB::alterTable(const sqlb::ObjectIdentifier& tablename, const sqlb::Table& table, QString name, const sqlb::Field* to, int move, QString newSchemaName)
{
/*
* USE CASES:
@@ -1306,16 +1314,6 @@ bool DBBrowserDB::alterTable(const sqlb::ObjectIdentifier& tablename, const sqlb
* 4) Set table, name, to and move: Change table constraints, rename/edit column and move it.
*/
// NOTE: This function is working around the incomplete ALTER TABLE command in SQLite.
// If SQLite should fully support this command one day, this entire
// function can be changed to executing something like this:
//QString sql;
//if(to.isNull())
// sql = QString("ALTER TABLE %1 DROP COLUMN %2;").arg(sqlb::escapeIdentifier(table)).arg(sqlb::escapeIdentifier(column));
//else
// sql = QString("ALTER TABLE %1 MODIFY %2 %3").arg(sqlb::escapeIdentifier(tablename)).arg(sqlb::escapeIdentifier(to)).arg(type); // This is wrong...
//return executeSQL(sql);
// TODO: This function needs to be cleaned up. It might make sense to split it up in several parts than can be reused
// more easily. Besides that, it might make sense to support some potential use cases in a more sophisticated way. These include:
// 1) Allow modifying multiple columns at once in order to only have to call this function (including all its overhead) once instead of once per change.
@@ -1335,7 +1333,7 @@ bool DBBrowserDB::alterTable(const sqlb::ObjectIdentifier& tablename, const sqlb
}
// Create table schema
const sqlb::TablePtr oldSchema = getObjectByName<sqlb::Table>(tablename);
sqlb::TablePtr oldSchema = getObjectByName<sqlb::Table>(tablename);
// Check if field actually exists
if(!name.isNull() && sqlb::findField(oldSchema, name) == oldSchema->fields.end())
@@ -1352,6 +1350,55 @@ bool DBBrowserDB::alterTable(const sqlb::ObjectIdentifier& tablename, const sqlb
return false;
}
// No automatic schema updates from now on
NoStructureUpdateChecks nup(*this);
// Newer versions of SQLite add a better ALTER TABLE support which we can use
#if SQLITE_VERSION_NUMBER >= 3025000
// If the name of the field should be changed do that by using SQLite's ALTER TABLE feature
if(!name.isNull() && to && name != to->name())
{
if(!executeSQL(QString("ALTER TABLE %1 RENAME COLUMN %2 TO %3;")
.arg(tablename.toString())
.arg(sqlb::escapeIdentifier(name))
.arg(sqlb::escapeIdentifier(to->name()))))
{
QString error(tr("renameColumn: renaming the column failed. DB says:\n%1").arg(lastErrorMessage));
revertToSavepoint(savepointName);
lastErrorMessage = error;
return false;
}
// Update our schema representation to get all the changed triggers, views and indices
updateSchema();
// Check if that was all we were asked to do. That's the case if the field is not to be deleted (which we already checked for above), if the field
// is not to be moved, if the table is not to be moved and if nothing besides the name of the field changed in the field definition.
sqlb::Field oldFieldWithNewName = *sqlb::findField(oldSchema, name);
oldFieldWithNewName.setName(to->name());
if(move == 0 && tablename.schema() == newSchemaName && oldFieldWithNewName == *to)
{
// We're done.
// Release the savepoint - everything went fine
if(!releaseSavepoint(savepointName))
{
lastErrorMessage = tr("renameColumn: releasing savepoint failed. DB says: %1").arg(lastErrorMessage);
return false;
}
return true;
} else {
// There's more to do.
// We can have the rest of the function deal with the remaining changes by reloading the table schema as it is now and updating the name of the column
// to change.
oldSchema = getObjectByName<sqlb::Table>(tablename);
name = to->name();
}
}
#endif
// Create a new table with a name that hopefully doesn't exist yet.
// Its layout is exactly the same as the one of the table to change - except for the column to change
// of course, and the table constraints which are copied from the table parameter.
@@ -1391,7 +1438,6 @@ bool DBBrowserDB::alterTable(const sqlb::ObjectIdentifier& tablename, const sqlb
}
// Create the new table
NoStructureUpdateChecks nup(*this);
if(!executeSQL(newSchema.sql(newSchemaName), true, true))
{
QString error(tr("renameColumn: creating new table failed. DB says: %1").arg(lastErrorMessage));
@@ -1689,37 +1735,12 @@ void DBBrowserDB::updateSchema()
QString DBBrowserDB::getPragma(const QString& pragma)
{
waitForDbRelease();
if(!isOpen())
return QString();
QString sql;
if (pragma=="case_sensitive_like")
sql = "SELECT 'x' NOT LIKE 'X'";
else
sql = QString("PRAGMA %1").arg(pragma);
sqlite3_stmt* vm;
const char* tail;
QString retval;
// Get value from DB
int err = sqlite3_prepare_v2(_db, sql.toUtf8(), sql.toUtf8().length(), &vm, &tail);
if(err == SQLITE_OK){
logSQL(sql, kLogMsg_App);
if(sqlite3_step(vm) == SQLITE_ROW)
retval = QString::fromUtf8((const char *) sqlite3_column_text(vm, 0));
else
qWarning() << tr("didn't receive any output from pragma %1").arg(pragma);
sqlite3_finalize(vm);
} else {
qWarning() << tr("could not execute pragma command: %1, %2").arg(err).arg(sqlite3_errmsg(_db));
}
// Return it
return retval;
return querySingleValueFromDb(sql);
}
bool DBBrowserDB::setPragma(const QString& pragma, const QString& value)
+2 -2
View File
@@ -96,7 +96,7 @@ public:
bool executeSQL(QString statement, bool dirtyDB = true, bool logsql = true);
bool executeMultiSQL(const QString& statement, bool dirty = true, bool log = false);
QVariant querySingleValueFromDb(const QString& statement, bool log = true);
QByteArray querySingleValueFromDb(const QString& sql, bool log = true);
const QString& lastError() const { return lastErrorMessage; }
@@ -152,7 +152,7 @@ public:
* @param newSchema Set this to a non-empty string to move the table to a new schema
* @return true if renaming was successful, false if not. In the latter case also lastErrorMessage is set
*/
bool alterTable(const sqlb::ObjectIdentifier& tablename, const sqlb::Table& table, const QString& name, const sqlb::Field* to, int move = 0, QString newSchemaName = QString());
bool alterTable(const sqlb::ObjectIdentifier& tablename, const sqlb::Table& table, QString name, const sqlb::Field* to, int move = 0, QString newSchemaName = QString());
objectMap getBrowsableObjects(const QString& schema) const;
+35 -100
View File
@@ -107,6 +107,7 @@ void SqliteTableModel::reset()
m_vDataTypes.clear();
m_vDisplayFormat.clear();
m_pseudoPk.clear();
m_mCondFormats.clear();
endResetModel();
}
@@ -300,6 +301,24 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const
return QColor(Settings::getValue("databrowser", "null_bg_colour").toString());
else if (nosync_isBinary(index))
return QColor(Settings::getValue("databrowser", "bin_bg_colour").toString());
else if (m_mCondFormats.contains(index.column())) {
QString value = cached_row->at(index.column());
bool isNumber;
value.toFloat(&isNumber);
QString sql;
// For each conditional format for this column,
// if the condition matches the current data, return the associated colour.
for (const CondFormat& eachCondFormat : m_mCondFormats.value(index.column())) {
if (isNumber && !eachCondFormat.sqlCondition().contains("'"))
sql = QString("SELECT %1 %2").arg(value, eachCondFormat.sqlCondition());
else
sql = QString("SELECT '%1' %2").arg(value, eachCondFormat.sqlCondition());
if (m_db.querySingleValueFromDb(sql, false) == "1")
return eachCondFormat.color();
}
}
// Regular case (not null, not binary and no matching conditional format)
return QColor(Settings::getValue("databrowser", "reg_bg_colour").toString());
} else if(role == Qt::ToolTipRole) {
sqlb::ForeignKeyClause fk = getForeignKeyClause(index.column()-1);
@@ -714,104 +733,26 @@ QStringList SqliteTableModel::getColumns(std::shared_ptr<sqlite3> pDb, const QSt
return listColumns;
}
void SqliteTableModel::addCondFormat(int column, const CondFormat& condFormat)
{
m_mCondFormats[column].append(condFormat);
emit layoutChanged();
}
void SqliteTableModel::setCondFormats(int column, const QVector<CondFormat>& condFormats)
{
m_mCondFormats[column] = condFormats;
emit layoutChanged();
}
void SqliteTableModel::updateFilter(int column, const QString& value, bool applyQuery)
{
// Check for any special comparison operators at the beginning of the value string. If there are none default to LIKE.
QString op = "LIKE";
QString val, val2;
QString escape;
bool numeric = false, ok = false;
// range/BETWEEN operator
if (value.contains("~")) {
int sepIdx = value.indexOf('~');
val = value.mid(0, sepIdx);
val2 = value.mid(sepIdx+1);
val.toFloat(&ok);
if (ok) {
val2.toFloat(&ok);
ok = ok && (val.toFloat() < val2.toFloat());
}
}
if (ok) {
op = "BETWEEN";
numeric = true;
} else {
val.clear();
val2.clear();
if(value.left(2) == ">=" || value.left(2) == "<=" || value.left(2) == "<>")
{
// Check if we're filtering for '<> NULL'. In this case we need a special comparison operator.
if(value.left(2) == "<>" && value.mid(2) == "NULL")
{
// We are filtering for '<>NULL'. Override the comparison operator to search for NULL values in this column. Also treat search value (NULL) as number,
// in order to avoid putting quotes around it.
op = "IS NOT";
numeric = true;
val = "NULL";
} else if(value.left(2) == "<>" && value.mid(2) == "''") {
// We are filtering for "<>''", i.e. for everything which is not an empty string
op = "<>";
numeric = true;
val = "''";
} else {
value.mid(2).toFloat(&numeric);
op = value.left(2);
val = value.mid(2);
}
} else if(value.left(1) == ">" || value.left(1) == "<") {
value.mid(1).toFloat(&numeric);
op = value.left(1);
val = value.mid(1);
} else if(value.left(1) == "=") {
val = value.mid(1);
// Check if value to compare with is 'NULL'
if(val != "NULL")
{
// It's not, so just compare normally to the value, whatever it is.
op = "=";
} else {
// It is NULL. Override the comparison operator to search for NULL values in this column. Also treat search value (NULL) as number,
// in order to avoid putting quotes around it.
op = "IS";
numeric = true;
}
} else {
// Keep the default LIKE operator
// Set the escape character if one has been specified in the settings dialog
QString escape_character = Settings::getValue("databrowser", "filter_escape").toString();
if(escape_character == "'") escape_character = "''";
if(escape_character.length())
escape = QString("ESCAPE '%1'").arg(escape_character);
// Add % wildcards at the start and at the beginning of the filter query, but only if there weren't set any
// wildcards manually. The idea is to assume that a user who's just typing characters expects the wildcards to
// be added but a user who adds them herself knows what she's doing and doesn't want us to mess up her query.
if(!value.contains("%"))
{
val = value;
val.prepend('%');
val.append('%');
}
}
}
if(val.isEmpty())
val = value;
QString whereClause = CondFormat::filterToSqlCondition(value, m_encoding);
// If the value was set to an empty string remove any filter for this column. Otherwise insert a new filter rule or replace the old one if there is already one
if(val == "" || val == "%" || val == "%%")
if(whereClause.isEmpty())
m_mWhere.remove(column);
else {
// Quote and escape value, but only if it's not numeric and not the empty string sequence
if(!numeric && val != "''")
val = QString("'%1'").arg(val.replace("'", "''"));
QString whereClause(op + " " + QString(encode(val.toUtf8())));
if (!val2.isEmpty())
whereClause += " AND " + QString(encode(val2.toUtf8()));
whereClause += " " + escape;
m_mWhere.insert(column, whereClause);
}
@@ -858,18 +799,12 @@ bool SqliteTableModel::nosync_isBinary(const QModelIndex& index) const
QByteArray SqliteTableModel::encode(const QByteArray& str) const
{
if(m_encoding.isEmpty())
return str;
else
return QTextCodec::codecForName(m_encoding.toUtf8())->fromUnicode(str);
return encodeString(str, m_encoding);
}
QByteArray SqliteTableModel::decode(const QByteArray& str) const
{
if(m_encoding.isEmpty())
return str;
else
return QTextCodec::codecForName(m_encoding.toUtf8())->toUnicode(str).toUtf8();
return decodeString(str, m_encoding);
}
Qt::DropActions SqliteTableModel::supportedDropActions() const
+6
View File
@@ -6,10 +6,12 @@
#include <QVector>
#include <QThread>
#include <QMutex>
#include <QColor>
#include <memory>
#include "sqlitetypes.h"
#include "RowCache.h"
#include "CondFormat.h"
struct sqlite3;
class DBBrowserDB;
@@ -109,6 +111,9 @@ public:
// Helper function for removing all comments from a SQL query
static void removeCommentsFromQuery(QString& query);
void addCondFormat(int column, const CondFormat& condFormat);
void setCondFormats(int column, const QVector<CondFormat>& condFormats);
public slots:
void updateFilter(int column, const QString& value, bool applyQuery = true);
@@ -175,6 +180,7 @@ private:
QMap<int, QString> m_mWhere;
QVector<QString> m_vDisplayFormat;
QVector<int> m_vDataTypes;
QMap<int, QVector<CondFormat>> m_mCondFormats;
/**
* @brief m_chunkSize Size of the next chunk fetch more will try to fetch.
+6 -2
View File
@@ -68,7 +68,9 @@ HEADERS += \
FileExtensionManager.h \
Data.h \
CipherSettings.h \
DotenvFormat.h
DotenvFormat.h \
Palette.h \
CondFormat.h
SOURCES += \
sqlitedb.cpp \
@@ -113,7 +115,9 @@ SOURCES += \
FileExtensionManager.cpp \
Data.cpp \
CipherSettings.cpp \
DotenvFormat.cpp
DotenvFormat.cpp \
Palette.cpp \
CondFormat.cpp
RESOURCES += icons/icons.qrc \
translations/flags/flags.qrc \
+4
View File
@@ -19,6 +19,7 @@ set(TESTSQLOBJECTS_SRC
../Data.cpp
../CipherSettings.cpp
../DotenvFormat.cpp
../CondFormat.cpp
)
set(TESTSQLOBJECTS_HDR
@@ -36,6 +37,7 @@ set(TESTSQLOBJECTS_MOC_HDR
testsqlobjects.h
../CipherSettings.h
../DotenvFormat.h
../CondFormat.h
)
if(sqlcipher)
@@ -100,6 +102,7 @@ set(TESTREGEX_SRC
../Data.cpp
../CipherSettings.cpp
../DotenvFormat.cpp
../CondFormat.cpp
)
set(TESTREGEX_HDR
@@ -117,6 +120,7 @@ set(TESTREGEX_MOC_HDR
TestRegex.h
../CipherSettings.h
../DotenvFormat.h
../CondFormat.h
)
if(sqlcipher)
+2 -2
View File
@@ -2243,12 +2243,12 @@ You can drag SQL statements from an object row and drop them into other applicat
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
+2 -2
View File
@@ -3286,12 +3286,12 @@ You can drag SQL statements from an object row and drop them into other applicat
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
+2 -2
View File
@@ -2479,12 +2479,12 @@ Sie können SQL-Statements aus einer Objektzeile fassen und in anderen Anwendung
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation>Aktuell angezeigte Tabellendaten drucken [Strg+P]</translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation>Die aktuell angezeigten Tabellendaten drucken. Druckauswahl, falls mehr als eine Zelle ausgewählt ist.</translation>
</message>
<message>
+2 -2
View File
@@ -2359,12 +2359,12 @@ You can drag SQL statements from an object row and drop them into other applicat
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
+7 -7
View File
@@ -85,7 +85,7 @@
<message>
<location filename="../AddRecordDialog.ui" line="84"/>
<source>Value</source>
<translation >Valor</translation>
<translation>Valor</translation>
</message>
<message>
<location filename="../AddRecordDialog.ui" line="87"/>
@@ -182,12 +182,12 @@
<message>
<location filename="../Application.cpp" line="90"/>
<source> -s, --sql [file] Execute this SQL file after opening the DB</source>
<translation> -s, --sql [archivo] Ejecutar este archivo de SQL tras abrir la base de datos</translation>
<translation> -s, --sql [archivo] Ejecutar este archivo de SQL tras abrir la base de datos</translation>
</message>
<message>
<location filename="../Application.cpp" line="91"/>
<source> -t, --table [table] Browse this table after opening the DB</source>
<translation> -t, --table [table] Mostrar esta tabla en la hoja de datos tras abrir la base de datos</translation>
<translation> -t, --table [tabla] Mostrar esta tabla en la hoja de datos tras abrir la base de datos</translation>
</message>
<message>
<location filename="../Application.cpp" line="89"/>
@@ -197,7 +197,7 @@
<message>
<location filename="../Application.cpp" line="92"/>
<source> -R, --read-only Open database in read-only mode</source>
<translation> -R, --read-only\tAbrir base de datos en modo de solo-lectura</translation>
<translation> -R, --read-only Abrir base de datos en modo de solo-lectura</translation>
</message>
<message>
<location filename="../Application.cpp" line="93"/>
@@ -2850,12 +2850,12 @@ Usted puede arrastrar sentencias SQL desde una fila de objeto y soltarlas en otr
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation>Imprime los datos de la tabla mostrada actualmente [Ctrl+P]</translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation>Imprime los datos de la tabla mostrada actualmente. Imprime la selección si se ha seleccionado más de una celda.</translation>
</message>
<message>
@@ -4493,7 +4493,7 @@ Are you sure?</source>
<message>
<location filename="../MainWindow.cpp" line="3316"/>
<source>Set a new name for the SQL tab. Use the &apos;&amp;&amp;&apos; character to allow using the following character as a keyboard shortcut.</source>
<translation>Establezca el nuevo nombre para la pestaña SQL. Use el carácter «&amp;» para permitir usar el carácter siguiente como un atajo de teclado.</translation>
<translation>Establezca el nuevo nombre para la pestaña SQL. Use el carácter «&amp;&amp;» para permitir usar el carácter siguiente como un atajo de teclado.</translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="3359"/>
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -3242,12 +3242,12 @@ You can drag SQL statements from an object row and drop them into other applicat
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
+2 -2
View File
@@ -2566,12 +2566,12 @@ You can drag SQL statements from the Schema column and drop them into the SQL ed
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
+2 -2
View File
@@ -3706,11 +3706,11 @@ Faça um backup!</translation>
<translation>Pressione Help para abrir a página de referência SQL correspondente.</translation>
</message>
<message>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation>Imprimir dados da tabela atual [Ctrl+P]</translation>
</message>
<message>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation>Imprimir dados da tabela atual. Imprime a seleção se mais de uma célula está selecionada.</translation>
</message>
<message>
+2 -2
View File
@@ -2274,12 +2274,12 @@ x~y Диапазон: значения между x и y</translation>
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation>Печатать отображаемую таблицу [Ctrl+P]</translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation>Распечатывайте текущие данные таблицы. Выбор печати, если выбрано несколько ячеек.</translation>
</message>
<message>
+2 -2
View File
@@ -3100,12 +3100,12 @@ You can drag SQL statements from an object row and drop them into other applicat
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
+2 -2
View File
@@ -3625,12 +3625,12 @@ You can drag SQL statements from an object row and drop them into other applicat
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -3026,12 +3026,12 @@ You can drag SQL statements from an object row and drop them into other applicat
</message>
<message>
<location filename="../MainWindow.ui" line="197"/>
<source>Print currrently browsed table data [Ctrl+P]</source>
<source>Print currently browsed table data [Ctrl+P]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="200"/>
<source>Print currrently browsed table data. Print selection if more than one cell is selected.</source>
<source>Print currently browsed table data. Print selection if more than one cell is selected.</source>
<translation type="unfinished"></translation>
</message>
<message>