kexi

kexidataawareobjectiface.cpp

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