From 9959879a877399c51b56e6aa9526e59cc84c0620 Mon Sep 17 00:00:00 2001 From: stv Date: Wed, 11 Oct 2006 12:29:28 +0000 Subject: [PATCH] Cell properties support Cell foreground and background colors. Rich text support in headers. Multiheaders support Horizontal span in headers. --- src/Qtx/QtxTable.cxx | 1074 ++++++++++++++++++++++++++++++++++++++++-- src/Qtx/QtxTable.h | 81 +++- 2 files changed, 1119 insertions(+), 36 deletions(-) diff --git a/src/Qtx/QtxTable.cxx b/src/Qtx/QtxTable.cxx index 657babab3..d2c6ca8ec 100644 --- a/src/Qtx/QtxTable.cxx +++ b/src/Qtx/QtxTable.cxx @@ -23,24 +23,472 @@ #ifndef QT_NO_TABLE +#include "QtxStyleWrap.h" + +#include +#include #include #include +#include +#include +#include +#include + +/*! + Class QtxTable::StyleItem +*/ + +class QtxTable::StyleItem : public QtxStyleWrapItem +{ +public: + StyleItem( QtxStyleWrap* ); + ~StyleItem(); + + virtual bool drawControl( QStyle::ControlElement, QPainter*, const QWidget*, const QRect&, + const QColorGroup&, QStyle::SFlags, const QStyleOption& ) const; +}; + +QtxTable::StyleItem::StyleItem( QtxStyleWrap* wrap ) +: QtxStyleWrapItem( wrap ) +{ +} + +QtxTable::StyleItem::~StyleItem() +{ +} + +bool QtxTable::StyleItem::drawControl( QStyle::ControlElement element, QPainter* p, const QWidget* widget, + const QRect& r, const QColorGroup& cg, QStyle::SFlags flags, + const QStyleOption& opt ) const +{ + if ( element != QStyle::CE_HeaderLabel ) + return false; + + const QHeader* header = (const QHeader*)widget; + int section = opt.headerSection(); + + QString lab = header->label( section ); + if ( !QStyleSheet::mightBeRichText( lab ) ) + return false; + + QRect rect = r; + QIconSet* icon = header->iconSet( section ); + if ( icon ) + { + QPixmap pixmap = icon->pixmap( QIconSet::Small, flags & QStyle::Style_Enabled ? QIconSet::Normal : + QIconSet::Disabled ); + int pixw = pixmap.width(); + int pixh = pixmap.height(); + + QRect pixRect = rect; + pixRect.setY( rect.center().y() - (pixh - 1) / 2 ); + if ( style() ) + style()->drawItem( p, pixRect, AlignVCenter, cg, flags & QStyle::Style_Enabled, &pixmap, QString::null ); + rect.setLeft( rect.left() + pixw + 2 ); + } + + QStyleSheet sheet; + QStyleSheetItem* i = sheet.item( "p" ); + if ( i ) + i->setMargin( QStyleSheetItem::MarginAll, 0 ); + + QSimpleRichText rt( lab, header->font(), QString::null, &sheet ); + rt.setWidth( rect.width() ); + rt.draw( p, rect.x(), rect.y() + ( rect.height() - rt.height() ) / 2, rect, cg ); + + return true; +} + +/*! + Class QtxTable::Header + Internal header for QtxTable +*/ + +class QtxTable::Header : public QHeader +{ +public: + Header( int, QtxTable*, const char* = 0 ); + virtual ~Header(); + + QtxTable* table() const; + + int horizontalSpan( const int ) const; + void setHorizontalSpan( const int, const int ); + + int verticalSpan( const int ) const; + void setVerticalSpan( const int, const int ); + + void removeSection( const int ); + virtual void setLabel( int, const QString&, int = -1 ); + + QRect indexRect( const int, int* = 0 ); + void swapSections( const int, const int ); + + virtual QSize sizeHint() const; + +protected: + virtual void mouseMoveEvent( QMouseEvent* ); + virtual void mousePressEvent( QMouseEvent* ); + virtual void mouseReleaseEvent( QMouseEvent* ); + virtual void paintSection( QPainter*, int, const QRect& ); + +private: + typedef QMap SpanMap; + typedef QPair SpanRange; + typedef QValueList SpanRangeList; + +private: + QHeader* mainHeader() const; + bool filterEvent( QMouseEvent* ) const; + + SpanRange findSpanRange( const int ) const; + void spanRanges( SpanRangeList& ) const; + + QPoint sectionCenter( const int, const QPoint& = QPoint() ) const; + +private: + SpanMap myHSpan; + SpanMap myVSpan; + QtxTable* myTable; + int myPressed; +}; + +QtxTable::Header::Header( int n, QtxTable* table, const char* name ) +: QHeader( n, table, name ), +myTable( table ), +myPressed( -1 ) +{ +} + +QtxTable::Header::~Header() +{ +} + +QtxTable* QtxTable::Header::table() const +{ + return myTable; +} + +QSize QtxTable::Header::sizeHint() const +{ + QSize sz = QHeader::sizeHint(); + + int size = orientation() == Horizontal ? sz.height() : sz.width(); + + for ( int i = 0; i < (int)count(); i++ ) + { + QString lab = label( mapToSection( i ) ); + if ( !QStyleSheet::mightBeRichText( lab ) ) + continue; + + int s = 0; + QIconSet* icon = iconSet( mapToSection( i ) ); + if ( icon ) + { + QPixmap pixmap = icon->pixmap( QIconSet::Small, QIconSet::Normal ); + s = orientation() == Horizontal ? pixmap.height() : pixmap.width(); + } + + QStyleSheet sheet; + QStyleSheetItem* item = sheet.item( "p" ); + if ( item ) + item->setMargin( QStyleSheetItem::MarginAll, 0 ); + + QSimpleRichText rt( lab, font(), QString::null, &sheet ); + if ( orientation() == Horizontal ) + rt.setWidth( sectionSize( mapToSection( i ) ) ); + s += orientation() == Horizontal ? rt.height() : rt.width(); + + size = QMAX( size, s ); + } + + if ( orientation() == Horizontal ) + sz.setHeight( size ); + else + sz.setWidth( size ); + + return sz; +} + +void QtxTable::Header::removeSection( const int section ) +{ + int old = count(); + + removeLabel( section ); + + if ( old == count() ) + return; + + SpanMap newHMap, newVMap; + for ( SpanMap::ConstIterator hIt = myHSpan.begin(); hIt != myHSpan.end(); ++hIt ) + { + if ( hIt.key() == section ) + continue; + newHMap.insert( hIt.key() < section ? hIt.key() : hIt.key() - 1, hIt.data() ); + } + myHSpan = newHMap; + + for ( SpanMap::ConstIterator vIt = myVSpan.begin(); vIt != myVSpan.end(); ++vIt ) + { + if ( vIt.key() == section ) + continue; + newVMap.insert( vIt.key() < section ? vIt.key() : vIt.key() - 1, vIt.data() ); + } + myVSpan = newVMap; +} + +void QtxTable::Header::setLabel( int section, const QString& s, int size ) +{ + QHeader::setLabel( section, s, size ); + if ( table() && isUpdatesEnabled() ) + table()->updateHeaderGeometries( orientation() ); +} + +int QtxTable::Header::horizontalSpan( const int section ) const +{ + return myHSpan.contains( section ) ? myHSpan[section] : 1; +} + +void QtxTable::Header::setHorizontalSpan( const int section, const int sp ) +{ + if ( horizontalSpan( section ) == sp ) + return; + + myHSpan.insert( section, sp ); + repaint( indexRect( mapToIndex( section ) ) ); +} + +int QtxTable::Header::verticalSpan( const int section ) const +{ + return myVSpan.contains( section ) ? myVSpan[section] : 1; +} + +void QtxTable::Header::setVerticalSpan( const int section, const int sp ) +{ + if ( verticalSpan( section ) == sp ) + return; + + myVSpan.insert( section, sp ); + repaint( indexRect( mapToIndex( section ) ) ); +} + +void QtxTable::Header::swapSections( const int oldIdx, const int newIdx ) +{ + QIconSet oldIconSet, newIconSet; + if ( iconSet( oldIdx ) ) + oldIconSet = *iconSet( oldIdx ); + if ( iconSet( newIdx ) ) + newIconSet = *iconSet( newIdx ); + QString oldLabel = label( oldIdx ); + QString newLabel = label( newIdx ); + QHeader::setLabel( oldIdx, newIconSet, newLabel ); + QHeader::setLabel( newIdx, oldIconSet, oldLabel ); + + int oldHSpan = horizontalSpan( oldIdx ); + int newHSpan = horizontalSpan( newIdx ); + setHorizontalSpan( oldIdx, newHSpan ); + setHorizontalSpan( newIdx, oldHSpan ); + + int oldVSpan = verticalSpan( oldIdx ); + int newVSpan = verticalSpan( newIdx ); + setVerticalSpan( oldIdx, newVSpan ); + setVerticalSpan( newIdx, oldVSpan ); +} + +void QtxTable::Header::mouseMoveEvent( QMouseEvent* e ) +{ + if ( e->button() == NoButton && filterEvent( e ) ) + return; + + QHeader::mouseMoveEvent( e ); + if ( mainHeader() ) + QApplication::sendEvent( mainHeader(), e ); +} + +void QtxTable::Header::mousePressEvent( QMouseEvent* e ) +{ + if ( filterEvent( e ) ) + return; + + int index = mapToIndex( sectionAt( ( orientation() == Horizontal ? e->pos().x() : e->pos().y() ) + offset() ) ); + SpanRange range = findSpanRange( index ); + + bool shift = e->state() & ShiftButton; + if ( mainHeader() ) + QApplication::sendEvent( mainHeader(), e ); + + for ( int i = range.second; i >= range.first && !shift; i-- ) + { + QPoint pos = sectionCenter( i, e->pos() ); + QMouseEvent me( e->type(), pos, e->button(), e->state() | ControlButton ); + if ( i == range.first ) + QHeader::mousePressEvent( &me ); + if ( mainHeader() ) + QApplication::sendEvent( mainHeader(), &me ); + } + + if ( !shift ) + { + QMouseEvent me( e->type(), sectionCenter( index, e->pos() ), e->button(), e->state() | ControlButton ); + if ( mainHeader() ) + QApplication::sendEvent( mainHeader(), &me ); + } + + myPressed = range.first; + repaint( indexRect( range.first ) ); +} + +void QtxTable::Header::mouseReleaseEvent( QMouseEvent* e ) +{ + QHeader::mouseReleaseEvent( e ); + if ( mainHeader() ) + QApplication::sendEvent( mainHeader(), e ); + + repaint( indexRect( myPressed ) ); + myPressed = -1; +} + +void QtxTable::Header::paintSection( QPainter* p, int index, const QRect& fr ) +{ + int idx = index; + QRect r = fr; + if ( index < count() ) + r = indexRect( index, &idx ); + QHeader::paintSection( p, idx, r ); +} + +bool QtxTable::Header::filterEvent( QMouseEvent* e ) const +{ + int c = orientation() == Horizontal ? e->pos().x() : e->pos().y(); + c += offset(); +/* + if ( reverse() ) + c = d->lastPos - c; +*/ + int section = sectionAt( c ); + if ( section < 0 ) + return false; + + int handleIdx = -1; + int margin = style().pixelMetric( QStyle::PM_HeaderGripMargin ); + int index = mapToIndex( section ); + + QRect r = sectionRect( section ); + int size = sectionSize( section ); + int pos = ( orientation() == Horizontal ? r.x() : r.y() ) + offset(); + if ( c < pos + margin ) + handleIdx = index - 1; + else if ( c > pos + size - margin ) + handleIdx = index; + + if ( handleIdx == -1 ) + return false; + + bool ok = false; + SpanRangeList ranges; + spanRanges( ranges ); + for ( SpanRangeList::const_iterator it = ranges.begin(); it != ranges.end() && !ok; ++it ) + ok = (*it).second == handleIdx; + + return !ok; +} + +QPoint QtxTable::Header::sectionCenter( const int index, const QPoint& p ) const +{ + QPoint pos = p; + if ( orientation() == Horizontal ) + pos.setX( sectionPos( mapToSection( index ) ) + sectionSize( mapToSection( index ) ) / 2 - offset() ); + else + pos.setY( sectionPos( mapToSection( index ) ) + sectionSize( mapToSection( index ) ) / 2 - offset() ); + + if ( p.isNull() ) + { + if ( orientation() == Horizontal ) + pos.setY( height() / 2 ); + else + pos.setX( width() / 2 ); + } + return pos; +} + +void QtxTable::Header::spanRanges( SpanRangeList& lst ) const +{ + lst.clear(); + for ( int i = 0; i < (int)count(); i++ ) + { + int sp = horizontalSpan( mapToSection( i ) ); + SpanRange range( i, QMIN( i + sp - 1, count() - 1 ) ); + lst.append( range ); + i += sp - 1; + } +} + +QtxTable::Header::SpanRange QtxTable::Header::findSpanRange( const int index ) const +{ + SpanRangeList ranges; + spanRanges( ranges ); + + SpanRange res( -1, -1 ); + for ( SpanRangeList::const_iterator it = ranges.begin(); it != ranges.end() && res.first < 0; ++it ) + { + if ( (*it).first <= index && index <= (*it).second ) + res = *it; + } + return res; +} + +QRect QtxTable::Header::indexRect( const int index, int* start ) +{ + SpanRange range = findSpanRange( index ); + + if ( start ) + *start = range.first; + + QRect r; + for ( int i = range.first; i <= range.second; i++ ) + r = r.unite( sRect( i ) ); + return r; +} + +QHeader* QtxTable::Header::mainHeader() const +{ + if ( !table() ) + return 0; + + return orientation() == Horizontal ? table()->horizontalHeader() : + table()->verticalHeader(); +} + +/*! + Class QtxTable +*/ /*! Constructor */ QtxTable::QtxTable( QWidget* parent, const char* name ) : QTable( parent, name ), +myStyleWrapper( 0 ), myHeaderEditor( 0 ), myEditedHeader( 0 ), -myEditedSection( -1 ) +myEditedSection( -1 ), +mySelectAll( 0 ) { + myVerHeaders.setAutoDelete( true ); + myHorHeaders.setAutoDelete( true ); + + verticalHeader()->setStyle( styleWrapper() ); + horizontalHeader()->setStyle( styleWrapper() ); + connect( verticalHeader(), SIGNAL( sizeChange( int, int, int ) ), this, SLOT( onHeaderSizeChange( int, int, int ) ) ); connect( horizontalHeader(), SIGNAL( sizeChange( int, int, int ) ), this, SLOT( onHeaderSizeChange( int, int, int ) ) ); connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ), this, SLOT( onScrollBarMoved( int ) ) ); connect( horizontalScrollBar(), SIGNAL( valueChanged( int ) ), this, SLOT( onScrollBarMoved( int ) ) ); + + updateHeaders( Horizontal ); + updateHeaders( Vertical ); } /*! @@ -48,10 +496,18 @@ myEditedSection( -1 ) */ QtxTable::QtxTable( int numRows, int numCols, QWidget* parent, const char* name ) : QTable( numRows, numCols, parent, name ), +myStyleWrapper( 0 ), myHeaderEditor( 0 ), myEditedHeader( 0 ), -myEditedSection( -1 ) +myEditedSection( -1 ), +mySelectAll( 0 ) { + myVerHeaders.setAutoDelete( true ); + myHorHeaders.setAutoDelete( true ); + + verticalHeader()->setStyle( styleWrapper() ); + horizontalHeader()->setStyle( styleWrapper() ); + connect( verticalHeader(), SIGNAL( sizeChange( int, int, int ) ), this, SLOT( onHeaderSizeChange( int, int, int ) ) ); connect( horizontalHeader(), SIGNAL( sizeChange( int, int, int ) ), @@ -65,6 +521,65 @@ myEditedSection( -1 ) */ QtxTable::~QtxTable() { + delete myStyleWrapper; +} + +bool QtxTable::isSelectAllEnabled() const +{ + return mySelectAll; +} + +void QtxTable::setSelectAllEnabled( const bool on ) +{ + if ( isSelectAllEnabled() == on ) + return; + + if ( on ) + connect( mySelectAll = new QToolButton( this ), SIGNAL( clicked() ), this, SLOT( selectAll() ) ); + else + { + delete mySelectAll; + mySelectAll = 0; + } + updateSelectAllButton(); +} + +/*! + Select the all cells of the table. +*/ +void QtxTable::selectAll() +{ + if ( selectionMode() != Multi && selectionMode() != MultiRow ) + return; + + selectCells( 0, 0, numRows() - 1, numCols() - 1 ); +} + +/*! + Reimplemented for 'SelectAll' button geometry updating. +*/ +void QtxTable::setTopMargin( int m ) +{ + QTable::setTopMargin( m ); + updateSelectAllButton(); +} + +/*! + Reimplemented for 'SelectAll' button geometry updating. +*/ +void QtxTable::setLeftMargin( int m ) +{ + QTable::setLeftMargin( m ); + updateSelectAllButton(); +} + +/*! + Reimplemented for 'SelectAll' button updating. +*/ +void QtxTable::setSelectionMode( SelectionMode mode ) +{ + QTable::setSelectionMode( mode ); + updateSelectAllButton(); } /*! @@ -128,14 +643,10 @@ bool QtxTable::eventFilter( QObject* o, QEvent* e ) if ( e->type() == QEvent::MouseButtonDblClick ) { QMouseEvent* me = (QMouseEvent*)e; - if ( o == horizontalHeader() ) - { - beginHeaderEdit( Horizontal, me->pos() ); - return true; - } - else if ( o == verticalHeader() ) + QHeader* hdr = ::qt_cast( o ); + if ( hdr ) { - beginHeaderEdit( Vertical, me->pos() ); + beginHeaderEdit( hdr, me->pos() ); return true; } } @@ -149,7 +660,7 @@ bool QtxTable::eventFilter( QObject* o, QEvent* e ) return true; } - if ( ke->key() == Key_Return || ke->key() == Key_Enter ) + if ( ke->state() == NoButton && ( ke->key() == Key_Return || ke->key() == Key_Enter ) ) { endHeaderEdit( true ); return true; @@ -178,7 +689,21 @@ bool QtxTable::eventFilter( QObject* o, QEvent* e ) void QtxTable::setNumRows( int rows ) { endHeaderEdit(); + int old = numRows(); + QTable::setNumRows( rows ); + + for ( uint i = 0; i < myVerHeaders.count(); i++ ) + { + Header* hdr = (Header*)myVerHeaders.at( i ); + for ( int d = hdr->count() - 1; d >= (int)numRows(); d-- ) + hdr->removeSection( d ); + for ( int c = hdr->count(); c < (int)numRows(); c++ ) + hdr->addLabel( QString::null ); + } + + for ( int j = rows + 1; j <= old; j++ ) + myCellProps.remove( j ); } /*! @@ -188,7 +713,25 @@ void QtxTable::setNumRows( int rows ) void QtxTable::setNumCols( int cols ) { endHeaderEdit(); + int old = numCols(); + QTable::setNumCols( cols ); + + for ( uint i = 0; i < myHorHeaders.count(); i++ ) + { + Header* hdr = (Header*)myHorHeaders.at( i ); + for ( int d = hdr->count() - 1; d >= (int)numCols(); d-- ) + hdr->removeSection( d ); + for ( int c = hdr->count(); c < (int)numCols(); c++ ) + hdr->addLabel( QString::null ); + } + + for ( CellMap::Iterator it = myCellProps.begin(); it != myCellProps.end(); ++it ) + { + PropsMap& map = it.data(); + for ( int i = cols + 1; i <= old; i++ ) + map.remove( i ); + } } /*! @@ -198,7 +741,23 @@ void QtxTable::setNumCols( int cols ) void QtxTable::insertRows( int row, int count ) { endHeaderEdit(); + QTable::insertRows( row, count ); + + for ( uint i = 0; i < myVerHeaders.count(); i++ ) + { + Header* hdr = (Header*)myVerHeaders.at( i ); + for ( int j = numRows() - count - 1; j >= row; --j ) + hdr->swapSections( j, j + count ); + } + + CellMap tmp = myCellProps; + myCellProps.clear(); + for ( CellMap::Iterator it = tmp.begin(); it != tmp.end(); ++it ) + { + int r = it.key() < row ? it.key() : it.key() + count; + myCellProps.insert( r, it.data() ); + } } /*! @@ -208,7 +767,26 @@ void QtxTable::insertRows( int row, int count ) void QtxTable::insertColumns( int col, int count ) { endHeaderEdit(); + QTable::insertColumns( col, count ); + + for ( uint i = 0; i < myHorHeaders.count(); i++ ) + { + Header* hdr = (Header*)myHorHeaders.at( i ); + for ( int j = numCols() - count - 1; j >= col; --j ) + hdr->swapSections( j, j + count ); + } + + for ( CellMap::Iterator itr = myCellProps.begin(); itr != myCellProps.end(); ++itr ) + { + PropsMap tmp = itr.data(); + itr.data().clear(); + for ( PropsMap::Iterator it = tmp.begin(); it != tmp.end(); ++it ) + { + int c = it.key() < col ? it.key() : it.key() + count; + itr.data().insert( c, it.data() ); + } + } } /*! @@ -218,7 +796,25 @@ void QtxTable::insertColumns( int col, int count ) void QtxTable::removeRow( int row ) { endHeaderEdit(); + QTable::removeRow( row ); + + for ( uint i = 0; i < myVerHeaders.count(); i++ ) + { + Header* hdr = (Header*)myVerHeaders.at( i ); + hdr->removeSection( row ); + } + + CellMap tmp = myCellProps; + myCellProps.clear(); + for ( CellMap::Iterator it = tmp.begin(); it != tmp.end(); ++it ) + { + if ( it.key() == row ) + continue; + + int r = it.key() < row ? it.key() : it.key() - 1; + myCellProps.insert( r, it.data() ); + } } /*! @@ -228,7 +824,33 @@ void QtxTable::removeRow( int row ) void QtxTable::removeRows( const QMemArray& rows ) { endHeaderEdit(); + + int decr = 0; + QMap indexes; + for ( int r = 0; r < numRows(); r++ ) + { + if ( rows.contains( r ) ) + decr++; + else + indexes.insert( r, r - decr ); + } + QTable::removeRows( rows ); + + for ( uint i = 0; i < myVerHeaders.count(); i++ ) + { + Header* hdr = (Header*)myVerHeaders.at( i ); + for ( int d = rows.count() - 1; d >= 0; d-- ) + hdr->removeSection( rows[d] ); + } + + CellMap tmp = myCellProps; + myCellProps.clear(); + for ( CellMap::Iterator it = tmp.begin(); it != tmp.end(); ++it ) + { + if ( indexes.contains( it.key() ) ) + myCellProps.insert( indexes[it.key()], it.data() ); + } } /*! @@ -238,7 +860,28 @@ void QtxTable::removeRows( const QMemArray& rows ) void QtxTable::removeColumn( int col ) { endHeaderEdit(); + QTable::removeColumn( col ); + + for ( uint i = 0; i < myHorHeaders.count(); i++ ) + { + Header* hdr = (Header*)myHorHeaders.at( i ); + hdr->removeSection( col ); + } + + for ( CellMap::Iterator itr = myCellProps.begin(); itr != myCellProps.end(); ++itr ) + { + PropsMap tmp = itr.data(); + itr.data().clear(); + for ( PropsMap::Iterator it = tmp.begin(); it != tmp.end(); ++it ) + { + if ( it.key() == col ) + continue; + + int c = it.key() < col ? it.key() : it.key() - 1; + itr.data().insert( c, it.data() ); + } + } } /*! @@ -248,7 +891,137 @@ void QtxTable::removeColumn( int col ) void QtxTable::removeColumns( const QMemArray& cols ) { endHeaderEdit(); + + int decr = 0; + QMap indexes; + for ( int c = 0; c < numCols(); c++ ) + { + if ( cols.contains( c ) ) + decr++; + else + indexes.insert( c, c - decr ); + } + QTable::removeColumns( cols ); + + for ( uint i = 0; i < myHorHeaders.count(); i++ ) + { + Header* hdr = (Header*)myHorHeaders.at( i ); + for ( int d = cols.count() - 1; d >= 0; d-- ) + hdr->removeSection( cols[d] ); + } + + for ( CellMap::Iterator itr = myCellProps.begin(); itr != myCellProps.end(); ++itr ) + { + PropsMap tmp = itr.data(); + itr.data().clear(); + for ( PropsMap::Iterator it = tmp.begin(); it != tmp.end(); ++it ) + { + if ( indexes.contains( it.key() ) ) + itr.data().insert( indexes[it.key()], it.data() ); + } + } +} + +QHeader* QtxTable::header( const Orientation o, const int idx ) const +{ + HeaderVector* vec = headerVector( o ); + + if ( idx < 0 || idx >= (int)vec->count() ) + return 0; + + return vec->at( idx ); +} + +int QtxTable::numHeaders( const Orientation o ) const +{ + return headerVector( o )->count(); +} + +void QtxTable::setNumHeaders( const Orientation o, const int n ) +{ + headerVector( o )->resize( QMAX( n, 0 ) ); + + updateHeaders( o ); + updateHeaderSizes( o ); + updateHeaderSpace( o ); + + updateGeometries(); +} + +int QtxTable::verticalSpan( const Orientation o, const int idx, const int section ) const +{ + Header* hdr = (Header*)header( o, idx ); + if ( !hdr ) + return -1; + + return hdr->verticalSpan( section ); +} + +int QtxTable::horizontalSpan( const Orientation o, const int idx, const int section ) const +{ + Header* hdr = (Header*)header( o, idx ); + if ( !hdr ) + return -1; + + return hdr->horizontalSpan( section ); +} + +void QtxTable::setVerticalSpan( const Orientation o, const int idx, const int section, const int span ) +{ + Header* hdr = (Header*)header( o, idx ); + if ( hdr ) + hdr->setVerticalSpan( section, span ); +} + +void QtxTable::setHorizontalSpan( const Orientation o, const int idx, const int section, const int span ) +{ + Header* hdr = (Header*)header( o, idx ); + if ( hdr ) + hdr->setHorizontalSpan( section, span ); +} + +QColor QtxTable::cellForegroundColor( const int row, const int col ) const +{ + QColor res; + QVariant val = cellProperty( row, col, FgColor ); + if ( val.canCast( QVariant::Color ) ) + res = val.toColor(); + return res; +} + +QColor QtxTable::cellBackgroundColor( const int row, const int col ) const +{ + QColor res; + QVariant val = cellProperty( row, col, BgColor ); + if ( val.canCast( QVariant::Color ) ) + res = val.toColor(); + return res; +} + +void QtxTable::setCellForegroundColor( const int row, const int col, const QColor& c ) +{ + setCellProperty( row, col, FgColor, c ); +} + +void QtxTable::setCellBackgroundColor( const int row, const int col, const QColor& c ) +{ + setCellProperty( row, col, BgColor, c ); +} + +void QtxTable::paintCell( QPainter* p, int row, int col, const QRect& cr, + bool selected, const QColorGroup& cg ) +{ + QColor fg = cellForegroundColor( row, col ); + QColor bg = cellBackgroundColor( row, col ); + + QColorGroup cGroup = cg; + if ( fg.isValid() ) + cGroup.setColor( QColorGroup::Text, fg ); + if ( bg.isValid() ) + cGroup.setColor( QColorGroup::Base, bg ); + + QTable::paintCell( p, row, col, cr, selected, cGroup ); } /*! @@ -262,9 +1035,20 @@ void QtxTable::onScrollBarMoved( int ) /*! SLOT: called on header size changing */ -void QtxTable::onHeaderSizeChange( int, int, int ) +void QtxTable::onHeaderSizeChange( int section, int oldSize, int newSize ) { - if ( sender() == myEditedHeader ) + const QHeader* header = ::qt_cast( sender() ); + if ( !header ) + return; + + HeaderVector* vec = headerVector( header->orientation() ); + for ( int i = 0; i < (int)vec->size(); i++ ) + { + QHeader* hdr = vec->at( i ); + hdr->resizeSection( section, newSize ); + } + + if ( header == myEditedHeader ) updateHeaderEditor(); } @@ -286,6 +1070,54 @@ void QtxTable::resizeEvent( QResizeEvent* e ) QTable::resizeEvent( e ); updateHeaderEditor(); + + updateGeometries(); +} + +/*! + Returns the specified property of the cell. + \param row - row number of cell + \param col - column number of cell + \param name - property name +*/ +QVariant QtxTable::cellProperty( const int row, const int col, const int name ) const +{ + QVariant res; + + if ( myCellProps.contains( row ) ) + { + const PropsMap& map = myCellProps[row]; + if ( map.contains( col ) ) + { + const Properties& props = map[col]; + if ( props.contains( name ) ) + res = props[name]; + } + } + return res; +} + +/*! + Sets the specified property value of the cell. + \param row - row number of cell + \param col - column number of cell + \param name - property name + \param val - setted property value +*/ +void QtxTable::setCellProperty( const int row, const int col, const int name, const QVariant& val ) +{ + if ( row < 0 || row >= numRows() || col < 0 || col >= numCols() ) + return; + + if ( !myCellProps.contains( row ) ) + myCellProps.insert( row, PropsMap() ); + + PropsMap& map = myCellProps[row]; + if ( !map.contains( col ) ) + map.insert( col, Properties() ); + + Properties& props = map[col]; + props.insert( name, val ); } /*! @@ -293,20 +1125,30 @@ void QtxTable::resizeEvent( QResizeEvent* e ) \param o - header orientation \param sec - column/row */ -bool QtxTable::beginHeaderEdit( Orientation o, const int section ) +bool QtxTable::beginHeaderEdit( Orientation o, const int section, const int idx ) { - if ( !headerEditable( o ) || !header( o ) || !header( o )->isVisibleTo( this ) ) + QHeader* hdr = 0; + if ( idx < 0 ) + hdr = header( o ); + if ( !hdr->isVisibleTo( this ) ) + hdr = header( o, idx ); + + return beginHeaderEdit( hdr, section ); +} + +bool QtxTable::beginHeaderEdit( QHeader* hdr, const int s ) +{ + if ( !hdr || !headerEditable( hdr->orientation() ) || !hdr->isVisibleTo( this ) ) return false; endHeaderEdit(); - QHeader* hdr = header( o ); - - QRect r = headerSectionRect( hdr, section ); + int section = s; + QRect r = headerSectionRect( hdr, s, §ion ); if ( !r.isValid() ) return false; - if ( o == Horizontal ) + if ( hdr->orientation() == Horizontal ) r.setLeft( QMAX( r.left(), leftMargin() ) ); else r.setTop( QMAX( r.top(), topMargin() ) ); @@ -382,12 +1224,31 @@ bool QtxTable::isHeaderEditing() const */ QWidget* QtxTable::createHeaderEditor( QHeader* hdr, const int sec, const bool init ) { - QLineEdit* ed = new QLineEdit( 0 ); + QString lab = hdr ? hdr->label( sec ) : QString::null; - if ( init && hdr ) - ed->setText( hdr->label( sec ) ); + QWidget* wid = 0; + if ( !QStyleSheet::mightBeRichText( lab ) && !lab.contains( "\n" ) ) + { + QLineEdit* le = new QLineEdit( 0 ); + if ( init ) + le->setText( lab ); + wid = le; + } + else + { + QTextEdit* te = new QTextEdit( 0 ); + QStyleSheet* sheet = new QStyleSheet( te ); + QStyleSheetItem* i = sheet->item( "p" ); + if ( i ) + i->setMargin( QStyleSheetItem::MarginAll, 0 ); + te->setStyleSheet( sheet ); + + if ( init ) + te->setText( lab ); + wid = te; + } - return ed; + return wid; } /*! @@ -401,8 +1262,15 @@ void QtxTable::setHeaderContentFromEditor( QHeader* hdr, const int sec, QWidget* if ( !hdr || !editor ) return; - if ( editor->inherits( "QLineEdit" ) ) - hdr->setLabel( sec, ((QLineEdit*)editor)->text() ); + QLineEdit* le = ::qt_cast( editor ); + if ( le ) + hdr->setLabel( sec, le->text() ); + else + { + QTextEdit* te = ::qt_cast( editor ); + if ( te ) + hdr->setLabel( sec, te->text() ); + } } /*! @@ -419,16 +1287,15 @@ QHeader* QtxTable::header( Orientation o ) const \param o - header orientation \param p - point */ -void QtxTable::beginHeaderEdit( Orientation o, const QPoint& p ) +void QtxTable::beginHeaderEdit( QHeader* hdr, const QPoint& p ) { - QHeader* hdr = header( o ); if ( !hdr ) return; - int pos = o == Horizontal ? p.x() : p.y(); + int pos = hdr->orientation() == Horizontal ? p.x() : p.y(); int sec = hdr->sectionAt( hdr->offset() + pos ); - beginHeaderEdit( o, sec ); + beginHeaderEdit( hdr, sec ); } /*! @@ -436,7 +1303,7 @@ void QtxTable::beginHeaderEdit( Orientation o, const QPoint& p ) \param hdr - header \param sec - column/row */ -QRect QtxTable::headerSectionRect( QHeader* hdr, const int sec ) const +QRect QtxTable::headerSectionRect( QHeader* hdr, const int sec, int* main ) const { QRect r( -1, -1, -1, -1 ); @@ -444,6 +1311,9 @@ QRect QtxTable::headerSectionRect( QHeader* hdr, const int sec ) const return r; r = hdr->sectionRect( sec ); + if ( hdr != verticalHeader() && hdr != horizontalHeader() ) + r = ((Header*)hdr)->indexRect( hdr->mapToIndex( sec ), main ); + if ( r.isValid() ) r = QRect( mapFromGlobal( hdr->mapToGlobal( r.topLeft() ) ), r.size() ); @@ -462,7 +1332,7 @@ void QtxTable::updateHeaderEditor() if ( !r.isValid() ) return; - if ( myEditedHeader == horizontalHeader() ) + if ( myEditedHeader->orientation() == Horizontal ) { r.setLeft( QMAX( r.left(), leftMargin() ) ); r.setRight( QMIN( r.right(), width() - rightMargin() - 2 ) ); @@ -508,9 +1378,149 @@ void QtxTable::removeSelected( const bool row ) idsArr[ i ] = *it; if ( row ) - removeRows ( idsArr ); + removeRows( idsArr ); else - removeColumns ( idsArr ); + removeColumns( idsArr ); +} + +QtxTable::HeaderVector* QtxTable::headerVector( const Orientation o ) const +{ + return o == Horizontal ? (HeaderVector*)&myHorHeaders : (HeaderVector*)&myVerHeaders; +} + +QtxStyleWrap* QtxTable::styleWrapper() +{ + if ( !myStyleWrapper ) + { + myStyleWrapper = new QtxStyleWrap( &style() ); + new StyleItem( myStyleWrapper ); + } + return myStyleWrapper; +} + +void QtxTable::updateHeaders( const Orientation o ) +{ + HeaderVector& vec = o == Vertical ? myVerHeaders : myHorHeaders; + QScrollBar* sb = o == Vertical ? verticalScrollBar() : horizontalScrollBar(); + QHeader* main = o == Vertical ? verticalHeader() : horizontalHeader(); + + main->setShown( !vec.size() ); + + for ( uint i = 0; i < vec.size(); i++ ) + { + if ( vec.at( i ) ) + continue; + + QHeader* hdr = new Header( o == Horizontal ? numCols() : numRows(), this ); + vec.insert( i, hdr ); + hdr->setStyle( styleWrapper() ); + hdr->setMovingEnabled( false ); + hdr->setOrientation( o ); + hdr->installEventFilter( this ); + hdr->show(); + + connect( sb, SIGNAL( valueChanged( int ) ), hdr, SLOT( setOffset( int ) ) ); + } + + if ( o == Horizontal ) + { + for ( uint j = 0; j < vec.size(); j++ ) + { + QHeader* hdr = vec.at( j ); + bool upd = hdr->isUpdatesEnabled(); + hdr->setUpdatesEnabled( false ); + for ( int s = 0; s < (int)hdr->count(); s++ ) + hdr->setLabel( s, QString( "" ) ); + hdr->setUpdatesEnabled( upd ); + } + } + + // slot onScrollBarMoved should be last connection. + // QHeader::setOffset() should be invoked before it for every header. + // Otherwise header editor geometry will not properly updated. + disconnect( verticalScrollBar(), SIGNAL( valueChanged( int ) ), this, SLOT( onScrollBarMoved( int ) ) ); + disconnect( horizontalScrollBar(), SIGNAL( valueChanged( int ) ), this, SLOT( onScrollBarMoved( int ) ) ); + connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ), this, SLOT( onScrollBarMoved( int ) ) ); + connect( horizontalScrollBar(), SIGNAL( valueChanged( int ) ), this, SLOT( onScrollBarMoved( int ) ) ); +} + +void QtxTable::updateHeaderSpace( const Orientation o ) +{ + HeaderVector& vec = o == Vertical ? myVerHeaders : myHorHeaders; + + int size = 0; + for ( uint i = 0; i < vec.size(); i++ ) + { + QHeader* hdr = vec.at( i ); + int sz = o == Vertical ? hdr->width() : hdr->height(); + if ( o == Vertical ) + hdr->move( size, 0 ); + else + hdr->move( 0, size ); + size += sz; + } + + if ( vec.size() == 0 ) + size = o == Vertical ? verticalHeader()->width() : horizontalHeader()->height(); + + if ( o == Vertical ) + setLeftMargin( size ); + else + setTopMargin( size ); +} + +void QtxTable::updateHeaderSizes( const Orientation o ) +{ + HeaderVector& vec = o == Vertical ? myVerHeaders : myHorHeaders; + + for ( uint j = 0; j < vec.count(); j++ ) + { + QHeader* hdr = vec.at( j ); + for ( int s = 0; s < (int)hdr->count(); s++ ) + hdr->resizeSection( s, o == Vertical ? rowHeight( s ) : columnWidth( s ) ); + if ( o == Vertical ) + hdr->resize( hdr->sizeHint().width(), hdr->height() ); + else + hdr->resize( hdr->width(), hdr->sizeHint().height() ); + } +} + +void QtxTable::updateGeometries() +{ + QSize sz = horizontalHeader()->size(); + int size = frameWidth(); + for ( int h = 0; h < (int)myHorHeaders.size(); h++ ) + { + QHeader* hdr = myHorHeaders.at( h ); + hdr->setGeometry( frameWidth() + leftMargin(), size, sz.width(), hdr->height() ); + size += hdr->height(); + } + + sz = verticalHeader()->size(); + size = frameWidth(); + for ( int v = 0; v < (int)myVerHeaders.size(); v++ ) + { + QHeader* hdr = myVerHeaders.at( v ); + hdr->setGeometry( size, frameWidth() + topMargin(), hdr->width(), sz.height() ); + size += hdr->width(); + } +} + +void QtxTable::updateHeaderGeometries( const Orientation o ) +{ + updateHeaderSizes( o ); + updateHeaderSpace( o ); + updateGeometries(); +} + +void QtxTable::updateSelectAllButton() +{ + if ( !mySelectAll ) + return; + + mySelectAll->setShown( selectionMode() == Multi || selectionMode() == MultiRow ); + if ( mySelectAll->isVisibleTo( this ) ) + mySelectAll->setGeometry( frameWidth(), frameWidth(), leftMargin(), topMargin() ); } #endif diff --git a/src/Qtx/QtxTable.h b/src/Qtx/QtxTable.h index dd38208c7..ea786562b 100644 --- a/src/Qtx/QtxTable.h +++ b/src/Qtx/QtxTable.h @@ -24,17 +24,32 @@ #include "Qtx.h" +#include #include +#include +#include #ifndef QT_NO_TABLE class QHeader; +class QToolButton; +class QtxStyleWrap; + +#ifdef WIN32 +#pragma warning( disable : 4251 ) +#endif class QTX_EXPORT QtxTable : public QTable { Q_OBJECT - class HeaderEditor; + Q_PROPERTY( bool selectAllEnabled READ isSelectAllEnabled WRITE setSelectAllEnabled ) + + class Header; + class StyleItem; + +protected: + enum { FgColor, BgColor, User }; public: QtxTable( QWidget* = 0, const char* = 0 ); @@ -46,6 +61,8 @@ public: bool editHeader( Orientation, const int ); void endEditHeader( const bool = true ); + virtual void setSelectionMode( SelectionMode ); + void removeSelected( const bool row = true ); virtual bool eventFilter( QObject*, QEvent* ); @@ -53,11 +70,33 @@ public: virtual void setNumRows( int ); virtual void setNumCols( int ); + bool isSelectAllEnabled() const; + virtual void setSelectAllEnabled( const bool ); + + QHeader* header( const Orientation, const int ) const; + int numHeaders( const Orientation ) const; + void setNumHeaders( const Orientation, const int ); + + int verticalSpan( const Orientation, const int, const int ) const; + int horizontalSpan( const Orientation, const int, const int ) const; + void setVerticalSpan( const Orientation, const int, const int, const int ); + void setHorizontalSpan( const Orientation, const int, const int, const int ); + + QColor cellForegroundColor( const int, const int ) const; + QColor cellBackgroundColor( const int, const int ) const; + void setCellForegroundColor( const int, const int, const QColor& ); + void setCellBackgroundColor( const int, const int, const QColor& ); + + virtual void paintCell( QPainter*, int, int, const QRect&, bool, const QColorGroup& ); + signals: void headerEdited( QHeader*, int ); void headerEdited( Orientation, int ); public slots: + virtual void selectAll(); + virtual void setTopMargin( int ); + virtual void setLeftMargin( int ); virtual void setHeaderEditable( Orientation, bool ); virtual void insertRows( int, int = 1 ); @@ -75,26 +114,60 @@ protected: virtual void hideEvent( QHideEvent* ); virtual void resizeEvent( QResizeEvent* ); - virtual bool beginHeaderEdit( Orientation, const int ); + virtual bool beginHeaderEdit( Orientation, const int, const int = -1 ); virtual void endHeaderEdit( const bool = true ); bool isHeaderEditing() const; virtual QWidget* createHeaderEditor( QHeader*, const int, const bool = true ); virtual void setHeaderContentFromEditor( QHeader*, const int, QWidget* ); QHeader* header( Orientation o ) const; + virtual QRect headerSectionRect( QHeader*, const int, int* = 0 ) const; + + QVariant cellProperty( const int, const int, const int ) const; + void setCellProperty( const int, const int, const int, const QVariant& ); + +private: + typedef QMap Properties; + typedef QMap PropsMap; + typedef QMap CellMap; + typedef QPtrVector HeaderVector; private: void updateHeaderEditor(); - void beginHeaderEdit( Orientation, const QPoint& ); - QRect headerSectionRect( QHeader*, const int ) const; + bool beginHeaderEdit( QHeader*, const int ); + void beginHeaderEdit( QHeader*, const QPoint& ); + + QtxStyleWrap* styleWrapper(); + HeaderVector* headerVector( const Orientation ) const; + + void updateGeometries(); + void updateSelectAllButton(); + + void updateHeaders( const Orientation ); + void updateHeaderSizes( const Orientation ); + void updateHeaderSpace( const Orientation ); + void updateHeaderGeometries( const Orientation ); private: + CellMap myCellProps; + HeaderVector myVerHeaders; + HeaderVector myHorHeaders; + QtxStyleWrap* myStyleWrapper; + QWidget* myHeaderEditor; QHeader* myEditedHeader; int myEditedSection; QMap myHeaderEditable; + + QToolButton* mySelectAll; + + friend class QtxTable::Header; }; +#ifdef WIN32 +#pragma warning( default: 4251 ) +#endif + #endif #endif -- 2.39.2