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