Kexi API Documentation (2.0 alpha)

kexitableview.cpp

Go to the documentation of this file.
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_curre