diff --git a/kexi/plugins/queries/kexiquerydesignersql.cpp b/kexi/plugins/queries/kexiquerydesignersql.cpp index c3b42231ace..6595877d063 100644 --- a/kexi/plugins/queries/kexiquerydesignersql.cpp +++ b/kexi/plugins/queries/kexiquerydesignersql.cpp @@ -1,441 +1,442 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2004-2014 Jarosław Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kexiquerydesignersqleditor.h" #include "kexiquerydesignersql.h" #include "kexiquerypart.h" #include "kexisectionheader.h" static bool compareSQL(const QString& sql1, const QString& sql2) { //! @todo use reformatting functions here return sql1.trimmed() == sql2.trimmed(); } //=================== //! @internal class KexiQueryDesignerSQLView::Private { public: Private() : statusPixmapOk(koDesktopIcon("dialog-ok")) , statusPixmapErr(koDesktopIcon("dialog-error")) , statusPixmapInfo(koDesktopIcon("dialog-information")) , parsedQuery(0) , heightForStatusMode(-1) , justSwitchedFromNoViewMode(false) , slotTextChangedEnabled(true) { } KexiQueryDesignerSQLEditor *editor; QLabel *pixmapStatus, *lblStatus; QHBoxLayout *statusHLyr; QFrame *statusMainWidget; KexiSectionHeader *head; QWidget *bottomPane; QPixmap statusPixmapOk, statusPixmapErr, statusPixmapInfo; QSplitter *splitter; //! For internal use, this pointer is usually copied to TempData structure, //! when switching out of this view (then it's cleared). KexiDB::QuerySchema *parsedQuery; //! For internal use, statement passed in switching to this view QString origStatement; //! needed to remember height for both modes, between switching int heightForStatusMode; //! helper for beforeSwitchTo() bool justSwitchedFromNoViewMode; //! helper for slotTextChanged() bool slotTextChangedEnabled; }; //=================== KexiQueryDesignerSQLView::KexiQueryDesignerSQLView(QWidget *parent) : KexiView(parent) , d(new Private()) { d->splitter = new QSplitter(this); d->splitter->setOrientation(Qt::Vertical); d->head = new KexiSectionHeader(i18n("SQL Query Text"), Qt::Vertical, d->splitter); d->splitter->addWidget(d->head); d->splitter->setStretchFactor( d->splitter->indexOf(d->head), 3/*stretch*/); d->editor = new KexiQueryDesignerSQLEditor(d->head); d->editor->setObjectName("sqleditor"); d->head->setWidget(d->editor); connect(d->editor, SIGNAL(textChanged()), this, SLOT(slotTextChanged())); // -- bottom pane (status) d->bottomPane = new QWidget(d->splitter); QVBoxLayout *bottomPaneLyr = new QVBoxLayout(d->bottomPane); d->splitter->addWidget(d->bottomPane); d->splitter->setStretchFactor( d->splitter->indexOf(d->bottomPane), 1/*KeepSize*/); // -- status pane d->statusMainWidget = new QFrame(d->bottomPane); bottomPaneLyr->addWidget(d->statusMainWidget); d->statusMainWidget->setAutoFillBackground(true); d->statusMainWidget->setFrameShape(QFrame::StyledPanel); d->statusMainWidget->setFrameShadow(QFrame::Plain); d->statusMainWidget->setBackgroundRole(QPalette::Base); QPalette pal(QToolTip::palette()); pal.setBrush(QPalette::Base, QToolTip::palette().brush(QPalette::Button)); d->statusMainWidget->setPalette(pal); d->splitter->setCollapsible(1, false); d->statusHLyr = new QHBoxLayout(d->statusMainWidget); d->statusHLyr->setContentsMargins(0, KDialog::marginHint() / 2, 0, KDialog::marginHint() / 2); d->statusHLyr->setSpacing(0); d->pixmapStatus = new QLabel(d->statusMainWidget); d->statusHLyr->addWidget(d->pixmapStatus); d->pixmapStatus->setFixedWidth(d->statusPixmapOk.width()*3 / 2); d->pixmapStatus->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); d->pixmapStatus->setAutoFillBackground(true); d->lblStatus = new QLabel(d->statusMainWidget); d->statusHLyr->addWidget(d->lblStatus); d->lblStatus->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); d->lblStatus->setWordWrap(true); d->lblStatus->setTextInteractionFlags(Qt::TextBrowserInteraction); d->lblStatus->setMinimumHeight(d->statusPixmapOk.width()); addChildView(d->editor); setViewWidget(d->splitter, false/* no focus proxy*/); d->splitter->setFocusProxy(d->editor); setFocusProxy(d->editor); // -- setup local actions QList viewActions; QAction* a; viewActions << (a = new KAction(koIcon("test_it"), i18n("Check Query"), this)); a->setObjectName("querypart_check_query"); - a->setShortcut(Qt::Key_F9); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F5)); a->setToolTip(i18n("Check Query")); a->setWhatsThis(i18n("Checks query for validity.")); + addAction(a); connect(a, SIGNAL(triggered()), this, SLOT(slotCheckQuery())); setViewActions(viewActions); slotUpdateMode(); slotCheckQuery(); updateGeometry(); } KexiQueryDesignerSQLView::~KexiQueryDesignerSQLView() { delete d; } KexiQueryDesignerSQLEditor *KexiQueryDesignerSQLView::editor() const { return d->editor; } void KexiQueryDesignerSQLView::setStatusOk() { d->pixmapStatus->setPixmap(d->statusPixmapOk); setStatusText("

" + i18n("The query is correct") + "

"); } void KexiQueryDesignerSQLView::setStatusError(const QString& msg) { d->pixmapStatus->setPixmap(d->statusPixmapErr); setStatusText("

" + i18n("The query is incorrect") + "

" + msg + "

"); } void KexiQueryDesignerSQLView::setStatusEmpty() { d->pixmapStatus->setPixmap(d->statusPixmapInfo); setStatusText( i18n("Please enter your query and execute \"Check query\" function to verify it.")); } void KexiQueryDesignerSQLView::setStatusText(const QString& text) { d->lblStatus->setText(text); } tristate KexiQueryDesignerSQLView::beforeSwitchTo(Kexi::ViewMode mode, bool &dontStore) { //! @todo dontStore = true; if (mode == Kexi::DesignViewMode || mode == Kexi::DataViewMode) { QString sqlText = d->editor->text().trimmed(); KexiQueryPart::TempData * temp = tempData(); if (sqlText.isEmpty()) { //special case: empty SQL text if (temp->query()) { temp->setQueryChangedInPreviousView(true); //query changed temp->setQuery(0); } } else { const bool designViewWasVisible = window()->viewForMode(mode) != 0; //should we check SQL text? if (designViewWasVisible && !d->justSwitchedFromNoViewMode //unchanged, but we should check SQL text && compareSQL(d->origStatement, d->editor->text())) { //statement unchanged! - nothing to do temp->setQueryChangedInPreviousView(false); } else { //yes: parse SQL text if (!slotCheckQuery()) { if (KMessageBox::No == KMessageBox::warningYesNo(this, "

" + i18n("The query you entered is incorrect.") + "

" + i18n("Do you want to cancel any changes made to this SQL text?") + "

" + "

" + i18n("Answering \"No\" allows you to make corrections.") + "

")) { return cancelled; } //do not change original query - it's invalid temp->setQueryChangedInPreviousView(false); //this view is no longer _just_ switched from "NoViewMode" d->justSwitchedFromNoViewMode = false; return true; } //this view is no longer _just_ switched from "NoViewMode" d->justSwitchedFromNoViewMode = false; //replace old query schema with new one temp->setQuery(d->parsedQuery); //this will also delete temp->query() d->parsedQuery = 0; temp->setQueryChangedInPreviousView(true); } } d->origStatement = d->editor->text(); } d->editor->setFocus(); return true; } tristate KexiQueryDesignerSQLView::afterSwitchFrom(Kexi::ViewMode mode) { kDebug(); if (mode == Kexi::NoViewMode) { //User opened text view _directly_. //This flag is set to indicate for beforeSwitchTo() that even if text has not been changed, //SQL text should be invalidated. d->justSwitchedFromNoViewMode = true; } KexiQueryPart::TempData * temp = tempData(); KexiDB::QuerySchema *query = temp->query(); if (!query) {//try to just get saved schema, instead of temporary one query = dynamic_cast(window()->schemaData()); } if (mode != 0/*failure only if it is switching from prev. view*/ && !query) { //! @todo msg return false; } if (query) { // Use query with Kexi keywords (but not driver-specific keywords) escaped. temp->setQuery(query); if (temp->queryChangedInPreviousView()) { KexiDB::Connection::SelectStatementOptions options; options.identifierEscaping = KexiDB::Driver::EscapeKexi; options.addVisibleLookupColumns = false; d->origStatement = KexiDB::selectStatement(0, *query, options).trimmed(); } } if (d->origStatement.isEmpty()) { //no valid query delivered or query has not been modified: // just load sql text, no matter if it's valid if (!loadDataBlock(d->origStatement, "sql", true /*canBeEmpty*/)) return false; } if (!compareSQL(d->origStatement, d->editor->text())) { d->slotTextChangedEnabled = false; d->editor->setText(d->origStatement); d->slotTextChangedEnabled = true; } QTimer::singleShot(100, d->editor, SLOT(setFocus())); return true; } QString KexiQueryDesignerSQLView::sqlText() const { return d->editor->text(); } bool KexiQueryDesignerSQLView::slotCheckQuery() { QString sqlText(d->editor->text().trimmed()); if (sqlText.isEmpty()) { delete d->parsedQuery; d->parsedQuery = 0; setStatusEmpty(); return true; } kDebug(); KexiDB::Parser *parser = KexiMainWindowIface::global()->project()->sqlParser(); const bool ok = parser->parse(sqlText); delete d->parsedQuery; d->parsedQuery = parser->query(); if (!d->parsedQuery || !ok || !parser->error().type().isEmpty()) { KexiDB::ParserError err = parser->error(); setStatusError(err.error()); d->editor->jump(err.at()); delete d->parsedQuery; d->parsedQuery = 0; return false; } setStatusOk(); return true; } void KexiQueryDesignerSQLView::slotUpdateMode() { slotCheckQuery(); } void KexiQueryDesignerSQLView::slotTextChanged() { if (!d->slotTextChangedEnabled) return; setDirty(true); setStatusEmpty(); } void KexiQueryDesignerSQLView::updateActions(bool activated) { if (activated) { slotUpdateMode(); } setAvailable("querypart_check_query", true); KexiView::updateActions(activated); } KexiQueryPart::TempData* KexiQueryDesignerSQLView::tempData() const { return dynamic_cast(window()->data()); } KexiDB::SchemaData* KexiQueryDesignerSQLView::storeNewData(const KexiDB::SchemaData& sdata, KexiView::StoreNewDataOptions options, bool &cancel) { Q_UNUSED(options); //here: we won't store query layout: it will be recreated 'by hand' in GUI Query Editor const bool queryOK = slotCheckQuery(); bool ok = true; KexiDB::SchemaData* query = 0; if (queryOK) { if (d->parsedQuery) { query = d->parsedQuery; //will be returned, so: don't keep it d->parsedQuery = 0; } else { //empty query query = new KexiDB::SchemaData(); //just empty } } else { //the query is not ok if (KMessageBox::Yes != KMessageBox::questionYesNo(this, i18n("Do you want to save invalid query?"), 0, KStandardGuiItem::yes(), KStandardGuiItem::no(), "askBeforeSavingInvalidQueries"/*config entry*/)) { cancel = true; return 0; } query = new KexiDB::SchemaData(); //just empty } (KexiDB::SchemaData&)*query = sdata; //copy main attributes ok = KexiMainWindowIface::global()->project()->dbConnection()->storeObjectSchemaData( *query, true /*newObject*/); if (ok) { ok = KexiMainWindowIface::global()->project()->removeUserDataBlock(query->id()); // for sanity } if (ok) { window()->setId(query->id()); ok = storeDataBlock(d->editor->text(), "sql"); } if (!ok) { delete query; query = 0; } return query; } tristate KexiQueryDesignerSQLView::storeData(bool dontAsk) { if (window()->schemaData()) { //set this instance as obsolete (only if it's stored) KexiMainWindowIface::global()->project()->dbConnection()->setQuerySchemaObsolete(window()->schemaData()->name()); } tristate res = KexiView::storeData(dontAsk); if (~res) return res; if (res == true) { res = storeDataBlock(d->editor->text(), "sql"); #if 0 bool queryOK = slotCheckQuery(); if (queryOK) { res = storeDataBlock(d->editor->text(), "sql"); } else { //query is not ok //! @todo allow saving invalid queries //! @todo just ask this question: res = false; } #endif } if (res == true) { QString empty_xml; res = storeDataBlock(empty_xml, "query_layout"); //clear } if (!res) setDirty(true); return res; } #include "kexiquerydesignersql.moc"