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 options.addVisibleLookupColumns = false;
01143 QString sqlText = dbConn->selectStatement( *temp->query(), options );
01144 if (!storeDataBlock( sqlText, "sql" )) {
01145 return false;
01146 }
01147
01148
01149 QString xml = "<query_layout>", tmp;
01150 for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
01151 KexiRelationViewTableContainer *table_cont = it.current();
01153 tmp = QString("<table name=\"")+QString(table_cont->schema()->name())+"\" x=\""
01154 +QString::number(table_cont->x())
01155 +"\" y=\""+QString::number(table_cont->y())
01156 +"\" width=\""+QString::number(table_cont->width())
01157 +"\" height=\""+QString::number(table_cont->height())
01158 +"\"/>";
01159 xml += tmp;
01160 }
01161
01162 KexiRelationViewConnection *con;
01163 for (ConnectionListIterator it(*d->relations->connections()); (con = it.current()); ++it) {
01164 tmp = QString("<conn mtable=\"") + QString(con->masterTable()->schema()->name())
01165 + "\" mfield=\"" + con->masterField() + "\" dtable=\""
01166 + QString(con->detailsTable()->schema()->name())
01167 + "\" dfield=\"" + con->detailsField() + "\"/>";
01168 xml += tmp;
01169 }
01170 xml += "</query_layout>";
01171 if (!storeDataBlock( xml, "query_layout" )) {
01172 return false;
01173 }
01174
01175
01176
01177 return true;
01178 }
01179
01180 QSize KexiQueryDesignerGuiEditor::sizeHint() const
01181 {
01182 QSize s1 = d->relations->sizeHint();
01183 QSize s2 = d->head->sizeHint();
01184 return QSize(QMAX(s1.width(),s2.width()), s1.height()+s2.height());
01185 }
01186
01187 KexiTableItem*
01188 KexiQueryDesignerGuiEditor::createNewRow(const QString& tableName, const QString& fieldName,
01189 bool visible) const
01190 {
01191 KexiTableItem *newItem = d->data->createItem();
01192 QString key;
01193 if (tableName=="*")
01194 key="*";
01195 else {
01196 if (!tableName.isEmpty())
01197 key = (tableName+".");
01198 key += fieldName;
01199 }
01200 (*newItem)[COLUMN_ID_COLUMN]=key;
01201 (*newItem)[COLUMN_ID_TABLE]=tableName;
01202 (*newItem)[COLUMN_ID_VISIBLE]=QVariant(visible, 1);
01203 #ifndef KEXI_NO_QUERY_TOTALS
01204 (*newItem)[COLUMN_ID_TOTALS]=QVariant(0);
01205 #endif
01206 return newItem;
01207 }
01208
01209 void KexiQueryDesignerGuiEditor::slotDragOverTableRow(
01210 KexiTableItem * , int , QDragMoveEvent* e)
01211 {
01212 if (e->provides("kexi/field")) {
01213 e->acceptAction(true);
01214 }
01215 }
01216
01217 void
01218 KexiQueryDesignerGuiEditor::slotDroppedAtRow(KexiTableItem * , int ,
01219 QDropEvent *ev, KexiTableItem*& newItem)
01220 {
01221 QString sourceMimeType;
01222 QString srcTable;
01223 QString srcField;
01224
01225 if (!KexiFieldDrag::decodeSingle(ev,sourceMimeType,srcTable,srcField))
01226 return;
01227
01228 newItem = createNewRow(srcTable, srcField, true );
01229 d->droppedNewItem = newItem;
01230 d->droppedNewTable = srcTable;
01231 d->droppedNewField = srcField;
01232
01233 }
01234
01235 void KexiQueryDesignerGuiEditor::slotNewItemAppendedForAfterDeletingInSpreadSheetMode()
01236 {
01237 KexiTableItem *item = d->data->last();
01238 if (item)
01239 item->at(COLUMN_ID_VISIBLE) = QVariant(false, 0);
01240 }
01241
01242 void KexiQueryDesignerGuiEditor::slotRowInserted(KexiTableItem* item, uint row, bool )
01243 {
01244 if (d->droppedNewItem && d->droppedNewItem==item) {
01245 createPropertySet( row, d->droppedNewTable, d->droppedNewField, true );
01246 propertySetSwitched();
01247 d->droppedNewItem=0;
01248 }
01249 }
01250
01251 void KexiQueryDesignerGuiEditor::slotTableAdded(KexiDB::TableSchema & )
01252 {
01253 if (!d->slotTableAdded_enabled)
01254 return;
01255 updateColumnsData();
01256 setDirty();
01257 d->dataTable->setFocus();
01258 }
01259
01260 void KexiQueryDesignerGuiEditor::slotTableHidden(KexiDB::TableSchema & )
01261 {
01262 updateColumnsData();
01263 setDirty();
01264 }
01265
01267 QCString KexiQueryDesignerGuiEditor::generateUniqueAlias() const
01268 {
01269
01270 const QCString expStr
01271 = i18n("short for 'expression' word (only latin letters, please)", "expr").latin1();
01272
01273 QAsciiDict<char> aliases(101);
01274 for (int r = 0; r<(int)d->sets->size(); r++) {
01275 KoProperty::Set *set = d->sets->at(r);
01276 if (set) {
01277 const QCString a = (*set)["alias"].value().toCString().lower();
01278 if (!a.isEmpty())
01279 aliases.insert(a,(char*)1);
01280 }
01281 }
01282 int aliasNr=1;
01283 for (;;aliasNr++) {
01284 if (!aliases[expStr+QString::number(aliasNr).latin1()])
01285 break;
01286 }
01287 return expStr+QString::number(aliasNr).latin1();
01288 }
01289
01291 KexiDB::BaseExpr*
01292 KexiQueryDesignerGuiEditor::parseExpressionString(const QString& fullString, int& token,
01293 bool allowRelationalOperator)
01294 {
01295 QString str = fullString.stripWhiteSpace();
01296 int len = 0;
01297
01298
01299 token = 0;
01300
01301 if (str.startsWith(">="))
01302 token = GREATER_OR_EQUAL;
01303 else if (str.startsWith("<="))
01304 token = LESS_OR_EQUAL;
01305 else if (str.startsWith("<>"))
01306 token = NOT_EQUAL;
01307 else if (str.startsWith("!="))
01308 token = NOT_EQUAL2;
01309 else if (str.startsWith("=="))
01310 token = '=';
01311
01312 if (token!=0)
01313 len = 2;
01314 else if (str.startsWith("=")
01315 || str.startsWith("<")
01316 || str.startsWith(">"))
01317 {
01318 token = str[0].latin1();
01319 len = 1;
01320 }
01321 else {
01322 if (allowRelationalOperator)
01323 token = '=';
01324 }
01325
01326 if (!allowRelationalOperator && token!=0)
01327 return 0;
01328
01329
01330 if (len>0)
01331 str = str.mid(len).stripWhiteSpace();
01332 if (str.isEmpty())
01333 return 0;
01334
01335 KexiDB::BaseExpr *valueExpr = 0;
01336 QRegExp re;
01337 if (str.length()>=2 &&
01338 (
01339 (str.startsWith("\"") && str.endsWith("\""))
01340 || (str.startsWith("'") && str.endsWith("'")))
01341 )
01342 {
01343 valueExpr = new KexiDB::ConstExpr(CHARACTER_STRING_LITERAL, str.mid(1,str.length()-2));
01344 }
01345 else if (str.startsWith("[") && str.endsWith("]")) {
01346 valueExpr = new KexiDB::QueryParameterExpr(str.mid(1,str.length()-2));
01347 }
01348 else if ((re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})")).exactMatch( str ))
01349 {
01350 valueExpr = new KexiDB::ConstExpr(DATE_CONST, QDate::fromString(
01351 re.cap(1).rightJustify(4, '0')+"-"+re.cap(2).rightJustify(2, '0')
01352 +"-"+re.cap(3).rightJustify(2, '0'), Qt::ISODate));
01353 }
01354 else if ((re = QRegExp("(\\d{1,2}):(\\d{1,2})")).exactMatch( str )
01355 || (re = QRegExp("(\\d{1,2}):(\\d{1,2}):(\\d{1,2})")).exactMatch( str ))
01356 {
01357 QString res = re.cap(1).rightJustify(2, '0')+":"+re.cap(2).rightJustify(2, '0')
01358 +":"+re.cap(3).rightJustify(2, '0');
01359
01360 valueExpr = new KexiDB::ConstExpr(TIME_CONST, QTime::fromString(res, Qt::ISODate));
01361 }
01362 else if ((re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2})")).exactMatch( str )
01363 || (re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2}):(\\d{1,2})")).exactMatch( str ))
01364 {
01365 QString res = re.cap(1).rightJustify(4, '0')+"-"+re.cap(2).rightJustify(2, '0')
01366 +"-"+re.cap(3).rightJustify(2, '0')
01367 +"T"+re.cap(4).rightJustify(2, '0')+":"+re.cap(5).rightJustify(2, '0')
01368 +":"+re.cap(6).rightJustify(2, '0');
01369
01370 valueExpr = new KexiDB::ConstExpr(DATETIME_CONST,
01371 QDateTime::fromString(res, Qt::ISODate));
01372 }
01373 else if (str[0]>='0' && str[0]<='9' || str[0]=='-' || str[0]=='+') {
01374
01375 QString decimalSym = KGlobal::locale()->decimalSymbol();
01376 bool ok;
01377 int pos = str.find('.');
01378 if (pos==-1) {
01379 pos = str.find(decimalSym);
01380 }
01381 if (pos>=0) {
01382 const int left = str.left(pos).toInt(&ok);
01383 if (!ok)
01384 return 0;
01385 const int right = str.mid(pos+1).toInt(&ok);
01386 if (!ok)
01387 return 0;
01388 valueExpr = new KexiDB::ConstExpr(REAL_CONST, QPoint(left,right));
01389 }
01390 else {
01391
01392 const Q_LLONG val = str.toLongLong(&ok);
01393 if (!ok)
01394 return 0;
01395 valueExpr = new KexiDB::ConstExpr(INTEGER_CONST, val);
01396 }
01397 }
01398 else if (str.lower()=="null") {
01399 valueExpr = new KexiDB::ConstExpr(SQL_NULL, QVariant());
01400 }
01401 else {
01402 if (!KexiUtils::isIdentifier(str))
01403 return 0;
01404 valueExpr = new KexiDB::VariableExpr(str);
01405
01406 for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
01408 if (it.current()->schema()->table() && it.current()->schema()->table()->field(str)) {
01409 valueExpr->toVariable()->field = it.current()->schema()->table()->field(str);
01410 break;
01411 }
01412 }
01413 }
01414 return valueExpr;
01415 }
01416
01417 void KexiQueryDesignerGuiEditor::slotBeforeCellChanged(KexiTableItem *item, int colnum,
01418 QVariant& newValue, KexiDB::ResultInfo* result)
01419 {
01420 if (colnum == COLUMN_ID_COLUMN) {
01421 if (newValue.isNull()) {
01422 d->data->updateRowEditBuffer(item, COLUMN_ID_TABLE, QVariant(), false);
01423 d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(false,1));
01424 d->data->updateRowEditBuffer(item, COLUMN_ID_SORTING, QVariant());
01425 #ifndef KEXI_NO_QUERY_TOTALS
01426 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant());
01427 #endif
01428 d->data->updateRowEditBuffer(item, COLUMN_ID_CRITERIA, QVariant());
01429 d->sets->removeCurrentPropertySet();
01430 }
01431 else {
01432
01433 QString fieldId( newValue.toString().stripWhiteSpace() );
01434 QString fieldName;
01435 QString tableName;
01436 QCString alias;
01437 QString columnValueForExpr;
01438 const bool isExpression = !d->fieldColumnIdentifiers[fieldId];
01439 if (isExpression) {
01440
01441
01442
01443
01444 const int id = fieldId.find(':');
01445 if (id>0) {
01446 alias = fieldId.left(id).stripWhiteSpace().latin1();
01447 if (!KexiUtils::isIdentifier(alias)) {
01448 result->success = false;
01449 result->allowToDiscardChanges = true;
01450 result->column = colnum;
01451 result->msg = i18n("Entered column alias \"%1\" is not a valid identifier.")
01452 .arg(alias);
01453 result->desc = i18n("Identifiers should start with a letter or '_' character");
01454 return;
01455 }
01456 }
01457 fieldName = fieldId.mid(id+1).stripWhiteSpace();
01458
01459 KexiDB::BaseExpr *e;
01460 int dummyToken;
01461 if ((e = parseExpressionString(fieldName, dummyToken, false)))
01462 {
01463 fieldName = e->toString();
01464
01465 delete e;
01466 }
01467 else {
01468 result->success = false;
01469 result->allowToDiscardChanges = true;
01470 result->column = colnum;
01471 result->msg = i18n("Invalid expression \"%1\"").arg(fieldName);
01472 return;
01473 }
01474 }
01475 else {
01476
01477 if (fieldId=="*") {
01478 tableName = "*";
01479 }
01480 else {
01481 if (!KexiDB::splitToTableAndFieldParts(
01482 fieldId, tableName, fieldName, KexiDB::SetFieldNameIfNoTableName))
01483 {
01484 kexipluginswarn << "KexiQueryDesignerGuiEditor::slotBeforeCellChanged(): no 'field' or 'table.field'" << endl;
01485 return;
01486 }
01487 }
01488 }
01489 bool saveOldValue = true;
01490 KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
01491 if (!set) {
01492 saveOldValue = false;
01493 const int row = d->data->findRef(item);
01494 if (row<0) {
01495 result->success = false;
01496 return;
01497 }
01498 set = createPropertySet( row, tableName, fieldName, true );
01499 propertySetSwitched();
01500 }
01501 d->data->updateRowEditBuffer(item, COLUMN_ID_TABLE, QVariant(tableName), false);
01502 d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(true,1));
01503 #ifndef KEXI_NO_QUERY_TOTALS
01504 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant(0));
01505 #endif
01506 if (!sortingAllowed(fieldName, tableName)) {
01507
01509 d->data->updateRowEditBuffer(item, COLUMN_ID_SORTING, QVariant());
01510 }
01511
01512 (*set)["field"].setValue(fieldName, saveOldValue);
01513 if (isExpression) {
01514
01515 if (alias.isEmpty())
01516 alias = (*set)["alias"].value().toCString();
01517 if (alias.isEmpty())
01518 alias = generateUniqueAlias();
01519 }
01520 (*set)["isExpression"].setValue(QVariant(isExpression,1), saveOldValue);
01521 if (!alias.isEmpty()) {
01522 (*set)["alias"].setValue(alias, saveOldValue);
01523
01524 newValue = QString(alias) + ": " + fieldName;
01525 }
01526 (*set)["caption"].setValue(QString::null, saveOldValue);
01527 (*set)["table"].setValue(tableName, saveOldValue);
01528 updatePropertiesVisibility(*set);
01529 }
01530 }
01531 else if (colnum==COLUMN_ID_TABLE) {
01532 if (newValue.isNull()) {
01533 if (!item->at(COLUMN_ID_COLUMN).toString().isEmpty())
01534 d->data->updateRowEditBuffer(item, COLUMN_ID_COLUMN, QVariant(), false);
01535 d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(false,1));
01536 #ifndef KEXI_NO_QUERY_TOTALS
01537 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant());
01538 #endif
01539 d->data->updateRowEditBuffer(item, COLUMN_ID_CRITERIA, QVariant());
01540 d->sets->removeCurrentPropertySet();
01541 }
01542
01543 KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
01544 if (set) {
01545 if ((*set)["isExpression"].value().toBool()==false) {
01546 (*set)["table"] = newValue;
01547 (*set)["caption"] = QString::null;
01548 }
01549 else {
01550
01551 newValue = QVariant();
01552 }
01553
01554 updatePropertiesVisibility(*set);
01555 }
01556 }
01557 else if (colnum==COLUMN_ID_VISIBLE) {
01558 bool saveOldValue = true;
01559 if (!propertySet()) {
01560 saveOldValue = false;
01561 createPropertySet( d->dataTable->dataAwareObject()->currentRow(),
01562 item->at(COLUMN_ID_TABLE).toString(), item->at(COLUMN_ID_COLUMN).toString(), true );
01563 #ifndef KEXI_NO_QUERY_TOTALS
01564 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant(0));
01565 #endif
01566 propertySetSwitched();
01567 }
01568 KoProperty::Set &set = *propertySet();
01569 set["visible"].setValue(newValue, saveOldValue);
01570 }
01571 #ifndef KEXI_NO_QUERY_TOTALS
01572 else if (colnum==COLUMN_ID_TOTALS) {
01573
01574
01575 setDirty(true);
01576 }
01577 #endif
01578 else if (colnum==COLUMN_ID_SORTING) {
01579 KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
01580 QString table( set->property("table").value().toString() );
01581 QString field( set->property("field").value().toString() );
01582 if (newValue.toInt()==0 || sortingAllowed(field, table)) {
01583 KoProperty::Property &property = set->property("sorting");
01584 QString key( property.listData()->keysAsStringList()[ newValue.toInt() ] );
01585 kexipluginsdbg << "new key=" << key << endl;
01586 property.setValue(key, true);
01587 }
01588 else {
01589 result->success = false;
01590 result->allowToDiscardChanges = true;
01591 result->column = colnum;
01592 result->msg = i18n("Could not set sorting for multiple columns (%1)")
01593 .arg(table=="*" ? table : (table+".*"));
01594 }
01595 }
01596 else if (colnum==COLUMN_ID_CRITERIA) {
01598 QString operatorStr, argStr;
01599 KexiDB::BaseExpr* e = 0;
01600 const QString str = newValue.toString().stripWhiteSpace();
01601 int token;
01602 QString field, table;
01603 KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
01604 if (set) {
01605 field = (*set)["field"].value().toString();
01606 table = (*set)["table"].value().toString();
01607 }
01608 if (!str.isEmpty() && (!set || table=="*" || field.find("*")!=-1)) {
01609
01610 result->success = false;
01611 result->allowToDiscardChanges = true;
01612 result->column = colnum;
01613 if (propertySet())
01614 result->msg = i18n("Could not set criteria for \"%1\"")
01615 .arg(table=="*" ? table : field);
01616 else
01617 result->msg = i18n("Could not set criteria for empty row");
01618
01619 }
01620 else if (str.isEmpty() || (e = parseExpressionString(str, token, true)))
01621 {
01622 if (e) {
01623 QString tokenStr;
01624 if (token!='=') {
01625 KexiDB::BinaryExpr be(KexiDBExpr_Relational, 0, token, 0);
01626 tokenStr = be.tokenToString() + " ";
01627 }
01628 (*set)["criteria"] = tokenStr + e->toString();
01629
01630 delete e;
01631 }
01632 else if (str.isEmpty()) {
01633 (*set)["criteria"] = QVariant();
01634 }
01635 setDirty(true);
01636 }
01637 else {
01638 result->success = false;
01639 result->allowToDiscardChanges = true;
01640 result->column = colnum;
01641 result->msg = i18n("Invalid criteria \"%1\"").arg(newValue.toString());
01642 }
01643 }
01644 }
01645
01646 void KexiQueryDesignerGuiEditor::slotTablePositionChanged(KexiRelationViewTableContainer*)
01647 {
01648 setDirty(true);
01649 }
01650
01651 void KexiQueryDesignerGuiEditor::slotAboutConnectionRemove(KexiRelationViewConnection*)
01652 {
01653 setDirty(true);
01654 }
01655
01656 void KexiQueryDesignerGuiEditor::slotTableFieldDoubleClicked(
01657 KexiDB::TableSchema* table, const QString& fieldName )
01658 {
01659 if (!table || (!table->field(fieldName) && fieldName!="*"))
01660 return;
01661 int row_num;
01662
01663 for (row_num=d->sets->size()-1; row_num>=0 && !d->sets->at(row_num); row_num--)
01664 ;
01665 row_num++;
01666
01667 KexiTableItem *newItem = createNewRow(table->name(), fieldName, true );
01668 d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
01669 d->dataTable->dataAwareObject()->setCursorPosition(row_num, 0);
01670
01671 createPropertySet( row_num, table->name(), fieldName, true );
01672 propertySetSwitched();
01673 d->dataTable->setFocus();
01674 }
01675
01676 KoProperty::Set *KexiQueryDesignerGuiEditor::propertySet()
01677 {
01678 return d->sets->currentPropertySet();
01679 }
01680
01681 void KexiQueryDesignerGuiEditor::updatePropertiesVisibility(KoProperty::Set& set)
01682 {
01683 const bool asterisk = isAsterisk(
01684 set["table"].value().toString(), set["field"].value().toString()
01685 );
01686 #ifndef KEXI_NO_UNFINISHED
01687 set["caption"].setVisible( !asterisk );
01688 #endif
01689 set["alias"].setVisible( !asterisk );
01690
01691
01692
01693 propertySetReloaded(true);
01694 }
01695
01696 KoProperty::Set*
01697 KexiQueryDesignerGuiEditor::createPropertySet( int row,
01698 const QString& tableName, const QString& fieldName, bool newOne )
01699 {
01700
01701 QString typeName = "KexiQueryDesignerGuiEditor::Column";
01702 KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
01703 KoProperty::Property *prop;
01704
01705
01706 set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Query column")) );
01707 prop->setVisible(false);
01709
01710
01711 set->addProperty(prop = new KoProperty::Property("table", QVariant(tableName)) );
01712 prop->setVisible(false);
01713
01714 set->addProperty(prop = new KoProperty::Property("field", QVariant(fieldName)) );
01715 prop->setVisible(false);
01716
01717 set->addProperty(prop = new KoProperty::Property("caption", QVariant(QString::null), i18n("Caption") ) );
01718 #ifdef KEXI_NO_UNFINISHED
01719 prop->setVisible(false);
01720 #endif
01721
01722 set->addProperty(prop = new KoProperty::Property("alias", QVariant(QString::null), i18n("Alias")) );
01723
01724 set->addProperty(prop = new KoProperty::Property("visible", QVariant(true, 4)) );
01725 prop->setVisible(false);
01726
01727
01728
01729
01730
01731
01732 QStringList slist, nlist;
01733 slist << "nosorting" << "ascending" << "descending";
01734 nlist << i18n("None") << i18n("Ascending") << i18n("Descending");
01735 set->addProperty(prop = new KoProperty::Property("sorting",
01736 slist, nlist, *slist.at(0), i18n("Sorting")));
01737 prop->setVisible(false);
01738
01739 set->addProperty(prop = new KoProperty::Property("criteria", QVariant(QString::null)) );
01740 prop->setVisible(false);
01741
01742 set->addProperty(prop = new KoProperty::Property("isExpression", QVariant(false, 1)) );
01743 prop->setVisible(false);
01744
01745 connect(set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
01746 this, SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
01747
01748 d->sets->insert(row, set, newOne);
01749
01750 updatePropertiesVisibility(*set);
01751 return set;
01752 }
01753
01754 void KexiQueryDesignerGuiEditor::setFocus()
01755 {
01756 d->dataTable->setFocus();
01757 }
01758
01759 void KexiQueryDesignerGuiEditor::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
01760 {
01761 const QCString& pname = property.name();
01762
01763
01764
01765 if (pname=="alias" || pname=="name") {
01766 const QVariant& v = property.value();
01767 if (!v.toString().stripWhiteSpace().isEmpty() && !KexiUtils::isIdentifier( v.toString() )) {
01768 KMessageBox::sorry(this,
01769 KexiUtils::identifierExpectedMessage(property.caption(), v.toString()));
01770 property.resetValue();
01771 }
01772 if (pname=="alias") {
01773 if (set["isExpression"].value().toBool()==true) {
01774
01775 d->dataTable->dataAwareObject()->acceptEditor();
01776
01777
01778 d->data->updateRowEditBuffer(d->dataTable->dataAwareObject()->selectedItem(),
01779 0, QVariant(set["alias"].value().toString() + ": " + set["field"].value().toString()));
01780 d->data->saveRowChanges(*d->dataTable->dataAwareObject()->selectedItem(), true);
01781
01782 }
01783 }
01784 }
01785 }
01786
01787 void KexiQueryDesignerGuiEditor::slotNewItemStored(KexiPart::Item& item)
01788 {
01789 d->relations->objectCreated(item.mimeType(), item.name().latin1());
01790 }
01791
01792 void KexiQueryDesignerGuiEditor::slotItemRemoved(const KexiPart::Item& item)
01793 {
01794 d->relations->objectDeleted(item.mimeType(), item.name().latin1());
01795 }
01796
01797 void KexiQueryDesignerGuiEditor::slotItemRenamed(const KexiPart::Item& item, const QCString& oldName)
01798 {
01799 d->relations->objectRenamed(item.mimeType(), oldName, item.name().latin1());
01800 }
01801
01802 #include "kexiquerydesignerguieditor.moc"
01803