kexidataawareobjectiface.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl>
3
4 Based on KexiTableView code.
5 Copyright (C) 2002 Till Busch <till@bux.at>
6 Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
7 Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
8 Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
9
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Library General Public License for more details.
19
20 You should have received a copy of the GNU Library General Public License
21 along with this program; see the file COPYING. If not, write to
22 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24*/
25
26#include "kexidataawareobjectiface.h"
27
28#include <tqscrollview.h>
29#include <tqlabel.h>
30#include <tqtooltip.h>
31
32#include <tdemessagebox.h>
33
34#include <kexi.h>
35#include <kexiutils/validator.h>
36#include <widget/utils/kexirecordnavigator.h>
37#include <widget/utils/kexirecordmarker.h>
38#include <kexidb/roweditbuffer.h>
39#include <kexidataiteminterface.h>
40
41#include "kexitableviewheader.h"
42
43using namespace KexiUtils;
44
45KexiDataAwareObjectInterface::KexiDataAwareObjectInterface()
46{
47 m_data = 0;
49 m_readOnly = -1; //don't know
50 m_insertingEnabled = -1; //don't know
51 m_isSortingEnabled = true;
53 m_deletionPolicy = AskDelete;
56 m_internal_acceptsRowEditAfterCellAccepting = false;
58 m_navPanel = 0;
63 m_insertItem = 0;
64// m_rowEditBuffer = 0;
65 m_spreadSheetMode = false;
66 m_dropsAtRowEnabled = false;
70 m_popupMenu = 0;
71 m_contextMenuEnabled = true;
73 m_alsoUpdateNextRow = false;
79 m_recentSearchDirection = KexiSearchAndReplaceViewInterface::Options::DefaultSearchDirection;
80
81 // setup scrollbar tooltip and related members
82 m_scrollBarTip = new TQLabel("",0, "vScrollBarToolTip",
83 TQt::WStyle_Customize |TQt::WStyle_NoBorder|TQt::WX11BypassWM|TQt::WStyle_StaysOnTop|TQt::WStyle_Tool);
84 m_scrollBarTip->setPalette(TQToolTip::palette());
85 m_scrollBarTip->setMargin(2);
86 m_scrollBarTip->setIndent(0);
87 m_scrollBarTip->setAlignment(TQt::AlignCenter);
88 m_scrollBarTip->setFrameStyle( TQFrame::Plain | TQFrame::Box );
89 m_scrollBarTip->setLineWidth(1);
90
92}
93
94KexiDataAwareObjectInterface::~KexiDataAwareObjectInterface()
95{
96 delete m_insertItem;
97// delete m_rowEditBuffer;
98 delete m_itemIterator;
99 delete m_scrollBarTip;
100 //we cannot delete m_data here... subclasses should do this
101}
102
104{
105 m_editor = 0;
106// m_rowEditBuffer = 0;
107 m_rowEditing = false;
108 m_newRowEditing = false;
109 m_curRow = -1;
110 m_curCol = -1;
111 m_currentItem = 0;
112}
113
115{
116 const bool theSameData = m_data && m_data==data;
117 if (m_owner && m_data && m_data!=data/*don't destroy if it's the same*/) {
118 kexidbg << "KexiDataAwareObjectInterface::setData(): destroying old data (owned)" << endl;
119 delete m_itemIterator;
120 delete m_data; //destroy old data
121 m_data = 0;
122 m_itemIterator = 0;
123 }
124 m_owner = owner;
125 m_data = data;
126 if (m_data)
127 m_itemIterator = m_data->createIterator();
128
129 kdDebug(44021) << "KexiDataAwareObjectInterface::setData(): using shared data" << endl;
130 //add columns
131//OK?
133 if (m_data) {
134 int i = 0;
135 for (KexiTableViewColumn::ListIterator it(m_data->columns);
136 it.current(); ++it, i++)
137 {
138 KexiDB::Field *f = it.current()->field();
139 if (it.current()->visible()) {
140 int wid = f->width();
141 if (wid==0)
142 wid=KEXI_DEFAULT_DATA_COLUMN_WIDTH;//default col width in pixels
144 addHeaderColumn(it.current()->isHeaderTextVisible()
145 ? it.current()->captionAliasOrName() : TQString(),
146 f->description(), it.current()->icon(), wid);
147 }
148 }
149 }
150 if (m_verticalHeader) {
151 m_verticalHeader->clear();
152 if (m_data)
154 }
155 if (m_data && m_data->count()==0)
157
158 if (m_data && !theSameData) {
160 setSorting(-1);
161// connect(m_data, TQ_SIGNAL(refreshRequested()), this, TQ_SLOT(slotRefreshRequested()));
162 connectToReloadDataSlot(m_data, TQ_SIGNAL(reloadRequested()));
163 TQObject* thisObject = dynamic_cast<TQObject*>(this);
164 if (thisObject) {
165 TQObject::connect(m_data, TQ_SIGNAL(destroying()), thisObject, TQ_SLOT(slotDataDestroying()));
166 TQObject::connect(m_data, TQ_SIGNAL(rowsDeleted( const TQValueList<int> & )),
167 thisObject, TQ_SLOT(slotRowsDeleted( const TQValueList<int> & )));
168 TQObject::connect(m_data, TQ_SIGNAL(aboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)),
170 TQObject::connect(m_data, TQ_SIGNAL(rowDeleted()), thisObject, TQ_SLOT(slotRowDeleted()));
171 TQObject::connect(m_data, TQ_SIGNAL(rowInserted(KexiTableItem*,bool)),
172 thisObject, TQ_SLOT(slotRowInserted(KexiTableItem*,bool)));
173 TQObject::connect(m_data, TQ_SIGNAL(rowInserted(KexiTableItem*,uint,bool)),
174 thisObject, TQ_SLOT(slotRowInserted(KexiTableItem*,uint,bool))); //not db-aware
175 TQObject::connect(m_data, TQ_SIGNAL(rowRepaintRequested(KexiTableItem&)),
176 thisObject, TQ_SLOT(slotRowRepaintRequested(KexiTableItem&)));
177 // setup scrollbar's tooltip
178 TQObject::connect(verticalScrollBar(),TQ_SIGNAL(sliderReleased()),
180 TQObject::connect(verticalScrollBar(),TQ_SIGNAL(valueChanged(int)),
182 TQObject::connect(&m_scrollBarTipTimer,TQ_SIGNAL(timeout()),
184 }
185 }
186
187 if (!m_data) {
188// clearData();
190 //m_data->clearInternal();
192 }
193 else {
194 if (!m_insertItem) {//first setData() call - add 'insert' item
195 m_insertItem = m_data->createItem(); //new KexiTableItem(m_data->columns.count());
196 }
197 else {//just reinit
198 m_insertItem->init(m_data->columns.count());
199 }
200 }
201
202 //update gui mode
205 m_verticalHeader->showInsertRow(m_data && isInsertingEnabled());
206
209
210 if (m_data)
211 /*emit*/ dataSet( m_data );
212}
213
215{
216 m_editor = 0;
217// TQSize s(tableSize());
218// resizeContents(s.width(),s.height());
219
221
223 //set current row:
224 m_currentItem = 0;
225 int curRow = -1, curCol = -1;
226 if (m_data->columnsCount()>0) {
227 if (rows()>0) {
228 m_itemIterator->toFirst();
230 curRow = 0;
231 curCol = 0;
232 }
233 else {//no data
234 if (isInsertingEnabled()) {
236 curRow = 0;
237 curCol = 0;
238 }
239 }
240 }
241 setCursorPosition(curRow, curCol, true/*force*/);
242 }
243 ensureCellVisible(m_curRow, m_curCol);
244// updateRowCountInfo();
245// setNavRowCount(rows());
246
247//OK?
248// updateContents();
250
252
253 /*emit*/ dataRefreshed();
254}
255
257{
258 if (m_isSortingEnabled && !set)
259 setSorting(-1);
260 m_isSortingEnabled = set;
261 /*emit*/ reloadActions();
262}
263
265{
266 if (!m_data || !m_isSortingEnabled)
267 return;
268// d->pTopHeader->setSortIndicator(col, ascending ? Ascending : Descending);
269 setLocalSortingOrder(col, ascending ? 1 : -1);
270 m_data->setSorting(col, ascending);
271}
272
274{
276 return m_data->sortedColumn();
277 return -1;
278}
279
284
286{
287 if (!m_data || !m_isSortingEnabled)
288 return false;
289
290 if (rows() < 2)
291 return true;
292
293 if (!acceptRowEdit())
294 return false;
295
296 const int oldRow = m_curRow;
297 if (m_data->sortedColumn()!=-1)
298 m_data->sort();
299
300 //locate current record
301 if (!m_currentItem) {
302 m_itemIterator->toFirst();
303 m_currentItem = **m_itemIterator; //m_data->first();
304 m_curRow = 0;
305 if (!m_currentItem)
306 return true;
307 }
309 m_curRow = m_data->findRef(m_currentItem);
310 int jump = m_curRow - oldRow;
311 if (jump<0)
312 (*m_itemIterator) -= -jump;
313 else
314 (*m_itemIterator) += jump;
315 }
316
317 updateGUIAfterSorting();
318 editorShowFocus( m_curRow, m_curCol );
320 m_verticalHeader->setCurrentRow(m_curRow);
322 m_horizontalHeader->setSelectedSection(m_curCol);
323 if (m_navPanel)
325 return true;
326}
327
329{
330 if (currentColumn()<0)
331 return;
332 sortColumnInternal( currentColumn(), 1 );
333}
334
336{
337 if (currentColumn()<0)
338 return;
339 sortColumnInternal( currentColumn(), -1 );
340}
341
342void KexiDataAwareObjectInterface::sortColumnInternal(int col, int order)
343{
344 //-select sorting
345 bool asc;
346 if (order == 0) {// invert
348 asc = dataSortingOrder()==-1; //inverse sorting for this column -> descending order
349 else
350 asc = true;
351 }
352 else
353 asc = (order==1);
354
355 int prevSortOrder = currentLocalSortingOrder();
356 const int prevSortColumn = currentLocalSortingOrder();
357 setSorting( col, asc );
358 //-perform sorting
359 if (!sort())
360 setLocalSortingOrder(prevSortColumn, prevSortOrder); //this will also remove indicator
361 //if prevSortColumn==-1
362 if (col != prevSortColumn)
363 /*emit*/ sortedColumnChanged(col);
364}
365
367{
368 if (isReadOnly())
369 return false;
370 if (m_insertingEnabled == 1 || m_insertingEnabled == 0)
371 return (bool)m_insertingEnabled;
372 if (!hasData())
373 return true;
374 return m_data->isInsertingEnabled();
375}
376
381
383{
384 return (m_deletionPolicy != NoDelete) && !isReadOnly();
385}
386
387void KexiDataAwareObjectInterface::setDeletionPolicy(DeletionPolicy policy)
388{
389 m_deletionPolicy = policy;
390// updateContextMenu();
391}
392
394{
395 if (isReadOnly() == set || (m_data && m_data->isReadOnly() && !set))
396 return; //not allowed!
397 m_readOnly = (set ? 1 : 0);
398 if (set)
399 setInsertingEnabled(false);
401 /*emit*/ reloadActions();
402}
403
405{
406 if (!hasData())
407 return true;
408 if (m_readOnly == 1 || m_readOnly == 0)
409 return (bool)m_readOnly;
410 if (!hasData())
411 return true;
412 return m_data->isReadOnly();
413}
414
416{
417 if (isInsertingEnabled() == set || (m_data && !m_data->isInsertingEnabled() && set))
418 return; //not allowed!
419 m_insertingEnabled = (set ? 1 : 0);
422 m_verticalHeader->showInsertRow(set);
423 if (set)
424 setReadOnly(false);
425// update();
427 /*emit*/ reloadActions();
428}
429
431{
432 m_spreadSheetMode = true;
433 setSortingEnabled( false );
434 setInsertingEnabled( false );
436 setFilteringEnabled( false );
438 m_navPanelEnabled = false;
439}
440
441void KexiDataAwareObjectInterface::selectNextRow()
442{
443 selectRow( TQMIN( rows() - 1 +(isInsertingEnabled()?1:0), m_curRow + 1 ) );
444}
445
447{
448 selectRow(
449 TQMAX( 0, m_curRow - rowsPerPage() )
450 );
451}
452
454{
455 selectRow(
456 TQMIN(
457 rows() - 1 + (isInsertingEnabled()?1:0),
459 )
460 );
461}
462
463void KexiDataAwareObjectInterface::selectFirstRow()
464{
465 selectRow(0);
466}
467
468void KexiDataAwareObjectInterface::selectLastRow()
469{
470// selectRow(rows() - 1 + (isInsertingEnabled()?1:0));
471 selectRow(rows() - 1);
472}
473
474void KexiDataAwareObjectInterface::selectRow(int row)
475{
476 m_vScrollBarValueChanged_enabled = false; //disable tooltip
479}
480
481void KexiDataAwareObjectInterface::selectPrevRow()
482{
483 selectRow( TQMAX( 0, m_curRow - 1 ) );
484}
485
487{
488// selectRow( -1 );
489 int oldRow = m_curRow;
490// int oldCol = m_curCol;
491 m_curRow = -1;
492 m_curCol = -1;
493 m_currentItem = 0;
494 updateRow( oldRow );
496// setNavRowNumber(-1);
497}
498
500{
501 int newrow = row;
502 int newcol = col;
503
504 if(rows() <= 0) {
506 m_verticalHeader->setCurrentRow(-1);
508 m_horizontalHeader->setSelectedSection(-1);
509 if (isInsertingEnabled()) {
511 newrow=0;
512 if (col>=0)
513 newcol=col;
514 else
515 newcol=0;
516 }
517 else {
519 m_curRow=-1;
520 m_curCol=-1;
521 return;
522 }
523 }
524
525 if(col>=0)
526 {
527 newcol = TQMAX(0, col);
528 newcol = TQMIN(columns() - 1, newcol);
529 }
530 else {
531 newcol = m_curCol; //no changes
532 newcol = TQMAX(0, newcol); //may not be < 0 !
533 }
534 newrow = TQMAX(0, row);
535 newrow = TQMIN(rows() - 1 + (isInsertingEnabled()?1:0), newrow);
536
537// d->pCurrentItem = itemAt(d->curRow);
538// kdDebug(44021) << "setCursorPosition(): d->curRow=" << d->curRow << " oldRow=" << oldRow << " d->curCol=" << d->curCol << " oldCol=" << oldCol << endl;
539
540 if ( forceSet || m_curRow != newrow || m_curCol != newcol )
541 {
542 kexidbg << "setCursorPosition(): " <<TQString("old:%1,%2 new:%3,%4").arg(m_curCol)
543 .arg(m_curRow).arg(newcol).arg(newrow) << endl;
544
545 // cursor moved: get rid of editor
546 if (m_editor) {
548 if (!acceptEditor()) {
549 return;
550 }
551 //update row num. again
552 newrow = TQMIN( rows() - 1 + (isInsertingEnabled()?1:0), newrow);
553 }
554 }
556 m_errorMessagePopup->close();
557 }
558
559 if (m_curRow != newrow || forceSet) {//update current row info
561// setNavRowNumber(newrow);
562// d->navBtnPrev->setEnabled(newrow>0);
563// d->navBtnFirst->setEnabled(newrow>0);
564// d->navBtnNext->setEnabled(newrow<(rows()-1+(isInsertingEnabled()?1:0)));
565// d->navBtnLast->setEnabled(newrow!=(rows()-1));
566 }
567
568 // cursor moved to other row: end of row editing
569 bool newRowInserted = false;
570 if (m_rowEditing && m_curRow != newrow) {
572 if (!acceptRowEdit()) {
573 //accepting failed: cancel setting the cursor
574 return;
575 }
576 //update row number, because number of rows changed
577 newrow = TQMIN( rows() - 1 + (isInsertingEnabled()?1:0), newrow);
578
580 }
581
582 //change position
583 int oldRow = m_curRow;
584 int oldCol = m_curCol;
586 m_curCol = newcol;
587
588// int cw = columnWidth( d->curCol );
589// int rh = rowHeight();
590// ensureVisible( columnPos( d->curCol ) + cw / 2, rowPos( d->curRow ) + rh / 2, cw / 2, rh / 2 );
591// center(columnPos(d->curCol) + cw / 2, rowPos(d->curRow) + rh / 2, cw / 2, rh / 2);
592// kdDebug(44021) << " contentsY() = "<< contentsY() << endl;
593
594//js if (oldRow > d->curRow)
595//js ensureVisible(columnPos(d->curCol), rowPos(d->curRow) + rh, columnWidth(d->curCol), rh);
596//js else// if (oldRow <= d->curRow)
597//js ensureVisible(columnPos(d->curCol), rowPos(d->curRow), columnWidth(d->curCol), rh);
598
599
600 //show editor-dependent focus, if we're changing the current column
601 if (oldCol>=0 && oldCol<columns() && m_curCol!=oldCol) {
602 //find the editor for this column
604 if (edit) {
605 edit->hideFocus();
606 }
607 }
608
609 // position changed, so subsequent searching should be started from scratch
610 // (e.g. from the current cell or the top-left cell)
611 m_positionOfRecentlyFoundValue.exists = false;
612
613 //show editor-dependent focus, if needed
614 editorShowFocus( m_curRow, m_curCol );
615
617 updateRow( oldRow );
618 else
620
621// //quite clever: ensure the cell is visible:
622// ensureCellVisible(m_curRow, m_curCol);
623
624// TQPoint pcenter = TQRect( columnPos(d->curCol), rowPos(d->curRow), columnWidth(d->curCol), rh).center();
625// ensureVisible(pcenter.x(), pcenter.y(), columnWidth(d->curCol)/2, rh/2);
626
627// ensureVisible(columnPos(d->curCol), rowPos(d->curRow) - contentsY(), columnWidth(d->curCol), rh);
629 m_verticalHeader->setCurrentRow(m_curRow);
630
633 else
634 updateCell( m_curRow, m_curCol );
635
636 if (m_curCol != oldCol || m_curRow != oldRow || forceSet) {//ensure this is also refreshed
637 if (!m_updateEntireRowWhenMovingToOtherRow) //only if entire row has not been updated
638 updateCell( oldRow, m_curCol );
639 }
640 //update row
641 if (forceSet || m_curRow != oldRow) {
642 if (isInsertingEnabled() && m_curRow == rows()) {
643 kdDebug(44021) << "NOW insert item is current" << endl;
645 }
646 else {
647 kdDebug(44021) << TQString("NOW item at %1 (%2) is current")
648 .arg(m_curRow).arg((ulong)itemAt(m_curRow)) << endl;
649 //NOT EFFECTIVE!!!!!!!!!!!
650 //set item iterator
652 //moving from 'insert item' to last item
653 m_itemIterator->toLast();
654 }
656 m_itemIterator->toFirst();
657 else if (!newRowInserted && !forceSet && m_currentItem != m_insertItem && oldRow>=0 && (oldRow+1)==m_curRow) //just move next
658 ++(*m_itemIterator);
659 else if (!newRowInserted && !forceSet && m_currentItem != m_insertItem && oldRow>=0 && (oldRow-1)==m_curRow) //just move back
660 --(*m_itemIterator);
661 else { //move at:
662 m_itemIterator->toFirst();
663 (*m_itemIterator)+=m_curRow;
664 }
665 if (!**m_itemIterator) { //sanity
666 m_itemIterator->toFirst();
667 (*m_itemIterator)+=m_curRow;
668 }
670 //itemAt(m_curRow);
671 }
672 }
673
674 //quite clever: ensure the cell is visible:
675 ensureCellVisible(m_curRow, m_curCol);
676
677 if (m_horizontalHeader && (oldCol != m_curCol || forceSet))
678 m_horizontalHeader->setSelectedSection(m_curCol);
679
680 /*emit*/ itemSelected(m_currentItem);
681 /*emit*/ cellSelected(m_curCol, m_curRow);
682 /* only needed for forms */
684 }
685 else {
686 kexidbg << "setCursorPosition(): NO CHANGE" << endl;
687 }
688
691 }
692}
693
695{
696 if (!m_rowEditing || /*sanity*/!m_data->rowEditBuffer())
697 return true;
699 m_internal_acceptsRowEditAfterCellAccepting = true;
700 return true;
701 }
702 m_internal_acceptsRowEditAfterCellAccepting = false;
703
704 const int columnEditedBeforeAccepting = m_editor ? currentColumn() : -1;
705 if (!acceptEditor())
706 return false;
707 kdDebug() << "EDIT ROW ACCEPTING..." << endl;
708
709 bool success = true;
710// bool allow = true;
711// int faultyColumn = -1; // will be !=-1 if cursor has to be moved to that column
712 const bool inserting = m_newRowEditing;
713// TQString msg, desc;
714// bool inserting = d->pInsertItem && d->pInsertItem==d->pCurrentItem;
715
716 if (m_data->rowEditBuffer()->isEmpty() && !m_newRowEditing) {
717/* if (d->newRowEditing) {
718 cancelRowEdit();
719 kdDebug() << "-- NOTHING TO INSERT!!!" << endl;
720 return true;
721 }
722 else {*/
723 kdDebug() << "-- NOTHING TO ACCEPT!!!" << endl;
724// }
725 }
726 else {//not empty edit buffer or new row to insert:
727 if (m_newRowEditing) {
728// emit aboutToInsertRow(d->pCurrentItem, m_data->rowEditBuffer(), success, &faultyColumn);
729// if (success) {
730 kdDebug() << "-- INSERTING: " << endl;
731 m_data->rowEditBuffer()->debug();
732 success = m_data->saveNewRow(*m_currentItem);
733// if (!success) {
734// }
735// }
736 }
737 else {
738// emit aboutToUpdateRow(d->pCurrentItem, m_data->rowEditBuffer(), success, &faultyColumn);
739 if (success) {
740 //accept changes for this row:
741 kdDebug() << "-- UPDATING: " << endl;
742 m_data->rowEditBuffer()->debug();
743 kdDebug() << "-- BEFORE: " << endl;
744 m_currentItem->debug();
745 success = m_data->saveRowChanges(*m_currentItem);//, &msg, &desc, &faultyColumn);
746 kdDebug() << "-- AFTER: " << endl;
747 m_currentItem->debug();
748
749// if (!success) {
750// }
751 }
752 }
753 }
754
755 if (success) {
756 //editing is finished:
757 if (m_newRowEditing) {
758 //update current-item-iterator
759 m_itemIterator->toLast();
761 }
762 m_rowEditing = false;
763 m_newRowEditing = false;
764 //indicate on the vheader that we are not editing
767
768 updateAfterAcceptRowEdit();
769
770 kdDebug() << "EDIT ROW ACCEPTED:" << endl;
771// /*debug*/itemAt(m_curRow);
772
773 if (inserting) {
774// emit rowInserted(d->pCurrentItem);
775 //update navigator's data
777 }
778 else {
779// emit rowUpdated(d->pCurrentItem);
780 }
781
782 /*emit*/ rowEditTerminated(m_curRow);
783 }
784 else {
785// if (!allow) {
786// kdDebug() << "INSERT/EDIT ROW - DISALLOWED by signal!" << endl;
787// }
788// else {
789// kdDebug() << "EDIT ROW - ERROR!" << endl;
790// }
791 int faultyColumn = -1;
792 if (m_data->result()->column >= 0 && m_data->result()->column < columns())
794 else if (columnEditedBeforeAccepting >= 0)
796 if (faultyColumn >= 0) {
798 }
799
800 const int button = showErrorMessageForResult( m_data->result() );
801 if (KMessageBox::No == button) {
802 //discard changes
804 }
805 else {
806 if (faultyColumn >= 0) {
807 //edit this cell
809 }
810 }
811 }
812
813 return success;
814}
815
817{
818 if (!hasData())
819 return false;
820 if (!m_rowEditing)
821 return false;
822 cancelEditor();
823 m_rowEditing = false;
824 //indicate on the vheader that we are not editing
828 if (m_newRowEditing) {
829 m_newRowEditing = false;
830 //remove current edited row (it is @ the end of list)
831 m_data->removeLast();
832 //current item is now empty, last row
834 //update visibility
836 m_verticalHeader->removeLabel(false); //-1 label
837// updateContents(columnPos(0), rowPos(rows()),
838// viewport()->width(), d->rowHeight*3 + (m_navPanel ? m_navPanel->height() : 0)*3 );
839// updateContents(); //js: above did not work well so we do that dirty
841//TODO: still doesn't repaint properly!!
842// TQSize s(tableSize());
843// resizeContents(s.width(), s.height());
845// m_verticalHeader->update();
846 //--no cancel action is needed for datasource,
847 // because the row was not yet stored.
848 }
849
851 updateAfterCancelRowEdit();
852
854 kexidbg << "EDIT ROW CANCELLED." << endl;
855
856 /*emit*/ rowEditTerminated(m_curRow);
857 return true;
858}
859
860void KexiDataAwareObjectInterface::updateAfterCancelRowEdit()
861{
865 m_alsoUpdateNextRow = false;
866}
867
868void KexiDataAwareObjectInterface::updateAfterAcceptRowEdit()
869{
871}
872
873void KexiDataAwareObjectInterface::removeEditor()
874{
875 if (!m_editor)
876 return;
877 m_editor->hideWidget();
878 m_editor = 0;
879}
880
882{
884 m_errorMessagePopup->close();
885 }
886 if (!m_editor)
887 return false;
888 removeEditor();
889 return true;
890}
891
893class KexiDataAwareObjectInterfaceToolTip : public TQToolTip {
894 public:
895 KexiDataAwareObjectInterfaceToolTip( const TQString & text, const TQPoint & pos, TQWidget * widget )
896 : TQToolTip(widget), m_text(text)
897 {
898 tip( TQRect(pos, TQSize(100, 100)), text );
899 }
900 virtual void maybeTip(const TQPoint & p) {
901 tip( TQRect(p, TQSize(100, 100)), m_text);
902 }
903 TQString m_text;
904};
905
907{
908 if (!hasData())
909 return true;
910 if (!m_editor || m_inside_acceptEditor)
911 return true;
912
913 m_inside_acceptEditor = true;//avoid recursion
914
915 TQVariant newval;
916 Validator::Result res = Validator::Ok;
917 TQString msg, desc;
918 bool setNull = false;
919// bool allow = true;
920// static const TQString msg_NOT_NULL = i18n("\"%1\" column requires a value to be entered.");
921
922 //autoincremented field can be omitted (left as null or empty) if we're inserting a new row
923 const bool autoIncColumnCanBeOmitted = m_newRowEditing && m_editor->field()->isAutoIncrement();
924// const bool autoIncColumnCanBeOmitted = m_newRowEditing && m_editor->columnInfo()->field->isAutoIncrement();
925
926 bool valueChanged = m_editor->valueChanged();
927 bool editCurrentCellAgain = false;
928
929 if (valueChanged) {
930 if (!m_editor->valueIsValid()) {
931 //used e.g. for date or time values - the value can be null but not necessary invalid
932 res = Validator::Error;
934 TQWidget *par = dynamic_cast<TQScrollView*>(this) ? dynamic_cast<TQScrollView*>(this)->viewport() :
935 dynamic_cast<TQWidget*>(this);
936 TQWidget *edit = dynamic_cast<TQWidget*>(m_editor);
937 if (par && edit) {
940 if (!m_errorMessagePopup) {
941// m_errorMessagePopup->close();
943 i18n("Error: %1").arg(m_editor->columnInfo()->field->typeName())+"?",
944 dynamic_cast<TQWidget*>(this));
945 m_errorMessagePopup->move(
946 par->mapToGlobal(edit->pos()) + TQPoint(6, edit->height() + 0) );
947 m_errorMessagePopup->show();
948 }
949 m_editor->setFocus();
950 }
951 }
952 else if (m_editor->valueIsNull()) {//null value entered
953// if (m_editor->columnInfo()->field->isNotNull() && !autoIncColumnCanBeOmitted) {
954 if (m_editor->field()->isNotNull() && !autoIncColumnCanBeOmitted) {
955 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NULL NOT ALLOWED!" << endl;
956 res = Validator::Error;
957// msg = Validator::msgColumnNotEmpty().arg(m_editor->columnInfo()->field->captionOrName())
958 msg = Validator::msgColumnNotEmpty().arg(m_editor->field()->captionOrName())
959 + "\n\n" + Kexi::msgYouCanImproveData();
960 desc = i18n("The column's constraint is declared as NOT NULL.");
962 // allow = false;
963 // removeEditor();
964 // return true;
965 }
966 else {
967 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NULL VALUE WILL BE SET" << endl;
968 //ok, just leave newval as NULL
969 setNull = true;
970 }
971 }
972 else if (m_editor->valueIsEmpty()) {//empty value entered
973// if (m_editor->columnInfo()->field->hasEmptyProperty()) {
974 if (m_editor->field()->hasEmptyProperty()) {
975// if (m_editor->columnInfo()->field->isNotEmpty() && !autoIncColumnCanBeOmitted) {
976 if (m_editor->field()->isNotEmpty() && !autoIncColumnCanBeOmitted) {
977 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): EMPTY NOT ALLOWED!" << endl;
978 res = Validator::Error;
979// msg = Validator::msgColumnNotEmpty().arg(m_editor->columnInfo()->field->captionOrName())
980 msg = Validator::msgColumnNotEmpty().arg(m_editor->field()->captionOrName())
981 + "\n\n" + Kexi::msgYouCanImproveData();
982 desc = i18n("The column's constraint is declared as NOT EMPTY.");
984 // allow = false;
985 // removeEditor();
986 // return true;
987 }
988 else {
989 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): EMPTY VALUE WILL BE SET" << endl;
990 }
991 }
992 else {
993// if (m_editor->columnInfo()->field->isNotNull() && !autoIncColumnCanBeOmitted) {
994 if (m_editor->field()->isNotNull() && !autoIncColumnCanBeOmitted) {
995 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NEITHER NULL NOR EMPTY VALUE CAN BE SET!" << endl;
996 res = Validator::Error;
997// msg = Validator::msgColumnNotEmpty().arg(m_editor->columnInfo()->field->captionOrName())
998 msg = Validator::msgColumnNotEmpty().arg(m_editor->field()->captionOrName())
999 + "\n\n" + Kexi::msgYouCanImproveData();
1000 desc = i18n("The column's constraint is declared as NOT EMPTY and NOT NULL.");
1001 editCurrentCellAgain = true;
1002// allow = false;
1003 // removeEditor();
1004 // return true;
1005 }
1006 else {
1007 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NULL VALUE WILL BE SET BECAUSE EMPTY IS NOT ALLOWED" << endl;
1008 //ok, just leave newval as NULL
1009 setNull = true;
1010 }
1011 }
1012 }
1013 }//changed
1014
1015 const int realFieldNumber = fieldNumberForColumn(m_curCol);
1016 if (realFieldNumber < 0) {
1017 kdWarning() << "KexiDataAwareObjectInterface::acceptEditor(): fieldNumberForColumn(m_curCol) < 0" << endl;
1018 m_inside_acceptEditor = false;
1019 return false;
1020 }
1021
1023
1024 //try to get the value entered:
1025 if (res == Validator::Ok) {
1026 if ((!setNull && !valueChanged)
1027 || (m_editor->field()->type()!=KexiDB::Field::Boolean && setNull && m_currentItem->at( realFieldNumber ).isNull())) {
1028 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): VALUE NOT CHANGED." << endl;
1029 removeEditor();
1030 if (m_acceptsRowEditAfterCellAccepting || m_internal_acceptsRowEditAfterCellAccepting)
1031 acceptRowEdit();
1032 m_inside_acceptEditor = false;
1033 return true;
1034 }
1035 if (!setNull) {//get the new value
1036// bool ok;
1037 newval = m_editor->value();
1039/*
1040 if (!ok) {
1041 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): INVALID VALUE - NOT CHANGED." << endl;
1042 res = KexiValidator::Error;
1043//js: TODO get detailed info on why m_editor->value() failed
1044 msg = i18n("Entered value is invalid.")
1045 + "\n\n" + KexiValidator::msgYouCanImproveData();
1046 editCurrentCellAgain = true;
1047// removeEditor();
1048// return true;
1049 }*/
1050 }
1051
1052 //Check other validation rules:
1053 //1. check using validator
1054// KexiValidator *validator = m_data->column(m_curCol)->validator();
1055 Validator *validator = currentTVColumn->validator();
1056 if (validator) {
1057// res = validator->check(m_data->column(m_curCol)->field()->captionOrName(),
1058 res = validator->check(currentTVColumn->field()->captionOrName(),
1059 newval, msg, desc);
1060 }
1061 }
1062
1063 //show the validation result if not OK:
1064 if (res == Validator::Error) {
1065 if (!msg.isEmpty()) {
1066 if (desc.isEmpty())
1067 KMessageBox::sorry(dynamic_cast<TQWidget*>(this), msg);
1068 else
1069 KMessageBox::detailedSorry(dynamic_cast<TQWidget*>(this), msg, desc);
1070 }
1071 editCurrentCellAgain = true;
1072// allow = false;
1073 }
1074 else if (res == Validator::Warning) {
1075 //js: todo: message!!!
1076 KMessageBox::messageBox(dynamic_cast<TQWidget*>(this), KMessageBox::Sorry, msg + "\n" + desc);
1077 editCurrentCellAgain = true;
1078 }
1079
1080 if (res == Validator::Ok) {
1081 //2. check using signal
1082 //bool allow = true;
1083// emit aboutToChangeCell(d->pCurrentItem, newval, allow);
1084// if (allow) {
1085 //send changes to the backend
1086 TQVariant visibleValue;
1087 if (!newval.isNull()/* visible value should be null if value is null */
1088 && currentTVColumn->visibleLookupColumnInfo)
1089 {
1090 visibleValue = m_editor->visibleValue(); //visible value for lookup field
1091 }
1092 //should be also added to the buffer
1094 newval, /*allowSignals*/true, currentTVColumn->visibleLookupColumnInfo ? &visibleValue : 0))
1095 {
1096 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): ------ EDIT BUFFER CHANGED TO:" << endl;
1097 m_data->rowEditBuffer()->debug();
1098 } else {
1099 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): ------ CHANGE FAILED in KexiDataAwareObjectInterface::updateRowEditBuffer()" << endl;
1100 res = Validator::Error;
1101
1102 //now: there might be called cancelEditor() in updateRowEditBuffer() handler,
1103 //if this is true, d->pEditor is NULL.
1104
1105 if (m_editor && m_data->result()->column>=0 && m_data->result()->column<columns()) {
1106 //move to faulty column (if m_editor is not cleared)
1108 }
1109 if (!m_data->result()->msg.isEmpty()) {
1110 const int button = showErrorMessageForResult( m_data->result() );
1111 if (KMessageBox::No == button) {
1112 //discard changes
1113 cancelEditor();
1115 cancelRowEdit();
1116 m_inside_acceptEditor = false;
1117 return false;
1118 }
1119 }
1120 }
1121 }
1122
1123 if (res == Validator::Ok) {
1124 removeEditor();
1125 /*emit*/ itemChanged(m_currentItem, m_curRow, m_curCol,
1127 /*emit*/ itemChanged(m_currentItem, m_curRow, m_curCol);
1128 }
1129 m_inside_acceptEditor = false;
1130 if (res == Validator::Ok) {
1131 if (m_acceptsRowEditAfterCellAccepting || m_internal_acceptsRowEditAfterCellAccepting)
1132 acceptRowEdit();
1133 return true;
1134 }
1135 if (m_editor) {
1136 //allow to edit the cell again, (if m_pEditor is not cleared)
1137
1138 if (m_editor->hasFocusableWidget()) {
1139 m_editor->showWidget();
1140 m_editor->setFocus();
1141 }
1142// startEditCurrentCell(newval.type()==TQVariant::String ? newval.toString() : TQString());
1143// m_editor->setFocus();
1144 }
1145 return false;
1146}
1147
1149{
1150 kdDebug() << "** KexiDataAwareObjectInterface::startEditCurrentCell("<<setText<<")"<<endl;
1151// if (columnType(d->curCol) == KexiDB::Field::Boolean)
1152// return;
1153 if (isReadOnly() || !columnEditable(m_curCol))
1154 return;
1155 if (m_editor) {
1156 if (m_editor->hasFocusableWidget()) {
1157 m_editor->showWidget();
1158 m_editor->setFocus();
1159 }
1160 }
1161// ensureVisible(columnPos(m_curCol), rowPos(m_curRow)+rowHeight(),
1162// columnWidth(m_curCol), rowHeight());
1163//OK?
1164 //ensureCellVisible(m_curRow+1, m_curCol);
1165 if (!m_editor)
1166 createEditor(m_curRow, m_curCol, setText, !setText.isEmpty());
1167}
1168
1170{
1171 if (isReadOnly() || !columnEditable(m_curCol))
1172 return;
1173 if (m_editor) {//if we've editor - just clear it
1174 m_editor->clear();
1175 return;
1176 }
1177//js if (columnType(m_curCol) == KexiDB::Field::Boolean)
1178//js return;
1179// ensureVisible(columnPos(m_curCol), rowPos(m_curRow) + rowHeight(),
1180// columnWidth(m_curCol), rowHeight());
1181//OK?
1182 ensureCellVisible(m_curRow+1, m_curCol);
1183 createEditor(m_curRow, m_curCol, TQString(), false/*removeOld*/);
1184 if (!m_editor)
1185 return;
1186 m_editor->clear();
1187 if (m_editor->acceptEditorAfterDeleteContents())
1188 acceptEditor();
1189 if (!m_editor || !m_editor->hasFocusableWidget())
1190 updateCell(m_curRow, m_curCol);
1191}
1192
1194{
1195 if (m_newRowEditing) {//we're editing fresh new row: just cancel this!
1196 cancelRowEdit();
1197 return;
1198 }
1199
1200 if (!acceptRowEdit())
1201 return;
1202
1204 return;
1205 switch (m_deletionPolicy) {
1206 case NoDelete:
1207 return;
1208 case ImmediateDelete:
1209 break;
1210 case AskDelete:
1211 if (KMessageBox::Cancel == KMessageBox::warningContinueCancel(dynamic_cast<TQWidget*>(this),
1212 i18n("Do you want to delete selected row?"), 0,
1213 KGuiItem(i18n("&Delete Row"),"edit-delete"),
1214 "dontAskBeforeDeleteRow"/*config entry*/,
1215 KMessageBox::Notify|KMessageBox::Dangerous))
1216 return;
1217 break;
1218 case SignalDelete:
1219 /*emit*/ itemDeleteRequest(m_currentItem, m_curRow, m_curCol);
1220 /*emit*/ currentItemDeleteRequest();
1221 return;
1222 default:
1223 return;
1224 }
1225
1226 if (!deleteItem(m_currentItem)) {//nothing
1227 }
1228}
1229
1231{
1233 || (row!=-1 && row >= ((int)rows()+(isInsertingEnabled()?1:0) ) ) )
1234 return 0;
1235
1236 KexiTableItem *newItem = m_data->createItem(); //new KexiTableItem(m_data->columns.count());
1237 insertItem(newItem, row);
1238 return newItem;
1239}
1240
1241void KexiDataAwareObjectInterface::insertItem(KexiTableItem *newItem, int row)
1242{
1243 const bool changeCurrentRow = row==-1 || row==m_curRow;
1244 if (changeCurrentRow) {
1245 //change current row
1246 row = (m_curRow >= 0 ? m_curRow : 0);
1247 m_currentItem = newItem;
1248 m_curRow = row;
1249 }
1250 else if (m_curRow >= row) {
1251 m_curRow++;
1252 }
1253
1254 m_data->insertRow(*newItem, row, true /*repaint*/);
1255
1256 if (changeCurrentRow) {
1257 //update iter...
1258 m_itemIterator->toFirst();
1259 (*m_itemIterator)+=m_curRow;
1260 }
1261/*
1262 TQSize s(tableSize());
1263 resizeContents(s.width(),s.height());
1264
1265 //redraw only this row and below:
1266 int leftcol = d->pTopHeader->sectionAt( d->pTopHeader->offset() );
1267// updateContents( columnPos( leftcol ), rowPos(d->curRow),
1268// clipper()->width(), clipper()->height() - (rowPos(d->curRow) - contentsY()) );
1269 updateContents( columnPos( leftcol ), rowPos(row),
1270 clipper()->width(), clipper()->height() - (rowPos(row) - contentsY()) );
1271
1272 m_verticalHeader->addLabel();
1273
1274 //update navigator's data
1275 setNavRowCount(rows());
1276
1277 if (d->curRow >= row) {
1278 //update
1279 editorShowFocus( d->curRow, d->curCol );
1280 }
1281 */
1282}
1283
1285{
1286 int row = m_data->findRef(item);
1287 slotRowInserted( item, row, repaint );
1288}
1289
1290void KexiDataAwareObjectInterface::slotRowInserted(KexiTableItem * /*item*/, uint row, bool repaint)
1291{
1292 if (repaint && (int)row<rows()) {
1294
1295/* updateAllVisibleRowsBelow() used instead
1296 //redraw only this row and below:
1297 int leftcol = d->pTopHeader->sectionAt( d->pTopHeader->offset() );
1298 updateContents( columnPos( leftcol ), rowPos(row),
1299 clipper()->width(), clipper()->height() - (rowPos(row) - contentsY()) );
1300*/
1302
1304 if (m_verticalHeader)
1305 m_verticalHeader->addLabel();
1306 }
1307 else //it was added because this inserting was interactive
1309
1310 //update navigator's data
1312
1313 if (m_curRow >= (int)row) {
1314 //update
1315 editorShowFocus( m_curRow, m_curCol );
1316 }
1317 }
1318}
1319
1321{
1322 if (!hasData())
1323 return true;
1324 if (m_data->count()<1)
1325 return true;
1326
1327 if (ask) {
1328 TQString tableName = m_data->dbTableName();
1329 if (!tableName.isEmpty()) {
1330 tableName.prepend(" \"");
1331 tableName.append("\"");
1332 }
1333 if (KMessageBox::Cancel == KMessageBox::warningContinueCancel(dynamic_cast<TQWidget*>(this),
1334 i18n("Do you want to clear the contents of table %1?").arg(tableName),
1335 0, KGuiItem(i18n("&Clear Contents")) ))
1336 return cancelled;
1337 }
1338
1339 cancelRowEdit();
1340// acceptRowEdit();
1341// m_verticalHeader->clear();
1342 const bool repaintLater = repaint && m_spreadSheetMode;
1343 const int oldRows = rows();
1344
1346
1347 if (res) {
1348 if (m_spreadSheetMode) {
1349// const uint columns = m_data->columns.count();
1350 for (int i=0; i<oldRows; i++) {
1351 m_data->append(m_data->createItem());//new KexiTableItem(columns));
1352 }
1353 }
1354 }
1355 if (repaintLater)
1356 m_data->reload();
1357
1358// d->clearVariables();
1359// m_verticalHeader->setCurrentRow(-1);
1360
1361// d->pUpdateTimer->start(1,true);
1362// if (repaint)
1363// viewport()->repaint();
1364 return res;
1365}
1366
1368{
1369 cancelRowEdit();
1370 m_data->clearInternal();
1371
1374
1375 if (repaint)
1376// viewport()->repaint();
1377//OK?
1379
1380/* for(int i=0; i < rows(); i++)
1381 {
1382 m_verticalHeader->removeLabel();
1383 }
1384
1385 editorCancel();
1386 m_contents->clear();
1387
1388 d->clearVariables();
1389 d->numCols = 0;
1390
1391 while(d->pTopHeader->count()>0)
1392 d->pTopHeader->removeLabel(0);
1393
1394 m_verticalHeader->setCurrentRow(-1);
1395
1396 viewport()->repaint();
1397
1398// d->pColumnTypes.resize(0);
1399// d->pColumnModes.resize(0);
1400// d->pColumnDefaults.clear();*/
1401}
1402
1404{
1405// cancelRowEdit();
1406 acceptRowEdit();
1407 if (m_verticalHeader)
1408 m_verticalHeader->clear();
1409
1410 if (m_curCol>=0 && m_curCol<columns()) {
1411 //find the editor for this column
1412 KexiDataItemInterface *edit = editor( m_curCol );
1413 if (edit) {
1414 edit->hideFocus();
1415 }
1416 }
1417// setCursorPosition(-1, -1, true);
1419 if (m_verticalHeader)
1420 m_verticalHeader->setCurrentRow(-1);
1421
1422 if (dynamic_cast<TQWidget*>(this) && dynamic_cast<TQWidget*>(this)->isVisible())
1424 else
1426
1427 if (m_verticalHeader)
1429
1431}
1432
1434{
1436 return c ? c->field()->type() : KexiDB::Field::InvalidType;
1437}
1438
1440{
1442 return c ? (! c->isReadOnly()) : false;
1443}
1444
1446{
1447 if (!hasData())
1448 return 0;
1449 return m_data->count();
1450}
1451
1453{
1454 if (!hasData())
1455 return 0;
1456 return m_data->columns.count();
1457}
1458
1460{
1461 return TQVariant(0);
1462//TODO(js)
1463// return m_data->columns[col].defaultValue;
1464}
1465
1470
1472{
1473// const bool old = d->dropsAtRowEnabled;
1474 if (!set)
1476 if (m_dropsAtRowEnabled && !set) {
1477 m_dropsAtRowEnabled = false;
1478// update();
1480 }
1481 else {
1482 m_dropsAtRowEnabled = set;
1483 }
1484}
1485
1491
1493 KexiDB::ResultInfo* /*result*/, bool repaint)
1494{
1495 if (repaint) {
1496 m_rowWillBeDeleted = m_data->findRef(&item);
1497 }
1498}
1499
1501{
1502 if (m_rowWillBeDeleted >= 0) {
1504 m_rowWillBeDeleted = rows()-1; //move up if it's the last row
1506
1507 if (! (m_spreadSheetMode && m_rowWillBeDeleted>=(rows()-1)))
1508 setCursorPosition(m_rowWillBeDeleted, m_curCol, true/*forceSet*/);
1509 if (m_verticalHeader)
1510 m_verticalHeader->removeLabel();
1511
1512 updateAllVisibleRowsBelow(m_curRow); //needed for KexiTableView
1513
1514 //update navigator's data
1516
1517 m_rowWillBeDeleted = -1;
1518 }
1519}
1520
1522{
1523 //always return
1524 return true;
1525}
1526
1527bool KexiDataAwareObjectInterface::deleteItem(KexiTableItem *item)/*, bool moveCursor)*/
1528{
1529 if (!item || !beforeDeleteItem(item))
1530 return false;
1531
1532 TQString msg, desc;
1533// bool current = (item == d->pCurrentItem);
1534 const bool lastRowDeleted = m_spreadSheetMode && m_data->last() == item; //we need to know this so we
1535 //can return to the last row
1536 //after reinserting it
1537 if (!m_data->deleteRow(*item, true /*repaint*/)) {
1538 /*const int button =*/
1540// if (KMessageBox::No == button) {
1541 //discard changes
1542 // }
1543 return false;
1544 }
1545 else {
1546//setCursorPosition() wil lset this! if (current)
1547 //d->pCurrentItem = m_data->current();
1548 }
1549
1550// repaintAfterDelete();
1551 if (m_spreadSheetMode) { //append empty row for spreadsheet mode
1552 m_data->append(m_data->createItem());//new KexiTableItem(m_data->columns.count()));
1553 if (m_verticalHeader)
1555 if (lastRowDeleted) //back to the last row
1556 setCursorPosition(rows()-1, m_curCol, true/*forceSet*/);
1558 }
1559 return true;
1560}
1561
1566
1567bool KexiDataAwareObjectInterface::hasDefaultValueAt(const KexiTableViewColumn& tvcol)
1568{
1569 if (m_rowEditing && m_data->rowEditBuffer() && m_data->rowEditBuffer()->isDBAware()) {
1570 return m_data->rowEditBuffer()->hasDefaultValueAt( *tvcol.columnInfo );
1571 }
1572 return false;
1573}
1574
1575const TQVariant* KexiDataAwareObjectInterface::bufferedValueAt(int col, bool useDefaultValueIfPossible)
1576{
1577 if (m_rowEditing && m_data->rowEditBuffer())
1578 {
1580 if (tvcol->isDBAware) {
1581 //get the stored value
1583 if (realFieldNumber < 0) {
1584 kdWarning() << "KexiDataAwareObjectInterface::bufferedValueAt(): "
1585 "fieldNumberForColumn(m_curCol) < 0" << endl;
1586 return 0;
1587 }
1588 TQVariant *storedValue = &m_currentItem->at( realFieldNumber );
1589
1590 //db-aware data: now, try to find a buffered value (or default one)
1591 const TQVariant *cv = m_data->rowEditBuffer()->at( *tvcol->columnInfo,
1593 if (cv)
1594 return cv;
1595 return storedValue;
1596 }
1597 //not db-aware data:
1598 const TQVariant *cv = m_data->rowEditBuffer()->at( tvcol->field()->name() );
1599 if (cv)
1600 return cv;
1601 }
1602 //not db-aware data:
1604 if (realFieldNumber < 0) {
1605 kdWarning() << "KexiDataAwareObjectInterface::bufferedValueAt(): "
1606 "fieldNumberForColumn(m_curCol) < 0" << endl;
1607 return 0;
1608 }
1609 return &m_currentItem->at( realFieldNumber );
1610}
1611
1613{
1614 if ( !isReadOnly() && columnEditable(m_curCol) ) {
1615 if (columnType(m_curCol) == KexiDB::Field::Boolean) {
1616 boolToggled();
1617 }
1618 else {
1620 return;
1621 }
1622 }
1623}
1624
1626{
1628 if (m_editor) {
1629 m_editor->clickedOnContents();
1630 }
1631 acceptEditor();
1632 updateCell(m_curRow, m_curCol);
1633
1634/* int s = m_currentItem->at(m_curCol).toInt();
1635 TQVariant oldValue=m_currentItem->at(m_curCol);
1636 (*m_currentItem)[m_curCol] = TQVariant(s ? 0 : 1);
1637 updateCell(m_curRow, m_curCol);
1638// emit itemChanged(m_currentItem, m_curRow, m_curCol, oldValue);
1639// emit itemChanged(m_currentItem, m_curRow, m_curCol);*/
1640}
1641
1642void KexiDataAwareObjectInterface::slotDataDestroying()
1643{
1644 m_data = 0;
1645 m_itemIterator = 0;
1646}
1647
1648void KexiDataAwareObjectInterface::addNewRecordRequested()
1649{
1650 if (!isInsertingEnabled())
1651 return;
1652 if (m_rowEditing) {
1653 if (!acceptRowEdit())
1654 return;
1655 }
1656// setFocus();
1657 selectRow(rows());
1659 if (m_editor)
1660 m_editor->setFocus();
1661}
1662
1664 bool fullRowSelection, bool *moveToFirstField, bool *moveToLastField)
1665{
1666 if (moveToFirstField)
1667 *moveToFirstField = false;
1668 if (moveToLastField)
1669 *moveToLastField = false;
1670
1671 const bool nobtn = e->state()==TQt::NoButton;
1672 const int k = e->key();
1673 //kdDebug() << "-----------" << e->state() << " " << k << endl;
1674
1675 if ((k == TQt::Key_Up && nobtn) || (k == TQt::Key_PageUp && e->state()==TQt::ControlButton)) {
1676 selectPrevRow();
1677 e->accept();
1678 }
1679 else if ((k == TQt::Key_Down && nobtn) || (k == TQt::Key_PageDown && e->state()==TQt::ControlButton)) {
1680 selectNextRow();
1681 e->accept();
1682 }
1683 else if (k == TQt::Key_PageUp && nobtn) {
1685 e->accept();
1686 }
1687 else if (k == TQt::Key_PageDown && nobtn) {
1689 e->accept();
1690 }
1691 else if (k == TQt::Key_Home) {
1692 if (fullRowSelection) {
1693 //we're in row-selection mode: home key always moves to 1st row
1694 curRow = 0;//to 1st row
1695 }
1696 else {//cell selection mode: different actions depending on ctrl and shift keys state
1697 if (nobtn) {
1698 curCol = 0;//to 1st col
1699 }
1700 else if (e->state()==TQt::ControlButton) {
1701 curRow = 0;//to 1st row and col
1702 curCol = 0;
1703 }
1704 else
1705 return false;
1706 }
1707 if (moveToFirstField)
1708 *moveToFirstField = true;
1709 //do not accept yet
1710 e->ignore();
1711 }
1712 else if (k == TQt::Key_End) {
1713 if (fullRowSelection) {
1714 //we're in row-selection mode: home key always moves to last row
1715 curRow = m_data->count()-1+(isInsertingEnabled()?1:0);//to last row
1716 }
1717 else {//cell selection mode: different actions depending on ctrl and shift keys state
1718 if (nobtn) {
1719 curCol = columns()-1;//to last col
1720 }
1721 else if (e->state()==TQt::ControlButton) {
1722 curRow = m_data->count()-1 /*+(isInsertingEnabled()?1:0)*/; //to last row and col
1723 curCol = columns()-1;//to last col
1724 }
1725 else
1726 return false;
1727 }
1728 if (moveToLastField)
1729 *moveToLastField = true;
1730 //do not accept yet
1731 e->ignore();
1732 }
1733 else if (isInsertingEnabled() && (e->state()==TQt::ControlButton && k == TQt::Key_Equal
1734 || e->state()==(TQt::ControlButton|TQt::ShiftButton) && k == TQt::Key_Equal)) {
1735 curRow = m_data->count(); //to the new row
1736 curCol = 0;//to first col
1737 if (moveToFirstField)
1738 *moveToFirstField = true;
1739 //do not accept yet
1740 e->ignore();
1741 }
1742 else
1743 return false;
1744
1745 return true;
1746}
1747
1749{
1750 Q_UNUSED(v);
1752 return;
1753
1755 const TQRect r( verticalScrollBar()->sliderRect() );
1756 const int row = lastVisibleRow()+1;
1757 if (row<=0) {
1758 m_scrollBarTipTimer.stop();
1759 m_scrollBarTip->hide();
1760 return;
1761 }
1762 m_scrollBarTip->setText( i18n("Row: ") + TQString::number(row) );
1763 m_scrollBarTip->adjustSize();
1764 TQWidget* thisWidget = dynamic_cast<TQWidget*>(this);
1765 m_scrollBarTip->move(
1766 thisWidget->mapToGlobal( r.topLeft() + verticalScrollBar()->pos() )
1767 + TQPoint( - m_scrollBarTip->width()-5,
1768 r.height()/2 - m_scrollBarTip->height()/2) );
1769 if (verticalScrollBar()->draggingSlider()) {
1770 kdDebug(44021) << " draggingSlider() " << endl;
1771 m_scrollBarTipTimer.stop();
1772 m_scrollBarTip->show();
1773 m_scrollBarTip->raise();
1774 }
1775 else {
1777 if (m_scrollBarTipTimerCnt>4) {
1779 m_scrollBarTip->show();
1780 m_scrollBarTip->raise();
1781 m_scrollBarTipTimer.start(500, true);
1782 }
1783 }
1784 }
1785 //update bottom view region
1786/* if (m_navPanel && (contentsHeight() - contentsY() - clipper()->height()) <= TQMAX(d->rowHeight,m_navPanel->height())) {
1787 slotUpdate();
1788 triggerUpdate();
1789 }*/
1790}
1791
1796
1801
1803{
1804 kdDebug(44021) << "vScrollBarSliderReleased()" << endl;
1805 m_scrollBarTip->hide();
1806}
1807
1809{
1810 if (m_scrollBarTip->isVisible()) {
1811// kdDebug(44021) << "TIMEOUT! - hide" << endl;
1812 if (m_scrollBarTipTimerCnt>0) {
1814 m_scrollBarTipTimer.start(500, true);
1815 return;
1816 }
1817 m_scrollBarTip->hide();
1818 }
1820}
1821
1823{
1824 Q_UNUSED(e);
1825 m_scrollBarTipTimer.stop();
1826 m_scrollBarTip->hide();
1827
1828 updateCell(m_curRow, m_curCol);
1829}
1830
1832{
1833 TQWidget *thisWidget = dynamic_cast<TQWidget*>(this);
1834 if (resultInfo->allowToDiscardChanges) {
1835 return KMessageBox::questionYesNo(thisWidget, resultInfo->msg
1836 + (resultInfo->desc.isEmpty() ? TQString() : ("\n"+resultInfo->desc)),
1837 TQString(),
1838 KGuiItem(i18n("Correct Changes", "Correct"), TQString(), i18n("Correct changes")),
1839 KGuiItem(i18n("Discard Changes")) );
1840 }
1841
1842 if (resultInfo->desc.isEmpty())
1843 KMessageBox::sorry(thisWidget, resultInfo->msg);
1844 else
1845 KMessageBox::detailedSorry(thisWidget, resultInfo->msg, resultInfo->desc);
1846
1847 return KMessageBox::Ok;
1848}
1849
1851{
1852 m_indicesForVisibleValues.resize( m_data ? m_data->columnsCount() : 0 );
1853 if (!m_data)
1854 return;
1855 for (uint i=0; i < m_data->columnsCount(); i++) {
1856 KexiTableViewColumn* tvCol = m_data->column(i);
1857 if (tvCol->columnInfo && tvCol->columnInfo->indexForVisibleLookupValue()!=-1)
1858 // retrieve visible value from lookup field
1860 else
1862 }
1863}
1864
1876static inline bool findInString(const TQString& stringValue, int stringLength, const TQString& where,
1877 int& firstCharacter, bool matchAnyPartOfField, bool matchWholeField,
1878 bool caseSensitive, bool wholeWordsOnly, bool forward)
1879{
1880 if (where.isEmpty()) {
1881 firstCharacter = -1;
1882 return false;
1883 }
1884
1885 if (matchAnyPartOfField) {
1886 if (forward) {
1887 int pos = firstCharacter == -1 ? 0 : firstCharacter;
1888 if (wholeWordsOnly) {
1889 const int whereLength = where.length();
1890 while (true) {
1891 pos = where.find( stringValue, pos, caseSensitive );
1892 if (pos == -1)
1893 break;
1894 if ((pos > 0 && where.at(pos-1).isLetterOrNumber())
1895 ||((pos+stringLength-1) < (whereLength-1) && where.at(pos+stringLength-1+1).isLetterOrNumber()))
1896 {
1897 pos++; // invalid match because before or after the string there is non-white space
1898 }
1899 else
1900 break;
1901 }//while
1902 firstCharacter = pos;
1903 }
1904 else {// !wholeWordsOnly
1905 firstCharacter = where.find( stringValue, pos, caseSensitive );
1906 }
1907 return firstCharacter != -1;
1908 }
1909 else { // !matchAnyPartOfField
1910 if (firstCharacter == INT_MAX) {
1911 firstCharacter = -1; //next time we'll be looking at different cell
1912 return false;
1913 }
1914 int pos = firstCharacter;
1915 if (wholeWordsOnly) {
1916 const int whereLength = where.length();
1917 while (true) {
1918 pos = where.findRev( stringValue, pos, caseSensitive );
1919 if (pos == -1)
1920 break;
1921 if ((pos > 0 && where.at(pos-1).isLetterOrNumber())
1922 ||((pos+stringLength-1) < (whereLength-1) && where.at(pos+stringLength-1+1).isLetterOrNumber()))
1923 {
1924 // invalid match because before or after the string there is non-white space
1925 pos--;
1926 if (pos < 0) // it can make pos < 0
1927 break;
1928 }
1929 else
1930 break;
1931 }//while
1932 firstCharacter = pos;
1933 }
1934 else {// !wholeWordsOnly
1935 firstCharacter = where.findRev( stringValue, pos, caseSensitive );
1936 }
1937 return firstCharacter != -1;
1938 }
1939 }
1940 else if (matchWholeField) {
1941 if (firstCharacter != -1 && firstCharacter != 0) { //we're not at 0-th char
1942 firstCharacter = -1;
1943 }
1944 else if ( (caseSensitive ? where : where.lower()) == stringValue) {
1945 firstCharacter = 0;
1946 return true;
1947 }
1948 }
1949 else {// matchStartOfField
1950 if (firstCharacter != -1 && firstCharacter != 0) { //we're not at 0-th char
1951 firstCharacter = -1;
1952 }
1953 else if (where.startsWith(stringValue, caseSensitive)) {
1954 if (wholeWordsOnly) {
1955 // If where.length() < stringValue.length(), true will be returned too - fine.
1956 return !where.at( stringValue.length() ).isLetterOrNumber();
1957 }
1958 firstCharacter = 0;
1959 return true;
1960 }
1961 }
1962 return false;
1963}
1964
1965tristate KexiDataAwareObjectInterface::find(const TQVariant& valueToFind,
1966 const KexiSearchAndReplaceViewInterface::Options& options, bool next)
1967{
1968 if (!hasData())
1969 return cancelled;
1970 const TQVariant prevSearchedValue( m_recentlySearchedValue );
1971 m_recentlySearchedValue = valueToFind;
1974 if (valueToFind.isNull() || valueToFind.toString().isEmpty())
1975 return cancelled;
1976
1978 ? !next : next; //direction can be reversed
1979
1980 if ((!prevSearchedValue.isNull() && prevSearchedValue!=valueToFind)
1982 {
1983 // restart searching when value has been changed or new direction is SearchAllRows
1984 m_positionOfRecentlyFoundValue.exists = false;
1985 }
1986
1987 const bool startFrom1stRowAndCol = !m_positionOfRecentlyFoundValue.exists && next
1989 const bool startFromLastRowAndCol =
1991 ||(m_curRow >= rows() && !forward); //we're at "insert" row, and searching backwards: move to the last cell
1992
1994 //we're at "insert" row, and searching forward: no chances to find something
1995 return false;
1996 }
1997 KexiTableViewData::Iterator it( (startFrom1stRowAndCol || startFromLastRowAndCol)
1998 ? m_data->iterator() : *m_itemIterator /*start from the current cell*/ );
2000 it.toLast();
2001 int firstCharacter;
2002 if (m_positionOfRecentlyFoundValue.exists) {// start after the next/prev char position
2003 if (forward)
2004 firstCharacter = m_positionOfRecentlyFoundValue.lastCharacter + 1;
2005 else {
2006 firstCharacter = (m_positionOfRecentlyFoundValue.firstCharacter > 0) ?
2007 (m_positionOfRecentlyFoundValue.firstCharacter - 1) : INT_MAX /* this means 'before first'*/;
2008 }
2009 }
2010 else {
2011 firstCharacter = -1; //forward ? -1 : INT_MAX;
2012 }
2013
2014 const int columnsCount = m_data->columnsCount();
2015 int row, col;
2017 row = 0;
2018 col = 0;
2019 }
2020 else if (startFromLastRowAndCol) {
2021 row = rows()-1;
2022 col = columnsCount-1;
2023 }
2024 else {
2025 row = m_curRow;
2026 col = m_curCol;
2027 }
2028
2029 //sache some flags for efficiency
2030 const bool matchAnyPartOfField
2032 const bool matchWholeField
2034 const bool caseSensitive = options.caseSensitive;
2035 const bool wholeWordsOnly = options.wholeWordsOnly;
2036//unused const bool promptOnReplace = options.promptOnReplace;
2038 ? m_curCol : options.columnNumber;
2039 if (columnNumber>=0)
2040 col = columnNumber;
2042 int firstColumn; // real number of the first column, can be smaller than lastColumn if forward==true
2043 int lastColumn; // real number of the last column
2044 if (lookInAllColumns) {
2045 firstColumn = forward ? 0 : columnsCount-1;
2046 lastColumn = forward ? columnsCount-1 : 0;
2047 }
2048 else {
2049 firstColumn = columnNumber;
2050 lastColumn = columnNumber;
2051 }
2052 const TQString stringValue( caseSensitive ? valueToFind.toString() : valueToFind.toString().lower() );
2053 const int stringLength = stringValue.length();
2054
2055 // search
2056 const int prevRow = m_curRow;
2057 KexiTableItem *item;
2058 while ( (item = it.current()) ) {
2060 col = forward ? (col+1) : (col-1))
2061 {
2062 const TQVariant cell( item->at( m_indicesForVisibleValues[ col ] ) );
2063 if (findInString(stringValue, stringLength, cell.toString(), firstCharacter,
2064 matchAnyPartOfField, matchWholeField, caseSensitive, wholeWordsOnly, forward))
2065 {
2066 //*m_itemIterator = it;
2067 //m_currentItem = *it;
2068 //m_curRow = row;
2069 //m_curCol = col;
2070 setCursorPosition(row, col, true/*forceSet*/);
2071 if (prevRow != m_curRow)
2073 // remember the exact position for the found value
2074 m_positionOfRecentlyFoundValue.exists = true;
2075 m_positionOfRecentlyFoundValue.firstCharacter = firstCharacter;
2077 m_positionOfRecentlyFoundValue.lastCharacter = firstCharacter + stringLength - 1;
2078 return true;
2079 }
2080 }//for
2081 if (forward) {
2082 ++it;
2083 ++row;
2084 }
2085 else {
2086 --it;
2087 --row;
2088 }
2089 col = firstColumn;
2090 }//while
2091 return false;
2092}
2093
2095 const TQVariant& valueToFind, const TQVariant& replacement,
2096 const KexiSearchAndReplaceViewInterface::Options& options, bool replaceAll)
2097{
2099 Q_UNUSED(options);
2100 Q_UNUSED(replaceAll);
2101
2102 if (isReadOnly())
2103 return cancelled;
2104 if (valueToFind.isNull() || valueToFind.toString().isEmpty())
2105 return cancelled;
2107 return false;
2108}
A tooltip-like widget with additional arrow.
Meta-data for a field.
Definition field.h:73
bool isNotNull() const
Definition field.h:243
Type type() const
Definition field.cpp:134
@ InvalidType
Definition field.h:84
TQString captionOrName() const
Definition field.h:374
bool hasEmptyProperty() const
Definition field.h:388
bool isAutoIncrement() const
Definition field.h:231
static TQString typeName(uint type)
Definition field.cpp:173
bool isNotEmpty() const
Definition field.h:246
TQString description() const
Definition field.h:377
uint width() const
Definition field.h:381
int indexForVisibleLookupValue() const
Definition queryschema.h:75
int column
faulty column, -1 (the default) means: there is no faulty column
Definition error.h:132
const TQVariant * at(QueryColumnInfo &ci, bool useDefaultValueIfPossible=true) const
bool hasDefaultValueAt(QueryColumnInfo &ci) const
Useful only for db-aware buffer:
void debug()
For debugging purposes.
virtual void slotRowInserted(KexiTableItem *item, bool repaint)
Handles KexiTableViewData::rowInserted() signal to repaint when needed.
virtual void updateWidgetContents()=0
bool m_alsoUpdateNextRow
Used by updateAfterCancelRowEdit()
KexiTableViewData * data() const
virtual void setSorting(int col, bool ascending=true)
virtual void setCursorPosition(int row, int col=-1, bool forceSet=false)
virtual tristate find(const TQVariant &valueToFind, const KexiSearchAndReplaceViewInterface::Options &options, bool next)
void insertItem(KexiTableItem *newItem, int row=-1)
KexiTableItem * m_insertItem
item data for inserting
TQLabel * m_scrollBarTip
scrollbar tooltip
virtual void rowEditTerminated(int row)=0
virtual tristate findNextAndReplace(const TQVariant &valueToFind, const TQVariant &replacement, const KexiSearchAndReplaceViewInterface::Options &options, bool replaceAll)
virtual TQScrollBar * verticalScrollBar() const =0
uint m_scrollBarTipTimerCnt
helper for timeout counting (scrollbar tooltip)
virtual void ensureCellVisible(int row, int col)=0
virtual void selectNextPage()
page down action
bool m_rowEditing
when (current or new) row is edited - changed field values are temporary stored here
virtual void clearColumnsInternal(bool repaint)=0
virtual bool handleKeyPress(TQKeyEvent *e, int &curRow, int &curCol, bool fullRowSelection, bool *moveToFirstField=0, bool *moveToLastField=0)
void setData(KexiTableViewData *data, bool owner=true)
virtual void setAcceptsRowEditAfterCellAccepting(bool set)
virtual void updateRow(int row)=0
virtual KexiTableViewColumn * column(int col)
TQGuardedPtr< KexiArrowTip > m_errorMessagePopup
TQTimer m_scrollBarTipTimer
scrollbar tooltip's timer
bool hasData() const
for sanity checks (return true if m_data is present; else: outputs warning)
virtual int lastVisibleRow() const =0
KexiSearchAndReplaceViewInterface::Options::SearchDirection m_recentSearchDirection
KexiTableViewData * m_data
data structure displayed for this object
virtual void slotRowRepaintRequested(KexiTableItem &item)
Handles KexiTableViewData::rowRepaintRequested() signal.
virtual void createEditor(int row, int col, const TQString &addText=TQString(), bool removeOld=false)=0
Creates editors and shows it, what usually means the beginning of a cell editing.
tristate deleteAllRows(bool ask=false, bool repaint=true)
virtual void cellSelected(int col, int row)=0
virtual void updateWidgetContentsSize()=0
virtual void updateAllVisibleRowsBelow(int row)
virtual void slotRowDeleted()
Handles KexiTableViewData::rowDeleted() signal to repaint when needed.
virtual void updateCell(int row, int col)=0
virtual void dataRefreshed()=0
virtual bool acceptEditor()
Accepst changes made to the currently active editor.
virtual void updateWidgetScrollBars()=0
KexiDataItemInterface * editor() const
virtual void itemChanged(KexiTableItem *, int row, int col)=0
signals
virtual void focusOutEvent(TQFocusEvent *e)
Call this from the subclass. *‍/.
TQValueVector< uint > m_indicesForVisibleValues
Setup by updateIndicesForVisibleValues() and used by find()
TQVariant columnDefaultValue(int col) const
virtual void itemSelected(KexiTableItem *)=0
virtual bool beforeDeleteItem(KexiTableItem *item)
virtual int rowsPerPage() const =0
KexiRecordNavigator * m_navPanel
main navigation widget
virtual void reloadActions()=0
virtual KexiTableItem * insertEmptyRow(int row=-1)
virtual void sortedColumnChanged(int col)=0
KexiTableViewData::Iterator * m_itemIterator
data item's iterator
virtual void startEditCurrentCell(const TQString &setText=TQString())
int showErrorMessageForResult(KexiDB::ResultInfo *resultInfo)
virtual void editorShowFocus(int row, int col)=0
KexiTableItem * m_currentItem
current data item
virtual void newItemAppendedForAfterDeletingInSpreadSheetMode()=0
Emitted for spreadsheet mode when an item was deleted and a new item has been appended.
bool m_owner
true if m_data member is owned by this object
virtual void selectPrevPage()
page up action
virtual void slotAboutToDeleteRow(KexiTableItem &item, KexiDB::ResultInfo *result, bool repaint)
Handles KexiTableViewData::aboutToDeleteRow() signal. Prepares info for slotRowDeleted().
virtual void clearColumns(bool repaint=true)
An interface for declaring widgets to be data-aware.
virtual TQVariant value()=0
virtual bool valueIsNull()=0
virtual bool valueIsEmpty()=0
virtual KexiDB::Field * field() const =0
virtual KexiDB::QueryColumnInfo * columnInfo() const =0
virtual void clear()=0
clears item's data, so the data will contain NULL data
bool acceptEditorAfterDeleteContents() const
virtual TQVariant visibleValue()
void setEditRow(int row)
void addLabels(int num, bool upd=true)
void setInsertingEnabled(bool set)
void setRecordCount(uint count)
Specifies options for find and replace operations.
@ MatchAnyPartOfField
Matched text can be any part of field (the default)
@ MatchWholeField
Matched text must be the whole field.
int columnNumber
Column number to look in, AllColumns means "all columns" (the default) and CurrentColumn means "curre...
bool caseSensitive
True for searching is case-sensitive (false by default)
TextMatching textMatching
Specifies possible options for text matching.
bool wholeWordsOnly
True for searching for whole words only (false by default)
@ SearchAllRows
Search from the first to the last row.
@ SearchUp
Search up (previous) from the current position.
SearchDirection searchDirection
Specifies search direction.
KexiDB::QueryColumnInfo * columnInfo
KexiTableViewColumn::List columns
void reload()
Emits reloadRequested() signal to reload presenters.
KexiDB::ResultInfo * result()
void insertRow(KexiTableItem &item, uint index, bool repaint=false)
virtual bool deleteAllRows(bool repaint=false)
virtual bool isInsertingEnabled() const
bool updateRowEditBufferRef(KexiTableItem *item, int colnum, KexiTableViewColumn *col, TQVariant &newval, bool allowSignals=true, TQVariant *visibleValueForLookupField=0)
virtual bool isReadOnly() const
void setSorting(int column, bool ascending=true)
TQString dbTableName() const
A validator extending TQValidator with offline-checking for value's validity.
Definition validator.h:46
static const TQString msgColumnNotEmpty()
A generic error/warning message.
Definition validator.h:77
Result check(const TQString &valueName, const TQVariant &v, TQString &message, TQString &details)
Definition validator.cpp:34
3-state logical type with three values: true, false and cancelled and convenient operators.
Definition tristate.h:100
General Utils.
Definition identifier.h:27
type * findParent(TQObject *o, const char *className)