00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <qsplitter.h>
00022 #include <qlayout.h>
00023 #include <qhbox.h>
00024 #include <qvbox.h>
00025
00026 #include <kapplication.h>
00027 #include <kdebug.h>
00028 #include <kmessagebox.h>
00029 #include <kiconloader.h>
00030
00031 #include <kexiutils/utils.h>
00032 #include <kexidb/driver.h>
00033 #include <kexidb/connection.h>
00034 #include <kexidb/parser/parser.h>
00035
00036 #include <kexiproject.h>
00037 #include <keximainwindow.h>
00038
00039 #include "kexiquerydesignersqleditor.h"
00040 #include "kexiquerydesignersqlhistory.h"
00041 #include "kexiquerydesignersql.h"
00042 #include "kexiquerypart.h"
00043
00044 #include "kexisectionheader.h"
00045
00046
00047 static bool compareSQL(const QString& sql1, const QString& sql2)
00048 {
00049
00050 return sql1.stripWhiteSpace()==sql2.stripWhiteSpace();
00051 }
00052
00053
00054
00056 class KexiQueryDesignerSQLView::Private
00057 {
00058 public:
00059 Private() :
00060 history(0)
00061 , historyHead(0)
00062 , statusPixmapOk( DesktopIcon("button_ok") )
00063 , statusPixmapErr( DesktopIcon("button_cancel") )
00064 , statusPixmapInfo( DesktopIcon("messagebox_info") )
00065 , parsedQuery(0)
00066 , heightForStatusMode(-1)
00067 , heightForHistoryMode(-1)
00068 , eventFilterForSplitterEnabled(true)
00069 , justSwitchedFromNoViewMode(false)
00070 {
00071 }
00072 KexiQueryDesignerSQLEditor *editor;
00073 KexiQueryDesignerSQLHistory *history;
00074 QLabel *pixmapStatus, *lblStatus;
00075 QHBox *status_hbox;
00076 QVBox *history_section;
00077 KexiSectionHeader *head, *historyHead;
00078 QPixmap statusPixmapOk, statusPixmapErr, statusPixmapInfo;
00079 QSplitter *splitter;
00080 KToggleAction *action_toggle_history;
00083 KexiDB::QuerySchema *parsedQuery;
00085 QString origStatement;
00087 int heightForStatusMode, heightForHistoryMode;
00089 bool action_toggle_history_was_checked : 1;
00091 bool eventFilterForSplitterEnabled : 1;
00093 bool justSwitchedFromNoViewMode : 1;
00094 };
00095
00096
00097
00098 KexiQueryDesignerSQLView::KexiQueryDesignerSQLView(KexiMainWindow *mainWin, QWidget *parent, const char *name)
00099 : KexiViewBase(mainWin, parent, name)
00100 , d( new Private() )
00101 {
00102 d->splitter = new QSplitter(this);
00103 d->splitter->setOrientation(Vertical);
00104 d->head = new KexiSectionHeader(i18n("SQL Query Text"), Vertical, d->splitter);
00105 d->editor = new KexiQueryDesignerSQLEditor(mainWin, d->head, "sqle");
00106
00107 connect(d->editor, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
00108 addChildView(d->editor);
00109 setViewWidget(d->editor);
00110 d->splitter->setFocusProxy(d->editor);
00111 setFocusProxy(d->editor);
00112
00113 d->history_section = new QVBox(d->splitter);
00114
00115 d->status_hbox = new QHBox(d->history_section);
00116 d->status_hbox->installEventFilter(this);
00117 d->splitter->setResizeMode(d->history_section, QSplitter::KeepSize);
00118 d->status_hbox->setSpacing(0);
00119 d->pixmapStatus = new QLabel(d->status_hbox);
00120 d->pixmapStatus->setFixedWidth(d->statusPixmapOk.width()*3/2);
00121 d->pixmapStatus->setAlignment(AlignHCenter | AlignTop);
00122 d->pixmapStatus->setMargin(d->statusPixmapOk.width()/4);
00123 d->pixmapStatus->setPaletteBackgroundColor( palette().active().color(QColorGroup::Base) );
00124
00125 d->lblStatus = new QLabel(d->status_hbox);
00126 d->lblStatus->setAlignment(AlignLeft | AlignTop | WordBreak);
00127 d->lblStatus->setMargin(d->statusPixmapOk.width()/4);
00128 d->lblStatus->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
00129 d->lblStatus->resize(d->lblStatus->width(),d->statusPixmapOk.width()*3);
00130 d->lblStatus->setPaletteBackgroundColor( palette().active().color(QColorGroup::Base) );
00131
00132 QHBoxLayout *b = new QHBoxLayout(this);
00133 b->addWidget(d->splitter);
00134
00135 plugSharedAction("querypart_check_query", this, SLOT(slotCheckQuery()));
00136 plugSharedAction("querypart_view_toggle_history", this, SLOT(slotUpdateMode()));
00137 d->action_toggle_history = static_cast<KToggleAction*>( sharedAction( "querypart_view_toggle_history" ) );
00138
00139 d->historyHead = new KexiSectionHeader(i18n("SQL Query History"), Vertical, d->history_section);
00140 d->historyHead->installEventFilter(this);
00141 d->history = new KexiQueryDesignerSQLHistory(d->historyHead, "sql_history");
00142
00143 static const QString msg_back = i18n("Back to Selected Query");
00144 static const QString msg_clear = i18n("Clear History");
00145 d->historyHead->addButton("select_item", msg_back, this, SLOT(slotSelectQuery()));
00146 d->historyHead->addButton("editclear", msg_clear, d->history, SLOT(clear()));
00147 d->history->popupMenu()->insertItem(SmallIcon("select_item"), msg_back, this, SLOT(slotSelectQuery()));
00148 d->history->popupMenu()->insertItem(SmallIcon("editclear"), msg_clear, d->history, SLOT(clear()));
00149 connect(d->history, SIGNAL(currentItemDoubleClicked()), this, SLOT(slotSelectQuery()));
00150
00151 d->heightForHistoryMode = -1;
00152
00153 d->action_toggle_history_was_checked = !d->action_toggle_history->isChecked();
00154 slotUpdateMode();
00155 slotCheckQuery();
00156 }
00157
00158 KexiQueryDesignerSQLView::~KexiQueryDesignerSQLView()
00159 {
00160 delete d;
00161 }
00162
00163 KexiQueryDesignerSQLEditor *KexiQueryDesignerSQLView::editor() const
00164 {
00165 return d->editor;
00166 }
00167
00168 void KexiQueryDesignerSQLView::setStatusOk()
00169 {
00170 d->pixmapStatus->setPixmap(d->statusPixmapOk);
00171 setStatusText("<h2>"+i18n("The query is correct")+"</h2>");
00172 d->history->addEvent(d->editor->text().stripWhiteSpace(), true, QString::null);
00173 }
00174
00175 void KexiQueryDesignerSQLView::setStatusError(const QString& msg)
00176 {
00177 d->pixmapStatus->setPixmap(d->statusPixmapErr);
00178 setStatusText("<h2>"+i18n("The query is incorrect")+"</h2><p>"+msg+"</p>");
00179 d->history->addEvent(d->editor->text().stripWhiteSpace(), false, msg);
00180 }
00181
00182 void KexiQueryDesignerSQLView::setStatusEmpty()
00183 {
00184 d->pixmapStatus->setPixmap(d->statusPixmapInfo);
00185 setStatusText(i18n("Please enter your query and execute \"Check query\" function to verify it."));
00186 }
00187
00188 void KexiQueryDesignerSQLView::setStatusText(const QString& text)
00189 {
00190 if (!d->action_toggle_history->isChecked()) {
00191 QSimpleRichText rt(text, d->lblStatus->font());
00192 rt.setWidth(d->lblStatus->width());
00193 QValueList<int> sz = d->splitter->sizes();
00194 const int newHeight = rt.height()+d->lblStatus->margin()*2;
00195 if (sz[1]<newHeight) {
00196 sz[1] = newHeight;
00197 d->splitter->setSizes(sz);
00198 }
00199 d->lblStatus->setText(text);
00200 }
00201 }
00202
00203 tristate
00204 KexiQueryDesignerSQLView::beforeSwitchTo(int mode, bool &dontStore)
00205 {
00206
00207 dontStore = true;
00208 if (mode==Kexi::DesignViewMode || mode==Kexi::DataViewMode) {
00209 QString sqlText = d->editor->text().stripWhiteSpace();
00210 KexiQueryPart::TempData * temp = tempData();
00211 if (sqlText.isEmpty()) {
00212
00213 if (temp->query()) {
00214 temp->queryChangedInPreviousView = true;
00215 temp->setQuery(0);
00216
00217
00218 }
00219 }
00220 else {
00221 const bool designViewWasVisible = parentDialog()->viewForMode(mode)!=0;
00222
00223 if (designViewWasVisible
00224 && !d->justSwitchedFromNoViewMode
00225 && compareSQL(d->origStatement, d->editor->text())) {
00226
00227 temp->queryChangedInPreviousView = false;
00228 }
00229 else {
00230
00231 if (!slotCheckQuery()) {
00232 if (KMessageBox::No==KMessageBox::warningYesNo(this, "<p>"+i18n("The query you entered is incorrect.")
00233 +"</p><p>"+i18n("Do you want to cancel any changes made to this SQL text?")+"</p>"
00234 +"</p><p>"+i18n("Answering \"No\" allows you to make corrections.")+"</p>"))
00235 {
00236 return cancelled;
00237 }
00238
00239 temp->queryChangedInPreviousView = false;
00240
00241 d->justSwitchedFromNoViewMode = false;
00242 return true;
00243 }
00244
00245 d->justSwitchedFromNoViewMode = false;
00246
00247 temp->setQuery( d->parsedQuery );
00248
00249
00250 d->parsedQuery = 0;
00251 temp->queryChangedInPreviousView = true;
00252 }
00253 }
00254 }
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274 d->editor->setFocus();
00275 return true;
00276 }
00277
00278 tristate
00279 KexiQueryDesignerSQLView::afterSwitchFrom(int mode)
00280 {
00281 kdDebug() << "KexiQueryDesignerSQLView::afterSwitchFrom()" << endl;
00282
00283 if (mode==Kexi::NoViewMode) {
00284
00285
00286
00287 d->justSwitchedFromNoViewMode = true;
00288 }
00289 KexiQueryPart::TempData * temp = tempData();
00290 KexiDB::QuerySchema *query = temp->query();
00291 if (!query) {
00292 query = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
00293 }
00294
00295 if (mode!=0 && !query) {
00296
00297 return false;
00298 }
00299
00300 if (!query) {
00301
00302 if (!loadDataBlock( d->origStatement, "sql", true ))
00303 return false;
00304 }
00305 else {
00306
00307 temp->setQuery( query );
00308
00309 KexiDB::Connection* conn = mainWin()->project()->dbConnection();
00310 int flags = KexiDB::Driver::EscapeKexi;
00311 d->origStatement = conn->selectStatement(*query, flags).stripWhiteSpace();
00312 }
00313
00314 d->editor->setText( d->origStatement );
00315 return true;
00316 }
00317
00318 QString
00319 KexiQueryDesignerSQLView::sqlText() const
00320 {
00321 return d->editor->text();
00322 }
00323
00324 bool KexiQueryDesignerSQLView::slotCheckQuery()
00325 {
00326 QString sqlText( d->editor->text().stripWhiteSpace() );
00327 if (sqlText.isEmpty()) {
00328 delete d->parsedQuery;
00329 d->parsedQuery = 0;
00330 setStatusEmpty();
00331 return true;
00332 }
00333
00334 kdDebug() << "KexiQueryDesignerSQLView::slotCheckQuery()" << endl;
00335
00336 KexiDB::Parser *parser = mainWin()->project()->sqlParser();
00337 const bool ok = parser->parse( sqlText );
00338 delete d->parsedQuery;
00339 d->parsedQuery = parser->query();
00340 if (!d->parsedQuery || !ok || !parser->error().type().isEmpty()) {
00341 KexiDB::ParserError err = parser->error();
00342 setStatusError(err.error());
00343 d->editor->jump(err.at());
00344 delete d->parsedQuery;
00345 d->parsedQuery = 0;
00346 return false;
00347 }
00348
00349 setStatusOk();
00350 return true;
00351 }
00352
00353 void KexiQueryDesignerSQLView::slotUpdateMode()
00354 {
00355 if (d->action_toggle_history->isChecked() == d->action_toggle_history_was_checked)
00356 return;
00357
00358 d->eventFilterForSplitterEnabled = false;
00359
00360 QValueList<int> sz = d->splitter->sizes();
00361 d->action_toggle_history_was_checked = d->action_toggle_history->isChecked();
00362 int heightToSet = -1;
00363 if (d->action_toggle_history->isChecked()) {
00364 d->status_hbox->hide();
00365 d->historyHead->show();
00366 d->history->show();
00367 if (d->heightForHistoryMode==-1)
00368 d->heightForHistoryMode = m_dialog->height() / 2;
00369 heightToSet = d->heightForHistoryMode;
00370 d->heightForStatusMode = sz[1];
00371 }
00372 else {
00373 if (d->historyHead)
00374 d->historyHead->hide();
00375 d->status_hbox->show();
00376 if (d->heightForStatusMode>=0) {
00377 heightToSet = d->heightForStatusMode;
00378 } else {
00379 d->heightForStatusMode = d->status_hbox->height();
00380 }
00381 if (d->heightForHistoryMode>=0)
00382 d->heightForHistoryMode = sz[1];
00383 }
00384
00385 if (heightToSet>=0) {
00386 sz[1] = heightToSet;
00387 d->splitter->setSizes(sz);
00388 }
00389 d->eventFilterForSplitterEnabled = true;
00390 slotCheckQuery();
00391 }
00392
00393 void KexiQueryDesignerSQLView::slotTextChanged()
00394 {
00395 setDirty(true);
00396 setStatusEmpty();
00397 }
00398
00399 bool KexiQueryDesignerSQLView::eventFilter( QObject *o, QEvent *e )
00400 {
00401 if (d->eventFilterForSplitterEnabled) {
00402 if (e->type()==QEvent::Resize && o && o==d->historyHead && d->historyHead->isVisible()) {
00403 d->heightForHistoryMode = d->historyHead->height();
00404 }
00405 else if (e->type()==QEvent::Resize && o && o==d->status_hbox && d->status_hbox->isVisible()) {
00406 d->heightForStatusMode = d->status_hbox->height();
00407 }
00408 }
00409 return KexiViewBase::eventFilter(o, e);
00410 }
00411
00412 void KexiQueryDesignerSQLView::updateActions(bool activated)
00413 {
00414 if (activated) {
00415 slotUpdateMode();
00416 }
00417 setAvailable("querypart_check_query", true);
00418 setAvailable("querypart_view_toggle_history", true);
00419 KexiViewBase::updateActions(activated);
00420 }
00421
00422 void KexiQueryDesignerSQLView::slotSelectQuery()
00423 {
00424 QString sql = d->history->selectedStatement();
00425 if (!sql.isEmpty()) {
00426 d->editor->setText( sql );
00427 }
00428 }
00429
00430 KexiQueryPart::TempData *
00431 KexiQueryDesignerSQLView::tempData() const
00432 {
00433 return dynamic_cast<KexiQueryPart::TempData*>(parentDialog()->tempData());
00434 }
00435
00436 KexiDB::SchemaData*
00437 KexiQueryDesignerSQLView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
00438 {
00439 Q_UNUSED( cancel );
00440
00441
00442 bool queryOK = slotCheckQuery();
00443 bool ok = true;
00444 KexiDB::SchemaData* query = 0;
00445 if (queryOK) {
00446
00447 if (d->parsedQuery) {
00448 query = d->parsedQuery;
00449 d->parsedQuery = 0;
00450 }
00451 else {
00452 query = new KexiDB::SchemaData();
00453 }
00454
00455 (KexiDB::SchemaData&)*query = sdata;
00456 ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *query, true );
00457 if (ok) {
00458 m_dialog->setId( query->id() );
00459 ok = storeDataBlock( d->editor->text(), "sql" );
00460 }
00461 }
00462 else {
00463
00464
00465
00466
00467 query = new KexiDB::SchemaData();
00468
00469 ok = (KMessageBox::questionYesNo(this, i18n("Do you want to save invalid query?"),
00470 0, KStdGuiItem::yes(), KStdGuiItem::no(), "askBeforeSavingInvalidQueries")==KMessageBox::Yes);
00471 if (ok) {
00472 (KexiDB::SchemaData&)*query = sdata;
00473 ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *query, true );
00474 }
00475 if (ok) {
00476 m_dialog->setId( query->id() );
00477 ok = storeDataBlock( d->editor->text(), "sql" );
00478 }
00479
00480
00481
00482 }
00483 if (!ok) {
00484 delete query;
00485 query = 0;
00486 }
00487 return query;
00488 }
00489
00490 tristate KexiQueryDesignerSQLView::storeData(bool dontAsk)
00491 {
00492 tristate res = KexiViewBase::storeData(dontAsk);
00493 if (~res)
00494 return res;
00495 if (res) {
00496 res = storeDataBlock( d->editor->text(), "sql" );
00497 #if 0
00498 bool queryOK = slotCheckQuery();
00499 if (queryOK) {
00500 res = storeDataBlock( d->editor->text(), "sql" );
00501 }
00502 else {
00503
00504
00505
00506 res = false;
00507 }
00508 #endif
00509 }
00510 if (res) {
00511 QString empty_xml;
00512 res = storeDataBlock( empty_xml, "query_layout" );
00513 }
00514 if (!res)
00515 setDirty(true);
00516 return res;
00517 }
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530 #include "kexiquerydesignersql.moc"
00531