Module can register action Window adds this action to its menu Clicking the menu item for the action has the expected result But menu structure still needs work (everything is now put into one dropdown menu)
691 lines
19 KiB
C++
691 lines
19 KiB
C++
|
|
#include "QueryTab.h"
|
|
#include "ui_QueryTab.h"
|
|
#include "SqlSyntaxHighlighter.h"
|
|
#include <QStandardPaths>
|
|
#include <QPushButton>
|
|
#include <QAction>
|
|
#include <QFileDialog>
|
|
#include <QStatusBar>
|
|
#include <QMessageBox>
|
|
#include <QTabWidget>
|
|
#include <QTextCodec>
|
|
#include <QTextDocumentFragment>
|
|
#include <QTextStream>
|
|
#include <QClipboard>
|
|
#include "ExplainTreeModelItem.h"
|
|
#include "json/json.h"
|
|
#include "OpenDatabase.h"
|
|
#include "catalog/PgDatabaseCatalog.h"
|
|
#include "QueryParamListController.h"
|
|
#include "util.h"
|
|
#include "GlobalIoService.h"
|
|
#include "UserConfiguration.h"
|
|
#include "plugin_support/IPluginContentWidgetContext.h"
|
|
#include "plugin_support/PluginRegister.h"
|
|
|
|
QueryTab::QueryTab(IPluginContentWidgetContext *context_, QWidget *parent)
|
|
: PluginContentWidget(context_, parent)
|
|
, ui(new Ui::QueryTab)
|
|
, m_dbConnection(*getGlobalAsioIoService())
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
m_config = context()->getDatabase()->config();
|
|
m_catalog = context()->getDatabase()->catalog();
|
|
|
|
connect(&m_dbConnection, &ASyncDBConnection::onStateChanged, this, &QueryTab::connectionStateChanged);
|
|
connect(&m_dbConnection, &ASyncDBConnection::onNotice, this, &QueryTab::receiveNotice);
|
|
|
|
ui->queryEdit->setFont(UserConfiguration::instance()->codeFont());
|
|
|
|
highlighter = new SqlSyntaxHighlighter(ui->queryEdit->document());
|
|
auto open_database = context()->getDatabase();
|
|
if (open_database) {
|
|
auto cat = open_database->catalog();
|
|
highlighter->setTypes(*cat->types());
|
|
}
|
|
|
|
connect(ui->queryEdit, &QPlainTextEdit::textChanged, this, &QueryTab::queryTextChanged);
|
|
|
|
m_queryParamListController = new QueryParamListController(ui->paramTableView, open_database, this);
|
|
connect(ui->addButton, &QPushButton::clicked, m_queryParamListController,
|
|
&QueryParamListController::on_addParam);
|
|
connect(ui->removeButton, &QPushButton::clicked, m_queryParamListController,
|
|
&QueryParamListController::on_removeParam);
|
|
|
|
startConnect();
|
|
}
|
|
|
|
QueryTab::~QueryTab()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
bool QueryTab::canClose()
|
|
{
|
|
bool can_close;
|
|
if (m_queryTextChanged) {
|
|
can_close = continueWithoutSavingWarning();
|
|
}
|
|
else {
|
|
can_close = true;
|
|
}
|
|
return can_close;
|
|
}
|
|
|
|
void QueryTab::newdoc()
|
|
{
|
|
ui->queryEdit->clear();
|
|
setFileName(tr("new"));
|
|
m_queryTextChanged = false;
|
|
m_new = true;
|
|
}
|
|
|
|
bool QueryTab::load(const QString &filename)
|
|
{
|
|
bool result = false;
|
|
QFile file(filename);
|
|
if (file.open(QIODevice::ReadOnly)) {
|
|
QByteArray ba = file.readAll();
|
|
const char *ptr = ba.constData();
|
|
|
|
QTextCodec *codec = QTextCodec::codecForUtfText(ba, QTextCodec::codecForName("utf-8"));
|
|
QTextCodec::ConverterState state;
|
|
QString text = codec->toUnicode(ptr, ba.size(), &state);
|
|
if (state.invalidChars > 0) {
|
|
file.reset();
|
|
QTextStream stream(&file);
|
|
text = stream.readAll();
|
|
}
|
|
|
|
ui->queryEdit->setPlainText(text);
|
|
m_queryTextChanged = false;
|
|
setFileName(filename);
|
|
m_new = false;
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool QueryTab::save()
|
|
{
|
|
bool result;
|
|
if (m_fileName.isEmpty() || m_new) {
|
|
result = saveAs();
|
|
}
|
|
else {
|
|
result = saveSqlTo(m_fileName);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool QueryTab::saveAs()
|
|
{
|
|
bool result = false;
|
|
QString filename = promptUserForSaveSqlFilename();
|
|
if (!filename.isEmpty()) {
|
|
result = saveSqlTo(filename);
|
|
if (result) {
|
|
setFileName(filename);
|
|
m_new = false;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void QueryTab::saveCopyAs()
|
|
{
|
|
QString filename = promptUserForSaveSqlFilename();
|
|
if (!filename.isEmpty()) {
|
|
saveSqlTo(filename);
|
|
}
|
|
}
|
|
|
|
void QueryTab::execute()
|
|
{
|
|
if (m_dbConnection.state() == ASyncDBConnection::State::Connected) {
|
|
addLog("Query clicked");
|
|
|
|
clearResult();
|
|
ui->messagesEdit->clear();
|
|
|
|
std::string cmd = getCommandUtf8();
|
|
m_stopwatch.start();
|
|
|
|
auto cb = [this](Expected<std::shared_ptr<Pgsql::Result>> res, qint64 elapsedms)
|
|
{
|
|
if (res.valid()) {
|
|
auto && dbresult = res.get();
|
|
QMetaObject::invokeMethod(this, "query_ready",
|
|
Q_ARG(std::shared_ptr<Pgsql::Result>, dbresult),
|
|
Q_ARG(qint64, elapsedms));
|
|
}
|
|
else {
|
|
/// \todo handle error
|
|
|
|
}
|
|
};
|
|
|
|
if (m_queryParamListController->empty())
|
|
m_dbConnection.send(cmd, cb);
|
|
else
|
|
m_dbConnection.send(cmd, m_queryParamListController->params(), cb);
|
|
}
|
|
}
|
|
|
|
void QueryTab::explain(bool analyze)
|
|
{
|
|
ui->explainTreeView->setModel(nullptr);
|
|
explainModel.reset();
|
|
ui->messagesEdit->clear();
|
|
|
|
addLog("Explain clicked");
|
|
|
|
std::string analyze_str;
|
|
if (analyze) {
|
|
analyze_str = "ANALYZE, BUFFERS, ";
|
|
}
|
|
m_stopwatch.start();
|
|
std::string cmd = "EXPLAIN (" + analyze_str + "VERBOSE, FORMAT JSON) " + getCommandUtf8();
|
|
|
|
auto cb = [this](Expected<std::shared_ptr<Pgsql::Result>> exp_res, qint64 )
|
|
{
|
|
if (exp_res.valid()) {
|
|
// Process explain data seperately
|
|
auto res = exp_res.get();
|
|
if (res) {
|
|
std::thread([this,res]()
|
|
{
|
|
std::shared_ptr<ExplainRoot> explain;
|
|
if (res->cols() == 1 && res->rows() == 1) {
|
|
std::string s = res->val(0, 0);
|
|
Json::Value root; // will contains the root value after parsing.
|
|
Json::Reader reader;
|
|
bool parsingSuccessful = reader.parse(s, root);
|
|
if (parsingSuccessful) {
|
|
explain = ExplainRoot::createFromJson(root);
|
|
}
|
|
}
|
|
QMetaObject::invokeMethod(this, "explain_ready",
|
|
Q_ARG(ExplainRoot::SPtr, explain));
|
|
}).detach();
|
|
}
|
|
}
|
|
};
|
|
|
|
if (m_queryParamListController->empty())
|
|
m_dbConnection.send(cmd, cb);
|
|
else
|
|
m_dbConnection.send(cmd, m_queryParamListController->params(), cb);
|
|
}
|
|
|
|
void QueryTab::cancel()
|
|
{
|
|
m_dbConnection.cancel();
|
|
}
|
|
|
|
|
|
void QueryTab::setFileName(const QString &filename)
|
|
{
|
|
m_fileName = filename;
|
|
QFileInfo fileInfo(filename);
|
|
QString fn(fileInfo.fileName());
|
|
context()->setCaption(this, fn, m_fileName);
|
|
}
|
|
|
|
bool QueryTab::continueWithoutSavingWarning()
|
|
{
|
|
QMessageBox msgBox;
|
|
msgBox.setIcon(QMessageBox::Warning);
|
|
msgBox.setText(QString("Save changes in document \"%1\" before closing?").arg(m_fileName));
|
|
msgBox.setInformativeText("The changes will be lost when you choose Discard.");
|
|
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
|
|
msgBox.setDefaultButton(QMessageBox::Cancel);
|
|
int ret = msgBox.exec();
|
|
if (ret == QMessageBox::Save) {
|
|
if (!save()) {
|
|
// save failed or was a saveAs and was cancelled, don't close!
|
|
ret = QMessageBox::Cancel;
|
|
}
|
|
}
|
|
|
|
return ret != QMessageBox::Cancel;
|
|
}
|
|
|
|
bool QueryTab::saveSqlTo(const QString &filename)
|
|
{
|
|
bool result = false;
|
|
QFileInfo fileinfo(filename);
|
|
|
|
|
|
QFile file(filename);
|
|
if (file.open(QIODevice::WriteOnly)) {
|
|
QTextStream stream(&file);
|
|
stream.setCodec("utf-8");
|
|
QString text = ui->queryEdit->toPlainText();
|
|
stream << text;
|
|
|
|
stream.flush();
|
|
if (stream.status() == QTextStream::Ok) {
|
|
m_queryTextChanged = false;
|
|
result = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
QString QueryTab::promptUserForSaveSqlFilename()
|
|
{
|
|
QString home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory);
|
|
return QFileDialog::getSaveFileName(this, tr("Save query"), home_dir, tr("SQL file (*.sql)"));
|
|
}
|
|
|
|
void QueryTab::queryTextChanged()
|
|
{
|
|
m_queryTextChanged = true;
|
|
}
|
|
|
|
|
|
void QueryTab::connectionStateChanged(ASyncDBConnection::State state)
|
|
{
|
|
QString iconname;
|
|
switch (state) {
|
|
case ASyncDBConnection::State::NotConnected:
|
|
case ASyncDBConnection::State::Connecting:
|
|
iconname = "document_red.png";
|
|
break;
|
|
case ASyncDBConnection::State::Connected:
|
|
iconname = "document_green.png";
|
|
break;
|
|
case ASyncDBConnection::State::QuerySend:
|
|
case ASyncDBConnection::State::CancelSend:
|
|
iconname = "document_yellow.png";
|
|
break;
|
|
case ASyncDBConnection::State::Terminating:
|
|
break;
|
|
}
|
|
context()->setIcon(this, iconname);
|
|
}
|
|
|
|
|
|
void QueryTab::addLog(QString s)
|
|
{
|
|
QTextCursor text_cursor = QTextCursor(ui->edtLog->document());
|
|
text_cursor.movePosition(QTextCursor::End);
|
|
text_cursor.insertText(s + "\r\n");
|
|
}
|
|
|
|
void QueryTab::receiveNotice(Pgsql::ErrorDetails notice)
|
|
{
|
|
ui->messagesEdit->append(QString::fromStdString(notice.errorMessage));
|
|
|
|
ui->messagesEdit->append(QString::fromStdString(notice.severity));
|
|
ui->messagesEdit->append(QString("At position: %1").arg(notice.statementPosition));
|
|
ui->messagesEdit->append(QString::fromStdString("State: " + notice.state));
|
|
ui->messagesEdit->append(QString::fromStdString("Primary: " + notice.messagePrimary));
|
|
ui->messagesEdit->append(QString::fromStdString("Detail: " + notice.messageDetail));
|
|
ui->messagesEdit->append(QString::fromStdString("Hint: " + notice.messageHint));
|
|
ui->messagesEdit->append(QString::fromStdString("Context: " + notice.context));
|
|
// std::string state; ///< PG_DIAG_SQLSTATE Error code as listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
|
|
// std::string severity;
|
|
// std::string messagePrimary;
|
|
// std::string messageDetail;
|
|
// std::string messageHint;
|
|
// int statementPosition; ///< First character is one, measured in characters not bytes!
|
|
// std::string context;
|
|
|
|
// int internalPosition;
|
|
// std::string internalQuery;
|
|
// std::string schemaName;
|
|
// std::string tableName;
|
|
// std::string columnName;
|
|
// std::string datatypeName;
|
|
// std::string constraintName;
|
|
// std::string sourceFile;
|
|
// std::string sourceLine;
|
|
// std::string sourceFunction;
|
|
|
|
// QTextCursor cursor = ui->messagesEdit->textCursor();
|
|
// cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
|
|
|
|
// QTextTable *table = cursor.insertTable(4, 2);
|
|
// if (table) {
|
|
// table->cellAt(1, 0).firstCursorPosition().insertText("State");
|
|
// table->cellAt(1, 1).firstCursorPosition().insertText(QString::fromStdString(notice.state));
|
|
// table->cellAt(2, 0).firstCursorPosition().insertText("Primary");
|
|
// table->cellAt(2, 1).firstCursorPosition().insertText(QString::fromStdString(notice.messagePrimary));
|
|
// table->cellAt(3, 0).firstCursorPosition().insertText("Detail");
|
|
// table->cellAt(3, 1).firstCursorPosition().insertText(QString::fromStdString(notice.messageDetail));
|
|
// }
|
|
|
|
// syntax error at or near "limit
|
|
// statementPosition
|
|
}
|
|
|
|
void QueryTab::startConnect()
|
|
{
|
|
m_dbConnection.setupConnection(m_config);
|
|
}
|
|
|
|
void QueryTab::explain_ready(ExplainRoot::SPtr explain)
|
|
{
|
|
m_stopwatch.stop();
|
|
if (explain) {
|
|
addLog("Explain ready");
|
|
QString times_str;
|
|
if (explain->totalRuntime > 0.f)
|
|
times_str = QString("Total time: %1").arg(
|
|
msfloatToHumanReadableString(explain->totalRuntime));
|
|
else
|
|
times_str = QString("Execution time: %1, Planning time: %2").arg(
|
|
msfloatToHumanReadableString(explain->executionTime)
|
|
, msfloatToHumanReadableString(explain->planningTime));
|
|
ui->lblTimes->setText(times_str);
|
|
explainModel.reset(new QueryExplainModel(nullptr, explain));
|
|
ui->explainTreeView->setModel(explainModel.get());
|
|
ui->explainTreeView->expandAll();
|
|
ui->explainTreeView->setColumnWidth(0, 200);
|
|
ui->explainTreeView->setColumnWidth(1, 80);
|
|
ui->explainTreeView->setColumnWidth(2, 80);
|
|
ui->explainTreeView->setColumnWidth(3, 80);
|
|
ui->explainTreeView->setColumnWidth(4, 80);
|
|
ui->explainTreeView->setColumnWidth(5, 80);
|
|
ui->explainTreeView->setColumnWidth(6, 600);
|
|
ui->tabWidget->setCurrentWidget(ui->explainTab);
|
|
|
|
context()->showStatusMessage(tr("Explain ready."));
|
|
}
|
|
else {
|
|
addLog("Explain no result");
|
|
ui->tabWidget->setCurrentWidget(ui->messageTab);
|
|
// statusBar()->showMessage(tr("Explain failed."));
|
|
}
|
|
}
|
|
|
|
QString QueryTab::getCommand() const
|
|
{
|
|
QString command;
|
|
QTextCursor cursor = ui->queryEdit->textCursor();
|
|
if (cursor.hasSelection()) {
|
|
command = cursor.selection().toPlainText();
|
|
}
|
|
else {
|
|
command = ui->queryEdit->toPlainText();
|
|
}
|
|
return command;
|
|
}
|
|
|
|
std::string QueryTab::getCommandUtf8() const
|
|
{
|
|
return getCommand().toUtf8().data();
|
|
}
|
|
|
|
//QTabWidget *QueryTab::getTabWidget()
|
|
//{
|
|
// QWidget * w = parentWidget();
|
|
// QWidget * p = w->parentWidget();
|
|
// QTabWidget *tw = dynamic_cast<QTabWidget*>(p);
|
|
// return tw;
|
|
//}
|
|
|
|
//void QueryTab::setTabCaption(const QString &caption, const QString &tooltip)
|
|
//{
|
|
// QTabWidget *tabwidget = getTabWidget();
|
|
// if (tabwidget) {
|
|
// int i = tabwidget->indexOf(this);
|
|
// if (i >= 0) {
|
|
// tabwidget->setTabText(i, caption);
|
|
// tabwidget->setTabToolTip(i, tooltip);
|
|
// }
|
|
// }
|
|
|
|
//}
|
|
|
|
void QueryTab::query_ready(std::shared_ptr<Pgsql::Result> dbres, qint64 elapsedms)
|
|
{
|
|
if (dbres) {
|
|
addLog("query_ready with result");
|
|
auto st = dbres->resultStatus();
|
|
if (st == PGRES_TUPLES_OK) {
|
|
//int n_rows = dbres->getRows();
|
|
//QString rowcount_str = QString("rows: %1").arg(dbres->getRows());
|
|
|
|
auto result_model = std::make_shared<QueryResultModel>(nullptr , dbres,
|
|
m_catalog);
|
|
TuplesResultWidget *trw = new TuplesResultWidget;
|
|
trw->setResult(result_model, elapsedms);
|
|
resultList.push_back(trw);
|
|
ui->tabWidget->addTab(trw, "Data");
|
|
if (resultList.size() == 1)
|
|
ui->tabWidget->setCurrentWidget(trw);
|
|
|
|
}
|
|
else {
|
|
if (st == PGRES_COMMAND_OK) {
|
|
int tuples_affected = dbres->tuplesAffected();
|
|
QString msg;
|
|
if (tuples_affected >= 0)
|
|
msg = tr("Query returned succesfully: %1 rows affected, execution time %2")
|
|
.arg(QString::number(tuples_affected))
|
|
.arg(msfloatToHumanReadableString(elapsedms));
|
|
else
|
|
msg = tr("Query returned succesfully, execution time %1")
|
|
.arg(msfloatToHumanReadableString(elapsedms));
|
|
|
|
ui->messagesEdit->append(msg);
|
|
|
|
ui->tabWidget->setCurrentWidget(ui->messageTab);
|
|
}
|
|
else {
|
|
// if (st == PGRES_EMPTY_QUERY) {
|
|
// statusBar()->showMessage(tr("Empty query."));
|
|
// }
|
|
// else if (st == PGRES_COPY_OUT) {
|
|
// statusBar()->showMessage(tr("COPY OUT."));
|
|
// }
|
|
// else if (st == PGRES_COPY_IN) {
|
|
// statusBar()->showMessage(tr("COPY IN."));
|
|
// }
|
|
// else if (st == PGRES_BAD_RESPONSE) {
|
|
// statusBar()->showMessage(tr("BAD RESPONSE."));
|
|
// }
|
|
// else if (st == PGRES_NONFATAL_ERROR) {
|
|
// statusBar()->showMessage(tr("NON FATAL ERROR."));
|
|
// }
|
|
// else if (st == PGRES_FATAL_ERROR) {
|
|
// statusBar()->showMessage(tr("FATAL ERROR."));
|
|
// }
|
|
// else if (st == PGRES_COPY_BOTH) {
|
|
// statusBar()->showMessage(tr("COPY BOTH shouldn't happen is for replication."));
|
|
// }
|
|
// else if (st == PGRES_SINGLE_TUPLE) {
|
|
// statusBar()->showMessage(tr("SINGLE TUPLE result."));
|
|
// }
|
|
// else {
|
|
// statusBar()->showMessage(tr("No tuples returned, possibly an error..."));
|
|
// }
|
|
ui->tabWidget->setCurrentWidget(ui->messageTab);
|
|
auto details = dbres->diagDetails();
|
|
markError(details);
|
|
receiveNotice(details);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
m_stopwatch.stop();
|
|
addLog("query_ready with NO result");
|
|
}
|
|
}
|
|
|
|
void QueryTab::markError(const Pgsql::ErrorDetails &details)
|
|
{
|
|
if (details.statementPosition > 0) {
|
|
QTextCursor cursor = ui->queryEdit->textCursor();
|
|
|
|
// Following finds out the start of the current selection
|
|
// theoreticallly the selection might have changed however
|
|
// theoretically all the text might have changed also so we ignore
|
|
// both issues for know and we solve both when we decide it is to much of
|
|
// a problem but in practice syntax errors come back very quickly...
|
|
int position_offset = 0;
|
|
if (cursor.hasSelection()) {
|
|
position_offset = cursor.selectionStart();
|
|
}
|
|
|
|
cursor.setPosition(details.statementPosition - 1 + position_offset);
|
|
ui->queryEdit->setTextCursor(cursor);
|
|
|
|
int length = 0;
|
|
if (details.state == "42703") {
|
|
std::size_t pos = details.messagePrimary.find('"');
|
|
if (pos != std::string::npos) {
|
|
std::size_t pos2 = details.messagePrimary.find('"', pos+1);
|
|
if (pos2 != std::string::npos) {
|
|
length = static_cast<int>(pos2 - pos);
|
|
}
|
|
}
|
|
}
|
|
else if (details.state == "42P01") {
|
|
std::size_t pos = details.messagePrimary.find('"');
|
|
if (pos != std::string::npos) {
|
|
std::size_t pos2 = details.messagePrimary.find('"', pos+1);
|
|
if (pos2 != std::string::npos) {
|
|
length = static_cast<int>(pos2 - pos);
|
|
}
|
|
}
|
|
}
|
|
ui->queryEdit->addErrorMarker(details.statementPosition - 1 + position_offset, length);
|
|
}
|
|
}
|
|
|
|
void QueryTab::clearResult()
|
|
{
|
|
for (auto e : resultList)
|
|
delete e;
|
|
resultList.clear();
|
|
}
|
|
|
|
void QueryTab::copyQueryAsCString()
|
|
{
|
|
// QString command;
|
|
// QTextCursor cursor = ui->queryEdit->textCursor();
|
|
// if (cursor.hasSelection()) {
|
|
// command = cursor.selection().toPlainText();
|
|
// }
|
|
// else {
|
|
// command = ui->queryEdit->toPlainText();
|
|
// }
|
|
QString command = getCommand();
|
|
QString cs = ConvertToMultiLineCString(command);
|
|
QApplication::clipboard()->setText(cs);
|
|
}
|
|
|
|
#include <codebuilder/CodeBuilder.h>
|
|
#include <codebuilder/DefaultConfigs.h>
|
|
|
|
void QueryTab::copyQueryAsRawCppString()
|
|
{
|
|
QString command = getCommand();
|
|
//auto sql = getAllOrSelectedSql();
|
|
QString cs = ConvertToMultiLineRawCppString(command);
|
|
QApplication::clipboard()->setText(cs);
|
|
}
|
|
|
|
void QueryTab::generateCode()
|
|
{
|
|
QString command = getCommand();
|
|
|
|
if (resultList.empty()) {
|
|
QMessageBox::question(this, "pglab", tr("Please execute the query first"), QMessageBox::Ok);
|
|
}
|
|
if (resultList.size() == 1) {
|
|
std::shared_ptr<const Pgsql::Result> dbres = resultList[0]->GetPgsqlResult();
|
|
//context()->newCodeGenPage(command, dbres);
|
|
}
|
|
}
|
|
|
|
void QueryTab::exportData(const QString &file_name)
|
|
{
|
|
auto widget = ui->tabWidget->currentWidget();
|
|
auto fi = std::find(resultList.begin(), resultList.end(), widget);
|
|
if (fi != resultList.end()) {
|
|
TuplesResultWidget* rw = *fi;
|
|
rw->exportData(file_name);
|
|
}
|
|
}
|
|
|
|
void QueryTab::focusEditor()
|
|
{
|
|
ui->queryEdit->setFocus();
|
|
}
|
|
|
|
|
|
std::vector<QAction*> QueryTab::getToolbarActions()
|
|
{
|
|
if (actions.empty()) {
|
|
QAction *action;
|
|
// New
|
|
// action = new QAction(QIcon(":/icons/new_query_tab.png"), tr("New"), this);
|
|
// action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N));
|
|
// connect(action, &QAction::triggered, this, &QueryTab::);
|
|
// actions.push_back(action);
|
|
// Load
|
|
|
|
// Save
|
|
action = new QAction(QIcon(":/icons/script_save.png"), tr("Save SQL"), this);
|
|
action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
|
|
connect(action, &QAction::triggered, this, &QueryTab::save);
|
|
actions.push_back(action);
|
|
// Save as (menu only)
|
|
// action = new QAction(QIcon(":/icons/script_save.png"), tr("Save SQL as"), this);
|
|
// //action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
|
|
// connect(action, &QAction::triggered, this, &QueryTab::saveAs);
|
|
// actions.push_back(action);
|
|
// Save copy as
|
|
// Copy
|
|
// Copy as C-string
|
|
// Copy as raw cpp string
|
|
// Execute SQL
|
|
action = new QAction(QIcon(":/icons/script_go.png"), tr("Execute"), this);
|
|
action->setShortcut(QKeySequence(Qt::Key_F5));
|
|
connect(action, &QAction::triggered, this, &QueryTab::execute);
|
|
actions.push_back(action);
|
|
// Explain
|
|
action = new QAction(QIcon(":/icons/lightbulb_off.png"), tr("Explain"), this);
|
|
action->setShortcut(QKeySequence(Qt::Key_F7));
|
|
connect(action, &QAction::triggered, this, [this] () { explain(false); });
|
|
actions.push_back(action);
|
|
// Explain Anaylze
|
|
action = new QAction(QIcon(":/icons/lightbulb.png"), tr("Analyze"), this);
|
|
action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_F7));
|
|
connect(action, &QAction::triggered, this, [this] () { explain(true); });
|
|
actions.push_back(action);
|
|
// Cancel
|
|
action = new QAction(QIcon(":/icons/script_delete.png"), tr("Cancel"), this);
|
|
action->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Pause));
|
|
connect(action, &QAction::triggered, this, &QueryTab::cancel);
|
|
actions.push_back(action);
|
|
}
|
|
return actions;
|
|
}
|
|
|
|
|
|
void QueryToolModule::init()
|
|
{
|
|
MenuAction ma_new("New", [this] (IPluginContentWidgetContext* context)
|
|
{ menuAction_new(context); });
|
|
ma_new.setMenuLocation(MenuPath("File/New"));
|
|
ma_new.setIcon(QIcon(":/icons/new_query_tab.png"));
|
|
registerMenuAction(ma_new);
|
|
}
|
|
|
|
void QueryToolModule::menuAction_new(IPluginContentWidgetContext* context)
|
|
{
|
|
auto *ct = new QueryTab(context, nullptr);
|
|
context->addContentWidget(ct);
|
|
ct->newdoc();
|
|
}
|
|
|
|
REGISTER_PLUGIN_MODULE(QueryToolModule, "Query tool", "pglab.querytool")
|