You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2608 lines
77 KiB
2608 lines
77 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2002 Till Busch <till@bux.at>
|
|
Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
|
|
Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
|
|
Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
|
|
Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this program; see the file COPYING. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
Original Author: Till Busch <till@bux.at>
|
|
Original Project: buX (www.bux.at)
|
|
*/
|
|
|
|
#include <tqpainter.h>
|
|
#include <tqkeycode.h>
|
|
#include <tqlineedit.h>
|
|
#include <tqcombobox.h>
|
|
#include <tqwmatrix.h>
|
|
#include <tqtimer.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqcursor.h>
|
|
#include <tqstyle.h>
|
|
#include <tqlayout.h>
|
|
#include <tqlabel.h>
|
|
#include <tqwhatsthis.h>
|
|
|
|
#include <kglobal.h>
|
|
#include <klocale.h>
|
|
#include <kdebug.h>
|
|
#include <kapplication.h>
|
|
#include <kiconloader.h>
|
|
#include <kmessagebox.h>
|
|
|
|
#ifndef KEXI_NO_PRINT
|
|
# include <kprinter.h>
|
|
#endif
|
|
|
|
#include "kexitableview.h"
|
|
#include <kexiutils/utils.h>
|
|
#include <kexiutils/validator.h>
|
|
|
|
#include "kexicelleditorfactory.h"
|
|
#include "kexitableviewheader.h"
|
|
#include "kexitableview_p.h"
|
|
#include <widget/utils/kexirecordmarker.h>
|
|
#include <widget/utils/kexidisplayutils.h>
|
|
#include <kexidb/cursor.h>
|
|
|
|
KexiTableView::Appearance::Appearance(TQWidget *widget)
|
|
: alternateBackgroundColor( TDEGlobalSettings::alternateBackgroundColor() )
|
|
{
|
|
//set defaults
|
|
if (tqApp) {
|
|
TQPalette p = widget ? widget->palette() : tqApp->palette();
|
|
baseColor = p.active().base();
|
|
textColor = p.active().text();
|
|
borderColor = TQColor(200,200,200);
|
|
emptyAreaColor = p.active().color(TQColorGroup::Base);
|
|
rowHighlightingColor = KexiUtils::blendedColors(p.active().highlight(), baseColor, 33, 66);
|
|
rowMouseOverHighlightingColor = KexiUtils::blendedColors(p.active().highlight(), baseColor, 10, 90);
|
|
rowMouseOverAlternateHighlightingColor = KexiUtils::blendedColors(p.active().highlight(), alternateBackgroundColor, 10, 90);
|
|
rowHighlightingTextColor = textColor;
|
|
rowMouseOverHighlightingTextColor = textColor;
|
|
}
|
|
backgroundAltering = true;
|
|
rowMouseOverHighlightingEnabled = true;
|
|
rowHighlightingEnabled = true;
|
|
persistentSelections = true;
|
|
navigatorEnabled = true;
|
|
fullRowSelection = false;
|
|
gridEnabled = true;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
|
|
//! @internal A special What's This class displaying information about a given column
|
|
class KexiTableView::WhatsThis : public TQWhatsThis
|
|
{
|
|
public:
|
|
WhatsThis(KexiTableView* tv) : TQWhatsThis(tv), m_tv(tv)
|
|
{
|
|
Q_ASSERT(tv);
|
|
}
|
|
virtual ~WhatsThis()
|
|
{
|
|
}
|
|
virtual TQString text( const TQPoint & pos)
|
|
{
|
|
const int leftMargin = m_tv->verticalHeaderVisible() ? m_tv->verticalHeader()->width() : 0;
|
|
//const int topMargin = m_tv->horizontalHeaderVisible() ? m_tv->d->pTopHeader->height() : 0;
|
|
//const int bottomMargin = m_tv->d->appearance.navigatorEnabled ? m_tv->m_navPanel->height() : 0;
|
|
if (KexiUtils::hasParent(TQT_TQOBJECT(m_tv->verticalHeader()), TQT_TQOBJECT(m_tv->childAt(pos)))) {
|
|
return i18n("Contains a pointer to the currently selected row");
|
|
}
|
|
else if (KexiUtils::hasParent(TQT_TQOBJECT(m_tv->m_navPanel), TQT_TQOBJECT(m_tv->childAt(pos)))) {
|
|
return i18n("Row navigator");
|
|
// return TQWhatsThis::textFor(m_tv->m_navPanel, TQPoint( pos.x(), pos.y() - m_tv->height() + bottomMargin ));
|
|
}
|
|
KexiDB::Field *f = m_tv->field( m_tv->columnAt(pos.x()-leftMargin) );
|
|
if (!f)
|
|
return TQString();
|
|
return f->description().isEmpty() ? f->captionOrName() : f->description();
|
|
}
|
|
protected:
|
|
KexiTableView *m_tv;
|
|
};
|
|
|
|
//-----------------------------------------
|
|
|
|
KexiTableViewCellToolTip::KexiTableViewCellToolTip( KexiTableView * tableView )
|
|
: TQToolTip(tableView->viewport())
|
|
, m_tableView(tableView)
|
|
{
|
|
}
|
|
|
|
KexiTableViewCellToolTip::~KexiTableViewCellToolTip()
|
|
{
|
|
remove(parentWidget());
|
|
}
|
|
|
|
void KexiTableViewCellToolTip::maybeTip( const TQPoint & p )
|
|
{
|
|
const TQPoint cp( m_tableView->viewportToContents( p ) );
|
|
const int row = m_tableView->rowAt( cp.y(), true/*ignoreEnd*/ );
|
|
const int col = m_tableView->columnAt( cp.x() );
|
|
|
|
//show tooltip if needed
|
|
if (col>=0 && row>=0) {
|
|
KexiTableEdit *editor = m_tableView->tableEditorWidget( col );
|
|
const bool insertRowSelected = m_tableView->isInsertingEnabled() && row==m_tableView->rows();
|
|
KexiTableItem *item = insertRowSelected ? m_tableView->m_insertItem : m_tableView->itemAt( row );
|
|
if (editor && item && (col < (int)item->count())) {
|
|
int w = m_tableView->columnWidth( col );
|
|
int h = m_tableView->rowHeight();
|
|
int x = 0;
|
|
int y_offset = 0;
|
|
int align = TQt::SingleLine | TQt::AlignVCenter;
|
|
TQString txtValue;
|
|
TQVariant cellValue;
|
|
KexiTableViewColumn *tvcol = m_tableView->column(col);
|
|
if (!m_tableView->getVisibleLookupValue(cellValue, editor, item, tvcol))
|
|
cellValue = insertRowSelected ? editor->displayedField()->defaultValue() : item->at(col); //display default value if available
|
|
const bool focused = m_tableView->selectedItem() == item && col == m_tableView->currentColumn();
|
|
editor->setupContents( 0, focused, cellValue, txtValue, align, x, y_offset, w, h );
|
|
TQRect realRect(m_tableView->columnPos(col)-m_tableView->contentsX(),
|
|
m_tableView->rowPos(row)-m_tableView->contentsY(), w, h); //m_tableView->cellGeometry( row, col ));
|
|
if (editor->showToolTipIfNeeded(
|
|
txtValue.isEmpty() ? item->at(col) : TQVariant(txtValue),
|
|
realRect, m_tableView->fontMetrics(), focused))
|
|
{
|
|
TQString squeezedTxtValue;
|
|
if (txtValue.length() > 50)
|
|
squeezedTxtValue = txtValue.left(100) + "...";
|
|
else
|
|
squeezedTxtValue = txtValue;
|
|
tip( realRect, squeezedTxtValue );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------
|
|
|
|
KexiTableView::KexiTableView(KexiTableViewData* data, TQWidget* parent, const char* name)
|
|
: TQScrollView(parent, name, /*TQt::WRepaintNoErase | */TQt::WStaticContents /*| TQt::WResizeNoErase*/)
|
|
, KexiRecordNavigatorHandler()
|
|
, KexiSharedActionClient()
|
|
, KexiDataAwareObjectInterface()
|
|
{
|
|
//not needed KexiTableView::initCellEditorFactories();
|
|
|
|
d = new KexiTableViewPrivate(this);
|
|
|
|
connect( kapp, TQT_SIGNAL( settingsChanged(int) ), TQT_SLOT( slotSettingsChanged(int) ) );
|
|
slotSettingsChanged(TDEApplication::SETTINGS_SHORTCUTS);
|
|
|
|
m_data = new KexiTableViewData(); //to prevent crash because m_data==0
|
|
m_owner = true; //-this will be deleted if needed
|
|
|
|
setResizePolicy(Manual);
|
|
viewport()->setBackgroundMode(TQt::NoBackground);
|
|
// viewport()->setFocusPolicy(TQ_StrongFocus);
|
|
viewport()->setFocusPolicy(TQ_WheelFocus);
|
|
setFocusPolicy(TQ_WheelFocus); //<--- !!!!! important (was NoFocus),
|
|
// otherwise TQApplication::setActiveWindow() won't activate
|
|
// this widget when needed!
|
|
// setFocusProxy(viewport());
|
|
viewport()->installEventFilter(this);
|
|
|
|
//setup colors defaults
|
|
setBackgroundMode(TQt::PaletteBackground);
|
|
// setEmptyAreaColor(d->appearance.baseColor);//palette().active().color(TQColorGroup::Base));
|
|
|
|
// d->baseColor = colorGroup().base();
|
|
// d->textColor = colorGroup().text();
|
|
|
|
// d->altColor = TDEGlobalSettings::alternateBackgroundColor();
|
|
// d->grayColor = TQColor(200,200,200);
|
|
d->diagonalGrayPattern = TQBrush(d->appearance.borderColor, TQt::BDiagPattern);
|
|
|
|
setLineWidth(1);
|
|
horizontalScrollBar()->installEventFilter(this);
|
|
horizontalScrollBar()->raise();
|
|
verticalScrollBar()->raise();
|
|
|
|
//context menu
|
|
m_popupMenu = new TDEPopupMenu(this, "contextMenu");
|
|
#if 0 //moved to mainwindow's actions
|
|
d->menu_id_addRecord = m_popupMenu->insertItem(i18n("Add Record"), this, TQT_SLOT(addRecord()), TQt::CTRL+TQt::Key_Insert);
|
|
d->menu_id_removeRecord = m_popupMenu->insertItem(
|
|
kapp->iconLoader()->loadIcon("button_cancel", TDEIcon::Small),
|
|
i18n("Remove Record"), this, TQT_SLOT(removeRecord()), TQt::CTRL+TQt::Key_Delete);
|
|
#endif
|
|
|
|
#ifdef TQ_WS_WIN
|
|
d->rowHeight = fontMetrics().lineSpacing() + 4;
|
|
#else
|
|
d->rowHeight = fontMetrics().lineSpacing() + 1;
|
|
#endif
|
|
|
|
if(d->rowHeight < 17)
|
|
d->rowHeight = 17;
|
|
|
|
d->pUpdateTimer = new TQTimer(this);
|
|
|
|
// setMargins(14, fontMetrics().height() + 4, 0, 0);
|
|
|
|
// Create headers
|
|
m_horizontalHeader = new KexiTableViewHeader(this, "topHeader");
|
|
m_horizontalHeader->setSelectionBackgroundColor( palette().active().highlight() );
|
|
m_horizontalHeader->setOrientation(Qt::Horizontal);
|
|
m_horizontalHeader->setTracking(false);
|
|
m_horizontalHeader->setMovingEnabled(false);
|
|
connect(m_horizontalHeader, TQT_SIGNAL(sizeChange(int,int,int)), this, TQT_SLOT(slotTopHeaderSizeChange(int,int,int)));
|
|
|
|
m_verticalHeader = new KexiRecordMarker(this, "rm");
|
|
m_verticalHeader->setSelectionBackgroundColor( palette().active().highlight() );
|
|
m_verticalHeader->setCellHeight(d->rowHeight);
|
|
// m_verticalHeader->setFixedWidth(d->rowHeight);
|
|
m_verticalHeader->setCurrentRow(-1);
|
|
|
|
setMargins(
|
|
TQMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight),
|
|
m_horizontalHeader->sizeHint().height(), 0, 0);
|
|
|
|
setupNavigator();
|
|
|
|
// setMinimumHeight(horizontalScrollBar()->height() + d->rowHeight + topMargin());
|
|
|
|
// navPanelLyr->addStretch(25);
|
|
// enableClipper(true);
|
|
|
|
if (data)
|
|
setData( data );
|
|
|
|
#if 0//(js) doesn't work!
|
|
d->scrollTimer = new TQTimer(this);
|
|
connect(d->scrollTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotAutoScroll()));
|
|
#endif
|
|
|
|
// setBackgroundAltering(true);
|
|
// setFullRowSelectionEnabled(false);
|
|
|
|
setAcceptDrops(true);
|
|
viewport()->setAcceptDrops(true);
|
|
|
|
// Connect header, table and scrollbars
|
|
connect(horizontalScrollBar(), TQT_SIGNAL(valueChanged(int)), m_horizontalHeader, TQT_SLOT(setOffset(int)));
|
|
connect(verticalScrollBar(), TQT_SIGNAL(valueChanged(int)), m_verticalHeader, TQT_SLOT(setOffset(int)));
|
|
connect(m_horizontalHeader, TQT_SIGNAL(sizeChange(int, int, int)), this, TQT_SLOT(slotColumnWidthChanged(int, int, int)));
|
|
connect(m_horizontalHeader, TQT_SIGNAL(sectionHandleDoubleClicked(int)), this, TQT_SLOT(slotSectionHandleDoubleClicked(int)));
|
|
connect(m_horizontalHeader, TQT_SIGNAL(clicked(int)), this, TQT_SLOT(sortColumnInternal(int)));
|
|
|
|
connect(d->pUpdateTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotUpdate()));
|
|
|
|
// horizontalScrollBar()->show();
|
|
updateScrollBars();
|
|
// resize(sizeHint());
|
|
// updateContents();
|
|
// setMinimumHeight(horizontalScrollBar()->height() + d->rowHeight + topMargin());
|
|
|
|
//TMP
|
|
//setVerticalHeaderVisible(false);
|
|
//setHorizontalHeaderVisible(false);
|
|
|
|
//will be updated by setAppearance: updateFonts();
|
|
setAppearance(d->appearance); //refresh
|
|
|
|
d->cellToolTip = new KexiTableViewCellToolTip(this);
|
|
new WhatsThis(this);
|
|
}
|
|
|
|
KexiTableView::~KexiTableView()
|
|
{
|
|
cancelRowEdit();
|
|
|
|
KexiTableViewData *data = m_data;
|
|
m_data = 0;
|
|
if (m_owner) {
|
|
if (data)
|
|
data->deleteLater();
|
|
}
|
|
delete d;
|
|
}
|
|
|
|
void KexiTableView::clearVariables()
|
|
{
|
|
KexiDataAwareObjectInterface::clearVariables();
|
|
d->clearVariables();
|
|
}
|
|
|
|
/*void KexiTableView::initActions(TDEActionCollection *ac)
|
|
{
|
|
emit reloadActions(ac);
|
|
}*/
|
|
|
|
void KexiTableView::setupNavigator()
|
|
{
|
|
updateScrollBars();
|
|
|
|
m_navPanel = new KexiRecordNavigator(this, leftMargin(), "navPanel");
|
|
m_navPanel->setRecordHandler(this);
|
|
m_navPanel->setSizePolicy(TQSizePolicy::Minimum,TQSizePolicy::Preferred);
|
|
}
|
|
|
|
void KexiTableView::initDataContents()
|
|
{
|
|
updateWidgetContentsSize();
|
|
|
|
KexiDataAwareObjectInterface::initDataContents();
|
|
|
|
m_navPanel->showEditingIndicator(false);
|
|
}
|
|
|
|
void KexiTableView::addHeaderColumn(const TQString& caption, const TQString& description,
|
|
const TQIconSet& icon, int width)
|
|
{
|
|
const int nr = m_horizontalHeader->count();
|
|
if (icon.isNull())
|
|
m_horizontalHeader->addLabel(caption, width);
|
|
else
|
|
m_horizontalHeader->addLabel(icon, caption, width);
|
|
|
|
if (!description.isEmpty())
|
|
m_horizontalHeader->setToolTip(nr, description);
|
|
}
|
|
|
|
void KexiTableView::updateWidgetContentsSize()
|
|
{
|
|
TQSize s(tableSize());
|
|
resizeContents(s.width(), s.height());
|
|
}
|
|
|
|
void KexiTableView::slotRowsDeleted( const TQValueList<int> &rows )
|
|
{
|
|
viewport()->repaint();
|
|
updateWidgetContentsSize();
|
|
setCursorPosition(TQMAX(0, (int)m_curRow - (int)rows.count()), -1, true);
|
|
}
|
|
|
|
|
|
/*void KexiTableView::addDropFilter(const TQString &filter)
|
|
{
|
|
d->dropFilters.append(filter);
|
|
viewport()->setAcceptDrops(true);
|
|
}*/
|
|
|
|
void KexiTableView::setFont( const TQFont &font )
|
|
{
|
|
TQScrollView::setFont(font);
|
|
updateFonts(true);
|
|
}
|
|
|
|
void KexiTableView::updateFonts(bool repaint)
|
|
{
|
|
#ifdef TQ_WS_WIN
|
|
d->rowHeight = fontMetrics().lineSpacing() + 4;
|
|
#else
|
|
d->rowHeight = fontMetrics().lineSpacing() + 1;
|
|
#endif
|
|
if (d->appearance.fullRowSelection) {
|
|
d->rowHeight -= 1;
|
|
}
|
|
if(d->rowHeight < 17)
|
|
d->rowHeight = 17;
|
|
// if(d->rowHeight < 22)
|
|
// d->rowHeight = 22;
|
|
setMargins(
|
|
TQMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight),
|
|
m_horizontalHeader->sizeHint().height(), 0, 0);
|
|
// setMargins(14, d->rowHeight, 0, 0);
|
|
m_verticalHeader->setCellHeight(d->rowHeight);
|
|
|
|
KexiDisplayUtils::initDisplayForAutonumberSign(d->autonumberSignDisplayParameters, this);
|
|
KexiDisplayUtils::initDisplayForDefaultValue(d->defaultValueDisplayParameters, this);
|
|
|
|
if (repaint)
|
|
updateContents();
|
|
}
|
|
|
|
void KexiTableView::updateAllVisibleRowsBelow(int row)
|
|
{
|
|
//get last visible row
|
|
int r = rowAt(clipper()->height()+contentsY());
|
|
if (r==-1) {
|
|
r = rows()+1+(isInsertingEnabled()?1:0);
|
|
}
|
|
//update all visible rows below
|
|
int leftcol = m_horizontalHeader->sectionAt( m_horizontalHeader->offset() );
|
|
// int row = m_curRow;
|
|
updateContents( columnPos( leftcol ), rowPos(row),
|
|
clipper()->width(), clipper()->height() - (rowPos(row) - contentsY()) );
|
|
}
|
|
|
|
void KexiTableView::clearColumnsInternal(bool /*repaint*/)
|
|
{
|
|
while(m_horizontalHeader->count()>0)
|
|
m_horizontalHeader->removeLabel(0);
|
|
}
|
|
|
|
void KexiTableView::slotUpdate()
|
|
{
|
|
// kdDebug(44021) << " KexiTableView::slotUpdate() -- " << endl;
|
|
// TQSize s(tableSize());
|
|
// viewport()->setUpdatesEnabled(false);
|
|
/// resizeContents(s.width(), s.height());
|
|
// viewport()->setUpdatesEnabled(true);
|
|
|
|
updateContents();
|
|
updateScrollBars();
|
|
if (m_navPanel)
|
|
m_navPanel->updateGeometry(leftMargin());
|
|
// updateNavPanelGeometry();
|
|
|
|
updateWidgetContentsSize();
|
|
// updateContents(0, contentsY()+clipper()->height()-2*d->rowHeight, clipper()->width(), d->rowHeight*3);
|
|
|
|
//updateGeometries();
|
|
// updateContents(0, 0, viewport()->width(), contentsHeight());
|
|
// updateGeometries();
|
|
}
|
|
|
|
int KexiTableView::currentLocalSortingOrder() const
|
|
{
|
|
if (m_horizontalHeader->sortIndicatorSection()==-1)
|
|
return 0;
|
|
return (m_horizontalHeader->sortIndicatorOrder() == TQt::Ascending) ? 1 : -1;
|
|
}
|
|
|
|
void KexiTableView::setLocalSortingOrder(int col, int order)
|
|
{
|
|
if (order == 0)
|
|
col = -1;
|
|
if (col>=0)
|
|
m_horizontalHeader->setSortIndicator(col, (order==1) ? TQt::Ascending : TQt::Descending);
|
|
}
|
|
|
|
int KexiTableView::currentLocalSortColumn() const
|
|
{
|
|
return m_horizontalHeader->sortIndicatorSection();
|
|
}
|
|
|
|
void KexiTableView::updateGUIAfterSorting()
|
|
{
|
|
int cw = columnWidth(m_curCol);
|
|
int rh = rowHeight();
|
|
|
|
// m_verticalHeader->setCurrentRow(m_curRow);
|
|
center(columnPos(m_curCol) + cw / 2, rowPos(m_curRow) + rh / 2);
|
|
// updateCell(oldRow, m_curCol);
|
|
// updateCell(m_curRow, m_curCol);
|
|
// slotUpdate();
|
|
|
|
updateContents();
|
|
// d->pUpdateTimer->start(1,true);
|
|
}
|
|
|
|
TQSizePolicy KexiTableView::sizePolicy() const
|
|
{
|
|
// this widget is expandable
|
|
return TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding);
|
|
}
|
|
|
|
TQSize KexiTableView::sizeHint() const
|
|
{
|
|
const TQSize &ts = tableSize();
|
|
int w = TQMAX( ts.width() + leftMargin()+ verticalScrollBar()->sizeHint().width() + 2*2,
|
|
(m_navPanel->isVisible() ? m_navPanel->width() : 0) );
|
|
int h = TQMAX( ts.height()+topMargin()+horizontalScrollBar()->sizeHint().height(),
|
|
minimumSizeHint().height() );
|
|
w = TQMIN( w, tqApp->desktop()->width()*3/4 ); //stretch
|
|
h = TQMIN( h, tqApp->desktop()->height()*3/4 ); //stretch
|
|
|
|
// kexidbg << "KexiTableView::sizeHint()= " <<w <<", " <<h << endl;
|
|
|
|
return TQSize(w, h);
|
|
/*TQSize(
|
|
TQMAX( ts.width() + leftMargin() + 2*2, (m_navPanel ? m_navPanel->width() : 0) ),
|
|
//+ TQMIN(m_verticalHeader->width(),d->rowHeight) + margin()*2,
|
|
TQMAX( ts.height()+topMargin()+horizontalScrollBar()->sizeHint().height(),
|
|
minimumSizeHint().height() )
|
|
);*/
|
|
// TQMAX(ts.height() + topMargin(), minimumSizeHint().height()) );
|
|
}
|
|
|
|
TQSize KexiTableView::minimumSizeHint() const
|
|
{
|
|
return TQSize(
|
|
leftMargin() + ((columns()>0)?columnWidth(0):KEXI_DEFAULT_DATA_COLUMN_WIDTH) + 2*2,
|
|
d->rowHeight*5/2 + topMargin() + (m_navPanel && m_navPanel->isVisible() ? m_navPanel->height() : 0)
|
|
);
|
|
}
|
|
|
|
void KexiTableView::createBuffer(int width, int height)
|
|
{
|
|
if(!d->pBufferPm)
|
|
d->pBufferPm = new TQPixmap(width, height);
|
|
else
|
|
if(d->pBufferPm->width() < width || d->pBufferPm->height() < height)
|
|
d->pBufferPm->resize(width, height);
|
|
// d->pBufferPm->fill();
|
|
}
|
|
|
|
//internal
|
|
inline void KexiTableView::paintRow(KexiTableItem *item,
|
|
TQPainter *pb, int r, int rowp, int cx, int cy,
|
|
int colfirst, int collast, int maxwc)
|
|
{
|
|
if (!item)
|
|
return;
|
|
// Go through the columns in the row r
|
|
// if we know from where to where, go through [colfirst, collast],
|
|
// else go through all of them
|
|
if (colfirst==-1)
|
|
colfirst=0;
|
|
if (collast==-1)
|
|
collast=columns()-1;
|
|
|
|
int transly = rowp-cy;
|
|
|
|
if (d->appearance.rowHighlightingEnabled && r == m_curRow && !d->appearance.fullRowSelection) {
|
|
pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.rowHighlightingColor);
|
|
}
|
|
else if (d->appearance.rowMouseOverHighlightingEnabled && r == d->highlightedRow) {
|
|
if(d->appearance.backgroundAltering && (r%2 != 0))
|
|
pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.rowMouseOverAlternateHighlightingColor);
|
|
else
|
|
pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.rowMouseOverHighlightingColor);
|
|
}
|
|
else {
|
|
if(d->appearance.backgroundAltering && (r%2 != 0))
|
|
pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.alternateBackgroundColor);
|
|
else
|
|
pb->fillRect(0, transly, maxwc, d->rowHeight, d->appearance.baseColor);
|
|
}
|
|
|
|
for(int c = colfirst; c <= collast; c++)
|
|
{
|
|
// get position and width of column c
|
|
int colp = columnPos(c);
|
|
if (colp==-1)
|
|
continue; //invisible column?
|
|
int colw = columnWidth(c);
|
|
int translx = colp-cx;
|
|
|
|
// Translate painter and draw the cell
|
|
pb->saveWorldMatrix();
|
|
pb->translate(translx, transly);
|
|
paintCell( pb, item, c, r, TQRect(colp, rowp, colw, d->rowHeight));
|
|
pb->restoreWorldMatrix();
|
|
}
|
|
|
|
if (m_dragIndicatorLine>=0) {
|
|
int y_line = -1;
|
|
if (r==(rows()-1) && m_dragIndicatorLine==rows()) {
|
|
y_line = transly+d->rowHeight-3; //draw at last line
|
|
}
|
|
if (m_dragIndicatorLine==r) {
|
|
y_line = transly+1;
|
|
}
|
|
if (y_line>=0) {
|
|
RasterOp op = pb->rasterOp();
|
|
pb->setRasterOp(XorROP);
|
|
pb->setPen( TQPen(TQt::white, 3) );
|
|
pb->drawLine(0, y_line, maxwc, y_line);
|
|
pb->setRasterOp(op);
|
|
}
|
|
}
|
|
}
|
|
|
|
void KexiTableView::drawContents( TQPainter *p, int cx, int cy, int cw, int ch)
|
|
{
|
|
if (d->disableDrawContents)
|
|
return;
|
|
int colfirst = columnAt(cx);
|
|
int rowfirst = rowAt(cy);
|
|
int collast = columnAt(cx + cw-1);
|
|
int rowlast = rowAt(cy + ch-1);
|
|
bool inserting = isInsertingEnabled();
|
|
bool plus1row = false; //true if we should show 'inserting' row at the end
|
|
bool paintOnlyInsertRow = false;
|
|
|
|
/* kdDebug(44021) << TQString(" KexiTableView::drawContents(cx:%1 cy:%2 cw:%3 ch:%4)")
|
|
.arg(cx).arg(cy).arg(cw).arg(ch) << endl;*/
|
|
|
|
if (rowlast == -1) {
|
|
rowlast = rows() - 1;
|
|
plus1row = inserting;
|
|
if (rowfirst == -1) {
|
|
if (rowAt(cy - d->rowHeight) != -1) {
|
|
paintOnlyInsertRow = true;
|
|
// kdDebug(44021) << "-- paintOnlyInsertRow --" << endl;
|
|
}
|
|
}
|
|
}
|
|
// kdDebug(44021) << "rowfirst="<<rowfirst<<" rowlast="<<rowlast<<" rows()="<<rows()<<endl;
|
|
// kdDebug(44021)<<" plus1row=" << plus1row<<endl;
|
|
|
|
if ( collast == -1 )
|
|
collast = columns() - 1;
|
|
|
|
if (colfirst>collast) {
|
|
int tmp = colfirst;
|
|
colfirst = collast;
|
|
collast = tmp;
|
|
}
|
|
if (rowfirst>rowlast) {
|
|
int tmp = rowfirst;
|
|
rowfirst = rowlast;
|
|
rowlast = tmp;
|
|
}
|
|
|
|
// tqDebug("cx:%3d cy:%3d w:%3d h:%3d col:%2d..%2d row:%2d..%2d tsize:%4d,%4d",
|
|
// cx, cy, cw, ch, colfirst, collast, rowfirst, rowlast, tableSize().width(), tableSize().height());
|
|
// triggerUpdate();
|
|
|
|
if (rowfirst == -1 || colfirst == -1) {
|
|
if (!paintOnlyInsertRow && !plus1row) {
|
|
paintEmptyArea(p, cx, cy, cw, ch);
|
|
return;
|
|
}
|
|
}
|
|
|
|
createBuffer(cw, ch);
|
|
if(d->pBufferPm->isNull())
|
|
return;
|
|
TQPainter *pb = new TQPainter(d->pBufferPm, this);
|
|
// pb->fillRect(0, 0, cw, ch, colorGroup().base());
|
|
|
|
// int maxwc = TQMIN(cw, (columnPos(d->numCols - 1) + columnWidth(d->numCols - 1)));
|
|
int maxwc = columnPos(columns() - 1) + columnWidth(columns() - 1);
|
|
// kdDebug(44021) << "KexiTableView::drawContents(): maxwc: " << maxwc << endl;
|
|
|
|
pb->fillRect(cx, cy, cw, ch, d->appearance.baseColor);
|
|
|
|
int rowp;
|
|
int r;
|
|
if (paintOnlyInsertRow) {
|
|
r = rows();
|
|
rowp = rowPos(r); // 'insert' row's position
|
|
}
|
|
else {
|
|
TQPtrListIterator<KexiTableItem> it = m_data->iterator();
|
|
it += rowfirst;//move to 1st row
|
|
rowp = rowPos(rowfirst); // row position
|
|
for (r = rowfirst;r <= rowlast; r++, ++it, rowp+=d->rowHeight) {
|
|
paintRow(it.current(), pb, r, rowp, cx, cy, colfirst, collast, maxwc);
|
|
}
|
|
}
|
|
|
|
if (plus1row) { //additional - 'insert' row
|
|
paintRow(m_insertItem, pb, r, rowp, cx, cy, colfirst, collast, maxwc);
|
|
}
|
|
|
|
delete pb;
|
|
|
|
p->drawPixmap(cx,cy,*d->pBufferPm, 0,0,cw,ch);
|
|
|
|
//(js)
|
|
paintEmptyArea(p, cx, cy, cw, ch);
|
|
}
|
|
|
|
bool KexiTableView::isDefaultValueDisplayed(KexiTableItem *item, int col, TQVariant* value)
|
|
{
|
|
const bool cursorAtInsertRowOrEditingNewRow = (item == m_insertItem || (m_newRowEditing && m_currentItem == item));
|
|
KexiTableViewColumn *tvcol;
|
|
if (cursorAtInsertRowOrEditingNewRow
|
|
&& (tvcol = m_data->column(col))
|
|
&& hasDefaultValueAt(*tvcol)
|
|
&& !tvcol->field()->isAutoIncrement())
|
|
{
|
|
if (value)
|
|
*value = tvcol->field()->defaultValue();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void KexiTableView::paintCell(TQPainter* p, KexiTableItem *item, int col, int row, const TQRect &cr, bool print)
|
|
{
|
|
p->save();
|
|
Q_UNUSED(print);
|
|
int w = cr.width();
|
|
int h = cr.height();
|
|
int x2 = w - 1;
|
|
int y2 = h - 1;
|
|
|
|
/* if (0==qstrcmp("KexiComboBoxPopup",parentWidget()->className())) {
|
|
kexidbg << parentWidget()->className() << " >>>>>> KexiTableView::paintCell(col=" << col <<"row="<<row<<") w="<<w<<endl;
|
|
}*/
|
|
|
|
// Draw our lines
|
|
TQPen pen(p->pen());
|
|
|
|
if (d->appearance.gridEnabled) {
|
|
p->setPen(d->appearance.borderColor);
|
|
p->drawLine( x2, 0, x2, y2 ); // right
|
|
p->drawLine( 0, y2, x2, y2 ); // bottom
|
|
}
|
|
p->setPen(pen);
|
|
|
|
if (m_editor && row == m_curRow && col == m_curCol //don't paint contents of edited cell
|
|
&& m_editor->hasFocusableWidget() //..if it's visible
|
|
) {
|
|
p->restore();
|
|
return;
|
|
}
|
|
|
|
KexiTableEdit *edit = tableEditorWidget( col, /*ignoreMissingEditor=*/true );
|
|
// if (!edit)
|
|
// return;
|
|
|
|
int x = edit ? edit->leftMargin() : 0;
|
|
int y_offset=0;
|
|
|
|
int align = TQt::SingleLine | TQt::AlignVCenter;
|
|
TQString txt; //text to draw
|
|
|
|
KexiTableViewColumn *tvcol = m_data->column(col);
|
|
|
|
TQVariant cellValue;
|
|
if (col < (int)item->count()) {
|
|
if (m_currentItem == item) {
|
|
if (m_editor && row == m_curRow && col == m_curCol
|
|
&& !m_editor->hasFocusableWidget())
|
|
{
|
|
//we're over editing cell and the editor has no widget
|
|
// - we're displaying internal values, not buffered
|
|
// bool ok;
|
|
cellValue = m_editor->value();
|
|
}
|
|
else {
|
|
//we're displaying values from edit buffer, if available
|
|
// this assignment will also get default value if there's no actual value set
|
|
cellValue = *bufferedValueAt(col);
|
|
}
|
|
}
|
|
else {
|
|
cellValue = item->at(col);
|
|
}
|
|
}
|
|
|
|
bool defaultValueDisplayed = isDefaultValueDisplayed(item, col);
|
|
|
|
if ((item == m_insertItem /*|| m_newRowEditing*/) && cellValue.isNull()) {
|
|
if (!tvcol->field()->isAutoIncrement() && !tvcol->field()->defaultValue().isNull()) {
|
|
//display default value in the "insert row", if available
|
|
//(but not if there is autoincrement flag set)
|
|
cellValue = tvcol->field()->defaultValue();
|
|
defaultValueDisplayed = true;
|
|
}
|
|
}
|
|
|
|
const bool columnReadOnly = tvcol->isReadOnly();
|
|
const bool dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
|
|
= d->appearance.rowHighlightingEnabled && !d->appearance.persistentSelections
|
|
&& m_curRow /*d->highlightedRow*/ >= 0 && row != m_curRow; //d->highlightedRow;
|
|
|
|
// setup default pen
|
|
TQPen defaultPen;
|
|
const bool usesSelectedTextColor = edit && edit->usesSelectedTextColor();
|
|
if (defaultValueDisplayed) {
|
|
if (col == m_curCol && row == m_curRow && usesSelectedTextColor)
|
|
defaultPen = TQPen(d->defaultValueDisplayParameters.selectedTextColor);
|
|
else
|
|
defaultPen = TQPen(d->defaultValueDisplayParameters.textColor);
|
|
}
|
|
else if (d->appearance.fullRowSelection
|
|
&& (row == d->highlightedRow || (row == m_curRow && d->highlightedRow==-1))
|
|
&& usesSelectedTextColor )
|
|
{
|
|
defaultPen = TQPen(d->appearance.rowHighlightingTextColor); //special case: highlighted row
|
|
}
|
|
else if (d->appearance.fullRowSelection && row == m_curRow && usesSelectedTextColor)
|
|
{
|
|
defaultPen = TQPen(d->appearance.textColor); //special case for full row selection
|
|
}
|
|
else if (m_currentItem == item && col == m_curCol && !columnReadOnly
|
|
&& !dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
|
|
&& usesSelectedTextColor)
|
|
{
|
|
defaultPen = TQPen(colorGroup().highlightedText()); //selected text
|
|
}
|
|
else if (d->appearance.rowHighlightingEnabled && row == m_curRow
|
|
&& !dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
|
|
&& usesSelectedTextColor)
|
|
{
|
|
defaultPen = TQPen(d->appearance.rowHighlightingTextColor);
|
|
}
|
|
else if (d->appearance.rowMouseOverHighlightingEnabled && row == d->highlightedRow
|
|
&& !dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
|
|
&& usesSelectedTextColor)
|
|
{
|
|
defaultPen = TQPen(d->appearance.rowMouseOverHighlightingTextColor);
|
|
}
|
|
else
|
|
defaultPen = TQPen(d->appearance.textColor);
|
|
|
|
if (edit) {
|
|
if (defaultValueDisplayed)
|
|
p->setFont( d->defaultValueDisplayParameters.font );
|
|
p->setPen( defaultPen );
|
|
|
|
//get visible lookup value if available
|
|
getVisibleLookupValue(cellValue, edit, item, tvcol);
|
|
|
|
edit->setupContents( p, m_currentItem == item && col == m_curCol,
|
|
cellValue, txt, align, x, y_offset, w, h );
|
|
}
|
|
if (!d->appearance.gridEnabled)
|
|
y_offset++; //correction because we're not drawing cell borders
|
|
|
|
if (d->appearance.fullRowSelection && d->appearance.fullRowSelection) {
|
|
// p->fillRect(x, y_offset, x+w-1, y_offset+h-1, red);
|
|
}
|
|
if (m_currentItem == item && (col == m_curCol || d->appearance.fullRowSelection)) {
|
|
if (edit && ((d->appearance.rowHighlightingEnabled && !d->appearance.fullRowSelection) || (row == m_curRow && d->highlightedRow==-1 && d->appearance.fullRowSelection))) //!dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted)
|
|
edit->paintSelectionBackground( p, isEnabled(), txt, align, x, y_offset, w, h,
|
|
isEnabled() ? colorGroup().highlight() : TQColor(200,200,200),//d->grayColor,
|
|
p->fontMetrics(), columnReadOnly, d->appearance.fullRowSelection );
|
|
}
|
|
|
|
if (!edit) {
|
|
p->fillRect(0, 0, x2, y2, d->diagonalGrayPattern);
|
|
}
|
|
|
|
// If we are in the focus cell, draw indication
|
|
if(m_currentItem == item && col == m_curCol //js: && !d->recordIndicator)
|
|
&& !d->appearance.fullRowSelection)
|
|
{
|
|
// kexidbg << ">>> CURRENT CELL ("<<m_curCol<<"," << m_curRow<<") focus="<<has_focus<<endl;
|
|
// if (has_focus) {
|
|
if (isEnabled()) {
|
|
p->setPen(d->appearance.textColor);
|
|
}
|
|
else {
|
|
TQPen gray_pen(p->pen());
|
|
gray_pen.setColor(d->appearance.borderColor);
|
|
p->setPen(gray_pen);
|
|
}
|
|
if (edit)
|
|
edit->paintFocusBorders( p, cellValue, 0, 0, x2, y2 );
|
|
else
|
|
p->drawRect(0, 0, x2, y2);
|
|
}
|
|
|
|
/// bool autonumber = false;
|
|
if ((!m_newRowEditing && item == m_insertItem)
|
|
|| (m_newRowEditing && item == m_currentItem && cellValue.isNull())) {
|
|
//we're in "insert row"
|
|
if (tvcol->field()->isAutoIncrement()) {
|
|
//"autonumber" column
|
|
// txt = i18n("(autonumber)");
|
|
// autonumber = true;
|
|
// if (autonumber) {
|
|
KexiDisplayUtils::paintAutonumberSign(d->autonumberSignDisplayParameters, p,
|
|
x, y_offset, w - x - x - ((align & TQt::AlignLeft)?2:0), h, align);
|
|
// }
|
|
}
|
|
}
|
|
|
|
// draw text
|
|
if (!txt.isEmpty()) {
|
|
if (defaultValueDisplayed)
|
|
p->setFont( d->defaultValueDisplayParameters.font );
|
|
p->setPen( defaultPen );
|
|
p->drawText(x, y_offset, w - (x + x)- ((align & TQt::AlignLeft)?2:0)/*right space*/, h,
|
|
align, txt);
|
|
}
|
|
p->restore();
|
|
}
|
|
|
|
TQPoint KexiTableView::contentsToViewport2( const TQPoint &p )
|
|
{
|
|
return TQPoint( p.x() - contentsX(), p.y() - contentsY() );
|
|
}
|
|
|
|
void KexiTableView::contentsToViewport2( int x, int y, int& vx, int& vy )
|
|
{
|
|
const TQPoint v = contentsToViewport2( TQPoint( x, y ) );
|
|
vx = v.x();
|
|
vy = v.y();
|
|
}
|
|
|
|
TQPoint KexiTableView::viewportToContents2( const TQPoint& vp )
|
|
{
|
|
return TQPoint( vp.x() + contentsX(),
|
|
vp.y() + contentsY() );
|
|
}
|
|
|
|
void KexiTableView::paintEmptyArea( TQPainter *p, int cx, int cy, int cw, int ch )
|
|
{
|
|
// tqDebug("%s: paintEmptyArea(x:%d y:%d w:%d h:%d)", (const char*)parentWidget()->caption(),cx,cy,cw,ch);
|
|
|
|
// Regions work with shorts, so avoid an overflow and adjust the
|
|
// table size to the visible size
|
|
TQSize ts( tableSize() );
|
|
// ts.setWidth( TQMIN( ts.width(), visibleWidth() ) );
|
|
// ts.setHeight( TQMIN( ts.height() - (m_navPanel ? m_navPanel->height() : 0), visibleHeight()) );
|
|
/* kdDebug(44021) << TQString(" (cx:%1 cy:%2 cw:%3 ch:%4)")
|
|
.arg(cx).arg(cy).arg(cw).arg(ch) << endl;
|
|
kdDebug(44021) << TQString(" (w:%3 h:%4)")
|
|
.arg(ts.width()).arg(ts.height()) << endl;*/
|
|
|
|
// Region of the rect we should draw, calculated in viewport
|
|
// coordinates, as a region can't handle bigger coordinates
|
|
contentsToViewport2( cx, cy, cx, cy );
|
|
TQRegion reg( TQRect( cx, cy, cw, ch ) );
|
|
|
|
//kexidbg << "---cy-- " << contentsY() << endl;
|
|
|
|
// Subtract the table from it
|
|
// reg = reg.subtract( TQRect( TQPoint( 0, 0 ), ts-TQSize(0,m_navPanel->isVisible() ? m_navPanel->height() : 0) ) );
|
|
reg = reg.subtract( TQRect( TQPoint( 0, 0 ), ts
|
|
-TQSize(0,TQMAX((m_navPanel ? m_navPanel->height() : 0), horizontalScrollBar()->sizeHint().height())
|
|
- (horizontalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height()/2 : 0)
|
|
+ (horizontalScrollBar()->isVisible() ? 0 :
|
|
d->internal_bottomMargin
|
|
// horizontalScrollBar()->sizeHint().height()/2
|
|
)
|
|
//- /*d->bottomMargin */ horizontalScrollBar()->sizeHint().height()*3/2
|
|
+ contentsY()
|
|
// - (verticalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height()/2 : 0)
|
|
)
|
|
) );
|
|
// reg = reg.subtract( TQRect( TQPoint( 0, 0 ), ts ) );
|
|
|
|
// And draw the rectangles (transformed inc contents coordinates as needed)
|
|
TQMemArray<TQRect> r = reg.rects();
|
|
for ( int i = 0; i < (int)r.count(); i++ ) {
|
|
TQRect rect( viewportToContents2(r[i].topLeft()), r[i].size() );
|
|
/* kdDebug(44021) << TQString("- pEA: p->fillRect(x:%1 y:%2 w:%3 h:%4)")
|
|
.arg(rect.x()).arg(rect.y())
|
|
.arg(rect.width()).arg(rect.height()) << endl;*/
|
|
// p->fillRect( TQRect(viewportToContents2(r[i].topLeft()),r[i].size()), d->emptyAreaColor );
|
|
p->fillRect( rect, d->appearance.emptyAreaColor );
|
|
// p->fillRect( TQRect(viewportToContents2(r[i].topLeft()),r[i].size()), viewport()->backgroundBrush() );
|
|
}
|
|
}
|
|
|
|
void KexiTableView::contentsMouseDoubleClickEvent(TQMouseEvent *e)
|
|
{
|
|
// kdDebug(44021) << "KexiTableView::contentsMouseDoubleClickEvent()" << endl;
|
|
m_contentsMousePressEvent_dblClick = true;
|
|
contentsMousePressEvent(e);
|
|
m_contentsMousePressEvent_dblClick = false;
|
|
|
|
if(m_currentItem)
|
|
{
|
|
if(d->editOnDoubleClick && columnEditable(m_curCol) && columnType(m_curCol) != KexiDB::Field::Boolean) {
|
|
KexiTableEdit *edit = tableEditorWidget( m_curCol, /*ignoreMissingEditor=*/true );
|
|
if (edit && edit->handleDoubleClick()) {
|
|
//nothing to do: editors like BLOB editor has custom handling of double clicking
|
|
}
|
|
else {
|
|
startEditCurrentCell();
|
|
// createEditor(m_curRow, m_curCol, TQString());
|
|
}
|
|
}
|
|
|
|
emit itemDblClicked(m_currentItem, m_curRow, m_curCol);
|
|
}
|
|
}
|
|
|
|
void KexiTableView::contentsMousePressEvent( TQMouseEvent* e )
|
|
{
|
|
// kdDebug(44021) << "KexiTableView::contentsMousePressEvent() ??" << endl;
|
|
setFocus();
|
|
if(m_data->count()==0 && !isInsertingEnabled()) {
|
|
TQScrollView::contentsMousePressEvent( e );
|
|
return;
|
|
}
|
|
|
|
if (columnAt(e->pos().x())==-1) { //outside a colums
|
|
TQScrollView::contentsMousePressEvent( e );
|
|
return;
|
|
}
|
|
// d->contentsMousePressEvent_ev = *e;
|
|
// d->contentsMousePressEvent_enabled = true;
|
|
// TQTimer::singleShot(2000, this, TQT_SLOT( contentsMousePressEvent_Internal() ));
|
|
// d->contentsMousePressEvent_timer.start(100,true);
|
|
|
|
// if (!d->contentsMousePressEvent_enabled)
|
|
// return;
|
|
// d->contentsMousePressEvent_enabled=false;
|
|
|
|
if (!d->moveCursorOnMouseRelease) {
|
|
if (!handleContentsMousePressOrRelease(e, false))
|
|
return;
|
|
}
|
|
|
|
// kdDebug(44021)<<"void KexiTableView::contentsMousePressEvent( TQMouseEvent* e ) by now the current items should be set, if not -> error + crash"<<endl;
|
|
if(e->button() == Qt::RightButton)
|
|
{
|
|
showContextMenu(e->globalPos());
|
|
}
|
|
else if(e->button() == Qt::LeftButton)
|
|
{
|
|
if(columnType(m_curCol) == KexiDB::Field::Boolean && columnEditable(m_curCol))
|
|
{
|
|
//only accept clicking on the [x] rect (copied from KexiBoolTableEdit::setupContents())
|
|
int s = TQMAX(d->rowHeight - 5, 12);
|
|
s = TQMIN( d->rowHeight-3, s );
|
|
s = TQMIN( columnWidth(m_curCol)-3, s ); //avoid too large box
|
|
const TQRect r( columnPos(m_curCol) + TQMAX( columnWidth(m_curCol)/2 - s/2, 0 ), rowPos(m_curRow) +d->rowHeight/2 - s/2 /*- 1*/, s, s);
|
|
//kexidbg << r << endl;
|
|
if (r.contains(e->pos())) {
|
|
// kexidbg << "e->x:" << e->x() << " e->y:" << e->y() << " " << rowPos(m_curRow) <<
|
|
// " " << columnPos(m_curCol) << endl;
|
|
boolToggled();
|
|
}
|
|
}
|
|
#if 0 //js: TODO
|
|
else if(columnType(m_curCol) == TQVariant::StringList && columnEditable(m_curCol))
|
|
{
|
|
createEditor(m_curRow, m_curCol);
|
|
}
|
|
#endif
|
|
}
|
|
//ScrollView::contentsMousePressEvent( e );
|
|
}
|
|
|
|
void KexiTableView::contentsMouseReleaseEvent( TQMouseEvent* e )
|
|
{
|
|
// kdDebug(44021) << "KexiTableView::contentsMousePressEvent() ??" << endl;
|
|
if(m_data->count()==0 && !isInsertingEnabled())
|
|
return;
|
|
|
|
if (d->moveCursorOnMouseRelease)
|
|
handleContentsMousePressOrRelease(e, true);
|
|
|
|
int col = columnAt(e->pos().x());
|
|
int row = rowAt(e->pos().y());
|
|
|
|
if (!m_currentItem || col==-1 || row==-1 || col!=m_curCol || row!=m_curRow)//outside a current cell
|
|
return;
|
|
|
|
TQScrollView::contentsMouseReleaseEvent( e );
|
|
|
|
emit itemMouseReleased(m_currentItem, m_curRow, m_curCol);
|
|
}
|
|
|
|
bool KexiTableView::handleContentsMousePressOrRelease(TQMouseEvent* e, bool release)
|
|
{
|
|
// remember old focus cell
|
|
int oldRow = m_curRow;
|
|
int oldCol = m_curCol;
|
|
kdDebug(44021) << "oldRow=" << oldRow <<" oldCol=" << oldCol <<endl;
|
|
bool onInsertItem = false;
|
|
|
|
int newrow, newcol;
|
|
//compute clicked row nr
|
|
if (isInsertingEnabled()) {
|
|
if (rowAt(e->pos().y())==-1) {
|
|
newrow = rowAt(e->pos().y() - d->rowHeight);
|
|
if (newrow==-1 && m_data->count()>0) {
|
|
if (release)
|
|
TQScrollView::contentsMouseReleaseEvent( e );
|
|
else
|
|
TQScrollView::contentsMousePressEvent( e );
|
|
return false;
|
|
}
|
|
newrow++;
|
|
kdDebug(44021) << "Clicked just on 'insert' row." << endl;
|
|
onInsertItem=true;
|
|
}
|
|
else {
|
|
// get new focus cell
|
|
newrow = rowAt(e->pos().y());
|
|
}
|
|
}
|
|
else {
|
|
if (rowAt(e->pos().y())==-1 || columnAt(e->pos().x())==-1) {
|
|
if (release)
|
|
TQScrollView::contentsMouseReleaseEvent( e );
|
|
else
|
|
TQScrollView::contentsMousePressEvent( e );
|
|
return false; //clicked outside a grid
|
|
}
|
|
// get new focus cell
|
|
newrow = rowAt(e->pos().y());
|
|
}
|
|
newcol = columnAt(e->pos().x());
|
|
|
|
if(e->button() != Qt::NoButton) {
|
|
setCursorPosition(newrow,newcol);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void KexiTableView::showContextMenu(const TQPoint& _pos)
|
|
{
|
|
if (!d->contextMenuEnabled || m_popupMenu->count()<1)
|
|
return;
|
|
TQPoint pos(_pos);
|
|
if (pos==TQPoint(-1,-1)) {
|
|
pos = viewport()->mapToGlobal( TQPoint( columnPos(m_curCol), rowPos(m_curRow) + d->rowHeight ) );
|
|
}
|
|
//show own context menu if configured
|
|
// if (updateContextMenu()) {
|
|
selectRow(m_curRow);
|
|
m_popupMenu->exec(pos);
|
|
/* }
|
|
else {
|
|
//request other context menu
|
|
emit contextMenuRequested(m_currentItem, m_curCol, pos);
|
|
}*/
|
|
}
|
|
|
|
void KexiTableView::contentsMouseMoveEvent( TQMouseEvent *e )
|
|
{
|
|
int row;
|
|
const int col = columnAt(e->x());
|
|
if (col < 0) {
|
|
row = -1;
|
|
} else {
|
|
row = rowAt( e->y(), true /*ignoreEnd*/ );
|
|
if (row > (rows() - 1 + (isInsertingEnabled()?1:0)))
|
|
row = -1; //no row to paint
|
|
}
|
|
// kexidbg << " row="<<row<< " col="<<col<<endl;
|
|
//update row highlight if needed
|
|
if (d->appearance.rowMouseOverHighlightingEnabled) {
|
|
if (row != d->highlightedRow) {
|
|
const int oldRow = d->highlightedRow;
|
|
d->highlightedRow = row;
|
|
updateRow(oldRow);
|
|
updateRow(d->highlightedRow);
|
|
//currently selected (not necessary highlighted) row needs to be repainted
|
|
updateRow(m_curRow);
|
|
m_verticalHeader->setHighlightedRow(d->highlightedRow);
|
|
}
|
|
}
|
|
|
|
#if 0//(js) doesn't work!
|
|
|
|
// do the same as in mouse press
|
|
int x,y;
|
|
contentsToViewport(e->x(), e->y(), x, y);
|
|
|
|
if(y > visibleHeight())
|
|
{
|
|
d->needAutoScroll = true;
|
|
d->scrollTimer->start(70, false);
|
|
d->scrollDirection = ScrollDown;
|
|
}
|
|
else if(y < 0)
|
|
{
|
|
d->needAutoScroll = true;
|
|
d->scrollTimer->start(70, false);
|
|
d->scrollDirection = ScrollUp;
|
|
}
|
|
else if(x > visibleWidth())
|
|
{
|
|
d->needAutoScroll = true;
|
|
d->scrollTimer->start(70, false);
|
|
d->scrollDirection = ScrollRight;
|
|
}
|
|
else if(x < 0)
|
|
{
|
|
d->needAutoScroll = true;
|
|
d->scrollTimer->start(70, false);
|
|
d->scrollDirection = ScrollLeft;
|
|
}
|
|
else
|
|
{
|
|
d->needAutoScroll = false;
|
|
d->scrollTimer->stop();
|
|
contentsMousePressEvent(e);
|
|
}
|
|
#endif
|
|
TQScrollView::contentsMouseMoveEvent(e);
|
|
}
|
|
|
|
#if 0//(js) doesn't work!
|
|
void KexiTableView::contentsMouseReleaseEvent(TQMouseEvent *)
|
|
{
|
|
if(d->needAutoScroll)
|
|
{
|
|
d->scrollTimer->stop();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static bool overrideEditorShortcutNeeded(TQKeyEvent *e)
|
|
{
|
|
//perhaps more to come...
|
|
return e->key() == TQt::Key_Delete && e->state()==TQt::ControlButton;
|
|
}
|
|
|
|
bool KexiTableView::shortCutPressed( TQKeyEvent *e, const TQCString &action_name )
|
|
{
|
|
const int k = e->key();
|
|
TDEAction *action = m_sharedActions[action_name];
|
|
if (action) {
|
|
if (!action->isEnabled())//this action is disabled - don't process it!
|
|
return false;
|
|
if (action->shortcut() == TDEShortcut( KKey(e) )) {
|
|
//special cases when we need to override editor's shortcut
|
|
if (overrideEditorShortcutNeeded(e)) {
|
|
return true;
|
|
}
|
|
return false;//this shortcut is owned by shared action - don't process it!
|
|
}
|
|
}
|
|
|
|
//check default shortcut (when user app has no action shortcuts defined
|
|
// but we want these shortcuts to still work)
|
|
if (action_name=="data_save_row")
|
|
return (k == TQt::Key_Return || k == TQt::Key_Enter) && e->state()==TQt::ShiftButton;
|
|
if (action_name=="edit_delete_row")
|
|
return k == TQt::Key_Delete && e->state()==TQt::ControlButton;
|
|
if (action_name=="edit_delete")
|
|
return k == TQt::Key_Delete && e->state()==Qt::NoButton;
|
|
if (action_name=="edit_edititem")
|
|
return k == TQt::Key_F2 && e->state()==Qt::NoButton;
|
|
if (action_name=="edit_insert_empty_row")
|
|
return k == TQt::Key_Insert && e->state()==(TQt::ShiftButton | TQt::ControlButton);
|
|
|
|
return false;
|
|
}
|
|
|
|
void KexiTableView::keyPressEvent(TQKeyEvent* e)
|
|
{
|
|
if (!hasData())
|
|
return;
|
|
// kexidbg << "KexiTableView::keyPressEvent: key=" <<e->key() << " txt=" <<e->text()<<endl;
|
|
|
|
const int k = e->key();
|
|
const bool ro = isReadOnly();
|
|
TQWidget *w = focusWidget();
|
|
// if (!w || w!=viewport() && w!=this && (!m_editor || w!=m_editor->view() && w!=m_editor)) {
|
|
// if (!w || w!=viewport() && w!=this && (!m_editor || w!=m_editor->view())) {
|
|
if (!w || TQT_BASE_OBJECT(w)!=TQT_BASE_OBJECT(viewport()) && TQT_TQOBJECT(w)!=TQT_TQOBJECT(this) && (!m_editor || !KexiUtils::hasParent(TQT_TQOBJECT(dynamic_cast<TQT_BASE_OBJECT_NAME*>(m_editor)), TQT_TQOBJECT(w)))) {
|
|
//don't process stranger's events
|
|
e->ignore();
|
|
return;
|
|
}
|
|
if (d->skipKeyPress) {
|
|
d->skipKeyPress=false;
|
|
e->ignore();
|
|
return;
|
|
}
|
|
|
|
if(m_currentItem == 0 && (m_data->count() > 0 || isInsertingEnabled()))
|
|
{
|
|
setCursorPosition(0,0);
|
|
}
|
|
else if(m_data->count() == 0 && !isInsertingEnabled())
|
|
{
|
|
e->accept();
|
|
return;
|
|
}
|
|
|
|
if(m_editor) {// if a cell is edited, do some special stuff
|
|
if (k == TQt::Key_Escape) {
|
|
cancelEditor();
|
|
e->accept();
|
|
return;
|
|
} else if (k == TQt::Key_Return || k == TQt::Key_Enter) {
|
|
if (columnType(m_curCol) == KexiDB::Field::Boolean) {
|
|
boolToggled();
|
|
}
|
|
else {
|
|
acceptEditor();
|
|
}
|
|
e->accept();
|
|
return;
|
|
}
|
|
}
|
|
else if (m_rowEditing) {// if a row is in edit mode, do some special stuff
|
|
if (shortCutPressed( e, "data_save_row")) {
|
|
kexidbg << "shortCutPressed!!!" <<endl;
|
|
acceptRowEdit();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(k == TQt::Key_Return || k == TQt::Key_Enter)
|
|
{
|
|
emit itemReturnPressed(m_currentItem, m_curRow, m_curCol);
|
|
}
|
|
|
|
int curRow = m_curRow;
|
|
int curCol = m_curCol;
|
|
|
|
const bool nobtn = e->state()==Qt::NoButton;
|
|
bool printable = false;
|
|
|
|
//check shared shortcuts
|
|
if (!ro) {
|
|
if (shortCutPressed(e, "edit_delete_row")) {
|
|
deleteCurrentRow();
|
|
e->accept();
|
|
return;
|
|
} else if (shortCutPressed(e, "edit_delete")) {
|
|
deleteAndStartEditCurrentCell();
|
|
e->accept();
|
|
return;
|
|
}
|
|
else if (shortCutPressed(e, "edit_insert_empty_row")) {
|
|
insertEmptyRow();
|
|
e->accept();
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* case TQt::Key_Delete:
|
|
if (e->state()==TQt::ControlButton) {//remove current row
|
|
deleteCurrentRow();
|
|
}
|
|
else if (nobtn) {//remove contents of the current cell
|
|
deleteAndStartEditCurrentCell();
|
|
}
|
|
break;*/
|
|
|
|
// bool _return;
|
|
if (k == TQt::Key_Shift || k == TQt::Key_Alt || k == TQt::Key_Control || k == TQt::Key_Meta) {
|
|
e->ignore();
|
|
}
|
|
else if (KexiDataAwareObjectInterface::handleKeyPress(e, curRow, curCol, d->appearance.fullRowSelection)) {
|
|
if (e->isAccepted())
|
|
return;
|
|
}
|
|
else if (k == TQt::Key_Backspace && nobtn) {
|
|
if (!ro && columnType(curCol) != KexiDB::Field::Boolean && columnEditable(curCol))
|
|
createEditor(curRow, curCol, TQString(), true);
|
|
}
|
|
else if (k == TQt::Key_Space) {
|
|
if (nobtn && !ro && columnEditable(curCol)) {
|
|
if (columnType(curCol) == KexiDB::Field::Boolean) {
|
|
boolToggled();
|
|
}
|
|
else
|
|
printable = true; //just space key
|
|
}
|
|
}
|
|
else if (k == TQt::Key_Escape) {
|
|
if (nobtn && m_rowEditing) {
|
|
cancelRowEdit();
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
//others:
|
|
if (nobtn && (k==TQt::Key_Tab || k==TQt::Key_Right)) {
|
|
//! \todo add option for stopping at 1st column for TQt::Key_left
|
|
//tab
|
|
if (acceptEditor()) {
|
|
if (curCol == (columns() - 1)) {
|
|
if (curRow < (rows()-1+(isInsertingEnabled()?1:0))) {//skip to next row
|
|
curRow++;
|
|
curCol = 0;
|
|
}
|
|
}
|
|
else
|
|
curCol++;
|
|
}
|
|
}
|
|
else if ((e->state()==TQt::ShiftButton && k==TQt::Key_Tab)
|
|
|| (nobtn && k==TQt::Key_Backtab)
|
|
|| (e->state()==TQt::ShiftButton && k==TQt::Key_Backtab)
|
|
|| (nobtn && k==TQt::Key_Left)
|
|
) {
|
|
//! \todo add option for stopping at last column
|
|
//backward tab
|
|
if (acceptEditor()) {
|
|
if (curCol == 0) {
|
|
if (curRow>0) {//skip to previous row
|
|
curRow--;
|
|
curCol = columns() - 1;
|
|
}
|
|
}
|
|
else
|
|
curCol--;
|
|
}
|
|
}
|
|
else if (nobtn && k==d->contextMenuKey) { //TQt::Key_Menu:
|
|
showContextMenu();
|
|
}
|
|
else {
|
|
KexiTableEdit *edit = tableEditorWidget( m_curCol );
|
|
if (edit && edit->handleKeyPress(e, m_editor==edit)) {
|
|
//try to handle the event @ editor's level
|
|
e->accept();
|
|
return;
|
|
}
|
|
else if ( nobtn && (k==TQt::Key_Enter || k==TQt::Key_Return || shortCutPressed(e, "edit_edititem")) ) {
|
|
//this condition is moved after handleKeyPress() to allow to everride enter key as well
|
|
startEditOrToggleValue();
|
|
}
|
|
else {
|
|
kexidbg << "KexiTableView::KeyPressEvent(): default" << endl;
|
|
if (e->text().isEmpty() || !e->text().isEmpty() && !e->text()[0].isPrint() ) {
|
|
kdDebug(44021) << "NOT PRINTABLE: 0x0" << TQString("%1").arg(k,0,16) <<endl;
|
|
// e->ignore();
|
|
TQScrollView::keyPressEvent(e);
|
|
return;
|
|
}
|
|
printable = true;
|
|
}
|
|
}
|
|
}
|
|
//finally: we've printable char:
|
|
if (printable && !ro) {
|
|
KexiTableViewColumn *tvcol = m_data->column(curCol);
|
|
if (tvcol->acceptsFirstChar(TQString(e->text())[0])) {
|
|
kdDebug(44021) << "KexiTableView::KeyPressEvent(): ev pressed: acceptsFirstChar()==true" << endl;
|
|
// if (e->text()[0].isPrint())
|
|
createEditor(curRow, curCol, e->text(), true);
|
|
}
|
|
else {
|
|
//TODO show message "key not allowed eg. on a statusbar"
|
|
kdDebug(44021) << "KexiTableView::KeyPressEvent(): ev pressed: acceptsFirstChar()==false" << endl;
|
|
}
|
|
}
|
|
|
|
m_vScrollBarValueChanged_enabled=false;
|
|
|
|
// if focus cell changes, repaint
|
|
setCursorPosition(curRow, curCol);
|
|
|
|
m_vScrollBarValueChanged_enabled=true;
|
|
|
|
e->accept();
|
|
}
|
|
|
|
void KexiTableView::emitSelected()
|
|
{
|
|
if(m_currentItem)
|
|
emit itemSelected(m_currentItem);
|
|
}
|
|
|
|
int KexiTableView::rowsPerPage() const
|
|
{
|
|
return visibleHeight() / d->rowHeight;
|
|
}
|
|
|
|
KexiDataItemInterface *KexiTableView::editor( int col, bool ignoreMissingEditor )
|
|
{
|
|
if (!m_data || col<0 || col>=columns())
|
|
return 0;
|
|
KexiTableViewColumn *tvcol = m_data->column(col);
|
|
// int t = tvcol->field->type();
|
|
|
|
//find the editor for this column
|
|
KexiTableEdit *editor = d->editors[ tvcol ];
|
|
if (editor)
|
|
return editor;
|
|
|
|
//not found: create
|
|
// editor = KexiCellEditorFactory::createEditor(*m_data->column(col)->field, this);
|
|
editor = KexiCellEditorFactory::createEditor(*tvcol, this);
|
|
if (!editor) {//create error!
|
|
if (!ignoreMissingEditor) {
|
|
//js TODO: show error???
|
|
cancelRowEdit();
|
|
}
|
|
return 0;
|
|
}
|
|
editor->hide();
|
|
if (m_data->cursor() && m_data->cursor()->query())
|
|
editor->createInternalEditor(*m_data->cursor()->query());
|
|
|
|
connect(editor,TQT_SIGNAL(editRequested()),this,TQT_SLOT(slotEditRequested()));
|
|
connect(editor,TQT_SIGNAL(cancelRequested()),this,TQT_SLOT(cancelEditor()));
|
|
connect(editor,TQT_SIGNAL(acceptRequested()),this,TQT_SLOT(acceptEditor()));
|
|
|
|
editor->resize(columnWidth(col)-1, rowHeight()-1);
|
|
editor->installEventFilter(this);
|
|
if (editor->widget())
|
|
editor->widget()->installEventFilter(this);
|
|
//store
|
|
d->editors.insert( tvcol, editor );
|
|
return editor;
|
|
}
|
|
|
|
void KexiTableView::editorShowFocus( int /*row*/, int col )
|
|
{
|
|
KexiDataItemInterface *edit = editor( col );
|
|
/*nt p = rowPos(row);
|
|
(!edit || (p < contentsY()) || (p > (contentsY()+clipper()->height()))) {
|
|
kexidbg<< "KexiTableView::editorShowFocus() : OUT" << endl;
|
|
return;
|
|
}*/
|
|
if (edit) {
|
|
kexidbg<< "KexiTableView::editorShowFocus() : IN" << endl;
|
|
TQRect rect = cellGeometry( m_curRow, m_curCol );
|
|
// rect.moveBy( -contentsX(), -contentsY() );
|
|
edit->showFocus( rect, isReadOnly() || m_data->column(col)->isReadOnly() );
|
|
}
|
|
}
|
|
|
|
void KexiTableView::slotEditRequested()
|
|
{
|
|
createEditor(m_curRow, m_curCol);
|
|
}
|
|
|
|
void KexiTableView::reloadData() {
|
|
KexiDataAwareObjectInterface::reloadData();
|
|
updateContents();
|
|
}
|
|
|
|
void KexiTableView::createEditor(int row, int col, const TQString& addText, bool removeOld)
|
|
{
|
|
kdDebug(44021) << "KexiTableView::createEditor('"<<addText<<"',"<<removeOld<<")"<<endl;
|
|
if (isReadOnly()) {
|
|
kdDebug(44021) << "KexiTableView::createEditor(): DATA IS READ ONLY!"<<endl;
|
|
return;
|
|
}
|
|
|
|
if (m_data->column(col)->isReadOnly()) {//d->pColumnModes.at(d->numCols-1) & ColumnReadOnly)
|
|
kdDebug(44021) << "KexiTableView::createEditor(): COL IS READ ONLY!"<<endl;
|
|
return;
|
|
}
|
|
|
|
const bool startRowEdit = !m_rowEditing; //remember if we're starting row edit
|
|
|
|
if (!m_rowEditing) {
|
|
//we're starting row editing session
|
|
m_data->clearRowEditBuffer();
|
|
|
|
m_rowEditing = true;
|
|
//indicate on the vheader that we are editing:
|
|
m_verticalHeader->setEditRow(m_curRow);
|
|
if (isInsertingEnabled() && m_currentItem==m_insertItem) {
|
|
//we should know that we are in state "new row editing"
|
|
m_newRowEditing = true;
|
|
//'insert' row editing: show another row after that:
|
|
m_data->append( m_insertItem );
|
|
//new empty 'inserting' item
|
|
m_insertItem = m_data->createItem();
|
|
m_verticalHeader->addLabel();
|
|
m_verticalHeaderAlreadyAdded = true;
|
|
updateWidgetContentsSize();
|
|
//refr. current and next row
|
|
updateContents(columnPos(0), rowPos(row), viewport()->width(), d->rowHeight*2);
|
|
// updateContents(columnPos(0), rowPos(row+1), viewport()->width(), d->rowHeight);
|
|
//js: warning this breaks behaviour (cursor is skipping, etc.): tqApp->processEvents(500);
|
|
ensureVisible(columnPos(m_curCol), rowPos(row+1)+d->rowHeight-1, columnWidth(m_curCol), d->rowHeight);
|
|
|
|
m_verticalHeader->setOffset(contentsY());
|
|
}
|
|
}
|
|
|
|
KexiTableEdit *editorWidget = tableEditorWidget( col );
|
|
m_editor = editorWidget;
|
|
if (!editorWidget)
|
|
return;
|
|
|
|
m_editor->setValue(*bufferedValueAt(col, !removeOld/*useDefaultValueIfPossible*/), addText, removeOld);
|
|
if (m_editor->hasFocusableWidget()) {
|
|
moveChild(editorWidget, columnPos(m_curCol), rowPos(m_curRow));
|
|
|
|
editorWidget->resize(columnWidth(m_curCol)-1, rowHeight()-1);
|
|
editorWidget->show();
|
|
|
|
m_editor->setFocus();
|
|
}
|
|
|
|
if (startRowEdit) {
|
|
m_navPanel->showEditingIndicator(true); //this will allow to enable 'next' btn
|
|
// m_navPanel->updateButtons(rows()); //refresh 'next' btn
|
|
emit rowEditStarted(m_curRow);
|
|
}
|
|
}
|
|
|
|
void KexiTableView::focusInEvent(TQFocusEvent* e)
|
|
{
|
|
Q_UNUSED(e);
|
|
updateCell(m_curRow, m_curCol);
|
|
}
|
|
|
|
void KexiTableView::focusOutEvent(TQFocusEvent* e)
|
|
{
|
|
KexiDataAwareObjectInterface::focusOutEvent(e);
|
|
}
|
|
|
|
bool KexiTableView::focusNextPrevChild(bool /*next*/)
|
|
{
|
|
return false; //special Tab/BackTab meaning
|
|
/* if (m_editor)
|
|
return true;
|
|
return TQScrollView::focusNextPrevChild(next);*/
|
|
}
|
|
|
|
void KexiTableView::resizeEvent(TQResizeEvent *e)
|
|
{
|
|
TQScrollView::resizeEvent(e);
|
|
//updateGeometries();
|
|
|
|
if (m_navPanel)
|
|
m_navPanel->updateGeometry(leftMargin());
|
|
// updateNavPanelGeometry();
|
|
|
|
if ((contentsHeight() - e->size().height()) <= d->rowHeight) {
|
|
slotUpdate();
|
|
triggerUpdate();
|
|
}
|
|
// d->pTopHeader->repaint();
|
|
|
|
|
|
/* m_navPanel->setGeometry(
|
|
frameWidth(),
|
|
viewport()->height() +d->pTopHeader->height()
|
|
-(horizontalScrollBar()->isVisible() ? 0 : horizontalScrollBar()->sizeHint().height())
|
|
+frameWidth(),
|
|
m_navPanel->sizeHint().width(), // - verticalScrollBar()->sizeHint().width() - horizontalScrollBar()->sizeHint().width(),
|
|
horizontalScrollBar()->sizeHint().height()
|
|
);*/
|
|
// updateContents();
|
|
// m_navPanel->setGeometry(1,horizontalScrollBar()->pos().y(),
|
|
// m_navPanel->width(), horizontalScrollBar()->height());
|
|
// updateContents(0,0,2000,2000);//js
|
|
// erase(); repaint();
|
|
}
|
|
|
|
void KexiTableView::viewportResizeEvent( TQResizeEvent *e )
|
|
{
|
|
TQScrollView::viewportResizeEvent( e );
|
|
updateGeometries();
|
|
// erase(); repaint();
|
|
}
|
|
|
|
void KexiTableView::showEvent(TQShowEvent *e)
|
|
{
|
|
TQScrollView::showEvent(e);
|
|
if (!d->maximizeColumnsWidthOnShow.isEmpty()) {
|
|
maximizeColumnsWidth(d->maximizeColumnsWidthOnShow);
|
|
d->maximizeColumnsWidthOnShow.clear();
|
|
}
|
|
|
|
if (m_initDataContentsOnShow) {
|
|
//full init
|
|
m_initDataContentsOnShow = false;
|
|
initDataContents();
|
|
}
|
|
else {
|
|
//just update size
|
|
TQSize s(tableSize());
|
|
// TQRect r(cellGeometry(rows() - 1 + (isInsertingEnabled()?1:0), columns() - 1 ));
|
|
// resizeContents(r.right() + 1, r.bottom() + 1);
|
|
resizeContents(s.width(),s.height());
|
|
}
|
|
updateGeometries();
|
|
|
|
//now we can ensure cell's visibility ( if there was such a call before show() )
|
|
if (d->ensureCellVisibleOnShow!=TQPoint(-1,-1)) {
|
|
ensureCellVisible( d->ensureCellVisibleOnShow.x(), d->ensureCellVisibleOnShow.y() );
|
|
d->ensureCellVisibleOnShow = TQPoint(-1,-1); //reset the flag
|
|
}
|
|
if (m_navPanel)
|
|
m_navPanel->updateGeometry(leftMargin());
|
|
// updateNavPanelGeometry();
|
|
}
|
|
|
|
void KexiTableView::contentsDragMoveEvent(TQDragMoveEvent *e)
|
|
{
|
|
if (!hasData())
|
|
return;
|
|
if (m_dropsAtRowEnabled) {
|
|
TQPoint p = e->pos();
|
|
int row = rowAt(p.y());
|
|
KexiTableItem *item = 0;
|
|
// if (row==(rows()-1) && (p.y() % d->rowHeight) > (d->rowHeight*2/3) ) {
|
|
if ((p.y() % d->rowHeight) > (d->rowHeight*2/3) ) {
|
|
row++;
|
|
}
|
|
item = m_data->at(row);
|
|
emit dragOverRow(item, row, e);
|
|
if (e->isAccepted()) {
|
|
if (m_dragIndicatorLine>=0 && m_dragIndicatorLine != row) {
|
|
//erase old indicator
|
|
updateRow(m_dragIndicatorLine);
|
|
}
|
|
if (m_dragIndicatorLine != row) {
|
|
m_dragIndicatorLine = row;
|
|
updateRow(m_dragIndicatorLine);
|
|
}
|
|
}
|
|
else {
|
|
if (m_dragIndicatorLine>=0) {
|
|
//erase old indicator
|
|
updateRow(m_dragIndicatorLine);
|
|
}
|
|
m_dragIndicatorLine = -1;
|
|
}
|
|
}
|
|
else
|
|
e->acceptAction(false);
|
|
/* TQStringList::ConstIterator it, end( d->dropFilters.constEnd() );
|
|
for( it = d->dropFilters.constBegin(); it != end; it++)
|
|
{
|
|
if(e->provides((*it).latin1()))
|
|
{
|
|
e->acceptAction(true);
|
|
return;
|
|
}
|
|
}*/
|
|
// e->acceptAction(false);
|
|
}
|
|
|
|
void KexiTableView::contentsDropEvent(TQDropEvent *e)
|
|
{
|
|
if (!hasData())
|
|
return;
|
|
if (m_dropsAtRowEnabled) {
|
|
//we're no longer dragging over the table
|
|
if (m_dragIndicatorLine>=0) {
|
|
int row2update = m_dragIndicatorLine;
|
|
m_dragIndicatorLine = -1;
|
|
updateRow(row2update);
|
|
}
|
|
TQPoint p = e->pos();
|
|
int row = rowAt(p.y());
|
|
if ((p.y() % d->rowHeight) > (d->rowHeight*2/3) ) {
|
|
row++;
|
|
}
|
|
KexiTableItem *item = m_data->at(row);
|
|
KexiTableItem *newItem = 0;
|
|
emit droppedAtRow(item, row, e, newItem);
|
|
if (newItem) {
|
|
const int realRow = (row==m_curRow ? -1 : row);
|
|
insertItem(newItem, realRow);
|
|
setCursorPosition(row, 0);
|
|
// m_currentItem = newItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KexiTableView::viewportDragLeaveEvent( TQDragLeaveEvent *e )
|
|
{
|
|
Q_UNUSED(e);
|
|
if (!hasData())
|
|
return;
|
|
if (m_dropsAtRowEnabled) {
|
|
//we're no longer dragging over the table
|
|
if (m_dragIndicatorLine>=0) {
|
|
int row2update = m_dragIndicatorLine;
|
|
m_dragIndicatorLine = -1;
|
|
updateRow(row2update);
|
|
}
|
|
}
|
|
}
|
|
|
|
void KexiTableView::updateCell(int row, int col)
|
|
{
|
|
// kdDebug(44021) << "updateCell("<<row<<", "<<col<<")"<<endl;
|
|
updateContents(cellGeometry(row, col));
|
|
/* TQRect r = cellGeometry(row, col);
|
|
r.setHeight(r.height()+6);
|
|
r.setTop(r.top()-3);
|
|
updateContents();*/
|
|
}
|
|
|
|
void KexiTableView::updateCurrentCell()
|
|
{
|
|
updateCell(m_curRow, m_curCol);
|
|
}
|
|
|
|
void KexiTableView::updateRow(int row)
|
|
{
|
|
// kdDebug(44021) << "updateRow("<<row<<")"<<endl;
|
|
if (row < 0 || row >= (rows() + 2/* sometimes we want to refresh the row after last*/ ))
|
|
return;
|
|
//int leftcol = d->pTopHeader->sectionAt( d->pTopHeader->offset() );
|
|
|
|
//kexidbg << contentsX() << " " << contentsY() << endl;
|
|
//kexidbg << TQRect( columnPos( leftcol ), rowPos(row), clipper()->width(), rowHeight() ) << endl;
|
|
// updateContents( TQRect( columnPos( leftcol ), rowPos(row), clipper()->width(), rowHeight() ) ); //columnPos(rightcol)+columnWidth(rightcol), rowHeight() ) );
|
|
updateContents( TQRect( contentsX(), rowPos(row), clipper()->width(), rowHeight() ) ); //columnPos(rightcol)+columnWidth(rightcol), rowHeight() ) );
|
|
}
|
|
|
|
void KexiTableView::slotColumnWidthChanged( int, int, int )
|
|
{
|
|
TQSize s(tableSize());
|
|
int w = contentsWidth();
|
|
viewport()->setUpdatesEnabled(false);
|
|
resizeContents( s.width(), s.height() );
|
|
viewport()->setUpdatesEnabled(true);
|
|
if (contentsWidth() < w) {
|
|
updateContents(contentsX(), 0, viewport()->width(), contentsHeight());
|
|
// repaintContents( s.width(), 0, w - s.width() + 1, contentsHeight(), true );
|
|
}
|
|
else {
|
|
// updateContents( columnPos(col), 0, contentsWidth(), contentsHeight() );
|
|
updateContents(contentsX(), 0, viewport()->width(), contentsHeight());
|
|
// viewport()->repaint();
|
|
}
|
|
|
|
// updateContents(0, 0, d->pBufferPm->width(), d->pBufferPm->height());
|
|
TQWidget *editorWidget = dynamic_cast<TQWidget*>(m_editor);
|
|
if (editorWidget)
|
|
{
|
|
editorWidget->resize(columnWidth(m_curCol)-1, rowHeight()-1);
|
|
moveChild(editorWidget, columnPos(m_curCol), rowPos(m_curRow));
|
|
}
|
|
updateGeometries();
|
|
updateScrollBars();
|
|
if (m_navPanel)
|
|
m_navPanel->updateGeometry(leftMargin());
|
|
// updateNavPanelGeometry();
|
|
}
|
|
|
|
void KexiTableView::slotSectionHandleDoubleClicked( int section )
|
|
{
|
|
adjustColumnWidthToContents(section);
|
|
slotColumnWidthChanged(0,0,0); //to update contents and redraw
|
|
}
|
|
|
|
|
|
void KexiTableView::updateGeometries()
|
|
{
|
|
TQSize ts = tableSize();
|
|
if (m_horizontalHeader->offset() && ts.width() < (m_horizontalHeader->offset() + m_horizontalHeader->width()))
|
|
horizontalScrollBar()->setValue(ts.width() - m_horizontalHeader->width());
|
|
|
|
// m_verticalHeader->setGeometry(1, topMargin() + 1, leftMargin(), visibleHeight());
|
|
m_horizontalHeader->setGeometry(leftMargin() + 1, 1, visibleWidth(), topMargin());
|
|
m_verticalHeader->setGeometry(1, topMargin() + 1, leftMargin(), visibleHeight());
|
|
}
|
|
|
|
int KexiTableView::columnWidth(int col) const
|
|
{
|
|
if (!hasData())
|
|
return 0;
|
|
int vcID = m_data->visibleColumnID( col );
|
|
return (vcID==-1) ? 0 : m_horizontalHeader->sectionSize( vcID );
|
|
}
|
|
|
|
int KexiTableView::rowHeight() const
|
|
{
|
|
return d->rowHeight;
|
|
}
|
|
|
|
int KexiTableView::columnPos(int col) const
|
|
{
|
|
if (!hasData())
|
|
return 0;
|
|
//if this column is hidden, find first column before that is visible
|
|
int c = TQMIN(col, (int)m_data->columnsCount()-1), vcID = 0;
|
|
while (c>=0 && (vcID=m_data->visibleColumnID( c ))==-1)
|
|
c--;
|
|
if (c<0)
|
|
return 0;
|
|
if (c==col)
|
|
return m_horizontalHeader->sectionPos(vcID);
|
|
return m_horizontalHeader->sectionPos(vcID)+m_horizontalHeader->sectionSize(vcID);
|
|
}
|
|
|
|
int KexiTableView::rowPos(int row) const
|
|
{
|
|
return d->rowHeight*row;
|
|
}
|
|
|
|
int KexiTableView::columnAt(int pos) const
|
|
{
|
|
if (!hasData())
|
|
return -1;
|
|
int r = m_horizontalHeader->sectionAt(pos);
|
|
if (r<0)
|
|
return r;
|
|
return m_data->globalColumnID( r );
|
|
|
|
// if (r==-1)
|
|
// kexidbg << "columnAt("<<pos<<")==-1 !!!" << endl;
|
|
// return r;
|
|
}
|
|
|
|
int KexiTableView::rowAt(int pos, bool ignoreEnd) const
|
|
{
|
|
if (!hasData())
|
|
return -1;
|
|
pos /=d->rowHeight;
|
|
if (pos < 0)
|
|
return 0;
|
|
if ((pos >= (int)m_data->count()) && !ignoreEnd)
|
|
return -1;
|
|
return pos;
|
|
}
|
|
|
|
TQRect KexiTableView::cellGeometry(int row, int col) const
|
|
{
|
|
return TQRect(columnPos(col), rowPos(row),
|
|
columnWidth(col), rowHeight());
|
|
}
|
|
|
|
TQSize KexiTableView::tableSize() const
|
|
{
|
|
if ((rows()+ (isInsertingEnabled()?1:0) ) > 0 && columns() > 0) {
|
|
/* kexidbg << "tableSize()= " << columnPos( columns() - 1 ) + columnWidth( columns() - 1 )
|
|
<< ", " << rowPos( rows()-1+(isInsertingEnabled()?1:0)) + d->rowHeight
|
|
// + TQMAX(m_navPanel ? m_navPanel->height() : 0, horizontalScrollBar()->sizeHint().height())
|
|
+ (m_navPanel->isVisible() ? TQMAX( m_navPanel->height(), horizontalScrollBar()->sizeHint().height() ) :0 )
|
|
+ margin() << endl;
|
|
*/
|
|
// kexidbg<< m_navPanel->isVisible() <<" "<<m_navPanel->height()<<" "
|
|
// <<horizontalScrollBar()->sizeHint().height()<<" "<<rowPos( rows()-1+(isInsertingEnabled()?1:0))<<endl;
|
|
|
|
//int xx = horizontalScrollBar()->sizeHint().height()/2;
|
|
|
|
TQSize s(
|
|
columnPos( columns() - 1 ) + columnWidth( columns() - 1 ),
|
|
// + verticalScrollBar()->sizeHint().width(),
|
|
rowPos( rows()-1+(isInsertingEnabled()?1:0) ) + d->rowHeight
|
|
+ (horizontalScrollBar()->isVisible() ? 0 : horizontalScrollBar()->sizeHint().height())
|
|
+ d->internal_bottomMargin
|
|
// horizontalScrollBar()->sizeHint().height()/2
|
|
// - /*d->bottomMargin */ horizontalScrollBar()->sizeHint().height()*3/2
|
|
|
|
// + ( (m_navPanel && m_navPanel->isVisible() && verticalScrollBar()->isVisible()
|
|
// && !horizontalScrollBar()->isVisible())
|
|
// ? horizontalScrollBar()->sizeHint().height() : 0)
|
|
|
|
// + TQMAX( (m_navPanel && m_navPanel->isVisible()) ? m_navPanel->height() : 0,
|
|
// horizontalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height() : 0)
|
|
|
|
// + (m_navPanel->isVisible()
|
|
// ? TQMAX( m_navPanel->height(), horizontalScrollBar()->sizeHint().height() ) :0 )
|
|
|
|
// - (horizontalScrollBar()->isVisible() ? horizontalScrollBar()->sizeHint().height() :0 )
|
|
+ margin()
|
|
//-2*d->rowHeight
|
|
);
|
|
|
|
// kexidbg << rows()-1 <<" "<< (isInsertingEnabled()?1:0) <<" "<< (m_rowEditing?1:0) << " " << s << endl;
|
|
return s;
|
|
// +horizontalScrollBar()->sizeHint().height() + margin() );
|
|
}
|
|
return TQSize(0,0);
|
|
}
|
|
|
|
void KexiTableView::ensureCellVisible(int row, int col/*=-1*/)
|
|
{
|
|
if (!isVisible()) {
|
|
//the table is invisible: we can't ensure visibility now
|
|
d->ensureCellVisibleOnShow = TQPoint(row,col);
|
|
return;
|
|
}
|
|
|
|
//quite clever: ensure the cell is visible:
|
|
TQRect r( columnPos(col==-1 ? m_curCol : col), rowPos(row) +(d->appearance.fullRowSelection?1:0),
|
|
columnWidth(col==-1 ? m_curCol : col), rowHeight());
|
|
|
|
/* if (m_navPanel && horizontalScrollBar()->isHidden() && row == rows()-1) {
|
|
//when cursor is moved down and navigator covers the cursor's area,
|
|
//area is scrolled up
|
|
if ((viewport()->height() - m_navPanel->height()) < r.bottom()) {
|
|
scrollBy(0,r.bottom() - (viewport()->height() - m_navPanel->height()));
|
|
}
|
|
}*/
|
|
|
|
if (m_navPanel && m_navPanel->isVisible() && horizontalScrollBar()->isHidden()) {
|
|
//a hack: for visible navigator: increase height of the visible rect 'r'
|
|
r.setBottom(r.bottom()+m_navPanel->height());
|
|
}
|
|
|
|
TQPoint pcenter = r.center();
|
|
ensureVisible(pcenter.x(), pcenter.y(), r.width()/2, r.height()/2);
|
|
// updateContents();
|
|
// updateNavPanelGeometry();
|
|
// slotUpdate();
|
|
}
|
|
|
|
void KexiTableView::updateAfterCancelRowEdit()
|
|
{
|
|
KexiDataAwareObjectInterface::updateAfterCancelRowEdit();
|
|
m_navPanel->showEditingIndicator(false);
|
|
}
|
|
|
|
void KexiTableView::updateAfterAcceptRowEdit()
|
|
{
|
|
KexiDataAwareObjectInterface::updateAfterAcceptRowEdit();
|
|
m_navPanel->showEditingIndicator(false);
|
|
}
|
|
|
|
bool KexiTableView::getVisibleLookupValue(TQVariant& cellValue, KexiTableEdit *edit,
|
|
KexiTableItem *item, KexiTableViewColumn *tvcol) const
|
|
{
|
|
if (edit->columnInfo() && edit->columnInfo()->indexForVisibleLookupValue()!=-1
|
|
&& edit->columnInfo()->indexForVisibleLookupValue() < (int)item->count())
|
|
{
|
|
const TQVariant *visibleFieldValue = 0;
|
|
if (m_currentItem == item && m_data->rowEditBuffer()) {
|
|
visibleFieldValue = m_data->rowEditBuffer()->at(
|
|
*tvcol->visibleLookupColumnInfo, false/*!useDefaultValueIfPossible*/ );
|
|
}
|
|
|
|
if (visibleFieldValue)
|
|
//(use bufferedValueAt() - try to get buffered visible value for lookup field)
|
|
cellValue = *visibleFieldValue; //txt = visibleFieldValue->toString();
|
|
else
|
|
cellValue /*txt*/ = item->at( edit->columnInfo()->indexForVisibleLookupValue() ); //.toString();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//reimpl.
|
|
void KexiTableView::removeEditor()
|
|
{
|
|
if (!m_editor)
|
|
return;
|
|
KexiDataAwareObjectInterface::removeEditor();
|
|
viewport()->setFocus();
|
|
}
|
|
|
|
void KexiTableView::slotRowRepaintRequested(KexiTableItem& item)
|
|
{
|
|
updateRow( m_data->findRef(&item) );
|
|
}
|
|
|
|
//(js) unused
|
|
void KexiTableView::slotAutoScroll()
|
|
{
|
|
kdDebug(44021) << "KexiTableView::slotAutoScroll()" <<endl;
|
|
if (!d->needAutoScroll)
|
|
return;
|
|
|
|
switch(d->scrollDirection)
|
|
{
|
|
case ScrollDown:
|
|
setCursorPosition(m_curRow + 1, m_curCol);
|
|
break;
|
|
|
|
case ScrollUp:
|
|
setCursorPosition(m_curRow - 1, m_curCol);
|
|
break;
|
|
case ScrollLeft:
|
|
setCursorPosition(m_curRow, m_curCol - 1);
|
|
break;
|
|
|
|
case ScrollRight:
|
|
setCursorPosition(m_curRow, m_curCol + 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifndef KEXI_NO_PRINT
|
|
void
|
|
KexiTableView::print(KPrinter &/*printer*/)
|
|
{
|
|
// printer.setFullPage(true);
|
|
#if 0
|
|
int leftMargin = printer.margins().width() + 2 + d->rowHeight;
|
|
int topMargin = printer.margins().height() + 2;
|
|
// int bottomMargin = topMargin + ( printer.realPageSize()->height() * printer.resolution() + 36 ) / 72;
|
|
int bottomMargin = 0;
|
|
kdDebug(44021) << "KexiTableView::print: bottom = " << bottomMargin << endl;
|
|
|
|
TQPainter p(&printer);
|
|
|
|
KexiTableItem *i;
|
|
int width = leftMargin;
|
|
for(int col=0; col < columns(); col++)
|
|
{
|
|
p.fillRect(width, topMargin - d->rowHeight, columnWidth(col), d->rowHeight, TQBrush(TQt::gray));
|
|
p.drawRect(width, topMargin - d->rowHeight, columnWidth(col), d->rowHeight);
|
|
p.drawText(width, topMargin - d->rowHeight, columnWidth(col), d->rowHeight, TQt::AlignLeft | TQt::AlignVCenter,
|
|
m_horizontalHeader->label(col));
|
|
width = width + columnWidth(col);
|
|
}
|
|
|
|
int yOffset = topMargin;
|
|
int row = 0;
|
|
int right = 0;
|
|
for(i = m_data->first(); i; i = m_data->next())
|
|
{
|
|
if(!i->isInsertItem())
|
|
{ kdDebug(44021) << "KexiTableView::print: row = " << row << " y = " << yOffset << endl;
|
|
int xOffset = leftMargin;
|
|
for(int col=0; col < columns(); col++)
|
|
{
|
|
kdDebug(44021) << "KexiTableView::print: col = " << col << " x = " << xOffset << endl;
|
|
p.saveWorldMatrix();
|
|
p.translate(xOffset, yOffset);
|
|
paintCell(&p, i, col, TQRect(0, 0, columnWidth(col) + 1, d->rowHeight), true);
|
|
p.restoreWorldMatrix();
|
|
// p.drawRect(xOffset, yOffset, columnWidth(col), d->rowHeight);
|
|
xOffset = xOffset + columnWidth(col);
|
|
right = xOffset;
|
|
}
|
|
|
|
row++;
|
|
yOffset = topMargin + row * d->rowHeight;
|
|
}
|
|
|
|
if(yOffset > 900)
|
|
{
|
|
p.drawLine(leftMargin, topMargin, leftMargin, yOffset);
|
|
p.drawLine(leftMargin, topMargin, right - 1, topMargin);
|
|
printer.newPage();
|
|
yOffset = topMargin;
|
|
row = 0;
|
|
}
|
|
}
|
|
p.drawLine(leftMargin, topMargin, leftMargin, yOffset);
|
|
p.drawLine(leftMargin, topMargin, right - 1, topMargin);
|
|
|
|
// p.drawLine(60,60,120,150);
|
|
p.end();
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
TQString KexiTableView::columnCaption(int colNum) const
|
|
{
|
|
return m_horizontalHeader->label(colNum);
|
|
}
|
|
|
|
KexiDB::Field* KexiTableView::field(int colNum) const
|
|
{
|
|
if (!m_data || !m_data->column(colNum))
|
|
return 0;
|
|
return m_data->column(colNum)->field();
|
|
}
|
|
|
|
void KexiTableView::adjustColumnWidthToContents(int colNum)
|
|
{
|
|
if (!hasData())
|
|
return;
|
|
if (colNum==-1) {
|
|
const int cols = columns();
|
|
for (int i=0; i<cols; i++)
|
|
adjustColumnWidthToContents(i);
|
|
return;
|
|
}
|
|
|
|
int indexOfVisibleColumn = (m_data->column(colNum) && m_data->column(colNum)->columnInfo)
|
|
? m_data->column(colNum)->columnInfo->indexForVisibleLookupValue() : -1;
|
|
if (-1==indexOfVisibleColumn)
|
|
indexOfVisibleColumn = colNum;
|
|
|
|
if (indexOfVisibleColumn < 0)
|
|
return;
|
|
|
|
TQPtrListIterator<KexiTableItem> it = m_data->iterator();
|
|
if (it.current() && it.current()->count()<=(uint)indexOfVisibleColumn)
|
|
return;
|
|
|
|
KexiCellEditorFactoryItem *item = KexiCellEditorFactory::item( columnType(indexOfVisibleColumn) );
|
|
if (!item)
|
|
return;
|
|
TQFontMetrics fm(fontMetrics());
|
|
int maxw = horizontalHeaderVisible()
|
|
? fm.width( m_horizontalHeader->label( colNum/* not indexOfVisibleColumn*/ ) ) : 0;
|
|
if (maxw == 0 && m_data->isEmpty())
|
|
return; //nothing to adjust
|
|
|
|
//! \todo js: this is NOT EFFECTIVE for big data sets!!!!
|
|
|
|
KexiTableEdit *ed = tableEditorWidget( colNum/* not indexOfVisibleColumn*/ );
|
|
if (ed) {
|
|
for (it = m_data->iterator(); it.current(); ++it) {
|
|
const int wfw = ed->widthForValue( it.current()->at( indexOfVisibleColumn ), fm );
|
|
maxw = TQMAX( maxw, wfw );
|
|
}
|
|
const bool focused = currentColumn() == colNum;
|
|
maxw += (fm.width(" ") + ed->leftMargin() + ed->rightMargin(focused));
|
|
}
|
|
if (maxw < KEXITV_MINIMUM_COLUMN_WIDTH )
|
|
maxw = KEXITV_MINIMUM_COLUMN_WIDTH; //not too small
|
|
kexidbg << "KexiTableView: setColumnWidth(colNum=" << colNum
|
|
<< ", indexOfVisibleColumn=" << indexOfVisibleColumn << ", width=" << maxw <<" )" << endl;
|
|
setColumnWidth( colNum/* not indexOfVisibleColumn*/, maxw );
|
|
}
|
|
|
|
void KexiTableView::setColumnWidth(int colNum, int width)
|
|
{
|
|
if (columns()<=colNum || colNum < 0)
|
|
return;
|
|
const int oldWidth = m_horizontalHeader->sectionSize( colNum );
|
|
m_horizontalHeader->resizeSection( colNum, width );
|
|
slotTopHeaderSizeChange( colNum, oldWidth, m_horizontalHeader->sectionSize( colNum ) );
|
|
}
|
|
|
|
void KexiTableView::maximizeColumnsWidth( const TQValueList<int> &columnList )
|
|
{
|
|
if (!isVisible()) {
|
|
d->maximizeColumnsWidthOnShow += columnList;
|
|
return;
|
|
}
|
|
if (width() <= m_horizontalHeader->headerWidth())
|
|
return;
|
|
//sort the list and make it unique
|
|
TQValueList<int> cl, sortedList = columnList;
|
|
qHeapSort(sortedList);
|
|
int i=-999;
|
|
|
|
TQValueList<int>::ConstIterator it, end( sortedList.constEnd() );
|
|
for ( it = sortedList.constBegin(); it != end; ++it) {
|
|
if (i != (*it)) {
|
|
cl += (*it);
|
|
i = (*it);
|
|
}
|
|
}
|
|
//resize
|
|
int sizeToAdd = (width() - m_horizontalHeader->headerWidth()) / cl.count() - verticalHeader()->width();
|
|
if (sizeToAdd<=0)
|
|
return;
|
|
end = cl.constEnd();
|
|
for ( it = cl.constBegin(); it != end; ++it) {
|
|
int w = m_horizontalHeader->sectionSize(*it);
|
|
if (w>0) {
|
|
m_horizontalHeader->resizeSection(*it, w+sizeToAdd);
|
|
}
|
|
}
|
|
updateContents();
|
|
editorShowFocus( m_curRow, m_curCol );
|
|
}
|
|
|
|
void KexiTableView::adjustHorizontalHeaderSize()
|
|
{
|
|
m_horizontalHeader->adjustHeaderSize();
|
|
}
|
|
|
|
void KexiTableView::setColumnStretchEnabled( bool set, int colNum )
|
|
{
|
|
m_horizontalHeader->setStretchEnabled( set, colNum );
|
|
}
|
|
|
|
void KexiTableView::setEditableOnDoubleClick(bool set)
|
|
{
|
|
d->editOnDoubleClick = set;
|
|
}
|
|
bool KexiTableView::editableOnDoubleClick() const
|
|
{
|
|
return d->editOnDoubleClick;
|
|
}
|
|
|
|
bool KexiTableView::verticalHeaderVisible() const
|
|
{
|
|
return m_verticalHeader->isVisible();
|
|
}
|
|
|
|
void KexiTableView::setVerticalHeaderVisible(bool set)
|
|
{
|
|
int left_width;
|
|
if (set) {
|
|
m_verticalHeader->show();
|
|
left_width = TQMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight);
|
|
}
|
|
else {
|
|
m_verticalHeader->hide();
|
|
left_width = 0;
|
|
}
|
|
setMargins( left_width, horizontalHeaderVisible() ? m_horizontalHeader->sizeHint().height() : 0, 0, 0);
|
|
}
|
|
|
|
bool KexiTableView::horizontalHeaderVisible() const
|
|
{
|
|
return d->horizontalHeaderVisible;
|
|
}
|
|
|
|
void KexiTableView::setHorizontalHeaderVisible(bool set)
|
|
{
|
|
int top_height;
|
|
d->horizontalHeaderVisible = set; //needed because isVisible() is not always accurate
|
|
if (set) {
|
|
m_horizontalHeader->show();
|
|
top_height = m_horizontalHeader->sizeHint().height();
|
|
}
|
|
else {
|
|
m_horizontalHeader->hide();
|
|
top_height = 0;
|
|
}
|
|
setMargins( verticalHeaderVisible() ? m_verticalHeader->width() : 0, top_height, 0, 0);
|
|
}
|
|
|
|
void KexiTableView::triggerUpdate()
|
|
{
|
|
// kdDebug(44021) << "KexiTableView::triggerUpdate()" << endl;
|
|
// if (!d->pUpdateTimer->isActive())
|
|
d->pUpdateTimer->start(20, true);
|
|
// d->pUpdateTimer->start(200, true);
|
|
}
|
|
|
|
void KexiTableView::setHBarGeometry( TQScrollBar & hbar, int x, int y, int w, int h )
|
|
{
|
|
/*todo*/
|
|
kdDebug(44021)<<"KexiTableView::setHBarGeometry"<<endl;
|
|
if (d->appearance.navigatorEnabled) {
|
|
m_navPanel->setHBarGeometry( hbar, x, y, w, h );
|
|
}
|
|
else {
|
|
hbar.setGeometry( x , y, w, h );
|
|
}
|
|
}
|
|
|
|
void KexiTableView::setSpreadSheetMode()
|
|
{
|
|
KexiDataAwareObjectInterface::setSpreadSheetMode();
|
|
//copy m_navPanelEnabled flag
|
|
Appearance a = d->appearance;
|
|
a.navigatorEnabled = m_navPanelEnabled;
|
|
setAppearance( a );
|
|
}
|
|
|
|
int KexiTableView::validRowNumber(const TQString& text)
|
|
{
|
|
bool ok=true;
|
|
int r = text.toInt(&ok);
|
|
if (!ok || r<1)
|
|
r = 1;
|
|
else if (r > (rows()+(isInsertingEnabled()?1:0)))
|
|
r = rows()+(isInsertingEnabled()?1:0);
|
|
return r-1;
|
|
}
|
|
|
|
void KexiTableView::moveToRecordRequested( uint r )
|
|
{
|
|
if (r > uint(rows()+(isInsertingEnabled()?1:0)))
|
|
r = rows()+(isInsertingEnabled()?1:0);
|
|
setFocus();
|
|
selectRow( r );
|
|
}
|
|
|
|
void KexiTableView::moveToLastRecordRequested()
|
|
{
|
|
setFocus();
|
|
selectRow(rows()>0 ? (rows()-1) : 0);
|
|
}
|
|
|
|
void KexiTableView::moveToPreviousRecordRequested()
|
|
{
|
|
setFocus();
|
|
selectPrevRow();
|
|
}
|
|
|
|
void KexiTableView::moveToNextRecordRequested()
|
|
{
|
|
setFocus();
|
|
selectNextRow();
|
|
}
|
|
|
|
void KexiTableView::moveToFirstRecordRequested()
|
|
{
|
|
setFocus();
|
|
selectFirstRow();
|
|
}
|
|
|
|
void KexiTableView::copySelection()
|
|
{
|
|
if (m_currentItem && m_curCol!=-1) {
|
|
KexiTableEdit *edit = tableEditorWidget( m_curCol );
|
|
TQVariant defaultValue;
|
|
const bool defaultValueDisplayed
|
|
= isDefaultValueDisplayed(m_currentItem, m_curCol, &defaultValue);
|
|
if (edit) {
|
|
TQVariant visibleValue;
|
|
getVisibleLookupValue(visibleValue, edit, m_currentItem, m_data->column(m_curCol));
|
|
edit->handleCopyAction(
|
|
defaultValueDisplayed ? defaultValue : m_currentItem->at( m_curCol ),
|
|
visibleValue );
|
|
}
|
|
}
|
|
}
|
|
|
|
void KexiTableView::cutSelection()
|
|
{
|
|
//try to handle @ editor's level
|
|
KexiTableEdit *edit = tableEditorWidget( m_curCol );
|
|
if (edit)
|
|
edit->handleAction("edit_cut");
|
|
}
|
|
|
|
void KexiTableView::paste()
|
|
{
|
|
//try to handle @ editor's level
|
|
KexiTableEdit *edit = tableEditorWidget( m_curCol );
|
|
if (edit)
|
|
edit->handleAction("edit_paste");
|
|
}
|
|
|
|
bool KexiTableView::eventFilter( TQObject *o, TQEvent *e )
|
|
{
|
|
//don't allow to stole key my events by others:
|
|
// kexidbg << "spontaneous " << e->spontaneous() << " type=" << e->type() << endl;
|
|
|
|
if (e->type()==TQEvent::KeyPress) {
|
|
if (e->spontaneous() /*|| e->type()==TQEvent::AccelOverride*/) {
|
|
TQKeyEvent *ke = TQT_TQKEYEVENT(e);
|
|
const int k = ke->key();
|
|
int s = ke->state();
|
|
//cell editor's events:
|
|
//try to handle the event @ editor's level
|
|
KexiTableEdit *edit = tableEditorWidget( m_curCol );
|
|
if (edit && edit->handleKeyPress(ke, m_editor==edit)) {
|
|
ke->accept();
|
|
return true;
|
|
}
|
|
else if (m_editor && (o==dynamic_cast<TQT_BASE_OBJECT_NAME*>(m_editor) || TQT_BASE_OBJECT(o)==TQT_BASE_OBJECT(m_editor->widget()))) {
|
|
if ( (k==TQt::Key_Tab && (s==Qt::NoButton || s==TQt::ShiftButton))
|
|
|| (overrideEditorShortcutNeeded(ke))
|
|
|| (k==TQt::Key_Enter || k==TQt::Key_Return || k==TQt::Key_Up || k==TQt::Key_Down)
|
|
|| (k==TQt::Key_Left && m_editor->cursorAtStart())
|
|
|| (k==TQt::Key_Right && m_editor->cursorAtEnd())
|
|
)
|
|
{
|
|
//try to steal the key press from editor or it's internal widget...
|
|
keyPressEvent(ke);
|
|
if (ke->isAccepted())
|
|
return true;
|
|
}
|
|
}
|
|
/*
|
|
else if (e->type()==TQEvent::KeyPress && (o==this || (m_editor && o==m_editor->widget()))){//|| o==viewport())
|
|
keyPressEvent(ke);
|
|
if (ke->isAccepted())
|
|
return true;
|
|
}*/
|
|
/*todo else if ((k==TQt::Key_Tab || k==(TQt::SHIFT|TQt::Key_Tab)) && o==d->navRowNumber) {
|
|
//tab key focuses tv
|
|
ke->accept();
|
|
setFocus();
|
|
return true;
|
|
}*/
|
|
}
|
|
}
|
|
else if (TQT_BASE_OBJECT(o)==TQT_BASE_OBJECT(horizontalScrollBar())) {
|
|
if ((e->type()==TQEvent::Show && !horizontalScrollBar()->isVisible())
|
|
|| (e->type()==TQEvent::Hide && horizontalScrollBar()->isVisible())) {
|
|
updateWidgetContentsSize();
|
|
}
|
|
}
|
|
else if (e->type()==TQEvent::Leave) {
|
|
if (TQT_BASE_OBJECT(o)==TQT_BASE_OBJECT(viewport()) && d->appearance.rowMouseOverHighlightingEnabled
|
|
&& d->appearance.persistentSelections)
|
|
{
|
|
if (d->highlightedRow!=-1) {
|
|
int oldRow = d->highlightedRow;
|
|
d->highlightedRow = -1;
|
|
updateRow(oldRow);
|
|
const bool dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted
|
|
= d->appearance.rowHighlightingEnabled && !d->appearance.persistentSelections;
|
|
if (oldRow!=m_curRow && m_curRow>=0) {
|
|
if (!dontPaintNonpersistentSelectionBecauseDifferentRowHasBeenHighlighted)
|
|
//no highlight for now: show selection again
|
|
updateRow(m_curRow);
|
|
m_verticalHeader->setHighlightedRow(-1);
|
|
}
|
|
}
|
|
}
|
|
d->recentCellWithToolTip = TQPoint(-1,-1);
|
|
}
|
|
/* else if (e->type()==TQEvent::FocusOut && o->inherits(TQWIDGET_OBJECT_NAME_STRING)) {
|
|
//hp==true if currently focused widget is a child of this table view
|
|
const bool hp = KexiUtils::hasParent( static_cast<TQWidget*>(o), focusWidget());
|
|
if (!hp && KexiUtils::hasParent( this, static_cast<TQWidget*>(o))) {
|
|
//accept row editing if focus is moved to foreign widget
|
|
//(not a child, like eg. editor) from one of our table view's children
|
|
//or from table view itself
|
|
if (!acceptRowEdit()) {
|
|
static_cast<TQWidget*>(o)->setFocus();
|
|
return true;
|
|
}
|
|
}
|
|
}*/
|
|
return TQScrollView::eventFilter(o,e);
|
|
}
|
|
|
|
void KexiTableView::slotTopHeaderSizeChange(
|
|
int /*section*/, int /*oldSize*/, int /*newSize*/ )
|
|
{
|
|
editorShowFocus( m_curRow, m_curCol );
|
|
}
|
|
|
|
void KexiTableView::setBottomMarginInternal(int pixels)
|
|
{
|
|
d->internal_bottomMargin = pixels;
|
|
}
|
|
|
|
void KexiTableView::paletteChange( const TQPalette &oldPalette )
|
|
{
|
|
Q_UNUSED(oldPalette);
|
|
//update:
|
|
if (m_verticalHeader)
|
|
m_verticalHeader->setSelectionBackgroundColor( palette().active().highlight() );
|
|
if (m_horizontalHeader)
|
|
m_horizontalHeader->setSelectionBackgroundColor( palette().active().highlight() );
|
|
}
|
|
|
|
const KexiTableView::Appearance& KexiTableView::appearance() const
|
|
{
|
|
return d->appearance;
|
|
}
|
|
|
|
void KexiTableView::setAppearance(const Appearance& a)
|
|
{
|
|
// if (d->appearance.fullRowSelection != a.fullRowSelection) {
|
|
if (a.fullRowSelection) {
|
|
d->rowHeight -= 1;
|
|
}
|
|
else {
|
|
d->rowHeight += 1;
|
|
}
|
|
if (m_verticalHeader)
|
|
m_verticalHeader->setCellHeight(d->rowHeight);
|
|
if (m_horizontalHeader) {
|
|
setMargins(
|
|
TQMIN(m_horizontalHeader->sizeHint().height(), d->rowHeight),
|
|
m_horizontalHeader->sizeHint().height(), 0, 0);
|
|
}
|
|
// }
|
|
if (a.rowHighlightingEnabled)
|
|
m_updateEntireRowWhenMovingToOtherRow = true;
|
|
|
|
if(!a.navigatorEnabled)
|
|
m_navPanel->hide();
|
|
else
|
|
m_navPanel->show();
|
|
// }
|
|
|
|
d->highlightedRow = -1;
|
|
//! @todo is setMouseTracking useful for other purposes?
|
|
viewport()->setMouseTracking(a.rowMouseOverHighlightingEnabled);
|
|
|
|
d->appearance = a;
|
|
|
|
setFont(font()); //this also updates contents
|
|
}
|
|
|
|
int KexiTableView::highlightedRow() const
|
|
{
|
|
return d->highlightedRow;
|
|
}
|
|
|
|
void KexiTableView::setHighlightedRow(int row)
|
|
{
|
|
if (row!=-1) {
|
|
row = TQMIN(rows() - 1 + (isInsertingEnabled()?1:0), row);
|
|
row = TQMAX(0, row);
|
|
ensureCellVisible(row, -1);
|
|
}
|
|
const int previouslyHighlightedRow = d->highlightedRow;
|
|
if (previouslyHighlightedRow == row) {
|
|
if (previouslyHighlightedRow!=-1)
|
|
updateRow(previouslyHighlightedRow);
|
|
return;
|
|
}
|
|
d->highlightedRow = row;
|
|
if (d->highlightedRow!=-1)
|
|
updateRow(d->highlightedRow);
|
|
|
|
if (previouslyHighlightedRow!=-1)
|
|
updateRow(previouslyHighlightedRow);
|
|
|
|
if (m_curRow>=0 && (previouslyHighlightedRow==-1 || previouslyHighlightedRow==m_curRow)
|
|
&& d->highlightedRow!=m_curRow && !d->appearance.persistentSelections)
|
|
{
|
|
//currently selected row needs to be repainted
|
|
updateRow(m_curRow);
|
|
}
|
|
}
|
|
|
|
KexiTableItem *KexiTableView::highlightedItem() const
|
|
{
|
|
return d->highlightedRow == -1 ? 0 : m_data->at(d->highlightedRow);
|
|
}
|
|
|
|
void KexiTableView::slotSettingsChanged(int category)
|
|
{
|
|
if (category==TDEApplication::SETTINGS_SHORTCUTS) {
|
|
d->contextMenuKey = TDEGlobalSettings::contextMenuKey();
|
|
}
|
|
}
|
|
|
|
int KexiTableView::lastVisibleRow() const
|
|
{
|
|
return rowAt( contentsY() );
|
|
}
|
|
|
|
#include "kexitableview.moc"
|
|
|