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