kexi

kexitableview.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002 Till Busch <till@bux.at>
00003    Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
00004    Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
00005    Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
00006    Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
00007 
00008    This program is free software; you can redistribute it and/or
00009    modify it under the terms of the GNU Library General Public
00010    License as published by the Free Software Foundation; either
00011    version 2 of the License, or (at your option) any later version.
00012 
00013    This program is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016    Library General Public License for more details.
00017 
00018    You should have received a copy of the GNU Library General Public License
00019    along with this program; see the file COPYING.  If not, write to
00020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021  * Boston, MA 02110-1301, USA.
00022 
00023    Original Author:  Till Busch <till@bux.at>
00024    Original Project: buX (www.bux.at)
00025 */
00026 
00027 #include <qpainter.h>
00028 #include <qkeycode.h>
00029 #include <qlineedit.h>
00030 #include <qcombobox.h>
00031 #include <qwmatrix.h>
00032 #include <qtimer.h>
00033 #include <qpopupmenu.h>
00034 #include <qcursor.h>
00035 #include <qstyle.h>
00036 #include <qlayout.h>
00037 #include <qlabel.h>
00038 #include <qwhatsthis.h>
00039 
00040 #include <kglobal.h>
00041 #include <klocale.h>
00042 #include <kdebug.h>
00043 #include <kapplication.h>
00044 #include <kiconloader.h>
00045 #include <kmessagebox.h>
00046 
00047 #ifndef KEXI_NO_PRINT
00048 # include <kprinter.h>
00049 #endif
00050 
00051 #include "kexitableview.h"
00052 #include <kexiutils/utils.h>
00053 #include <kexiutils/validator.h>
00054 
00055 #include "kexicelleditorfactory.h"
00056 #include "kexitableviewheader.h"
00057 #include "kexitableview_p.h"
00058 #include <widget/utils/kexirecordmarker.h>
00059 #include <widget/utils/kexidisplayutils.h>
00060 #include <kexidb/cursor.h>
00061 
00062 KexiTableView::Appearance::Appearance(QWidget *widget)
00063  : alternateBackgroundColor( KGlobalSettings::alternateBackgroundColor() )
00064 {
00065     //set defaults
00066     if (qApp) {
00067         QPalette p = widget ? widget->palette() : qApp->palette();
00068         baseColor = p.active().base();
00069         textColor = p.active().text();
00070         borderColor = QColor(200,200,200);
00071         emptyAreaColor = p.active().color(QColorGroup::Base);
00072         rowHighlightingColor = KexiUtils::blendedColors(p.active().highlight(), baseColor, 33, 66);
00073         rowMouseOverHighlightingColor = KexiUtils::blendedColors(p.active().highlight(), baseColor, 10, 90);
00074         rowMouseOverAlternateHighlightingColor = KexiUtils::blendedColors(p.active().highlight(), alternateBackgroundColor, 10, 90);
00075         rowHighlightingTextColor = textColor;
00076         rowMouseOverHighlightingTextColor = textColor;
00077     }
00078     backgroundAltering = true;
00079     rowMouseOverHighlightingEnabled = true;
00080     rowHighlightingEnabled = true;
00081     persistentSelections = true;
00082     navigatorEnabled = true;
00083     fullRowSelection = false;
00084     gridEnabled = true;
00085 }
00086 
00087 //-----------------------------------------
00088 
00090 class KexiTableView::WhatsThis : public QWhatsThis
00091 {
00092     public:
00093         WhatsThis(KexiTableView* tv) : QWhatsThis(tv), m_tv(tv)
00094         {
00095             Q_ASSERT(tv);
00096         }
00097         virtual ~WhatsThis()
00098         {
00099         }
00100         virtual QString text( const QPoint & pos)
00101         {
00102             const int leftMargin = m_tv->verticalHeaderVisible() ? m_tv->verticalHeader()->width() : 0;
00103             //const int topMargin = m_tv->horizontalHeaderVisible() ? m_tv->d->pTopHeader->height() : 0;
00104             //const int bottomMargin = m_tv->d->appearance.navigatorEnabled ? m_tv->m_navPanel->height() : 0;
00105             if (KexiUtils::hasParent(m_tv->verticalHeader(), m_tv->childAt(pos))) {
00106                 return i18n("Contains a pointer to the currently selected row");
00107             }
00108             else if (KexiUtils::hasParent(m_tv->m_navPanel, m_tv->childAt(pos))) {
00109                 return i18n("Row navigator");
00110 //              return QWhatsThis::textFor(m_tv->m_navPanel, QPoint( pos.x(), pos.y() - m_tv->height() + bottomMargin ));
00111             }
00112             KexiDB::Field *f = m_tv->field( m_tv->columnAt(pos.x()-leftMargin) );
00113             if (!f)
00114                 return QString::null;
00115             return f->description().isEmpty() ? f->captionOrName() : f->description();
00116         }
00117         protected:
00118             KexiTableView *m_tv;
00119 };
00120 
00121 //-----------------------------------------
00122 
00123 KexiTableViewCellToolTip::KexiTableViewCellToolTip( KexiTableView * tableView )
00124  : QToolTip(tableView->viewport())
00125  , m_tableView(tableView)
00126 {
00127 }
00128 
00129 KexiTableViewCellToolTip::~KexiTableViewCellToolTip()
00130 {
00131     remove(parentWidget());
00132 }
00133 
00134 void KexiTableViewCellToolTip::maybeTip( const QPoint & p )
00135 {
00136     const QPoint cp( m_tableView->viewportToContents( p ) );
00137     const int row = m_tableView->rowAt( cp.y(), true/*ignoreEnd*/ );
00138     const int col = m_tableView->columnAt( cp.x() );
00139 
00140     //show tooltip if needed
00141     if (col>=0 && row>=0) {
00142         KexiTableEdit *editor = m_tableView->tableEditorWidget( col );
00143         const bool insertRowSelected = m_tableView->isInsertingEnabled() && row==m_tableView->rows();
00144         KexiTableItem *item = insertRowSelected ? m_tableView->m_insertItem : m_tableView->itemAt( row );
00145         if (editor && item && (col < (int)item->count())) {
00146             int w = m_tableView->columnWidth( col );
00147             int h = m_tableView->rowHeight();
00148             int x = 0;
00149             int y_offset = 0;
00150             int align = Qt::SingleLine | Qt::AlignVCenter;
00151             QString txtValue;
00152             QVariant cellValue;
00153             KexiTableViewColumn *tvcol = m_tableView->column(col);
00154             if (!m_tableView->getVisibleLookupValue(cellValue, editor, item, tvcol))
00155                 cellValue = insertRowSelected ? editor->displayedField()->defaultValue() : item->at(col); //display default value if available
00156             const bool focused = m_tableView->selectedItem() == item && col == m_tableView->currentColumn();
00157             editor->setupContents( 0, focused, cellValue, txtValue, align, x, y_offset, w, h );
00158             QRect realRect(m_tableView->columnPos(col)-m_tableView->contentsX(), 
00159                 m_tableView->rowPos(row)-m_tableView->contentsY(), w, h); //m_tableView->cellGeometry( row, col ));
00160             if (editor->showToolTipIfNeeded(
00161                 txtValue.isEmpty() ? item->at(col) : QVariant(txtValue), 
00162                 realRect, m_tableView->fontMetrics(), focused))
00163             {
00164                 QString squeezedTxtValue;
00165                 if (txtValue.length() > 50)
00166                     squeezedTxtValue = txtValue.left(100) + "...";
00167                 else
00168                     squeezedTxtValue = txtValue;
00169                 tip( realRect, squeezedTxtValue );
00170             }
00171         }
00172     }
00173 }
00174 
00175 //-----------------------------------------
00176 
00177 KexiTableView::KexiTableView(KexiTableViewData* data, QWidget* parent, const char* name)
00178 : QScrollView(parent, name, /*Qt::WRepaintNoErase | */Qt::WStaticContents /*| Qt::WResizeNoErase*/)
00179 , KexiRecordNavigatorHandler()
00180 , KexiSharedActionClient()
00181 , KexiDataAwareObjectInterface()
00182 {
00183 //not needed    KexiTableView::initCellEditorFactories();
00184 
00185     d = new KexiTableViewPrivate(this);
00186 
00187     connect( kapp, SIGNAL( settingsChanged(int) ), SLOT( slotSettingsChanged(int) ) );
00188     slotSettingsChanged(KApplication::SETTINGS_SHORTCUTS);
00189 
00190     m_data = new KexiTableViewData(); //to prevent crash because m_data==0
00191     m_owner = true;                   //-this will be deleted if needed
00192 
00193     setResizePolicy(Manual);
00194     viewport()->setBackgroundMode(Qt::NoBackground);
00195 //  viewport()->setFocusPolicy(StrongFocus);
00196     viewport()->setFocusPolicy(WheelFocus);
00197     setFocusPolicy(WheelFocus); //<--- !!!!! important (was NoFocus), 
00198     //                             otherwise QApplication::setActiveWindow() won't activate 
00199     //                             this widget when needed!
00200 //  setFocusProxy(viewport());
00201     viewport()->installEventFilter(this);
00202 
00203     //setup colors defaults
00204     setBackgroundMode(Qt::PaletteBackground);
00205 //  setEmptyAreaColor(d->appearance.baseColor);//palette().active().color(QColorGroup::Base));
00206 
00207 //  d->baseColor = colorGroup().base();
00208 //  d->textColor = colorGroup().text();
00209 
00210 //  d->altColor = KGlobalSettings::alternateBackgroundColor();
00211 //  d->grayColor = QColor(200,200,200);
00212     d->diagonalGrayPattern = QBrush(d->appearance.borderColor, Qt::BDiagPattern);
00213 
00214     setLineWidth(1);
00215     horizontalScrollBar()->installEventFilter(this);
00216     horizontalScrollBar()->raise();
00217     verticalScrollBar()->raise();
00218 
00219     //context menu
00220     m_popupMenu = new KPopupMenu(this, "contextMenu");
00221 #if 0 //moved to mainwindow's actions
00222     d->menu_id_addRecord = m_popupMenu->insertItem(i18n("Add Record"), this, SLOT(addRecord()), Qt::CTRL+Qt::Key_Insert);
00223     d->menu_id_removeRecord = m_popupMenu->insertItem(
00224         kapp->iconLoader()->loadIcon("button_cancel", KIcon::Small),
00225         i18n("Remove Record"), this, SLOT(removeRecord()), Qt::CTRL+Qt::Key_Delete);
00226 #endif
00227 
00228 #ifdef Q_WS_WIN
00229     d->rowHeight = fontMetrics().lineSpacing() + 4;
00230 #else
00231     d->rowHeight = fontMetrics().lineSpacing() + 1;
00232 #endif
00233 
00234     if(d->rowHeight < 17)
00235         d->rowHeight = 17;
00236 
00237     d->pUpdateTimer = new QTimer(this);
00238 
00239 //  setMargins(14, fontMetrics().height() + 4, 0, 0);
00240 
00241     // Create headers
00242     m_horizontalHeader = new KexiTableViewHeader(this, "topHeader");
00243     m_horizontalHeader->setSelectionBackgroundColor( palette().active().highlight() );
00244     m_horizontalHeader->setOrientation(Qt::Horizontal);
00245     m_horizontalHeader->setTracking(false);
00246     m_horizontalHeader->setMovingEnabled(false);
00247     connect(m_horizontalHeader, SIGNAL(sizeChange(int,int,int)), this, SLOT(slotTopHeaderSizeChange(int,int,int)));
00248 
00249     m_verticalHeader = new KexiRecordMarker(this, "rm");
00250     m_verticalHeader->setSelectionBackgroundColor( palette().active().highlight() );
00251     m_verticalHeader->setCellHeight(d->rowHeight);
00252 //  m_verticalHeader->setFixedWidth(d->rowHeight);
00253     m_verticalHeader->setCurrentRow(-1);
00254 
00255     setMargins(
00256         QMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight),
00257         m_horizontalHeader->sizeHint().height(), 0, 0);
00258 
00259     setupNavigator();
00260 
00261 //  setMinimumHeight(horizontalScrollBar()->height() + d->rowHeight + topMargin());
00262 
00263 //  navPanelLyr->addStretch(25);
00264 //  enableClipper(true);
00265 
00266     if (data)
00267         setData( data );
00268 
00269 #if 0//(js) doesn't work!
00270     d->scrollTimer = new QTimer(this);
00271     connect(d->scrollTimer, SIGNAL(timeout()), this, SLOT(slotAutoScroll()));
00272 #endif
00273 
00274 //  setBackgroundAltering(true);
00275 //  setFullRowSelectionEnabled(false);
00276 
00277     setAcceptDrops(true);
00278     viewport()->setAcceptDrops(true);
00279 
00280     // Connect header, table and scrollbars
00281     connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), m_horizontalHeader, SLOT(setOffset(int)));
00282     connect(verticalScrollBar(), SIGNAL(valueChanged(int)), m_verticalHeader, SLOT(setOffset(int)));
00283     connect(m_horizontalHeader, SIGNAL(sizeChange(int, int, int)), this, SLOT(slotColumnWidthChanged(int, int, int)));
00284     connect(m_horizontalHeader, SIGNAL(sectionHandleDoubleClicked(int)), this, SLOT(slotSectionHandleDoubleClicked(int)));
00285     connect(m_horizontalHeader, SIGNAL(clicked(int)), this, SLOT(sortColumnInternal(int)));
00286 
00287     connect(d->pUpdateTimer, SIGNAL(timeout()), this, SLOT(slotUpdate()));
00288     
00289 //  horizontalScrollBar()->show();
00290     updateScrollBars();
00291 //  resize(sizeHint());
00292 //  updateContents();
00293 //  setMinimumHeight(horizontalScrollBar()->height() + d->rowHeight + topMargin());
00294 
00295 //TMP
00296 //setVerticalHeaderVisible(false);
00297 //setHorizontalHeaderVisible(false);
00298 
00299 //will be updated by setAppearance: updateFonts();
00300     setAppearance(d->appearance); //refresh
00301 
00302     d->cellToolTip = new KexiTableViewCellToolTip(this);
00303     new WhatsThis(this);
00304 }
00305 
00306 KexiTableView::~KexiTableView()
00307 {
00308     cancelRowEdit();
00309 
00310     KexiTableViewData *data = m_data;
00311     m_data = 0;
00312     if (m_owner) {
00313         if (data)
00314             data->deleteLater();
00315     }
00316     delete d;
00317 }
00318 
00319 void KexiTableView::clearVariables()
00320 {
00321     KexiDataAwareObjectInterface::clearVariables();
00322     d->clearVariables();
00323 }
00324 
00325 /*void KexiTableView::initActions(KActionCollection *ac)
00326 {
00327     emit reloadActions(ac);
00328 }*/
00329 
00330 void KexiTableView::setupNavigator()
00331 {
00332     updateScrollBars();
00333     
00334     m_navPanel = new KexiRecordNavigator(this, leftMargin(), "navPanel");
00335     m_navPanel->setRecordHandler(this);
00336     m_navPanel->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Preferred);
00337 }
00338 
00339 void KexiTableView::initDataContents()
00340 {
00341     updateWidgetContentsSize();
00342 
00343     KexiDataAwareObjectInterface::initDataContents();
00344 
00345     m_navPanel->showEditingIndicator(false);
00346 }
00347 
00348 void KexiTableView::addHeaderColumn(const QString& caption, const QString& description, 
00349     const QIconSet& icon, int width)
00350 {
00351     const int nr = m_horizontalHeader->count();
00352     if (icon.isNull())
00353         m_horizontalHeader->addLabel(caption, width);
00354     else
00355         m_horizontalHeader->addLabel(icon, caption, width);
00356 
00357     if (!description.isEmpty())
00358         m_horizontalHeader->setToolTip(nr, description);
00359 }
00360 
00361 void KexiTableView::updateWidgetContentsSize()
00362 {
00363     QSize s(tableSize());
00364     resizeContents(s.width(), s.height());
00365 }
00366 
00367 void KexiTableView::slotRowsDeleted( const QValueList<int> &rows )
00368 {
00369     viewport()->repaint();
00370     updateWidgetContentsSize();
00371     setCursorPosition(QMAX(0, (int)m_curRow - (int)rows.count()), -1, true);
00372 }
00373 
00374 
00375 /*void KexiTableView::addDropFilter(const QString &filter)
00376 {
00377     d->dropFilters.append(filter);
00378     viewport()->setAcceptDrops(true);
00379 }*/
00380 
00381 void KexiTableView::setFont( const QFont &font )
00382 {
00383     QScrollView::setFont(font);
00384     updateFonts(true);
00385 }
00386 
00387 void KexiTableView::updateFonts(bool repaint)
00388 {
00389 #ifdef Q_WS_WIN
00390     d->rowHeight = fontMetrics().lineSpacing() + 4;
00391 #else
00392     d->rowHeight = fontMetrics().lineSpacing() + 1;
00393 #endif
00394     if (d->appearance.fullRowSelection) {
00395         d->rowHeight -= 1;
00396     }
00397     if(d->rowHeight < 17)
00398         d->rowHeight = 17;
00399 //  if(d->rowHeight < 22)
00400 //      d->rowHeight = 22;
00401     setMargins(
00402         QMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight),
00403         m_horizontalHeader->sizeHint().height(), 0, 0);
00404 //  setMargins(14, d->rowHeight, 0, 0);
00405     m_verticalHeader->setCellHeight(d->rowHeight);
00406 
00407     KexiDisplayUtils::initDisplayForAutonumberSign(d->autonumberSignDisplayParameters, this);
00408     KexiDisplayUtils::initDisplayForDefaultValue(d->defaultValueDisplayParameters, this);
00409 
00410     if (repaint)
00411         updateContents();
00412 }
00413 
00414 void KexiTableView::updateAllVisibleRowsBelow(int row)
00415 {
00416     //get last visible row
00417     int r = rowAt(clipper()->height()+contentsY());
00418     if (r==-1) {
00419         r = rows()+1+(isInsertingEnabled()?1:0);
00420     }
00421     //update all visible rows below 
00422     int leftcol = m_horizontalHeader->sectionAt( m_horizontalHeader->offset() );
00423 //  int row = m_curRow;
00424     updateContents( columnPos( leftcol ), rowPos(row), 
00425         clipper()->width(), clipper()->height() - (rowPos(row) - contentsY()) );
00426 }
00427 
00428 void KexiTableView::clearColumnsInternal(bool /*repaint*/)
00429 {
00430     while(m_horizontalHeader->count()>0)
00431         m_horizontalHeader->removeLabel(0);
00432 }
00433 
00434 void KexiTableView::slotUpdate()
00435 {
00436 //  kdDebug(44021) << " KexiTableView::slotUpdate() -- " << endl;
00437 //  QSize s(tableSize());
00438 //  viewport()->setUpdatesEnabled(false);
00440 //  viewport()->setUpdatesEnabled(true);
00441 
00442     updateContents();
00443     updateScrollBars();
00444     if (m_navPanel)
00445         m_navPanel->updateGeometry(leftMargin());
00446 //  updateNavPanelGeometry();
00447 
00448     updateWidgetContentsSize();
00449 //  updateContents(0, contentsY()+clipper()->height()-2*d->rowHeight, clipper()->width(), d->rowHeight*3);
00450     
00451     //updateGeometries();
00452 //  updateContents(0, 0, viewport()->width(), contentsHeight());
00453 //  updateGeometries();
00454 }
00455 
00456 int KexiTableView::currentLocalSortingOrder() const
00457 {
00458     if (m_horizontalHeader->sortIndicatorSection()==-1)
00459         return 0;
00460     return (m_horizontalHeader->sortIndicatorOrder() == Qt::Ascending) ? 1 : -1;
00461 }
00462 
00463 void KexiTableView::setLocalSortingOrder(int col, int order)
00464 {
00465     if (order == 0)
00466         col = -1;
00467     if (col>=0)
00468         m_horizontalHeader->setSortIndicator(col, (order==1) ? Qt::Ascending : Qt::Descending);
00469 }
00470 
00471 int KexiTableView::currentLocalSortColumn() const
00472 {
00473     return m_horizontalHeader->sortIndicatorSection();
00474 }
00475 
00476 void KexiTableView::updateGUIAfterSorting()
00477 {
00478     int cw = columnWidth(m_curCol);
00479     int rh = rowHeight();
00480 
00481 //  m_verticalHeader->setCurrentRow(m_curRow);
00482     center(columnPos(m_curCol) + cw / 2, rowPos(m_curRow) + rh / 2);
00483 //  updateCell(oldRow, m_curCol);
00484 //  updateCell(m_curRow, m_curCol);
00485 //  slotUpdate();
00486 
00487     updateContents();
00488 //  d->pUpdateTimer->start(1,true);
00489 }
00490 
00491 QSizePolicy KexiTableView::sizePolicy() const
00492 {
00493     // this widget is expandable
00494     return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
00495 }
00496 
00497 QSize KexiTableView::sizeHint() const
00498 {
00499     const QSize &ts = tableSize();
00500     int w = QMAX( ts.width() + leftMargin()+ verticalScrollBar()->sizeHint().width() + 2*2, 
00501         (m_navPanel->isVisible() ? m_navPanel->width() : 0) );
00502     int h = QMAX( ts.height()+topMargin()+horizontalScrollBar()->sizeHint().height(), 
00503         minimumSizeHint().height() );
00504     w = QMIN( w, qApp->desktop()->width()*3/4 ); //stretch
00505     h = QMIN( h, qApp->desktop()->height()*3/4 ); //stretch
00506 
00507 //  kexidbg << "KexiTableView::sizeHint()= " <<w <<", " <<h << endl;
00508 
00509     return QSize(w, h);
00510         /*QSize(
00511         QMAX( ts.width() + leftMargin() + 2*2, (m_navPanel ? m_navPanel->width() : 0) ),
00512         //+ QMIN(m_verticalHeader->width(),d->rowHeight) + margin()*2,
00513         QMAX( ts.height()+topMargin()+horizontalScrollBar()->sizeHint().height(), 
00514             minimumSizeHint().height() )
00515     );*/
00516 //      QMAX(ts.height() + topMargin(), minimumSizeHint().height()) );
00517 }
00518 
00519 QSize KexiTableView::minimumSizeHint() const
00520 {
00521     return QSize(
00522         leftMargin() + ((columns()>0)?columnWidth(0):KEXI_DEFAULT_DATA_COLUMN_WIDTH) + 2*2, 
00523         d->rowHeight*5/2 + topMargin() + (m_navPanel && m_navPanel->isVisible() ? m_navPanel->height() : 0)
00524     );
00525 }
00526 
00527 void KexiTableView::createBuffer(int width, int height)
00528 {
00529     if(!d->pBufferPm)
00530         d->pBufferPm = new QPixmap(width, height);
00531     else
00532         if(d->pBufferPm->width() < width || d->pBufferPm->height() < height)
00533             d->pBufferPm->resize(width, height);
00534 //  d->pBufferPm->fill();
00535 }
00536 
00537 //internal
00538 inline void KexiTableView::paintRow(KexiTableItem *item,
00539     QPainter *pb, int r, int rowp, int cx, int cy, 
00540     int colfirst, int collast, int maxwc)
00541 {
00542     if (!item)
00543         return;
00544     // Go through the columns in the row r
00545     // if we know from where to where, go through [colfirst, collast],
00546     // else go through all of them
00547     if (colfirst==-1)
00548         colfirst=0;
00549     if (collast==-1)
00550         collast=columns()-1;
00551 
00552     int transly = rowp-cy;
00553 
00554     if (d->appearance.rowHighlightingEnabled && r == m_curRow && !d->appearance.fullRowSelection) {
00555         pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.rowHighlightingColor);
00556     }
00557     else if (d->appearance.rowMouseOverHighlightingEnabled && r == d->highlightedRow) {
00558         if(d->appearance.backgroundAltering && (r%2 != 0))
00559             pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.rowMouseOverAlternateHighlightingColor);
00560         else
00561             pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.rowMouseOverHighlightingColor);
00562     }
00563     else {
00564         if(d->appearance.backgroundAltering && (r%2 != 0))
00565             pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.alternateBackgroundColor);
00566         else
00567             pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.baseColor);
00568     }
00569 
00570     for(int c = colfirst; c <= collast; c++)
00571     {
00572         // get position and width of column c
00573         int colp = columnPos(c);
00574         if (colp==-1)
00575             continue; //invisible column?
00576         int colw = columnWidth(c);
00577         int translx = colp-cx;
00578 
00579         // Translate painter and draw the cell
00580         pb->saveWorldMatrix();
00581         pb->translate(translx, transly);
00582             paintCell( pb, item, c, r, QRect(colp, rowp, colw, d->rowHeight));
00583         pb->restoreWorldMatrix();
00584     }
00585 
00586     if (m_dragIndicatorLine>=0) {
00587         int y_line = -1;
00588         if (r==(rows()-1) && m_dragIndicatorLine==rows()) {
00589             y_line = transly+d->rowHeight-3; //draw at last line
00590         }
00591         if (m_dragIndicatorLine==r) {
00592             y_line = transly+1;
00593         }
00594         if (y_line>=0) {
00595             RasterOp op = pb->rasterOp();
00596             pb->setRasterOp(XorROP);
00597             pb->setPen( QPen(Qt::white, 3) );
00598             pb->drawLine(0, y_line, maxwc, y_line);
00599             pb->setRasterOp(op);
00600         }
00601     }
00602 }
00603 
00604 void KexiTableView::drawContents( QPainter *p, int cx, int cy, int cw, int ch)
00605 {
00606     if (d->disableDrawContents)
00607         return;
00608     int colfirst = columnAt(cx);
00609     int rowfirst = rowAt(cy);
00610     int collast = columnAt(cx + cw-1);
00611     int rowlast = rowAt(cy + ch-1);
00612     bool inserting = isInsertingEnabled();
00613     bool plus1row = false; //true if we should show 'inserting' row at the end
00614     bool paintOnlyInsertRow = false;
00615 
00616 /*  kdDebug(44021) << QString(" KexiTableView::drawContents(cx:%1 cy:%2 cw:%3 ch:%4)")
00617             .arg(cx).arg(cy).arg(cw).arg(ch) << endl;*/
00618 
00619     if (rowlast == -1) {
00620         rowlast = rows() - 1;
00621         plus1row = inserting;
00622         if (rowfirst == -1) {
00623             if (rowAt(cy - d->rowHeight) != -1) {
00624                 paintOnlyInsertRow = true;
00625 //              kdDebug(44021) << "-- paintOnlyInsertRow --" << endl;
00626             }
00627         }
00628     }
00629 //  kdDebug(44021) << "rowfirst="<<rowfirst<<" rowlast="<<rowlast<<" rows()="<<rows()<<endl;
00630 //  kdDebug(44021)<<" plus1row=" << plus1row<<endl;
00631     
00632     if ( collast == -1 )
00633         collast = columns() - 1;
00634 
00635     if (colfirst>collast) {
00636         int tmp = colfirst;
00637         colfirst = collast;
00638         collast = tmp;
00639     }
00640     if (rowfirst>rowlast) {
00641         int tmp = rowfirst;
00642         rowfirst = rowlast;
00643         rowlast = tmp;
00644     }
00645 
00646 //  qDebug("cx:%3d cy:%3d w:%3d h:%3d col:%2d..%2d row:%2d..%2d tsize:%4d,%4d", 
00647 //  cx, cy, cw, ch, colfirst, collast, rowfirst, rowlast, tableSize().width(), tableSize().height());
00648 //  triggerUpdate();
00649 
00650     if (rowfirst == -1 || colfirst == -1) {
00651         if (!paintOnlyInsertRow && !plus1row) {
00652             paintEmptyArea(p, cx, cy, cw, ch);
00653             return;
00654         }
00655     }
00656 
00657     createBuffer(cw, ch);
00658     if(d->pBufferPm->isNull())
00659         return;
00660     QPainter *pb = new QPainter(d->pBufferPm, this);
00661 //  pb->fillRect(0, 0, cw, ch, colorGroup().base());
00662 
00663 //  int maxwc = QMIN(cw, (columnPos(d->numCols - 1) + columnWidth(d->numCols - 1)));
00664     int maxwc = columnPos(columns() - 1) + columnWidth(columns() - 1);
00665 //  kdDebug(44021) << "KexiTableView::drawContents(): maxwc: " << maxwc << endl;
00666 
00667     pb->fillRect(cx, cy, cw, ch, d->appearance.baseColor);
00668 
00669     int rowp;
00670     int r;
00671     if (paintOnlyInsertRow) {
00672         r = rows();
00673         rowp = rowPos(r); // 'insert' row's position
00674     }
00675     else {
00676         QPtrListIterator<KexiTableItem> it = m_data->iterator();
00677         it += rowfirst;//move to 1st row
00678         rowp = rowPos(rowfirst); // row position 
00679         for (r = rowfirst;r <= rowlast; r++, ++it, rowp+=d->rowHeight) {
00680             paintRow(it.current(), pb, r, rowp, cx, cy, colfirst, collast, maxwc);
00681         }
00682     }
00683 
00684     if (plus1row) { //additional - 'insert' row
00685         paintRow(m_insertItem, pb, r, rowp, cx, cy, colfirst, collast, maxwc);
00686     }
00687 
00688     delete pb;
00689 
00690     p->drawPixmap(cx,cy,*d->pBufferPm, 0,0,cw,ch);
00691 
00692   //(js)
00693     paintEmptyArea(p, cx, cy, cw, ch);
00694 }
00695 
00696 bool KexiTableView::isDefaultValueDisplayed(KexiTableItem *item, int col, QVariant* value)
00697 {
00698     const bool cursorAtInsertRowOrEditingNewRow = (item == m_insertItem || (m_newRowEditing && m_currentItem == item));
00699     KexiTableViewColumn *tvcol;
00700     if (cursorAtInsertRowOrEditingNewRow 
00701         && (tvcol = m_data->column(col)) 
00702         && hasDefaultValueAt(*tvcol) 
00703         && !tvcol->field()->isAutoIncrement())
00704     {
00705         if (value)
00706             *value = tvcol->field()->defaultValue();
00707         return true;
00708     }
00709     return false;
00710 }
00711 
00712 void KexiTableView::paintCell(QPainter* p, KexiTableItem *item, int col, int row, const QRect &cr, bool print)
00713 {
00714     p->save();
00715     Q_UNUSED(print);
00716     int w = cr.width();
00717     int h = cr.height();
00718     int x2 = w - 1;
00719     int y2 = h - 1;
00720 
00721 /*  if (0==qstrcmp("KexiComboBoxPopup",parentWidget()->className())) {
00722       kexidbg  << parentWidget()->className() << " >>>>>> KexiTableView::paintCell(col=" << col <<"row="<<row<<") w="<<w<<endl;
00723     }*/
00724 
00725     //  Draw our lines
00726     QPen pen(p->pen());
00727 
00728     if (d->appearance.gridEnabled) {
00729         p->setPen(d->appearance.borderColor);
00730         p->drawLine( x2, 0, x2, y2 );   // right
00731         p->drawLine( 0, y2, x2, y2 );   // bottom
00732     }
00733     p->setPen(pen);
00734 
00735     if (m_editor && row == m_curRow && col == m_curCol //don't paint contents of edited cell
00736         && m_editor->hasFocusableWidget() //..if it's visible
00737        ) {
00738         p->restore();
00739         return;
00740     }
00741 
00742     KexiTableEdit *edit = tableEditorWidget( col, /*ignoreMissingEditor=*/true );
00743 //  if (!edit)
00744 //      return;
00745 
00746     int x = edit ? edit->leftMargin() : 0;
00747     int y_offset=0;
00748 
00749     int align = Qt::SingleLine | Qt::AlignVCenter;
00750     QString txt; //text to draw
00751 
00752     KexiTableViewColumn *tvcol = m_data->column(col);
00753 
00754     QVariant cellValue;
00755     if (col < (int)item->count()) {
00756         if (m_currentItem == item) {
00757             if (m_editor && row == m_curRow && col == m_curCol 
00758                 && !m_editor->hasFocusableWidget())
00759             {
00760                 //we're over editing cell and the editor has no widget
00761                 // - we're displaying internal values, not buffered
00762 //              bool ok;
00763                 cellValue = m_editor->value();
00764             }
00765             else {
00766                 //we're displaying values from edit buffer, if available
00767                 // this assignment will also get default value if there's no actual value set
00768                 cellValue = *bufferedValueAt(col);
00769             }
00770         }
00771         else {
00772             cellValue = item->at(col);
00773         }
00774     }
00775 
00776     bool defaultValueDisplayed = isDefaultValueDisplayed(item, col);
00777 
00778     if ((item == m_insertItem /*|| m_newRowEditing*/) && cellValue.isNull()) {
00779         if (!tvcol->field()->isAutoIncrement() && !tvcol->field()->defaultValue().isNull()) {
00780             //display default value in the "insert row", if available
00781             //(but not if there is autoincrement flag set)
00782             cellValue = tvcol->field()->defaultValue();
00783             defaultValueDisplayed = true;
00784         }
00785     }
00786 
00787     const bool columnReadOnly = tvcol->isReadOnly();
00788     const bool dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted 
00789         = d->appearance.rowHighlightingEnabled && !d->appearance.persistentSelections 
00790             && m_curRow /*d->highlightedRow*/ >= 0 && row != m_curRow; //d->highlightedRow;
00791 
00792     // setup default pen
00793     QPen defaultPen;
00794     const bool usesSelectedTextColor = edit && edit->usesSelectedTextColor();
00795     if (defaultValueDisplayed) {
00796         if (col == m_curCol && row == m_curRow && usesSelectedTextColor)
00797             defaultPen = d->defaultValueDisplayParameters.selectedTextColor;
00798         else
00799             defaultPen = d->defaultValueDisplayParameters.textColor;
00800     }
00801     else if (d->appearance.fullRowSelection 
00802         && (row == d->highlightedRow || (row == m_curRow && d->highlightedRow==-1)) 
00803         && usesSelectedTextColor )
00804     {
00805         defaultPen = d->appearance.rowHighlightingTextColor; //special case: highlighted row
00806     }
00807     else if (d->appearance.fullRowSelection && row == m_curRow && usesSelectedTextColor)
00808     {
00809         defaultPen = d->appearance.textColor; //special case for full row selection
00810     }
00811     else if (m_currentItem == item && col == m_curCol && !columnReadOnly 
00812         && !dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
00813         && usesSelectedTextColor)
00814     {
00815         defaultPen = colorGroup().highlightedText(); //selected text
00816     }
00817     else if (d->appearance.rowHighlightingEnabled && row == m_curRow 
00818         && !dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
00819         && usesSelectedTextColor)
00820     {
00821         defaultPen = d->appearance.rowHighlightingTextColor;
00822     }
00823     else if (d->appearance.rowMouseOverHighlightingEnabled && row == d->highlightedRow 
00824         && !dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
00825         && usesSelectedTextColor)
00826     {
00827         defaultPen = d->appearance.rowMouseOverHighlightingTextColor;
00828     }
00829     else
00830         defaultPen = d->appearance.textColor;
00831 
00832     if (edit) {
00833         if (defaultValueDisplayed)
00834             p->setFont( d->defaultValueDisplayParameters.font );
00835         p->setPen( defaultPen );
00836 
00837         //get visible lookup value if available
00838         getVisibleLookupValue(cellValue, edit, item, tvcol);
00839 
00840         edit->setupContents( p, m_currentItem == item && col == m_curCol, 
00841             cellValue, txt, align, x, y_offset, w, h );
00842     }
00843     if (!d->appearance.gridEnabled)
00844         y_offset++; //correction because we're not drawing cell borders
00845 
00846     if (d->appearance.fullRowSelection && d->appearance.fullRowSelection) {
00847 //      p->fillRect(x, y_offset, x+w-1, y_offset+h-1, red);
00848     }
00849     if (m_currentItem == item && (col == m_curCol || d->appearance.fullRowSelection)) {
00850         if (edit && ((d->appearance.rowHighlightingEnabled && !d->appearance.fullRowSelection) || (row == m_curRow && d->highlightedRow==-1 && d->appearance.fullRowSelection))) 
00851             edit->paintSelectionBackground( p, isEnabled(), txt, align, x, y_offset, w, h,
00852                 isEnabled() ? colorGroup().highlight() : QColor(200,200,200),//d->grayColor,
00853                 p->fontMetrics(), columnReadOnly, d->appearance.fullRowSelection );
00854     }
00855 
00856     if (!edit) {
00857         p->fillRect(0, 0, x2, y2, d->diagonalGrayPattern);
00858     }
00859 
00860 //  If we are in the focus cell, draw indication
00861     if(m_currentItem == item && col == m_curCol //js: && !d->recordIndicator)
00862         && !d->appearance.fullRowSelection) 
00863     {
00864 //      kexidbg << ">>> CURRENT CELL ("<<m_curCol<<"," << m_curRow<<") focus="<<has_focus<<endl;
00865 //      if (has_focus) {
00866         if (isEnabled()) {
00867             p->setPen(d->appearance.textColor);
00868         }
00869         else {
00870             QPen gray_pen(p->pen());
00871             gray_pen.setColor(d->appearance.borderColor);
00872             p->setPen(gray_pen);
00873         }
00874         if (edit)
00875             edit->paintFocusBorders( p, cellValue, 0, 0, x2, y2 );
00876         else
00877             p->drawRect(0, 0, x2, y2);
00878     }
00879 
00881     if ((!m_newRowEditing && item == m_insertItem) 
00882         || (m_newRowEditing && item == m_currentItem && cellValue.isNull())) {
00883         //we're in "insert row"
00884         if (tvcol->field()->isAutoIncrement()) {
00885             //"autonumber" column
00886 //          txt = i18n("(autonumber)");
00887 //          autonumber = true;
00888 //      if (autonumber) {
00889             KexiDisplayUtils::paintAutonumberSign(d->autonumberSignDisplayParameters, p, 
00890                 x, y_offset, w - x - x - ((align & Qt::AlignLeft)?2:0), h, align);
00891 //      }
00892         }
00893     }
00894     
00895     // draw text
00896     if (!txt.isEmpty()) {
00897         if (defaultValueDisplayed)
00898             p->setFont( d->defaultValueDisplayParameters.font );
00899         p->setPen( defaultPen );
00900         p->drawText(x, y_offset, w - (x + x)- ((align & Qt::AlignLeft)?2:0)/*right space*/, h,
00901             align, txt);
00902     }
00903     p->restore();
00904 }
00905 
00906 QPoint KexiTableView::contentsToViewport2( const QPoint &p )
00907 {
00908     return QPoint( p.x() - contentsX(), p.y() - contentsY() );
00909 }
00910 
00911 void KexiTableView::contentsToViewport2( int x, int y, int& vx, int& vy )
00912 {
00913     const QPoint v = contentsToViewport2( QPoint( x, y ) );
00914     vx = v.x();
00915     vy = v.y();
00916 }
00917 
00918 QPoint KexiTableView::viewportToContents2( const QPoint& vp )
00919 {
00920     return QPoint( vp.x() + contentsX(),
00921            vp.y() + contentsY() );
00922 }
00923 
00924 void KexiTableView::paintEmptyArea( QPainter *p, int cx, int cy, int cw, int ch )
00925 {
00926 //  qDebug("%s: paintEmptyArea(x:%d y:%d w:%d h:%d)", (const char*)parentWidget()->caption(),cx,cy,cw,ch);
00927 
00928     // Regions work with shorts, so avoid an overflow and adjust the
00929     // table size to the visible size
00930     QSize ts( tableSize() );
00931 //  ts.setWidth( QMIN( ts.width(), visibleWidth() ) );
00932 //  ts.setHeight( QMIN( ts.height() - (m_navPanel ? m_navPanel->height() : 0), visibleHeight()) );
00933 /*  kdDebug(44021) << QString(" (cx:%1 cy:%2 cw:%3 ch:%4)")
00934             .arg(cx).arg(cy).arg(cw).arg(ch) << endl;
00935     kdDebug(44021) << QString(" (w:%3 h:%4)")
00936             .arg(ts.width()).arg(ts.height()) << endl;*/
00937     
00938     // Region of the rect we should draw, calculated in viewport
00939     // coordinates, as a region can't handle bigger coordinates
00940     contentsToViewport2( cx, cy, cx, cy );
00941     QRegion reg( QRect( cx, cy, cw, ch ) );
00942 
00943 //kexidbg << "---cy-- " << contentsY() << endl;
00944 
00945     // Subtract the table from it
00946 //  reg = reg.subtract( QRect( QPoint( 0, 0 ), ts-QSize(0,m_navPanel->isVisible() ? m_navPanel->height() : 0) ) );
00947     reg = reg.subtract( QRect( QPoint( 0, 0 ), ts
00948         -QSize(0,QMAX((m_navPanel ? m_navPanel->height() : 0), horizontalScrollBar()->sizeHint().height())
00949             - (horizontalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height()/2 : 0)
00950             + (horizontalScrollBar()->isVisible() ? 0 : 
00951                 d->internal_bottomMargin
00952 //  horizontalScrollBar()->sizeHint().height()/2
00953         )
00954 //- /*d->bottomMargin */ horizontalScrollBar()->sizeHint().height()*3/2
00955             + contentsY()
00956 //          - (verticalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height()/2 : 0)
00957             )
00958         ) );
00959 //  reg = reg.subtract( QRect( QPoint( 0, 0 ), ts ) );
00960 
00961     // And draw the rectangles (transformed inc contents coordinates as needed)
00962     QMemArray<QRect> r = reg.rects();
00963     for ( int i = 0; i < (int)r.count(); i++ ) {
00964         QRect rect( viewportToContents2(r[i].topLeft()), r[i].size() );
00965 /*      kdDebug(44021) << QString("- pEA: p->fillRect(x:%1 y:%2 w:%3 h:%4)")
00966             .arg(rect.x()).arg(rect.y())
00967             .arg(rect.width()).arg(rect.height()) << endl;*/
00968 //      p->fillRect( QRect(viewportToContents2(r[i].topLeft()),r[i].size()), d->emptyAreaColor );
00969         p->fillRect( rect, d->appearance.emptyAreaColor );
00970 //      p->fillRect( QRect(viewportToContents2(r[i].topLeft()),r[i].size()), viewport()->backgroundBrush() );
00971     }
00972 }
00973 
00974 void KexiTableView::contentsMouseDoubleClickEvent(QMouseEvent *e)
00975 {
00976 //  kdDebug(44021) << "KexiTableView::contentsMouseDoubleClickEvent()" << endl;
00977     m_contentsMousePressEvent_dblClick = true;
00978     contentsMousePressEvent(e);
00979     m_contentsMousePressEvent_dblClick = false;
00980 
00981     if(m_currentItem)
00982     {
00983         if(d->editOnDoubleClick && columnEditable(m_curCol) && columnType(m_curCol) != KexiDB::Field::Boolean) {
00984             KexiTableEdit *edit = tableEditorWidget( m_curCol, /*ignoreMissingEditor=*/true );
00985             if (edit && edit->handleDoubleClick()) {
00986                 //nothing to do: editors like BLOB editor has custom handling of double clicking
00987             }
00988             else {
00989                 startEditCurrentCell();
00990     //          createEditor(m_curRow, m_curCol, QString::null);
00991             }
00992         }
00993 
00994         emit itemDblClicked(m_currentItem, m_curRow, m_curCol);
00995     }
00996 }
00997 
00998 void KexiTableView::contentsMousePressEvent( QMouseEvent* e )
00999 {
01000 //  kdDebug(44021) << "KexiTableView::contentsMousePressEvent() ??" << endl;
01001     setFocus();
01002     if(m_data->count()==0 && !isInsertingEnabled()) {
01003         QScrollView::contentsMousePressEvent( e );
01004         return;
01005     }
01006 
01007     if (columnAt(e->pos().x())==-1) { //outside a colums
01008         QScrollView::contentsMousePressEvent( e );
01009         return;
01010     }
01011 //  d->contentsMousePressEvent_ev = *e;
01012 //  d->contentsMousePressEvent_enabled = true;
01013 //  QTimer::singleShot(2000, this, SLOT( contentsMousePressEvent_Internal() ));
01014 //  d->contentsMousePressEvent_timer.start(100,true);
01015     
01016 //  if (!d->contentsMousePressEvent_enabled)
01017 //      return;
01018 //  d->contentsMousePressEvent_enabled=false;
01019 
01020     if (!d->moveCursorOnMouseRelease) {
01021         if (!handleContentsMousePressOrRelease(e, false))
01022             return;
01023     }
01024 
01025 //  kdDebug(44021)<<"void KexiTableView::contentsMousePressEvent( QMouseEvent* e ) by now the current items should be set, if not -> error + crash"<<endl;
01026     if(e->button() == Qt::RightButton)
01027     {
01028         showContextMenu(e->globalPos());
01029     }
01030     else if(e->button() == Qt::LeftButton)
01031     {
01032         if(columnType(m_curCol) == KexiDB::Field::Boolean && columnEditable(m_curCol))
01033         {
01034             //only accept clicking on the [x] rect (copied from KexiBoolTableEdit::setupContents())
01035             int s = QMAX(d->rowHeight - 5, 12);
01036             s = QMIN( d->rowHeight-3, s );
01037             s = QMIN( columnWidth(m_curCol)-3, s ); //avoid too large box
01038             const QRect r( columnPos(m_curCol) + QMAX( columnWidth(m_curCol)/2 - s/2, 0 ), rowPos(m_curRow) +d->rowHeight/2 - s/2 /*- 1*/, s, s);
01039             //kexidbg << r << endl;
01040             if (r.contains(e->pos())) {
01041 //              kexidbg << "e->x:" << e->x() << " e->y:" << e->y() << " " << rowPos(m_curRow) << 
01042 //                  " " << columnPos(m_curCol) << endl;
01043                 boolToggled();
01044             }
01045         }
01046 #if 0 //js: TODO
01047         else if(columnType(m_curCol) == QVariant::StringList && columnEditable(m_curCol))
01048         {
01049             createEditor(m_curRow, m_curCol);
01050         }
01051 #endif
01052     }
01053 //ScrollView::contentsMousePressEvent( e );
01054 }
01055 
01056 void KexiTableView::contentsMouseReleaseEvent( QMouseEvent* e )
01057 {
01058 //  kdDebug(44021) << "KexiTableView::contentsMousePressEvent() ??" << endl;
01059     if(m_data->count()==0 && !isInsertingEnabled())
01060         return;
01061 
01062     if (d->moveCursorOnMouseRelease)
01063         handleContentsMousePressOrRelease(e, true);
01064 
01065     int col = columnAt(e->pos().x());
01066     int row = rowAt(e->pos().y());
01067 
01068     if (!m_currentItem || col==-1 || row==-1 || col!=m_curCol || row!=m_curRow)//outside a current cell
01069         return;
01070 
01071     QScrollView::contentsMouseReleaseEvent( e );
01072 
01073     emit itemMouseReleased(m_currentItem, m_curRow, m_curCol);
01074 }
01075 
01076 bool KexiTableView::handleContentsMousePressOrRelease(QMouseEvent* e, bool release)
01077 {
01078     // remember old focus cell
01079     int oldRow = m_curRow;
01080     int oldCol = m_curCol;
01081     kdDebug(44021) << "oldRow=" << oldRow <<" oldCol=" << oldCol <<endl;
01082     bool onInsertItem = false;
01083 
01084     int newrow, newcol;
01085     //compute clicked row nr
01086     if (isInsertingEnabled()) {
01087         if (rowAt(e->pos().y())==-1) {
01088             newrow = rowAt(e->pos().y() - d->rowHeight);
01089             if (newrow==-1 && m_data->count()>0) {
01090                 if (release)
01091                     QScrollView::contentsMouseReleaseEvent( e );
01092                 else
01093                     QScrollView::contentsMousePressEvent( e );
01094                 return false;
01095             }
01096             newrow++;
01097             kdDebug(44021) << "Clicked just on 'insert' row." << endl;
01098             onInsertItem=true;
01099         }
01100         else {
01101             // get new focus cell
01102             newrow = rowAt(e->pos().y());
01103         }
01104     }
01105     else {
01106         if (rowAt(e->pos().y())==-1 || columnAt(e->pos().x())==-1) {
01107             if (release)
01108                 QScrollView::contentsMouseReleaseEvent( e );
01109             else
01110                 QScrollView::contentsMousePressEvent( e );
01111             return false; //clicked outside a grid
01112         }
01113         // get new focus cell
01114         newrow = rowAt(e->pos().y());
01115     }
01116     newcol = columnAt(e->pos().x());
01117 
01118     if(e->button() != Qt::NoButton) {
01119         setCursorPosition(newrow,newcol);
01120     }
01121     return true;
01122 }
01123 
01124 void KexiTableView::showContextMenu(const QPoint& _pos)
01125 {
01126     if (!d->contextMenuEnabled || m_popupMenu->count()<1)
01127         return;
01128     QPoint pos(_pos);
01129     if (pos==QPoint(-1,-1)) {
01130         pos = viewport()->mapToGlobal( QPoint( columnPos(m_curCol), rowPos(m_curRow) + d->rowHeight ) );
01131     }
01132     //show own context menu if configured
01133 //  if (updateContextMenu()) {
01134         selectRow(m_curRow);
01135         m_popupMenu->exec(pos);
01136 /*  }
01137     else {
01138         //request other context menu
01139         emit contextMenuRequested(m_currentItem, m_curCol, pos);
01140     }*/
01141 }
01142 
01143 void KexiTableView::contentsMouseMoveEvent( QMouseEvent *e )
01144 {
01145     int row;
01146     const int col = columnAt(e->x());
01147     if (col < 0) {
01148         row = -1;
01149     } else {
01150         row = rowAt( e->y(), true /*ignoreEnd*/ );
01151         if (row > (rows() - 1 + (isInsertingEnabled()?1:0)))
01152             row = -1; //no row to paint
01153     }
01154 //  kexidbg << " row="<<row<< " col="<<col<<endl;
01155     //update row highlight if needed
01156     if (d->appearance.rowMouseOverHighlightingEnabled) {
01157         if (row != d->highlightedRow) {
01158             const int oldRow = d->highlightedRow;
01159             d->highlightedRow = row;
01160             updateRow(oldRow);
01161             updateRow(d->highlightedRow);
01162             //currently selected (not necessary highlighted) row needs to be repainted
01163             updateRow(m_curRow);
01164             m_verticalHeader->setHighlightedRow(d->highlightedRow);
01165         }
01166     }
01167 
01168 #if 0//(js) doesn't work!
01169 
01170     // do the same as in mouse press
01171     int x,y;
01172     contentsToViewport(e->x(), e->y(), x, y);
01173 
01174     if(y > visibleHeight())
01175     {
01176         d->needAutoScroll = true;
01177         d->scrollTimer->start(70, false);
01178         d->scrollDirection = ScrollDown;
01179     }
01180     else if(y < 0)
01181     {
01182         d->needAutoScroll = true;
01183         d->scrollTimer->start(70, false);
01184         d->scrollDirection = ScrollUp;
01185     }
01186     else if(x > visibleWidth())
01187     {
01188         d->needAutoScroll = true;
01189         d->scrollTimer->start(70, false);
01190         d->scrollDirection = ScrollRight;
01191     }
01192     else if(x < 0)
01193     {
01194         d->needAutoScroll = true;
01195         d->scrollTimer->start(70, false);
01196         d->scrollDirection = ScrollLeft;
01197     }
01198     else
01199     {
01200         d->needAutoScroll = false;
01201         d->scrollTimer->stop();
01202         contentsMousePressEvent(e);
01203     }
01204 #endif
01205     QScrollView::contentsMouseMoveEvent(e);
01206 }
01207 
01208 #if 0//(js) doesn't work!
01209 void KexiTableView::contentsMouseReleaseEvent(QMouseEvent *)
01210 {
01211     if(d->needAutoScroll)
01212     {
01213         d->scrollTimer->stop();
01214     }
01215 }
01216 #endif
01217 
01218 static bool overrideEditorShortcutNeeded(QKeyEvent *e)
01219 {
01220     //perhaps more to come...
01221     return e->key() == Qt::Key_Delete && e->state()==Qt::ControlButton;
01222 }
01223 
01224 bool KexiTableView::shortCutPressed( QKeyEvent *e, const QCString &action_name )
01225 {
01226     const int k = e->key();
01227     KAction *action = m_sharedActions[action_name];
01228     if (action) {
01229         if (!action->isEnabled())//this action is disabled - don't process it!
01230             return false; 
01231         if (action->shortcut() == KShortcut( KKey(e) )) {
01232             //special cases when we need to override editor's shortcut
01233             if (overrideEditorShortcutNeeded(e)) {
01234                 return true;
01235             }
01236             return false;//this shortcut is owned by shared action - don't process it!
01237         }
01238     }
01239 
01240     //check default shortcut (when user app has no action shortcuts defined
01241     // but we want these shortcuts to still work)
01242     if (action_name=="data_save_row")
01243         return (k == Qt::Key_Return || k == Qt::Key_Enter) && e->state()==Qt::ShiftButton;
01244     if (action_name=="edit_delete_row")
01245         return k == Qt::Key_Delete && e->state()==Qt::ControlButton;
01246     if (action_name=="edit_delete")
01247         return k == Qt::Key_Delete && e->state()==Qt::NoButton;
01248     if (action_name=="edit_edititem")
01249         return k == Qt::Key_F2 && e->state()==Qt::NoButton;
01250     if (action_name=="edit_insert_empty_row")
01251         return k == Qt::Key_Insert && e->state()==(Qt::ShiftButton | Qt::ControlButton);
01252 
01253     return false;
01254 }
01255 
01256 void KexiTableView::keyPressEvent(QKeyEvent* e)
01257 {
01258     if (!hasData())
01259         return;
01260 //  kexidbg << "KexiTableView::keyPressEvent: key=" <<e->key() << " txt=" <<e->text()<<endl;
01261 
01262     const int k = e->key();
01263     const bool ro = isReadOnly();
01264     QWidget *w = focusWidget();
01265 //  if (!w || w!=viewport() && w!=this && (!m_editor || w!=m_editor->view() && w!=m_editor)) {
01266 //  if (!w || w!=viewport() && w!=this && (!m_editor || w!=m_editor->view())) {
01267     if (!w || w!=viewport() && w!=this && (!m_editor || !KexiUtils::hasParent(dynamic_cast<QObject*>(m_editor), w))) {
01268         //don't process stranger's events
01269         e->ignore();
01270         return;
01271     }
01272     if (d->skipKeyPress) {
01273         d->skipKeyPress=false;
01274         e->ignore();
01275         return;
01276     }
01277     
01278     if(m_currentItem == 0 && (m_data->count() > 0 || isInsertingEnabled()))
01279     {
01280         setCursorPosition(0,0);
01281     }
01282     else if(m_data->count() == 0 && !isInsertingEnabled())
01283     {
01284         e->accept();
01285         return;
01286     }
01287 
01288     if(m_editor) {// if a cell is edited, do some special stuff
01289         if (k == Qt::Key_Escape) {
01290             cancelEditor();
01291             e->accept();
01292             return;
01293         } else if (k == Qt::Key_Return || k == Qt::Key_Enter) {
01294             if (columnType(m_curCol) == KexiDB::Field::Boolean) {
01295                 boolToggled();
01296             }
01297             else {
01298                 acceptEditor();
01299             }
01300             e->accept();
01301             return;
01302         }
01303     }
01304     else if (m_rowEditing) {// if a row is in edit mode, do some special stuff
01305         if (shortCutPressed( e, "data_save_row")) {
01306             kexidbg << "shortCutPressed!!!" <<endl;
01307             acceptRowEdit();
01308             return;
01309         }
01310     }
01311 
01312     if(k == Qt::Key_Return || k == Qt::Key_Enter)
01313     {
01314         emit itemReturnPressed(m_currentItem, m_curRow, m_curCol);
01315     }
01316 
01317     int curRow = m_curRow;
01318     int curCol = m_curCol;
01319 
01320     const bool nobtn = e->state()==NoButton;
01321     bool printable = false;
01322 
01323     //check shared shortcuts
01324     if (!ro) {
01325         if (shortCutPressed(e, "edit_delete_row")) {
01326             deleteCurrentRow();
01327             e->accept();
01328             return;
01329         } else if (shortCutPressed(e, "edit_delete")) {
01330             deleteAndStartEditCurrentCell();
01331             e->accept();
01332             return;
01333         }
01334         else if (shortCutPressed(e, "edit_insert_empty_row")) {
01335             insertEmptyRow();
01336             e->accept();
01337             return;
01338         }
01339     }
01340 
01341 /*  case Qt::Key_Delete:
01342         if (e->state()==Qt::ControlButton) {//remove current row
01343             deleteCurrentRow();
01344         }
01345         else if (nobtn) {//remove contents of the current cell
01346             deleteAndStartEditCurrentCell();
01347         }
01348         break;*/
01349 
01350 //  bool _return;
01351     if (k == Qt::Key_Shift || k == Qt::Key_Alt || k == Qt::Key_Control || k == Qt::Key_Meta) {
01352         e->ignore();
01353     }
01354     else if (KexiDataAwareObjectInterface::handleKeyPress(e, curRow, curCol, d->appearance.fullRowSelection)) {
01355         if (e->isAccepted())
01356             return;
01357     }
01358     else if (k == Qt::Key_Backspace && nobtn) {
01359         if (!ro && columnType(curCol) != KexiDB::Field::Boolean && columnEditable(curCol))
01360             createEditor(curRow, curCol, QString::null, true);
01361     }
01362     else if (k == Qt::Key_Space) {
01363         if (nobtn && !ro && columnEditable(curCol)) {
01364             if (columnType(curCol) == KexiDB::Field::Boolean) {
01365                 boolToggled();
01366             }
01367             else
01368                 printable = true; //just space key
01369         }
01370     }
01371     else if (k == Qt::Key_Escape) {
01372         if (nobtn && m_rowEditing) {
01373             cancelRowEdit();
01374             return;
01375         }
01376     }
01377     else {
01378         //others:
01379         if (nobtn && (k==Qt::Key_Tab || k==Qt::Key_Right)) {
01381             //tab
01382             if (acceptEditor()) {
01383                 if (curCol == (columns() - 1)) {
01384                     if (curRow < (rows()-1+(isInsertingEnabled()?1:0))) {//skip to next row
01385                         curRow++;
01386                         curCol = 0;
01387                     }
01388                 }
01389                 else
01390                     curCol++;
01391             }
01392         }
01393         else if ((e->state()==Qt::ShiftButton && k==Qt::Key_Tab)
01394          || (nobtn && k==Qt::Key_Backtab)
01395          || (e->state()==Qt::ShiftButton && k==Qt::Key_Backtab)
01396          || (nobtn && k==Qt::Key_Left)
01397             ) {
01399             //backward tab
01400             if (acceptEditor()) {
01401                 if (curCol == 0) {
01402                     if (curRow>0) {//skip to previous row
01403                         curRow--;
01404                         curCol = columns() - 1;
01405                     }
01406                 }
01407                 else
01408                     curCol--;
01409             }
01410         }
01411         else if (nobtn && k==d->contextMenuKey) { //Qt::Key_Menu:
01412             showContextMenu();
01413         }
01414         else {
01415             KexiTableEdit *edit = tableEditorWidget( m_curCol );
01416             if (edit && edit->handleKeyPress(e, m_editor==edit)) {
01417                 //try to handle the event @ editor's level
01418                 e->accept();
01419                 return;
01420             }
01421             else if ( nobtn && (k==Qt::Key_Enter || k==Qt::Key_Return || shortCutPressed(e, "edit_edititem")) ) {
01422                 //this condition is moved after handleKeyPress() to allow to everride enter key as well
01423                 startEditOrToggleValue();
01424             }
01425             else {
01426                 kexidbg << "KexiTableView::KeyPressEvent(): default" << endl;
01427                 if (e->text().isEmpty() || !e->text().isEmpty() && !e->text()[0].isPrint() ) {
01428                     kdDebug(44021) << "NOT PRINTABLE: 0x0" << QString("%1").arg(k,0,16) <<endl;
01429     //              e->ignore();
01430                     QScrollView::keyPressEvent(e);
01431                     return;
01432                 }
01433                 printable = true;
01434             }
01435         }
01436     }
01437     //finally: we've printable char:
01438     if (printable && !ro) {
01439         KexiTableViewColumn *tvcol = m_data->column(curCol);
01440         if (tvcol->acceptsFirstChar(e->text()[0])) {
01441             kdDebug(44021) << "KexiTableView::KeyPressEvent(): ev pressed: acceptsFirstChar()==true" << endl;
01442     //          if (e->text()[0].isPrint())
01443             createEditor(curRow, curCol, e->text(), true);
01444         }
01445         else {
01446 //TODO show message "key not allowed eg. on a statusbar"
01447             kdDebug(44021) << "KexiTableView::KeyPressEvent(): ev pressed: acceptsFirstChar()==false" << endl;
01448         }
01449     }
01450 
01451     m_vScrollBarValueChanged_enabled=false;
01452 
01453     // if focus cell changes, repaint
01454     setCursorPosition(curRow, curCol);
01455 
01456     m_vScrollBarValueChanged_enabled=true;
01457 
01458     e->accept();
01459 }
01460 
01461 void KexiTableView::emitSelected()
01462 {
01463     if(m_currentItem)
01464         emit itemSelected(m_currentItem);
01465 }
01466 
01467 int KexiTableView::rowsPerPage() const
01468 {
01469     return visibleHeight() / d->rowHeight;
01470 }
01471 
01472 KexiDataItemInterface *KexiTableView::editor( int col, bool ignoreMissingEditor )
01473 {
01474     if (!m_data || col<0 || col>=columns())
01475         return 0;
01476     KexiTableViewColumn *tvcol = m_data->column(col);
01477 //  int t = tvcol->field->type();
01478 
01479     //find the editor for this column
01480     KexiTableEdit *editor = d->editors[ tvcol ];
01481     if (editor)
01482         return editor;
01483 
01484     //not found: create
01485 //  editor = KexiCellEditorFactory::createEditor(*m_data->column(col)->field, this);
01486     editor = KexiCellEditorFactory::createEditor(*tvcol, this);
01487     if (!editor) {//create error!
01488         if (!ignoreMissingEditor) {
01489             //js TODO: show error???
01490             cancelRowEdit();
01491         }
01492         return 0;
01493     }
01494     editor->hide();
01495     if (m_data->cursor() && m_data->cursor()->query())
01496         editor->createInternalEditor(*m_data->cursor()->query());
01497 
01498     connect(editor,SIGNAL(editRequested()),this,SLOT(slotEditRequested()));
01499     connect(editor,SIGNAL(cancelRequested()),this,SLOT(cancelEditor()));
01500     connect(editor,SIGNAL(acceptRequested()),this,SLOT(acceptEditor()));
01501 
01502     editor->resize(columnWidth(col)-1, rowHeight()-1);
01503     editor->installEventFilter(this);
01504     if (editor->widget())
01505         editor->widget()->installEventFilter(this);
01506     //store
01507     d->editors.insert( tvcol, editor );
01508     return editor;
01509 }
01510 
01511 void KexiTableView::editorShowFocus( int /*row*/, int col )
01512 {
01513     KexiDataItemInterface *edit = editor( col );
01514     /*nt p = rowPos(row);
01515      (!edit || (p < contentsY()) || (p > (contentsY()+clipper()->height()))) {
01516         kexidbg<< "KexiTableView::editorShowFocus() : OUT" << endl;
01517         return;
01518     }*/
01519     if (edit) {
01520         kexidbg<< "KexiTableView::editorShowFocus() : IN" << endl;
01521         QRect rect = cellGeometry( m_curRow, m_curCol );
01522 //      rect.moveBy( -contentsX(), -contentsY() );
01523         edit->showFocus( rect, isReadOnly() || m_data->column(col)->isReadOnly() );
01524     }
01525 }
01526 
01527 void KexiTableView::slotEditRequested()
01528 {
01529     createEditor(m_curRow, m_curCol);
01530 }
01531 
01532 void KexiTableView::reloadData() {
01533     KexiDataAwareObjectInterface::reloadData();
01534     updateContents();
01535 }
01536 
01537 void KexiTableView::createEditor(int row, int col, const QString& addText, bool removeOld)
01538 {
01539     kdDebug(44021) << "KexiTableView::createEditor('"<<addText<<"',"<<removeOld<<")"<<endl;
01540     if (isReadOnly()) {
01541         kdDebug(44021) << "KexiTableView::createEditor(): DATA IS READ ONLY!"<<endl;
01542         return;
01543     }
01544 
01545     if (m_data->column(col)->isReadOnly()) {//d->pColumnModes.at(d->numCols-1) & ColumnReadOnly)
01546         kdDebug(44021) << "KexiTableView::createEditor(): COL IS READ ONLY!"<<endl;
01547         return;
01548     }
01549 
01550     const bool startRowEdit = !m_rowEditing; //remember if we're starting row edit
01551 
01552     if (!m_rowEditing) {
01553         //we're starting row editing session
01554         m_data->clearRowEditBuffer();
01555         
01556         m_rowEditing = true;
01557         //indicate on the vheader that we are editing:
01558         m_verticalHeader->setEditRow(m_curRow);
01559         if (isInsertingEnabled() && m_currentItem==m_insertItem) {
01560             //we should know that we are in state "new row editing"
01561             m_newRowEditing = true;
01562             //'insert' row editing: show another row after that:
01563             m_data->append( m_insertItem );
01564             //new empty 'inserting' item
01565             m_insertItem = m_data->createItem();
01566             m_verticalHeader->addLabel();
01567             m_verticalHeaderAlreadyAdded = true;
01568             updateWidgetContentsSize();
01569             //refr. current and next row
01570             updateContents(columnPos(0), rowPos(row), viewport()->width(), d->rowHeight*2);
01571 //          updateContents(columnPos(0), rowPos(row+1), viewport()->width(), d->rowHeight);
01572 //js: warning this breaks behaviour (cursor is skipping, etc.): qApp->processEvents(500);
01573             ensureVisible(columnPos(m_curCol), rowPos(row+1)+d->rowHeight-1, columnWidth(m_curCol), d->rowHeight);
01574 
01575             m_verticalHeader->setOffset(contentsY());
01576         }
01577     }   
01578 
01579     KexiTableEdit *editorWidget = tableEditorWidget( col );
01580     m_editor = editorWidget;
01581     if (!editorWidget)
01582         return;
01583 
01584     m_editor->setValue(*bufferedValueAt(col, !removeOld/*useDefaultValueIfPossible*/), addText, removeOld);
01585     if (m_editor->hasFocusableWidget()) {
01586         moveChild(editorWidget, columnPos(m_curCol), rowPos(m_curRow));
01587 
01588         editorWidget->resize(columnWidth(m_curCol)-1, rowHeight()-1);
01589         editorWidget->show();
01590 
01591         m_editor->setFocus();
01592     }
01593 
01594     if (startRowEdit) {
01595         m_navPanel->showEditingIndicator(true); //this will allow to enable 'next' btn
01596 //      m_navPanel->updateButtons(rows()); //refresh 'next' btn
01597         emit rowEditStarted(m_curRow);
01598     }
01599 }
01600 
01601 void KexiTableView::focusInEvent(QFocusEvent* e)
01602 {
01603     Q_UNUSED(e);
01604     updateCell(m_curRow, m_curCol);
01605 }
01606 
01607 void KexiTableView::focusOutEvent(QFocusEvent* e)
01608 {
01609     KexiDataAwareObjectInterface::focusOutEvent(e);
01610 }
01611 
01612 bool KexiTableView::focusNextPrevChild(bool /*next*/)
01613 {
01614     return false; //special Tab/BackTab meaning
01615 /*  if (m_editor)
01616         return true;
01617     return QScrollView::focusNextPrevChild(next);*/
01618 }
01619 
01620 void KexiTableView::resizeEvent(QResizeEvent *e)
01621 {
01622     QScrollView::resizeEvent(e);
01623     //updateGeometries();
01624     
01625     if (m_navPanel)
01626         m_navPanel->updateGeometry(leftMargin());
01627 //  updateNavPanelGeometry();
01628 
01629     if ((contentsHeight() - e->size().height()) <= d->rowHeight) {
01630         slotUpdate();
01631         triggerUpdate();
01632     }
01633 //  d->pTopHeader->repaint();
01634 
01635 
01636 /*      m_navPanel->setGeometry(
01637             frameWidth(),
01638             viewport()->height() +d->pTopHeader->height() 
01639             -(horizontalScrollBar()->isVisible() ? 0 : horizontalScrollBar()->sizeHint().height())
01640             +frameWidth(),
01641             m_navPanel->sizeHint().width(), // - verticalScrollBar()->sizeHint().width() - horizontalScrollBar()->sizeHint().width(),
01642             horizontalScrollBar()->sizeHint().height()
01643         );*/
01644 //      updateContents();
01645 //      m_navPanel->setGeometry(1,horizontalScrollBar()->pos().y(),
01646     //      m_navPanel->width(), horizontalScrollBar()->height());
01647 //  updateContents(0,0,2000,2000);//js
01648 //  erase(); repaint();
01649 }
01650 
01651 void KexiTableView::viewportResizeEvent( QResizeEvent *e )
01652 {
01653     QScrollView::viewportResizeEvent( e );
01654     updateGeometries();
01655 //  erase(); repaint();
01656 }
01657 
01658 void KexiTableView::showEvent(QShowEvent *e)
01659 {
01660     QScrollView::showEvent(e);
01661     if (!d->maximizeColumnsWidthOnShow.isEmpty()) {
01662         maximizeColumnsWidth(d->maximizeColumnsWidthOnShow);
01663         d->maximizeColumnsWidthOnShow.clear();
01664     }
01665 
01666     if (m_initDataContentsOnShow) {
01667         //full init
01668         m_initDataContentsOnShow = false;
01669         initDataContents();
01670     }
01671     else {
01672         //just update size
01673         QSize s(tableSize());
01674 //  QRect r(cellGeometry(rows() - 1 + (isInsertingEnabled()?1:0), columns() - 1 ));
01675 //  resizeContents(r.right() + 1, r.bottom() + 1);
01676         resizeContents(s.width(),s.height());
01677     }
01678     updateGeometries();
01679 
01680     //now we can ensure cell's visibility ( if there was such a call before show() )
01681     if (d->ensureCellVisibleOnShow!=QPoint(-1,-1)) {
01682         ensureCellVisible( d->ensureCellVisibleOnShow.x(), d->ensureCellVisibleOnShow.y() );
01683         d->ensureCellVisibleOnShow = QPoint(-1,-1); //reset the flag
01684     }
01685     if (m_navPanel)
01686         m_navPanel->updateGeometry(leftMargin());
01687 //  updateNavPanelGeometry();
01688 }
01689 
01690 void KexiTableView::contentsDragMoveEvent(QDragMoveEvent *e)
01691 {
01692     if (!hasData())
01693         return;
01694     if (m_dropsAtRowEnabled) {
01695         QPoint p = e->pos();
01696         int row = rowAt(p.y());
01697         KexiTableItem *item = 0;
01698 //      if (row==(rows()-1) && (p.y() % d->rowHeight) > (d->rowHeight*2/3) ) {
01699         if ((p.y() % d->rowHeight) > (d->rowHeight*2/3) ) {
01700             row++;
01701         }
01702         item = m_data->at(row);
01703         emit dragOverRow(item, row, e);
01704         if (e->isAccepted()) {
01705             if (m_dragIndicatorLine>=0 && m_dragIndicatorLine != row) {
01706                 //erase old indicator
01707                 updateRow(m_dragIndicatorLine);
01708             }
01709             if (m_dragIndicatorLine != row) {
01710                 m_dragIndicatorLine = row;
01711                 updateRow(m_dragIndicatorLine);
01712             }
01713         }
01714         else {
01715             if (m_dragIndicatorLine>=0) {
01716                 //erase old indicator
01717                 updateRow(m_dragIndicatorLine);
01718             }
01719             m_dragIndicatorLine = -1;
01720         }
01721     }
01722     else
01723         e->acceptAction(false);
01724 /*  QStringList::ConstIterator it, end( d->dropFilters.constEnd() ); 
01725     for( it = d->dropFilters.constBegin(); it != end; it++)
01726     {
01727         if(e->provides((*it).latin1()))
01728         {
01729             e->acceptAction(true);
01730             return;
01731         }
01732     }*/
01733 //  e->acceptAction(false);
01734 }
01735 
01736 void KexiTableView::contentsDropEvent(QDropEvent *e)
01737 {
01738     if (!hasData())
01739         return;
01740     if (m_dropsAtRowEnabled) {
01741         //we're no longer dragging over the table
01742         if (m_dragIndicatorLine>=0) {
01743             int row2update = m_dragIndicatorLine;
01744             m_dragIndicatorLine = -1;
01745             updateRow(row2update);
01746         }
01747         QPoint p = e->pos();
01748         int row = rowAt(p.y());
01749         if ((p.y() % d->rowHeight) > (d->rowHeight*2/3) ) {
01750             row++;
01751         }
01752         KexiTableItem *item = m_data->at(row);
01753         KexiTableItem *newItem = 0;
01754         emit droppedAtRow(item, row, e, newItem);
01755         if (newItem) {
01756             const int realRow = (row==m_curRow ? -1 : row);
01757             insertItem(newItem, realRow);
01758             setCursorPosition(row, 0);
01759 //          m_currentItem = newItem;
01760         }
01761     }
01762 }
01763 
01764 void KexiTableView::viewportDragLeaveEvent( QDragLeaveEvent *e )
01765 {
01766     Q_UNUSED(e);
01767     if (!hasData())
01768         return;
01769     if (m_dropsAtRowEnabled) {
01770         //we're no longer dragging over the table
01771         if (m_dragIndicatorLine>=0) {
01772             int row2update = m_dragIndicatorLine;
01773             m_dragIndicatorLine = -1;
01774             updateRow(row2update);
01775         }
01776     }
01777 }
01778 
01779 void KexiTableView::updateCell(int row, int col)
01780 {
01781 //  kdDebug(44021) << "updateCell("<<row<<", "<<col<<")"<<endl;
01782     updateContents(cellGeometry(row, col));
01783 /*  QRect r = cellGeometry(row, col);
01784     r.setHeight(r.height()+6);
01785     r.setTop(r.top()-3);
01786     updateContents();*/
01787 }
01788 
01789 void KexiTableView::updateCurrentCell()
01790 {
01791     updateCell(m_curRow, m_curCol);
01792 }
01793 
01794 void KexiTableView::updateRow(int row)
01795 {
01796 //  kdDebug(44021) << "updateRow("<<row<<")"<<endl;
01797     if (row < 0 || row >= (rows() + 2/* sometimes we want to refresh the row after last*/ ))
01798         return;
01799     //int leftcol = d->pTopHeader->sectionAt( d->pTopHeader->offset() );
01800 
01801     //kexidbg << contentsX() << " " << contentsY() << endl;
01802     //kexidbg << QRect( columnPos( leftcol ), rowPos(row), clipper()->width(), rowHeight() ) << endl;
01803     //  updateContents( QRect( columnPos( leftcol ), rowPos(row), clipper()->width(), rowHeight() ) ); //columnPos(rightcol)+columnWidth(rightcol), rowHeight() ) );
01804     updateContents( QRect( contentsX(), rowPos(row), clipper()->width(), rowHeight() ) ); //columnPos(rightcol)+columnWidth(rightcol), rowHeight() ) );
01805 }
01806 
01807 void KexiTableView::slotColumnWidthChanged( int, int, int )
01808 {
01809     QSize s(tableSize());
01810     int w = contentsWidth();
01811     viewport()->setUpdatesEnabled(false);
01812     resizeContents( s.width(), s.height() );
01813     viewport()->setUpdatesEnabled(true);
01814     if (contentsWidth() < w) {
01815         updateContents(contentsX(), 0, viewport()->width(), contentsHeight());
01816 //      repaintContents( s.width(), 0, w - s.width() + 1, contentsHeight(), true );
01817     } 
01818     else {
01819     //  updateContents( columnPos(col), 0, contentsWidth(), contentsHeight() );
01820         updateContents(contentsX(), 0, viewport()->width(), contentsHeight());
01821     //  viewport()->repaint();
01822     }
01823 
01824 //  updateContents(0, 0, d->pBufferPm->width(), d->pBufferPm->height());
01825     QWidget *editorWidget = dynamic_cast<QWidget*>(m_editor);
01826     if (editorWidget)
01827     {
01828         editorWidget->resize(columnWidth(m_curCol)-1, rowHeight()-1);
01829         moveChild(editorWidget, columnPos(m_curCol), rowPos(m_curRow));
01830     }
01831     updateGeometries();
01832     updateScrollBars();
01833     if (m_navPanel)
01834         m_navPanel->updateGeometry(leftMargin());
01835 //  updateNavPanelGeometry();
01836 }
01837 
01838 void KexiTableView::slotSectionHandleDoubleClicked( int section )
01839 {
01840     adjustColumnWidthToContents(section);
01841     slotColumnWidthChanged(0,0,0); //to update contents and redraw
01842 }
01843 
01844 
01845 void KexiTableView::updateGeometries()
01846 {
01847     QSize ts = tableSize();
01848     if (m_horizontalHeader->offset() && ts.width() < (m_horizontalHeader->offset() + m_horizontalHeader->width()))
01849         horizontalScrollBar()->setValue(ts.width() - m_horizontalHeader->width());
01850 
01851 //  m_verticalHeader->setGeometry(1, topMargin() + 1, leftMargin(), visibleHeight());
01852     m_horizontalHeader->setGeometry(leftMargin() + 1, 1, visibleWidth(), topMargin());
01853     m_verticalHeader->setGeometry(1, topMargin() + 1, leftMargin(), visibleHeight());
01854 }
01855 
01856 int KexiTableView::columnWidth(int col) const
01857 {
01858     if (!hasData())
01859         return 0;
01860     int vcID = m_data->visibleColumnID( col );
01861     return (vcID==-1) ? 0 : m_horizontalHeader->sectionSize( vcID );
01862 }
01863 
01864 int KexiTableView::rowHeight() const
01865 {
01866     return d->rowHeight;
01867 }
01868 
01869 int KexiTableView::columnPos(int col) const
01870 {
01871     if (!hasData())
01872         return 0;
01873     //if this column is hidden, find first column before that is visible
01874     int c = QMIN(col, (int)m_data->columnsCount()-1), vcID = 0;
01875     while (c>=0 && (vcID=m_data->visibleColumnID( c ))==-1)
01876         c--;
01877     if (c<0)
01878         return 0;
01879     if (c==col)
01880         return m_horizontalHeader->sectionPos(vcID);
01881     return m_horizontalHeader->sectionPos(vcID)+m_horizontalHeader->sectionSize(vcID);
01882 }
01883 
01884 int KexiTableView::rowPos(int row) const
01885 {
01886     return d->rowHeight*row;
01887 }
01888 
01889 int KexiTableView::columnAt(int pos) const
01890 {
01891     if (!hasData())
01892         return -1;
01893     int r = m_horizontalHeader->sectionAt(pos);
01894     if (r<0)
01895         return r;
01896     return m_data->globalColumnID( r );
01897 
01898 //  if (r==-1)
01899 //      kexidbg << "columnAt("<<pos<<")==-1 !!!" << endl;
01900 //  return r;
01901 }
01902 
01903 int KexiTableView::rowAt(int pos, bool ignoreEnd) const
01904 {
01905     if (!hasData())
01906         return -1;
01907     pos /=d->rowHeight;
01908     if (pos < 0)
01909         return 0;
01910     if ((pos >= (int)m_data->count()) && !ignoreEnd)
01911         return -1;
01912     return pos;
01913 }
01914 
01915 QRect KexiTableView::cellGeometry(int row, int col) const
01916 {
01917     return QRect(columnPos(col), rowPos(row),
01918         columnWidth(col), rowHeight());
01919 }
01920 
01921 QSize KexiTableView::tableSize() const
01922 {
01923     if ((rows()+ (isInsertingEnabled()?1:0) ) > 0 && columns() > 0) {
01924 /*      kexidbg << "tableSize()= " << columnPos( columns() - 1 ) + columnWidth( columns() - 1 ) 
01925             << ", " << rowPos( rows()-1+(isInsertingEnabled()?1:0)) + d->rowHeight
01926 //          + QMAX(m_navPanel ? m_navPanel->height() : 0, horizontalScrollBar()->sizeHint().height())
01927             + (m_navPanel->isVisible() ? QMAX( m_navPanel->height(), horizontalScrollBar()->sizeHint().height() ) :0 )
01928             + margin() << endl;
01929 */
01930 //      kexidbg<< m_navPanel->isVisible() <<" "<<m_navPanel->height()<<" "
01931 //      <<horizontalScrollBar()->sizeHint().height()<<" "<<rowPos( rows()-1+(isInsertingEnabled()?1:0))<<endl;
01932 
01933         //int xx = horizontalScrollBar()->sizeHint().height()/2;
01934 
01935         QSize s( 
01936             columnPos( columns() - 1 ) + columnWidth( columns() - 1 ),
01937 //          + verticalScrollBar()->sizeHint().width(),
01938             rowPos( rows()-1+(isInsertingEnabled()?1:0) ) + d->rowHeight
01939             + (horizontalScrollBar()->isVisible() ? 0 : horizontalScrollBar()->sizeHint().height())
01940             + d->internal_bottomMargin
01941 //              horizontalScrollBar()->sizeHint().height()/2
01942 //          - /*d->bottomMargin */ horizontalScrollBar()->sizeHint().height()*3/2
01943 
01944 //          + ( (m_navPanel && m_navPanel->isVisible() && verticalScrollBar()->isVisible()
01945     //          && !horizontalScrollBar()->isVisible()) 
01946         //      ? horizontalScrollBar()->sizeHint().height() : 0)
01947 
01948 //          + QMAX( (m_navPanel && m_navPanel->isVisible()) ? m_navPanel->height() : 0, 
01949 //              horizontalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height() : 0)
01950 
01951 //          + (m_navPanel->isVisible() 
01952 //              ? QMAX( m_navPanel->height(), horizontalScrollBar()->sizeHint().height() ) :0 )
01953 
01954 //          - (horizontalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height() :0 )
01955             + margin() 
01956 //-2*d->rowHeight
01957         );
01958 
01959 //      kexidbg << rows()-1 <<" "<< (isInsertingEnabled()?1:0) <<" "<< (m_rowEditing?1:0) << " " <<  s << endl;
01960         return s;
01961 //          +horizontalScrollBar()->sizeHint().height() + margin() );
01962     }
01963     return QSize(0,0);
01964 }
01965 
01966 void KexiTableView::ensureCellVisible(int row, int col/*=-1*/)
01967 {
01968     if (!isVisible()) {
01969         //the table is invisible: we can't ensure visibility now
01970         d->ensureCellVisibleOnShow = QPoint(row,col);
01971         return;
01972     }
01973 
01974     //quite clever: ensure the cell is visible:
01975     QRect r( columnPos(col==-1 ? m_curCol : col), rowPos(row) +(d->appearance.fullRowSelection?1:0), 
01976         columnWidth(col==-1 ? m_curCol : col), rowHeight());
01977 
01978 /*  if (m_navPanel && horizontalScrollBar()->isHidden() && row == rows()-1) {
01979         //when cursor is moved down and navigator covers the cursor's area,
01980         //area is scrolled up
01981         if ((viewport()->height() - m_navPanel->height()) < r.bottom()) {
01982             scrollBy(0,r.bottom() - (viewport()->height() - m_navPanel->height()));
01983         }
01984     }*/
01985 
01986     if (m_navPanel && m_navPanel->isVisible() && horizontalScrollBar()->isHidden()) {
01987         //a hack: for visible navigator: increase height of the visible rect 'r'
01988         r.setBottom(r.bottom()+m_navPanel->height());
01989     }
01990 
01991     QPoint pcenter = r.center();
01992     ensureVisible(pcenter.x(), pcenter.y(), r.width()/2, r.height()/2);
01993 //  updateContents();
01994 //  updateNavPanelGeometry();
01995 //  slotUpdate();
01996 }
01997 
01998 void KexiTableView::updateAfterCancelRowEdit()
01999 {
02000     KexiDataAwareObjectInterface::updateAfterCancelRowEdit();
02001     m_navPanel->showEditingIndicator(false);
02002 }
02003 
02004 void KexiTableView::updateAfterAcceptRowEdit()
02005 {
02006     KexiDataAwareObjectInterface::updateAfterAcceptRowEdit();
02007     m_navPanel->showEditingIndicator(false);
02008 }
02009 
02010 bool KexiTableView::getVisibleLookupValue(QVariant& cellValue, KexiTableEdit *edit, 
02011     KexiTableItem *item, KexiTableViewColumn *tvcol) const
02012 {
02013     if (edit->columnInfo() && edit->columnInfo()->indexForVisibleLookupValue()!=-1
02014         && edit->columnInfo()->indexForVisibleLookupValue() < (int)item->count())
02015     {
02016         const QVariant *visibleFieldValue = 0;
02017         if (m_currentItem == item && m_data->rowEditBuffer()) {
02018             visibleFieldValue = m_data->rowEditBuffer()->at( 
02019                 *tvcol->visibleLookupColumnInfo, false );
02020         }
02021         
02022         if (visibleFieldValue)
02023             //(use bufferedValueAt() - try to get buffered visible value for lookup field)
02024             cellValue = *visibleFieldValue; //txt = visibleFieldValue->toString();
02025         else
02026             cellValue /*txt*/ = item->at( edit->columnInfo()->indexForVisibleLookupValue() ); //.toString();
02027         return true;
02028     }
02029     return false;
02030 }
02031 
02032 //reimpl.
02033 void KexiTableView::removeEditor()
02034 {
02035     if (!m_editor)
02036         return;
02037     KexiDataAwareObjectInterface::removeEditor();
02038     viewport()->setFocus();
02039 }
02040 
02041 void KexiTableView::slotRowRepaintRequested(KexiTableItem& item)
02042 {
02043     updateRow( m_data->findRef(&item) );
02044 }
02045 
02046 //(js) unused
02047 void KexiTableView::slotAutoScroll()
02048 {
02049     kdDebug(44021) << "KexiTableView::slotAutoScroll()" <<endl;
02050     if (!d->needAutoScroll)
02051         return;
02052 
02053     switch(d->scrollDirection)
02054     {
02055         case ScrollDown:
02056             setCursorPosition(m_curRow + 1, m_curCol);
02057             break;
02058 
02059         case ScrollUp:
02060             setCursorPosition(m_curRow - 1, m_curCol);
02061             break;
02062         case ScrollLeft:
02063             setCursorPosition(m_curRow, m_curCol - 1);
02064             break;
02065 
02066         case ScrollRight:
02067             setCursorPosition(m_curRow, m_curCol + 1);
02068             break;
02069     }
02070 }
02071 
02072 #ifndef KEXI_NO_PRINT
02073 void
02074 KexiTableView::print(KPrinter &/*printer*/)
02075 {
02076 //  printer.setFullPage(true);
02077 #if 0
02078     int leftMargin = printer.margins().width() + 2 + d->rowHeight;
02079     int topMargin = printer.margins().height() + 2;
02080 //  int bottomMargin = topMargin + ( printer.realPageSize()->height() * printer.resolution() + 36 ) / 72;
02081     int bottomMargin = 0;
02082     kdDebug(44021) << "KexiTableView::print: bottom = " << bottomMargin << endl;
02083 
02084     QPainter p(&printer);
02085 
02086     KexiTableItem *i;
02087     int width = leftMargin;
02088     for(int col=0; col < columns(); col++)
02089     {
02090         p.fillRect(width, topMargin - d->rowHeight, columnWidth(col), d->rowHeight, QBrush(Qt::gray));
02091         p.drawRect(width, topMargin - d->rowHeight, columnWidth(col), d->rowHeight);
02092         p.drawText(width, topMargin - d->rowHeight, columnWidth(col), d->rowHeight, Qt::AlignLeft | Qt::AlignVCenter, 
02093             m_horizontalHeader->label(col));
02094         width = width + columnWidth(col);
02095     }
02096 
02097     int yOffset = topMargin;
02098     int row = 0;
02099     int right = 0;
02100     for(i = m_data->first(); i; i = m_data->next())
02101     {
02102         if(!i->isInsertItem())
02103         {   kdDebug(44021) << "KexiTableView::print: row = " << row << " y = " << yOffset << endl;
02104             int xOffset = leftMargin;
02105             for(int col=0; col < columns(); col++)
02106             {
02107                 kdDebug(44021) << "KexiTableView::print: col = " << col << " x = " << xOffset << endl;
02108                 p.saveWorldMatrix();
02109                 p.translate(xOffset, yOffset);
02110                 paintCell(&p, i, col, QRect(0, 0, columnWidth(col) + 1, d->rowHeight), true);
02111                 p.restoreWorldMatrix();
02112 //          p.drawRect(xOffset, yOffset, columnWidth(col), d->rowHeight);
02113                 xOffset = xOffset + columnWidth(col);
02114                 right = xOffset;
02115             }
02116 
02117             row++;
02118             yOffset = topMargin  + row * d->rowHeight;
02119         }
02120 
02121         if(yOffset > 900)
02122         {
02123             p.drawLine(leftMargin, topMargin, leftMargin, yOffset);
02124             p.drawLine(leftMargin, topMargin, right - 1, topMargin);
02125             printer.newPage();
02126             yOffset = topMargin;
02127             row = 0;
02128         }
02129     }
02130     p.drawLine(leftMargin, topMargin, leftMargin, yOffset);
02131     p.drawLine(leftMargin, topMargin, right - 1, topMargin);
02132 
02133 //  p.drawLine(60,60,120,150);
02134     p.end();
02135 #endif
02136 }
02137 #endif
02138 
02139 QString KexiTableView::columnCaption(int colNum) const
02140 {
02141     return m_horizontalHeader->label(colNum);
02142 }
02143 
02144 KexiDB::Field* KexiTableView::field(int colNum) const
02145 {
02146     if (!m_data || !m_data->column(colNum))
02147         return 0;
02148     return m_data->column(colNum)->field();
02149 }
02150 
02151 void KexiTableView::adjustColumnWidthToContents(int colNum)
02152 {
02153     if (!hasData())
02154         return;
02155     if (colNum==-1) {
02156         const int cols = columns();
02157         for (int i=0; i<cols; i++)
02158             adjustColumnWidthToContents(i);
02159         return;
02160     }
02161 
02162     int indexOfVisibleColumn = (m_data->column(colNum) && m_data->column(colNum)->columnInfo) 
02163         ? m_data->column(colNum)->columnInfo->indexForVisibleLookupValue() : -1;
02164     if (-1==indexOfVisibleColumn)
02165         indexOfVisibleColumn = colNum;
02166 
02167     if (indexOfVisibleColumn < 0)
02168         return;
02169 
02170     QPtrListIterator<KexiTableItem> it = m_data->iterator();
02171     if (it.current() && it.current()->count()<=(uint)indexOfVisibleColumn)
02172         return;
02173 
02174     KexiCellEditorFactoryItem *item = KexiCellEditorFactory::item( columnType(indexOfVisibleColumn) );
02175     if (!item)
02176         return;
02177     QFontMetrics fm(fontMetrics());
02178     int maxw = horizontalHeaderVisible()
02179         ? fm.width( m_horizontalHeader->label( colNum/* not indexOfVisibleColumn*/ ) ) : 0;
02180     if (maxw == 0 && m_data->isEmpty())
02181         return; //nothing to adjust
02182 
02184 
02185     KexiTableEdit *ed = tableEditorWidget( colNum/* not indexOfVisibleColumn*/ );
02186     if (ed) {
02187         for (it = m_data->iterator(); it.current(); ++it) {
02188             const int wfw = ed->widthForValue( it.current()->at( indexOfVisibleColumn ), fm );
02189             maxw = QMAX( maxw, wfw );
02190         }
02191         const bool focused = currentColumn() == colNum;
02192         maxw += (fm.width("  ") + ed->leftMargin() + ed->rightMargin(focused));
02193     }
02194     if (maxw < KEXITV_MINIMUM_COLUMN_WIDTH )
02195         maxw = KEXITV_MINIMUM_COLUMN_WIDTH; //not too small
02196     kexidbg << "KexiTableView: setColumnWidth(colNum=" << colNum 
02197         << ", indexOfVisibleColumn=" << indexOfVisibleColumn << ", width=" << maxw <<" )" << endl;
02198     setColumnWidth( colNum/* not indexOfVisibleColumn*/, maxw );
02199 }
02200 
02201 void KexiTableView::setColumnWidth(int colNum, int width)
02202 {
02203     if (columns()<=colNum || colNum < 0)
02204         return;
02205     const int oldWidth = m_horizontalHeader->sectionSize( colNum );
02206     m_horizontalHeader->resizeSection( colNum, width );
02207     slotTopHeaderSizeChange( colNum, oldWidth, m_horizontalHeader->sectionSize( colNum ) );
02208 }
02209 
02210 void KexiTableView::maximizeColumnsWidth( const QValueList<int> &columnList )
02211 {
02212     if (!isVisible()) {
02213         d->maximizeColumnsWidthOnShow += columnList;
02214         return;
02215     }
02216     if (width() <= m_horizontalHeader->headerWidth())
02217         return;
02218     //sort the list and make it unique
02219     QValueList<int> cl, sortedList = columnList;
02220     qHeapSort(sortedList);
02221     int i=-999;
02222 
02223     QValueList<int>::ConstIterator it, end( sortedList.constEnd() );
02224     for ( it = sortedList.constBegin(); it != end; ++it) {
02225         if (i != (*it)) {
02226             cl += (*it);
02227             i = (*it);
02228         }
02229     }
02230     //resize
02231     int sizeToAdd = (width() - m_horizontalHeader->headerWidth()) / cl.count() - verticalHeader()->width();
02232     if (sizeToAdd<=0)
02233         return;
02234     end = cl.constEnd();
02235     for ( it = cl.constBegin(); it != end; ++it) {
02236         int w = m_horizontalHeader->sectionSize(*it);
02237         if (w>0) {
02238             m_horizontalHeader->resizeSection(*it, w+sizeToAdd);
02239         }
02240     }
02241     updateContents();
02242     editorShowFocus( m_curRow, m_curCol );
02243 }
02244 
02245 void KexiTableView::adjustHorizontalHeaderSize()
02246 {
02247     m_horizontalHeader->adjustHeaderSize();
02248 }
02249 
02250 void KexiTableView::setColumnStretchEnabled( bool set, int colNum )
02251 {
02252     m_horizontalHeader->setStretchEnabled( set, colNum );
02253 }
02254 
02255 void KexiTableView::setEditableOnDoubleClick(bool set)
02256 {
02257     d->editOnDoubleClick = set;
02258 }
02259 bool KexiTableView::editableOnDoubleClick() const
02260 {
02261     return d->editOnDoubleClick;
02262 }
02263 
02264 bool KexiTableView::verticalHeaderVisible() const
02265 {
02266     return m_verticalHeader->isVisible();
02267 }
02268 
02269 void KexiTableView::setVerticalHeaderVisible(bool set)
02270 {
02271     int left_width;
02272     if (set) {
02273         m_verticalHeader->show();
02274         left_width = QMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight);
02275     }
02276     else {
02277         m_verticalHeader->hide();
02278         left_width = 0;
02279     }
02280     setMargins( left_width, horizontalHeaderVisible() ? m_horizontalHeader->sizeHint().height() : 0, 0, 0);
02281 }
02282 
02283 bool KexiTableView::horizontalHeaderVisible() const
02284 {
02285     return d->horizontalHeaderVisible;
02286 }
02287 
02288 void KexiTableView::setHorizontalHeaderVisible(bool set)
02289 {
02290     int top_height;
02291     d->horizontalHeaderVisible = set; //needed because isVisible() is not always accurate
02292     if (set) {
02293         m_horizontalHeader->show();
02294         top_height = m_horizontalHeader->sizeHint().height();
02295     }
02296     else {
02297         m_horizontalHeader->hide();
02298         top_height = 0;
02299     }
02300     setMargins( verticalHeaderVisible() ? m_verticalHeader->width() : 0, top_height, 0, 0);
02301 }
02302 
02303 void KexiTableView::triggerUpdate()
02304 {
02305 //  kdDebug(44021) << "KexiTableView::triggerUpdate()" << endl;
02306 //  if (!d->pUpdateTimer->isActive())
02307         d->pUpdateTimer->start(20, true);
02308 //      d->pUpdateTimer->start(200, true);
02309 }
02310 
02311 void KexiTableView::setHBarGeometry( QScrollBar & hbar, int x, int y, int w, int h )
02312 {
02313 /*todo*/
02314     kdDebug(44021)<<"KexiTableView::setHBarGeometry"<<endl;
02315     if (d->appearance.navigatorEnabled) {
02316         m_navPanel->setHBarGeometry( hbar, x, y, w, h );
02317     }
02318     else {
02319         hbar.setGeometry( x , y, w, h );
02320     }
02321 }
02322 
02323 void KexiTableView::setSpreadSheetMode()
02324 {
02325     KexiDataAwareObjectInterface::setSpreadSheetMode();
02326     //copy m_navPanelEnabled flag
02327     Appearance a = d->appearance;
02328     a.navigatorEnabled = m_navPanelEnabled;
02329     setAppearance( a );
02330 }
02331 
02332 int KexiTableView::validRowNumber(const QString& text)
02333 {
02334     bool ok=true;
02335     int r = text.toInt(&ok);
02336     if (!ok || r<1)
02337         r = 1;
02338     else if (r > (rows()+(isInsertingEnabled()?1:0)))
02339         r = rows()+(isInsertingEnabled()?1:0);
02340     return r-1;
02341 }
02342 
02343 void KexiTableView::moveToRecordRequested( uint r )
02344 {
02345     if (r > uint(rows()+(isInsertingEnabled()?1:0)))
02346         r = rows()+(isInsertingEnabled()?1:0);
02347     setFocus();
02348     selectRow( r );
02349 }
02350 
02351 void KexiTableView::moveToLastRecordRequested()
02352 {
02353     setFocus();
02354     selectRow(rows()>0 ? (rows()-1) : 0);
02355 }
02356 
02357 void KexiTableView::moveToPreviousRecordRequested()
02358 {
02359     setFocus();
02360     selectPrevRow();
02361 }
02362 
02363 void KexiTableView::moveToNextRecordRequested()
02364 {
02365     setFocus();
02366     selectNextRow();
02367 }
02368 
02369 void KexiTableView::moveToFirstRecordRequested()
02370 {
02371     setFocus();
02372     selectFirstRow();
02373 }
02374 
02375 void KexiTableView::copySelection()
02376 {
02377     if (m_currentItem && m_curCol!=-1) {
02378         KexiTableEdit *edit = tableEditorWidget( m_curCol );
02379         QVariant defaultValue;
02380         const bool defaultValueDisplayed 
02381             = isDefaultValueDisplayed(m_currentItem, m_curCol, &defaultValue);
02382         if (edit) {
02383             QVariant visibleValue;
02384             getVisibleLookupValue(visibleValue, edit, m_currentItem, m_data->column(m_curCol));
02385             edit->handleCopyAction( 
02386                 defaultValueDisplayed ? defaultValue : m_currentItem->at( m_curCol ),
02387                 visibleValue );
02388         }
02389     }
02390 }
02391 
02392 void KexiTableView::cutSelection()
02393 {
02394     //try to handle @ editor's level
02395     KexiTableEdit *edit = tableEditorWidget( m_curCol );
02396     if (edit)
02397         edit->handleAction("edit_cut");
02398 }
02399 
02400 void KexiTableView::paste()
02401 {
02402     //try to handle @ editor's level
02403     KexiTableEdit *edit = tableEditorWidget( m_curCol );
02404     if (edit)
02405         edit->handleAction("edit_paste");
02406 }
02407 
02408 bool KexiTableView::eventFilter( QObject *o, QEvent *e )
02409 {
02410     //don't allow to stole key my events by others:
02411 //  kexidbg << "spontaneous " << e->spontaneous() << " type=" << e->type() << endl;
02412 
02413     if (e->type()==QEvent::KeyPress) {
02414         if (e->spontaneous() /*|| e->type()==QEvent::AccelOverride*/) {
02415             QKeyEvent *ke = static_cast<QKeyEvent*>(e);
02416             const int k = ke->key();
02417             int s = ke->state();
02418             //cell editor's events:
02419             //try to handle the event @ editor's level
02420             KexiTableEdit *edit = tableEditorWidget( m_curCol );
02421             if (edit && edit->handleKeyPress(ke, m_editor==edit)) {
02422                 ke->accept();
02423                 return true;
02424             }
02425             else if (m_editor && (o==dynamic_cast<QObject*>(m_editor) || o==m_editor->widget())) {
02426                 if ( (k==Qt::Key_Tab && (s==Qt::NoButton || s==Qt::ShiftButton))
02427                     || (overrideEditorShortcutNeeded(ke))
02428                     || (k==Qt::Key_Enter || k==Qt::Key_Return || k==Qt::Key_Up || k==Qt::Key_Down) 
02429                     || (k==Qt::Key_Left && m_editor->cursorAtStart())
02430                     || (k==Qt::Key_Right && m_editor->cursorAtEnd())
02431                     )
02432                 {
02433                     //try to steal the key press from editor or it's internal widget...
02434                     keyPressEvent(ke);
02435                     if (ke->isAccepted())
02436                         return true;
02437                 }
02438             }
02439             /*
02440             else if (e->type()==QEvent::KeyPress && (o==this || (m_editor && o==m_editor->widget()))){//|| o==viewport())
02441                 keyPressEvent(ke);
02442                 if (ke->isAccepted())
02443                     return true;
02444             }*/
02445 /*todo          else if ((k==Qt::Key_Tab || k==(Qt::SHIFT|Qt::Key_Tab)) && o==d->navRowNumber) {
02446                 //tab key focuses tv
02447                 ke->accept();
02448                 setFocus();
02449                 return true;
02450             }*/
02451         }
02452     }
02453     else if (o==horizontalScrollBar()) {
02454         if ((e->type()==QEvent::Show && !horizontalScrollBar()->isVisible()) 
02455             || (e->type()==QEvent::Hide && horizontalScrollBar()->isVisible())) {
02456             updateWidgetContentsSize();
02457         }
02458     }
02459     else if (e->type()==QEvent::Leave) {
02460         if (o==viewport() && d->appearance.rowMouseOverHighlightingEnabled
02461             && d->appearance.persistentSelections)
02462         {
02463             if (d->highlightedRow!=-1) {
02464                 int oldRow = d->highlightedRow;
02465                 d->highlightedRow = -1;
02466                 updateRow(oldRow);
02467                 const bool dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted 
02468                     = d->appearance.rowHighlightingEnabled && !d->appearance.persistentSelections;
02469                 if (oldRow!=m_curRow && m_curRow>=0) {
02470                     if (!dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted)
02471                         //no highlight for now: show selection again
02472                         updateRow(m_curRow);
02473                     m_verticalHeader->setHighlightedRow(-1);
02474                 }
02475             }
02476         }
02477         d->recentCellWithToolTip = QPoint(-1,-1);
02478     }
02479 /*  else if (e->type()==QEvent::FocusOut && o->inherits("QWidget")) {
02480         //hp==true if currently focused widget is a child of this table view
02481         const bool hp = KexiUtils::hasParent( static_cast<QWidget*>(o), focusWidget());
02482         if (!hp && KexiUtils::hasParent( this, static_cast<QWidget*>(o))) {
02483             //accept row editing if focus is moved to foreign widget 
02484             //(not a child, like eg. editor) from one of our table view's children
02485             //or from table view itself
02486             if (!acceptRowEdit()) {
02487                 static_cast<QWidget*>(o)->setFocus();
02488                 return true;
02489             }
02490         }
02491     }*/
02492     return QScrollView::eventFilter(o,e);
02493 }
02494 
02495 void KexiTableView::slotTopHeaderSizeChange( 
02496     int /*section*/, int /*oldSize*/, int /*newSize*/ )
02497 {
02498     editorShowFocus( m_curRow, m_curCol );
02499 }
02500 
02501 void KexiTableView::setBottomMarginInternal(int pixels)
02502 {
02503     d->internal_bottomMargin = pixels;
02504 }
02505 
02506 void KexiTableView::paletteChange( const QPalette &oldPalette )
02507 {
02508     Q_UNUSED(oldPalette);
02509     //update:
02510     if (m_verticalHeader)
02511         m_verticalHeader->setSelectionBackgroundColor( palette().active().highlight() );
02512     if (m_horizontalHeader)
02513         m_horizontalHeader->setSelectionBackgroundColor( palette().active().highlight() );
02514 }
02515 
02516 const KexiTableView::Appearance& KexiTableView::appearance() const
02517 {
02518     return d->appearance;
02519 }
02520 
02521 void KexiTableView::setAppearance(const Appearance& a)
02522 {
02523 //  if (d->appearance.fullRowSelection != a.fullRowSelection) {
02524     if (a.fullRowSelection) {
02525         d->rowHeight -= 1;
02526     }
02527     else {
02528         d->rowHeight += 1;
02529     }
02530     if (m_verticalHeader)
02531         m_verticalHeader->setCellHeight(d->rowHeight);
02532     if (m_horizontalHeader) {
02533         setMargins(
02534             QMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight),
02535             m_horizontalHeader->sizeHint().height(), 0, 0);
02536     }
02537 //  }
02538     if (a.rowHighlightingEnabled)
02539         m_updateEntireRowWhenMovingToOtherRow = true;
02540 
02541     if(!a.navigatorEnabled)
02542         m_navPanel->hide();
02543     else
02544         m_navPanel->show();
02545 //  }
02546 
02547     d->highlightedRow = -1;
02549     viewport()->setMouseTracking(a.rowMouseOverHighlightingEnabled);
02550 
02551     d->appearance = a;
02552 
02553     setFont(font()); //this also updates contents
02554 }
02555 
02556 int KexiTableView::highlightedRow() const
02557 {
02558     return d->highlightedRow;
02559 }
02560 
02561 void KexiTableView::setHighlightedRow(int row)
02562 {
02563     if (row!=-1) {
02564         row = QMIN(rows() - 1 + (isInsertingEnabled()?1:0), row);
02565         row = QMAX(0, row);
02566         ensureCellVisible(row, -1);
02567     }
02568     const int previouslyHighlightedRow = d->highlightedRow;
02569     if (previouslyHighlightedRow == row) {
02570         if (previouslyHighlightedRow!=-1)
02571             updateRow(previouslyHighlightedRow);
02572         return;
02573     }
02574     d->highlightedRow = row;
02575     if (d->highlightedRow!=-1)
02576         updateRow(d->highlightedRow);
02577 
02578     if (previouslyHighlightedRow!=-1)
02579         updateRow(previouslyHighlightedRow);
02580 
02581     if (m_curRow>=0 && (previouslyHighlightedRow==-1 || previouslyHighlightedRow==m_curRow)
02582         && d->highlightedRow!=m_curRow && !d->appearance.persistentSelections)
02583     {
02584         //currently selected row needs to be repainted
02585         updateRow(m_curRow);
02586     }
02587 }
02588 
02589 KexiTableItem *KexiTableView::highlightedItem() const
02590 {
02591     return d->highlightedRow == -1 ? 0 : m_data->at(d->highlightedRow);
02592 }
02593 
02594 void KexiTableView::slotSettingsChanged(int category)
02595 {
02596     if (category==KApplication::SETTINGS_SHORTCUTS) {
02597         d->contextMenuKey = KGlobalSettings::contextMenuKey();
02598     }
02599 }
02600 
02601 int KexiTableView::lastVisibleRow() const
02602 {
02603     return rowAt( contentsY() );
02604 }
02605 
02606 #include "kexitableview.moc"
02607 
KDE Home | KDE Accessibility Home | Description of Access Keys