kexi

kexidataawareobjectiface.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    Based on KexiTableView code.
00005    Copyright (C) 2002 Till Busch <till@bux.at>
00006    Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
00007    Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
00008    Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
00009 
00010    This program is free software; you can redistribute it and/or
00011    modify it under the terms of the GNU Library General Public
00012    License as published by the Free Software Foundation; either
00013    version 2 of the License, or (at your option) any later version.
00014 
00015    This program is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018    Library General Public License for more details.
00019 
00020    You should have received a copy of the GNU Library General Public License
00021    along with this program; see the file COPYING.  If not, write to
00022    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00023  * Boston, MA 02110-1301, USA.
00024 */
00025 
00026 #include "kexidataawareobjectiface.h"
00027 
00028 #include <qscrollview.h>
00029 #include <qlabel.h>
00030 #include <qtooltip.h>
00031 
00032 #include <kmessagebox.h>
00033 
00034 #include <kexi.h>
00035 #include <kexiutils/validator.h>
00036 #include <widget/utils/kexirecordnavigator.h>
00037 #include <widget/utils/kexirecordmarker.h>
00038 #include <kexidb/roweditbuffer.h>
00039 #include <kexidataiteminterface.h>
00040 
00041 #include "kexitableviewheader.h"
00042 
00043 using namespace KexiUtils;
00044 
00045 KexiDataAwareObjectInterface::KexiDataAwareObjectInterface()
00046 {
00047     m_data = 0;
00048     m_itemIterator = 0;
00049     m_readOnly = -1; //don't know
00050     m_insertingEnabled = -1; //don't know
00051     m_isSortingEnabled = true;
00052     m_isFilteringEnabled = true;
00053     m_deletionPolicy = AskDelete;
00054     m_inside_acceptEditor = false;
00055     m_acceptsRowEditAfterCellAccepting = false;
00056     m_internal_acceptsRowEditAfterCellAccepting = false;
00057     m_contentsMousePressEvent_dblClick = false;
00058     m_navPanel = 0;
00059     m_initDataContentsOnShow = false;
00060     m_cursorPositionSetExplicityBeforeShow = false;
00061     m_verticalHeader = 0;
00062     m_horizontalHeader = 0;
00063     m_insertItem = 0;
00064 //  m_rowEditBuffer = 0;
00065     m_spreadSheetMode = false;
00066     m_dropsAtRowEnabled = false;
00067     m_updateEntireRowWhenMovingToOtherRow = false;
00068     m_dragIndicatorLine = -1;
00069     m_emptyRowInsertingEnabled = false;
00070     m_popupMenu = 0;
00071     m_contextMenuEnabled = true;
00072     m_rowWillBeDeleted = -1;
00073     m_alsoUpdateNextRow = false;
00074     m_verticalHeaderAlreadyAdded = false;
00075     m_vScrollBarValueChanged_enabled = true;
00076     m_scrollbarToolTipsEnabled = true;
00077     m_scrollBarTipTimerCnt = 0;
00078     m_scrollBarTip = 0;
00079     m_recentSearchDirection = KexiSearchAndReplaceViewInterface::Options::DefaultSearchDirection;
00080 
00081     // setup scrollbar tooltip and related members
00082     m_scrollBarTip = new QLabel("",0, "vScrollBarToolTip",
00083         Qt::WStyle_Customize |Qt::WStyle_NoBorder|Qt::WX11BypassWM|Qt::WStyle_StaysOnTop|Qt::WStyle_Tool);
00084     m_scrollBarTip->setPalette(QToolTip::palette());
00085     m_scrollBarTip->setMargin(2);
00086     m_scrollBarTip->setIndent(0);
00087     m_scrollBarTip->setAlignment(Qt::AlignCenter);
00088     m_scrollBarTip->setFrameStyle( QFrame::Plain | QFrame::Box );
00089     m_scrollBarTip->setLineWidth(1);
00090 
00091     clearVariables();
00092 }
00093 
00094 KexiDataAwareObjectInterface::~KexiDataAwareObjectInterface()
00095 {
00096     delete m_insertItem;
00097 //  delete m_rowEditBuffer;
00098     delete m_itemIterator;
00099     delete m_scrollBarTip;
00100     //we cannot delete m_data here... subclasses should do this
00101 }
00102 
00103 void KexiDataAwareObjectInterface::clearVariables()
00104 {
00105     m_editor = 0;
00106 //  m_rowEditBuffer = 0;
00107     m_rowEditing = false;
00108     m_newRowEditing = false;
00109     m_curRow = -1;
00110     m_curCol = -1;
00111     m_currentItem = 0;
00112 }
00113 
00114 void KexiDataAwareObjectInterface::setData( KexiTableViewData *data, bool owner )
00115 {
00116     const bool theSameData = m_data && m_data==data;
00117     if (m_owner && m_data && m_data!=data/*don't destroy if it's the same*/) {
00118         kexidbg << "KexiDataAwareObjectInterface::setData(): destroying old data (owned)" << endl;
00119         delete m_itemIterator;
00120         delete m_data; //destroy old data
00121         m_data = 0;
00122         m_itemIterator = 0;
00123     }
00124     m_owner = owner;
00125     m_data = data;
00126     if (m_data)
00127         m_itemIterator = m_data->createIterator();
00128 
00129     kdDebug(44021) << "KexiDataAwareObjectInterface::setData(): using shared data" << endl;
00130         //add columns
00131 //OK?
00132     clearColumnsInternal(false);
00133     if (m_data) {
00134         int i = 0;
00135         for (KexiTableViewColumn::ListIterator it(m_data->columns);
00136             it.current(); ++it, i++) 
00137         {
00138             KexiDB::Field *f = it.current()->field();
00139             if (it.current()->visible()) {
00140                 int wid = f->width();
00141                 if (wid==0)
00142                     wid=KEXI_DEFAULT_DATA_COLUMN_WIDTH;//default col width in pixels
00144                 addHeaderColumn(it.current()->isHeaderTextVisible()
00145                     ? it.current()->captionAliasOrName() : QString::null,
00146                     f->description(), it.current()->icon(), wid);
00147             }
00148         }
00149     }
00150     if (m_verticalHeader) {
00151         m_verticalHeader->clear();
00152         if (m_data)
00153             m_verticalHeader->addLabels(m_data->count());
00154     }
00155     if (m_data && m_data->count()==0)
00156         m_navPanel->setCurrentRecordNumber(0+1);
00157     
00158     if (m_data && !theSameData) {
00160         setSorting(-1);
00161 //      connect(m_data, SIGNAL(refreshRequested()), this, SLOT(slotRefreshRequested()));
00162         connectToReloadDataSlot(m_data, SIGNAL(reloadRequested()));
00163         QObject* thisObject = dynamic_cast<QObject*>(this);
00164         if (thisObject) {
00165             QObject::connect(m_data, SIGNAL(destroying()), thisObject, SLOT(slotDataDestroying()));
00166             QObject::connect(m_data, SIGNAL(rowsDeleted( const QValueList<int> & )), 
00167                 thisObject, SLOT(slotRowsDeleted( const QValueList<int> & )));
00168             QObject::connect(m_data, SIGNAL(aboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)),
00169                 thisObject, SLOT(slotAboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)));
00170             QObject::connect(m_data, SIGNAL(rowDeleted()), thisObject, SLOT(slotRowDeleted()));
00171             QObject::connect(m_data, SIGNAL(rowInserted(KexiTableItem*,bool)), 
00172                 thisObject, SLOT(slotRowInserted(KexiTableItem*,bool)));
00173             QObject::connect(m_data, SIGNAL(rowInserted(KexiTableItem*,uint,bool)), 
00174                 thisObject, SLOT(slotRowInserted(KexiTableItem*,uint,bool))); //not db-aware
00175             QObject::connect(m_data, SIGNAL(rowRepaintRequested(KexiTableItem&)), 
00176                 thisObject, SLOT(slotRowRepaintRequested(KexiTableItem&)));
00177             // setup scrollbar's tooltip
00178             QObject::connect(verticalScrollBar(),SIGNAL(sliderReleased()),
00179                 thisObject,SLOT(vScrollBarSliderReleased()));
00180             QObject::connect(verticalScrollBar(),SIGNAL(valueChanged(int)),
00181                 thisObject,SLOT(vScrollBarValueChanged(int)));
00182             QObject::connect(&m_scrollBarTipTimer,SIGNAL(timeout()),
00183                 thisObject,SLOT(scrollBarTipTimeout()));
00184         }
00185     }
00186 
00187     if (!m_data) {
00188 //      clearData();
00189         cancelRowEdit();
00190         //m_data->clearInternal();
00191         clearVariables();
00192     }
00193     else {
00194         if (!m_insertItem) {//first setData() call - add 'insert' item
00195             m_insertItem = m_data->createItem(); //new KexiTableItem(m_data->columns.count());
00196         }
00197         else {//just reinit
00198             m_insertItem->init(m_data->columns.count());
00199         }
00200     }
00201 
00202     //update gui mode
00203     m_navPanel->setInsertingEnabled(m_data && isInsertingEnabled());
00204     if (m_verticalHeader)
00205         m_verticalHeader->showInsertRow(m_data && isInsertingEnabled());
00206 
00207     initDataContents();
00208     updateIndicesForVisibleValues();
00209 
00210     if (m_data)
00211         /*emit*/ dataSet( m_data );
00212 }
00213 
00214 void KexiDataAwareObjectInterface::initDataContents()
00215 {
00216     m_editor = 0;
00217 //  QSize s(tableSize());
00218 //  resizeContents(s.width(),s.height());
00219 
00220     m_navPanel->setRecordCount(rows());
00221 
00222     if (m_data && !m_cursorPositionSetExplicityBeforeShow) {
00223         //set current row:
00224         m_currentItem = 0;
00225         int curRow = -1, curCol = -1;
00226         if (m_data->columnsCount()>0) {
00227             if (rows()>0) {
00228                 m_itemIterator->toFirst();
00229                 m_currentItem = **m_itemIterator;
00230                 curRow = 0;
00231                 curCol = 0;
00232             }
00233             else {//no data
00234                 if (isInsertingEnabled()) {
00235                     m_currentItem = m_insertItem;
00236                     curRow = 0;
00237                     curCol = 0;
00238                 }
00239             }
00240         }
00241         setCursorPosition(curRow, curCol, true/*force*/);
00242     }
00243     ensureCellVisible(m_curRow, m_curCol);
00244 //  updateRowCountInfo();
00245 //  setNavRowCount(rows());
00246 
00247 //OK?
00248 // updateContents();
00249     updateWidgetContents();
00250 
00251     m_cursorPositionSetExplicityBeforeShow = false;
00252 
00253     /*emit*/ dataRefreshed();
00254 }
00255 
00256 void KexiDataAwareObjectInterface::setSortingEnabled(bool set)
00257 {
00258     if (m_isSortingEnabled && !set)
00259         setSorting(-1);
00260     m_isSortingEnabled = set;
00261     /*emit*/ reloadActions();
00262 }
00263 
00264 void KexiDataAwareObjectInterface::setSorting(int col, bool ascending)
00265 {
00266     if (!m_data || !m_isSortingEnabled)
00267         return;
00268 //  d->pTopHeader->setSortIndicator(col, ascending ? Ascending : Descending);
00269     setLocalSortingOrder(col, ascending ? 1 : -1);
00270     m_data->setSorting(col, ascending);
00271 }
00272 
00273 int KexiDataAwareObjectInterface::dataSortedColumn() const
00274 {
00275     if (m_data && m_isSortingEnabled)
00276         return m_data->sortedColumn();
00277     return -1;
00278 }
00279 
00280 int KexiDataAwareObjectInterface::dataSortingOrder() const
00281 {
00282     return m_data ? m_data->sortingOrder() : 0;
00283 }
00284 
00285 bool KexiDataAwareObjectInterface::sort()
00286 {
00287     if (!m_data || !m_isSortingEnabled)
00288         return false;
00289 
00290     if (rows() < 2)
00291         return true;
00292 
00293     if (!acceptRowEdit())
00294         return false;
00295 
00296     const int oldRow = m_curRow;
00297     if (m_data->sortedColumn()!=-1)
00298         m_data->sort();
00299 
00300     //locate current record
00301     if (!m_currentItem) {
00302         m_itemIterator->toFirst();
00303         m_currentItem = **m_itemIterator; //m_data->first();
00304         m_curRow = 0;
00305         if (!m_currentItem)
00306             return true;
00307     }
00308     if (m_currentItem != m_insertItem) {
00309         m_curRow = m_data->findRef(m_currentItem);
00310         int jump = m_curRow - oldRow;
00311         if (jump<0)
00312             (*m_itemIterator) -= -jump;
00313         else
00314             (*m_itemIterator) += jump;
00315     }
00316 
00317     updateGUIAfterSorting();
00318     editorShowFocus( m_curRow, m_curCol );
00319     if (m_verticalHeader)
00320         m_verticalHeader->setCurrentRow(m_curRow);
00321     if (m_horizontalHeader)
00322         m_horizontalHeader->setSelectedSection(m_curCol);
00323     if (m_navPanel)
00324         m_navPanel->setCurrentRecordNumber(m_curRow+1);
00325     return true;
00326 }
00327 
00328 void KexiDataAwareObjectInterface::sortAscending()
00329 {
00330     if (currentColumn()<0)
00331         return;
00332     sortColumnInternal( currentColumn(), 1 );
00333 }
00334 
00335 void KexiDataAwareObjectInterface::sortDescending()
00336 {
00337     if (currentColumn()<0)
00338         return;
00339     sortColumnInternal( currentColumn(), -1 );
00340 }
00341 
00342 void KexiDataAwareObjectInterface::sortColumnInternal(int col, int order)
00343 {
00344     //-select sorting 
00345     bool asc;
00346     if (order == 0) {// invert
00347         if (col==dataSortedColumn() && dataSortingOrder()==1)
00348             asc = dataSortingOrder()==-1; //inverse sorting for this column -> descending order
00349         else
00350             asc = true;
00351     }
00352     else
00353         asc = (order==1);
00354     
00355     int prevSortOrder = currentLocalSortingOrder();
00356     const int prevSortColumn = currentLocalSortingOrder();
00357     setSorting( col, asc );
00358     //-perform sorting 
00359     if (!sort())
00360         setLocalSortingOrder(prevSortColumn, prevSortOrder); //this will also remove indicator
00361                                                              //if prevSortColumn==-1
00362     if (col != prevSortColumn)
00363         /*emit*/ sortedColumnChanged(col);
00364 }
00365 
00366 bool KexiDataAwareObjectInterface::isInsertingEnabled() const
00367 {
00368     if (isReadOnly())
00369         return false;
00370     if (m_insertingEnabled == 1 || m_insertingEnabled == 0)
00371         return (bool)m_insertingEnabled;
00372     if (!hasData())
00373         return true;
00374     return m_data->isInsertingEnabled();
00375 }
00376 
00377 void KexiDataAwareObjectInterface::setFilteringEnabled(bool set)
00378 {
00379     m_isFilteringEnabled = set;
00380 }
00381 
00382 bool KexiDataAwareObjectInterface::isDeleteEnabled() const
00383 {
00384     return (m_deletionPolicy != NoDelete) && !isReadOnly();
00385 }
00386 
00387 void KexiDataAwareObjectInterface::setDeletionPolicy(DeletionPolicy policy)
00388 {
00389     m_deletionPolicy = policy;
00390 //  updateContextMenu();
00391 }
00392 
00393 void KexiDataAwareObjectInterface::setReadOnly(bool set)
00394 {
00395     if (isReadOnly() == set || (m_data && m_data->isReadOnly() && !set))
00396         return; //not allowed!
00397     m_readOnly = (set ? 1 : 0);
00398     if (set)
00399         setInsertingEnabled(false);
00400     updateWidgetContents();
00401     /*emit*/ reloadActions();
00402 }
00403 
00404 bool KexiDataAwareObjectInterface::isReadOnly() const
00405 {
00406     if (!hasData())
00407         return true;
00408     if (m_readOnly == 1 || m_readOnly == 0)
00409         return (bool)m_readOnly;
00410     if (!hasData())
00411         return true;
00412     return m_data->isReadOnly();
00413 }
00414 
00415 void KexiDataAwareObjectInterface::setInsertingEnabled(bool set)
00416 {
00417     if (isInsertingEnabled() == set || (m_data && !m_data->isInsertingEnabled() && set))
00418         return; //not allowed!
00419     m_insertingEnabled = (set ? 1 : 0);
00420     m_navPanel->setInsertingEnabled(set);
00421     if (m_verticalHeader)
00422         m_verticalHeader->showInsertRow(set);
00423     if (set)
00424         setReadOnly(false);
00425 //  update();
00426     updateWidgetContents();
00427     /*emit*/ reloadActions();
00428 }
00429 
00430 void KexiDataAwareObjectInterface::setSpreadSheetMode()
00431 {
00432     m_spreadSheetMode = true;
00433     setSortingEnabled( false );
00434     setInsertingEnabled( false );
00435     setAcceptsRowEditAfterCellAccepting( true );
00436     setFilteringEnabled( false );
00437     setEmptyRowInsertingEnabled( true );
00438     m_navPanelEnabled = false;
00439 }
00440 
00441 void KexiDataAwareObjectInterface::selectNextRow()
00442 {
00443     selectRow( QMIN( rows() - 1 +(isInsertingEnabled()?1:0), m_curRow + 1 ) );
00444 }
00445 
00446 void KexiDataAwareObjectInterface::selectPrevPage()
00447 {
00448     selectRow( 
00449         QMAX( 0, m_curRow - rowsPerPage() )
00450     );
00451 }
00452 
00453 void KexiDataAwareObjectInterface::selectNextPage()
00454 {
00455     selectRow( 
00456         QMIN( 
00457             rows() - 1 + (isInsertingEnabled()?1:0),
00458             m_curRow + rowsPerPage()
00459         )
00460     );
00461 }
00462 
00463 void KexiDataAwareObjectInterface::selectFirstRow()
00464 {
00465     selectRow(0);
00466 }
00467 
00468 void KexiDataAwareObjectInterface::selectLastRow()
00469 {
00470 //  selectRow(rows() - 1 + (isInsertingEnabled()?1:0));
00471     selectRow(rows() - 1);
00472 }
00473 
00474 void KexiDataAwareObjectInterface::selectRow(int row)
00475 {
00476     m_vScrollBarValueChanged_enabled = false; //disable tooltip
00477     setCursorPosition(row, -1);
00478     m_vScrollBarValueChanged_enabled = true;
00479 }
00480 
00481 void KexiDataAwareObjectInterface::selectPrevRow()
00482 {
00483     selectRow( QMAX( 0, m_curRow - 1 ) );
00484 }
00485 
00486 void KexiDataAwareObjectInterface::clearSelection()
00487 {
00488 //  selectRow( -1 );
00489     int oldRow = m_curRow;
00490 //  int oldCol = m_curCol;
00491     m_curRow = -1;
00492     m_curCol = -1;
00493     m_currentItem = 0;
00494     updateRow( oldRow );
00495     m_navPanel->setCurrentRecordNumber(0);
00496 //  setNavRowNumber(-1);
00497 }
00498 
00499 void KexiDataAwareObjectInterface::setCursorPosition(int row, int col/*=-1*/, bool forceSet)
00500 {
00501     int newrow = row;
00502     int newcol = col;
00503 
00504     if(rows() <= 0) {
00505         if (m_verticalHeader)
00506             m_verticalHeader->setCurrentRow(-1);
00507         if (m_horizontalHeader)
00508             m_horizontalHeader->setSelectedSection(-1);
00509         if (isInsertingEnabled()) {
00510             m_currentItem=m_insertItem;
00511             newrow=0;
00512             if (col>=0)
00513                 newcol=col;
00514             else
00515                 newcol=0;
00516         }
00517         else {
00518             m_currentItem=0;
00519             m_curRow=-1;
00520             m_curCol=-1;
00521             return;
00522         }
00523     }
00524 
00525     if(col>=0)
00526     {
00527         newcol = QMAX(0, col);
00528         newcol = QMIN(columns() - 1, newcol);
00529     }
00530     else {
00531         newcol = m_curCol; //no changes
00532         newcol = QMAX(0, newcol); //may not be < 0 !
00533     }
00534     newrow = QMAX(0, row);
00535     newrow = QMIN(rows() - 1 + (isInsertingEnabled()?1:0), newrow);
00536 
00537 //  d->pCurrentItem = itemAt(d->curRow);
00538 //  kdDebug(44021) << "setCursorPosition(): d->curRow=" << d->curRow << " oldRow=" << oldRow << " d->curCol=" << d->curCol << " oldCol=" << oldCol << endl;
00539 
00540     if ( forceSet || m_curRow != newrow || m_curCol != newcol )
00541     {
00542         kexidbg << "setCursorPosition(): " <<QString("old:%1,%2 new:%3,%4").arg(m_curCol)
00543             .arg(m_curRow).arg(newcol).arg(newrow) << endl;
00544         
00545         // cursor moved: get rid of editor
00546         if (m_editor) {
00547             if (!m_contentsMousePressEvent_dblClick) {
00548                 if (!acceptEditor()) {
00549                     return;
00550                 }
00551                 //update row num. again
00552                 newrow = QMIN( rows() - 1 + (isInsertingEnabled()?1:0), newrow);
00553             }
00554         }
00555         if (m_errorMessagePopup) {
00556             m_errorMessagePopup->close();
00557         }
00558 
00559         if (m_curRow != newrow || forceSet)  {//update current row info
00560             m_navPanel->setCurrentRecordNumber(newrow+1);
00561 //          setNavRowNumber(newrow);
00562 //          d->navBtnPrev->setEnabled(newrow>0);
00563 //          d->navBtnFirst->setEnabled(newrow>0);
00564 //          d->navBtnNext->setEnabled(newrow<(rows()-1+(isInsertingEnabled()?1:0)));
00565 //          d->navBtnLast->setEnabled(newrow!=(rows()-1));
00566         }
00567 
00568         // cursor moved to other row: end of row editing
00569         bool newRowInserted = false;
00570         if (m_rowEditing && m_curRow != newrow) {
00571             newRowInserted = m_newRowEditing;
00572             if (!acceptRowEdit()) {
00573                 //accepting failed: cancel setting the cursor
00574                 return;
00575             }
00576             //update row number, because number of rows changed
00577             newrow = QMIN( rows() - 1 + (isInsertingEnabled()?1:0), newrow);
00578 
00579             m_navPanel->setCurrentRecordNumber(newrow+1); //refresh
00580         }
00581 
00582         //change position
00583         int oldRow = m_curRow;
00584         int oldCol = m_curCol;
00585         m_curRow = newrow;
00586         m_curCol = newcol;
00587 
00588 //      int cw = columnWidth( d->curCol );
00589 //      int rh = rowHeight();
00590 //      ensureVisible( columnPos( d->curCol ) + cw / 2, rowPos( d->curRow ) + rh / 2, cw / 2, rh / 2 );
00591 //      center(columnPos(d->curCol) + cw / 2, rowPos(d->curRow) + rh / 2, cw / 2, rh / 2);
00592 //  kdDebug(44021) << " contentsY() = "<< contentsY() << endl;
00593 
00594 //js        if (oldRow > d->curRow)
00595 //js            ensureVisible(columnPos(d->curCol), rowPos(d->curRow) + rh, columnWidth(d->curCol), rh);
00596 //js        else// if (oldRow <= d->curRow)
00597 //js        ensureVisible(columnPos(d->curCol), rowPos(d->curRow), columnWidth(d->curCol), rh);
00598 
00599 
00600         //show editor-dependent focus, if we're changing the current column
00601         if (oldCol>=0 && oldCol<columns() && m_curCol!=oldCol) {
00602             //find the editor for this column
00603             KexiDataItemInterface *edit = editor( oldCol );
00604             if (edit) {
00605                 edit->hideFocus();
00606             }
00607         }
00608 
00609         // position changed, so subsequent searching should be started from scratch 
00610         // (e.g. from the current cell or the top-left cell)
00611         m_positionOfRecentlyFoundValue.exists = false;
00612 
00613         //show editor-dependent focus, if needed
00614         editorShowFocus( m_curRow, m_curCol );
00615 
00616         if (m_updateEntireRowWhenMovingToOtherRow)
00617             updateRow( oldRow );
00618         else
00619             updateCell( oldRow, oldCol );
00620 
00621 //      //quite clever: ensure the cell is visible:
00622 //      ensureCellVisible(m_curRow, m_curCol);
00623 
00624 //      QPoint pcenter = QRect( columnPos(d->curCol), rowPos(d->curRow), columnWidth(d->curCol), rh).center();
00625 //      ensureVisible(pcenter.x(), pcenter.y(), columnWidth(d->curCol)/2, rh/2);
00626 
00627 //      ensureVisible(columnPos(d->curCol), rowPos(d->curRow) - contentsY(), columnWidth(d->curCol), rh);
00628         if (m_verticalHeader && (oldRow != m_curRow || forceSet))
00629             m_verticalHeader->setCurrentRow(m_curRow);
00630 
00631         if (m_updateEntireRowWhenMovingToOtherRow)
00632             updateRow( m_curRow );
00633         else
00634             updateCell( m_curRow, m_curCol );
00635 
00636         if (m_curCol != oldCol || m_curRow != oldRow || forceSet) {//ensure this is also refreshed
00637             if (!m_updateEntireRowWhenMovingToOtherRow) //only if entire row has not been updated
00638                 updateCell( oldRow, m_curCol );
00639         }
00640         //update row
00641         if (forceSet || m_curRow != oldRow) {
00642             if (isInsertingEnabled() && m_curRow == rows()) {
00643                 kdDebug(44021) << "NOW insert item is current" << endl;
00644                 m_currentItem = m_insertItem;
00645             }
00646             else {
00647                 kdDebug(44021) << QString("NOW item at %1 (%2) is current")
00648                     .arg(m_curRow).arg((ulong)itemAt(m_curRow)) << endl;
00649     //NOT EFFECTIVE!!!!!!!!!!!
00650                 //set item iterator
00651                 if (!newRowInserted && isInsertingEnabled() && m_currentItem == m_insertItem && m_curRow == (rows()-1)) {
00652                     //moving from 'insert item' to last item
00653                     m_itemIterator->toLast();
00654                 }
00655                 else if (!newRowInserted && !forceSet && m_currentItem != m_insertItem && 0==m_curRow)
00656                     m_itemIterator->toFirst();
00657                 else if (!newRowInserted && !forceSet && m_currentItem != m_insertItem && oldRow>=0 && (oldRow+1)==m_curRow) //just move next
00658                     ++(*m_itemIterator);
00659                 else if (!newRowInserted && !forceSet && m_currentItem != m_insertItem && oldRow>=0 && (oldRow-1)==m_curRow) //just move back
00660                     --(*m_itemIterator);
00661                 else { //move at:
00662                     m_itemIterator->toFirst();
00663                     (*m_itemIterator)+=m_curRow;
00664                 }
00665                 if (!**m_itemIterator) { //sanity
00666                     m_itemIterator->toFirst();
00667                     (*m_itemIterator)+=m_curRow;
00668                 }
00669                 m_currentItem = **m_itemIterator;
00670                     //itemAt(m_curRow);
00671             }
00672         }
00673 
00674         //quite clever: ensure the cell is visible:
00675         ensureCellVisible(m_curRow, m_curCol);
00676 
00677         if (m_horizontalHeader && (oldCol != m_curCol || forceSet))
00678             m_horizontalHeader->setSelectedSection(m_curCol);
00679 
00680         /*emit*/ itemSelected(m_currentItem);
00681         /*emit*/ cellSelected(m_curCol, m_curRow);
00682         /* only needed for forms */
00683         selectCellInternal();
00684     }
00685     else {
00686             kexidbg << "setCursorPosition(): NO CHANGE" << endl;
00687     }
00688 
00689     if(m_initDataContentsOnShow) {
00690         m_cursorPositionSetExplicityBeforeShow = true;
00691     }
00692 }
00693 
00694 bool KexiDataAwareObjectInterface::acceptRowEdit()
00695 {
00696     if (!m_rowEditing || /*sanity*/!m_data->rowEditBuffer())
00697         return true;
00698     if (m_inside_acceptEditor) {
00699         m_internal_acceptsRowEditAfterCellAccepting = true;
00700         return true;
00701     }
00702     m_internal_acceptsRowEditAfterCellAccepting = false;
00703 
00704     const int columnEditedBeforeAccepting = m_editor ? currentColumn() : -1;
00705     if (!acceptEditor())
00706         return false;
00707     kdDebug() << "EDIT ROW ACCEPTING..." << endl;
00708 
00709     bool success = true;
00710 //  bool allow = true;
00711 //  int faultyColumn = -1; // will be !=-1 if cursor has to be moved to that column
00712     const bool inserting = m_newRowEditing;
00713 //  QString msg, desc;
00714 //  bool inserting = d->pInsertItem && d->pInsertItem==d->pCurrentItem;
00715 
00716     if (m_data->rowEditBuffer()->isEmpty() && !m_newRowEditing) {
00717 /*      if (d->newRowEditing) {
00718             cancelRowEdit();
00719             kdDebug() << "-- NOTHING TO INSERT!!!" << endl;
00720             return true;
00721         }
00722         else {*/
00723             kdDebug() << "-- NOTHING TO ACCEPT!!!" << endl;
00724 //      }
00725     }
00726     else {//not empty edit buffer or new row to insert:
00727         if (m_newRowEditing) {
00728 //          emit aboutToInsertRow(d->pCurrentItem, m_data->rowEditBuffer(), success, &faultyColumn);
00729 //          if (success) {
00730             kdDebug() << "-- INSERTING: " << endl;
00731             m_data->rowEditBuffer()->debug();
00732             success = m_data->saveNewRow(*m_currentItem);
00733 //              if (!success) {
00734 //              }
00735 //          }
00736         }
00737         else {
00738 //          emit aboutToUpdateRow(d->pCurrentItem, m_data->rowEditBuffer(), success, &faultyColumn);
00739             if (success) {
00740                 //accept changes for this row:
00741                 kdDebug() << "-- UPDATING: " << endl;
00742                 m_data->rowEditBuffer()->debug();
00743                 kdDebug() << "-- BEFORE: " << endl;
00744                 m_currentItem->debug();
00745                 success = m_data->saveRowChanges(*m_currentItem);//, &msg, &desc, &faultyColumn);
00746                 kdDebug() << "-- AFTER: " << endl;
00747                 m_currentItem->debug();
00748 
00749 //              if (!success) {
00750 //              }
00751             }
00752         }
00753     }
00754 
00755     if (success) {
00756         //editing is finished:
00757         if (m_newRowEditing) {
00758             //update current-item-iterator
00759             m_itemIterator->toLast();
00760             m_currentItem = **m_itemIterator;
00761         }
00762         m_rowEditing = false;
00763         m_newRowEditing = false;
00764         //indicate on the vheader that we are not editing
00765         if (m_verticalHeader)
00766             m_verticalHeader->setEditRow(-1);
00767 
00768         updateAfterAcceptRowEdit();
00769 
00770         kdDebug() << "EDIT ROW ACCEPTED:" << endl;
00771 //      /*debug*/itemAt(m_curRow);
00772 
00773         if (inserting) {
00774 //          emit rowInserted(d->pCurrentItem);
00775             //update navigator's data
00776             m_navPanel->setRecordCount(rows());
00777         }
00778         else {
00779 //          emit rowUpdated(d->pCurrentItem);
00780         }
00781 
00782         /*emit*/ rowEditTerminated(m_curRow);
00783     }
00784     else {
00785 //      if (!allow) {
00786 //          kdDebug() << "INSERT/EDIT ROW - DISALLOWED by signal!" << endl;
00787 //      }
00788 //      else {
00789 //          kdDebug() << "EDIT ROW - ERROR!" << endl;
00790 //      }
00791         int faultyColumn = -1;
00792         if (m_data->result()->column >= 0 && m_data->result()->column < columns())
00793             faultyColumn = m_data->result()->column;
00794         else if (columnEditedBeforeAccepting >= 0)
00795             faultyColumn = columnEditedBeforeAccepting;
00796         if (faultyColumn >= 0) {
00797             setCursorPosition(m_curRow, faultyColumn);
00798         }
00799 
00800         const int button = showErrorMessageForResult( m_data->result() );
00801         if (KMessageBox::No == button) {
00802             //discard changes
00803             cancelRowEdit();
00804         }
00805         else {
00806             if (faultyColumn >= 0) {
00807                 //edit this cell
00808                 startEditCurrentCell();
00809             }
00810         }
00811     }
00812 
00813     return success;
00814 }
00815 
00816 bool KexiDataAwareObjectInterface::cancelRowEdit()
00817 {
00818     if (!hasData())
00819         return false;
00820     if (!m_rowEditing)
00821         return false;
00822     cancelEditor();
00823     m_rowEditing = false;
00824     //indicate on the vheader that we are not editing
00825     if (m_verticalHeader)
00826         m_verticalHeader->setEditRow(-1);
00827     m_alsoUpdateNextRow = m_newRowEditing;
00828     if (m_newRowEditing) {
00829         m_newRowEditing = false;
00830         //remove current edited row (it is @ the end of list)
00831         m_data->removeLast();
00832         //current item is now empty, last row
00833         m_currentItem = m_insertItem;
00834         //update visibility
00835         if (m_verticalHeader)
00836             m_verticalHeader->removeLabel(false); //-1 label
00837 //      updateContents(columnPos(0), rowPos(rows()), 
00838 //          viewport()->width(), d->rowHeight*3 + (m_navPanel ? m_navPanel->height() : 0)*3 );
00839 //      updateContents(); //js: above did not work well so we do that dirty
00840         updateWidgetContents();
00841 //TODO: still doesn't repaint properly!!
00842 //      QSize s(tableSize());
00843 //      resizeContents(s.width(), s.height());
00844         updateWidgetContentsSize();
00845 //      m_verticalHeader->update();
00846         //--no cancel action is needed for datasource, 
00847         //  because the row was not yet stored.
00848     }
00849 
00850     m_data->clearRowEditBuffer();
00851     updateAfterCancelRowEdit();
00852     
00854     kexidbg << "EDIT ROW CANCELLED." << endl;
00855 
00856     /*emit*/ rowEditTerminated(m_curRow);
00857     return true;
00858 }
00859 
00860 void KexiDataAwareObjectInterface::updateAfterCancelRowEdit()
00861 {
00862     updateRow(m_curRow);
00863     if (m_alsoUpdateNextRow)
00864         updateRow(m_curRow+1);
00865     m_alsoUpdateNextRow = false;
00866 }
00867 
00868 void KexiDataAwareObjectInterface::updateAfterAcceptRowEdit()
00869 {
00870     updateRow(m_curRow);
00871 }
00872 
00873 void KexiDataAwareObjectInterface::removeEditor()
00874 {
00875     if (!m_editor)
00876         return;
00877     m_editor->hideWidget();
00878     m_editor = 0;
00879 }
00880 
00881 bool KexiDataAwareObjectInterface::cancelEditor()
00882 {
00883     if (m_errorMessagePopup) {
00884         m_errorMessagePopup->close();
00885     }
00886     if (!m_editor)
00887         return false;
00888     removeEditor();
00889     return true;
00890 }
00891 
00893 class KexiDataAwareObjectInterfaceToolTip : public QToolTip {
00894     public:
00895     KexiDataAwareObjectInterfaceToolTip( const QString & text, const QPoint & pos, QWidget * widget )
00896         : QToolTip(widget), m_text(text)
00897     {
00898         tip( QRect(pos, QSize(100, 100)), text );
00899     }
00900     virtual void maybeTip(const QPoint & p) {
00901         tip( QRect(p, QSize(100, 100)), m_text);
00902     }
00903     QString m_text;
00904 };
00905 
00906 bool KexiDataAwareObjectInterface::acceptEditor()
00907 {
00908     if (!hasData())
00909         return true;
00910     if (!m_editor || m_inside_acceptEditor)
00911         return true;
00912 
00913     m_inside_acceptEditor = true;//avoid recursion
00914 
00915     QVariant newval;
00916     Validator::Result res = Validator::Ok;
00917     QString msg, desc;
00918     bool setNull = false;
00919 //  bool allow = true;
00920 //  static const QString msg_NOT_NULL = i18n("\"%1\" column requires a value to be entered.");
00921 
00922     //autoincremented field can be omitted (left as null or empty) if we're inserting a new row
00923     const bool autoIncColumnCanBeOmitted = m_newRowEditing && m_editor->field()->isAutoIncrement();
00924 //  const bool autoIncColumnCanBeOmitted = m_newRowEditing && m_editor->columnInfo()->field->isAutoIncrement();
00925 
00926     bool valueChanged = m_editor->valueChanged();
00927     bool editCurrentCellAgain = false;
00928 
00929     if (valueChanged) {
00930         if (!m_editor->valueIsValid()) {
00931             //used e.g. for date or time values - the value can be null but not necessary invalid
00932             res = Validator::Error;
00933             editCurrentCellAgain = true;
00934             QWidget *par = dynamic_cast<QScrollView*>(this) ? dynamic_cast<QScrollView*>(this)->viewport() :
00935                     dynamic_cast<QWidget*>(this);
00936             QWidget *edit = dynamic_cast<QWidget*>(m_editor);
00937             if (par && edit) {
00940                 if (!m_errorMessagePopup) {
00941 //                  m_errorMessagePopup->close();
00942                     m_errorMessagePopup = new KexiArrowTip(
00943                         i18n("Error: %1").arg(m_editor->columnInfo()->field->typeName())+"?", 
00944                         dynamic_cast<QWidget*>(this));
00945                     m_errorMessagePopup->move( 
00946                         par->mapToGlobal(edit->pos()) + QPoint(6, edit->height() + 0) );
00947                     m_errorMessagePopup->show();
00948                 }
00949                 m_editor->setFocus();
00950             }
00951         }
00952         else if (m_editor->valueIsNull()) {//null value entered
00953 //          if (m_editor->columnInfo()->field->isNotNull() && !autoIncColumnCanBeOmitted) {
00954             if (m_editor->field()->isNotNull() && !autoIncColumnCanBeOmitted) {
00955                 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NULL NOT ALLOWED!" << endl;
00956                 res = Validator::Error;
00957 //              msg = Validator::msgColumnNotEmpty().arg(m_editor->columnInfo()->field->captionOrName())
00958                 msg = Validator::msgColumnNotEmpty().arg(m_editor->field()->captionOrName())
00959                     + "\n\n" + Kexi::msgYouCanImproveData();
00960                 desc = i18n("The column's constraint is declared as NOT NULL.");
00961                 editCurrentCellAgain = true;
00962     //          allow = false;
00963     //          removeEditor();
00964     //          return true;
00965             }
00966             else {
00967                 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NULL VALUE WILL BE SET" << endl;
00968                 //ok, just leave newval as NULL
00969                 setNull = true;
00970             }
00971         }
00972         else if (m_editor->valueIsEmpty()) {//empty value entered
00973 //          if (m_editor->columnInfo()->field->hasEmptyProperty()) {
00974             if (m_editor->field()->hasEmptyProperty()) {
00975 //              if (m_editor->columnInfo()->field->isNotEmpty() && !autoIncColumnCanBeOmitted) {
00976                 if (m_editor->field()->isNotEmpty() && !autoIncColumnCanBeOmitted) {
00977                     kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): EMPTY NOT ALLOWED!" << endl;
00978                     res = Validator::Error;
00979 //                  msg = Validator::msgColumnNotEmpty().arg(m_editor->columnInfo()->field->captionOrName())
00980                     msg = Validator::msgColumnNotEmpty().arg(m_editor->field()->captionOrName())
00981                         + "\n\n" + Kexi::msgYouCanImproveData();
00982                     desc = i18n("The column's constraint is declared as NOT EMPTY.");
00983                     editCurrentCellAgain = true;
00984     //              allow = false;
00985     //              removeEditor();
00986     //              return true;
00987                 }
00988                 else {
00989                     kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): EMPTY VALUE WILL BE SET" << endl;
00990                 }
00991             }
00992             else {
00993 //              if (m_editor->columnInfo()->field->isNotNull() && !autoIncColumnCanBeOmitted) {
00994                 if (m_editor->field()->isNotNull() && !autoIncColumnCanBeOmitted) {
00995                     kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NEITHER NULL NOR EMPTY VALUE CAN BE SET!" << endl;
00996                     res = Validator::Error;
00997 //                  msg = Validator::msgColumnNotEmpty().arg(m_editor->columnInfo()->field->captionOrName())
00998                     msg = Validator::msgColumnNotEmpty().arg(m_editor->field()->captionOrName())
00999                         + "\n\n" + Kexi::msgYouCanImproveData();
01000                     desc = i18n("The column's constraint is declared as NOT EMPTY and NOT NULL.");
01001                     editCurrentCellAgain = true;
01002 //              allow = false;
01003     //              removeEditor();
01004     //              return true;
01005                 }
01006                 else {
01007                     kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): NULL VALUE WILL BE SET BECAUSE EMPTY IS NOT ALLOWED" << endl;
01008                     //ok, just leave newval as NULL
01009                     setNull = true;
01010                 }
01011             }
01012         }
01013     }//changed
01014 
01015     const int realFieldNumber = fieldNumberForColumn(m_curCol);
01016     if (realFieldNumber < 0) {
01017         kdWarning() << "KexiDataAwareObjectInterface::acceptEditor(): fieldNumberForColumn(m_curCol) < 0" << endl;
01018         m_inside_acceptEditor = false;
01019         return false;
01020     }
01021 
01022     KexiTableViewColumn *currentTVColumn = column(m_curCol);
01023 
01024     //try to get the value entered:
01025     if (res == Validator::Ok) {
01026         if ((!setNull && !valueChanged)
01027             || (m_editor->field()->type()!=KexiDB::Field::Boolean && setNull && m_currentItem->at( realFieldNumber ).isNull())) {
01028             kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): VALUE NOT CHANGED." << endl;
01029             removeEditor();
01030             if (m_acceptsRowEditAfterCellAccepting || m_internal_acceptsRowEditAfterCellAccepting)
01031                 acceptRowEdit();
01032             m_inside_acceptEditor = false;
01033             return true;
01034         }
01035         if (!setNull) {//get the new value 
01036 //          bool ok;
01037             newval = m_editor->value();
01039 /*
01040             if (!ok) {
01041                 kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): INVALID VALUE - NOT CHANGED." << endl;
01042                 res = KexiValidator::Error;
01043 //js: TODO get detailed info on why m_editor->value() failed
01044                 msg = i18n("Entered value is invalid.")
01045                     + "\n\n" + KexiValidator::msgYouCanImproveData();
01046                 editCurrentCellAgain = true;
01047 //              removeEditor();
01048 //              return true;
01049             }*/
01050         }
01051 
01052         //Check other validation rules:
01053         //1. check using validator
01054 //      KexiValidator *validator = m_data->column(m_curCol)->validator();
01055         Validator *validator = currentTVColumn->validator();
01056         if (validator) {
01057 //          res = validator->check(m_data->column(m_curCol)->field()->captionOrName(), 
01058             res = validator->check(currentTVColumn->field()->captionOrName(), 
01059                 newval, msg, desc);
01060         }
01061     }
01062 
01063     //show the validation result if not OK:
01064     if (res == Validator::Error) {
01065         if (!msg.isEmpty()) {
01066             if (desc.isEmpty())
01067                 KMessageBox::sorry(dynamic_cast<QWidget*>(this), msg);
01068             else
01069                 KMessageBox::detailedSorry(dynamic_cast<QWidget*>(this), msg, desc);
01070         }
01071         editCurrentCellAgain = true;
01072 //      allow = false;
01073     }
01074     else if (res == Validator::Warning) {
01075         //js: todo: message!!!
01076         KMessageBox::messageBox(dynamic_cast<QWidget*>(this), KMessageBox::Sorry, msg + "\n" + desc);
01077         editCurrentCellAgain = true;
01078     }
01079 
01080     if (res == Validator::Ok) {
01081         //2. check using signal
01082         //bool allow = true;
01083 //      emit aboutToChangeCell(d->pCurrentItem, newval, allow);
01084 //      if (allow) {
01085         //send changes to the backend
01086         QVariant visibleValue;
01087         if (!newval.isNull()/* visible value should be null if value is null */ 
01088             && currentTVColumn->visibleLookupColumnInfo)
01089         {
01090             visibleValue = m_editor->visibleValue(); //visible value for lookup field 
01091         }
01092         //should be also added to the buffer
01093         if (m_data->updateRowEditBufferRef(m_currentItem, m_curCol, currentTVColumn, 
01094             newval, /*allowSignals*/true, currentTVColumn->visibleLookupColumnInfo ? &visibleValue : 0))
01095         {
01096             kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): ------ EDIT BUFFER CHANGED TO:" << endl;
01097             m_data->rowEditBuffer()->debug();
01098         } else {
01099             kdDebug() << "KexiDataAwareObjectInterface::acceptEditor(): ------ CHANGE FAILED in KexiDataAwareObjectInterface::updateRowEditBuffer()" << endl;
01100             res = Validator::Error;
01101 
01102             //now: there might be called cancelEditor() in updateRowEditBuffer() handler,
01103             //if this is true, d->pEditor is NULL.
01104 
01105             if (m_editor && m_data->result()->column>=0 && m_data->result()->column<columns()) {
01106                 //move to faulty column (if m_editor is not cleared)
01107                 setCursorPosition(m_curRow, m_data->result()->column);
01108             }
01109             if (!m_data->result()->msg.isEmpty()) {
01110                 const int button = showErrorMessageForResult( m_data->result() );
01111                 if (KMessageBox::No == button) {
01112                     //discard changes
01113                     cancelEditor();
01114                     if (m_acceptsRowEditAfterCellAccepting)
01115                         cancelRowEdit();
01116                     m_inside_acceptEditor = false;
01117                     return false;
01118                 }
01119             }
01120         }
01121     }
01122 
01123     if (res == Validator::Ok) {
01124         removeEditor();
01125         /*emit*/ itemChanged(m_currentItem, m_curRow, m_curCol, 
01126             m_currentItem->at( realFieldNumber ));
01127         /*emit*/ itemChanged(m_currentItem, m_curRow, m_curCol);
01128     }
01129     m_inside_acceptEditor = false;
01130     if (res == Validator::Ok) {
01131         if (m_acceptsRowEditAfterCellAccepting || m_internal_acceptsRowEditAfterCellAccepting)
01132             acceptRowEdit();
01133         return true;
01134     }
01135     if (m_editor) {
01136         //allow to edit the cell again, (if m_pEditor is not cleared)
01137 
01138         if (m_editor->hasFocusableWidget()) {
01139             m_editor->showWidget();
01140             m_editor->setFocus();
01141         }
01142 //      startEditCurrentCell(newval.type()==QVariant::String ? newval.toString() : QString::null);
01143 //      m_editor->setFocus();
01144     }
01145     return false;
01146 }
01147 
01148 void KexiDataAwareObjectInterface::startEditCurrentCell(const QString &setText)
01149 {
01150     kdDebug() << "** KexiDataAwareObjectInterface::startEditCurrentCell("<<setText<<")"<<endl;
01151 //  if (columnType(d->curCol) == KexiDB::Field::Boolean)
01152 //      return;
01153     if (isReadOnly() || !columnEditable(m_curCol))
01154         return;
01155     if (m_editor) {
01156         if (m_editor->hasFocusableWidget()) {
01157             m_editor->showWidget();
01158             m_editor->setFocus();
01159         }
01160     }
01161 //  ensureVisible(columnPos(m_curCol), rowPos(m_curRow)+rowHeight(), 
01162 //      columnWidth(m_curCol), rowHeight());
01163 //OK?   
01164     //ensureCellVisible(m_curRow+1, m_curCol);
01165     if (!m_editor)
01166         createEditor(m_curRow, m_curCol, setText, !setText.isEmpty());
01167 }
01168 
01169 void KexiDataAwareObjectInterface::deleteAndStartEditCurrentCell()
01170 {
01171     if (isReadOnly() || !columnEditable(m_curCol))
01172         return;
01173     if (m_editor) {//if we've editor - just clear it
01174         m_editor->clear();
01175         return;
01176     }
01177 //js    if (columnType(m_curCol) == KexiDB::Field::Boolean)
01178 //js        return;
01179 //  ensureVisible(columnPos(m_curCol), rowPos(m_curRow) + rowHeight(), 
01180 //      columnWidth(m_curCol), rowHeight());
01181 //OK?
01182     ensureCellVisible(m_curRow+1, m_curCol);
01183     createEditor(m_curRow, m_curCol, QString::null, false/*removeOld*/);
01184     if (!m_editor)
01185         return;
01186     m_editor->clear();
01187     if (m_editor->acceptEditorAfterDeleteContents())
01188         acceptEditor();
01189     if (!m_editor || !m_editor->hasFocusableWidget())
01190         updateCell(m_curRow, m_curCol);
01191 }
01192 
01193 void KexiDataAwareObjectInterface::deleteCurrentRow()
01194 {
01195     if (m_newRowEditing) {//we're editing fresh new row: just cancel this!
01196         cancelRowEdit();
01197         return;
01198     }
01199 
01200     if (!acceptRowEdit())
01201         return;
01202 
01203     if (!isDeleteEnabled() || !m_currentItem || m_currentItem == m_insertItem)
01204         return;
01205     switch (m_deletionPolicy) {
01206     case NoDelete:
01207         return;
01208     case ImmediateDelete:
01209         break;
01210     case AskDelete:
01211         if (KMessageBox::Cancel == KMessageBox::warningContinueCancel(dynamic_cast<QWidget*>(this), 
01212             i18n("Do you want to delete selected row?"), 0, 
01213             KGuiItem(i18n("&Delete Row"),"editdelete"),
01214             "dontAskBeforeDeleteRow"/*config entry*/,
01215             KMessageBox::Notify|KMessageBox::Dangerous))
01216             return;
01217         break;
01218     case SignalDelete:
01219         /*emit*/ itemDeleteRequest(m_currentItem, m_curRow, m_curCol);
01220         /*emit*/ currentItemDeleteRequest();
01221         return;
01222     default:
01223         return;
01224     }
01225 
01226     if (!deleteItem(m_currentItem)) {//nothing
01227     }
01228 }
01229 
01230 KexiTableItem *KexiDataAwareObjectInterface::insertEmptyRow(int row)
01231 {
01232     if ( !acceptRowEdit() || !isEmptyRowInsertingEnabled() 
01233         || (row!=-1 && row >= ((int)rows()+(isInsertingEnabled()?1:0) ) ) )
01234         return 0;
01235 
01236     KexiTableItem *newItem = m_data->createItem(); //new KexiTableItem(m_data->columns.count());
01237     insertItem(newItem, row);
01238     return newItem;
01239 }
01240 
01241 void KexiDataAwareObjectInterface::insertItem(KexiTableItem *newItem, int row)
01242 {
01243     const bool changeCurrentRow = row==-1 || row==m_curRow;
01244     if (changeCurrentRow) {
01245         //change current row
01246         row = (m_curRow >= 0 ? m_curRow : 0);
01247         m_currentItem = newItem;
01248         m_curRow = row;
01249     }
01250     else if (m_curRow >= row) {
01251         m_curRow++;
01252     }
01253 
01254     m_data->insertRow(*newItem, row, true /*repaint*/);
01255 
01256     if (changeCurrentRow) {
01257         //update iter...
01258         m_itemIterator->toFirst();
01259         (*m_itemIterator)+=m_curRow;
01260     }
01261 /*
01262     QSize s(tableSize());
01263     resizeContents(s.width(),s.height());
01264 
01265     //redraw only this row and below:
01266     int leftcol = d->pTopHeader->sectionAt( d->pTopHeader->offset() );
01267 //  updateContents( columnPos( leftcol ), rowPos(d->curRow), 
01268 //      clipper()->width(), clipper()->height() - (rowPos(d->curRow) - contentsY()) );
01269     updateContents( columnPos( leftcol ), rowPos(row), 
01270         clipper()->width(), clipper()->height() - (rowPos(row) - contentsY()) );
01271 
01272     m_verticalHeader->addLabel();
01273 
01274     //update navigator's data
01275     setNavRowCount(rows());
01276 
01277     if (d->curRow >= row) {
01278         //update
01279         editorShowFocus( d->curRow, d->curCol );
01280     }
01281     */
01282 }
01283 
01284 void KexiDataAwareObjectInterface::slotRowInserted(KexiTableItem *item, bool repaint)
01285 {
01286     int row = m_data->findRef(item);
01287     slotRowInserted( item, row, repaint );
01288 }
01289 
01290 void KexiDataAwareObjectInterface::slotRowInserted(KexiTableItem * /*item*/, uint row, bool repaint)
01291 {
01292     if (repaint && (int)row<rows()) {
01293         updateWidgetContentsSize();
01294 
01295 /* updateAllVisibleRowsBelow() used instead
01296         //redraw only this row and below:
01297         int leftcol = d->pTopHeader->sectionAt( d->pTopHeader->offset() );
01298         updateContents( columnPos( leftcol ), rowPos(row), 
01299             clipper()->width(), clipper()->height() - (rowPos(row) - contentsY()) );
01300 */
01301         updateAllVisibleRowsBelow(row);
01302 
01303         if (!m_verticalHeaderAlreadyAdded) {
01304             if (m_verticalHeader)
01305                 m_verticalHeader->addLabel();
01306         }
01307         else //it was added because this inserting was interactive
01308             m_verticalHeaderAlreadyAdded = false;
01309 
01310         //update navigator's data
01311         m_navPanel->setRecordCount(rows());
01312 
01313         if (m_curRow >= (int)row) {
01314             //update
01315             editorShowFocus( m_curRow, m_curCol );
01316         }
01317     }
01318 }
01319 
01320 tristate KexiDataAwareObjectInterface::deleteAllRows(bool ask, bool repaint)
01321 {
01322     if (!hasData())
01323         return true;
01324     if (m_data->count()<1)
01325         return true;
01326 
01327     if (ask) {
01328         QString tableName = m_data->dbTableName();
01329         if (!tableName.isEmpty()) {
01330             tableName.prepend(" \"");
01331             tableName.append("\"");
01332         }
01333         if (KMessageBox::Cancel == KMessageBox::warningContinueCancel(dynamic_cast<QWidget*>(this),
01334                 i18n("Do you want to clear the contents of table %1?").arg(tableName),
01335                 0, KGuiItem(i18n("&Clear Contents")) ))
01336             return cancelled;
01337     }
01338 
01339     cancelRowEdit();
01340 //  acceptRowEdit();
01341 //  m_verticalHeader->clear();
01342     const bool repaintLater = repaint && m_spreadSheetMode;
01343     const int oldRows = rows();
01344 
01345     bool res = m_data->deleteAllRows(repaint && !repaintLater);
01346 
01347     if (res) {
01348         if (m_spreadSheetMode) {
01349 //          const uint columns = m_data->columns.count();
01350             for (int i=0; i<oldRows; i++) {
01351                 m_data->append(m_data->createItem());//new KexiTableItem(columns));
01352             }
01353         }
01354     }
01355     if (repaintLater)
01356         m_data->reload();
01357 
01358 //  d->clearVariables();
01359 //  m_verticalHeader->setCurrentRow(-1);
01360 
01361 //  d->pUpdateTimer->start(1,true);
01362 //  if (repaint)
01363 //      viewport()->repaint();
01364     return res;
01365 }
01366 
01367 void KexiDataAwareObjectInterface::clearColumns(bool repaint)
01368 {
01369     cancelRowEdit();
01370     m_data->clearInternal();
01371 
01372     clearColumnsInternal(repaint);
01373     updateIndicesForVisibleValues();
01374 
01375     if (repaint)
01376 //      viewport()->repaint();
01377 //OK?
01378         updateWidgetContents();
01379 
01380 /*  for(int i=0; i < rows(); i++)
01381     {
01382         m_verticalHeader->removeLabel();
01383     }
01384 
01385     editorCancel();
01386     m_contents->clear();
01387 
01388     d->clearVariables();
01389     d->numCols = 0;
01390 
01391     while(d->pTopHeader->count()>0)
01392         d->pTopHeader->removeLabel(0);
01393 
01394     m_verticalHeader->setCurrentRow(-1);
01395 
01396     viewport()->repaint();
01397 
01398 //  d->pColumnTypes.resize(0);
01399 //  d->pColumnModes.resize(0);
01400 //  d->pColumnDefaults.clear();*/
01401 }
01402 
01403 void KexiDataAwareObjectInterface::reloadData()
01404 {
01405 //  cancelRowEdit();
01406     acceptRowEdit();
01407     if (m_verticalHeader)
01408         m_verticalHeader->clear();
01409 
01410     if (m_curCol>=0 && m_curCol<columns()) {
01411         //find the editor for this column
01412         KexiDataItemInterface *edit = editor( m_curCol );
01413         if (edit) {
01414             edit->hideFocus();
01415         }
01416     }
01417 //  setCursorPosition(-1, -1, true);
01418     clearVariables();
01419     if (m_verticalHeader)
01420         m_verticalHeader->setCurrentRow(-1);
01421     
01422     if (dynamic_cast<QWidget*>(this) && dynamic_cast<QWidget*>(this)->isVisible())
01423         initDataContents();
01424     else
01425         m_initDataContentsOnShow = true;
01426 
01427     if (m_verticalHeader)
01428         m_verticalHeader->addLabels(m_data->count());
01429 
01430     updateWidgetScrollBars();
01431 }
01432 
01433 int KexiDataAwareObjectInterface::columnType(int col)
01434 {
01435     KexiTableViewColumn* c = m_data ? column(col) : 0;
01436     return c ? c->field()->type() : KexiDB::Field::InvalidType;
01437 }
01438 
01439 bool KexiDataAwareObjectInterface::columnEditable(int col)
01440 {
01441     KexiTableViewColumn* c = m_data ? column(col) : 0;
01442     return c ? (! c->isReadOnly()) : false;
01443 }
01444 
01445 int KexiDataAwareObjectInterface::rows() const
01446 {
01447     if (!hasData())
01448         return 0;
01449     return m_data->count();
01450 }
01451 
01452 int KexiDataAwareObjectInterface::dataColumns() const
01453 {
01454     if (!hasData())
01455         return 0;
01456     return m_data->columns.count();
01457 }
01458 
01459 QVariant KexiDataAwareObjectInterface::columnDefaultValue(int /*col*/) const
01460 {
01461     return QVariant(0);
01462 //TODO(js)  
01463 //  return m_data->columns[col].defaultValue;
01464 }
01465 
01466 void KexiDataAwareObjectInterface::setAcceptsRowEditAfterCellAccepting(bool set)
01467 {
01468     m_acceptsRowEditAfterCellAccepting = set;
01469 }
01470 
01471 void KexiDataAwareObjectInterface::setDropsAtRowEnabled(bool set)
01472 {
01473 //  const bool old = d->dropsAtRowEnabled;
01474     if (!set)
01475         m_dragIndicatorLine = -1;
01476     if (m_dropsAtRowEnabled && !set) {
01477         m_dropsAtRowEnabled = false;
01478 //      update();
01479         updateWidgetContents();
01480     }
01481     else {
01482         m_dropsAtRowEnabled = set;
01483     }
01484 }
01485 
01486 void KexiDataAwareObjectInterface::setEmptyRowInsertingEnabled(bool set)
01487 {
01488     m_emptyRowInsertingEnabled = set;
01489     /*emit*/ reloadActions();
01490 }
01491 
01492 void KexiDataAwareObjectInterface::slotAboutToDeleteRow(KexiTableItem& item, 
01493     KexiDB::ResultInfo* /*result*/, bool repaint)
01494 {
01495     if (repaint) {
01496         m_rowWillBeDeleted = m_data->findRef(&item);
01497     }
01498 }
01499 
01500 void KexiDataAwareObjectInterface::slotRowDeleted()
01501 {
01502     if (m_rowWillBeDeleted >= 0) {
01503         if (m_rowWillBeDeleted > 0 && m_rowWillBeDeleted >= (rows()-1) && !m_spreadSheetMode)
01504             m_rowWillBeDeleted = rows()-1; //move up if it's the last row
01505         updateWidgetContentsSize();
01506 
01507         if (! (m_spreadSheetMode && m_rowWillBeDeleted>=(rows()-1)))
01508             setCursorPosition(m_rowWillBeDeleted, m_curCol, true/*forceSet*/);
01509         if (m_verticalHeader)
01510             m_verticalHeader->removeLabel();
01511 
01512         updateAllVisibleRowsBelow(m_curRow); //needed for KexiTableView
01513 
01514         //update navigator's data
01515         m_navPanel->setRecordCount(rows());
01516 
01517         m_rowWillBeDeleted = -1;
01518     }
01519 }
01520 
01521 bool KexiDataAwareObjectInterface::beforeDeleteItem(KexiTableItem *)
01522 {
01523     //always return
01524     return true;
01525 }
01526 
01527 bool KexiDataAwareObjectInterface::deleteItem(KexiTableItem *item)/*, bool moveCursor)*/
01528 {
01529     if (!item || !beforeDeleteItem(item))
01530         return false;
01531 
01532     QString msg, desc;
01533 //  bool current = (item == d->pCurrentItem);
01534     const bool lastRowDeleted = m_spreadSheetMode && m_data->last() == item; //we need to know this so we
01535                                                                              //can return to the last row 
01536                                                                              //after reinserting it
01537     if (!m_data->deleteRow(*item, true /*repaint*/)) {
01538         /*const int button =*/ 
01539         showErrorMessageForResult( m_data->result() );
01540 //      if (KMessageBox::No == button) {
01541             //discard changes
01542     //  }
01543         return false;
01544     }
01545     else {
01546 //setCursorPosition() wil lset this!        if (current)
01547             //d->pCurrentItem = m_data->current();
01548     }
01549 
01550 //  repaintAfterDelete();
01551     if (m_spreadSheetMode) { //append empty row for spreadsheet mode
01552         m_data->append(m_data->createItem());//new KexiTableItem(m_data->columns.count()));
01553         if (m_verticalHeader)
01554             m_verticalHeader->addLabels(1);
01555         if (lastRowDeleted) //back to the last row
01556             setCursorPosition(rows()-1, m_curCol, true/*forceSet*/);
01557         /*emit*/ newItemAppendedForAfterDeletingInSpreadSheetMode();
01558     }
01559     return true;
01560 }
01561 
01562 KexiTableViewColumn* KexiDataAwareObjectInterface::column(int col)
01563 {
01564     return m_data->column(col);
01565 }
01566 
01567 bool KexiDataAwareObjectInterface::hasDefaultValueAt(const KexiTableViewColumn& tvcol)
01568 {
01569     if (m_rowEditing && m_data->rowEditBuffer() && m_data->rowEditBuffer()->isDBAware()) {
01570         return m_data->rowEditBuffer()->hasDefaultValueAt( *tvcol.columnInfo );
01571     }
01572     return false;
01573 }
01574 
01575 const QVariant* KexiDataAwareObjectInterface::bufferedValueAt(int col, bool useDefaultValueIfPossible)
01576 {
01577     if (m_rowEditing && m_data->rowEditBuffer())
01578     {
01579         KexiTableViewColumn* tvcol = column(col);
01580         if (tvcol->isDBAware) {
01581             //get the stored value
01582             const int realFieldNumber = fieldNumberForColumn(col);
01583             if (realFieldNumber < 0) {
01584                 kdWarning() << "KexiDataAwareObjectInterface::bufferedValueAt(): "
01585                     "fieldNumberForColumn(m_curCol) < 0" << endl;
01586                 return 0;
01587             }
01588             QVariant *storedValue = &m_currentItem->at( realFieldNumber );
01589         
01590             //db-aware data: now, try to find a buffered value (or default one)
01591             const QVariant *cv = m_data->rowEditBuffer()->at( *tvcol->columnInfo, 
01592                 storedValue->isNull() && useDefaultValueIfPossible);
01593             if (cv)
01594                 return cv;
01595             return storedValue;
01596         }
01597         //not db-aware data:
01598         const QVariant *cv = m_data->rowEditBuffer()->at( tvcol->field()->name() );
01599         if (cv)
01600             return cv;
01601     }
01602     //not db-aware data:
01603     const int realFieldNumber = fieldNumberForColumn(col);
01604     if (realFieldNumber < 0) {
01605         kdWarning() << "KexiDataAwareObjectInterface::bufferedValueAt(): "
01606             "fieldNumberForColumn(m_curCol) < 0" << endl;
01607         return 0;
01608     }
01609     return &m_currentItem->at( realFieldNumber );
01610 }
01611 
01612 void KexiDataAwareObjectInterface::startEditOrToggleValue()
01613 {
01614     if ( !isReadOnly() && columnEditable(m_curCol) ) {
01615         if (columnType(m_curCol) == KexiDB::Field::Boolean) {
01616             boolToggled();
01617         }
01618         else {
01619             startEditCurrentCell();
01620             return;
01621         }
01622     }
01623 }
01624 
01625 void KexiDataAwareObjectInterface::boolToggled()
01626 {
01627     startEditCurrentCell();
01628     if (m_editor) {
01629         m_editor->clickedOnContents();
01630     }
01631     acceptEditor();
01632     updateCell(m_curRow, m_curCol);
01633 
01634 /*  int s = m_currentItem->at(m_curCol).toInt();
01635     QVariant oldValue=m_currentItem->at(m_curCol);
01636     (*m_currentItem)[m_curCol] = QVariant(s ? 0 : 1);
01637     updateCell(m_curRow, m_curCol);
01638 //  emit itemChanged(m_currentItem, m_curRow, m_curCol, oldValue);
01639 //  emit itemChanged(m_currentItem, m_curRow, m_curCol);*/
01640 }
01641 
01642 void KexiDataAwareObjectInterface::slotDataDestroying()
01643 {
01644     m_data = 0;
01645     m_itemIterator = 0;
01646 }
01647 
01648 void KexiDataAwareObjectInterface::addNewRecordRequested()
01649 {
01650     if (!isInsertingEnabled())
01651         return;
01652     if (m_rowEditing) {
01653         if (!acceptRowEdit())
01654             return;
01655     }
01656 //  setFocus();
01657     selectRow(rows());
01658     startEditCurrentCell();
01659     if (m_editor)
01660         m_editor->setFocus();
01661 }
01662 
01663 bool KexiDataAwareObjectInterface::handleKeyPress(QKeyEvent *e, int &curRow, int &curCol, 
01664     bool fullRowSelection, bool *moveToFirstField, bool *moveToLastField)
01665 {
01666     if (moveToFirstField)
01667         *moveToFirstField = false;
01668     if (moveToLastField)
01669         *moveToLastField = false;
01670 
01671     const bool nobtn = e->state()==Qt::NoButton;
01672     const int k = e->key();
01673     //kdDebug() << "-----------" << e->state() << " " << k << endl;
01674 
01675     if ((k == Qt::Key_Up && nobtn) || (k == Qt::Key_PageUp && e->state()==Qt::ControlButton)) {
01676         selectPrevRow();
01677         e->accept();
01678     }
01679     else if ((k == Qt::Key_Down && nobtn) || (k == Qt::Key_PageDown && e->state()==Qt::ControlButton)) {
01680         selectNextRow();
01681         e->accept();
01682     }
01683     else if (k == Qt::Key_PageUp && nobtn) {
01684         selectPrevPage();
01685         e->accept();
01686     }
01687     else if (k == Qt::Key_PageDown && nobtn) {
01688         selectNextPage();
01689         e->accept();
01690     }
01691     else if (k == Qt::Key_Home) {
01692         if (fullRowSelection) {
01693             //we're in row-selection mode: home key always moves to 1st row
01694             curRow = 0;//to 1st row
01695         }
01696         else {//cell selection mode: different actions depending on ctrl and shift keys state
01697             if (nobtn) {
01698                 curCol = 0;//to 1st col
01699             }
01700             else if (e->state()==Qt::ControlButton) {
01701                 curRow = 0;//to 1st row and col
01702                 curCol = 0;
01703             }
01704             else
01705                 return false;
01706         }
01707         if (moveToFirstField)
01708             *moveToFirstField = true;
01709         //do not accept yet
01710         e->ignore();
01711     }
01712     else if (k == Qt::Key_End) {
01713         if (fullRowSelection) {
01714             //we're in row-selection mode: home key always moves to last row
01715             curRow = m_data->count()-1+(isInsertingEnabled()?1:0);//to last row
01716         }
01717         else {//cell selection mode: different actions depending on ctrl and shift keys state
01718             if (nobtn) {
01719                 curCol = columns()-1;//to last col
01720             }
01721             else if (e->state()==Qt::ControlButton) {
01722                 curRow = m_data->count()-1 /*+(isInsertingEnabled()?1:0)*/; //to last row and col
01723                 curCol = columns()-1;//to last col
01724             }
01725             else
01726                 return false;
01727         }
01728         if (moveToLastField)
01729             *moveToLastField = true;
01730         //do not accept yet
01731         e->ignore();
01732     }
01733     else if (isInsertingEnabled() && (e->state()==Qt::ControlButton && k == Qt::Key_Equal
01734         || e->state()==(Qt::ControlButton|Qt::ShiftButton) && k == Qt::Key_Equal)) {
01735         curRow = m_data->count(); //to the new row
01736         curCol = 0;//to first col
01737         if (moveToFirstField)
01738             *moveToFirstField = true;
01739         //do not accept yet
01740         e->ignore();
01741     }
01742     else
01743         return false;
01744 
01745     return true;
01746 }
01747 
01748 void KexiDataAwareObjectInterface::vScrollBarValueChanged(int v)
01749 {
01750     Q_UNUSED(v);
01751     if (!m_vScrollBarValueChanged_enabled)
01752         return;
01753 
01754     if (m_scrollbarToolTipsEnabled) {
01755         const QRect r( verticalScrollBar()->sliderRect() );
01756         const int row = lastVisibleRow()+1;
01757         if (row<=0) {
01758             m_scrollBarTipTimer.stop();
01759             m_scrollBarTip->hide();
01760             return;
01761         }
01762         m_scrollBarTip->setText( i18n("Row: ") + QString::number(row) );
01763         m_scrollBarTip->adjustSize();
01764         QWidget* thisWidget = dynamic_cast<QWidget*>(this);
01765         m_scrollBarTip->move( 
01766             thisWidget->mapToGlobal( r.topLeft() + verticalScrollBar()->pos() ) 
01767                 + QPoint( - m_scrollBarTip->width()-5, 
01768             r.height()/2 - m_scrollBarTip->height()/2) );
01769         if (verticalScrollBar()->draggingSlider()) {
01770             kdDebug(44021) << "  draggingSlider()  " << endl;
01771             m_scrollBarTipTimer.stop();
01772             m_scrollBarTip->show();
01773             m_scrollBarTip->raise();
01774         }
01775         else {
01776             m_scrollBarTipTimerCnt++;
01777             if (m_scrollBarTipTimerCnt>4) {
01778                 m_scrollBarTipTimerCnt=0;
01779                 m_scrollBarTip->show();
01780                 m_scrollBarTip->raise();
01781                 m_scrollBarTipTimer.start(500, true);
01782             }
01783         }
01784     }
01785     //update bottom view region
01786 /*  if (m_navPanel && (contentsHeight() - contentsY() - clipper()->height()) <= QMAX(d->rowHeight,m_navPanel->height())) {
01787         slotUpdate();
01788         triggerUpdate();
01789     }*/
01790 }
01791 
01792 bool KexiDataAwareObjectInterface::scrollbarToolTipsEnabled() const
01793 {
01794     return m_scrollbarToolTipsEnabled;
01795 }
01796 
01797 void KexiDataAwareObjectInterface::setScrollbarToolTipsEnabled(bool set)
01798 {
01799     m_scrollbarToolTipsEnabled = set;
01800 }
01801 
01802 void KexiDataAwareObjectInterface::vScrollBarSliderReleased()
01803 {
01804     kdDebug(44021) << "vScrollBarSliderReleased()" << endl;
01805     m_scrollBarTip->hide();
01806 }
01807 
01808 void KexiDataAwareObjectInterface::scrollBarTipTimeout()
01809 {
01810     if (m_scrollBarTip->isVisible()) {
01811 //      kdDebug(44021) << "TIMEOUT! - hide" << endl;
01812         if (m_scrollBarTipTimerCnt>0) {
01813             m_scrollBarTipTimerCnt=0;
01814             m_scrollBarTipTimer.start(500, true);
01815             return;
01816         }
01817         m_scrollBarTip->hide();
01818     }
01819     m_scrollBarTipTimerCnt=0;
01820 }
01821 
01822 void KexiDataAwareObjectInterface::focusOutEvent(QFocusEvent* e)
01823 {
01824     Q_UNUSED(e);
01825     m_scrollBarTipTimer.stop();
01826     m_scrollBarTip->hide();
01827     
01828     updateCell(m_curRow, m_curCol);
01829 }
01830 
01831 int KexiDataAwareObjectInterface::showErrorMessageForResult(KexiDB::ResultInfo* resultInfo)
01832 {
01833     QWidget *thisWidget = dynamic_cast<QWidget*>(this);
01834     if (resultInfo->allowToDiscardChanges) {
01835         return KMessageBox::questionYesNo(thisWidget, resultInfo->msg 
01836             + (resultInfo->desc.isEmpty() ? QString::null : ("\n"+resultInfo->desc)),
01837             QString::null, 
01838             KGuiItem(i18n("Correct Changes", "Correct"), QString::null, i18n("Correct changes")),
01839             KGuiItem(i18n("Discard Changes")) );
01840     }
01841 
01842     if (resultInfo->desc.isEmpty())
01843         KMessageBox::sorry(thisWidget, resultInfo->msg);
01844     else
01845         KMessageBox::detailedSorry(thisWidget, resultInfo->msg, resultInfo->desc);
01846     
01847     return KMessageBox::Ok;
01848 }
01849 
01850 void KexiDataAwareObjectInterface::updateIndicesForVisibleValues()
01851 {
01852     m_indicesForVisibleValues.resize( m_data ? m_data->columnsCount() : 0 );
01853     if (!m_data)
01854         return;
01855     for (uint i=0; i < m_data->columnsCount(); i++) {
01856         KexiTableViewColumn* tvCol = m_data->column(i);
01857         if (tvCol->columnInfo && tvCol->columnInfo->indexForVisibleLookupValue()!=-1)
01858             // retrieve visible value from lookup field
01859             m_indicesForVisibleValues[ i ] = tvCol->columnInfo->indexForVisibleLookupValue();
01860         else
01861             m_indicesForVisibleValues[ i ] = i;
01862     }
01863 }
01864 
01876 static inline bool findInString(const QString& stringValue, int stringLength, const QString& where, 
01877     int& firstCharacter, bool matchAnyPartOfField, bool matchWholeField, 
01878     bool caseSensitive, bool wholeWordsOnly, bool forward)
01879 {
01880     if (where.isEmpty()) {
01881         firstCharacter = -1;
01882         return false;
01883     }
01884 
01885     if (matchAnyPartOfField) {
01886         if (forward) {
01887             int pos = firstCharacter == -1 ? 0 : firstCharacter;
01888             if (wholeWordsOnly) {
01889                 const int whereLength = where.length();
01890                 while (true) {
01891                     pos = where.find( stringValue, pos, caseSensitive );
01892                     if (pos == -1)
01893                         break;
01894                     if ((pos > 0 && where.at(pos-1).isLetterOrNumber())
01895                         ||((pos+stringLength-1) < (whereLength-1) && where.at(pos+stringLength-1+1).isLetterOrNumber()))
01896                     {
01897                         pos++; // invalid match because before or after the string there is non-white space
01898                     }
01899                     else
01900                         break;
01901                 }//while
01902                 firstCharacter = pos;
01903             }
01904             else {// !wholeWordsOnly
01905                 firstCharacter = where.find( stringValue, pos, caseSensitive );
01906             }
01907             return firstCharacter != -1;
01908         }
01909         else { // !matchAnyPartOfField
01910             if (firstCharacter == INT_MAX) {
01911                 firstCharacter = -1; //next time we'll be looking at different cell
01912                 return false;
01913             }
01914             int pos = firstCharacter;
01915             if (wholeWordsOnly) {
01916                 const int whereLength = where.length();
01917                 while (true) {
01918                     pos = where.findRev( stringValue, pos, caseSensitive );
01919                     if (pos == -1)
01920                         break;
01921                     if ((pos > 0 && where.at(pos-1).isLetterOrNumber())
01922                         ||((pos+stringLength-1) < (whereLength-1) && where.at(pos+stringLength-1+1).isLetterOrNumber()))
01923                     {
01924                         // invalid match because before or after the string there is non-white space
01925                         pos--;
01926                         if (pos < 0) // it can make pos < 0
01927                             break;
01928                     }
01929                     else
01930                         break;
01931                 }//while
01932                 firstCharacter = pos;
01933             }
01934             else {// !wholeWordsOnly
01935                 firstCharacter = where.findRev( stringValue, pos, caseSensitive );
01936             }
01937             return firstCharacter != -1;
01938         }
01939     }
01940     else if (matchWholeField) {
01941         if (firstCharacter != -1 && firstCharacter != 0) { //we're not at 0-th char
01942             firstCharacter = -1;
01943         }
01944         else if ( (caseSensitive ? where : where.lower()) == stringValue) {
01945             firstCharacter = 0;
01946             return true;
01947         }
01948     }
01949     else {// matchStartOfField
01950         if (firstCharacter != -1 && firstCharacter != 0) { //we're not at 0-th char
01951             firstCharacter = -1;
01952         }
01953         else if (where.startsWith(stringValue, caseSensitive)) {
01954             if (wholeWordsOnly) {
01955                 // If where.length() < stringValue.length(), true will be returned too - fine.
01956                 return !where.at( stringValue.length() ).isLetterOrNumber();
01957             }
01958             firstCharacter = 0;
01959             return true;
01960         }
01961     }
01962     return false;
01963 }
01964 
01965 tristate KexiDataAwareObjectInterface::find(const QVariant& valueToFind, 
01966     const KexiSearchAndReplaceViewInterface::Options& options, bool next)
01967 {
01968     if (!hasData())
01969         return cancelled;
01970     const QVariant prevSearchedValue( m_recentlySearchedValue );
01971     m_recentlySearchedValue = valueToFind;
01972     const KexiSearchAndReplaceViewInterface::Options::SearchDirection prevSearchDirection = m_recentSearchDirection;
01973     m_recentSearchDirection = options.searchDirection;
01974     if (valueToFind.isNull() || valueToFind.toString().isEmpty())
01975         return cancelled;
01976 
01977     const bool forward = (options.searchDirection == KexiSearchAndReplaceViewInterface::Options::SearchUp) 
01978         ? !next : next; //direction can be reversed
01979 
01980     if ((!prevSearchedValue.isNull() && prevSearchedValue!=valueToFind)
01981         || (prevSearchDirection!=options.searchDirection && options.searchDirection==KexiSearchAndReplaceViewInterface::Options::SearchAllRows))
01982     {
01983         // restart searching when value has been changed or new direction is SearchAllRows
01984         m_positionOfRecentlyFoundValue.exists = false;
01985     }
01986 
01987     const bool startFrom1stRowAndCol = !m_positionOfRecentlyFoundValue.exists && next
01988         && options.searchDirection == KexiSearchAndReplaceViewInterface::Options::SearchAllRows;
01989     const bool startFromLastRowAndCol = 
01990           (!m_positionOfRecentlyFoundValue.exists && !next && options.searchDirection == KexiSearchAndReplaceViewInterface::Options::SearchAllRows)
01991         ||(m_curRow >= rows() && !forward); //we're at "insert" row, and searching backwards: move to the last cell
01992 
01993     if (!startFrom1stRowAndCol && !startFromLastRowAndCol && m_curRow >= rows()) {
01994         //we're at "insert" row, and searching forward: no chances to find something
01995         return false;
01996     }
01997     KexiTableViewData::Iterator it( (startFrom1stRowAndCol || startFromLastRowAndCol)
01998         ? m_data->iterator() : *m_itemIterator /*start from the current cell*/ );
01999     if (startFromLastRowAndCol)
02000         it.toLast();
02001     int firstCharacter;
02002     if (m_positionOfRecentlyFoundValue.exists) {// start after the next/prev char position
02003         if (forward)
02004             firstCharacter = m_positionOfRecentlyFoundValue.lastCharacter + 1;
02005         else {
02006             firstCharacter = (m_positionOfRecentlyFoundValue.firstCharacter > 0) ? 
02007                 (m_positionOfRecentlyFoundValue.firstCharacter - 1) : INT_MAX /* this means 'before first'*/;
02008         }
02009     }
02010     else {
02011         firstCharacter = -1; //forward ? -1 : INT_MAX;
02012     }
02013 
02014     const int columnsCount = m_data->columnsCount();
02015     int row, col;
02016     if (startFrom1stRowAndCol) {
02017         row = 0;
02018         col = 0;
02019     }
02020     else if (startFromLastRowAndCol) {
02021         row = rows()-1;
02022         col = columnsCount-1;
02023     }
02024     else {
02025         row = m_curRow;
02026         col = m_curCol;
02027     }
02028 
02029     //sache some flags for efficiency
02030     const bool matchAnyPartOfField 
02031         = options.textMatching == KexiSearchAndReplaceViewInterface::Options::MatchAnyPartOfField;
02032     const bool matchWholeField 
02033         = options.textMatching == KexiSearchAndReplaceViewInterface::Options::MatchWholeField;
02034     const bool caseSensitive = options.caseSensitive;
02035     const bool wholeWordsOnly = options.wholeWordsOnly;
02036 //unused    const bool promptOnReplace = options.promptOnReplace;
02037     int columnNumber = (options.columnNumber == KexiSearchAndReplaceViewInterface::Options::CurrentColumn) 
02038         ? m_curCol : options.columnNumber;
02039     if (columnNumber>=0)
02040         col = columnNumber;
02041     const bool lookInAllColumns = columnNumber == KexiSearchAndReplaceViewInterface::Options::AllColumns;
02042     int firstColumn; // real number of the first column, can be smaller than lastColumn if forward==true
02043     int lastColumn; // real number of the last column
02044     if (lookInAllColumns) {
02045         firstColumn = forward ? 0 : columnsCount-1;
02046         lastColumn = forward ? columnsCount-1 : 0;
02047     }
02048     else {
02049         firstColumn = columnNumber;
02050         lastColumn = columnNumber;
02051     }
02052     const QString stringValue( caseSensitive ? valueToFind.toString() : valueToFind.toString().lower() );
02053     const int stringLength = stringValue.length();
02054 
02055     // search
02056     const int prevRow = m_curRow;
02057     KexiTableItem *item;
02058     while ( (item = it.current()) ) {
02059         for (; forward ? col <= lastColumn : col >= lastColumn; 
02060             col = forward ? (col+1) : (col-1))
02061         {
02062             const QVariant cell( item->at( m_indicesForVisibleValues[ col ] ) );
02063             if (findInString(stringValue, stringLength, cell.toString(), firstCharacter, 
02064                 matchAnyPartOfField, matchWholeField, caseSensitive, wholeWordsOnly, forward))
02065             {
02066                 //*m_itemIterator = it;
02067                 //m_currentItem = *it;
02068                 //m_curRow = row;
02069                 //m_curCol = col;
02070                 setCursorPosition(row, col, true/*forceSet*/);
02071                 if (prevRow != m_curRow)
02072                     updateRow(prevRow);
02073                 // remember the exact position for the found value
02074                 m_positionOfRecentlyFoundValue.exists = true;
02075                 m_positionOfRecentlyFoundValue.firstCharacter = firstCharacter;
02077                 m_positionOfRecentlyFoundValue.lastCharacter = firstCharacter + stringLength - 1;
02078                 return true;
02079             }
02080         }//for
02081         if (forward) {
02082             ++it;
02083             ++row;
02084         }
02085         else {
02086             --it;
02087             --row;
02088         }
02089         col = firstColumn;
02090     }//while
02091     return false;
02092 }
02093 
02094 tristate KexiDataAwareObjectInterface::findNextAndReplace(
02095     const QVariant& valueToFind, const QVariant& replacement, 
02096     const KexiSearchAndReplaceViewInterface::Options& options, bool replaceAll)
02097 {
02098     Q_UNUSED(replacement);
02099     Q_UNUSED(options);
02100     Q_UNUSED(replaceAll);
02101     
02102     if (isReadOnly())
02103         return cancelled;
02104     if (valueToFind.isNull() || valueToFind.toString().isEmpty())
02105         return cancelled;
02107     return false;
02108 }
KDE Home | KDE Accessibility Home | Description of Access Keys