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 options.addVisibleLookupColumns = false;
00317 d->origStatement = conn->selectStatement(*query, options).stripWhiteSpace();
00318 }
00319
00320 d->slotTextChangedEnabled = false;
00321 d->editor->setText( d->origStatement );
00322 d->slotTextChangedEnabled = true;
00323 QTimer::singleShot(100, d->editor, SLOT(setFocus()));
00324 return true;
00325 }
00326
00327 QString
00328 KexiQueryDesignerSQLView::sqlText() const
00329 {
00330 return d->editor->text();
00331 }
00332
00333 bool KexiQueryDesignerSQLView::slotCheckQuery()
00334 {
00335 QString sqlText( d->editor->text().stripWhiteSpace() );
00336 if (sqlText.isEmpty()) {
00337 delete d->parsedQuery;
00338 d->parsedQuery = 0;
00339 setStatusEmpty();
00340 return true;
00341 }
00342
00343 kdDebug() << "KexiQueryDesignerSQLView::slotCheckQuery()" << endl;
00344
00345 KexiDB::Parser *parser = mainWin()->project()->sqlParser();
00346 const bool ok = parser->parse( sqlText );
00347 delete d->parsedQuery;
00348 d->parsedQuery = parser->query();
00349 if (!d->parsedQuery || !ok || !parser->error().type().isEmpty()) {
00350 KexiDB::ParserError err = parser->error();
00351 setStatusError(err.error());
00352 d->editor->jump(err.at());
00353 delete d->parsedQuery;
00354 d->parsedQuery = 0;
00355 return false;
00356 }
00357
00358 setStatusOk();
00359 return true;
00360 }
00361
00362 void KexiQueryDesignerSQLView::slotUpdateMode()
00363 {
00364 if (d->action_toggle_history->isChecked() == d->action_toggle_history_was_checked)
00365 return;
00366
00367 d->eventFilterForSplitterEnabled = false;
00368
00369 QValueList<int> sz = d->splitter->sizes();
00370 d->action_toggle_history_was_checked = d->action_toggle_history->isChecked();
00371 int heightToSet = -1;
00372 if (d->action_toggle_history->isChecked()) {
00373 d->status_hbox->hide();
00374 d->historyHead->show();
00375 d->history->show();
00376 if (d->heightForHistoryMode==-1)
00377 d->heightForHistoryMode = m_dialog->height() / 2;
00378 heightToSet = d->heightForHistoryMode;
00379 d->heightForStatusMode = sz[1];
00380 }
00381 else {
00382 if (d->historyHead)
00383 d->historyHead->hide();
00384 d->status_hbox->show();
00385 if (d->heightForStatusMode>=0) {
00386 heightToSet = d->heightForStatusMode;
00387 } else {
00388 d->heightForStatusMode = d->status_hbox->height();
00389 }
00390 if (d->heightForHistoryMode>=0)
00391 d->heightForHistoryMode = sz[1];
00392 }
00393
00394 if (heightToSet>=0) {
00395 sz[1] = heightToSet;
00396 d->splitter->setSizes(sz);
00397 }
00398 d->eventFilterForSplitterEnabled = true;
00399 slotCheckQuery();
00400 }
00401
00402 void KexiQueryDesignerSQLView::slotTextChanged()
00403 {
00404 if (!d->slotTextChangedEnabled)
00405 return;
00406 setDirty(true);
00407 setStatusEmpty();
00408 }
00409
00410 bool KexiQueryDesignerSQLView::eventFilter( QObject *o, QEvent *e )
00411 {
00412 if (d->eventFilterForSplitterEnabled) {
00413 if (e->type()==QEvent::Resize && o && o==d->historyHead && d->historyHead->isVisible()) {
00414 d->heightForHistoryMode = d->historyHead->height();
00415 }
00416 else if (e->type()==QEvent::Resize && o && o==d->status_hbox && d->status_hbox->isVisible()) {
00417 d->heightForStatusMode = d->status_hbox->height();
00418 }
00419 }
00420 return KexiViewBase::eventFilter(o, e);
00421 }
00422
00423 void KexiQueryDesignerSQLView::updateActions(bool activated)
00424 {
00425 if (activated) {
00426 slotUpdateMode();
00427 }
00428 setAvailable("querypart_check_query", true);
00429 setAvailable("querypart_view_toggle_history", true);
00430 KexiViewBase::updateActions(activated);
00431 }
00432
00433 void KexiQueryDesignerSQLView::slotSelectQuery()
00434 {
00435 QString sql = d->history->selectedStatement();
00436 if (!sql.isEmpty()) {
00437 d->editor->setText( sql );
00438 }
00439 }
00440
00441 KexiQueryPart::TempData *
00442 KexiQueryDesignerSQLView::tempData() const
00443 {
00444 return dynamic_cast<KexiQueryPart::TempData*>(parentDialog()->tempData());
00445 }
00446
00447 KexiDB::SchemaData*
00448 KexiQueryDesignerSQLView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
00449 {
00450 Q_UNUSED( cancel );
00451
00452
00453 bool queryOK = slotCheckQuery();
00454 bool ok = true;
00455 KexiDB::SchemaData* query = 0;
00456 if (queryOK) {
00457
00458 if (d->parsedQuery) {
00459 query = d->parsedQuery;
00460 d->parsedQuery = 0;
00461 }
00462 else {
00463 query = new KexiDB::SchemaData();
00464 }
00465
00466 (KexiDB::SchemaData&)*query = sdata;
00467 ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *query, true );
00468 if (ok) {
00469 m_dialog->setId( query->id() );
00470 ok = storeDataBlock( d->editor->text(), "sql" );
00471 }
00472 }
00473 else {
00474
00475
00476
00477
00478 query = new KexiDB::SchemaData();
00479
00480 ok = (KMessageBox::questionYesNo(this, i18n("Do you want to save invalid query?"),
00481 0, KStdGuiItem::yes(), KStdGuiItem::no(), "askBeforeSavingInvalidQueries")==KMessageBox::Yes);
00482 if (ok) {
00483 (KexiDB::SchemaData&)*query = sdata;
00484 ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *query, true );
00485 }
00486 if (ok) {
00487 m_dialog->setId( query->id() );
00488 ok = storeDataBlock( d->editor->text(), "sql" );
00489 }
00490
00491
00492
00493 }
00494 if (!ok) {
00495 delete query;
00496 query = 0;
00497 }
00498 return query;
00499 }
00500
00501 tristate KexiQueryDesignerSQLView::storeData(bool dontAsk)
00502 {
00503 tristate res = KexiViewBase::storeData(dontAsk);
00504 if (~res)
00505 return res;
00506 if (res == true) {
00507 res = storeDataBlock( d->editor->text(), "sql" );
00508 #if 0
00509 bool queryOK = slotCheckQuery();
00510 if (queryOK) {
00511 res = storeDataBlock( d->editor->text(), "sql" );
00512 }
00513 else {
00514
00515
00516
00517 res = false;
00518 }
00519 #endif
00520 }
00521 if (res == true) {
00522 QString empty_xml;
00523 res = storeDataBlock( empty_xml, "query_layout" );
00524 }
00525 if (!res)
00526 setDirty(true);
00527 return res;
00528 }
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541 #include "kexiquerydesignersql.moc"
00542