kexi

kexiquerydesignerguieditor.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
3 Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "kexiquerydesignerguieditor.h"
22
23#include <tqlayout.h>
24#include <tqpainter.h>
25#include <tqdom.h>
26#include <tqregexp.h>
27
28#include <kdebug.h>
29#include <tdelocale.h>
30#include <tdemessagebox.h>
31
32#include <kexidb/field.h>
33#include <kexidb/queryschema.h>
34#include <kexidb/connection.h>
35#include <kexidb/parser/parser.h>
36#include <kexidb/parser/sqlparser.h>
37#include <kexidb/utils.h>
38#include <kexidb/roweditbuffer.h>
39#include <kexiutils/identifier.h>
40#include <kexiproject.h>
41#include <keximainwindow.h>
42#include <kexiinternalpart.h>
43#include <kexitableview.h>
44#include <kexitableitem.h>
45#include <kexitableviewdata.h>
46#include <kexidragobjects.h>
47#include <kexidialogbase.h>
48#include <kexidatatable.h>
49#include <kexi.h>
50#include <kexisectionheader.h>
51#include <widget/tableview/kexidataawarepropertyset.h>
52#include <widget/relations/kexirelationwidget.h>
53#include <widget/relations/kexirelationviewtable.h>
54#include <koproperty/property.h>
55#include <koproperty/set.h>
56
57#include "kexiquerypart.h"
58
60#define KEXI_NO_QUERY_TOTALS
61
63#define COLUMN_ID_COLUMN 0
64#define COLUMN_ID_TABLE 1
65#define COLUMN_ID_VISIBLE 2
66#ifdef KEXI_NO_QUERY_TOTALS
67# define COLUMN_ID_SORTING 3
68# define COLUMN_ID_CRITERIA 4
69#else
70# define COLUMN_ID_TOTALS 3
71# define COLUMN_ID_SORTING 4
72# define COLUMN_ID_CRITERIA 5
73#endif
74
76class KexiQueryDesignerGuiEditor::Private
77{
78public:
79 Private()
80 : fieldColumnIdentifiers(1009, false/*case insens.*/)
81 {
82 droppedNewItem = 0;
83 slotTableAdded_enabled = true;
84 }
85
86 bool changeSingleCellValue(KexiTableItem &item, int columnNumber,
87 const TQVariant& value, KexiDB::ResultInfo* result)
88 {
89 data->clearRowEditBuffer();
90 if (!data->updateRowEditBuffer(&item, columnNumber, value)
91 || !data->saveRowChanges(item, true))
92 {
93 if (result)
94 *result = *data->result();
95 return false;
96 }
97 return true;
98 }
99
100 KexiTableViewData *data;
101 KexiDataTable *dataTable;
102 TQGuardedPtr<KexiDB::Connection> conn;
103
104 KexiRelationWidget *relations;
105 KexiSectionHeader *head;
106 TQSplitter *spl;
107
111 KexiTableViewData *fieldColumnData, *tablesColumnData;
112
119 TQDict<char> fieldColumnIdentifiers;
120
121 KexiDataAwarePropertySet* sets;
122 KexiTableItem *droppedNewItem;
123
124 TQString droppedNewTable, droppedNewField;
125
126 bool slotTableAdded_enabled : 1;
127};
128
129static bool isAsterisk(const TQString& tableName, const TQString& fieldName)
130{
131 return tableName=="*" || fieldName.endsWith("*");
132}
133
135static bool sortingAllowed(const TQString& fieldName, const TQString& tableName) {
136 return ! (fieldName=="*" || (fieldName.isEmpty() && tableName=="*"));
137}
138
139//=========================================================
140
141KexiQueryDesignerGuiEditor::KexiQueryDesignerGuiEditor(
142 KexiMainWindow *mainWin, TQWidget *parent, const char *name)
143 : KexiViewBase(mainWin, parent, name)
144 , d( new Private() )
145{
146 d->conn = mainWin->project()->dbConnection();
147
148 d->spl = new TQSplitter(TQt::Vertical, this);
149 d->spl->setChildrenCollapsible(false);
150 d->relations = new KexiRelationWidget(mainWin, d->spl, "relations");
151 connect(d->relations, TQ_SIGNAL(tableAdded(KexiDB::TableSchema&)),
152 this, TQ_SLOT(slotTableAdded(KexiDB::TableSchema&)));
153 connect(d->relations, TQ_SIGNAL(tableHidden(KexiDB::TableSchema&)),
154 this, TQ_SLOT(slotTableHidden(KexiDB::TableSchema&)));
155 connect(d->relations, TQ_SIGNAL(tableFieldDoubleClicked(KexiDB::TableSchema*,const TQString&)),
156 this, TQ_SLOT(slotTableFieldDoubleClicked(KexiDB::TableSchema*,const TQString&)));
157
158 d->head = new KexiSectionHeader(i18n("Query Columns"),TQt::Vertical, d->spl);
159 d->dataTable = new KexiDataTable(mainWin, d->head, "guieditor_dataTable", false);
160 d->dataTable->dataAwareObject()->setSpreadSheetMode();
161
162 d->data = new KexiTableViewData(); //just empty data
163 d->sets = new KexiDataAwarePropertySet( this, d->dataTable->dataAwareObject() );
164 initTableColumns();
165 initTableRows();
166
167 TQValueList<int> c;
168 c << COLUMN_ID_COLUMN << COLUMN_ID_TABLE << COLUMN_ID_CRITERIA;
169 if (d->dataTable->tableView()/*sanity*/) {
170 d->dataTable->tableView()->adjustColumnWidthToContents(COLUMN_ID_VISIBLE);
171 d->dataTable->tableView()->adjustColumnWidthToContents(COLUMN_ID_SORTING);
172 d->dataTable->tableView()->maximizeColumnsWidth( c );
173 d->dataTable->tableView()->setDropsAtRowEnabled(true);
174 connect(d->dataTable->tableView(), TQ_SIGNAL(dragOverRow(KexiTableItem*,int,TQDragMoveEvent*)),
175 this, TQ_SLOT(slotDragOverTableRow(KexiTableItem*,int,TQDragMoveEvent*)));
176 connect(d->dataTable->tableView(), TQ_SIGNAL(droppedAtRow(KexiTableItem*,int,TQDropEvent*,KexiTableItem*&)),
177 this, TQ_SLOT(slotDroppedAtRow(KexiTableItem*,int,TQDropEvent*,KexiTableItem*&)));
178 connect(d->dataTable->tableView(), TQ_SIGNAL(newItemAppendedForAfterDeletingInSpreadSheetMode()),
179 this, TQ_SLOT(slotNewItemAppendedForAfterDeletingInSpreadSheetMode()));
180 }
181 connect(d->data, TQ_SIGNAL(aboutToChangeCell(KexiTableItem*,int,TQVariant&,KexiDB::ResultInfo*)),
182 this, TQ_SLOT(slotBeforeCellChanged(KexiTableItem*,int,TQVariant&,KexiDB::ResultInfo*)));
183 connect(d->data, TQ_SIGNAL(rowInserted(KexiTableItem*,uint,bool)),
184 this, TQ_SLOT(slotRowInserted(KexiTableItem*,uint,bool)));
185 connect(d->relations, TQ_SIGNAL(tablePositionChanged(KexiRelationViewTableContainer*)),
186 this, TQ_SLOT(slotTablePositionChanged(KexiRelationViewTableContainer*)));
187 connect(d->relations, TQ_SIGNAL(aboutConnectionRemove(KexiRelationViewConnection*)),
188 this, TQ_SLOT(slotAboutConnectionRemove(KexiRelationViewConnection*)));
189
190 TQVBoxLayout *l = new TQVBoxLayout(this);
191 l->addWidget(d->spl);
192
193 addChildView(d->relations);
194 addChildView(d->dataTable);
195 setViewWidget(d->dataTable, true);
196 d->relations->setSizePolicy(TQSizePolicy::Expanding,TQSizePolicy::Expanding);
197 d->head->setSizePolicy(TQSizePolicy::Expanding,TQSizePolicy::Expanding);
198 updateGeometry();
199 d->spl->setSizes(TQValueList<int>()<< 800<<400);
200}
201
202KexiQueryDesignerGuiEditor::~KexiQueryDesignerGuiEditor()
203{
204}
205
206void
208{
209 KexiTableViewColumn *col1 = new KexiTableViewColumn("column", KexiDB::Field::Enum, i18n("Column"),
210 i18n("Describes field name or expression for the designed query."));
211 col1->setRelatedDataEditable(true);
212
214 col1->setRelatedData( d->fieldColumnData );
215 d->data->addColumn(col1);
216
217 KexiTableViewColumn *col2 = new KexiTableViewColumn("table", KexiDB::Field::Enum, i18n("Table"),
218 i18n("Describes table for a given field. Can be empty."));
220 col2->setRelatedData( d->tablesColumnData );
221 d->data->addColumn(col2);
222
223 KexiTableViewColumn *col3 = new KexiTableViewColumn("visible", KexiDB::Field::Boolean, i18n("Visible"),
224 i18n("Describes visibility for a given field or expression."));
225 col3->field()->setDefaultValue( TQVariant(false) );
226 col3->field()->setNotNull( true );
227 d->data->addColumn(col3);
228
229#ifndef KEXI_NO_QUERY_TOTALS
230 KexiTableViewColumn *col4 = new KexiTableViewColumn("totals", KexiDB::Field::Enum, i18n("Totals"),
231 i18n("Describes a way of computing totals for a given field or expression."));
232 TQValueVector<TQString> totalsTypes;
233 totalsTypes.append( i18n("Group by") );
234 totalsTypes.append( i18n("Sum") );
235 totalsTypes.append( i18n("Average") );
236 totalsTypes.append( i18n("Min") );
237 totalsTypes.append( i18n("Max") );
238 //todo: more like this
239 col4->field()->setEnumHints(totalsTypes);
240 d->data->addColumn(col4);
241#endif
242
243 KexiTableViewColumn *col5 = new KexiTableViewColumn("sort", KexiDB::Field::Enum, i18n("Sorting"),
244 i18n("Describes a way of sorting for a given field."));
245 TQValueVector<TQString> sortTypes;
246 sortTypes.append( "" );
247 sortTypes.append( i18n("Ascending") );
248 sortTypes.append( i18n("Descending") );
249 col5->field()->setEnumHints(sortTypes);
250 d->data->addColumn(col5);
251
252 KexiTableViewColumn *col6 = new KexiTableViewColumn("criteria", KexiDB::Field::Text, i18n("Criteria"),
253 i18n("Describes the criteria for a given field or expression."));
254 d->data->addColumn(col6);
255
256// KexiTableViewColumn *col7 = new KexiTableViewColumn(i18n("Or"), KexiDB::Field::Text);
257// d->data->addColumn(col7);
258}
259
261{
262 d->data->deleteAllRows();
263 //const int columns = d->data->columnsCount();
264 for (int i=0; i<(int)d->sets->size(); i++) {
265 KexiTableItem* item;
266 d->data->append(item = d->data->createItem());
267 item->at(COLUMN_ID_VISIBLE) = TQVariant(false);
268 }
269 d->dataTable->dataAwareObject()->setData(d->data);
270
272}
273
275{
276 d->dataTable->dataAwareObject()->acceptRowEdit();
277
278 TQStringList sortedTableNames;
279 for (TablesDictIterator it(*d->relations->tables());it.current();++it)
280 sortedTableNames += it.current()->schema()->name();
281 qHeapSort( sortedTableNames );
282
283 //several tables can be hidden now, so remove rows for these tables
284 TQValueList<int> rowsToDelete;
285 for (int r = 0; r<(int)d->sets->size(); r++) {
286 KoProperty::Set *set = d->sets->at(r);
287 if (set) {
288 TQString tableName = (*set)["table"].value().toString();
289 TQString fieldName = (*set)["field"].value().toString();
290 const bool allTablesAsterisk = tableName=="*" && d->relations->tables()->isEmpty();
291 const bool fieldNotFound = tableName!="*"
292 && !(*set)["isExpression"].value().toBool()
293 && sortedTableNames.end() == tqFind( sortedTableNames.begin(), sortedTableNames.end(), tableName );
294
295 if (allTablesAsterisk || fieldNotFound) {
296 //table not found: mark this line for later removal
297 rowsToDelete += r;
298 }
299 }
300 }
301 d->data->deleteRows( rowsToDelete );
302
303 //update 'table' and 'field' columns
304 d->tablesColumnData->deleteAllRows();
305 d->fieldColumnData->deleteAllRows();
306 d->fieldColumnIdentifiers.clear();
307
308 KexiTableItem *item = d->fieldColumnData->createItem(); //new KexiTableItem(2);
309 (*item)[COLUMN_ID_COLUMN]="*";
310 (*item)[COLUMN_ID_TABLE]="*";
311 d->fieldColumnData->append( item );
312 d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1); //cache
313
314// tempData()->clearQuery();
315 tempData()->unregisterForTablesSchemaChanges();
316 for (TQStringList::const_iterator it = sortedTableNames.constBegin();
317 it!=sortedTableNames.constEnd(); ++it)
318 {
319 //table
321 KexiDB::TableSchema *table = d->relations->tables()->find(*it)->schema()->table();
322 d->conn->registerForTableSchemaChanges(*tempData(), *table); //this table will be used
323 item = d->tablesColumnData->createItem(); //new KexiTableItem(2);
324 (*item)[COLUMN_ID_COLUMN]=table->name();
325 (*item)[COLUMN_ID_TABLE]=(*item)[COLUMN_ID_COLUMN];
326 d->tablesColumnData->append( item );
327 //fields
328 item = d->fieldColumnData->createItem(); //new KexiTableItem(2);
329 (*item)[COLUMN_ID_COLUMN]=table->name()+".*";
330 (*item)[COLUMN_ID_TABLE]=(*item)[COLUMN_ID_COLUMN];
331 d->fieldColumnData->append( item );
332 d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1); //cache
333 for (KexiDB::Field::ListIterator t_it = table->fieldsIterator();t_it.current();++t_it) {
334 item = d->fieldColumnData->createItem(); // new KexiTableItem(2);
335 (*item)[COLUMN_ID_COLUMN]=table->name()+"."+t_it.current()->name();
336 (*item)[COLUMN_ID_TABLE]=TQString(" ") + t_it.current()->name();
337 d->fieldColumnData->append( item );
338 d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1); //cache
339 }
340 }
341//TODO
342}
343
344KexiRelationWidget *KexiQueryDesignerGuiEditor::relationView() const
345{
346 return d->relations;
347}
348
350KexiQueryDesignerGuiEditor::tempData() const
351{
352 return static_cast<KexiQueryPart::TempData*>(parentDialog()->tempData());
353}
354
355static TQString msgCannotSwitch_EmptyDesign() {
356 return i18n("Cannot switch to data view, because query design is empty.\n"
357 "First, please create your design.");
358}
359
360bool
362{
363 //build query schema
364 KexiQueryPart::TempData * temp = tempData();
365 if (temp->query()) {
366 temp->clearQuery();
367 } else {
368 temp->setQuery( new KexiDB::QuerySchema() );
369 }
370
371 //add tables
372 for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
374 temp->query()->addTable( it.current()->schema()->table() );
375 }
376
377 //add fields, also build:
378 // -WHERE expression
379 // -ORDER BY list
380 KexiDB::BaseExpr *whereExpr = 0;
381 const uint count = TQMIN(d->data->count(), d->sets->size());
382 bool fieldsFound = false;
383 KexiTableViewData::Iterator it(d->data->iterator());
384 for (uint i=0; i<count && it.current(); ++it, i++) {
385 if (!it.current()->at(COLUMN_ID_TABLE).isNull() && it.current()->at(COLUMN_ID_COLUMN).isNull()) {
386 //show message about missing field name, and set focus to that cell
387 kexipluginsdbg << "no field provided!" << endl;
388 d->dataTable->dataAwareObject()->setCursorPosition(i,0);
389 if (errMsg)
390 *errMsg = i18n("Select column for table \"%1\"")
391 .arg(it.current()->at(COLUMN_ID_TABLE).toString());
392 return false;
393 }
394
395 KoProperty::Set *set = d->sets->at(i);
396 if (set) {
397 TQString tableName = (*set)["table"].value().toString().stripWhiteSpace();
398 TQString fieldName = (*set)["field"].value().toString();
399 TQString fieldAndTableName = fieldName;
400 KexiDB::Field *currentField = 0; // will be set if this column is a single field
401 KexiDB::QueryColumnInfo* currentColumn = 0;
402 if (!tableName.isEmpty())
403 fieldAndTableName.prepend(tableName+".");
404 const bool fieldVisible = (*set)["visible"].value().toBool();
405 TQString criteriaStr = (*set)["criteria"].value().toString();
406 TQCString alias = (*set)["alias"].value().toCString();
407 if (!criteriaStr.isEmpty()) {
408 int token;
409 KexiDB::BaseExpr *criteriaExpr = parseExpressionString(criteriaStr, token,
410 true/*allowRelationalOperator*/);
411 if (!criteriaExpr) {//for sanity
412 if (errMsg)
413 *errMsg = i18n("Invalid criteria \"%1\"").arg(criteriaStr);
414 delete whereExpr;
415 return false;
416 }
417 //build relational expression for column variable
418 KexiDB::VariableExpr *varExpr = new KexiDB::VariableExpr(fieldAndTableName);
419 criteriaExpr = new KexiDB::BinaryExpr(KexiDBExpr_Relational, varExpr, token, criteriaExpr);
420 //critera ok: add it to WHERE section
421 if (whereExpr)
422 whereExpr = new KexiDB::BinaryExpr(KexiDBExpr_Logical, whereExpr, AND, criteriaExpr);
423 else //first expr.
424 whereExpr = criteriaExpr;
425 }
426 if (tableName.isEmpty()) {
427 if ((*set)["isExpression"].value().toBool()==true) {
428 //add expression column
429 int dummyToken;
430 KexiDB::BaseExpr *columnExpr = parseExpressionString(fieldName, dummyToken,
431 false);
432 if (!columnExpr) {
433 if (errMsg)
434 *errMsg = i18n("Invalid expression \"%1\"").arg(fieldName);
435 return false;
436 }
437 temp->query()->addExpression(columnExpr, fieldVisible);
438 if (fieldVisible)
439 fieldsFound = true;
440 if (!alias.isEmpty())
441 temp->query()->setColumnAlias( temp->query()->fieldCount()-1, alias );
442 }
443 //TODO
444 }
445 else if (tableName=="*") {
446 //all tables asterisk
447 temp->query()->addAsterisk( new KexiDB::QueryAsterisk( temp->query(), 0 ), fieldVisible );
448 if (fieldVisible)
449 fieldsFound = true;
450 continue;
451 }
452 else {
453 KexiDB::TableSchema *t = d->conn->tableSchema(tableName);
454 if (fieldName=="*") {
455 //single-table asterisk: <tablename> + ".*" + number
456 temp->query()->addAsterisk( new KexiDB::QueryAsterisk( temp->query(), t ), fieldVisible );
457 if (fieldVisible)
458 fieldsFound = true;
459 } else {
460 if (!t) {
461 kexipluginswarn << "query designer: NO TABLE '"
462 << (*set)["table"].value().toString() << "'" << endl;
463 continue;
464 }
465 currentField = t->field( fieldName );
466 if (!currentField) {
467 kexipluginswarn << "query designer: NO FIELD '" << fieldName << "'" << endl;
468 continue;
469 }
470 if (!fieldVisible && criteriaStr.isEmpty() && (*set)["isExpression"]
471 && (*set)["sorting"].value().toString()!="nosorting")
472 {
473 kexipluginsdbg << "invisible field with sorting: do not add it to the fields list" << endl;
474 continue;
475 }
476 temp->query()->addField(currentField, fieldVisible);
477 currentColumn = temp->query()->expandedOrInternalField(
478 temp->query()->fieldsExpanded().count() - 1 );
479 if (fieldVisible)
480 fieldsFound = true;
481 if (!alias.isEmpty())
482 temp->query()->setColumnAlias( temp->query()->fieldCount()-1, alias );
483 }
484 }
485 }
486 else {
487 kexipluginsdbg << it.current()->at(COLUMN_ID_TABLE).toString() << endl;
488 }
489 }
490 if (!fieldsFound) {
491 if (errMsg)
492 *errMsg = msgCannotSwitch_EmptyDesign();
493 return false;
494 }
495 if (whereExpr)
496 kexipluginsdbg << "KexiQueryDesignerGuiEditor::buildSchema(): setting CRITERIA: "
497 << whereExpr->debugString() << endl;
498
499 //set always, because if whereExpr==NULL,
500 //this will clear prev. expr
501 temp->query()->setWhereExpression( whereExpr );
502
503 //add relations (looking for connections)
504 for (ConnectionListIterator it(*d->relations->connections()); it.current(); ++it) {
505 KexiRelationViewTableContainer *masterTable = it.current()->masterTable();
506 KexiRelationViewTableContainer *detailsTable = it.current()->detailsTable();
507
509 temp->query()->addRelationship(
510 masterTable->schema()->table()->field(it.current()->masterField()),
511 detailsTable->schema()->table()->field(it.current()->detailsField()) );
512 }
513
514 // Add sorting information (ORDER BY) - we can do that only now
515 // after all QueryColumnInfo items are instantiated
516 KexiDB::OrderByColumnList orderByColumns;
517 it = d->data->iterator();
518 int fieldNumber = -1; //field number (empty rows are omitted)
519 for (uint i=0/*row number*/; i<count && it.current(); ++it, i++) {
520 KoProperty::Set *set = d->sets->at(i);
521 if (!set)
522 continue;
523 fieldNumber++;
524 KexiDB::Field *currentField = 0;
525 KexiDB::QueryColumnInfo *currentColumn = 0;
526 TQString sortingString( (*set)["sorting"].value().toString() );
527 if (sortingString!="ascending" && sortingString!="descending")
528 continue;
529 if (!(*set)["visible"].value().toBool()) {
530 // this row defines invisible field but contains sorting information,
531 // what means KexiDB::Field should be used as a reference for this sorting
532 // Note1: alias is not supported here.
533
534 // Try to find a field (not mentioned after SELECT):
535 currentField = temp->query()->findTableField( (*set)["field"].value().toString() );
536 if (!currentField) {
537 kexipluginswarn << "KexiQueryDesignerGuiEditor::buildSchema(): NO FIELD '"
538 << (*set)["field"].value().toString()
539 << " available for sorting" << endl;
540 continue;
541 }
542 orderByColumns.appendField(*currentField, sortingString=="ascending");
543 continue;
544 }
545 currentField = temp->query()->field( (uint)fieldNumber );
546 if (!currentField || currentField->isExpression() || currentField->isQueryAsterisk())
548 continue;
550 TQString aliasString( (*set)["alias"].value().toString() );
551 currentColumn = temp->query()->columnInfo(
552 (*set)["table"].value().toString() + "."
553 + (aliasString.isEmpty() ? currentField->name() : aliasString) );
554 if (currentField && currentColumn) {
555 if (currentColumn->visible)
556 orderByColumns.appendColumn(*currentColumn, sortingString=="ascending");
557 else if (currentColumn->field)
558 orderByColumns.appendField(*currentColumn->field, sortingString=="ascending");
559 }
560 }
561 temp->query()->setOrderByColumnList( orderByColumns );
562
563 temp->query()->debug();
564 temp->registerTableSchemaChanges(temp->query());
565 //TODO?
566 return true;
567}
568
571{
572 kexipluginsdbg << "KexiQueryDesignerGuiEditor::beforeSwitch()" << mode << endl;
573
574 if (!d->dataTable->dataAwareObject()->acceptRowEdit())
575 return cancelled;
576
577 if (mode==Kexi::DesignViewMode) {
578 return true;
579 }
580 else if (mode==Kexi::DataViewMode) {
581// if (!d->dataTable->dataAwareObject()->acceptRowEdit())
582 // return cancelled;
583
584 if (!dirty() && parentDialog()->neverSaved()) {
585 KMessageBox::information(this, msgCannotSwitch_EmptyDesign());
586 return cancelled;
587 }
588 if (dirty() || !tempData()->query()) {
589 //remember current design in a temporary structure
590 dontStore=true;
591 TQString errMsg;
592 //build schema; problems are not allowed
593 if (!buildSchema(&errMsg)) {
594 KMessageBox::sorry(this, errMsg);
595 return cancelled;
596 }
597 }
598 //TODO
599 return true;
600 }
601 else if (mode==Kexi::TextViewMode) {
602 dontStore=true;
603 //build schema; ignore problems
604 buildSchema();
605/* if (tempData()->query && tempData()->query->fieldCount()==0) {
606 //no fields selected: let's add "*" (all-tables asterisk),
607 // otherwise SQL statement will be invalid
608 tempData()->query->addAsterisk( new KexiDB::QueryAsterisk( tempData()->query ) );
609 }*/
610 //todo
611 return true;
612 }
613
614 return false;
615}
616
619{
620 const bool was_dirty = dirty();
621 KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
622 if (mode==Kexi::NoViewMode || (mode==Kexi::DataViewMode && !tempData()->query())) {
623 //this is not a SWITCH but a fresh opening in this view mode
624 if (!m_dialog->neverSaved()) {
625 if (!loadLayout()) {
626 //err msg
627 parentDialog()->setStatus(conn,
628 i18n("Query definition loading failed."),
629 i18n("Query design may be corrupted so it could not be opened even in text view.\n"
630 "You can delete the query and create it again."));
631 return false;
632 }
633 // Invalid queries case:
634 // KexiDialogBase::switchToViewMode() first opens DesignViewMode,
635 // and then KexiQueryPart::loadSchemaData() doesn't allocate QuerySchema object
636 // do we're carefully looking at parentDialog()->schemaData()
637 KexiDB::QuerySchema * q = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
638 if (q) {
639 KexiDB::ResultInfo result;
640 showFieldsForQuery( q, result );
641 if (!result.success) {
642 parentDialog()->setStatus(&result, i18n("Query definition loading failed."));
643 tempData()->proposeOpeningInTextViewModeBecauseOfProblems = true;
644 return false;
645 }
646 }
648 }
649 }
650 else if (mode==Kexi::TextViewMode || mode==Kexi::DataViewMode) {
651 // Switch from text or data view. In the second case, the design could be changed as well
652 // because there could be changes made in the text view before switching to the data view.
653 if (tempData()->queryChangedInPreviousView) {
654 //previous view changed query data
655 //-clear and regenerate GUI items
657 //todo
658 if (tempData()->query()) {
659 //there is a query schema to show
660 showTablesForQuery( tempData()->query() );
661 //-show fields
662 KexiDB::ResultInfo result;
663 showFieldsAndRelationsForQuery( tempData()->query(), result );
664 if (!result.success) {
665 parentDialog()->setStatus(&result, i18n("Query definition loading failed."));
666 return false;
667 }
668 }
669 else {
670 d->relations->clear();
671 }
672 }
674 }
675
676 if (mode==Kexi::DataViewMode) {
677 //this is just a SWITCH from data view
678 //set cursor if needed:
679 if (d->dataTable->dataAwareObject()->currentRow()<0
680 || d->dataTable->dataAwareObject()->currentColumn()<0)
681 {
682 d->dataTable->dataAwareObject()->ensureCellVisible(0,0);
683 d->dataTable->dataAwareObject()->setCursorPosition(0,0);
684 }
685 }
686 tempData()->queryChangedInPreviousView = false;
687 setFocus(); //to allow shared actions proper update
688 if (!was_dirty)
689 setDirty(false);
690 return true;
691}
692
693
696{
697 if (!d->dataTable->dataAwareObject()->acceptRowEdit()) {
698 cancel = true;
699 return 0;
700 }
701 TQString errMsg;
702 KexiQueryPart::TempData * temp = tempData();
703 if (!temp->query() || !(viewMode()==Kexi::DesignViewMode && !temp->queryChangedInPreviousView)) {
704 //only rebuild schema if it has not been rebuilt previously
705 if (!buildSchema(&errMsg)) {
706 KMessageBox::sorry(this, errMsg);
707 cancel = true;
708 return 0;
709 }
710 }
711 (KexiDB::SchemaData&)*temp->query() = sdata; //copy main attributes
712
713 bool ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *temp->query(), true /*newObject*/ );
714 m_dialog->setId( temp->query()->id() );
715
716 if (ok)
717 ok = storeLayout();
718
719// temp->query = 0; //will be returned, so: don't keep it
720 if (!ok) {
721 temp->setQuery( 0 );
722// delete query;
723 return 0;
724 }
725 return temp->takeQuery(); //will be returned, so: don't keep it in temp
726}
727
729{
730 if (!d->dataTable->dataAwareObject()->acceptRowEdit())
731 return cancelled;
732
733 const bool was_dirty = dirty();
734 tristate res = KexiViewBase::storeData(dontAsk); //this clears dirty flag
735 if (true == res)
736 res = buildSchema();
737 if (true == res)
738 res = storeLayout();
739 if (true != res) {
740 if (was_dirty)
741 setDirty(true);
742 }
743 return res;
744}
745
746void KexiQueryDesignerGuiEditor::showTablesForQuery(KexiDB::QuerySchema *query)
747{
748//replaced by code below that preserves geometries d->relations->clear();
749
750 // instead of hiding all tables and showing some tables,
751 // show only these new and hide these unncecessary; the same for connections)
752 d->slotTableAdded_enabled = false; //speedup
753 d->relations->removeAllConnections(); //connections will be recreated
754 d->relations->hideAllTablesExcept( query->tables() );
755 for (KexiDB::TableSchema::ListIterator it(*query->tables()); it.current(); ++it) {
756 d->relations->addTable( it.current() );
757 }
758
759 d->slotTableAdded_enabled = true;
761}
762
763void KexiQueryDesignerGuiEditor::addConnection(
764 KexiDB::Field *masterField, KexiDB::Field *detailsField)
765{
766 SourceConnection conn;
767 conn.masterTable = masterField->table()->name(); //<<<TODO
768 conn.masterField = masterField->name();
769 conn.detailsTable = detailsField->table()->name();
770 conn.detailsField = detailsField->name();
771 d->relations->addConnection( conn );
772}
773
778
783
789
791 KexiDB::QuerySchema *query, bool showFields, bool showRelations, KexiDB::ResultInfo& result)
792{
793 result.clear();
794 const bool was_dirty = dirty();
795
796 //1. Show explicitly declared relations:
797 if (showRelations) {
798 KexiDB::Relationship *rel;
799 for (KexiDB::Relationship::ListIterator it(*query->relationships());
800 (rel=it.current()); ++it)
801 {
803 KexiDB::Field *masterField = rel->masterIndex()->fields()->first();
804 KexiDB::Field *detailsField = rel->detailsIndex()->fields()->first();
805 addConnection(masterField, detailsField);
806 }
807 }
808
809 //2. Collect information about criterias
810 // --this must be top level chain of AND's
811 // --this will also show joins as: [table1.]field1 = [table2.]field2
812 TQDict<KexiDB::BaseExpr> criterias(1009, false);
813 KexiDB::BaseExpr* e = query->whereExpression();
814 KexiDB::BaseExpr* eItem = 0;
815 while (e) {
816 //eat parentheses because the expression can be (....) AND (... AND ... )
817 while (e && e->toUnary() && e->token()=='(')
818 e = e->toUnary()->arg();
819
820 if (e->toBinary() && e->token()==AND) {
821 eItem = e->toBinary()->left();
822 e = e->toBinary()->right();
823 }
824 else {
825 eItem = e;
826 e = 0;
827 }
828
829 //eat parentheses
830 while (eItem && eItem->toUnary() && eItem->token()=='(')
831 eItem = eItem->toUnary()->arg();
832
833 if (!eItem)
834 continue;
835
836 kexidbg << eItem->toString() << endl;
837 KexiDB::BinaryExpr* binary = eItem->toBinary();
838 if (binary && eItem->exprClass()==KexiDBExpr_Relational) {
839 KexiDB::Field *leftField = 0, *rightField = 0;
840 if (eItem->token()=='='
841 && binary->left()->toVariable()
842 && binary->right()->toVariable()
843 && (leftField = query->findTableField( binary->left()->toString() ))
844 && (rightField = query->findTableField( binary->right()->toString() )))
845 {
849
850 //this is relationship defined as following JOIN: [table1.]field1 = [table2.]field2
851 if (showRelations) {
854 if (leftField->isPrimaryKey())
855 addConnection(leftField /*master*/, rightField /*details*/);
856 else
857 addConnection(rightField /*master*/, leftField /*details*/);
859 }
860 }
861 else if (binary->left()->toVariable()) {
862 //this is: variable , op , argument
863 //store variable -> argument:
864 criterias.insert(binary->left()->toVariable()->name, binary->right());
865 }
866 else if (binary->right()->toVariable()) {
867 //this is: argument , op , variable
868 //store variable -> argument:
869 criterias.insert(binary->right()->toVariable()->name, binary->left());
870 }
871 }
872 } //while
873
874 if (!showFields)
875 return;
876
877 //3. show fields (including * and table.*)
878 uint row_num = 0;
879 KexiDB::Field *field;
880 TQPtrDict<char> usedCriterias(1009); // <-- used criterias will be saved here
881 // so in step 4. we will be able to add
882 // remaining invisible columns with criterias
883 for (KexiDB::Field::ListIterator it(*query->fields());
884 (field = it.current()); ++it, row_num++)
885 {
886 //append a new row
887 TQString tableName, fieldName, columnAlias, criteriaString;
888 KexiDB::BinaryExpr *criteriaExpr = 0;
889 KexiDB::BaseExpr *criteriaArgument = 0;
890 if (field->isQueryAsterisk()) {
891 if (field->table()) {//single-table asterisk
892 tableName = field->table()->name();
893 fieldName = "*";
894 }
895 else {//all-tables asterisk
896 tableName = "*";
897 fieldName = "";
898 }
899 }
900 else {
901 columnAlias = query->columnAlias(row_num);
902 if (field->isExpression()) {
903// if (columnAlias.isEmpty()) {
904// columnAlias = i18n("expression", "expr%1").arg(row_num); //TODO
905// }
906// if (columnAlias.isEmpty())
907//TODO: ok? perhaps do not allow to omit aliases?
908 fieldName = field->expression()->toString();
909// else
910// fieldName = columnAlias + ": " + field->expression()->toString();
911 }
912 else {
913 tableName = field->table()->name();
914 fieldName = field->name();
915 criteriaArgument = criterias[fieldName];
916 if (!criteriaArgument) {//try table.field
917 criteriaArgument = criterias[tableName+"."+fieldName];
918 }
919 if (criteriaArgument) {//criteria expression is just a parent of argument
920 criteriaExpr = criteriaArgument->parent()->toBinary();
921 usedCriterias.insert(criteriaArgument, (char*)1); //save info. about used criteria
922 }
923 }
924 }
925 //create new row data
926 KexiTableItem *newItem = createNewRow(tableName, fieldName, true /* visible*/);
927 if (criteriaExpr) {
929 if (criteriaExpr->token()=='=')
930 criteriaString = criteriaArgument->toString();
931 else
932 criteriaString = criteriaExpr->tokenToString() + " " + criteriaArgument->toString();
933 (*newItem)[COLUMN_ID_CRITERIA] = criteriaString;
934 }
935 d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
936 //OK, row inserted: create a new set for it
937 KoProperty::Set &set = *createPropertySet( row_num, tableName, fieldName, true/*new one*/ );
938 if (!columnAlias.isEmpty())
939 set["alias"].setValue(columnAlias, false);
940 if (!criteriaString.isEmpty())
941 set["criteria"].setValue( criteriaString, false );
942 if (field->isExpression()) {
943// (*newItem)[COLUMN_ID_COLUMN] = ;
944 if (!d->changeSingleCellValue(*newItem, COLUMN_ID_COLUMN,
945 TQVariant(columnAlias + ": " + field->expression()->toString()), &result))
946 return; //problems with setting column expression
947 }
948 }
949
950 //4. show ORDER BY information
951 d->data->clearRowEditBuffer();
952 KexiDB::OrderByColumnList &orderByColumns = query->orderByColumnList();
953 TQMap<KexiDB::QueryColumnInfo*,int> columnsOrder(
955 for (KexiDB::OrderByColumn::ListConstIterator orderByColumnsIt( orderByColumns.constBegin() );
956 orderByColumnsIt!=orderByColumns.constEnd(); ++orderByColumnsIt)
957 {
958 KexiDB::QueryColumnInfo *column = (*orderByColumnsIt).column();
959 KexiTableItem *rowItem = 0;
960 KoProperty::Set *rowPropertySet = 0;
961 if (column) {
962 //sorting for visible column
963 if (column->visible) {
964 if (columnsOrder.contains(column)) {
965 const int columnPosition = columnsOrder[ column ];
966 rowItem = d->data->at( columnPosition );
967 rowPropertySet = d->sets->at( columnPosition );
968 kexipluginsdbg << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal():\n\t"
969 "Setting \"" << (*orderByColumnsIt).debugString() << "\" sorting for row #"
970 << columnPosition << endl;
971 }
972 }
973 }
974 else if ((*orderByColumnsIt).field()) {
975 //this will be presented as invisible field: create new row
976 field = (*orderByColumnsIt).field();
977 TQString tableName( field->table() ? field->table()->name() : TQString() );
978 rowItem = createNewRow( tableName, field->name(), false /* !visible*/);
979 d->dataTable->dataAwareObject()->insertItem(rowItem, row_num);
980 rowPropertySet = createPropertySet( row_num, tableName, field->name(), true /*newOne*/ );
982 kexipluginsdbg << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal():\n\t"
983 "Setting \"" << (*orderByColumnsIt).debugString() << "\" sorting for invisible field "
984 << field->name() << ", table " << tableName << " -row #" << row_num << endl;
985 row_num++;
986 }
987 //alter sorting for either existing or new row
988 if (rowItem && rowPropertySet) {
989 d->data->updateRowEditBuffer(rowItem, COLUMN_ID_SORTING,
990 (*orderByColumnsIt).ascending() ? 1 : 2); // this will automatically update "sorting" property
991 // in slotBeforeCellChanged()
992 d->data->saveRowChanges(*rowItem, true);
993 (*rowPropertySet)["sorting"].clearModifiedFlag(); // this property should look "fresh"
994 if (!rowItem->at(COLUMN_ID_VISIBLE).toBool()) //update
995 (*rowPropertySet)["visible"].setValue(TQVariant(false), false/*rememberOldValue*/);
996 }
997 }
998
999 //5. Show fields for unused criterias (with "Visible" column set to false)
1000 KexiDB::BaseExpr *criteriaArgument; // <-- contains field or table.field
1001 for (TQDictIterator<KexiDB::BaseExpr> it(criterias); (criteriaArgument = it.current()); ++it) {
1002 if (usedCriterias[it.current()])
1003 continue;
1004 //unused: append a new row
1005 KexiDB::BinaryExpr *criteriaExpr = criteriaArgument->parent()->toBinary();
1006 if (!criteriaExpr) {
1007 kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
1008 "criteriaExpr is not a binary expr" << endl;
1009 continue;
1010 }
1011 KexiDB::VariableExpr *columnNameArgument = criteriaExpr->left()->toVariable(); //left or right
1012 if (!columnNameArgument) {
1013 columnNameArgument = criteriaExpr->right()->toVariable();
1014 if (!columnNameArgument) {
1015 kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
1016 "columnNameArgument is not a variable (table or table.field) expr" << endl;
1017 continue;
1018 }
1019 }
1020 KexiDB::Field* field = 0;
1021 if (-1 == columnNameArgument->name.find('.') && query->tables()->count()==1) {
1022 //extreme case: only field name provided for one-table query:
1023 field = query->tables()->first()->field(columnNameArgument->name);
1024 }
1025 else {
1026 field = query->findTableField(columnNameArgument->name);
1027 }
1028
1029 if (!field) {
1030 kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
1031 "no columnInfo found in the query for name \"" << columnNameArgument->name << endl;
1032 continue;
1033 }
1034 TQString tableName, fieldName, columnAlias, criteriaString;
1036 tableName = field->table()->name();
1037 fieldName = field->name();
1038 //create new row data
1039 KexiTableItem *newItem = createNewRow(tableName, fieldName, false /* !visible*/);
1040 if (criteriaExpr) {
1042 if (criteriaExpr->token()=='=')
1043 criteriaString = criteriaArgument->toString();
1044 else
1045 criteriaString = criteriaExpr->tokenToString() + " " + criteriaArgument->toString();
1046 (*newItem)[COLUMN_ID_CRITERIA] = criteriaString;
1047 }
1048 d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
1049 //OK, row inserted: create a new set for it
1050 KoProperty::Set &set = *createPropertySet( row_num++, tableName, fieldName, true/*new one*/ );
1054 set["criteria"].setValue( criteriaString, false );
1055 set["visible"].setValue( TQVariant(false), false );
1056 }
1057
1058 //current property set has most probably changed
1060
1061 if (!was_dirty)
1062 setDirty(false);
1063 //move to 1st column, 1st row
1064 d->dataTable->dataAwareObject()->ensureCellVisible(0,0);
1065// tempData()->registerTableSchemaChanges(query);
1066}
1067
1069{
1070 TQString xml;
1071// if (!loadDataBlock( xml, "query_layout" )) {
1072 loadDataBlock( xml, "query_layout" );
1073 //TODO errmsg
1074// return false;
1075// }
1076 if (xml.isEmpty()) {
1077 //in a case when query layout was not saved, build layout by hand
1078 // -- dynamic cast because of a need for handling invalid queries
1079 // (as in KexiQueryDesignerGuiEditor::afterSwitchFrom()):
1080 KexiDB::QuerySchema * q = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
1081 if (q) {
1082 showTablesForQuery( q );
1083 KexiDB::ResultInfo result;
1084 showRelationsForQuery( q, result );
1085 if (!result.success) {
1086 parentDialog()->setStatus(&result, i18n("Query definition loading failed."));
1087 return false;
1088 }
1089 }
1090 return true;
1091 }
1092
1093 TQDomDocument doc;
1094 doc.setContent(xml);
1095 TQDomElement doc_el = doc.documentElement(), el;
1096 if (doc_el.tagName()!="query_layout") {
1097 //TODO errmsg
1098 return false;
1099 }
1100
1101 const bool was_dirty = dirty();
1102
1103 //add tables and relations to the relation view
1104 for (el = doc_el.firstChild().toElement(); !el.isNull(); el=el.nextSibling().toElement()) {
1105 if (el.tagName()=="table") {
1106 KexiDB::TableSchema *t = d->conn->tableSchema(el.attribute("name"));
1107 int x = el.attribute("x","-1").toInt();
1108 int y = el.attribute("y","-1").toInt();
1109 int width = el.attribute("width","-1").toInt();
1110 int height = el.attribute("height","-1").toInt();
1111 TQRect rect;
1112 if (x!=-1 || y!=-1 || width!=-1 || height!=-1)
1113 rect = TQRect(x,y,width,height);
1114 d->relations->addTable( t, rect );
1115 }
1116 else if (el.tagName()=="conn") {
1117 SourceConnection src_conn;
1118 src_conn.masterTable = el.attribute("mtable");
1119 src_conn.masterField = el.attribute("mfield");
1120 src_conn.detailsTable = el.attribute("dtable");
1121 src_conn.detailsField = el.attribute("dfield");
1122 d->relations->addConnection(src_conn);
1123 }
1124 }
1125
1126 if (!was_dirty)
1127 setDirty(false);
1128 return true;
1129}
1130
1132{
1133 KexiQueryPart::TempData * temp = tempData();
1134
1135 // Save SQL without driver-escaped keywords.
1137 if (m_dialog->schemaData()) //set this instance as obsolete (only if it's stored)
1138 dbConn->setQuerySchemaObsolete( m_dialog->schemaData()->name() );
1139
1141 options.identifierEscaping = KexiDB::Driver::EscapeKexi|KexiDB::Driver::EscapeAsNecessary;
1142 options.addVisibleLookupColumns = false;
1143 TQString sqlText = dbConn->selectStatement( *temp->query(), options );
1144 if (!storeDataBlock( sqlText, "sql" )) {
1145 return false;
1146 }
1147
1148 //serialize detailed XML query definition
1149 TQString xml = "<query_layout>", tmp;
1150 for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
1151 KexiRelationViewTableContainer *table_cont = it.current();
1153 tmp = TQString("<table name=\"")+TQString(table_cont->schema()->name())+"\" x=\""
1154 +TQString::number(table_cont->x())
1155 +"\" y=\""+TQString::number(table_cont->y())
1156 +"\" width=\""+TQString::number(table_cont->width())
1157 +"\" height=\""+TQString::number(table_cont->height())
1158 +"\"/>";
1159 xml += tmp;
1160 }
1161
1162 KexiRelationViewConnection *con;
1163 for (ConnectionListIterator it(*d->relations->connections()); (con = it.current()); ++it) {
1164 tmp = TQString("<conn mtable=\"") + TQString(con->masterTable()->schema()->name())
1165 + "\" mfield=\"" + con->masterField() + "\" dtable=\""
1166 + TQString(con->detailsTable()->schema()->name())
1167 + "\" dfield=\"" + con->detailsField() + "\"/>";
1168 xml += tmp;
1169 }
1170 xml += "</query_layout>";
1171 if (!storeDataBlock( xml, "query_layout" )) {
1172 return false;
1173 }
1174
1175// mainWin()->project()->reloadPartItem( m_dialog );
1176
1177 return true;
1178}
1179
1180TQSize KexiQueryDesignerGuiEditor::sizeHint() const
1181{
1182 TQSize s1 = d->relations->sizeHint();
1183 TQSize s2 = d->head->sizeHint();
1184 return TQSize(TQMAX(s1.width(),s2.width()), s1.height()+s2.height());
1185}
1186
1187KexiTableItem*
1188KexiQueryDesignerGuiEditor::createNewRow(const TQString& tableName, const TQString& fieldName,
1189 bool visible) const
1190{
1191 KexiTableItem *newItem = d->data->createItem();
1192 TQString key;
1193 if (tableName=="*")
1194 key="*";
1195 else {
1196 if (!tableName.isEmpty())
1197 key = (tableName+".");
1198 key += fieldName;
1199 }
1200 (*newItem)[COLUMN_ID_COLUMN]=key;
1201 (*newItem)[COLUMN_ID_TABLE]=tableName;
1202 (*newItem)[COLUMN_ID_VISIBLE]=TQVariant(visible);
1203#ifndef KEXI_NO_QUERY_TOTALS
1204 (*newItem)[COLUMN_ID_TOTALS]=TQVariant(0);
1205#endif
1206 return newItem;
1207}
1208
1209void KexiQueryDesignerGuiEditor::slotDragOverTableRow(
1210 KexiTableItem * /*item*/, int /*row*/, TQDragMoveEvent* e)
1211{
1212 if (e->provides("kexi/field")) {
1213 e->acceptAction(true);
1214 }
1215}
1216
1217void
1218KexiQueryDesignerGuiEditor::slotDroppedAtRow(KexiTableItem * /*item*/, int /*row*/,
1219 TQDropEvent *ev, KexiTableItem*& newItem)
1220{
1221 TQString sourceMimeType;
1222 TQString srcTable;
1223 TQString srcField;
1224
1225 if (!KexiFieldDrag::decodeSingle(ev,sourceMimeType,srcTable,srcField))
1226 return;
1227 //insert new row at specific place
1228 newItem = createNewRow(srcTable, srcField, true /* visible*/);
1229 d->droppedNewItem = newItem;
1230 d->droppedNewTable = srcTable;
1231 d->droppedNewField = srcField;
1232 //TODO
1233}
1234
1236{
1237 KexiTableItem *item = d->data->last();
1238 if (item)
1239 item->at(COLUMN_ID_VISIBLE) = TQVariant(false); //the same init as in initTableRows()
1240}
1241
1242void KexiQueryDesignerGuiEditor::slotRowInserted(KexiTableItem* item, uint row, bool /*repaint*/)
1243{
1244 if (d->droppedNewItem && d->droppedNewItem==item) {
1245 createPropertySet( row, d->droppedNewTable, d->droppedNewField, true );
1247 d->droppedNewItem=0;
1248 }
1249}
1250
1251void KexiQueryDesignerGuiEditor::slotTableAdded(KexiDB::TableSchema & /*t*/)
1252{
1253 if (!d->slotTableAdded_enabled)
1254 return;
1256 setDirty();
1257 d->dataTable->setFocus();
1258}
1259
1260void KexiQueryDesignerGuiEditor::slotTableHidden(KexiDB::TableSchema & /*t*/)
1261{
1263 setDirty();
1264}
1265
1267TQCString KexiQueryDesignerGuiEditor::generateUniqueAlias() const
1268{
1269//TODO: add option for using non-i18n'd "expr" prefix?
1270 const TQCString expStr
1271 = i18n("short for 'expression' word (only latin letters, please)", "expr").latin1();
1272//TODO: optimization: cache it?
1273 TQAsciiDict<char> aliases(1009);
1274 for (int r = 0; r<(int)d->sets->size(); r++) {
1275 KoProperty::Set *set = d->sets->at(r);
1276 if (set) {
1277 const TQCString a = (*set)["alias"].value().toCString().lower();
1278 if (!a.isEmpty())
1279 aliases.insert(a,(char*)1);
1280 }
1281 }
1282 int aliasNr=1;
1283 for (;;aliasNr++) {
1284 if (!aliases[expStr+TQString::number(aliasNr).latin1()])
1285 break;
1286 }
1287 return expStr+TQString::number(aliasNr).latin1();
1288}
1289
1292KexiQueryDesignerGuiEditor::parseExpressionString(const TQString& fullString, int& token,
1293 bool allowRelationalOperator)
1294{
1295 TQString str = fullString.stripWhiteSpace();
1296 int len = 0;
1297 //KexiDB::BaseExpr *expr = 0;
1298 //1. get token
1299 token = 0;
1300 //2-char-long tokens
1301 if (str.startsWith(">="))
1302 token = GREATER_OR_EQUAL;
1303 else if (str.startsWith("<="))
1304 token = LESS_OR_EQUAL;
1305 else if (str.startsWith("<>"))
1306 token = NOT_EQUAL;
1307 else if (str.startsWith("!="))
1308 token = NOT_EQUAL2;
1309 else if (str.startsWith("=="))
1310 token = '=';
1311
1312 if (token!=0)
1313 len = 2;
1314 else if (str.startsWith("=") //1-char-long tokens
1315 || str.startsWith("<")
1316 || str.startsWith(">"))
1317 {
1318 token = str[0].latin1();
1319 len = 1;
1320 }
1321 else {
1322 if (allowRelationalOperator)
1323 token = '=';
1324 }
1325
1326 if (!allowRelationalOperator && token!=0)
1327 return 0;
1328
1329 //1. get expression after token
1330 if (len>0)
1331 str = str.mid(len).stripWhiteSpace();
1332 if (str.isEmpty())
1333 return 0;
1334
1335 KexiDB::BaseExpr *valueExpr = 0;
1336 TQRegExp re;
1337 if (str.length()>=2 &&
1338 (
1339 (str.startsWith("\"") && str.endsWith("\""))
1340 || (str.startsWith("'") && str.endsWith("'")))
1341 )
1342 {
1343 valueExpr = new KexiDB::ConstExpr(CHARACTER_STRING_LITERAL, str.mid(1,str.length()-2));
1344 }
1345 else if (str.startsWith("[") && str.endsWith("]")) {
1346 valueExpr = new KexiDB::QueryParameterExpr(str.mid(1,str.length()-2));
1347 }
1348 else if ((re = TQRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})")).exactMatch( str ))
1349 {
1350 valueExpr = new KexiDB::ConstExpr(DATE_CONST, TQDate::fromString(
1351 re.cap(1).rightJustify(4, '0')+"-"+re.cap(2).rightJustify(2, '0')
1352 +"-"+re.cap(3).rightJustify(2, '0'), TQt::ISODate));
1353 }
1354 else if ((re = TQRegExp("(\\d{1,2}):(\\d{1,2})")).exactMatch( str )
1355 || (re = TQRegExp("(\\d{1,2}):(\\d{1,2}):(\\d{1,2})")).exactMatch( str ))
1356 {
1357 TQString res = re.cap(1).rightJustify(2, '0')+":"+re.cap(2).rightJustify(2, '0')
1358 +":"+re.cap(3).rightJustify(2, '0');
1359// kexipluginsdbg << res << endl;
1360 valueExpr = new KexiDB::ConstExpr(TIME_CONST, TQTime::fromString(res, TQt::ISODate));
1361 }
1362 else if ((re = TQRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2})")).exactMatch( str )
1363 || (re = TQRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2}):(\\d{1,2})")).exactMatch( str ))
1364 {
1365 TQString res = re.cap(1).rightJustify(4, '0')+"-"+re.cap(2).rightJustify(2, '0')
1366 +"-"+re.cap(3).rightJustify(2, '0')
1367 +"T"+re.cap(4).rightJustify(2, '0')+":"+re.cap(5).rightJustify(2, '0')
1368 +":"+re.cap(6).rightJustify(2, '0');
1369// kexipluginsdbg << res << endl;
1370 valueExpr = new KexiDB::ConstExpr(DATETIME_CONST,
1371 TQDateTime::fromString(res, TQt::ISODate));
1372 }
1373 else if (str[0]>='0' && str[0]<='9' || str[0]=='-' || str[0]=='+') {
1374 //number
1375 TQString decimalSym = TDEGlobal::locale()->decimalSymbol();
1376 bool ok;
1377 int pos = str.find('.');
1378 if (pos==-1) {//second chance: local decimal symbol
1379 pos = str.find(decimalSym);
1380 }
1381 if (pos>=0) {//real const number
1382 const int left = str.left(pos).toInt(&ok);
1383 if (!ok)
1384 return 0;
1385 const int right = str.mid(pos+1).toInt(&ok);
1386 if (!ok)
1387 return 0;
1388 valueExpr = new KexiDB::ConstExpr(REAL_CONST, TQPoint(left,right)); //decoded to TQPoint
1389 }
1390 else {
1391 //integer const
1392 const TQ_LLONG val = str.toLongLong(&ok);
1393 if (!ok)
1394 return 0;
1395 valueExpr = new KexiDB::ConstExpr(INTEGER_CONST, val);
1396 }
1397 }
1398 else if (str.lower()=="null") {
1399 valueExpr = new KexiDB::ConstExpr(SQL_NULL, TQVariant());
1400 }
1401 else {//identfier
1402 if (!KexiUtils::isIdentifier(str))
1403 return 0;
1404 valueExpr = new KexiDB::VariableExpr(str);
1405 //find first matching field for name 'str':
1406 for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
1408 if (it.current()->schema()->table() && it.current()->schema()->table()->field(str)) {
1409 valueExpr->toVariable()->field = it.current()->schema()->table()->field(str);
1410 break;
1411 }
1412 }
1413 }
1414 return valueExpr;
1415}
1416
1417void KexiQueryDesignerGuiEditor::slotBeforeCellChanged(KexiTableItem *item, int colnum,
1418 TQVariant& newValue, KexiDB::ResultInfo* result)
1419{
1420 if (colnum == COLUMN_ID_COLUMN) {
1421 if (newValue.isNull()) {
1422 d->data->updateRowEditBuffer(item, COLUMN_ID_TABLE, TQVariant(), false);
1423 d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, TQVariant(false));//invisible
1424 d->data->updateRowEditBuffer(item, COLUMN_ID_SORTING, TQVariant());
1425#ifndef KEXI_NO_QUERY_TOTALS
1426 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, TQVariant());//remove totals
1427#endif
1428 d->data->updateRowEditBuffer(item, COLUMN_ID_CRITERIA, TQVariant());//remove crit.
1429 d->sets->removeCurrentPropertySet();
1430 }
1431 else {
1432 //auto fill 'table' column
1433 TQString fieldId( newValue.toString().stripWhiteSpace() ); //tmp, can look like "table.field"
1434 TQString fieldName; //"field" part of "table.field" or expression string
1435 TQString tableName; //empty for expressions
1436 TQCString alias;
1437 TQString columnValueForExpr; //for setting pretty printed "alias: expr" in 1st column
1438 const bool isExpression = !d->fieldColumnIdentifiers[fieldId];
1439 if (isExpression) {
1440 //this value is entered by hand and doesn't match
1441 //any value in the combo box -- we're assuming this is an expression
1442 //-table remains null
1443 //-find "alias" in something like "alias : expr"
1444 const int id = fieldId.find(':');
1445 if (id>0) {
1446 alias = fieldId.left(id).stripWhiteSpace().latin1();
1447 if (!KexiUtils::isIdentifier(alias)) {
1448 result->success = false;
1449 result->allowToDiscardChanges = true;
1450 result->column = colnum;
1451 result->msg = i18n("Entered column alias \"%1\" is not a valid identifier.")
1452 .arg(alias.data());
1453 result->desc = i18n("Identifiers should start with a letter or '_' character");
1454 return;
1455 }
1456 }
1457 fieldName = fieldId.mid(id+1).stripWhiteSpace();
1458 //check expr.
1460 int dummyToken;
1461 if ((e = parseExpressionString(fieldName, dummyToken, false/*allowRelationalOperator*/)))
1462 {
1463 fieldName = e->toString(); //print it prettier
1464 //this is just checking: destroy expr. object
1465 delete e;
1466 }
1467 else {
1468 result->success = false;
1469 result->allowToDiscardChanges = true;
1470 result->column = colnum;
1471 result->msg = i18n("Invalid expression \"%1\"").arg(fieldName);
1472 return;
1473 }
1474 }
1475 else {//not expr.
1476 //this value is properly selected from combo box list
1477 if (fieldId=="*") {
1478 tableName = "*";
1479 }
1480 else {
1482 fieldId, tableName, fieldName, KexiDB::SetFieldNameIfNoTableName))
1483 {
1484 kexipluginswarn << "KexiQueryDesignerGuiEditor::slotBeforeCellChanged(): no 'field' or 'table.field'" << endl;
1485 return;
1486 }
1487 }
1488 }
1489 bool saveOldValue = true;
1490 KoProperty::Set *set = d->sets->findPropertySetForItem(*item); //*propertyBuffer();
1491 if (!set) {
1492 saveOldValue = false; // no old val.
1493 const int row = d->data->findRef(item);
1494 if (row<0) {
1495 result->success = false;
1496 return;
1497 }
1498 set = createPropertySet( row, tableName, fieldName, true );
1500 }
1501 d->data->updateRowEditBuffer(item, COLUMN_ID_TABLE, TQVariant(tableName), false);
1502 d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, TQVariant(true));
1503#ifndef KEXI_NO_QUERY_TOTALS
1504 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, TQVariant(0));
1505#endif
1506 if (!sortingAllowed(fieldName, tableName)) {
1507 // sorting is not available for "*" or "table.*" rows
1509 d->data->updateRowEditBuffer(item, COLUMN_ID_SORTING, TQVariant());
1510 }
1511 //update properties
1512 (*set)["field"].setValue(fieldName, saveOldValue);
1513 if (isExpression) {
1514 //-no alias but it's needed:
1515 if (alias.isEmpty()) //-try oto get old alias
1516 alias = (*set)["alias"].value().toCString();
1517 if (alias.isEmpty()) //-generate smallest unique alias
1518 alias = generateUniqueAlias();
1519 }
1520 (*set)["isExpression"].setValue(TQVariant(isExpression), saveOldValue);
1521 if (!alias.isEmpty()) {
1522 (*set)["alias"].setValue(alias, saveOldValue);
1523 //pretty printed "alias: expr"
1524 newValue = TQString(alias) + ": " + fieldName;
1525 }
1526 (*set)["caption"].setValue(TQString(), saveOldValue);
1527 (*set)["table"].setValue(tableName, saveOldValue);
1528 updatePropertiesVisibility(*set);
1529 }
1530 }
1531 else if (colnum==COLUMN_ID_TABLE) {
1532 if (newValue.isNull()) {
1533 if (!item->at(COLUMN_ID_COLUMN).toString().isEmpty())
1534 d->data->updateRowEditBuffer(item, COLUMN_ID_COLUMN, TQVariant(), false);
1535 d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, TQVariant(false));//invisible
1536#ifndef KEXI_NO_QUERY_TOTALS
1537 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, TQVariant());//remove totals
1538#endif
1539 d->data->updateRowEditBuffer(item, COLUMN_ID_CRITERIA, TQVariant());//remove crit.
1540 d->sets->removeCurrentPropertySet();
1541 }
1542 //update property
1543 KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
1544 if (set) {
1545 if ((*set)["isExpression"].value().toBool()==false) {
1546 (*set)["table"] = newValue;
1547 (*set)["caption"] = TQString();
1548 }
1549 else {
1550 //do not set table for expr. columns
1551 newValue = TQVariant();
1552 }
1553// KoProperty::Set &set = *propertyBuffer();
1554 updatePropertiesVisibility(*set);
1555 }
1556 }
1557 else if (colnum==COLUMN_ID_VISIBLE) {
1558 bool saveOldValue = true;
1559 if (!propertySet()) {
1560 saveOldValue = false;
1561 createPropertySet( d->dataTable->dataAwareObject()->currentRow(),
1562 item->at(COLUMN_ID_TABLE).toString(), item->at(COLUMN_ID_COLUMN).toString(), true );
1563#ifndef KEXI_NO_QUERY_TOTALS
1564 d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, TQVariant(0));//totals
1565#endif
1567 }
1568 KoProperty::Set &set = *propertySet();
1569 set["visible"].setValue(newValue, saveOldValue);
1570 }
1571#ifndef KEXI_NO_QUERY_TOTALS
1572 else if (colnum==COLUMN_ID_TOTALS) {
1573 //TODO:
1574 //unused yet
1575 setDirty(true);
1576 }
1577#endif
1578 else if (colnum==COLUMN_ID_SORTING) {
1579 KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
1580 TQString table( set->property("table").value().toString() );
1581 TQString field( set->property("field").value().toString() );
1582 if (newValue.toInt()==0 || sortingAllowed(field, table)) {
1583 KoProperty::Property &property = set->property("sorting");
1584 TQString key( property.listData()->keysAsStringList()[ newValue.toInt() ] );
1585 kexipluginsdbg << "new key=" << key << endl;
1586 property.setValue(key, true);
1587 }
1588 else { //show msg: sorting is not available
1589 result->success = false;
1590 result->allowToDiscardChanges = true;
1591 result->column = colnum;
1592 result->msg = i18n("Could not set sorting for multiple columns (%1)")
1593 .arg(table=="*" ? table : (table+".*"));
1594 }
1595 }
1596 else if (colnum==COLUMN_ID_CRITERIA) {
1598 TQString operatorStr, argStr;
1599 KexiDB::BaseExpr* e = 0;
1600 const TQString str = newValue.toString().stripWhiteSpace();
1601 int token;
1602 TQString field, table;
1603 KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
1604 if (set) {
1605 field = (*set)["field"].value().toString();
1606 table = (*set)["table"].value().toString();
1607 }
1608 if (!str.isEmpty() && (!set || table=="*" || field.find("*")!=-1)) {
1609 //asterisk found! criteria not allowed
1610 result->success = false;
1611 result->allowToDiscardChanges = true;
1612 result->column = colnum;
1613 if (propertySet())
1614 result->msg = i18n("Could not set criteria for \"%1\"")
1615 .arg(table=="*" ? table : field);
1616 else
1617 result->msg = i18n("Could not set criteria for empty row");
1618 //moved to result->allowToDiscardChanges handler //d->dataTable->dataAwareObject()->cancelEditor(); //prevents further editing of this cell
1619 }
1620 else if (str.isEmpty() || (e = parseExpressionString(str, token, true/*allowRelationalOperator*/)))
1621 {
1622 if (e) {
1623 TQString tokenStr;
1624 if (token!='=') {
1625 KexiDB::BinaryExpr be(KexiDBExpr_Relational, 0, token, 0);
1626 tokenStr = be.tokenToString() + " ";
1627 }
1628 (*set)["criteria"] = tokenStr + e->toString(); //print it prettier
1629 //this is just checking: destroy expr. object
1630 delete e;
1631 }
1632 else if (str.isEmpty()) {
1633 (*set)["criteria"] = TQVariant(); //clear it
1634 }
1635 setDirty(true);
1636 }
1637 else {
1638 result->success = false;
1639 result->allowToDiscardChanges = true;
1640 result->column = colnum;
1641 result->msg = i18n("Invalid criteria \"%1\"").arg(newValue.toString());
1642 }
1643 }
1644}
1645
1646void KexiQueryDesignerGuiEditor::slotTablePositionChanged(KexiRelationViewTableContainer*)
1647{
1648 setDirty(true);
1649}
1650
1651void KexiQueryDesignerGuiEditor::slotAboutConnectionRemove(KexiRelationViewConnection*)
1652{
1653 setDirty(true);
1654}
1655
1656void KexiQueryDesignerGuiEditor::slotTableFieldDoubleClicked(
1657 KexiDB::TableSchema* table, const TQString& fieldName )
1658{
1659 if (!table || (!table->field(fieldName) && fieldName!="*"))
1660 return;
1661 int row_num;
1662 //find last filled row in the GUI table
1663 for (row_num=d->sets->size()-1; row_num>=0 && !d->sets->at(row_num); row_num--)
1664 ;
1665 row_num++; //after
1666 //add row
1667 KexiTableItem *newItem = createNewRow(table->name(), fieldName, true /* visible*/);
1668 d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
1669 d->dataTable->dataAwareObject()->setCursorPosition(row_num, 0);
1670 //create buffer
1671 createPropertySet( row_num, table->name(), fieldName, true/*new one*/ );
1673 d->dataTable->setFocus();
1674}
1675
1677{
1678 return d->sets->currentPropertySet();
1679}
1680
1681void KexiQueryDesignerGuiEditor::updatePropertiesVisibility(KoProperty::Set& set)
1682{
1683 const bool asterisk = isAsterisk(
1684 set["table"].value().toString(), set["field"].value().toString()
1685 );
1686#ifndef KEXI_NO_UNFINISHED
1687 set["caption"].setVisible( !asterisk );
1688#endif
1689 set["alias"].setVisible( !asterisk );
1690/*always invisible #ifndef KEXI_NO_UNFINISHED
1691 set["sorting"].setVisible( !asterisk );
1692#endif*/
1693 propertySetReloaded(true);
1694}
1695
1696KoProperty::Set*
1698 const TQString& tableName, const TQString& fieldName, bool newOne )
1699{
1700 //const bool asterisk = isAsterisk(tableName, fieldName);
1701 TQString typeName = "KexiQueryDesignerGuiEditor::Column";
1702 KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
1703 KoProperty::Property *prop;
1704
1705 //meta-info for property editor
1706 set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Query column")) );
1707 prop->setVisible(false);
1709// prop->setVisible(false);
1710
1711 set->addProperty(prop = new KoProperty::Property("table", TQVariant(tableName)) );
1712 prop->setVisible(false);//always hidden
1713
1714 set->addProperty(prop = new KoProperty::Property("field", TQVariant(fieldName)) );
1715 prop->setVisible(false);//always hidden
1716
1717 set->addProperty(prop = new KoProperty::Property("caption", TQVariant(TQString()), i18n("Caption") ) );
1718#ifdef KEXI_NO_UNFINISHED
1719 prop->setVisible(false);
1720#endif
1721
1722 set->addProperty(prop = new KoProperty::Property("alias", TQVariant(TQString()), i18n("Alias")) );
1723
1724 set->addProperty(prop = new KoProperty::Property("visible", TQVariant(true)) );
1725 prop->setVisible(false);
1726
1727/*TODO:
1728 set->addProperty(prop = new KexiProperty("totals", TQVariant(TQString())) );
1729 prop->setVisible(false);*/
1730
1731 //sorting
1732 TQStringList slist, nlist;
1733 slist << "nosorting" << "ascending" << "descending";
1734 nlist << i18n("None") << i18n("Ascending") << i18n("Descending");
1735 set->addProperty(prop = new KoProperty::Property("sorting",
1736 slist, nlist, *slist.at(0), i18n("Sorting")));
1737 prop->setVisible(false);
1738
1739 set->addProperty(prop = new KoProperty::Property("criteria", TQVariant(TQString())) );
1740 prop->setVisible(false);
1741
1742 set->addProperty(prop = new KoProperty::Property("isExpression", TQVariant(false)) );
1743 prop->setVisible(false);
1744
1745 connect(set, TQ_SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
1746 this, TQ_SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
1747
1748 d->sets->insert(row, set, newOne);
1749
1750 updatePropertiesVisibility(*set);
1751 return set;
1752}
1753
1754void KexiQueryDesignerGuiEditor::setFocus()
1755{
1756 d->dataTable->setFocus();
1757}
1758
1759void KexiQueryDesignerGuiEditor::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
1760{
1761 const TQCString& pname = property.name();
1762/*
1763 * TODO (js) use KexiProperty::setValidator(TQString) when implemented as described in TODO #60
1764 */
1765 if (pname=="alias" || pname=="name") {
1766 const TQVariant& v = property.value();
1767 if (!v.toString().stripWhiteSpace().isEmpty() && !KexiUtils::isIdentifier( v.toString() )) {
1768 KMessageBox::sorry(this,
1769 KexiUtils::identifierExpectedMessage(property.caption(), v.toString()));
1770 property.resetValue();
1771 }
1772 if (pname=="alias") {
1773 if (set["isExpression"].value().toBool()==true) {
1774 //update value in column #1
1775 d->dataTable->dataAwareObject()->acceptEditor();
1776// d->dataTable->dataAwareObject()->setCursorPosition(d->dataTable->dataAwareObject()->currentRow(),0);
1777 //d->dataTable->dataAwareObject()->startEditCurrentCell();
1778 d->data->updateRowEditBuffer(d->dataTable->dataAwareObject()->selectedItem(),
1779 0, TQVariant(set["alias"].value().toString() + ": " + set["field"].value().toString()));
1780 d->data->saveRowChanges(*d->dataTable->dataAwareObject()->selectedItem(), true);
1781// d->dataTable->dataAwareObject()->acceptRowEdit();
1782 }
1783 }
1784 }
1785}
1786
1787void KexiQueryDesignerGuiEditor::slotNewItemStored(KexiPart::Item& item)
1788{
1789 d->relations->objectCreated(item.mimeType(), item.name().latin1());
1790}
1791
1792void KexiQueryDesignerGuiEditor::slotItemRemoved(const KexiPart::Item& item)
1793{
1794 d->relations->objectDeleted(item.mimeType(), item.name().latin1());
1795}
1796
1797void KexiQueryDesignerGuiEditor::slotItemRenamed(const KexiPart::Item& item, const TQCString& oldName)
1798{
1799 d->relations->objectRenamed(item.mimeType(), oldName, item.name().latin1());
1800}
1801
1802#include "kexiquerydesignerguieditor.moc"
1803
A base class for all expressions.
Definition expression.h:67
virtual TQString toString(QuerySchemaParameterValueListIterator *params=0)=0
virtual TQString tokenToString()
options used in selectStatement()
Definition connection.h:672
int identifierEscaping
A mode for escaping identifier, Driver::EscapeDriver|DriverEscapeAsNecessary by default.
Definition connection.h:678
Provides database connection, allowing queries and data modification.
Definition connection.h:62
bool setQuerySchemaObsolete(const TQString &queryName)
TQString selectStatement(QuerySchema &querySchema, const TQValueList< TQVariant > &params, const SelectStatementOptions &options=SelectStatementOptions()) const
uint fieldCount() const
Definition fieldlist.h:58
Field * field(uint id)
Definition fieldlist.h:76
Meta-data for a field.
Definition field.h:73
bool isPrimaryKey() const
Definition field.h:234
void setDefaultValue(const TQVariant &def)
Definition field.cpp:396
void setEnumHints(const TQValueVector< TQString > &l)
Definition field.h:544
TQPtrListIterator< Field > ListIterator
iterator for list of fields
Definition field.h:77
virtual bool isQueryAsterisk() const
Definition field.h:510
bool isExpression() const
Definition field.h:537
void setNotNull(bool n)
Definition field.cpp:558
virtual TableSchema * table() const
Definition field.cpp:300
KexiDB::OrderByColumnList provides list of sorted columns for a query schema.
void appendColumn(QueryColumnInfo &columnInfo, bool ascending=true)
void appendField(Field &field, bool ascending=true)
KexiDB::QueryAsterisk class encapsulates information about single asterisk in query definition.
Helper class that assigns additional information for the column in a query.
Definition queryschema.h:49
bool visible
true if this column is visible to the user (and its data is fetched by the engine)
Definition queryschema.h:87
KexiDB::QuerySchema provides information about database query.
TQCString columnAlias(uint position) const
FieldList & addExpression(BaseExpr *expr, bool visible=true)
Relationship::List * relationships() const
void addTable(TableSchema *table, const TQCString &alias=TQCString())
virtual Field * field(const TQString &name, bool expanded=true)
FieldList & addAsterisk(QueryAsterisk *asterisk, bool visible=true)
void setWhereExpression(BaseExpr *expr)
Relationship * addRelationship(Field *field1, Field *field2)
OrderByColumnList & orderByColumnList() const
TQMap< QueryColumnInfo *, int > columnsOrder(ColumnsOrderOptions options=ExpandedList)
QueryColumnInfo::Vector fieldsExpanded(FieldsExpandedOptions options=Default)
@ UnexpandedListWithoutAsterisks
A map for unexpanded list is created, with asterisks skipped.
Field * findTableField(const TQString &tableOrTableAndFieldName) const
TableSchema::List * tables() const
QueryColumnInfo * columnInfo(const TQString &identifier, bool expanded=true)
void setOrderByColumnList(const OrderByColumnList &list)
BaseExpr * whereExpression() const
FieldList & addField(Field *field, bool visible=true)
QueryColumnInfo * expandedOrInternalField(uint index)
void setColumnAlias(uint position, const TQCString &alias)
bool success
result of the operation, true by default
Definition error.h:127
int column
faulty column, -1 (the default) means: there is no faulty column
Definition error.h:132
TQString desc
error message and detailed description, both empty by default
Definition error.h:131
bool allowToDiscardChanges
Definition error.h:128
TableSchema * table() const
TQPtrListIterator< TableSchema > ListIterator
Iterator for tables list.
Definition tableschema.h:46
Variables like fieldname or tablename.fieldname.
Definition expression.h:245
Provides a data-driven (record-based) tabular view.
KexiDialogTempData * tempData() const
static bool decodeSingle(TQDropEvent *e, TQString &sourceMimeType, TQString &sourceName, TQString &field)
Kexi's main window interface This interface is implemented by KexiMainWindowImpl class.
virtual KexiProject * project()=0
Project data of currently opened project or NULL if no project here yet.
Information about a single object that can be instantiated using Kexi Part.
KexiDB::Connection * dbConnection() const
virtual KexiDB::SchemaData * storeNewData(const KexiDB::SchemaData &sdata, bool &cancel)
void slotBeforeCellChanged(KexiTableItem *item, int colnum, TQVariant &newValue, KexiDB::ResultInfo *result)
Called before cell change in tableview.
KexiDB::BaseExpr * parseExpressionString(const TQString &fullString, int &token, bool allowRelationalOperator)
void initTableRows()
Called to have all rows empty.
virtual tristate beforeSwitchTo(int mode, bool &dontStore)
void showFieldsAndRelationsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo &result)
convenience method equal to showFieldsOrRelationsForQueryInternal(query, true, true)
void showFieldsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo &result)
convenience method equal to showFieldsOrRelationsForQueryInternal(query, true, false)
KexiTableItem * createNewRow(const TQString &tableName, const TQString &fieldName, bool visible) const
virtual tristate storeData(bool dontAsk=false)
virtual tristate afterSwitchFrom(int mode)
void slotNewItemAppendedForAfterDeletingInSpreadSheetMode()
Reaction on appending a new item after deleting one.
virtual KoProperty::Set * propertySet()
void showRelationsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo &result)
convenience method equal to showFieldsOrRelationsForQueryInternal(query, false, true)
void showFieldsOrRelationsForQueryInternal(KexiDB::QuerySchema *query, bool showFields, bool showRelations, KexiDB::ResultInfo &result)
KoProperty::Set * createPropertySet(int row, const TQString &tableName, const TQString &fieldName, bool newOne=false)
Temporary data kept in memory while switching between Query Dialog's views.
KexiDB::QuerySchema * takeQuery()
KexiDB::QuerySchema * query() const
void setQuery(KexiDB::QuerySchema *query)
KexiDB::Field * field() const
void setRelatedDataEditable(bool set)
void setRelatedData(KexiTableViewData *data)
Base class for single view embeddable of in KexiDialogBase.
virtual bool dirty() const
virtual tristate storeData(bool dontAsk=false)
virtual void propertySetSwitched()
void propertySetReloaded(bool preservePrevSelection=false, const TQCString &propertyToSelect=TQCString())
int viewMode() const
bool storeDataBlock(const TQString &dataString, const TQString &dataID=TQString())
bool loadDataBlock(TQString &dataString, const TQString &dataID=TQString(), bool canBeEmpty=false)
KexiDialogBase * parentDialog() const
KexiMainWindow * mainWin() const
3-state logical type with three values: true, false and cancelled and convenient operators.
Definition tristate.h:100
KEXI_DB_EXPORT bool splitToTableAndFieldParts(const TQString &string, TQString &tableName, TQString &fieldName, SplitToTableAndFieldPartsOptions option=FailIfNoTableOrFieldName)
@ SetFieldNameIfNoTableName
see splitToTableAndFieldParts()
KEXIUTILS_EXPORT TQString identifierExpectedMessage(const TQString &valueName, const TQVariant &v)
KEXIUTILS_EXPORT bool isIdentifier(const TQString &s)
@ TextViewMode
Also known as SQL View Mode.
Definition kexi.h:48
@ NoViewMode
Definition kexi.h:44