00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kexiquerydesignerguieditor.h"
00022
00023 #include <qlayout.h>
00024 #include <qpainter.h>
00025 #include <qdom.h>
00026 #include <qregexp.h>
00027
00028 #include <kdebug.h>
00029 #include <klocale.h>
00030 #include <kmessagebox.h>
00031
00032 #include <kexidb/field.h>
00033 #include <kexidb/queryschema.h>
00034 #include <kexidb/connection.h>
00035 #include <kexidb/parser/parser.h>
00036 #include <kexidb/parser/sqlparser.h>
00037 #include <kexidb/utils.h>
00038 #include <kexiutils/identifier.h>
00039 #include <kexiproject.h>
00040 #include <keximainwindow.h>
00041 #include <kexiinternalpart.h>
00042 #include <kexitableview.h>
00043 #include <kexitableitem.h>
00044 #include <kexitableviewdata.h>
00045 #include <kexidragobjects.h>
00046 #include <kexidialogbase.h>
00047 #include <kexidatatable.h>
00048 #include <kexi.h>
00049 #include <kexisectionheader.h>
00050 #include <widget/tableview/kexidataawarepropertyset.h>
00051 #include <widget/relations/kexirelationwidget.h>
00052 #include <widget/relations/kexirelationviewtable.h>
00053 #include <koproperty/property.h>
00054 #include <koproperty/set.h>
00055
00056 #include "kexiquerypart.h"
00057
00059 #define KEXI_NO_QUERY_TOTALS
00060
00062 #define COLUMN_ID_COLUMN 0
00063 #define COLUMN_ID_TABLE 1
00064 #define COLUMN_ID_VISIBLE 2
00065 #ifdef KEXI_NO_QUERY_TOTALS
00066 # define COLUMN_ID_CRITERIA 3
00067 #else
00068 # define COLUMN_ID_TOTALS 3
00069 # define COLUMN_ID_CRITERIA 4
00070 #endif
00071
00073 class KexiQueryDesignerGuiEditorPrivate
00074 {
00075 public:
00076 KexiQueryDesignerGuiEditorPrivate()
00077 : fieldColumnIdentifiers(101, false)
00078 {
00079 droppedNewItem = 0;
00080 slotTableAdded_enabled = true;
00081 }
00082
00083 KexiTableViewData *data;
00084 KexiDataTable *dataTable;
00085 QGuardedPtr<KexiDB::Connection> conn;
00086
00087 KexiRelationWidget *relations;
00088 KexiSectionHeader *head;
00089 QSplitter *spl;
00090
00094 KexiTableViewData *fieldColumnData, *tablesColumnData;
00095
00102 QDict<char> fieldColumnIdentifiers;
00103
00104 KexiDataAwarePropertySet* sets;
00105 KexiTableItem *droppedNewItem;
00106
00107 QString droppedNewTable, droppedNewField;
00108
00109 bool slotTableAdded_enabled : 1;
00110 };
00111
00112
00113
00114 KexiQueryDesignerGuiEditor::KexiQueryDesignerGuiEditor(
00115 KexiMainWindow *mainWin, QWidget *parent, const char *name)
00116 : KexiViewBase(mainWin, parent, name)
00117 , d( new KexiQueryDesignerGuiEditorPrivate() )
00118 {
00119 d->conn = mainWin->project()->dbConnection();
00120
00121 d->spl = new QSplitter(Vertical, this);
00122 d->spl->setChildrenCollapsible(false);
00123 d->relations = new KexiRelationWidget(mainWin, d->spl, "relations");
00124 connect(d->relations, SIGNAL(tableAdded(KexiDB::TableSchema&)),
00125 this, SLOT(slotTableAdded(KexiDB::TableSchema&)));
00126 connect(d->relations, SIGNAL(tableHidden(KexiDB::TableSchema&)),
00127 this, SLOT(slotTableHidden(KexiDB::TableSchema&)));
00128 connect(d->relations, SIGNAL(tableFieldDoubleClicked(KexiDB::TableSchema*,const QString&)),
00129 this, SLOT(slotTableFieldDoubleClicked(KexiDB::TableSchema*,const QString&)));
00130
00131 d->head = new KexiSectionHeader(i18n("Query Columns"), Vertical, d->spl);
00132 d->dataTable = new KexiDataTable(mainWin, d->head, "guieditor_dataTable", false);
00133 d->dataTable->dataAwareObject()->setSpreadSheetMode();
00134
00135 d->data = new KexiTableViewData();
00136 d->sets = new KexiDataAwarePropertySet( this, d->dataTable->dataAwareObject() );
00137 initTableColumns();
00138 initTableRows();
00139
00140 QValueList<int> c;
00141 c << COLUMN_ID_COLUMN << COLUMN_ID_TABLE << COLUMN_ID_CRITERIA;
00142 if (d->dataTable->tableView()) {
00143 d->dataTable->tableView()->maximizeColumnsWidth( c );
00144 d->dataTable->tableView()->adjustColumnWidthToContents(2);
00145 d->dataTable->tableView()->setDropsAtRowEnabled(true);
00146 connect(d->dataTable->tableView(), SIGNAL(dragOverRow(KexiTableItem*,int,QDragMoveEvent*)),
00147 this, SLOT(slotDragOverTableRow(KexiTableItem*,int,QDragMoveEvent*)));
00148 connect(d->dataTable->tableView(), SIGNAL(droppedAtRow(KexiTableItem*,int,QDropEvent*,KexiTableItem*&)),
00149 this, SLOT(slotDroppedAtRow(KexiTableItem*,int,QDropEvent*,KexiTableItem*&)));
00150 }
00151 connect(d->data, SIGNAL(aboutToChangeCell(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)),
00152 this, SLOT(slotBeforeCellChanged(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)));
00153 connect(d->data, SIGNAL(rowInserted(KexiTableItem*,uint,bool)),
00154 this, SLOT(slotRowInserted(KexiTableItem*,uint,bool)));
00155 connect(d->relations, SIGNAL(tablePositionChanged(KexiRelationViewTableContainer*)),
00156 this, SLOT(slotTablePositionChanged(KexiRelationViewTableContainer*)));
00157 connect(d->relations, SIGNAL(aboutConnectionRemove(KexiRelationViewConnection*)),
00158 this, SLOT(slotAboutConnectionRemove(KexiRelationViewConnection*)));
00159
00160 QVBoxLayout *l = new QVBoxLayout(this);
00161 l->addWidget(d->spl);
00162
00163 addChildView(d->relations);
00164 addChildView(d->dataTable);
00165 setViewWidget(d->dataTable, true);
00166 d->relations->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
00167 d->head->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
00168 updateGeometry();
00169 d->spl->setSizes(QValueList<int>()<< 800<<400);
00170 }
00171
00172 KexiQueryDesignerGuiEditor::~KexiQueryDesignerGuiEditor()
00173 {
00174 }
00175
00176 void
00177 KexiQueryDesignerGuiEditor::initTableColumns()
00178 {
00179 KexiTableViewColumn *col1 = new KexiTableViewColumn("column", KexiDB::Field::Enum, i18n("Column"),
00180 i18n("Describes field name or expression for the designed query."));
00181 col1->setRelatedDataEditable(true);
00182
00183 d->fieldColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text);
00184 col1->setRelatedData( d->fieldColumnData );
00185 d->data->addColumn(col1);
00186
00187 KexiTableViewColumn *col2 = new KexiTableViewColumn("table", KexiDB::Field::Enum, i18n("Table"),
00188 i18n("Describes table for a given field. Can be empty."));
00189 d->tablesColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text);
00190 col2->setRelatedData( d->tablesColumnData );
00191 d->data->addColumn(col2);
00192
00193 KexiTableViewColumn *col3 = new KexiTableViewColumn("visible", KexiDB::Field::Boolean, i18n("Visible"),
00194 i18n("Describes visibility for a given field or expression."));
00195 d->data->addColumn(col3);
00196 d->dataTable->tableView()->adjustColumnWidthToContents( COLUMN_ID_VISIBLE );
00197
00198 #ifndef KEXI_NO_QUERY_TOTALS
00199 KexiTableViewColumn *col4 = new KexiTableViewColumn("totals", KexiDB::Field::Enum, i18n("Totals"),
00200 i18n("Describes a way of computing totals for a given field or expression."));
00201 QValueVector<QString> totalsTypes;
00202 totalsTypes.append( i18n("Group by") );
00203 totalsTypes.append( i18n("Sum") );
00204 totalsTypes.append( i18n("Average") );
00205 totalsTypes.append( i18n("Min") );
00206 totalsTypes.append( i18n("Max") );
00207
00208 col4->field()->setEnumHints(totalsTypes);
00209 d->data->addColumn(col4);
00210 #endif
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222 KexiTableViewColumn *col6 = new KexiTableViewColumn("criteria", KexiDB::Field::Text, i18n("Criteria"),
00223 i18n("Describes the criteria for a given field or expression."));
00224 d->data->addColumn(col6);
00225
00226
00227
00228 }
00229
00230 void KexiQueryDesignerGuiEditor::initTableRows()
00231 {
00232 d->data->deleteAllRows();
00233
00234 for (int i=0; i<(int)d->sets->size(); i++) {
00235 d->data->append(d->data->createItem());
00236 }
00237 d->dataTable->dataAwareObject()->setData(d->data);
00238
00239 updateColumnsData();
00240 }
00241
00242 void KexiQueryDesignerGuiEditor::updateColumnsData()
00243 {
00244 d->dataTable->dataAwareObject()->acceptRowEdit();
00245
00246 QStringList sortedTableNames;
00247 for (TablesDictIterator it(*d->relations->tables());it.current();++it)
00248 sortedTableNames += it.current()->schema()->name();
00249 qHeapSort( sortedTableNames );
00250
00251
00252 QValueList<int> rowsToDelete;
00253 for (int r = 0; r<(int)d->sets->size(); r++) {
00254 KoProperty::Set *set = d->sets->at(r);
00255 if (set) {
00256 QString tableName = (*set)["table"].value().toString();
00257 QString fieldName = (*set)["field"].value().toString();
00258 const bool allTablesAsterisk = tableName=="*" && d->relations->tables()->isEmpty();
00259 const bool fieldNotFound = tableName!="*"
00260 && !(*set)["isExpression"].value().toBool()
00261 && sortedTableNames.end() == qFind( sortedTableNames.begin(), sortedTableNames.end(), tableName );
00262
00263 if (allTablesAsterisk || fieldNotFound) {
00264
00265 rowsToDelete += r;
00266 }
00267 }
00268 }
00269 d->data->deleteRows( rowsToDelete );
00270
00271
00272 d->tablesColumnData->deleteAllRows();
00273 d->fieldColumnData->deleteAllRows();
00274 d->fieldColumnIdentifiers.clear();
00275
00276 KexiTableItem *item = d->fieldColumnData->createItem();
00277 (*item)[COLUMN_ID_COLUMN]="*";
00278 (*item)[COLUMN_ID_TABLE]="*";
00279 d->fieldColumnData->append( item );
00280 d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1);
00281
00282
00283 tempData()->unregisterForTablesSchemaChanges();
00284 for (QStringList::const_iterator it = sortedTableNames.constBegin();
00285 it!=sortedTableNames.constEnd(); ++it)
00286 {
00287
00289 KexiDB::TableSchema *table = d->relations->tables()->find(*it)->schema()->table();
00290 d->conn->registerForTableSchemaChanges(*tempData(), *table);
00291 item = d->tablesColumnData->createItem();
00292 (*item)[COLUMN_ID_COLUMN]=table->name();
00293 (*item)[COLUMN_ID_TABLE]=(*item)[COLUMN_ID_COLUMN];
00294 d->tablesColumnData->append( item );
00295
00296 item = d->fieldColumnData->createItem();
00297 (*item)[COLUMN_ID_COLUMN]=table->name()+".*";
00298 (*item)[COLUMN_ID_TABLE]=(*item)[COLUMN_ID_COLUMN];
00299 d->fieldColumnData->append( item );
00300 d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1);
00301 for (KexiDB::Field::ListIterator t_it = table->fieldsIterator();t_it.current();++t_it) {
00302 item = d->fieldColumnData->createItem();
00303 (*item)[COLUMN_ID_COLUMN]=table->name()+"."+t_it.current()->name();
00304 (*item)[COLUMN_ID_TABLE]=QString(" ") + t_it.current()->name();
00305 d->fieldColumnData->append( item );
00306 d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1);
00307 }
00308 }
00309
00310 }
00311
00312 KexiRelationWidget *KexiQueryDesignerGuiEditor::relationView() const
00313 {
00314 return d->relations;
00315 }
00316
00317 KexiQueryPart::TempData *
00318 KexiQueryDesignerGuiEditor::tempData() const
00319 {
00320 return static_cast<KexiQueryPart::TempData*>(parentDialog()->tempData());
00321 }
00322
00323 static QString msgCannotSwitch_EmptyDesign() {
00324 return i18n("Cannot switch to data view, because query design is empty.\n"
00325 "First, please create your design.");
00326 }
00327
00328 bool
00329 KexiQueryDesignerGuiEditor::buildSchema(QString *errMsg)
00330 {
00331
00332 KexiQueryPart::TempData * temp = tempData();
00333 if (temp->query()) {
00334 temp->clearQuery();
00335 } else {
00336 temp->setQuery( new KexiDB::QuerySchema() );
00337 }
00338
00339
00340 for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
00342 temp->query()->addTable( it.current()->schema()->table() );
00343 }
00344
00345
00346 KexiDB::BaseExpr *whereExpr = 0;
00347 KexiTableViewData::Iterator it(d->data->iterator());
00348 const uint count = QMIN(d->data->count(), d->sets->size());
00349 bool fieldsFound = false;
00350 for (uint i=0; i<count && it.current(); ++it, i++) {
00351 if (!it.current()->at(COLUMN_ID_TABLE).isNull() && it.current()->at(COLUMN_ID_COLUMN).isNull()) {
00352
00353 kexipluginsdbg << "no field provided!" << endl;
00354 d->dataTable->dataAwareObject()->setCursorPosition(i,0);
00355 if (errMsg)
00356 *errMsg = i18n("Select column for table \"%1\"")
00357 .arg(it.current()->at(COLUMN_ID_TABLE).toString());
00358 return false;
00359 }
00360
00361 KoProperty::Set *set = d->sets->at(i);
00362 if (set) {
00363 QString tableName = (*set)["table"].value().toString().stripWhiteSpace();
00364 QString fieldName = (*set)["field"].value().toString();
00365 QString fieldAndTableName = fieldName;
00366 if (!tableName.isEmpty())
00367 fieldAndTableName.prepend(tableName+".");
00368 bool fieldVisible = (*set)["visible"].value().toBool();
00369 QString criteriaStr = (*set)["criteria"].value().toString();
00370 QCString alias = (*set)["alias"].value().toCString();
00371 if (!criteriaStr.isEmpty()) {
00372 int token;
00373 KexiDB::BaseExpr *criteriaExpr = parseExpressionString(criteriaStr, token, true);
00374 if (!criteriaExpr) {
00375 if (errMsg)
00376 *errMsg = i18n("Invalid criteria \"%1\"").arg(criteriaStr);
00377 delete whereExpr;
00378 return false;
00379 }
00380
00381 KexiDB::VariableExpr *varExpr = new KexiDB::VariableExpr(fieldAndTableName);
00382 criteriaExpr = new KexiDB::BinaryExpr(KexiDBExpr_Relational, varExpr, token, criteriaExpr);
00383
00384 if (whereExpr)
00385 whereExpr = new KexiDB::BinaryExpr(KexiDBExpr_Logical, whereExpr, AND, criteriaExpr);
00386 else
00387 whereExpr = criteriaExpr;
00388 }
00389 if (tableName.isEmpty()) {
00390 if ((*set)["isExpression"].value().toBool()==true) {
00391
00392 int dummyToken;
00393 KexiDB::BaseExpr *columnExpr = parseExpressionString(fieldName, dummyToken, false);
00394 if (!columnExpr) {
00395 if (errMsg)
00396 *errMsg = i18n("Invalid expression \"%1\"").arg(fieldName);
00397 return false;
00398 }
00399 KexiDB::Field *f = new KexiDB::Field(temp->query(), columnExpr);
00400 temp->query()->addField(f, fieldVisible);
00401 if (fieldVisible)
00402 fieldsFound = true;
00403 if (!alias.isEmpty())
00404 temp->query()->setColumnAlias( temp->query()->fieldCount()-1, alias );
00405 }
00406
00407 }
00408 else if (tableName=="*") {
00409
00410 temp->query()->addAsterisk( new KexiDB::QueryAsterisk( temp->query(), 0 ), fieldVisible );
00411 if (fieldVisible)
00412 fieldsFound = true;
00413 continue;
00414 }
00415 else {
00416 KexiDB::TableSchema *t = d->conn->tableSchema(tableName);
00417
00418 if (fieldName=="*") {
00419
00420 temp->query()->addAsterisk( new KexiDB::QueryAsterisk( temp->query(), t ), fieldVisible );
00421 if (fieldVisible)
00422 fieldsFound = true;
00423 } else {
00424 if (!t) {
00425 kexipluginswarn << "query designer: NO TABLE '" << (*set)["table"].value().toString() << "'" << endl;
00426 continue;
00427 }
00428 KexiDB::Field *f = t->field( fieldName );
00429 if (!f) {
00430 kexipluginswarn << "query designer: NO FIELD '" << fieldName << "'" << endl;
00431 continue;
00432 }
00433 temp->query()->addField(f, fieldVisible);
00434 if (fieldVisible)
00435 fieldsFound = true;
00436 if (!alias.isEmpty())
00437 temp->query()->setColumnAlias( temp->query()->fieldCount()-1, alias );
00438 }
00439 }
00440 }
00441 else {
00442 kexipluginsdbg << it.current()->at(COLUMN_ID_TABLE).toString() << endl;
00443 }
00444 }
00445 if (!fieldsFound) {
00446 if (errMsg)
00447 *errMsg = msgCannotSwitch_EmptyDesign();
00448 return false;
00449 }
00450 if (whereExpr)
00451 kexipluginsdbg << "KexiQueryDesignerGuiEditor::buildSchema(): setting CRITERIA: " << whereExpr->debugString() << endl;
00452
00453
00454
00455 temp->query()->setWhereExpression( whereExpr );
00456
00457
00458 for (ConnectionListIterator it(*d->relations->connections()); it.current(); ++it) {
00459 KexiRelationViewTableContainer *masterTable = it.current()->masterTable();
00460 KexiRelationViewTableContainer *detailsTable = it.current()->detailsTable();
00461
00463 temp->query()->addRelationship(
00464 masterTable->schema()->table()->field(it.current()->masterField()),
00465 detailsTable->schema()->table()->field(it.current()->detailsField()) );
00466 }
00467
00468 temp->query()->debug();
00469 temp->registerTableSchemaChanges(temp->query());
00470
00471 return true;
00472 }
00473
00474 tristate
00475 KexiQueryDesignerGuiEditor::beforeSwitchTo(int mode, bool &dontStore)
00476 {
00477 kexipluginsdbg << "KexiQueryDesignerGuiEditor::beforeSwitch()" << mode << endl;
00478
00479 if (!d->dataTable->dataAwareObject()->acceptRowEdit())
00480 return cancelled;
00481
00482 if (mode==Kexi::DesignViewMode) {
00483 return true;
00484 }
00485 else if (mode==Kexi::DataViewMode) {
00486
00487
00488
00489 if (!dirty() && parentDialog()->neverSaved()) {
00490 KMessageBox::information(this, msgCannotSwitch_EmptyDesign());
00491 return cancelled;
00492 }
00493 if (dirty() || !tempData()->query()) {
00494
00495 dontStore=true;
00496 QString errMsg;
00497
00498 if (!buildSchema(&errMsg)) {
00499 KMessageBox::sorry(this, errMsg);
00500 return cancelled;
00501 }
00502 }
00503
00504 return true;
00505 }
00506 else if (mode==Kexi::TextViewMode) {
00507 dontStore=true;
00508
00509 buildSchema();
00510
00511
00512
00513
00514
00515
00516 return true;
00517 }
00518
00519 return false;
00520 }
00521
00522 tristate
00523 KexiQueryDesignerGuiEditor::afterSwitchFrom(int mode)
00524 {
00525 if (mode==Kexi::NoViewMode || (mode==Kexi::DataViewMode && !tempData()->query())) {
00526
00527 if (!m_dialog->neverSaved()) {
00528 if (!loadLayout()) {
00529
00530 parentDialog()->setStatus(parentDialog()->mainWin()->project()->dbConnection(),
00531 i18n("Query definition loading failed."),
00532 i18n("Query design may be corrupted so it could not be opened even in text view.\n"
00533 "You can delete the query and create it again."));
00534 return false;
00535 }
00536
00537
00538
00539
00540 KexiDB::QuerySchema * q = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
00541 if (q)
00542 showFieldsForQuery( q );
00543
00544 }
00545 }
00546 else if (mode==Kexi::TextViewMode) {
00547 if (tempData()->queryChangedInPreviousView) {
00548
00549
00550 initTableRows();
00551
00552 if (tempData()->query()) {
00553
00554 showTablesForQuery( tempData()->query() );
00555
00556 showFieldsAndRelationsForQuery( tempData()->query() );
00557 }
00558 else {
00559 d->relations->clear();
00560 }
00561 }
00562
00563 }
00564 else if (mode==Kexi::DataViewMode) {
00565
00566
00567 if (d->dataTable->dataAwareObject()->currentRow()<0
00568 || d->dataTable->dataAwareObject()->currentColumn()<0)
00569 {
00570 d->dataTable->dataAwareObject()->ensureCellVisible(0,0);
00571 d->dataTable->dataAwareObject()->setCursorPosition(0,0);
00572 }
00573 }
00574 tempData()->queryChangedInPreviousView = false;
00575 setFocus();
00576 return true;
00577 }
00578
00579
00580 KexiDB::SchemaData*
00581 KexiQueryDesignerGuiEditor::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
00582 {
00583 if (!d->dataTable->dataAwareObject()->acceptRowEdit()) {
00584 cancel = true;
00585 return 0;
00586 }
00587 QString errMsg;
00588 if (!buildSchema(&errMsg)) {
00589 KMessageBox::sorry(this, errMsg);
00590 cancel = true;
00591 return 0;
00592 }
00593 KexiQueryPart::TempData * temp = tempData();
00594 (KexiDB::SchemaData&)*temp->query() = sdata;
00595
00596 bool ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *temp->query(), true );
00597 m_dialog->setId( temp->query()->id() );
00598
00599 if (ok)
00600 ok = storeLayout();
00601
00602
00603 if (!ok) {
00604 temp->setQuery( 0 );
00605
00606 return 0;
00607 }
00608 return temp->takeQuery();
00609 }
00610
00611 tristate KexiQueryDesignerGuiEditor::storeData(bool dontAsk)
00612 {
00613 if (!d->dataTable->dataAwareObject()->acceptRowEdit())
00614 return cancelled;
00615
00616 const bool was_dirty = dirty();
00617 tristate res = KexiViewBase::storeData(dontAsk);
00618 if (true == res)
00619 res = buildSchema();
00620 if (true == res)
00621 res = storeLayout();
00622 if (true != res) {
00623 if (was_dirty)
00624 setDirty(true);
00625 }
00626 return res;
00627 }
00628
00629
00630 void KexiQueryDesignerGuiEditor::showTablesForQuery(KexiDB::QuerySchema *query)
00631 {
00632
00633
00634
00635
00636 d->slotTableAdded_enabled = false;
00637 d->relations->removeAllConnections();
00638 d->relations->hideAllTablesExcept( query->tables() );
00639 for (KexiDB::TableSchema::ListIterator it(*query->tables()); it.current(); ++it) {
00640 d->relations->addTable( it.current() );
00641 }
00642
00643 d->slotTableAdded_enabled = true;
00644 updateColumnsData();
00645 }
00646
00647 void KexiQueryDesignerGuiEditor::addConnection(
00648 KexiDB::Field *masterField, KexiDB::Field *detailsField)
00649 {
00650 SourceConnection conn;
00651 conn.masterTable = masterField->table()->name();
00652 conn.masterField = masterField->name();
00653 conn.detailsTable = detailsField->table()->name();
00654 conn.detailsField = detailsField->name();
00655 d->relations->addConnection( conn );
00656 }
00657
00658 void KexiQueryDesignerGuiEditor::showFieldsForQuery(KexiDB::QuerySchema *query)
00659 {
00660 showFieldsOrRelationsForQueryInternal(query, true, false);
00661 }
00662
00663 void KexiQueryDesignerGuiEditor::showRelationsForQuery(KexiDB::QuerySchema *query)
00664 {
00665 showFieldsOrRelationsForQueryInternal(query, false, true);
00666 }
00667
00668 void KexiQueryDesignerGuiEditor::showFieldsAndRelationsForQuery(KexiDB::QuerySchema *query)
00669 {
00670 showFieldsOrRelationsForQueryInternal(query, true, true);
00671 }
00672
00673 void KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(
00674 KexiDB::QuerySchema *query, bool showFields, bool showRelations)
00675 {
00676 const bool was_dirty = dirty();
00677
00678
00679 if (showRelations) {
00680 KexiDB::Relationship *rel;
00681 for (KexiDB::Relationship::ListIterator it(*query->relationships());
00682 (rel=it.current()); ++it)
00683 {
00685 KexiDB::Field *masterField = rel->masterIndex()->fields()->first();
00686 KexiDB::Field *detailsField = rel->detailsIndex()->fields()->first();
00687 addConnection(masterField, detailsField);
00688 }
00689 }
00690
00691
00692
00693
00694 QDict<KexiDB::BaseExpr> criterias(101, false);
00695 KexiDB::BaseExpr* e = query->whereExpression();
00696 KexiDB::BaseExpr* eItem = 0;
00697 while (e) {
00698
00699 while (e && e->toUnary() && e->token()=='(')
00700 e = e->toUnary()->arg();
00701
00702 if (e->toBinary() && e->token()==AND) {
00703 eItem = e->toBinary()->left();
00704 e = e->toBinary()->right();
00705 }
00706 else {
00707 eItem = e;
00708 e = 0;
00709 }
00710
00711
00712 while (eItem && eItem->toUnary() && eItem->token()=='(')
00713 eItem = eItem->toUnary()->arg();
00714
00715 if (!eItem)
00716 continue;
00717
00718 kexidbg << eItem->toString() << endl;
00719 KexiDB::BinaryExpr* binary = eItem->toBinary();
00720 if (binary && eItem->exprClass()==KexiDBExpr_Relational) {
00721 KexiDB::Field *leftField = 0, *rightField = 0;
00722 if (eItem->token()=='='
00723 && binary->left()->toVariable()
00724 && binary->right()->toVariable()
00725 && (leftField = query->findTableField( binary->left()->toString() ))
00726 && (rightField = query->findTableField( binary->right()->toString() )))
00727 {
00731
00732
00733 if (showRelations) {
00736 if (leftField->isPrimaryKey())
00737 addConnection(leftField , rightField );
00738 else
00739 addConnection(rightField , leftField );
00741 }
00742 }
00743 else if (binary->left()->toVariable()) {
00744
00745
00746 criterias.insert(binary->left()->toVariable()->name, binary->right());
00747 }
00748 else if (binary->right()->toVariable()) {
00749
00750
00751 criterias.insert(binary->right()->toVariable()->name, binary->left());
00752 }
00753 }
00754 }
00755
00756 if (!showFields)
00757 return;
00758
00759
00760 uint row_num = 0;
00761 KexiDB::Field *field;
00762 QPtrDict<char> usedCriterias(101);
00763
00764
00765 for (KexiDB::Field::ListIterator it(*query->fields());
00766 (field = it.current()); ++it, row_num++)
00767 {
00768
00769 QString tableName, fieldName, columnAlias, criteriaString;
00770 KexiDB::BinaryExpr *criteriaExpr = 0;
00771 KexiDB::BaseExpr *criteriaArgument = 0;
00772 if (field->isQueryAsterisk()) {
00773 if (field->table()) {
00774 tableName = field->table()->name();
00775 fieldName = "*";
00776 }
00777 else {
00778 tableName = "*";
00779 fieldName = "";
00780 }
00781 }
00782 else {
00783 columnAlias = query->columnAlias(row_num);
00784 if (field->isExpression()) {
00785
00786
00787
00788
00789
00790 fieldName = field->expression()->toString();
00791
00792
00793 }
00794 else {
00795 tableName = field->table()->name();
00796 fieldName = field->name();
00797 criteriaArgument = criterias[fieldName];
00798 if (!criteriaArgument) {
00799 criteriaArgument = criterias[tableName+"."+fieldName];
00800 }
00801 if (criteriaArgument) {
00802 criteriaExpr = criteriaArgument->parent()->toBinary();
00803 usedCriterias.insert(criteriaArgument, (char*)1);
00804 }
00805 }
00806 }
00807
00808 KexiTableItem *newItem = createNewRow(tableName, fieldName);
00809 if (criteriaExpr) {
00811 if (criteriaExpr->token()=='=')
00812 criteriaString = criteriaArgument->toString();
00813 else
00814 criteriaString = criteriaExpr->tokenToString() + " " + criteriaArgument->toString();
00815 (*newItem)[COLUMN_ID_CRITERIA] = criteriaString;
00816 }
00817 d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
00818
00819 KoProperty::Set &set = *createPropertySet( row_num, tableName, fieldName, true );
00820 if (!columnAlias.isEmpty())
00821 set["alias"].setValue(columnAlias, false);
00822 if (!criteriaString.isEmpty())
00823 set["criteria"].setValue( criteriaString, false );
00824 if (field->isExpression()) {
00825 (*newItem)[COLUMN_ID_COLUMN] = criteriaString;
00826 d->data->clearRowEditBuffer();
00827 d->data->updateRowEditBuffer(newItem, COLUMN_ID_COLUMN,
00828 QVariant(columnAlias + ": " + field->expression()->toString()));
00829 d->data->saveRowChanges(*newItem, true);
00830 }
00831 }
00832
00833
00834 KexiDB::BaseExpr *criteriaArgument;
00835 for (QDictIterator<KexiDB::BaseExpr> it(criterias); (criteriaArgument = it.current()); ++it) {
00836 if (usedCriterias[it.current()])
00837 continue;
00838
00839 KexiDB::BinaryExpr *criteriaExpr = criteriaArgument->parent()->toBinary();
00840 if (!criteriaExpr) {
00841 kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
00842 "criteriaExpr is not a binary expr" << endl;
00843 continue;
00844 }
00845 KexiDB::VariableExpr *columnNameArgument = criteriaExpr->left()->toVariable();
00846 if (!columnNameArgument) {
00847 columnNameArgument = criteriaExpr->right()->toVariable();
00848 if (!columnNameArgument) {
00849 kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
00850 "columnNameArgument is not a variable (table or table.field) expr" << endl;
00851 continue;
00852 }
00853 }
00854 KexiDB::Field* field = 0;
00855 if (-1 == columnNameArgument->name.find('.') && query->tables()->count()==1) {
00856
00857 field = query->tables()->first()->field(columnNameArgument->name);
00858 }
00859 else {
00860 field = query->findTableField(columnNameArgument->name);
00861 }
00862
00863 if (!field) {
00864 kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
00865 "no columnInfo found in the query for name \"" << columnNameArgument->name << endl;
00866 continue;
00867 }
00868 QString tableName, fieldName, columnAlias, criteriaString;
00870 tableName = field->table()->name();
00871 fieldName = field->name();
00872
00873 KexiTableItem *newItem = createNewRow(tableName, fieldName, false );
00874 if (criteriaExpr) {
00876 if (criteriaExpr->token()=='=')
00877 criteriaString = criteriaArgument->toString();
00878 else
00879 criteriaString = criteriaExpr->tokenToString() + " " + criteriaArgument->toString();
00880 (*newItem)[COLUMN_ID_CRITERIA] = criteriaString;
00881 }
00882 d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
00883
00884 KoProperty::Set &set = *createPropertySet( row_num++, tableName, fieldName, true );
00888 set["criteria"].setValue( criteriaString, false );
00889 set["visible"].setValue( QVariant(false,1), false );
00890 }
00891
00892
00893 propertySetSwitched();
00894
00895 if (!was_dirty)
00896 setDirty(false);
00897
00898 d->dataTable->dataAwareObject()->ensureCellVisible(0,0);
00899
00900 }
00901
00902 bool KexiQueryDesignerGuiEditor::loadLayout()
00903 {
00904 QString xml;
00905
00906 loadDataBlock( xml, "query_layout" );
00907
00908
00909
00910 if (xml.isEmpty()) {
00911
00912
00913
00914 KexiDB::QuerySchema * q = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
00915 if (q) {
00916 showTablesForQuery( q );
00917 showRelationsForQuery( q );
00918 }
00919 return true;
00920 }
00921
00922 QDomDocument doc;
00923 doc.setContent(xml);
00924 QDomElement doc_el = doc.documentElement(), el;
00925 if (doc_el.tagName()!="query_layout") {
00926
00927 return false;
00928 }
00929
00930 const bool was_dirty = dirty();
00931
00932
00933 for (el = doc_el.firstChild().toElement(); !el.isNull(); el=el.nextSibling().toElement()) {
00934 if (el.tagName()=="table") {
00935 KexiDB::TableSchema *t = d->conn->tableSchema(el.attribute("name"));
00936 int x = el.attribute("x","-1").toInt();
00937 int y = el.attribute("y","-1").toInt();
00938 int width = el.attribute("width","-1").toInt();
00939 int height = el.attribute("height","-1").toInt();
00940 QRect rect;
00941 if (x!=-1 || y!=-1 || width!=-1 || height!=-1)
00942 rect = QRect(x,y,width,height);
00943 d->relations->addTable( t, rect );
00944 }
00945 else if (el.tagName()=="conn") {
00946 SourceConnection src_conn;
00947 src_conn.masterTable = el.attribute("mtable");
00948 src_conn.masterField = el.attribute("mfield");
00949 src_conn.detailsTable = el.attribute("dtable");
00950 src_conn.detailsField = el.attribute("dfield");
00951 d->relations->addConnection(src_conn);
00952 }
00953 }
00954
00955 if (!was_dirty)
00956 setDirty(false);
00957 return true;
00958 }
00959
00960 bool KexiQueryDesignerGuiEditor::storeLayout()
00961 {
00962 KexiQueryPart::TempData * temp = tempData();
00963
00964
00965 KexiDB::Connection* dbConn = mainWin()->project()->dbConnection();
00966 if (m_dialog->schemaData())
00967 dbConn->setQuerySchemaObsolete( m_dialog->schemaData()->name() );
00968
00969 QString sqlText = dbConn->selectStatement(
00970 *temp->query(), KexiDB::Driver::EscapeKexi|KexiDB::Driver::EscapeAsNecessary );
00971 if (!storeDataBlock( sqlText, "sql" )) {
00972 return false;
00973 }
00974
00975
00976 QString xml = "<query_layout>", tmp;
00977 for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
00978 KexiRelationViewTableContainer *table_cont = it.current();
00980 tmp = QString("<table name=\"")+QString(table_cont->schema()->name())+"\" x=\""
00981 +QString::number(table_cont->x())
00982 +"\" y=\""+QString::number(table_cont->y())
00983 +"\" width=\""+QString::number(table_cont->width())
00984 +"\" height=\""+QString::number(table_cont->height())
00985 +"\"/>";
00986 xml += tmp;
00987 }
00988
00989 KexiRelationViewConnection *con;
00990 for (ConnectionListIterator it(*d->relations->connections()); (con = it.current()); ++it) {
00991 tmp = QString("<conn mtable=\"") + QString(con->masterTable()->schema()->name())
00992 + "\" mfield=\"" + con->masterField() + "\" dtable=\""
00993 + QString(con->detailsTable()->schema()->name())
00994 + "\" dfield=\"" + con->detailsField() + "\"/>";
00995 xml += tmp;
00996 }
00997 xml += "</query_layout>";
00998 if (!storeDataBlock( xml, "query_layout" )) {
00999 return false;
01000 }
01001
01002
01003
01004 return true;
01005 }
01006
01007 QSize KexiQueryDesignerGuiEditor::sizeHint() const
01008 {
01009 QSize s1 = d->relations->sizeHint();
01010 QSize s2 = d->head->sizeHint();
01011 return QSize(QMAX(s1.width(),s2.width()), s1.height()+s2.height());
01012 }
01013
01014 KexiTableItem*
01015 KexiQueryDesignerGuiEditor::createNewRow(const QString& tableName, const QString& fieldName,
01016 bool visible) const
01017 {
01018 KexiTableItem *newItem = d->data->createItem();
01019 QString key;
01020
01021
01022 if (tableName=="*")
01023 key="*";
01024 else {
01025 if (!tableName.isEmpty())
01026 key = (tableName+".");
01027 key += fieldName;
01028 }
01029 (*newItem)[COLUMN_ID_COLUMN]=key;
01030 (*newItem)[COLUMN_ID_TABLE]=tableName;
01031 (*newItem)[COLUMN_ID_VISIBLE]=QVariant(visible, 1);
01032 #ifndef KEXI_NO_QUERY_TOTALS
01033 (*newItem)[COLUMN_ID_TOTALS]=QVariant(0);
01034 #endif
01035 return newItem;
01036 }
01037
01038 void KexiQueryDesignerGuiEditor::slotDragOverTableRow(
01039 KexiTableItem * , int , QDragMoveEvent* e)
01040 {
01041 if (e->provides("kexi/field")) {
01042 e->acceptAction(true);
01043 }
01044 }
01045
01046 void
01047 KexiQueryDesignerGuiEditor::slotDroppedAtRow(KexiTableItem * , int ,
01048 QDropEvent *ev, KexiTableItem*& newItem)
01049 {
01050 QString sourceMimeType;
01051 QString srcTable;
01052 QString srcField;
01053
01054 if (!KexiFieldDrag::decodeSingle(ev,sourceMimeType,srcTable,srcField))
01055 return;
01056
01057 newItem = createNewRow(srcTable, srcField);
01058 d->droppedNewItem = newItem;
01059 d->droppedNewTable = srcTable;
01060 d->droppedNewField = srcField;
01061
01062 }
01063
01064 void KexiQueryDesignerGuiEditor::slotRowInserted(KexiTableItem* item, uint row, bool )
01065 {
01066 if (d->droppedNewItem && d->droppedNewItem==item) {
01067 createPropertySet( row, d->droppedNewTable, d->droppedNewField, true );
01068 propertySetSwitched();
01069 d->droppedNewItem=0;
01070 }
01071 }
01072
01073 void KexiQueryDesignerGuiEditor::slotTableAdded(KexiDB::TableSchema & )
01074 {
01075 if (!d->slotTableAdded_enabled)
01076 return;
01077 updateColumnsData();
01078 setDirty();
01079 d->dataTable->setFocus();
01080 }
01081
01082 void KexiQueryDesignerGuiEditor::slotTableHidden(KexiDB::TableSchema & )
01083 {
01084 updateColumnsData();
01085 setDirty();
01086 }
01087
01089 QCString KexiQueryDesignerGuiEditor::generateUniqueAlias() const
01090 {
01091
01092 const QCString expStr
01093 = i18n("short for 'expression' word (only latin letters, please)", "expr").latin1();
01094
01095 QAsciiDict<char> aliases(101);
01096 for (int r = 0; r<(int)d->sets->size(); r++) {
01097 KoProperty::Set *set = d->sets->at(r);
01098 if (set) {
01099 const QCString a = (*set)["alias"].value().toCString().lower();
01100 if (!a.isEmpty())
01101 aliases.insert(a,(char*)1);
01102 }
01103 }
01104 int aliasNr=1;
01105 for (;;aliasNr++) {
01106 if (!aliases[expStr+QString::number(aliasNr).latin1()])
01107 break;
01108 }
01109 return expStr+QString::number(aliasNr).latin1();
01110 }
01111
01113 KexiDB::BaseExpr*
01114 KexiQueryDesignerGuiEditor::parseExpressionString(const QString& fullString, int& token,
01115 bool allowRelationalOperator)
01116 {
01117 QString str = fullString.stripWhiteSpace();
01118 int len = 0;
01119
01120
01121 token = 0;
01122
01123 if (str.startsWith(">="))
01124 token = GREATER_OR_EQUAL;
01125 else if (str.startsWith("<="))
01126 token = LESS_OR_EQUAL;
01127 else if (str.startsWith("<>"))
01128 token = NOT_EQUAL;
01129 else if (str.startsWith("!="))
01130 token = NOT_EQUAL2;
01131 else if (str.startsWith("=="))
01132 token = '=';
01133
01134 if (token!=0)
01135 len = 2;
01136 else if (str.startsWith("=")
01137 || str.startsWith("<")
01138 || str.startsWith(">"))
01139 {
01140 token = str[0].latin1();
01141 len = 1;
01142 }
01143 else {
01144 if (allowRelationalOperator)
01145 token = '=';
01146 }
01147
01148 if (!allowRelationalOperator && token!=0)
01149 return 0;
01150
01151
01152 if (len>0)
01153 str = str.mid(len).stripWhiteSpace();
01154 if (str.isEmpty())
01155 return 0;
01156
01157 KexiDB::BaseExpr *valueExpr = 0;
01158 QRegExp re;
01159 if (str.length()>=2
01160 && (
01161 (str.startsWith("\"") && str.endsWith("\""))
01162 || (str.startsWith("'") && str.endsWith("'")))
01163 )
01164 {
01165 valueExpr = new KexiDB::ConstExpr(CHARACTER_STRING_LITERAL, str.mid(1,str.length()-2));
01166 }
01167 else if ((re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})")).exactMatch( str ))
01168 {
01169 valueExpr = new KexiDB::ConstExpr(DATE_CONST, QDate::fromString(
01170 re.cap(1).rightJustify(4, '0')+"-"+re.cap(2).rightJustify(2, '0')
01171 +"-"+re.cap(3).rightJustify(2, '0'), Qt::ISODate));
01172 }
01173 else if ((re = QRegExp("(\\d{1,2}):(\\d{1,2})")).exactMatch( str )
01174 || (re = QRegExp("(\\d{1,2}):(\\d{1,2}):(\\d{1,2})")).exactMatch( str ))
01175 {
01176 QString res = re.cap(1).rightJustify(2, '0')+":"+re.cap(2).rightJustify(2, '0')
01177 +":"+re.cap(3).rightJustify(2, '0');
01178
01179 valueExpr = new KexiDB::ConstExpr(TIME_CONST, QTime::fromString(res, Qt::ISODate));
01180 }
01181 else if ((re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2})")).exactMatch( str )
01182 || (re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2}):(\\d{1,2})")).exactMatch( str ))
01183 {
01184 QString res = re.cap(1).rightJustify(4, '0')+"-"+re.cap(2).rightJustify(2, '0')
01185 +"-"+re.cap(3).rightJustify(2, '0')
01186 +"T"+re.cap(4).rightJustify(2, '0')+":"+re.cap(5).rightJustify(2, '0')
01187 +":"+re.cap(6).rightJustify(2, '0');
01188
01189 valueExpr = new KexiDB::ConstExpr(DATETIME_CONST,
01190 QDateTime::fromString(res, Qt::ISODate));
01191 }
01192 else if (str[0]>='0' && str[0]<='9' || str[0]=='-' || str[0]=='+') {
01193
01194 QString decimalSym = KGlobal::locale()->decimalSymbol();
01195 bool ok;
01196 int pos = str.find('.');
01197 if (pos==-1) {
01198 pos = str.find(decimalSym);
01199 }
01200 if (pos>=0) {
01201 const int left = str.left(pos).toInt(&ok);
01202 if (!ok)
01203 return 0;
01204 const int right = str.mid(pos+1).toInt(&ok);
01205 if (!ok)
01206 return 0;
01207 valueExpr = new KexiDB::ConstExpr(REAL_CONST, QPoint(left,right));
01208 }
01209 else {
01210
01211 const Q_LLONG val = str.toLongLong(&ok);
01212 if (!ok)
01213 return 0;
01214 valueExpr = new KexiDB::ConstExpr(INTEGER_CONST, val);
01215 }
01216 }
01217 else if (str.lower()=="null") {
01218 valueExpr = new KexiDB::ConstExpr(SQL_NULL, QVariant());
01219 }
01220 else {
01221 if (!KexiUtils::isIdentifier(str))
01222 return 0;
01223 valueExpr = new KexiDB::VariableExpr(str);
01224
01225 for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
01227 if (it.current()->schema()->table() && it.current()->schema()->table()->field(str)) {
01228 valueExpr->toVariable()->field = it.current()->schema()->table()->field(str);
01229 break;
01230 }
01231 }
01232 }
01233 return valueExpr;
01234 }
01235
01236 void KexiQueryDesignerGuiEditor::slotBeforeCellChanged(KexiTableItem *item, int colnum,
01237 QVariant& newValue, KexiDB::ResultInfo* result)
01238 {
01239 if (colnum == COLUMN_ID_COLUMN) {
01240 if (newValue.isNull()) {
01241 d->data->updateRowEditBuffer(item, COLUMN_ID_TABLE, QVariant(), false);
01242 d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(false,1));
01243 #ifndef KEXI_NO_QUERY_TOTALS
01244 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant());
01245 #endif
01246 d->data->updateRowEditBuffer(item, COLUMN_ID_CRITERIA, QVariant());
01247 d->sets->removeCurrentPropertySet();
01248 }
01249 else {
01250
01251 QString fieldId( newValue.toString().stripWhiteSpace() );
01252 QString fieldName;
01253 QString tableName;
01254 QCString alias;
01255 QString columnValueForExpr;
01256 const bool isExpression = !d->fieldColumnIdentifiers[fieldId];
01257 if (isExpression) {
01258
01259
01260
01261
01262 const int id = fieldId.find(':');
01263 if (id>0) {
01264 alias = fieldId.left(id).stripWhiteSpace().latin1();
01265 if (!KexiUtils::isIdentifier(alias)) {
01266 result->success = false;
01267 result->column = 0;
01268 result->msg = i18n("Entered column alias \"%1\" is not a valid identifier.")
01269 .arg(alias);
01270 result->desc = i18n("Identifiers should start with a letter or '_' character");
01271 return;
01272 }
01273 }
01274 fieldName = fieldId.mid(id+1).stripWhiteSpace();
01275
01276 KexiDB::BaseExpr *e;
01277 int dummyToken;
01278 if ((e = parseExpressionString(fieldName, dummyToken, false)))
01279 {
01280 fieldName = e->toString();
01281
01282 delete e;
01283 }
01284 else {
01285 result->success = false;
01286 result->column = 0;
01287 result->msg = i18n("Invalid expression \"%1\"").arg(fieldName);
01288 return;
01289 }
01290 }
01291 else {
01292
01293 if (fieldId=="*") {
01294 tableName = "*";
01295 }
01296 else {
01297 if (!KexiDB::splitToTableAndFieldParts(
01298 fieldId, tableName, fieldName, KexiDB::SetFieldNameIfNoTableName))
01299 {
01300 kexipluginswarn << "KexiQueryDesignerGuiEditor::slotBeforeCellChanged(): no 'field' or 'table.field'" << endl;
01301 return;
01302 }
01303 }
01304 }
01305 bool saveOldValue = true;
01306 KoProperty::Set *set = d->sets->listForItem(*item);
01307 if (!set) {
01308 saveOldValue = false;
01309 const int row = d->data->findRef(item);
01310 if (row<0) {
01311 result->success = false;
01312 return;
01313 }
01314 set = createPropertySet( row, tableName, fieldName, true );
01315 propertySetSwitched();
01316 }
01317 d->data->updateRowEditBuffer(item, COLUMN_ID_TABLE, QVariant(tableName), false);
01318 d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(true,1));
01319 #ifndef KEXI_NO_QUERY_TOTALS
01320 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant(0));
01321 #endif
01322
01323 (*set)["field"].setValue(fieldName, saveOldValue);
01324 if (isExpression) {
01325
01326 if (alias.isEmpty())
01327 alias = (*set)["alias"].value().toCString();
01328 if (alias.isEmpty())
01329 alias = generateUniqueAlias();
01330 }
01331 (*set)["isExpression"].setValue(QVariant(isExpression,1), saveOldValue);
01332 if (!alias.isEmpty()) {
01333 (*set)["alias"].setValue(alias, saveOldValue);
01334
01335 newValue = QString(alias) + ": " + fieldName;
01336 }
01337 (*set)["caption"].setValue(QString::null, saveOldValue);
01338 (*set)["table"].setValue(tableName, saveOldValue);
01339 updatePropertiesVisibility(*set);
01340 }
01341 }
01342 else if (colnum==COLUMN_ID_TABLE) {
01343 if (newValue.isNull()) {
01344 if (!item->at(COLUMN_ID_COLUMN).toString().isEmpty())
01345 d->data->updateRowEditBuffer(item, COLUMN_ID_COLUMN, QVariant(), false);
01346 d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(false,1));
01347 #ifndef KEXI_NO_QUERY_TOTALS
01348 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant());
01349 #endif
01350 d->data->updateRowEditBuffer(item, COLUMN_ID_CRITERIA, QVariant());
01351 d->sets->removeCurrentPropertySet();
01352 }
01353
01354 KoProperty::Set *set = d->sets->listForItem(*item);
01355 if (set) {
01356 if ((*set)["isExpression"].value().toBool()==false) {
01357 (*set)["table"] = newValue;
01358 (*set)["caption"] = QString::null;
01359 }
01360 else {
01361
01362 newValue = QVariant();
01363 }
01364
01365 updatePropertiesVisibility(*set);
01366 }
01367 }
01368 else if (colnum==COLUMN_ID_VISIBLE) {
01369 bool saveOldValue = true;
01370 if (!propertySet()) {
01371 saveOldValue = false;
01372 createPropertySet( d->dataTable->dataAwareObject()->currentRow(),
01373 item->at(COLUMN_ID_TABLE).toString(), item->at(COLUMN_ID_COLUMN).toString(), true );
01374 #ifndef KEXI_NO_QUERY_TOTALS
01375 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant(0));
01376 #endif
01377 propertySetSwitched();
01378 }
01379 KoProperty::Set &set = *propertySet();
01380 set["visible"].setValue(newValue, saveOldValue);
01381 }
01382 #ifndef KEXI_NO_QUERY_TOTALS
01383 else if (colnum==COLUMN_ID_TOTALS) {
01384
01385
01386 setDirty(true);
01387 }
01388 #endif
01389 else if (colnum==COLUMN_ID_CRITERIA) {
01390
01391 QString operatorStr, argStr;
01392 KexiDB::BaseExpr* e = 0;
01393 const QString str = newValue.toString().stripWhiteSpace();
01394
01395 int token;
01396 QString field, table;
01397 KoProperty::Set *set = d->sets->listForItem(*item);
01398 if (set) {
01399 field = (*set)["field"].value().toString();
01400 table = (*set)["table"].value().toString();
01401 }
01402 if (!str.isEmpty() && (!set || table=="*" || field.find("*")!=-1)) {
01403
01404 result->success = false;
01405 result->column = 4;
01406 if (propertySet())
01407 result->msg = i18n("Could not set criteria for \"%1\"")
01408 .arg(table=="*" ? table : field);
01409 else
01410 result->msg = i18n("Could not set criteria for empty row");
01411 d->dataTable->dataAwareObject()->cancelEditor();
01412 }
01413 else if (str.isEmpty() || (e = parseExpressionString(str, token, true)))
01414 {
01415 if (e) {
01416 QString tokenStr;
01417 if (token!='=') {
01418 KexiDB::BinaryExpr be(KexiDBExpr_Relational, 0, token, 0);
01419 tokenStr = be.tokenToString() + " ";
01420 }
01421 (*set)["criteria"] = tokenStr + e->toString();
01422
01423 delete e;
01424 }
01425 else if (str.isEmpty()) {
01426 (*set)["criteria"] = QVariant();
01427 }
01428 setDirty(true);
01429 }
01430 else {
01431 result->success = false;
01432 result->column = 4;
01433 result->msg = i18n("Invalid criteria \"%1\"").arg(newValue.toString());
01434 }
01435 }
01436 }
01437
01438 void KexiQueryDesignerGuiEditor::slotTablePositionChanged(KexiRelationViewTableContainer*)
01439 {
01440 setDirty(true);
01441 }
01442
01443 void KexiQueryDesignerGuiEditor::slotAboutConnectionRemove(KexiRelationViewConnection*)
01444 {
01445 setDirty(true);
01446 }
01447
01448 void KexiQueryDesignerGuiEditor::slotTableFieldDoubleClicked(
01449 KexiDB::TableSchema* table, const QString& fieldName )
01450 {
01451 if (!table || (!table->field(fieldName) && fieldName!="*"))
01452 return;
01453 int row_num;
01454
01455 for (row_num=d->sets->size()-1; row_num>=0 && !d->sets->at(row_num); row_num--)
01456 ;
01457 row_num++;
01458
01459 KexiTableItem *newItem = createNewRow(table->name(), fieldName);
01460 d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
01461 d->dataTable->dataAwareObject()->setCursorPosition(row_num, 0);
01462
01463 createPropertySet( row_num, table->name(), fieldName, true );
01464 propertySetSwitched();
01465 d->dataTable->setFocus();
01466 }
01467
01468 KoProperty::Set *KexiQueryDesignerGuiEditor::propertySet()
01469 {
01470 return d->sets->currentPropertySet();
01471 }
01472
01473 static bool isAsterisk(const QString& tableName, const QString& fieldName)
01474 {
01475 return tableName=="*" || fieldName.endsWith("*");
01476 }
01477
01478 void KexiQueryDesignerGuiEditor::updatePropertiesVisibility(KoProperty::Set& set)
01479 {
01480 const bool asterisk = isAsterisk(
01481 set["table"].value().toString(), set["field"].value().toString()
01482 );
01483 #ifndef KEXI_NO_UNFINISHED
01484 set["caption"].setVisible( !asterisk );
01485 #endif
01486 set["alias"].setVisible( !asterisk );
01487 #ifndef KEXI_NO_UNFINISHED
01488 set["sorting"].setVisible( !asterisk );
01489 #endif
01490 propertySetReloaded(true);
01491 }
01492
01493 KoProperty::Set*
01494 KexiQueryDesignerGuiEditor::createPropertySet( int row,
01495 const QString& tableName, const QString& fieldName, bool newOne )
01496 {
01497
01498 QString typeName = "KexiQueryDesignerGuiEditor::Column";
01499 KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
01500 KoProperty::Property *prop;
01501
01502
01503 set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Query column")) );
01504 prop->setVisible(false);
01506
01507
01508 set->addProperty(prop = new KoProperty::Property("table", QVariant(tableName)) );
01509 prop->setVisible(false);
01510
01511 set->addProperty(prop = new KoProperty::Property("field", QVariant(fieldName)) );
01512 prop->setVisible(false);
01513
01514 set->addProperty(prop = new KoProperty::Property("caption", QVariant(QString::null), i18n("Caption") ) );
01515 #ifdef KEXI_NO_UNFINISHED
01516 prop->setVisible(false);
01517 #endif
01518
01519 set->addProperty(prop = new KoProperty::Property("alias", QVariant(QString::null), i18n("Alias")) );
01520
01521 set->addProperty(prop = new KoProperty::Property("visible", QVariant(true, 4)) );
01522 prop->setVisible(false);
01523
01524
01525
01526
01527
01528
01529 QStringList slist, nlist;
01530 slist << "nosorting" << "ascending" << "descending";
01531 nlist << i18n("None") << i18n("Ascending") << i18n("Descending");
01532 set->addProperty(prop = new KoProperty::Property("sorting",
01533 slist, nlist, slist[0], i18n("Sorting")));
01534 #ifdef KEXI_NO_UNFINISHED
01535 prop->setVisible(false);
01536 #endif
01537
01538 set->addProperty(prop = new KoProperty::Property("criteria", QVariant(QString::null)) );
01539 prop->setVisible(false);
01540
01541 set->addProperty(prop = new KoProperty::Property("isExpression", QVariant(false, 1)) );
01542 prop->setVisible(false);
01543
01544 connect(set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
01545 this, SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
01546
01547 d->sets->insert(row, set, newOne);
01548
01549 updatePropertiesVisibility(*set);
01550 return set;
01551 }
01552
01553 void KexiQueryDesignerGuiEditor::setFocus()
01554 {
01555 d->dataTable->setFocus();
01556 }
01557
01558 void KexiQueryDesignerGuiEditor::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
01559 {
01560 const QCString& pname = property.name();
01561
01562
01563
01564 if (pname=="alias" || pname=="name") {
01565 const QVariant& v = property.value();
01566 if (!v.toString().stripWhiteSpace().isEmpty() && !KexiUtils::isIdentifier( v.toString() )) {
01567 KMessageBox::sorry(this,
01568 KexiUtils::identifierExpectedMessage(property.caption(), v.toString()));
01569 property.resetValue();
01570 }
01571 if (pname=="alias") {
01572 if (set["isExpression"].value().toBool()==true) {
01573
01574 d->dataTable->dataAwareObject()->acceptEditor();
01575
01576
01577 d->data->updateRowEditBuffer(d->dataTable->dataAwareObject()->selectedItem(),
01578 0, QVariant(set["alias"].value().toString() + ": " + set["field"].value().toString()));
01579 d->data->saveRowChanges(*d->dataTable->dataAwareObject()->selectedItem(), true);
01580
01581 }
01582 }
01583 }
01584 }
01585
01586 void KexiQueryDesignerGuiEditor::slotNewItemStored(KexiPart::Item& item)
01587 {
01588 d->relations->objectCreated(item.mimeType(), item.name().latin1());
01589 }
01590
01591 void KexiQueryDesignerGuiEditor::slotItemRemoved(const KexiPart::Item& item)
01592 {
01593 d->relations->objectDeleted(item.mimeType(), item.name().latin1());
01594 }
01595
01596 void KexiQueryDesignerGuiEditor::slotItemRenamed(const KexiPart::Item& item, const QCString& oldName)
01597 {
01598 d->relations->objectRenamed(item.mimeType(), oldName, item.name().latin1());
01599 }
01600
01601 #include "kexiquerydesignerguieditor.moc"
01602