diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bd9e6617827818fd043452c08c606f07b78014a0 (patch) | |
tree | 425bb4c3168f9c02f10150f235d2cb998dcc6108 /cervisia/qttableview.cpp | |
download | tdesdk-bd9e6617827818fd043452c08c606f07b78014a0.tar.gz tdesdk-bd9e6617827818fd043452c08c606f07b78014a0.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdesdk@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'cervisia/qttableview.cpp')
-rw-r--r-- | cervisia/qttableview.cpp | 2278 |
1 files changed, 2278 insertions, 0 deletions
diff --git a/cervisia/qttableview.cpp b/cervisia/qttableview.cpp new file mode 100644 index 00000000..22b2993c --- /dev/null +++ b/cervisia/qttableview.cpp @@ -0,0 +1,2278 @@ +/********************************************************************** +** $Id$ +** +** Implementation of QtTableView class +** +** Created : 941115 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file contains a class moved out of the Qt GUI Toolkit API. It +** may be used, distributed and modified without limitation. +** +**********************************************************************/ + +#include "qttableview.h" +#include "qscrollbar.h" +#include "qpainter.h" +#include "qdrawutil.h" +#include <qapplication.h> +#include <limits.h> + +enum ScrollBarDirtyFlags { + verGeometry = 0x01, + verSteps = 0x02, + verRange = 0x04, + verValue = 0x08, + horGeometry = 0x10, + horSteps = 0x20, + horRange = 0x40, + horValue = 0x80, + verMask = 0x0F, + horMask = 0xF0 +}; + + +#define HSBEXT horizontalScrollBar()->sizeHint().height() +#define VSBEXT verticalScrollBar()->sizeHint().width() + + +class QCornerSquare : public QWidget // internal class +{ +public: + QCornerSquare( QWidget *, const char* = 0 ); + void paintEvent( QPaintEvent * ); +}; + +QCornerSquare::QCornerSquare( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ +} + +void QCornerSquare::paintEvent( QPaintEvent * ) +{ +} + + +// NOT REVISED +/*! + \class QtTableView qttableview.h + \brief The QtTableView class provides an abstract base for tables. + + \obsolete + + A table view consists of a number of abstract cells organized in rows + and columns, and a visible part called a view. The cells are identified + with a row index and a column index. The top-left cell is in row 0, + column 0. + + The behavior of the widget can be finely tuned using + setTableFlags(); a typical subclass will consist of little more than a + call to setTableFlags(), some table content manipulation and an + implementation of paintCell(). Subclasses that need cells with + variable width or height must reimplement cellHeight() and/or + cellWidth(). Use updateTableSize() to tell QtTableView when the + width or height has changed. + + When you read this documentation, it is important to understand the + distinctions among the four pixel coordinate systems involved. + + \list 1 + \i The \e cell coordinates. (0,0) is the top-left corner of a cell. + Cell coordinates are used by functions such as paintCell(). + + \i The \e table coordinates. (0,0) is the top-left corner of the cell at + row 0 and column 0. These coordinates are absolute; that is, they are + independent of what part of the table is visible at the moment. They are + used by functions such as setXOffset() or maxYOffset(). + + \i The \e widget coordinates. (0,0) is the top-left corner of the widget, + \e including the frame. They are used by functions such as repaint(). + + \i The \e view coordinates. (0,0) is the top-left corner of the view, \e + excluding the frame. This is the least-used coordinate system; it is used by + functions such as viewWidth(). \endlist + + It is rather unfortunate that we have to use four different + coordinate systems, but there was no alternative to provide a flexible and + powerful base class. + + Note: The row,column indices are always given in that order, + i.e., first the vertical (row), then the horizontal (column). This is + the opposite order of all pixel operations, which take first the + horizontal (x) and then the vertical (y). + + <img src=qtablevw-m.png> <img src=qtablevw-w.png> + + \warning the functions setNumRows(), setNumCols(), setCellHeight(), + setCellWidth(), setTableFlags() and clearTableFlags() may cause + virtual functions such as cellWidth() and cellHeight() to be called, + even if autoUpdate() is FALSE. This may cause errors if relevant + state variables are not initialized. + + \warning Experience has shown that use of this widget tends to cause + more bugs than expected and our analysis indicates that the widget's + very flexibility is the problem. If QScrollView or QListBox can + easily be made to do the job you need, we recommend subclassing + those widgets rather than QtTableView. In addition, QScrollView makes + it easy to have child widgets inside tables, which QtTableView + doesn't support at all. + + \sa QScrollView + \link guibooks.html#fowler GUI Design Handbook: Table\endlink +*/ + + +/*! + Constructs a table view. The \a parent, \a name and \f arguments + are passed to the QFrame constructor. + + The \link setTableFlags() table flags\endlink are all cleared (set to 0). + Set \c Tbl_autoVScrollBar or \c Tbl_autoHScrollBar to get automatic scroll + bars and \c Tbl_clipCellPainting to get safe clipping. + + The \link setCellHeight() cell height\endlink and \link setCellWidth() + cell width\endlink are set to 0. + + Frame line shapes (QFrame::HLink and QFrame::VLine) are disallowed; + see QFrame::setFrameStyle(). + + Note that the \a f argument is \e not \link setTableFlags() table + flags \endlink but rather \link QWidget::QWidget() widget + flags. \endlink + +*/ + +QtTableView::QtTableView( QWidget *parent, const char *name, WFlags f ) + : QFrame( parent, name, f ) +{ + nRows = nCols = 0; // zero rows/cols + xCellOffs = yCellOffs = 0; // zero offset + xCellDelta = yCellDelta = 0; // zero cell offset + xOffs = yOffs = 0; // zero total pixel offset + cellH = cellW = 0; // user defined cell size + tFlags = 0; + vScrollBar = hScrollBar = 0; // no scroll bars + cornerSquare = 0; + sbDirty = 0; + eraseInPaint = FALSE; + verSliding = FALSE; + verSnappingOff = FALSE; + horSliding = FALSE; + horSnappingOff = FALSE; + coveringCornerSquare = FALSE; + inSbUpdate = FALSE; +} + +/*! + Destroys the table view. +*/ + +QtTableView::~QtTableView() +{ + delete vScrollBar; + delete hScrollBar; + delete cornerSquare; +} + + +/*! + \internal + Reimplements QWidget::setBackgroundColor() for binary compatibility. + \sa setPalette() +*/ + +void QtTableView::setBackgroundColor( const QColor &c ) +{ + QWidget::setBackgroundColor( c ); +} + +/*!\reimp +*/ + +void QtTableView::setPalette( const QPalette &p ) +{ + QWidget::setPalette( p ); +} + +/*!\reimp +*/ + +void QtTableView::show() +{ + showOrHideScrollBars(); + QWidget::show(); +} + + +/*! + \overload void QtTableView::repaint( bool erase ) + Repaints the entire view. +*/ + +/*! + Repaints the table view directly by calling paintEvent() directly + unless updates are disabled. + + Erases the view area \a (x,y,w,h) if \a erase is TRUE. Parameters \a + (x,y) are in \e widget coordinates. + + If \a w is negative, it is replaced with <code>width() - x</code>. + If \a h is negative, it is replaced with <code>height() - y</code>. + + Doing a repaint() usually is faster than doing an update(), but + calling update() many times in a row will generate a single paint + event. + + At present, QtTableView is the only widget that reimplements \link + QWidget::repaint() repaint()\endlink. It does this because by + clearing and then repainting one cell at at time, it can make the + screen flicker less than it would otherwise. */ + +void QtTableView::repaint( int x, int y, int w, int h, bool erase ) +{ + if ( !isVisible() || testWState(WState_BlockUpdates) ) + return; + if ( w < 0 ) + w = width() - x; + if ( h < 0 ) + h = height() - y; + QRect r( x, y, w, h ); + if ( r.isEmpty() ) + return; // nothing to do + QPaintEvent e( r ); + if ( erase && backgroundMode() != NoBackground ) + eraseInPaint = TRUE; // erase when painting + paintEvent( &e ); + eraseInPaint = FALSE; +} + +/*! + \overload void QtTableView::repaint( const QRect &r, bool erase ) + Replaints rectangle \a r. If \a erase is TRUE draws the background + using the palette's background. +*/ + + +/*! + \fn int QtTableView::numRows() const + Returns the number of rows in the table. + \sa numCols(), setNumRows() +*/ + +/*! + Sets the number of rows of the table to \a rows (must be non-negative). + Does not change topCell(). + + The table repaints itself automatically if autoUpdate() is set. + + \sa numCols(), setNumCols(), numRows() +*/ + +void QtTableView::setNumRows( int rows ) +{ + if ( rows < 0 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QtTableView::setNumRows: (%s) Negative argument %d.", + name( "unnamed" ), rows ); +#endif + return; + } + if ( nRows == rows ) + return; + + if ( autoUpdate() && isVisible() ) { + int oldLastVisible = lastRowVisible(); + int oldTopCell = topCell(); + nRows = rows; + if ( autoUpdate() && isVisible() && + ( oldLastVisible != lastRowVisible() || oldTopCell != topCell() ) ) + repaint( oldTopCell != topCell() ); + } else { + // Be more careful - if destructing, bad things might happen. + nRows = rows; + } + updateScrollBars( verRange ); + updateFrameSize(); +} + +/*! + \fn int QtTableView::numCols() const + Returns the number of columns in the table. + \sa numRows(), setNumCols() +*/ + +/*! + Sets the number of columns of the table to \a cols (must be non-negative). + Does not change leftCell(). + + The table repaints itself automatically if autoUpdate() is set. + + \sa numCols(), numRows(), setNumRows() +*/ + +void QtTableView::setNumCols( int cols ) +{ + if ( cols < 0 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QtTableView::setNumCols: (%s) Negative argument %d.", + name( "unnamed" ), cols ); +#endif + return; + } + if ( nCols == cols ) + return; + int oldCols = nCols; + nCols = cols; + if ( autoUpdate() && isVisible() ) { + int maxCol = lastColVisible(); + if ( maxCol >= oldCols || maxCol >= nCols ) + repaint(); + } + updateScrollBars( horRange ); + updateFrameSize(); +} + + +/*! + \fn int QtTableView::topCell() const + Returns the index of the first row in the table that is visible in + the view. The index of the first row is 0. + \sa leftCell(), setTopCell() +*/ + +/*! + Scrolls the table so that \a row becomes the top row. + The index of the very first row is 0. + \sa setYOffset(), setTopLeftCell(), setLeftCell() +*/ + +void QtTableView::setTopCell( int row ) +{ + setTopLeftCell( row, -1 ); + return; +} + +/*! + \fn int QtTableView::leftCell() const + Returns the index of the first column in the table that is visible in + the view. The index of the very leftmost column is 0. + \sa topCell(), setLeftCell() +*/ + +/*! + Scrolls the table so that \a col becomes the leftmost + column. The index of the leftmost column is 0. + \sa setXOffset(), setTopLeftCell(), setTopCell() +*/ + +void QtTableView::setLeftCell( int col ) +{ + setTopLeftCell( -1, col ); + return; +} + +/*! + Scrolls the table so that the cell at row \a row and colum \a + col becomes the top-left cell in the view. The cell at the extreme + top left of the table is at position (0,0). + \sa setLeftCell(), setTopCell(), setOffset() +*/ + +void QtTableView::setTopLeftCell( int row, int col ) +{ + int newX = xOffs; + int newY = yOffs; + + if ( col >= 0 ) { + if ( cellW ) { + newX = col*cellW; + if ( newX > maxXOffset() ) + newX = maxXOffset(); + } else { + newX = 0; + while ( col ) + newX += cellWidth( --col ); // optimize using current! ### + } + } + if ( row >= 0 ) { + if ( cellH ) { + newY = row*cellH; + if ( newY > maxYOffset() ) + newY = maxYOffset(); + } else { + newY = 0; + while ( row ) + newY += cellHeight( --row ); // optimize using current! ### + } + } + setOffset( newX, newY ); +} + + +/*! + \fn int QtTableView::xOffset() const + + Returns the x coordinate in \e table coordinates of the pixel that is + currently on the left edge of the view. + + \sa setXOffset(), yOffset(), leftCell() */ + +/*! + Scrolls the table so that \a x becomes the leftmost pixel in the view. + The \a x parameter is in \e table coordinates. + + The interaction with \link setTableFlags() Tbl_snapToHGrid + \endlink is tricky. + + \sa xOffset(), setYOffset(), setOffset(), setLeftCell() +*/ + +void QtTableView::setXOffset( int x ) +{ + setOffset( x, yOffset() ); +} + +/*! + \fn int QtTableView::yOffset() const + + Returns the y coordinate in \e table coordinates of the pixel that is + currently on the top edge of the view. + + \sa setYOffset(), xOffset(), topCell() +*/ + + +/*! + Scrolls the table so that \a y becomes the top pixel in the view. + The \a y parameter is in \e table coordinates. + + The interaction with \link setTableFlags() Tbl_snapToVGrid + \endlink is tricky. + + \sa yOffset(), setXOffset(), setOffset(), setTopCell() +*/ + +void QtTableView::setYOffset( int y ) +{ + setOffset( xOffset(), y ); +} + +/*! + Scrolls the table so that \a (x,y) becomes the top-left pixel + in the view. Parameters \a (x,y) are in \e table coordinates. + + The interaction with \link setTableFlags() Tbl_snapTo*Grid \endlink + is tricky. If \a updateScrBars is TRUE, the scroll bars are + updated. + + \sa xOffset(), yOffset(), setXOffset(), setYOffset(), setTopLeftCell() +*/ + +void QtTableView::setOffset( int x, int y, bool updateScrBars ) +{ + if ( (!testTableFlags(Tbl_snapToHGrid) || xCellDelta == 0) && + (!testTableFlags(Tbl_snapToVGrid) || yCellDelta == 0) && + (x == xOffs && y == yOffs) ) + return; + + if ( x < 0 ) + x = 0; + if ( y < 0 ) + y = 0; + + if ( cellW ) { + if ( x > maxXOffset() ) + x = maxXOffset(); + xCellOffs = x / cellW; + if ( !testTableFlags(Tbl_snapToHGrid) ) { + xCellDelta = (short)(x % cellW); + } else { + x = xCellOffs*cellW; + xCellDelta = 0; + } + } else { + int xn=0, xcd=0, col = 0; + while ( col < nCols-1 && x >= xn+(xcd=cellWidth(col)) ) { + xn += xcd; + col++; + } + xCellOffs = col; + if ( testTableFlags(Tbl_snapToHGrid) ) { + xCellDelta = 0; + x = xn; + } else { + xCellDelta = (short)(x-xn); + } + } + if ( cellH ) { + if ( y > maxYOffset() ) + y = maxYOffset(); + yCellOffs = y / cellH; + if ( !testTableFlags(Tbl_snapToVGrid) ) { + yCellDelta = (short)(y % cellH); + } else { + y = yCellOffs*cellH; + yCellDelta = 0; + } + } else { + int yn=0, yrd=0, row=0; + while ( row < nRows-1 && y >= yn+(yrd=cellHeight(row)) ) { + yn += yrd; + row++; + } + yCellOffs = row; + if ( testTableFlags(Tbl_snapToVGrid) ) { + yCellDelta = 0; + y = yn; + } else { + yCellDelta = (short)(y-yn); + } + } + int dx = (x - xOffs); + int dy = (y - yOffs); + xOffs = x; + yOffs = y; + if ( autoUpdate() && isVisible() ) + scroll( dx, dy ); + if ( updateScrBars ) + updateScrollBars( verValue | horValue ); +} + + +/*! + \overload int QtTableView::cellWidth() const + + Returns the column width in pixels. Returns 0 if the columns have + variable widths. + + \sa setCellWidth(), cellHeight() +*/ + +/*! + Returns the width of column \a col in pixels. + + This function is virtual and must be reimplemented by subclasses that + have variable cell widths. Note that if the total table width + changes, updateTableSize() must be called. + + \sa setCellWidth(), cellHeight(), totalWidth(), updateTableSize() +*/ + +int QtTableView::cellWidth( int ) +{ + return cellW; +} + + +/*! + Sets the width in pixels of the table cells to \a cellWidth. + + Setting it to 0 means that the column width is variable. When + set to 0 (this is the default) QtTableView calls the virtual function + cellWidth() to get the width. + + \sa cellWidth(), setCellHeight(), totalWidth(), numCols() +*/ + +void QtTableView::setCellWidth( int cellWidth ) +{ + if ( cellW == cellWidth ) + return; +#if defined(QT_CHECK_RANGE) + if ( cellWidth < 0 || cellWidth > SHRT_MAX ) { + qWarning( "QtTableView::setCellWidth: (%s) Argument out of range (%d)", + name( "unnamed" ), cellWidth ); + return; + } +#endif + cellW = (short)cellWidth; + + updateScrollBars( horSteps | horRange ); + if ( autoUpdate() && isVisible() ) + repaint(); + +} + +/*! + \overload int QtTableView::cellHeight() const + + Returns the row height, in pixels. Returns 0 if the rows have + variable heights. + + \sa setCellHeight(), cellWidth() +*/ + + +/*! + Returns the height of row \a row in pixels. + + This function is virtual and must be reimplemented by subclasses that + have variable cell heights. Note that if the total table height + changes, updateTableSize() must be called. + + \sa setCellHeight(), cellWidth(), totalHeight() +*/ + +int QtTableView::cellHeight( int ) +{ + return cellH; +} + +/*! + Sets the height in pixels of the table cells to \a cellHeight. + + Setting it to 0 means that the row height is variable. When set + to 0 (this is the default), QtTableView calls the virtual function + cellHeight() to get the height. + + \sa cellHeight(), setCellWidth(), totalHeight(), numRows() +*/ + +void QtTableView::setCellHeight( int cellHeight ) +{ + if ( cellH == cellHeight ) + return; +#if defined(QT_CHECK_RANGE) + if ( cellHeight < 0 || cellHeight > SHRT_MAX ) { + qWarning( "QtTableView::setCellHeight: (%s) Argument out of range (%d)", + name( "unnamed" ), cellHeight ); + return; + } +#endif + cellH = (short)cellHeight; + if ( autoUpdate() && isVisible() ) + repaint(); + updateScrollBars( verSteps | verRange ); +} + + +/*! + Returns the total width of the table in pixels. + + This function is virtual and should be reimplemented by subclasses that + have variable cell widths and a non-trivial cellWidth() function, or a + large number of columns in the table. + + The default implementation may be slow for very wide tables. + + \sa cellWidth(), totalHeight() */ + +int QtTableView::totalWidth() +{ + if ( cellW ) { + return cellW*nCols; + } else { + int tw = 0; + for( int i = 0 ; i < nCols ; i++ ) + tw += cellWidth( i ); + return tw; + } +} + +/*! + Returns the total height of the table in pixels. + + This function is virtual and should be reimplemented by subclasses that + have variable cell heights and a non-trivial cellHeight() function, or a + large number of rows in the table. + + The default implementation may be slow for very tall tables. + + \sa cellHeight(), totalWidth() +*/ + +int QtTableView::totalHeight() +{ + if ( cellH ) { + return cellH*nRows; + } else { + int th = 0; + for( int i = 0 ; i < nRows ; i++ ) + th += cellHeight( i ); + return th; + } +} + + +/*! + \fn uint QtTableView::tableFlags() const + + Returns the union of the table flags that are currently set. + + \sa setTableFlags(), clearTableFlags(), testTableFlags() +*/ + +/*! + \fn bool QtTableView::testTableFlags( uint f ) const + + Returns TRUE if any of the table flags in \a f are currently set, + otherwise FALSE. + + \sa setTableFlags(), clearTableFlags(), tableFlags() +*/ + +/*! + Sets the table flags to \a f. + + If a flag setting changes the appearance of the table, the table is + repainted if - and only if - autoUpdate() is TRUE. + + The table flags are mostly single bits, though there are some multibit + flags for convenience. Here is a complete list: + + <dl compact> + <dt> Tbl_vScrollBar <dd> - The table has a vertical scroll bar. + <dt> Tbl_hScrollBar <dd> - The table has a horizontal scroll bar. + <dt> Tbl_autoVScrollBar <dd> - The table has a vertical scroll bar if + - and only if - the table is taller than the view. + <dt> Tbl_autoHScrollBar <dd> The table has a horizontal scroll bar if + - and only if - the table is wider than the view. + <dt> Tbl_autoScrollBars <dd> - The union of the previous two flags. + <dt> Tbl_clipCellPainting <dd> - The table uses QPainter::setClipRect() to + make sure that paintCell() will not draw outside the cell + boundaries. + <dt> Tbl_cutCellsV <dd> - The table will never show part of a + cell at the bottom of the table; if there is not space for all of + a cell, the space is left blank. + <dt> Tbl_cutCellsH <dd> - The table will never show part of a + cell at the right side of the table; if there is not space for all of + a cell, the space is left blank. + <dt> Tbl_cutCells <dd> - The union of the previous two flags. + <dt> Tbl_scrollLastHCell <dd> - When the user scrolls horizontally, + let him/her scroll the last cell left until it is at the left + edge of the view. If this flag is not set, the user can only scroll + to the point where the last cell is completely visible. + <dt> Tbl_scrollLastVCell <dd> - When the user scrolls vertically, let + him/her scroll the last cell up until it is at the top edge of + the view. If this flag is not set, the user can only scroll to the + point where the last cell is completely visible. + <dt> Tbl_scrollLastCell <dd> - The union of the previous two flags. + <dt> Tbl_smoothHScrolling <dd> - The table scrolls as smoothly as + possible when the user scrolls horizontally. When this flag is not + set, scrolling is done one cell at a time. + <dt> Tbl_smoothVScrolling <dd> - The table scrolls as smoothly as + possible when scrolling vertically. When this flag is not set, + scrolling is done one cell at a time. + <dt> Tbl_smoothScrolling <dd> - The union of the previous two flags. + <dt> Tbl_snapToHGrid <dd> - Except when the user is actually scrolling, + the leftmost column shown snaps to the leftmost edge of the view. + <dt> Tbl_snapToVGrid <dd> - Except when the user is actually + scrolling, the top row snaps to the top edge of the view. + <dt> Tbl_snapToGrid <dd> - The union of the previous two flags. + </dl> + + You can specify more than one flag at a time using bitwise OR. + + Example: + \code + setTableFlags( Tbl_smoothScrolling | Tbl_autoScrollBars ); + \endcode + + \warning The cutCells options (\c Tbl_cutCells, \c Tbl_cutCellsH and + Tbl_cutCellsV) may cause painting problems when scrollbars are + enabled. Do not combine cutCells and scrollbars. + + + \sa clearTableFlags(), testTableFlags(), tableFlags() +*/ + +void QtTableView::setTableFlags( uint f ) +{ + f = (f ^ tFlags) & f; // clear flags already set + tFlags |= f; + + bool updateOn = autoUpdate(); + setAutoUpdate( FALSE ); + + uint repaintMask = Tbl_cutCellsV | Tbl_cutCellsH; + + if ( f & Tbl_vScrollBar ) { + setVerScrollBar( TRUE ); + } + if ( f & Tbl_hScrollBar ) { + setHorScrollBar( TRUE ); + } + if ( f & Tbl_autoVScrollBar ) { + updateScrollBars( verRange ); + } + if ( f & Tbl_autoHScrollBar ) { + updateScrollBars( horRange ); + } + if ( f & Tbl_scrollLastHCell ) { + updateScrollBars( horRange ); + } + if ( f & Tbl_scrollLastVCell ) { + updateScrollBars( verRange ); + } + if ( f & Tbl_snapToHGrid ) { + updateScrollBars( horRange ); + } + if ( f & Tbl_snapToVGrid ) { + updateScrollBars( verRange ); + } + if ( f & Tbl_snapToGrid ) { // Note: checks for 2 flags + if ( (f & Tbl_snapToHGrid) != 0 && xCellDelta != 0 || //have to scroll? + (f & Tbl_snapToVGrid) != 0 && yCellDelta != 0 ) { + snapToGrid( (f & Tbl_snapToHGrid) != 0, // do snapping + (f & Tbl_snapToVGrid) != 0 ); + repaintMask |= Tbl_snapToGrid; // repaint table + } + } + + if ( updateOn ) { + setAutoUpdate( TRUE ); + updateScrollBars(); + if ( isVisible() && (f & repaintMask) ) + repaint(); + } + +} + +/*! + Clears the \link setTableFlags() table flags\endlink that are set + in \a f. + + Example (clears a single flag): + \code + clearTableFlags( Tbl_snapToGrid ); + \endcode + + The default argument clears all flags. + + \sa setTableFlags(), testTableFlags(), tableFlags() +*/ + +void QtTableView::clearTableFlags( uint f ) +{ + f = (f ^ ~tFlags) & f; // clear flags that are already 0 + tFlags &= ~f; + + bool updateOn = autoUpdate(); + setAutoUpdate( FALSE ); + + uint repaintMask = Tbl_cutCellsV | Tbl_cutCellsH; + + if ( f & Tbl_vScrollBar ) { + setVerScrollBar( FALSE ); + } + if ( f & Tbl_hScrollBar ) { + setHorScrollBar( FALSE ); + } + if ( f & Tbl_scrollLastHCell ) { + int maxX = maxXOffset(); + if ( xOffs > maxX ) { + setOffset( maxX, yOffs ); + repaintMask |= Tbl_scrollLastHCell; + } + updateScrollBars( horRange ); + } + if ( f & Tbl_scrollLastVCell ) { + int maxY = maxYOffset(); + if ( yOffs > maxY ) { + setOffset( xOffs, maxY ); + repaintMask |= Tbl_scrollLastVCell; + } + updateScrollBars( verRange ); + } + if ( f & Tbl_smoothScrolling ) { // Note: checks for 2 flags + if ((f & Tbl_smoothHScrolling) != 0 && xCellDelta != 0 ||//must scroll? + (f & Tbl_smoothVScrolling) != 0 && yCellDelta != 0 ) { + snapToGrid( (f & Tbl_smoothHScrolling) != 0, // do snapping + (f & Tbl_smoothVScrolling) != 0 ); + repaintMask |= Tbl_smoothScrolling; // repaint table + } + } + if ( f & Tbl_snapToHGrid ) { + updateScrollBars( horRange ); + } + if ( f & Tbl_snapToVGrid ) { + updateScrollBars( verRange ); + } + if ( updateOn ) { + setAutoUpdate( TRUE ); + updateScrollBars(); // returns immediately if nothing to do + if ( isVisible() && (f & repaintMask) ) + repaint(); + } + +} + + +/*! + \fn bool QtTableView::autoUpdate() const + + Returns TRUE if the view updates itself automatically whenever it + is changed in some way. + + \sa setAutoUpdate() +*/ + +/*! + Sets the auto-update option of the table view to \a enable. + + If \a enable is TRUE (this is the default), the view updates itself + automatically whenever it has changed in some way (for example, when a + \link setTableFlags() flag\endlink is changed). + + If \a enable is FALSE, the view does NOT repaint itself or update + its internal state variables when it is changed. This can be + useful to avoid flicker during large changes and is singularly + useless otherwise. Disable auto-update, do the changes, re-enable + auto-update and call repaint(). + + \warning Do not leave the view in this state for a long time + (i.e., between events). If, for example, the user interacts with the + view when auto-update is off, strange things can happen. + + Setting auto-update to TRUE does not repaint the view; you must call + repaint() to do this. + + \sa autoUpdate(), repaint() +*/ + +void QtTableView::setAutoUpdate( bool enable ) +{ + if ( isUpdatesEnabled() == enable ) + return; + setUpdatesEnabled( enable ); + if ( enable ) { + showOrHideScrollBars(); + updateScrollBars(); + } +} + + +/*! + Repaints the cell at row \a row, column \a col if it is inside the view. + + If \a erase is TRUE, the relevant part of the view is cleared to the + background color/pixmap before the contents are repainted. + + \sa isVisible() +*/ + +void QtTableView::updateCell( int row, int col, bool erase ) +{ + int xPos, yPos; + if ( !colXPos( col, &xPos ) ) + return; + if ( !rowYPos( row, &yPos ) ) + return; + QRect uR = QRect( xPos, yPos, + cellW ? cellW : cellWidth(col), + cellH ? cellH : cellHeight(row) ); + repaint( uR.intersect(viewRect()), erase ); +} + + +/*! + \fn QRect QtTableView::cellUpdateRect() const + + This function should be called only from the paintCell() function in + subclasses. It returns the portion of a cell that actually needs to be + updated in \e cell coordinates. This is useful only for non-trivial + paintCell(). + +*/ + +/*! + Returns the rectangle that is the actual table, excluding any + frame, in \e widget coordinates. +*/ + +QRect QtTableView::viewRect() const +{ + return QRect( frameWidth(), frameWidth(), viewWidth(), viewHeight() ); +} + + +/*! + Returns the index of the last (bottom) row in the view. + The index of the first row is 0. + + If no rows are visible it returns -1. This can happen if the + view is too small for the first row and Tbl_cutCellsV is set. + + \sa lastColVisible() +*/ + +int QtTableView::lastRowVisible() const +{ + int cellMaxY; + int row = findRawRow( maxViewY(), &cellMaxY ); + if ( row == -1 || row >= nRows ) { // maxViewY() past end? + row = nRows - 1; // yes: return last row + } else { + if ( testTableFlags(Tbl_cutCellsV) && cellMaxY > maxViewY() ) { + if ( row == yCellOffs ) // cut by right margin? + return -1; // yes, nothing in the view + else + row = row - 1; // cut by margin, one back + } + } + return row; +} + +/*! + Returns the index of the last (right) column in the view. + The index of the first column is 0. + + If no columns are visible it returns -1. This can happen if the + view is too narrow for the first column and Tbl_cutCellsH is set. + + \sa lastRowVisible() +*/ + +int QtTableView::lastColVisible() const +{ + int cellMaxX; + int col = findRawCol( maxViewX(), &cellMaxX ); + if ( col == -1 || col >= nCols ) { // maxViewX() past end? + col = nCols - 1; // yes: return last col + } else { + if ( testTableFlags(Tbl_cutCellsH) && cellMaxX > maxViewX() ) { + if ( col == xCellOffs ) // cut by bottom margin? + return -1; // yes, nothing in the view + else + col = col - 1; // cell by margin, one back + } + } + return col; +} + +/*! + Returns TRUE if \a row is at least partially visible. + \sa colIsVisible() +*/ + +bool QtTableView::rowIsVisible( int row ) const +{ + return rowYPos( row, 0 ); +} + +/*! + Returns TRUE if \a col is at least partially visible. + \sa rowIsVisible() +*/ + +bool QtTableView::colIsVisible( int col ) const +{ + return colXPos( col, 0 ); +} + + +/*! + \internal + Called when both scroll bars are active at the same time. Covers the + bottom left corner between the two scroll bars with an empty widget. +*/ + +void QtTableView::coverCornerSquare( bool enable ) +{ + coveringCornerSquare = enable; + if ( !cornerSquare && enable ) { + cornerSquare = new QCornerSquare( this ); + Q_CHECK_PTR( cornerSquare ); + cornerSquare->setGeometry( maxViewX() + frameWidth() + 1, + maxViewY() + frameWidth() + 1, + VSBEXT, + HSBEXT); + } + if ( autoUpdate() && cornerSquare ) { + if ( enable ) + cornerSquare->show(); + else + cornerSquare->hide(); + } +} + + +/*! + \internal + Scroll the view to a position such that: + + If \a horizontal is TRUE, the leftmost column shown fits snugly + with the left edge of the view. + + If \a vertical is TRUE, the top row shown fits snugly with the top + of the view. + + You can achieve the same effect automatically by setting any of the + \link setTableFlags() Tbl_snapTo*Grid \endlink table flags. +*/ + +void QtTableView::snapToGrid( bool horizontal, bool vertical ) +{ + int newXCell = -1; + int newYCell = -1; + if ( horizontal && xCellDelta != 0 ) { + int w = cellW ? cellW : cellWidth( xCellOffs ); + if ( xCellDelta >= w/2 ) + newXCell = xCellOffs + 1; + else + newXCell = xCellOffs; + } + if ( vertical && yCellDelta != 0 ) { + int h = cellH ? cellH : cellHeight( yCellOffs ); + if ( yCellDelta >= h/2 ) + newYCell = yCellOffs + 1; + else + newYCell = yCellOffs; + } + setTopLeftCell( newYCell, newXCell ); //row,column +} + +/*! + \internal + This internal slot is connected to the horizontal scroll bar's + QScrollBar::valueChanged() signal. + + Moves the table horizontally to offset \a val without updating the + scroll bar. +*/ + +void QtTableView::horSbValue( int val ) +{ + if ( horSliding ) { + horSliding = FALSE; + if ( horSnappingOff ) { + horSnappingOff = FALSE; + tFlags |= Tbl_snapToHGrid; + } + } + setOffset( val, yOffs, FALSE ); +} + +/*! + \internal + This internal slot is connected to the horizontal scroll bar's + QScrollBar::sliderMoved() signal. + + Scrolls the table smoothly horizontally even if \c Tbl_snapToHGrid is set. +*/ + +void QtTableView::horSbSliding( int val ) +{ + if ( testTableFlags(Tbl_snapToHGrid) && + testTableFlags(Tbl_smoothHScrolling) ) { + tFlags &= ~Tbl_snapToHGrid; // turn off snapping while sliding + setOffset( val, yOffs, FALSE ); + tFlags |= Tbl_snapToHGrid; // turn on snapping again + } else { + setOffset( val, yOffs, FALSE ); + } +} + +/*! + \internal + This internal slot is connected to the horizontal scroll bar's + QScrollBar::sliderReleased() signal. +*/ + +void QtTableView::horSbSlidingDone( ) +{ + if ( testTableFlags(Tbl_snapToHGrid) && + testTableFlags(Tbl_smoothHScrolling) ) + snapToGrid( TRUE, FALSE ); +} + +/*! + \internal + This internal slot is connected to the vertical scroll bar's + QScrollBar::valueChanged() signal. + + Moves the table vertically to offset \a val without updating the + scroll bar. +*/ + +void QtTableView::verSbValue( int val ) +{ + if ( verSliding ) { + verSliding = FALSE; + if ( verSnappingOff ) { + verSnappingOff = FALSE; + tFlags |= Tbl_snapToVGrid; + } + } + setOffset( xOffs, val, FALSE ); +} + +/*! + \internal + This internal slot is connected to the vertical scroll bar's + QScrollBar::sliderMoved() signal. + + Scrolls the table smoothly vertically even if \c Tbl_snapToVGrid is set. +*/ + +void QtTableView::verSbSliding( int val ) +{ + if ( testTableFlags(Tbl_snapToVGrid) && + testTableFlags(Tbl_smoothVScrolling) ) { + tFlags &= ~Tbl_snapToVGrid; // turn off snapping while sliding + setOffset( xOffs, val, FALSE ); + tFlags |= Tbl_snapToVGrid; // turn on snapping again + } else { + setOffset( xOffs, val, FALSE ); + } +} + +/*! + \internal + This internal slot is connected to the vertical scroll bar's + QScrollBar::sliderReleased() signal. +*/ + +void QtTableView::verSbSlidingDone( ) +{ + if ( testTableFlags(Tbl_snapToVGrid) && + testTableFlags(Tbl_smoothVScrolling) ) + snapToGrid( FALSE, TRUE ); +} + + +/*! + This virtual function is called before painting of table cells + is started. It can be reimplemented by subclasses that want to + to set up the painter in a special way and that do not want to + do so for each cell. +*/ + +void QtTableView::setupPainter( QPainter * ) +{ +} + +/*! + \fn void QtTableView::paintCell( QPainter *p, int row, int col ) + + This pure virtual function is called to paint the single cell at \a + (row,col) using \a p, which is open when paintCell() is called and + must remain open. + + The coordinate system is \link QPainter::translate() translated \endlink + so that the origin is at the top-left corner of the cell to be + painted, i.e. \e cell coordinates. Do not scale or shear the coordinate + system (or if you do, restore the transformation matrix before you + return). + + The painter is not clipped by default and for maximum efficiency. For safety, + call setTableFlags(Tbl_clipCellPainting) to enable clipping. + + \sa paintEvent(), setTableFlags() */ + + +/*! + Handles paint events, \a e, for the table view. + + Calls paintCell() for the cells that needs to be repainted. +*/ + +void QtTableView::paintEvent( QPaintEvent *e ) +{ + QRect updateR = e->rect(); // update rectangle + if ( sbDirty ) { + bool e = eraseInPaint; + updateScrollBars(); + eraseInPaint = e; + } + + QPainter paint( this ); + + if ( !contentsRect().contains( updateR, TRUE ) ) {// update frame ? + drawFrame( &paint ); + if ( updateR.left() < frameWidth() ) //### + updateR.setLeft( frameWidth() ); + if ( updateR.top() < frameWidth() ) + updateR.setTop( frameWidth() ); + } + + int maxWX = maxViewX(); + int maxWY = maxViewY(); + if ( updateR.right() > maxWX ) + updateR.setRight( maxWX ); + if ( updateR.bottom() > maxWY ) + updateR.setBottom( maxWY ); + + setupPainter( &paint ); // prepare for painting table + + int firstRow = findRow( updateR.y() ); + int firstCol = findCol( updateR.x() ); + int xStart, yStart; + if ( !colXPos( firstCol, &xStart ) || !rowYPos( firstRow, &yStart ) ) { + paint.eraseRect( updateR ); // erase area outside cells but in view + return; + } + int maxX = updateR.right(); + int maxY = updateR.bottom(); + int row = firstRow; + int col; + int yPos = yStart; + int xPos = maxX+1; // in case the while() is empty + int nextX; + int nextY; + QRect winR = viewRect(); + QRect cellR; + QRect cellUR; +#ifndef QT_NO_TRANSFORMATIONS + QWMatrix matrix; +#endif + + while ( yPos <= maxY && row < nRows ) { + nextY = yPos + (cellH ? cellH : cellHeight( row )); + if ( testTableFlags( Tbl_cutCellsV ) && nextY > ( maxWY + 1 ) ) + break; + col = firstCol; + xPos = xStart; + while ( xPos <= maxX && col < nCols ) { + nextX = xPos + (cellW ? cellW : cellWidth( col )); + if ( testTableFlags( Tbl_cutCellsH ) && nextX > ( maxWX + 1 ) ) + break; + + cellR.setRect( xPos, yPos, cellW ? cellW : cellWidth(col), + cellH ? cellH : cellHeight(row) ); + cellUR = cellR.intersect( updateR ); + if ( cellUR.isValid() ) { + cellUpdateR = cellUR; + cellUpdateR.moveBy( -xPos, -yPos ); // cell coordinates + if ( eraseInPaint ) + paint.eraseRect( cellUR ); + +#ifndef QT_NO_TRANSFORMATIONS + matrix.translate( xPos, yPos ); + paint.setWorldMatrix( matrix ); + if ( testTableFlags(Tbl_clipCellPainting) || + frameWidth() > 0 && !winR.contains( cellR ) ) { //##arnt + paint.setClipRect( cellUR ); + paintCell( &paint, row, col ); + paint.setClipping( FALSE ); + } else { + paintCell( &paint, row, col ); + } + matrix.reset(); + paint.setWorldMatrix( matrix ); +#else + paint.translate( xPos, yPos ); + if ( testTableFlags(Tbl_clipCellPainting) || + frameWidth() > 0 && !winR.contains( cellR ) ) { //##arnt + paint.setClipRect( cellUR ); + paintCell( &paint, row, col ); + paint.setClipping( FALSE ); + } else { + paintCell( &paint, row, col ); + } + paint.translate( -xPos, -yPos ); +#endif + } + col++; + xPos = nextX; + } + row++; + yPos = nextY; + } + + // while painting we have to erase any areas in the view that + // are not covered by cells but are covered by the paint event + // rectangle these must be erased. We know that xPos is the last + // x pixel updated + 1 and that yPos is the last y pixel updated + 1. + + // Note that this needs to be done regardless whether we do + // eraseInPaint or not. Reason: a subclass may implement + // flicker-freeness and encourage the use of repaint(FALSE). + // The subclass, however, cannot draw all pixels, just those + // inside the cells. So QtTableView is reponsible for all pixels + // outside the cells. + + QRect viewR = viewRect(); + const QColorGroup g = colorGroup(); + + if ( xPos <= maxX ) { + QRect r = viewR; + r.setLeft( xPos ); + r.setBottom( yPos<maxY?yPos:maxY ); + if ( inherits( "QMultiLineEdit" ) ) + paint.fillRect( r.intersect( updateR ), g.base() ); + else + paint.eraseRect( r.intersect( updateR ) ); + } + if ( yPos <= maxY ) { + QRect r = viewR; + r.setTop( yPos ); + if ( inherits( "QMultiLineEdit" ) ) + paint.fillRect( r.intersect( updateR ), g.base() ); + else + paint.eraseRect( r.intersect( updateR ) ); + } +} + +/*!\reimp +*/ +void QtTableView::resizeEvent( QResizeEvent * ) +{ + updateScrollBars( horValue | verValue | horSteps | horGeometry | horRange | + verSteps | verGeometry | verRange ); + showOrHideScrollBars(); + updateFrameSize(); + int maxX = QMIN( xOffs, maxXOffset() ); // ### can be slow + int maxY = QMIN( yOffs, maxYOffset() ); + setOffset( maxX, maxY ); +} + +void QtTableView::wheelEvent( QWheelEvent * e ) +{ + if( e->orientation() == Vertical && vScrollBar && vScrollBar->isVisible() ) + QApplication::sendEvent( vScrollBar, e ); +} + +/*! + Redraws all visible cells in the table view. +*/ + +void QtTableView::updateView() +{ + repaint( viewRect() ); +} + +/*! + Returns a pointer to the vertical scroll bar mainly so you can + connect() to its signals. Note that the scroll bar works in pixel + values; use findRow() to translate to cell numbers. +*/ + +QScrollBar *QtTableView::verticalScrollBar() const +{ + QtTableView *that = (QtTableView*)this; // semantic const + if ( !vScrollBar ) { + QScrollBar *sb = new QScrollBar( QScrollBar::Vertical, that ); +#ifndef QT_NO_CURSOR + sb->setCursor( arrowCursor ); +#endif + sb->resize( sb->sizeHint() ); // height is irrelevant + Q_CHECK_PTR(sb); + sb->setTracking( FALSE ); + sb->setFocusPolicy( NoFocus ); + connect( sb, SIGNAL(valueChanged(int)), + SLOT(verSbValue(int))); + connect( sb, SIGNAL(sliderMoved(int)), + SLOT(verSbSliding(int))); + connect( sb, SIGNAL(sliderReleased()), + SLOT(verSbSlidingDone())); + sb->hide(); + that->vScrollBar = sb; + return sb; + } + return vScrollBar; +} + +/*! + Returns a pointer to the horizontal scroll bar mainly so you can + connect() to its signals. Note that the scroll bar works in pixel + values; use findCol() to translate to cell numbers. +*/ + +QScrollBar *QtTableView::horizontalScrollBar() const +{ + QtTableView *that = (QtTableView*)this; // semantic const + if ( !hScrollBar ) { + QScrollBar *sb = new QScrollBar( QScrollBar::Horizontal, that ); +#ifndef QT_NO_CURSOR + sb->setCursor( arrowCursor ); +#endif + sb->resize( sb->sizeHint() ); // width is irrelevant + sb->setFocusPolicy( NoFocus ); + Q_CHECK_PTR(sb); + sb->setTracking( FALSE ); + connect( sb, SIGNAL(valueChanged(int)), + SLOT(horSbValue(int))); + connect( sb, SIGNAL(sliderMoved(int)), + SLOT(horSbSliding(int))); + connect( sb, SIGNAL(sliderReleased()), + SLOT(horSbSlidingDone())); + sb->hide(); + that->hScrollBar = sb; + return sb; + } + return hScrollBar; +} + +/*! + Enables or disables the horizontal scroll bar, as required by + setAutoUpdate() and the \link setTableFlags() table flags\endlink. +*/ + +void QtTableView::setHorScrollBar( bool on, bool update ) +{ + if ( on ) { + tFlags |= Tbl_hScrollBar; + horizontalScrollBar(); // created + if ( update ) + updateScrollBars( horMask | verMask ); + else + sbDirty = sbDirty | (horMask | verMask); + if ( testTableFlags( Tbl_vScrollBar ) ) + coverCornerSquare( TRUE ); + if ( autoUpdate() ) + sbDirty = sbDirty | horMask; + } else { + tFlags &= ~Tbl_hScrollBar; + if ( !hScrollBar ) + return; + coverCornerSquare( FALSE ); + bool hideScrollBar = autoUpdate() && hScrollBar->isVisible(); + if ( hideScrollBar ) + hScrollBar->hide(); + if ( update ) + updateScrollBars( verMask ); + else + sbDirty = sbDirty | verMask; + if ( hideScrollBar && isVisible() ) + repaint( hScrollBar->x(), hScrollBar->y(), + width() - hScrollBar->x(), hScrollBar->height() ); + } + if ( update ) + updateFrameSize(); +} + + +/*! + Enables or disables the vertical scroll bar, as required by + setAutoUpdate() and the \link setTableFlags() table flags\endlink. +*/ + +void QtTableView::setVerScrollBar( bool on, bool update ) +{ + if ( on ) { + tFlags |= Tbl_vScrollBar; + verticalScrollBar(); // created + if ( update ) + updateScrollBars( verMask | horMask ); + else + sbDirty = sbDirty | (horMask | verMask); + if ( testTableFlags( Tbl_hScrollBar ) ) + coverCornerSquare( TRUE ); + if ( autoUpdate() ) + sbDirty = sbDirty | verMask; + } else { + tFlags &= ~Tbl_vScrollBar; + if ( !vScrollBar ) + return; + coverCornerSquare( FALSE ); + bool hideScrollBar = autoUpdate() && vScrollBar->isVisible(); + if ( hideScrollBar ) + vScrollBar->hide(); + if ( update ) + updateScrollBars( horMask ); + else + sbDirty = sbDirty | horMask; + if ( hideScrollBar && isVisible() ) + repaint( vScrollBar->x(), vScrollBar->y(), + vScrollBar->width(), height() - vScrollBar->y() ); + } + if ( update ) + updateFrameSize(); +} + + + + +int QtTableView::findRawRow( int yPos, int *cellMaxY, int *cellMinY, + bool goOutsideView ) const +{ + int r = -1; + if ( nRows == 0 ) + return r; + if ( goOutsideView || yPos >= minViewY() && yPos <= maxViewY() ) { + if ( yPos < minViewY() ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QtTableView::findRawRow: (%s) internal error: " + "yPos < minViewY() && goOutsideView " + "not supported. (%d,%d)", + name( "unnamed" ), yPos, yOffs ); +#endif + return -1; + } + if ( cellH ) { // uniform cell height + r = (yPos - minViewY() + yCellDelta)/cellH; // cell offs from top + if ( cellMaxY ) + *cellMaxY = (r + 1)*cellH + minViewY() - yCellDelta - 1; + if ( cellMinY ) + *cellMinY = r*cellH + minViewY() - yCellDelta; + r += yCellOffs; // absolute cell index + } else { // variable cell height + QtTableView *tw = (QtTableView *)this; + r = yCellOffs; + int h = minViewY() - yCellDelta; //##arnt3 + int oldH = h; + Q_ASSERT( r < nRows ); + while ( r < nRows ) { + oldH = h; + h += tw->cellHeight( r ); // Start of next cell + if ( yPos < h ) + break; + r++; + } + if ( cellMaxY ) + *cellMaxY = h - 1; + if ( cellMinY ) + *cellMinY = oldH; + } + } + return r; + +} + + +int QtTableView::findRawCol( int xPos, int *cellMaxX, int *cellMinX , + bool goOutsideView ) const +{ + int c = -1; + if ( nCols == 0 ) + return c; + if ( goOutsideView || xPos >= minViewX() && xPos <= maxViewX() ) { + if ( xPos < minViewX() ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QtTableView::findRawCol: (%s) internal error: " + "xPos < minViewX() && goOutsideView " + "not supported. (%d,%d)", + name( "unnamed" ), xPos, xOffs ); +#endif + return -1; + } + if ( cellW ) { // uniform cell width + c = (xPos - minViewX() + xCellDelta)/cellW; //cell offs from left + if ( cellMaxX ) + *cellMaxX = (c + 1)*cellW + minViewX() - xCellDelta - 1; + if ( cellMinX ) + *cellMinX = c*cellW + minViewX() - xCellDelta; + c += xCellOffs; // absolute cell index + } else { // variable cell width + QtTableView *tw = (QtTableView *)this; + c = xCellOffs; + int w = minViewX() - xCellDelta; //##arnt3 + int oldW = w; + Q_ASSERT( c < nCols ); + while ( c < nCols ) { + oldW = w; + w += tw->cellWidth( c ); // Start of next cell + if ( xPos < w ) + break; + c++; + } + if ( cellMaxX ) + *cellMaxX = w - 1; + if ( cellMinX ) + *cellMinX = oldW; + } + } + return c; +} + + +/*! + Returns the index of the row at position \a yPos, where \a yPos is in + \e widget coordinates. Returns -1 if \a yPos is outside the valid + range. + + \sa findCol(), rowYPos() +*/ + +int QtTableView::findRow( int yPos ) const +{ + int cellMaxY; + int row = findRawRow( yPos, &cellMaxY ); + if ( testTableFlags(Tbl_cutCellsV) && cellMaxY > maxViewY() ) + row = - 1; // cell cut by bottom margin + if ( row >= nRows ) + row = -1; + return row; +} + + +/*! + Returns the index of the column at position \a xPos, where \a xPos is + in \e widget coordinates. Returns -1 if \a xPos is outside the valid + range. + + \sa findRow(), colXPos() +*/ + +int QtTableView::findCol( int xPos ) const +{ + int cellMaxX; + int col = findRawCol( xPos, &cellMaxX ); + if ( testTableFlags(Tbl_cutCellsH) && cellMaxX > maxViewX() ) + col = - 1; // cell cut by right margin + if ( col >= nCols ) + col = -1; + return col; +} + + +/*! + Computes the position in the widget of row \a row. + + Returns TRUE and stores the result in \a *yPos (in \e widget + coordinates) if the row is visible. Returns FALSE and does not modify + \a *yPos if \a row is invisible or invalid. + + \sa colXPos(), findRow() +*/ + +bool QtTableView::rowYPos( int row, int *yPos ) const +{ + int y; + if ( row >= yCellOffs ) { + if ( cellH ) { + int lastVisible = lastRowVisible(); + if ( row > lastVisible || lastVisible == -1 ) + return FALSE; + y = (row - yCellOffs)*cellH + minViewY() - yCellDelta; + } else { + //##arnt3 + y = minViewY() - yCellDelta; // y of leftmost cell in view + int r = yCellOffs; + QtTableView *tw = (QtTableView *)this; + int maxY = maxViewY(); + while ( r < row && y <= maxY ) + y += tw->cellHeight( r++ ); + if ( y > maxY ) + return FALSE; + + } + } else { + return FALSE; + } + if ( yPos ) + *yPos = y; + return TRUE; +} + + +/*! + Computes the position in the widget of column \a col. + + Returns TRUE and stores the result in \a *xPos (in \e widget + coordinates) if the column is visible. Returns FALSE and does not + modify \a *xPos if \a col is invisible or invalid. + + \sa rowYPos(), findCol() +*/ + +bool QtTableView::colXPos( int col, int *xPos ) const +{ + int x; + if ( col >= xCellOffs ) { + if ( cellW ) { + int lastVisible = lastColVisible(); + if ( col > lastVisible || lastVisible == -1 ) + return FALSE; + x = (col - xCellOffs)*cellW + minViewX() - xCellDelta; + } else { + //##arnt3 + x = minViewX() - xCellDelta; // x of uppermost cell in view + int c = xCellOffs; + QtTableView *tw = (QtTableView *)this; + int maxX = maxViewX(); + while ( c < col && x <= maxX ) + x += tw->cellWidth( c++ ); + if ( x > maxX ) + return FALSE; + } + } else { + return FALSE; + } + if ( xPos ) + *xPos = x; + return TRUE; +} + + +/*! + Moves the visible area of the table right by \a xPixels and + down by \a yPixels pixels. Both may be negative. + + \warning You might find that QScrollView offers a higher-level of + functionality than using QtTableView and this function. + + This function is \e not the same as QWidget::scroll(); in particular, + the signs of \a xPixels and \a yPixels have the reverse semantics. + + \sa setXOffset(), setYOffset(), setOffset(), setTopCell(), + setLeftCell() +*/ + +void QtTableView::scroll( int xPixels, int yPixels ) +{ + QWidget::scroll( -xPixels, -yPixels, contentsRect() ); +} + + +/*! + Returns the leftmost pixel of the table view in \e view + coordinates. This excludes the frame and any header. + + \sa maxViewY(), viewWidth(), contentsRect() +*/ + +int QtTableView::minViewX() const +{ + return frameWidth(); +} + + +/*! + Returns the top pixel of the table view in \e view + coordinates. This excludes the frame and any header. + + \sa maxViewX(), viewHeight(), contentsRect() +*/ + +int QtTableView::minViewY() const +{ + return frameWidth(); +} + + +/*! + Returns the rightmost pixel of the table view in \e view + coordinates. This excludes the frame and any scroll bar, but + includes blank pixels to the right of the visible table data. + + \sa maxViewY(), viewWidth(), contentsRect() +*/ + +int QtTableView::maxViewX() const +{ + return width() - 1 - frameWidth() + - (tFlags & Tbl_vScrollBar ? VSBEXT + : 0); +} + + +/*! + Returns the bottom pixel of the table view in \e view + coordinates. This excludes the frame and any scroll bar, but + includes blank pixels below the visible table data. + + \sa maxViewX(), viewHeight(), contentsRect() +*/ + +int QtTableView::maxViewY() const +{ + return height() - 1 - frameWidth() + - (tFlags & Tbl_hScrollBar ? HSBEXT + : 0); +} + + +/*! + Returns the width of the table view, as such, in \e view + coordinates. This does not include any header, scroll bar or frame, + but it does include background pixels to the right of the table data. + + \sa minViewX() maxViewX(), viewHeight(), contentsRect() viewRect() +*/ + +int QtTableView::viewWidth() const +{ + return maxViewX() - minViewX() + 1; +} + + +/*! + Returns the height of the table view, as such, in \e view + coordinates. This does not include any header, scroll bar or frame, + but it does include background pixels below the table data. + + \sa minViewY() maxViewY() viewWidth() contentsRect() viewRect() +*/ + +int QtTableView::viewHeight() const +{ + return maxViewY() - minViewY() + 1; +} + + +void QtTableView::doAutoScrollBars() +{ + int viewW = width() - frameWidth() - minViewX(); + int viewH = height() - frameWidth() - minViewY(); + bool vScrollOn = testTableFlags(Tbl_vScrollBar); + bool hScrollOn = testTableFlags(Tbl_hScrollBar); + int w = 0; + int h = 0; + int i; + + if ( testTableFlags(Tbl_autoHScrollBar) ) { + if ( cellW ) { + w = cellW*nCols; + } else { + i = 0; + while ( i < nCols && w <= viewW ) + w += cellWidth( i++ ); + } + if ( w > viewW ) + hScrollOn = TRUE; + else + hScrollOn = FALSE; + } + + if ( testTableFlags(Tbl_autoVScrollBar) ) { + if ( cellH ) { + h = cellH*nRows; + } else { + i = 0; + while ( i < nRows && h <= viewH ) + h += cellHeight( i++ ); + } + + if ( h > viewH ) + vScrollOn = TRUE; + else + vScrollOn = FALSE; + } + + if ( testTableFlags(Tbl_autoHScrollBar) && vScrollOn && !hScrollOn ) + if ( w > viewW - VSBEXT ) + hScrollOn = TRUE; + + if ( testTableFlags(Tbl_autoVScrollBar) && hScrollOn && !vScrollOn ) + if ( h > viewH - HSBEXT ) + vScrollOn = TRUE; + + setHorScrollBar( hScrollOn, FALSE ); + setVerScrollBar( vScrollOn, FALSE ); + updateFrameSize(); +} + + +/*! + \fn void QtTableView::updateScrollBars() + + Updates the scroll bars' contents and presence to match the table's + state. Generally, you should not need to call this. + + \sa setTableFlags() +*/ + +/*! + Updates the scroll bars' contents and presence to match the table's + state \c or \a f. + + \sa setTableFlags() +*/ + +void QtTableView::updateScrollBars( uint f ) +{ + sbDirty = sbDirty | f; + if ( inSbUpdate ) + return; + inSbUpdate = TRUE; + + if ( testTableFlags(Tbl_autoHScrollBar) && (sbDirty & horRange) || + testTableFlags(Tbl_autoVScrollBar) && (sbDirty & verRange) ) + // if range change and auto + doAutoScrollBars(); // turn scroll bars on/off if needed + + if ( !autoUpdate() ) { + inSbUpdate = FALSE; + return; + } + if ( yOffset() > 0 && testTableFlags( Tbl_autoVScrollBar ) && + !testTableFlags( Tbl_vScrollBar ) ) { + setYOffset( 0 ); + } + if ( xOffset() > 0 && testTableFlags( Tbl_autoHScrollBar ) && + !testTableFlags( Tbl_hScrollBar ) ) { + setXOffset( 0 ); + } + if ( !isVisible() ) { + inSbUpdate = FALSE; + return; + } + + if ( testTableFlags(Tbl_hScrollBar) && (sbDirty & horMask) != 0 ) { + if ( sbDirty & horGeometry ) + hScrollBar->setGeometry( 0,height() - HSBEXT, + viewWidth() + frameWidth()*2, + HSBEXT); + + if ( sbDirty & horSteps ) { + if ( cellW ) + hScrollBar->setSteps( QMIN(cellW,viewWidth()/2), viewWidth() ); + else + hScrollBar->setSteps( 16, viewWidth() ); + } + + if ( sbDirty & horRange ) + hScrollBar->setRange( 0, maxXOffset() ); + + if ( sbDirty & horValue ) + hScrollBar->setValue( xOffs ); + + // show scrollbar only when it has a sane geometry + if ( !hScrollBar->isVisible() ) + hScrollBar->show(); + } + + if ( testTableFlags(Tbl_vScrollBar) && (sbDirty & verMask) != 0 ) { + if ( sbDirty & verGeometry ) + vScrollBar->setGeometry( width() - VSBEXT, 0, + VSBEXT, + viewHeight() + frameWidth()*2 ); + + if ( sbDirty & verSteps ) { + if ( cellH ) + vScrollBar->setSteps( QMIN(cellH,viewHeight()/2), viewHeight() ); + else + vScrollBar->setSteps( 16, viewHeight() ); // fttb! ### + } + + if ( sbDirty & verRange ) + vScrollBar->setRange( 0, maxYOffset() ); + + if ( sbDirty & verValue ) + vScrollBar->setValue( yOffs ); + + // show scrollbar only when it has a sane geometry + if ( !vScrollBar->isVisible() ) + vScrollBar->show(); + } + if ( coveringCornerSquare && + ( (sbDirty & verGeometry ) || (sbDirty & horGeometry)) ) + cornerSquare->move( maxViewX() + frameWidth() + 1, + maxViewY() + frameWidth() + 1 ); + + sbDirty = 0; + inSbUpdate = FALSE; +} + + +void QtTableView::updateFrameSize() +{ + int rw = width() - ( testTableFlags(Tbl_vScrollBar) ? + VSBEXT : 0 ); + int rh = height() - ( testTableFlags(Tbl_hScrollBar) ? + HSBEXT : 0 ); + if ( rw < 0 ) + rw = 0; + if ( rh < 0 ) + rh = 0; + + if ( autoUpdate() ) { + int fh = frameRect().height(); + int fw = frameRect().width(); + setFrameRect( QRect(0,0,rw,rh) ); + + if ( rw != fw ) + update( QMIN(fw,rw) - frameWidth() - 2, 0, frameWidth()+4, rh ); + if ( rh != fh ) + update( 0, QMIN(fh,rh) - frameWidth() - 2, rw, frameWidth()+4 ); + } +} + + +/*! + Returns the maximum horizontal offset within the table of the + view's left edge in \e table coordinates. + + This is used mainly to set the horizontal scroll bar's range. + + \sa maxColOffset(), maxYOffset(), totalWidth() +*/ + +int QtTableView::maxXOffset() +{ + int tw = totalWidth(); + int maxOffs; + if ( testTableFlags(Tbl_scrollLastHCell) ) { + if ( nCols != 1) + maxOffs = tw - ( cellW ? cellW : cellWidth( nCols - 1 ) ); + else + maxOffs = tw - viewWidth(); + } else { + if ( testTableFlags(Tbl_snapToHGrid) ) { + if ( cellW ) { + maxOffs = tw - (viewWidth()/cellW)*cellW; + } else { + int goal = tw - viewWidth(); + int pos = tw; + int nextCol = nCols - 1; + int nextCellWidth = cellWidth( nextCol ); + while( nextCol > 0 && pos > goal + nextCellWidth ) { + pos -= nextCellWidth; + nextCellWidth = cellWidth( --nextCol ); + } + if ( goal + nextCellWidth == pos ) + maxOffs = goal; + else if ( goal < pos ) + maxOffs = pos; + else + maxOffs = 0; + } + } else { + maxOffs = tw - viewWidth(); + } + } + return maxOffs > 0 ? maxOffs : 0; +} + + +/*! + Returns the maximum vertical offset within the table of the + view's top edge in \e table coordinates. + + This is used mainly to set the vertical scroll bar's range. + + \sa maxRowOffset(), maxXOffset(), totalHeight() +*/ + +int QtTableView::maxYOffset() +{ + int th = totalHeight(); + int maxOffs; + if ( testTableFlags(Tbl_scrollLastVCell) ) { + if ( nRows != 1) + maxOffs = th - ( cellH ? cellH : cellHeight( nRows - 1 ) ); + else + maxOffs = th - viewHeight(); + } else { + if ( testTableFlags(Tbl_snapToVGrid) ) { + if ( cellH ) { + maxOffs = th - (viewHeight()/cellH)*cellH; + } else { + int goal = th - viewHeight(); + int pos = th; + int nextRow = nRows - 1; + int nextCellHeight = cellHeight( nextRow ); + while( nextRow > 0 && pos > goal + nextCellHeight ) { + pos -= nextCellHeight; + nextCellHeight = cellHeight( --nextRow ); + } + if ( goal + nextCellHeight == pos ) + maxOffs = goal; + else if ( goal < pos ) + maxOffs = pos; + else + maxOffs = 0; + } + } else { + maxOffs = th - viewHeight(); + } + } + return maxOffs > 0 ? maxOffs : 0; +} + + +/*! + Returns the index of the last column, which may be at the left edge + of the view. + + Depending on the \link setTableFlags() Tbl_scrollLastHCell\endlink flag, + this may or may not be the last column. + + \sa maxXOffset(), maxRowOffset() +*/ + +int QtTableView::maxColOffset() +{ + int mx = maxXOffset(); + if ( cellW ) + return mx/cellW; + else { + int xcd=0, col=0; + while ( col < nCols && mx > (xcd=cellWidth(col)) ) { + mx -= xcd; + col++; + } + return col; + } +} + + +/*! + Returns the index of the last row, which may be at the top edge of + the view. + + Depending on the \link setTableFlags() Tbl_scrollLastVCell\endlink flag, + this may or may not be the last row. + + \sa maxYOffset(), maxColOffset() +*/ + +int QtTableView::maxRowOffset() +{ + int my = maxYOffset(); + if ( cellH ) + return my/cellH; + else { + int ycd=0, row=0; + while ( row < nRows && my > (ycd=cellHeight(row)) ) { + my -= ycd; + row++; + } + return row; + } +} + + +void QtTableView::showOrHideScrollBars() +{ + if ( !autoUpdate() ) + return; + if ( vScrollBar ) { + if ( testTableFlags(Tbl_vScrollBar) ) { + if ( !vScrollBar->isVisible() ) + sbDirty = sbDirty | verMask; + } else { + if ( vScrollBar->isVisible() ) + vScrollBar->hide(); + } + } + if ( hScrollBar ) { + if ( testTableFlags(Tbl_hScrollBar) ) { + if ( !hScrollBar->isVisible() ) + sbDirty = sbDirty | horMask; + } else { + if ( hScrollBar->isVisible() ) + hScrollBar->hide(); + } + } + if ( cornerSquare ) { + if ( testTableFlags(Tbl_hScrollBar) && + testTableFlags(Tbl_vScrollBar) ) { + if ( !cornerSquare->isVisible() ) + cornerSquare->show(); + } else { + if ( cornerSquare->isVisible() ) + cornerSquare->hide(); + } + } +} + + +/*! + Updates the scroll bars and internal state. + + Call this function when the table view's total size is changed; + typically because the result of cellHeight() or cellWidth() have changed. + + This function does not repaint the widget. +*/ + +void QtTableView::updateTableSize() +{ + bool updateOn = autoUpdate(); + setAutoUpdate( FALSE ); + int xofs = xOffset(); + xOffs++; //so that setOffset will not return immediately + setOffset(xofs,yOffset(),FALSE); //to calculate internal state correctly + setAutoUpdate(updateOn); + + updateScrollBars( horSteps | horRange | + verSteps | verRange ); + showOrHideScrollBars(); +} + + +#include "qttableview.moc" |