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