Table
The table program displays a spreadsheet-like table. It is an example of
how to inherit the QScrollView widget for presentations of 2D data arrays.
Left-click the mouse to set a current cell. Press the arrow buttons to
move the current mark around. Type something to edit a cell's
contents.
Header file: /****************************************************************************
** $Id: qt/examples/table/table.h 2.2.0-beta0 edited 2000-06-05 $
**
** Copyright (C) 1992-2000 Troll Tech AS. All rights reserved.
**
** This file is part of an example program for Qt. This example
** program may be used, distributed and modified without limitation.
**
*****************************************************************************/
#ifndef TABLE_H
#define TABLE_H
#include <qscrollview.h>
#include <qpixmap.h>
#include <qvector.h>
class QHeader;
class QLineEdit;
class TableItem
{
public:
TableItem( const QString &t, const QPixmap p )
: txt( t ), pix( p ) {}
QPixmap pixmap() const { return pix; }
QString text() const { return txt; }
void setPixmap( const QPixmap &p ) { pix = p; }
void setText( const QString &t ) { txt = t; }
private:
QString txt;
QPixmap pix;
};
class Table : public QScrollView
{
Q_OBJECT
public:
Table( int numRows, int numCols, QWidget* parent=0, const char* name=0 );
~Table();
TableItem *cellContent( int row, int col ) const;
void setCellContent( int row, int col, TableItem *item );
void setCellText( int row, int col, const QString &text );
void setCellPixmap( int row, int col, const QPixmap &pix );
QString cellText( int row, int col ) const;
QPixmap cellPixmap( int row, int col ) const;
QRect cellGeometry( int row, int col ) const;
int columnWidth( int col ) const;
int rowHeight( int row ) const;
int columnPos( int col ) const;
int rowPos( int row ) const;
int columnAt( int pos ) const;
int rowAt( int pos ) const;
QSize tableSize() const;
int rows() const;
int cols() const;
void updateCell( int row, int col );
protected:
void drawContents( QPainter *p, int cx, int cy, int cw, int ch );
void contentsMousePressEvent( QMouseEvent* );
void contentsMouseMoveEvent( QMouseEvent* );
void keyPressEvent( QKeyEvent* );
void focusInEvent( QFocusEvent* );
void focusOutEvent( QFocusEvent* );
void resizeEvent( QResizeEvent * );
void showEvent( QShowEvent *e );
virtual void paintEmptyArea( QPainter *p, int cx, int cy, int cw, int ch );
bool focusNextPrevChild( bool next );
protected slots:
virtual void columnWidthChanged( int col, int os, int ns );
virtual void rowHeightChanged( int col, int os, int ns );
private slots:
void editorOk();
void editorCancel();
private:
void paintCell( QPainter *p, int row, int col, const QRect &cr );
int indexOf( int row, int col ) const;
void updateGeometries();
private:
QVector<TableItem> contents;
int curRow;
int curCol;
QHeader *leftHeader, *topHeader;
QLineEdit *editor;
};
#endif // TABLE_H
Implementation: /****************************************************************************
** $Id: qt/examples/table/table.cpp 2.2.0-beta0 edited 2000-06-05 $
**
** Copyright (C) 1992-2000 Troll Tech AS. All rights reserved.
**
** This file is part of an example program for Qt. This example
** program may be used, distributed and modified without limitation.
**
*****************************************************************************/
#include "table.h"
#include <qpainter.h>
#include <qkeycode.h>
#include <qheader.h>
#include <qlineedit.h>
#include <qapplication.h>
#include <qwmatrix.h>
// Qt logo
static const char *qtlogo_xpm[] = {
"45 36 13 1",
" c #000000",
". c #999999",
"X c #333366",
"o c #6666CC",
"O c #333333",
"@ c #666699",
"# c #000066",
"$ c #666666",
"% c #3333CC",
"& c #000033",
"* c #9999CC",
"= c #333399",
"+ c None",
"+++++++++++++++++++++++++++++++++++++++++++++",
"+++++++++++++++.$OOO$.+++++++++++++++++++++++",
"+++++++++++++$ O.++++++++++++++++++++",
"+++++++++++.O $+++++++++++++++++++",
"++++++++++. $.++.$ O++++++++++++++++++",
"+++++++++. O.+++++++$ O+++++++++++++++++",
"+++++++++O ++++++++++$ $++++++++++++++++",
"++++++++$ .+++++++++++O .+++++++++++++++",
"+++++++. O+++++++++++++ O++++++.++++++++",
"+++++++$ .+++++++++++++$ .+++.O ++++++++",
"+++++++ +++++++++++++++ O+++. ++++++++",
"++++++. &Xoooo*++++++++++$ +++. ++++++++",
"++++++@=%%%%%%%%%%*+++++++. .++. ++++++++",
"+++**oooooo**++*o%%%%o+++++ $++O ++++++++",
"+*****$OOX@oooo*++*%%%%%*++O $+. OOO$++++",
"+.++....$O$+*ooooo*+*o%%%%%O O+$ $$O.++++",
"*+++++$$....+++*oooo**+*o%%# O++O ++++++**",
"++++++O $.....++**oooo**+*X &o*O ++++*ooo",
"++++++$ O++.....++**oooo*X &%%& ..*o%%*+",
"++++++$ ++++.....+++**ooO $*o& @oo*++++",
"++++++. .++++++.....+++*O Xo*O .+++++++",
"+++++++ O+++++++++...... .++O ++++++++",
"+++++++O +++.$$$.++++++. O+++O ++++++++",
"+++++++. $$OO O.++++O .+++O ++++++++",
"++++++++O .+++.O $++. O++++O ++++++++",
"++++++++. O+++++O $+O +++++O ++++++++",
"+++++++++. O+++++O O .+++++O .+++++++",
"++++++++++$ .++++O .++++.+$ O+.$.+++",
"+++++++++++. O$$O .+++++... ++++",
"+++++++++++++$ O+++++$$+.O O$.++++",
"+++++++++++++++$OO O$.O O.++. .+++++++++++",
"+++++++++++++++++++++++. OO .+++++++++++",
"++++++++++++++++++++++++. O++++++++++++",
"+++++++++++++++++++++++++. .++++++++++++",
"++++++++++++++++++++++++++.O O.+++++++++++++",
"+++++++++++++++++++++++++++++++++++++++++++++"
};
Table::Table( int numRows, int numCols, QWidget *parent, const char *name )
: QScrollView( parent, name, WRepaintNoErase | WNorthWestGravity )
{
setResizePolicy( Manual );
// Create headers
leftHeader = new QHeader( numRows, this );
leftHeader->setOrientation( Vertical );
leftHeader->setTracking( TRUE );
leftHeader->setMovingEnabled( FALSE );
topHeader = new QHeader( numCols, this );
topHeader->setOrientation( Horizontal );
topHeader->setTracking( TRUE );
topHeader->setMovingEnabled( FALSE );
setMargins( 30, fontMetrics().height() + 4, 0, 0 );
// Initialize headers
int i = 0;
for ( i = 0; i < cols(); ++i ) {
topHeader->setLabel( i, QString::number( i + 1 ) );
topHeader->resizeSection( i, 100 );
}
for ( i = 0; i < rows(); ++i ) {
leftHeader->setLabel( i, QString::number( i + 1 ) );
leftHeader->resizeSection( i, 20 );
}
// Enable clipper and set background mode
enableClipper( TRUE );
viewport()->setBackgroundMode( PaletteBase );
// Prepare for contents
contents.resize( numRows * numCols );
contents.setAutoDelete( TRUE );
QWMatrix wm;
wm.scale( 0.5, 0.5 );
QPixmap pix( qtlogo_xpm );
pix = pix.xForm( wm );
setCellPixmap( 3, 3, pix );
setCellText( 3, 3, "A Pixmap" );
// Connect header, table and scrollbars
connect( horizontalScrollBar(), SIGNAL( valueChanged( int ) ),
topHeader, SLOT( setOffset( int ) ) );
connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ),
leftHeader, SLOT( setOffset( int ) ) );
connect( topHeader, SIGNAL( sizeChange( int, int, int ) ),
this, SLOT( columnWidthChanged( int, int, int ) ) );
connect( leftHeader, SIGNAL( sizeChange( int, int, int ) ),
this, SLOT( rowHeightChanged( int, int, int ) ) );
// Initialize variables
curRow = curCol = 0;
editor = 0;
// Initial size
resize( 640, 480 );
}
Table::~Table()
{
}
/****************************************************************************
Two drawing functions, one which finds out which cells to draw
and one which actually draws a cell.
Also one drawing function to draw empty areas.
*****************************************************************************/
void Table::drawContents( QPainter *p, int cx, int cy, int cw, int ch )
{
int colfirst = columnAt( cx );
int collast = columnAt( cx + cw );
int rowfirst = rowAt( cy );
int rowlast = rowAt( cy + ch );
if ( rowfirst == -1 || colfirst == -1 ) {
paintEmptyArea( p, cx, cy, cw, ch );
return;
}
if ( rowlast == -1 )
rowlast = rows() - 1;
if ( collast == -1 )
collast = cols() - 1;
// Go through the rows
for ( int r = rowfirst; r <= rowlast; ++r ) {
// get row position and height
int rowp = rowPos( r );
int rowh = rowHeight( r );
// 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
for ( int c = colfirst; c <= collast; ++c ) {
// get position and width of column c
int colp, colw;
colp = columnPos( c );
colw = columnWidth( c );
// Translate painter and draw the cell
p->saveWorldMatrix();
p->translate( colp, rowp );
paintCell( p, r, c, QRect( colp, rowp, colw, rowh ) );
p->restoreWorldMatrix();
}
}
// Paint empty rects
paintEmptyArea( p, cx, cy, cw, ch );
}
void Table::paintCell( QPainter* p, int row, int col, const QRect &cr )
{
int w = cr.width();
int h = cr.height();
int x2 = w - 1;
int y2 = h - 1;
// Draw cell background
p->fillRect( 0, 0, w, h, colorGroup().brush( QColorGroup::Base ) );
// Draw our lines
QPen pen( p->pen() );
p->setPen( gray );
p->drawLine( x2, 0, x2, y2 );
p->drawLine( 0, y2, x2, y2 );
p->setPen( pen );
// If we are in the focus cell, draw indication
if ( row == curRow &&col == curCol ) {
if ( hasFocus() || viewport()->hasFocus() )
p->drawRect( 0, 0, x2, y2 );
}
int x = 0;
QPixmap pix( cellPixmap( row, col ) );
if ( !pix.isNull() ) {
p->drawPixmap( 0, ( cr.height() - pix.height() ) / 2, pix );
x = pix.width() + 2;
}
// Find out if contents is a number or a string
bool num;
bool ok1 = FALSE, ok2 = FALSE;
QString s( cellText( row, col ) );
s.toInt( &ok1 );
s.toDouble( &ok2 );
num = ok1 || ok2;
// Draw contents
p->drawText( x, 0, w - x, h, ( num ? AlignRight : AlignLeft ) | AlignVCenter, s );
}
void Table::paintEmptyArea( QPainter *p, int cx, int cy, int cw, int ch )
{
// Region of the rect we should draw
QRegion reg( QRect( cx, cy, cw, ch ) );
// Subtract the table from it
reg = reg.subtract( QRect( QPoint( 0, 0 ), tableSize() ) );
p->save();
// Set clip region...
p->setClipRegion( reg );
// ...and fill background
p->fillRect( cx, cy, cw, ch, colorGroup().brush( QColorGroup::Base ) );
p->restore();
}
/****************************************************************************
Functions to access and set the cell contents
plus helper functions.
*****************************************************************************/
TableItem *Table::cellContent( int row, int col ) const
{
return contents[ indexOf( row, col ) ]; // contents array lookup
}
void Table::setCellContent( int row, int col, TableItem *item )
{
if ( contents[ indexOf( row, col ) ] )
delete contents[ indexOf( row, col ) ];
contents.insert( indexOf( row, col ), item ); // contents lookup and assign
updateCell( row, col ); // repaint
}
int Table::indexOf( int row, int col ) const
{
return ( row * cols() ) + col; // mapping from 2D table to 1D array
}
void Table::setCellText( int row, int col, const QString &text )
{
TableItem *item = cellContent( row, col );
if ( item ) {
item->setText( text );
updateCell( row, col );
} else {
TableItem *i = new TableItem( text, QPixmap() );
setCellContent( row, col, i );
}
}
void Table::setCellPixmap( int row, int col, const QPixmap &pix )
{
TableItem *item = cellContent( row, col );
if ( item ) {
item->setPixmap( pix );
updateCell( row, col );
} else {
TableItem *i = new TableItem( QString::null, pix );
setCellContent( row, col, i );
}
}
QString Table::cellText( int row, int col ) const
{
TableItem *item = cellContent( row, col );
if ( item )
return item->text();
return QString::null;
}
QPixmap Table::cellPixmap( int row, int col ) const
{
TableItem *item = cellContent( row, col );
if ( item )
return item->pixmap();
return QPixmap();
}
/****************************************************************************
Event handling to react on mouse, key and focus events.
*****************************************************************************/
void Table::contentsMousePressEvent( QMouseEvent* e )
{
// get rid of editor
if ( editor )
editorOk();
// remember old focus cell
int oldRow = curRow;
int oldCol = curCol;
// get new focus cell
curRow = rowAt( e->pos().y() );
curCol = columnAt( e->pos().x() );
if ( curRow == -1 )
curRow = oldRow;
if ( curCol == -1 )
curCol = oldCol;
// if we have a new focus cell, repaint
if ( curRow != oldRow || curCol != oldCol ) {
updateCell( oldRow, oldCol );
updateCell( curRow, curCol );
int cw = columnWidth( curCol );
int rh = rowHeight( curRow );
ensureVisible( columnPos( curCol ) + cw / 2, rowPos( curRow ) + rh / 2, cw / 2, rh / 2 );
}
}
void Table::contentsMouseMoveEvent( QMouseEvent *e )
{
// do the same as in mouse press
contentsMousePressEvent( e );
}
void Table::keyPressEvent( QKeyEvent* e )
{
// if a cell is just editing, do some special stuff
if ( editor ) {
if ( e->key() == Key_Escape )
editorCancel();
else if ( e->key() == Key_Return || e->key() == Key_Enter )
editorOk();
return;
}
int oldRow = curRow;
int oldCol = curCol;
// navigate in the header...
switch ( e->key() ) {
case Key_Left:
curCol = QMAX( 0, curCol - 1 );
break;
case Key_Right:
curCol = QMIN( cols() - 1, curCol + 1 );
break;
case Key_Up:
curRow = QMAX( 0, curRow - 1 );
break;
case Key_Down:
curRow = QMIN( rows() - 1, curRow + 1 );
break;
case Key_Prior:
case Key_Next:
case Key_Home:
case Key_End:
break;
default: // ... or start in-place editing
if ( e->text()[ 0 ].isPrint() ) {
editor = new QLineEdit( cellText( curRow, curCol ) + e->text(), viewport() );
int d = cellPixmap( curRow, curCol ).width();
moveChild( editor, columnPos( curCol ) + 1 + d, rowPos( curRow ) + 1 );
editor->resize( columnWidth( curCol ) - 2 - d, rowHeight( curRow ) - 2 );
editor->setFrame( FALSE );
editor->show();
editor->setFocus();
connect( editor, SIGNAL( returnPressed() ),
this, SLOT( editorOk() ) );
}
}
// if focus cell changes, repaint
if ( curRow != oldRow || curCol != oldCol ) {
updateCell( oldRow, oldCol );
updateCell( curRow, curCol );
int cw = columnWidth( curCol );
int rh = rowHeight( curRow );
ensureVisible( columnPos( curCol ) + cw / 2, rowPos( curRow ) + rh / 2, cw / 2, rh / 2 );
}
}
void Table::focusInEvent( QFocusEvent* )
{
updateCell( curRow, curCol );
}
void Table::focusOutEvent( QFocusEvent* )
{
updateCell( curRow, curCol );
}
bool Table::focusNextPrevChild( bool next )
{
if ( editor )
return TRUE;
return QScrollView::focusNextPrevChild( next );
}
void Table::resizeEvent( QResizeEvent *e )
{
QScrollView::resizeEvent( e );
updateGeometries();
}
void Table::showEvent( QShowEvent *e )
{
QScrollView::showEvent( e );
QRect r( cellGeometry( rows() - 1, cols() - 1 ) );
resizeContents( r.right() + 1, r.bottom() + 1 );
updateGeometries();
}
/****************************************************************************
Functions to update cells, update geometries, etc.
*****************************************************************************/
void Table::updateCell( int row, int col )
{
QRect r( cellGeometry( row, col ) );
updateContents( r );
}
void Table::columnWidthChanged( int col, int, int )
{
updateContents( columnPos( col ), 0, contentsWidth(), contentsHeight() );
QSize s( tableSize() );
int w = contentsWidth();
resizeContents( s.width(), s.height() );
if ( contentsWidth() < w )
repaintContents( s.width(), 0, w - s.width() + 1, contentsHeight(), TRUE );
if ( editor ) {
moveChild( editor, columnPos( curCol ) + 1, rowPos( curRow ) + 1 );
editor->resize( columnWidth( curCol ) - 2, rowHeight( curRow ) - 2 );
}
updateGeometries();
}
void Table::rowHeightChanged( int row, int, int )
{
updateContents( 0, rowPos( row ), contentsWidth(), contentsHeight() );
QSize s( tableSize() );
int h = contentsHeight();
resizeContents( s.width(), s.height() );
if ( contentsHeight() < h )
repaintContents( 0, contentsHeight(), contentsWidth(), h - s.height() + 1, TRUE );
if ( editor ) {
moveChild( editor, columnPos( curCol ) + 1, rowPos( curRow ) + 1 );
editor->resize( columnWidth( curCol ) - 2, rowHeight( curRow ) - 2 );
}
updateGeometries();
}
void Table::updateGeometries()
{
QSize ts = tableSize();
if ( topHeader->offset() &&
ts.width() < topHeader->offset() + topHeader->width() )
horizontalScrollBar()->setValue( ts.width() - topHeader->width() );
if ( leftHeader->offset() &&
ts.height() < leftHeader->offset() + leftHeader->height() )
verticalScrollBar()->setValue( ts.height() - leftHeader->height() );
leftHeader->setGeometry( leftMargin() - 30 + 2, topMargin() + 2,
30, visibleHeight() );
topHeader->setGeometry( leftMargin() + 2, topMargin() + 2 - fontMetrics().height() - 4,
visibleWidth(),
fontMetrics().height() + 4 );
}
/****************************************************************************
Following functions return the position/sizes of table cells. The
data is stored in the two headers anyway, so simply use it.
*****************************************************************************/
int Table::columnWidth( int col ) const
{
return topHeader->sectionSize( col );
}
int Table::rowHeight( int row ) const
{
return leftHeader->sectionSize( row );
}
int Table::columnPos( int col ) const
{
return topHeader->sectionPos( col );
}
int Table::rowPos( int row ) const
{
return leftHeader->sectionPos( row );
}
int Table::columnAt( int pos ) const
{
return topHeader->sectionAt( pos );
}
int Table::rowAt( int pos ) const
{
return leftHeader->sectionAt( pos );
}
QRect Table::cellGeometry( int row, int col ) const
{
return QRect( columnPos( col ), rowPos( row ),
columnWidth( col ), rowHeight( row ) );
}
QSize Table::tableSize() const
{
return QSize( columnPos( cols() - 1 ) + columnWidth( cols() - 1 ),
rowPos( rows() - 1 ) + rowHeight( rows() - 1 ) );
}
int Table::rows() const
{
return leftHeader->count();
}
int Table::cols() const
{
return topHeader->count();
}
/****************************************************************************
Helper functions for the in-place editor
*****************************************************************************/
void Table::editorOk()
{
if ( !editor )
return;
setCellText( curRow, curCol, editor->text() );
editorCancel();
}
void Table::editorCancel()
{
if ( !editor )
return;
removeChild( editor );
delete editor;
viewport()->setFocus();
editor = 0;
}
Main:
/****************************************************************************
** $Id: qt/examples/table/main.cpp 2.2.0-beta0 edited 2000-06-05 $
**
** Copyright (C) 1992-2000 Troll Tech AS. All rights reserved.
**
** This file is part of an example program for Qt. This example
** program may be used, distributed and modified without limitation.
**
*****************************************************************************/
#include "table.h"
#include <qpushbutton.h>
#include <qapplication.h>
/*
Constants
*/
const int numRows = 100; // Tablesize: number of rows
const int numCols = 100; // Tablesize: number of columns
/*
The program starts here.
*/
int main( int argc, char **argv )
{
QApplication a(argc,argv);
Table v( numRows, numCols );
a.setMainWidget( &v );
v.show();
return a.exec();
}
Copyright © 2000 Trolltech | Trademarks
| Qt version 2.2.0-beta0
|