diff options
author | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
---|---|---|
committer | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
commit | f15b8a83e2e51955776a3f07cb85ebfc342dd8ef (patch) | |
tree | c5dc684986051654898db11ce73e03b9fec8db99 /src/qt3support/itemviews | |
download | Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.zip Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.gz Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.bz2 |
Initial import of statemachine branch from the old kinetic repository
Diffstat (limited to 'src/qt3support/itemviews')
-rw-r--r-- | src/qt3support/itemviews/itemviews.pri | 11 | ||||
-rw-r--r-- | src/qt3support/itemviews/q3iconview.cpp | 6203 | ||||
-rw-r--r-- | src/qt3support/itemviews/q3iconview.h | 519 | ||||
-rw-r--r-- | src/qt3support/itemviews/q3listbox.cpp | 4687 | ||||
-rw-r--r-- | src/qt3support/itemviews/q3listbox.h | 429 | ||||
-rw-r--r-- | src/qt3support/itemviews/q3listview.cpp | 7948 | ||||
-rw-r--r-- | src/qt3support/itemviews/q3listview.h | 609 | ||||
-rw-r--r-- | src/qt3support/itemviews/q3table.cpp | 7333 | ||||
-rw-r--r-- | src/qt3support/itemviews/q3table.h | 548 |
9 files changed, 28287 insertions, 0 deletions
diff --git a/src/qt3support/itemviews/itemviews.pri b/src/qt3support/itemviews/itemviews.pri new file mode 100644 index 0000000..cfc8c2d --- /dev/null +++ b/src/qt3support/itemviews/itemviews.pri @@ -0,0 +1,11 @@ +# Qt compat module, old itemviews + +HEADERS += itemviews/q3iconview.h \ + itemviews/q3listbox.h \ + itemviews/q3listview.h \ + itemviews/q3table.h + +SOURCES += itemviews/q3iconview.cpp \ + itemviews/q3listbox.cpp \ + itemviews/q3listview.cpp \ + itemviews/q3table.cpp diff --git a/src/qt3support/itemviews/q3iconview.cpp b/src/qt3support/itemviews/q3iconview.cpp new file mode 100644 index 0000000..d1a9c1e --- /dev/null +++ b/src/qt3support/itemviews/q3iconview.cpp @@ -0,0 +1,6203 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglobal.h" +#if defined(Q_CC_BOR) +// needed for qsort() because of a std namespace problem on Borland +#include "qplatformdefs.h" +#endif + +#include "q3iconview.h" + +#ifndef QT_NO_ICONVIEW + +#include "private/q3richtext_p.h" +#include "q3textedit.h" +#include "qapplication.h" +#include "qbitmap.h" +#include "qbrush.h" +#include "q3cleanuphandler.h" +#include "qcursor.h" +#include "qevent.h" +#include "qfontmetrics.h" +#include "qhash.h" +#include "qimage.h" +#include "qmime.h" +#include "qpainter.h" +#include "qpalette.h" +#include "qpen.h" +#include "qpixmapcache.h" +#include "qstringlist.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtimer.h" +#include "qtooltip.h" +#include "q3strlist.h" + +#include <limits.h> +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +#define RECT_EXTENSION 300 + +static const char * const unknown_xpm[] = { + "32 32 11 1", + "c c #ffffff", + "g c #c0c0c0", + "a c #c0ffc0", + "h c #a0a0a4", + "d c #585858", + "f c #303030", + "i c #400000", + "b c #00c000", + "e c #000000", + "# c #000000", + ". c None", + "...###..........................", + "...#aa##........................", + ".###baaa##......................", + ".#cde#baaa##....................", + ".#cccdeebaaa##..##f.............", + ".#cccccdeebaaa##aaa##...........", + ".#cccccccdeebaaaaaaaa##.........", + ".#cccccccccdeebaaaaaaa#.........", + ".#cccccgcgghhebbbbaaaaa#........", + ".#ccccccgcgggdebbbbbbaa#........", + ".#cccgcgcgcgghdeebiebbba#.......", + ".#ccccgcggggggghdeddeeba#.......", + ".#cgcgcgcggggggggghghdebb#......", + ".#ccgcggggggggghghghghd#b#......", + ".#cgcgcggggggggghghghhd#b#......", + ".#gcggggggggghghghhhhhd#b#......", + ".#cgcggggggggghghghhhhd#b#......", + ".#ggggggggghghghhhhhhhdib#......", + ".#gggggggggghghghhhhhhd#b#......", + ".#hhggggghghghhhhhhhhhd#b#......", + ".#ddhhgggghghghhhhhhhhd#b#......", + "..##ddhhghghhhhhhhhhhhdeb#......", + "....##ddhhhghhhhhhhhhhd#b#......", + "......##ddhhhhhhhhhhhhd#b#......", + "........##ddhhhhhhhhhhd#b#......", + "..........##ddhhhhhhhhd#b#......", + "............##ddhhhhhhd#b###....", + "..............##ddhhhhd#b#####..", + "................##ddhhd#b######.", + "..................##dddeb#####..", + "....................##d#b###....", + "......................####......"}; + +static QPixmap *unknown_icon = 0; +static QPixmap *qiv_buffer_pixmap = 0; +#if !defined(Q_WS_X11) +static QPixmap *qiv_selection = 0; +#endif +static bool optimize_layout = false; + +static Q3CleanupHandler<QPixmap> qiv_cleanup_pixmap; + + +static QPixmap *get_qiv_buffer_pixmap(const QSize &s) +{ + if (!qiv_buffer_pixmap) { + qiv_buffer_pixmap = new QPixmap(s); + qiv_cleanup_pixmap.add(&qiv_buffer_pixmap); + return qiv_buffer_pixmap; + } + + qiv_buffer_pixmap->resize(s); + return qiv_buffer_pixmap; +} + +#ifndef QT_NO_DRAGANDDROP + +class Q_COMPAT_EXPORT Q3IconDragData +{ +public: + Q3IconDragData(); + Q3IconDragData(const QRect &ir, const QRect &tr); + + QRect pixmapRect() const; + QRect textRect() const; + + void setPixmapRect(const QRect &r); + void setTextRect(const QRect &r); + + QRect iconRect_, textRect_; + QString key_; + + bool operator==(const Q3IconDragData &i) const; +}; + +class Q_COMPAT_EXPORT Q3IconDragDataItem +{ +public: + Q3IconDragDataItem() {} + Q3IconDragDataItem(const Q3IconDragItem &i1, const Q3IconDragData &i2) : data(i1), item(i2) {} + Q3IconDragItem data; + Q3IconDragData item; + bool operator== (const Q3IconDragDataItem&) const; +}; + +class Q3IconDragPrivate +{ +public: + QLinkedList<Q3IconDragDataItem> items; + static bool decode(QMimeSource* e, QLinkedList<Q3IconDragDataItem> &lst); +}; + +#endif + +class Q3IconViewPrivate +{ +public: + Q3IconViewItem *firstItem, *lastItem; + uint count; + Q3IconView::SelectionMode selectionMode; + Q3IconViewItem *currentItem, *tmpCurrentItem, *highlightedItem, + *startDragItem, *pressedItem, *selectAnchor, *renamingItem; + QRect *rubber; + QTimer *scrollTimer, *adjustTimer, *updateTimer, *inputTimer, + *fullRedrawTimer; + int rastX, rastY, spacing; + int dragItems; + QPoint oldDragPos; + Q3IconView::Arrangement arrangement; + Q3IconView::ResizeMode resizeMode; + QSize oldSize; +#ifndef QT_NO_DRAGANDDROP + QLinkedList<Q3IconDragDataItem> iconDragData; +#endif + int numDragItems, cachedW, cachedH; + int maxItemWidth, maxItemTextLength; + QPoint dragStart; + QString currInputString; + Q3IconView::ItemTextPos itemTextPos; +#ifndef QT_NO_CURSOR + QCursor oldCursor; +#endif + int cachedContentsX, cachedContentsY; + QBrush itemTextBrush; + QRegion clipRegion; + QPoint dragStartPos; + QFontMetrics *fm; + int minLeftBearing, minRightBearing; + + uint mousePressed : 1; + uint cleared : 1; + uint dropped : 1; + uint clearing : 1; + uint oldDragAcceptAction : 1; + uint isIconDrag : 1; + uint drawDragShapes : 1; + uint dirty : 1; + uint rearrangeEnabled : 1; + uint reorderItemsWhenInsert : 1; + uint drawAllBack : 1; + uint resortItemsWhenInsert : 1; + uint sortDirection : 1; + uint wordWrapIconText : 1; + uint containerUpdateLocked : 1; + uint firstSizeHint : 1; + uint showTips : 1; + uint pressedSelected : 1; + uint dragging : 1; + uint drawActiveSelection : 1; + uint inMenuMode : 1; + + QPoint dragPos; + QPixmapCache maskCache; + QHash<Q3IconViewItem *, Q3IconViewItem *> selectedItems; + + struct ItemContainer { + ItemContainer(ItemContainer *pr, ItemContainer *nx, const QRect &r) + : p(pr), n(nx), rect(r) + { + if (p) + p->n = this; + if (n) + n->p = this; + } + ItemContainer *p, *n; + QRect rect; + QList<Q3IconViewItem*> items; + } *firstContainer, *lastContainer; + + struct SortableItem { + Q3IconViewItem *item; + }; + +public: + + /* finds the containers that intersect with \a searchRect in the direction \a dir relative to \a relativeTo */ + QList<ItemContainer* >* findContainers( + Q3IconView:: Direction dir, + const QPoint &relativeTo, + const QRect &searchRect) const; + // friend int cmpIconViewItems(const void *n1, const void *n2); +}; + + +QList<Q3IconViewPrivate::ItemContainer *>* Q3IconViewPrivate::findContainers( + Q3IconView:: Direction dir, + const QPoint &relativeTo, + const QRect &searchRect) const +{ + + QList<Q3IconViewPrivate::ItemContainer *>* list = + new QList<Q3IconViewPrivate::ItemContainer*>(); + + if (arrangement == Q3IconView::LeftToRight) { + if (dir == Q3IconView::DirLeft || dir == Q3IconView::DirRight) { + ItemContainer *c = firstContainer; + for (; c; c = c->n) + if (c->rect.intersects(searchRect)) + list->append(c); + } else { + if (dir == Q3IconView::DirDown) { + ItemContainer *c = firstContainer; + for (; c; c = c->n) + if (c->rect.intersects(searchRect) && + c->rect.bottom() >= relativeTo.y()) + list->append(c); + } else { + ItemContainer *c = lastContainer; + for (; c; c = c->p) + if (c->rect.intersects(searchRect) && + c->rect.top() <= relativeTo.y()) + list->append(c); + } + } + } else { + if (dir == Q3IconView::DirUp || dir == Q3IconView::DirDown) { + ItemContainer *c = firstContainer; + for (; c; c = c->n) + if (c->rect.intersects(searchRect)) + list->append(c); + } else { + if (dir == Q3IconView::DirRight) { + ItemContainer *c = firstContainer; + for (; c; c = c->n) + if (c->rect.intersects(searchRect) && + c->rect.right() >= relativeTo.x()) + list->append(c); + } else { + ItemContainer *c = lastContainer; + for (; c; c = c->p) + if (c->rect.intersects(searchRect) && + c->rect.left() <= relativeTo.x()) + list->append(c); + } + } + } + return list; +} + + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +#ifdef Q_OS_WINCE +static int _cdecl cmpIconViewItems(const void *n1, const void *n2) +#else +static int cmpIconViewItems(const void *n1, const void *n2) +#endif +{ + if (!n1 || !n2) + return 0; + + Q3IconViewPrivate::SortableItem *i1 = (Q3IconViewPrivate::SortableItem *)n1; + Q3IconViewPrivate::SortableItem *i2 = (Q3IconViewPrivate::SortableItem *)n2; + + return i1->item->compare(i2->item); +} + +#if defined(Q_C_CALLBACKS) +} +#endif + +class Q3IconViewItemPrivate +{ +public: + Q3IconViewPrivate::ItemContainer *container1, *container2; +}; + +#ifndef QT_NO_TEXTEDIT + +class Q3IconViewItemLineEdit : public Q3TextEdit +{ + friend class Q3IconViewItem; + +public: + Q3IconViewItemLineEdit(const QString &text, QWidget *parent, Q3IconViewItem *theItem, const char* name=0); + +protected: + void keyPressEvent(QKeyEvent *e); + void focusOutEvent(QFocusEvent *e); + +protected: + Q3IconViewItem *item; + QString startText; + +private: + Q_DISABLE_COPY(Q3IconViewItemLineEdit) +}; + +Q3IconViewItemLineEdit::Q3IconViewItemLineEdit(const QString &text, QWidget *parent, + Q3IconViewItem *theItem, const char *name) + : Q3TextEdit(parent, name), item(theItem), startText(text) +{ + setFrameStyle(QFrame::Plain | QFrame::Box); + setLineWidth(1); + + setHScrollBarMode(AlwaysOff); + setVScrollBarMode(AlwaysOff); + + setWordWrap(WidgetWidth); + setWrapColumnOrWidth(item->iconView()->maxItemWidth() - + (item->iconView()->itemTextPos() == Q3IconView::Bottom ? + 0 : item->pixmapRect().width())); + document()->formatter()->setAllowBreakInWords(true); + resize(200, 200); // ### some size, there should be a forceReformat() + setTextFormat(Qt::PlainText); + setText(text); + setAlignment(Qt::AlignCenter); + + resize(wrapColumnOrWidth() + 2, heightForWidth(wrapColumnOrWidth()) + 2); +} + +void Q3IconViewItemLineEdit::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Escape) { + item->Q3IconViewItem::setText(startText); + item->cancelRenameItem(); + } else if (e->key() == Qt::Key_Enter || + e->key() == Qt::Key_Return) { + item->renameItem(); + } else { + Q3TextEdit::keyPressEvent(e); + sync(); + resize(width(), document()->height() + 2); + + } +} + +void Q3IconViewItemLineEdit::focusOutEvent(QFocusEvent *e) +{ + Q_UNUSED(e) // I need this to get rid of a Borland warning + if (e->reason() != Qt::PopupFocusReason) + item->cancelRenameItem(); +} +#endif + +#ifndef QT_NO_DRAGANDDROP + + +/*! + \class Q3IconDragItem + \brief The Q3IconDragItem class encapsulates a drag item. + \compat + + The Q3IconDrag class uses a list of Q3IconDragItems to support drag + and drop operations. + + In practice a Q3IconDragItem object (or an object of a class derived + from Q3IconDragItem) is created for each icon view item which is + dragged. Each of these Q3IconDragItems is stored in a Q3IconDrag + object. + + See Q3IconView::dragObject() for more information. +*/ + +/*! + Constructs a Q3IconDragItem with no data. +*/ + +Q3IconDragItem::Q3IconDragItem() +{ + ba = "no data"; +} + +/*! + Destructor. +*/ + +Q3IconDragItem::~Q3IconDragItem() +{ +} + +/*! + Returns the data contained in the Q3IconDragItem. +*/ + +QByteArray Q3IconDragItem::data() const +{ + return ba; +} + +/*! + Sets the data for the Q3IconDragItem to the data stored in the + QByteArray \a d. +*/ + +void Q3IconDragItem::setData(const QByteArray &d) +{ + ba = d; +} + +/*! + \internal +*/ + +bool Q3IconDragItem::operator==(const Q3IconDragItem &i) const +{ + return ba == i.ba; +} + +/*! + \reimp +*/ + +bool Q3IconDragDataItem::operator==(const Q3IconDragDataItem &i) const +{ + return (i.item == item && + i.data == data); +} + +/*! + \reimp +*/ + +bool Q3IconDragData::operator==(const Q3IconDragData &i) const +{ + return key_ == i.key_; +} + + +/*! + \class Q3IconDrag + \brief The Q3IconDrag class supports drag and drop operations + within a Q3IconView. + + \compat + + A Q3IconDrag object is used to maintain information about the + positions of dragged items and the data associated with them. + Q3IconViews are able to use this information to paint the dragged + items in the correct positions. Internally, Q3IconDrag stores the + data associated with drag items in Q3IconDragItem objects. + + If you want to use the extended drag and drop functionality of + Q3IconView, create a Q3IconDrag object in a reimplementation of + Q3IconView::dragObject(). Then create a Q3IconDragItem for each item + which should be dragged, set the data it represents with + Q3IconDragItem::setData(), and add each Q3IconDragItem to the drag + object using append(). + + The data in Q3IconDragItems is stored in a QByteArray and is + mime-typed (see QMimeSource and the + \link http://doc.trolltech.com/dnd.html Drag and Drop\endlink + overview). If you want to use your own mime-types derive a class + from Q3IconDrag and reimplement format(), encodedData() and + canDecode(). + + The fileiconview example program demonstrates the use of the + Q3IconDrag class including subclassing and reimplementing + dragObject(), format(), encodedData() and canDecode(). + + \sa QMimeSource::format() +*/ + +/*! + Constructs a drag object called \a name, which is a child of \a + dragSource. + + Note that the drag object will be deleted when \a dragSource is deleted. +*/ + +Q3IconDrag::Q3IconDrag(QWidget * dragSource, const char* name) + : Q3DragObject(dragSource, name) +{ + d = new Q3IconDragPrivate; +} + +/*! + Destructor. +*/ + +Q3IconDrag::~Q3IconDrag() +{ + delete d; +} + +/*! + Append the Q3IconDragItem, \a i, to the Q3IconDrag object's list of + items. You must also supply the geometry of the pixmap, \a pr, and + the textual caption, \a tr. + + \sa Q3IconDragItem +*/ + +void Q3IconDrag::append(const Q3IconDragItem &i, const QRect &pr, const QRect &tr) +{ + d->items.append(Q3IconDragDataItem(i, Q3IconDragData(pr, tr))); +} + +/*! + \reimp +*/ + +const char* Q3IconDrag::format(int i) const +{ + if (i == 0) + return "application/x-qiconlist"; + return 0; +} + +/*! + Returns the encoded data of the drag object if \a mime is + application/x-qiconlist. +*/ + +QByteArray Q3IconDrag::encodedData(const char* mime) const +{ + if (d->items.size() <= 0 || QString::fromLatin1(mime) != + QString::fromLatin1("application/x-qiconlist")) + return QByteArray(); + + QLinkedList<Q3IconDragDataItem>::ConstIterator it = d->items.begin(); + QString s; + for (; it != d->items.end(); ++it) { + QString k(QLatin1String("%1$@@$%2$@@$%3$@@$%4$@@$%5$@@$%6$@@$%7$@@$%8$@@$")); + k = k.arg((*it).item.pixmapRect().x()).arg( + (*it).item.pixmapRect().y()).arg((*it).item.pixmapRect().width()). + arg((*it).item.pixmapRect().height()).arg( + (*it).item.textRect().x()).arg((*it).item.textRect().y()). + arg((*it).item.textRect().width()).arg( + (*it).item.textRect().height()); + k += QString(QLatin1String((*it).data.data())) + QLatin1String("$@@$"); + s += k; + } + + QByteArray a; + a.resize(s.length() + 1); + memcpy(a.data(), s.latin1(), a.size()); + return a; +} + +/*! + Returns true if \a e can be decoded by the Q3IconDrag, otherwise + return false. +*/ + +bool Q3IconDrag::canDecode(QMimeSource* e) +{ + if (e->provides("application/x-qiconlist")) + return true; + return false; +} + +/*! + Decodes the data which is stored (encoded) in \a e and, if + successful, fills the \a list of icon drag items with the decoded + data. Returns true if there was some data, false otherwise. +*/ + +bool Q3IconDragPrivate::decode(QMimeSource* e, QLinkedList<Q3IconDragDataItem> &lst) +{ + QByteArray ba = e->encodedData("application/x-qiconlist"); + if (ba.size()) { + lst.clear(); + // #### unicode clean???? + QString s = QString::fromLatin1(ba); + Q3IconDragDataItem item; + QRect ir, tr; + QStringList l = QStringList::split(QLatin1String("$@@$"), s); + + int i = 0; + QStringList::Iterator it = l.begin(); + for (; it != l.end(); ++it) { + if (i == 0) { + ir.setX((*it).toInt()); + } else if (i == 1) { + ir.setY((*it).toInt()); + } else if (i == 2) { + ir.setWidth((*it).toInt()); + } else if (i == 3) { + ir.setHeight((*it).toInt()); + } else if (i == 4) { + tr.setX((*it).toInt()); + } else if (i == 5) { + tr.setY((*it).toInt()); + } else if (i == 6) { + tr.setWidth((*it).toInt()); + } else if (i == 7) { + tr.setHeight((*it).toInt()); + } else if (i == 8) { + QByteArray d; + d.resize((*it).length()); + memcpy(d.data(), (*it).latin1(), (*it).length()); + item.item.setPixmapRect(ir); + item.item.setTextRect(tr); + item.data.setData(d); + lst.append(item); + } + ++i; + if (i > 8) + i = 0; + } + return true; + } + + return false; +} + +Q3IconDragData::Q3IconDragData() + : iconRect_(), textRect_() +{ +} + +Q3IconDragData::Q3IconDragData(const QRect &ir, const QRect &tr) + : iconRect_(ir), textRect_(tr) +{ +} + +QRect Q3IconDragData::textRect() const +{ + return textRect_; +} + +QRect Q3IconDragData::pixmapRect() const +{ + return iconRect_; +} + +void Q3IconDragData::setPixmapRect(const QRect &r) +{ + iconRect_ = r; +} + +void Q3IconDragData::setTextRect(const QRect &r) +{ + textRect_ = r; +} + +#endif + + +/*! + \class Q3IconViewItem + \brief The Q3IconViewItem class provides a single item in a Q3IconView. + + \compat + + A Q3IconViewItem contains an icon, a string and optionally a sort + key, and can display itself in a Q3IconView. + + The simplest way to create a Q3IconViewItem and insert it into a + Q3IconView is to construct the item passing the constructor a + pointer to the icon view, a string and an icon: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 0 + + By default the text of an icon view item may not be edited by the + user but calling setRenameEnabled(true) will allow the user to + perform in-place editing of the item's text. + + When the icon view is deleted all items in it are deleted + automatically. + + The Q3IconView::firstItem() and Q3IconViewItem::nextItem() functions + provide a means of iterating over all the items in a Q3IconView: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 1 + + The item's icon view is available from iconView(), and its + position in the icon view from index(). + + The item's selection status is available from isSelected() and is + set and controlled by setSelected() and isSelectable(). + + The text and icon can be set with setText() and setPixmap() and + retrieved with text() and pixmap(). The item's sort key defaults + to text() but may be set with setKey() and retrieved with key(). + The comparison function, compare() uses key(). + + Items may be repositioned with move() and moveBy(). An item's + geometry is available from rect(), x(), y(), width(), height(), + size(), pos(), textRect() and pixmapRect(). You can also test + against the position of a point with contains() and intersects(). + + To remove an item from an icon view, just delete the item. The + Q3IconViewItem destructor removes it cleanly from its icon view. + + Because the icon view is designed to use drag-and-drop, the icon + view item also has functions for drag-and-drop which may be + reimplemented. + + The class is designed to be very similar to Q3ListView and Q3ListBox + in use, both via instantiation and subclassing. +*/ + +/*! + Constructs a Q3IconViewItem and inserts it into icon view \a parent + with no text and a default icon. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent) + : view(parent), itemText(), itemIcon(unknown_icon) +{ + init(); +} + +/*! + Constructs a Q3IconViewItem and inserts it into the icon view \a + parent with no text and a default icon, after the icon view item + \a after. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after) + : view(parent), itemText(), itemIcon(unknown_icon), + prev(0), next(0) +{ + init(after); +} + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and a default icon. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, const QString &text) + : view(parent), itemText(text), itemIcon(unknown_icon) +{ + init(0); +} + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and a default icon, after the + icon view item \a after. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, + const QString &text) + : view(parent), itemText(text), itemIcon(unknown_icon) +{ + init(after); +} + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and \a icon as the icon. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, const QString &text, + const QPixmap &icon) + : view(parent), + itemText(text), itemIcon(new QPixmap(icon)) +{ + init(0); +} + + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and \a icon as the icon, after + the icon view item \a after. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, + const QString &text, const QPixmap &icon) + : view(parent), itemText(text), itemIcon(new QPixmap(icon)) +{ + init(after); +} + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and \a picture as the icon. +*/ + +#ifndef QT_NO_PICTURE +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, const QString &text, + const QPicture &picture) + : view(parent), itemText(text), itemIcon(0) +{ + init(0, new QPicture(picture)); +} + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and \a picture as the icon, after + the icon view item \a after. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, + const QString &text, const QPicture &picture) + : view(parent), itemText(text), itemIcon(0) +{ + init(after, new QPicture(picture)); +} +#endif + +/*! + This private function initializes the icon view item and inserts it + into the icon view. +*/ + +void Q3IconViewItem::init(Q3IconViewItem *after +#ifndef QT_NO_PICTURE + , QPicture *pic +#endif + ) +{ + d = new Q3IconViewItemPrivate; + d->container1 = 0; + d->container2 = 0; + prev = next = 0; + allow_rename = false; + allow_drag = true; + allow_drop = true; + selected = false; + selectable = true; +#ifndef QT_NO_TEXTEDIT + renameBox = 0; +#endif +#ifndef QT_NO_PICTURE + itemPic = pic; +#endif + if (view) { + itemKey = itemText; + dirty = true; + wordWrapDirty = true; + itemRect = QRect(-1, -1, 0, 0); + calcRect(); + view->insertItem(this, after); + } +} + +/*! + Destroys the icon view item and tells the parent icon view that + the item has been destroyed. +*/ + +Q3IconViewItem::~Q3IconViewItem() +{ +#ifndef QT_NO_TEXTEDIT + removeRenameBox(); +#endif + if (view && !view->d->clearing) + view->takeItem(this); + view = 0; + if (itemIcon && itemIcon->serialNumber() != unknown_icon->serialNumber()) + delete itemIcon; +#ifndef QT_NO_PICTURE + delete itemPic; +#endif + delete d; +} + +int Q3IconViewItem::RTTI = 0; + +/*! + Returns 0. + + Make your derived classes return their own values for rtti(), so + that you can distinguish between icon view item types. You should + use values greater than 1000, preferably a large random number, to + allow for extensions to this class. +*/ + +int Q3IconViewItem::rtti() const +{ + return RTTI; +} + + +/*! + Sets \a text as the text of the icon view item. This function + might be a no-op if you reimplement text(). + + \sa text() +*/ + +void Q3IconViewItem::setText(const QString &text) +{ + if (text == itemText) + return; + + wordWrapDirty = true; + itemText = text; + if (itemKey.isEmpty()) + itemKey = itemText; + + QRect oR = rect(); + calcRect(); + oR = oR.united(rect()); + + if (view) { + if (QRect(view->contentsX(), view->contentsY(), + view->visibleWidth(), view->visibleHeight()). + intersects(oR)) + view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2); + } +} + +/*! + Sets \a k as the sort key of the icon view item. By default + text() is used for sorting. + + \sa compare() +*/ + +void Q3IconViewItem::setKey(const QString &k) +{ + if (k == itemKey) + return; + + itemKey = k; +} + +/*! + Sets \a icon as the item's icon in the icon view. This function + might be a no-op if you reimplement pixmap(). + + \sa pixmap() +*/ + +void Q3IconViewItem::setPixmap(const QPixmap &icon) +{ + if (itemIcon && itemIcon == unknown_icon) + itemIcon = 0; + + if (itemIcon) + *itemIcon = icon; + else + itemIcon = new QPixmap(icon); + QRect oR = rect(); + calcRect(); + oR = oR.united(rect()); + + if (view) { + if (QRect(view->contentsX(), view->contentsY(), + view->visibleWidth(), view->visibleHeight()). + intersects(oR)) + view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2); + } +} + +/*! + Sets \a icon as the item's icon in the icon view. This function + might be a no-op if you reimplement picture(). + + \sa picture() +*/ + +#ifndef QT_NO_PICTURE +void Q3IconViewItem::setPicture(const QPicture &icon) +{ + // clear assigned pixmap if any + if (itemIcon) { + if (itemIcon == unknown_icon) { + itemIcon = 0; + } else { + delete itemIcon; + itemIcon = 0; + } + } + if (itemPic) + delete itemPic; + itemPic = new QPicture(icon); + + QRect oR = rect(); + calcRect(); + oR = oR.united(rect()); + + if (view) { + if (QRect(view->contentsX(), view->contentsY(), + view->visibleWidth(), view->visibleHeight()). + intersects(oR)) + view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2); + } +} +#endif + +/*! + \overload + + Sets \a text as the text of the icon view item. If \a recalc is + true, the icon view's layout is recalculated. If \a redraw is true + (the default), the icon view is repainted. + + \sa text() +*/ + +void Q3IconViewItem::setText(const QString &text, bool recalc, bool redraw) +{ + if (text == itemText) + return; + + wordWrapDirty = true; + itemText = text; + + if (recalc) + calcRect(); + if (redraw) + repaint(); +} + +/*! + \overload + + Sets \a icon as the item's icon in the icon view. If \a recalc is + true, the icon view's layout is recalculated. If \a redraw is true + (the default), the icon view is repainted. + + \sa pixmap() +*/ + +void Q3IconViewItem::setPixmap(const QPixmap &icon, bool recalc, bool redraw) +{ + if (itemIcon && itemIcon == unknown_icon) + itemIcon = 0; + + if (itemIcon) + *itemIcon = icon; + else + itemIcon = new QPixmap(icon); + + if (redraw) { + if (recalc) { + QRect oR = rect(); + calcRect(); + oR = oR.united(rect()); + + if (view) { + if (QRect(view->contentsX(), view->contentsY(), + view->visibleWidth(), view->visibleHeight()). + intersects(oR)) + view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2); + } + } else { + repaint(); + } + } else if (recalc) { + calcRect(); + } +} + +/*! + If \a allow is true, the user can rename the icon view item by + clicking on the text (or pressing F2) while the item is selected + (in-place renaming). If \a allow is false, in-place renaming is + not possible. +*/ + +void Q3IconViewItem::setRenameEnabled(bool allow) +{ + allow_rename = (uint)allow; +} + +/*! + If \a allow is true, the icon view permits the user to drag the + icon view item either to another position within the icon view or + to somewhere outside of it. If \a allow is false, the item cannot + be dragged. +*/ + +void Q3IconViewItem::setDragEnabled(bool allow) +{ + allow_drag = (uint)allow; +} + +/*! + If \a allow is true, the icon view lets the user drop something on + this icon view item. +*/ + +void Q3IconViewItem::setDropEnabled(bool allow) +{ + allow_drop = (uint)allow; +} + +/*! + Returns the text of the icon view item. Normally you set the text + of the item with setText(), but sometimes it's inconvenient to + call setText() for every item; so you can subclass Q3IconViewItem, + reimplement this function, and return the text of the item. If you + do this, you must call calcRect() manually each time the text + (and therefore its size) changes. + + \sa setText() +*/ + +QString Q3IconViewItem::text() const +{ + return itemText; +} + +/*! + Returns the key of the icon view item or text() if no key has been + explicitly set. + + \sa setKey(), compare() +*/ + +QString Q3IconViewItem::key() const +{ + return itemKey; +} + +/*! + Returns the icon of the icon view item if it is a pixmap, or 0 if + it is a picture. In the latter case use picture() instead. + Normally you set the pixmap of the item with setPixmap(), but + sometimes it's inconvenient to call setPixmap() for every item. So + you can subclass Q3IconViewItem, reimplement this function and + return a pointer to the item's pixmap. If you do this, you \e must + call calcRect() manually each time the size of this pixmap + changes. + + \sa setPixmap() +*/ + +QPixmap *Q3IconViewItem::pixmap() const +{ + return itemIcon; +} + +/*! + Returns the icon of the icon view item if it is a picture, or 0 if + it is a pixmap. In the latter case use pixmap() instead. Normally + you set the picture of the item with setPicture(), but sometimes + it's inconvenient to call setPicture() for every item. So you can + subclass Q3IconViewItem, reimplement this function and return a + pointer to the item's picture. If you do this, you \e must call + calcRect() manually each time the size of this picture changes. + + \sa setPicture() +*/ + +#ifndef QT_NO_PICTURE +QPicture *Q3IconViewItem::picture() const +{ + return itemPic; +} +#endif + +/*! + Returns true if the item can be renamed by the user with in-place + renaming; otherwise returns false. + + \sa setRenameEnabled() +*/ + +bool Q3IconViewItem::renameEnabled() const +{ + return (bool)allow_rename; +} + +/*! + Returns true if the user is allowed to drag the icon view item; + otherwise returns false. + + \sa setDragEnabled() +*/ + +bool Q3IconViewItem::dragEnabled() const +{ + return (bool)allow_drag; +} + +/*! + Returns true if the user is allowed to drop something onto the + item; otherwise returns false. + + \sa setDropEnabled() +*/ + +bool Q3IconViewItem::dropEnabled() const +{ + return (bool)allow_drop; +} + +/*! + Returns a pointer to this item's icon view parent. +*/ + +Q3IconView *Q3IconViewItem::iconView() const +{ + return view; +} + +/*! + Returns a pointer to the previous item, or 0 if this is the first + item in the icon view. + + \sa nextItem() Q3IconView::firstItem() +*/ + +Q3IconViewItem *Q3IconViewItem::prevItem() const +{ + return prev; +} + +/*! + Returns a pointer to the next item, or 0 if this is the last item + in the icon view. + + To find the first item use Q3IconView::firstItem(). + + Example: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 2 + + \sa prevItem() +*/ + +Q3IconViewItem *Q3IconViewItem::nextItem() const +{ + return next; +} + +/*! + Returns the index of this item in the icon view, or -1 if an error + occurred. +*/ + +int Q3IconViewItem::index() const +{ + if (view) + return view->index(this); + + return -1; +} + + + +/*! + \overload + + This variant is equivalent to calling the other variant with \e cb + set to false. +*/ + +void Q3IconViewItem::setSelected(bool s) +{ + setSelected(s, false); +} + +/*! + Selects or unselects the item, depending on \a s; it may also + unselect other items, depending on Q3IconView::selectionMode() and + \a cb. + + If \a s is false, the item is unselected. + + If \a s is true and Q3IconView::selectionMode() is + Q3IconView::Single, the item is selected and the item previously + selected is unselected. + + If \a s is true and Q3IconView::selectionMode() is + Q3IconView::Extended, the item is selected. If \a cb is true, the + selection state of the other items is left unchanged. If \a cb is + false (the default) all other items are unselected. + + If \a s is true and Q3IconView::selectionMode() is + Q3IconView::Multi, the item is selected. + + Note that \a cb is used only if Q3IconView::selectionMode() is + Q3IconView::Extended; cb defaults to false. + + All items whose selection status changes repaint themselves. +*/ + +void Q3IconViewItem::setSelected(bool s, bool cb) +{ + if (!view) + return; + if (view->selectionMode() != Q3IconView::NoSelection && + selectable && s != (bool)selected) { + + if (view->d->selectionMode == Q3IconView::Single && this != view->d->currentItem) { + Q3IconViewItem *o = view->d->currentItem; + if (o && o->selected) + o->selected = false; + view->d->currentItem = this; + if (o) + o->repaint(); + emit view->currentChanged(this); + } + + if (!s) { + selected = false; + } else { + if (view->d->selectionMode == Q3IconView::Single && view->d->currentItem) { + view->d->currentItem->selected = false; + } + if ((view->d->selectionMode == Q3IconView::Extended && !cb) || + view->d->selectionMode == Q3IconView::Single) { + bool b = view->signalsBlocked(); + view->blockSignals(true); + view->selectAll(false); + view->blockSignals(b); + } + selected = s; + } + + repaint(); + if (!view->signalsBlocked()) { + bool emitIt = view->d->selectionMode == Q3IconView::Single && s; + Q3IconView *v = view; + emit v->selectionChanged(); + if (emitIt) + emit v->selectionChanged(this); + } + } +} + +/*! + Sets this item to be selectable if \a enable is true (the default) + or unselectable if \a enable is false. + + The user is unable to select a non-selectable item using either + the keyboard or the mouse. (The application programmer can select + an item in code regardless of this setting.) + + \sa isSelectable() +*/ + +void Q3IconViewItem::setSelectable(bool enable) +{ + selectable = (uint)enable; +} + +/*! + Returns true if the item is selected; otherwise returns false. + + \sa setSelected() +*/ + +bool Q3IconViewItem::isSelected() const +{ + return (bool)selected; +} + +/*! + Returns true if the item is selectable; otherwise returns false. + + \sa setSelectable() +*/ + +bool Q3IconViewItem::isSelectable() const +{ + return (bool)selectable; +} + +/*! + Repaints the item. +*/ + +void Q3IconViewItem::repaint() +{ + if (view) + view->repaintItem(this); +} + +/*! + Moves the item to position (\a x, \a y) in the icon view (these + are contents coordinates). Returns true if the item is moved. + Returns false if the item is already at the specified position. +*/ + +bool Q3IconViewItem::move(int x, int y) +{ + if (x == this->x() && y == this->y()) + return false; + itemRect.setRect(x, y, itemRect.width(), itemRect.height()); + checkRect(); + if (view) + view->updateItemContainer(this); + return true; +} + +/*! + Moves the item \a dx pixels in the x-direction and \a dy pixels in + the y-direction. +*/ + +void Q3IconViewItem::moveBy(int dx, int dy) +{ + itemRect.moveBy(dx, dy); + checkRect(); + if (view) + view->updateItemContainer(this); +} + +/*! + \overload + + Moves the item to the point \a pnt. +*/ + +bool Q3IconViewItem::move(const QPoint &pnt) +{ + return move(pnt.x(), pnt.y()); +} + +/*! + \overload + + Moves the item by the x, y values in point \a pnt. +*/ + +void Q3IconViewItem::moveBy(const QPoint &pnt) +{ + moveBy(pnt.x(), pnt.y()); +} + +/*! + Returns the bounding rectangle of the item (in contents + coordinates). +*/ + +QRect Q3IconViewItem::rect() const +{ + return itemRect; +} + +/*! + Returns the x-coordinate of the item (in contents coordinates). +*/ + +int Q3IconViewItem::x() const +{ + return itemRect.x(); +} + +/*! + Returns the y-coordinate of the item (in contents coordinates). +*/ + +int Q3IconViewItem::y() const +{ + return itemRect.y(); +} + +/*! + Returns the width of the item. +*/ + +int Q3IconViewItem::width() const +{ + return qMax(itemRect.width(), QApplication::globalStrut().width()); +} + +/*! + Returns the height of the item. +*/ + +int Q3IconViewItem::height() const +{ + return qMax(itemRect.height(), QApplication::globalStrut().height()); +} + +/*! + Returns the size of the item. +*/ + +QSize Q3IconViewItem::size() const +{ + return QSize(itemRect.width(), itemRect.height()); +} + +/*! + Returns the position of the item (in contents coordinates). +*/ + +QPoint Q3IconViewItem::pos() const +{ + return QPoint(itemRect.x(), itemRect.y()); +} + +/*! + Returns the bounding rectangle of the item's text. + + If \a relative is true, (the default), the returned rectangle is + relative to the origin of the item's rectangle. If \a relative is + false, the returned rectangle is relative to the origin of the + icon view's contents coordinate system. +*/ + +QRect Q3IconViewItem::textRect(bool relative) const +{ + if (relative) + return itemTextRect; + else + return QRect(x() + itemTextRect.x(), y() + itemTextRect.y(), itemTextRect.width(), itemTextRect.height()); +} + +/*! + Returns the bounding rectangle of the item's icon. + + If \a relative is true, (the default), the rectangle is relative to + the origin of the item's rectangle. If \a relative is false, the + returned rectangle is relative to the origin of the icon view's + contents coordinate system. +*/ + +QRect Q3IconViewItem::pixmapRect(bool relative) const +{ + if (relative) + return itemIconRect; + else + return QRect(x() + itemIconRect.x(), y() + itemIconRect.y(), itemIconRect.width(), itemIconRect.height()); +} + +/*! + Returns true if the item contains the point \a pnt (in contents + coordinates); otherwise returns false. +*/ + +bool Q3IconViewItem::contains(const QPoint& pnt) const +{ + QRect textArea = textRect(false); + QRect pixmapArea = pixmapRect(false); + if (iconView()->itemTextPos() == Q3IconView::Bottom) + textArea.setTop(pixmapArea.bottom()); + else + textArea.setLeft(pixmapArea.right()); + return textArea.contains(pnt) || pixmapArea.contains(pnt); +} + +/*! + Returns true if the item intersects the rectangle \a r (in + contents coordinates); otherwise returns false. +*/ + +bool Q3IconViewItem::intersects(const QRect& r) const +{ + return (textRect(false).intersects(r) || + pixmapRect(false).intersects(r)); +} + +/*! + \fn bool Q3IconViewItem::acceptDrop(const QMimeSource *mime) const + + Returns true if you can drop things with a QMimeSource of \a mime + onto this item; otherwise returns false. + + The default implementation always returns false. You must subclass + Q3IconViewItem and reimplement acceptDrop() to accept drops. +*/ + +bool Q3IconViewItem::acceptDrop(const QMimeSource *) const +{ + return false; +} + +#ifndef QT_NO_TEXTEDIT +/*! + Starts in-place renaming of an icon, if allowed. + + This function sets up the icon view so that the user can edit the + item text, and then returns. When the user is done, setText() will + be called and Q3IconView::itemRenamed() will be emitted (unless the + user canceled, e.g. by pressing the Escape key). + + \sa setRenameEnabled() +*/ + +void Q3IconViewItem::rename() +{ + if (!view) + return; + if (renameBox) + removeRenameBox(); + oldRect = rect(); + renameBox = new Q3IconViewItemLineEdit(itemText, view->viewport(), this, "qt_renamebox"); + iconView()->ensureItemVisible(this); + QRect tr(textRect(false)); + view->addChild(renameBox, tr.x() + (tr.width() / 2 - renameBox->width() / 2), tr.y() - 3); + renameBox->selectAll(); + view->viewport()->setFocusProxy(renameBox); + renameBox->setFocus(); + renameBox->show(); + Q_ASSERT(view->d->renamingItem == 0L); + view->d->renamingItem = this; +} +#endif + +/*! + Compares this icon view item to \a i. Returns -1 if this item is + less than \a i, 0 if they are equal, and 1 if this icon view item + is greater than \a i. + + The default implementation compares the item keys (key()) using + QString::localeAwareCompare(). A reimplementation may use + different values and a different comparison function. Here is a + reimplementation that uses plain Unicode comparison: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 3 + + \sa key() QString::localeAwareCompare() QString::compare() +*/ + +int Q3IconViewItem::compare(Q3IconViewItem *i) const +{ + return key().localeAwareCompare(i->key()); +} + +#ifndef QT_NO_TEXTEDIT +/*! + This private function is called when the user pressed Return during + in-place renaming. +*/ + +void Q3IconViewItem::renameItem() +{ + if (!renameBox || !view) + return; + + if (!view->d->wordWrapIconText) { + wordWrapDirty = true; + calcRect(); + } + QRect r = itemRect; + setText(renameBox->text()); + view->repaintContents(oldRect.x() - 1, oldRect.y() - 1, oldRect.width() + 2, oldRect.height() + 2); + view->repaintContents(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2); + removeRenameBox(); + + view->emitRenamed(this); +} + +/*! + Cancels in-place renaming. +*/ + +void Q3IconViewItem::cancelRenameItem() +{ + if (!view) + return; + + QRect r = itemRect; + calcRect(); + view->repaintContents(oldRect.x() - 1, oldRect.y() - 1, oldRect.width() + 2, oldRect.height() + 2); + view->repaintContents(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2); + + if (!renameBox) + return; + + removeRenameBox(); +} + +/*! + Removes the editbox that is used for in-place renaming. +*/ + +void Q3IconViewItem::removeRenameBox() +{ + if (!renameBox || !view) + return; + + bool resetFocus = view->viewport()->focusProxy() == renameBox; + renameBox->hide(); + renameBox->deleteLater(); + renameBox = 0; + if (resetFocus) { + view->viewport()->setFocusProxy(view); + view->setFocus(); + } + Q_ASSERT(view->d->renamingItem == this); + view->d->renamingItem = 0L; +} +#endif + +/*! + This virtual function is responsible for calculating the + rectangles returned by rect(), textRect() and pixmapRect(). + setRect(), setTextRect() and setPixmapRect() are provided mainly + for reimplementations of this function. + + \a text_ is an internal parameter which defaults to an empty + string. +*/ + +void Q3IconViewItem::calcRect(const QString &text_) +{ + if (!view) // ##### + return; + + wordWrapDirty = true; + int pw = 0; + int ph = 0; + +#ifndef QT_NO_PICTURE + if (picture()) { + QRect br = picture()->boundingRect(); + pw = br.width() + 2; + ph = br.height() + 2; + } else +#endif + { + pw = (pixmap() ? pixmap() : unknown_icon)->width() + 2; + ph = (pixmap() ? pixmap() : unknown_icon)->height() + 2; + } + + itemIconRect.setWidth(pw); + itemIconRect.setHeight(ph); + + calcTmpText(); + + QString t = text_; + if (t.isEmpty()) { + if (view->d->wordWrapIconText) + t = itemText; + else + t = tmpText; + } + + int tw = 0; + int th = 0; + // ##### TODO: fix font bearings! + QRect r; + if (view->d->wordWrapIconText) { + r = QRect(view->d->fm->boundingRect(0, 0, iconView()->maxItemWidth() - + (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 : + pixmapRect().width()), + 0xFFFFFFFF, Qt::AlignHCenter | Qt::WordBreak | Qt::BreakAnywhere, t)); + r.setWidth(r.width() + 4); + } else { + r = QRect(0, 0, view->d->fm->width(t), view->d->fm->height()); + r.setWidth(r.width() + 4); + } + + if (r.width() > iconView()->maxItemWidth() - + (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 : + pixmapRect().width())) + r.setWidth(iconView()->maxItemWidth() - (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 : + pixmapRect().width())); + + tw = r.width(); + th = r.height(); + if (tw < view->d->fm->width(QLatin1String("X"))) + tw = view->d->fm->width(QLatin1String("X")); + + itemTextRect.setWidth(tw); + itemTextRect.setHeight(th); + + int w = 0; + int h = 0; + if (view->itemTextPos() == Q3IconView::Bottom) { + w = qMax(itemTextRect.width(), itemIconRect.width()); + h = itemTextRect.height() + itemIconRect.height() + 1; + + itemRect.setWidth(w); + itemRect.setHeight(h); + + itemTextRect = QRect((width() - itemTextRect.width()) / 2, height() - itemTextRect.height(), + itemTextRect.width(), itemTextRect.height()); + itemIconRect = QRect((width() - itemIconRect.width()) / 2, 0, + itemIconRect.width(), itemIconRect.height()); + } else { + h = qMax(itemTextRect.height(), itemIconRect.height()); + w = itemTextRect.width() + itemIconRect.width() + 1; + + itemRect.setWidth(w); + itemRect.setHeight(h); + + itemTextRect = QRect(width() - itemTextRect.width(), (height() - itemTextRect.height()) / 2, + itemTextRect.width(), itemTextRect.height()); + itemIconRect = QRect(0, (height() - itemIconRect.height()) / 2, + itemIconRect.width(), itemIconRect.height()); + } + if (view) + view->updateItemContainer(this); +} + +/*! + Paints the item using the painter \a p and the color group \a cg. + If you want the item to be drawn with a different font or color, + reimplement this function, change the values of the color group or + the painter's font, and then call the Q3IconViewItem::paintItem() + with the changed values. +*/ + +void Q3IconViewItem::paintItem(QPainter *p, const QColorGroup &cg) +{ + if (!view) + return; + + p->save(); + + if (isSelected()) { + p->setPen(cg.highlightedText()); + } else { + p->setPen(cg.text()); + } + + calcTmpText(); + +#ifndef QT_NO_PICTURE + if (picture()) { + QPicture *pic = picture(); + if (isSelected()) { + p->fillRect(pixmapRect(false), QBrush(cg.highlight(), Qt::Dense4Pattern)); + } + p->drawPicture(x()-pic->boundingRect().x(), y()-pic->boundingRect().y(), *pic); + if (isSelected()) { + p->fillRect(textRect(false), cg.highlight()); + p->setPen(QPen(cg.highlightedText())); + } else if (view->d->itemTextBrush != QBrush(Qt::NoBrush)) + p->fillRect(textRect(false), view->d->itemTextBrush); + + int align = view->itemTextPos() == Q3IconView::Bottom ? Qt::AlignHCenter : Qt::AlignAuto; + if (view->d->wordWrapIconText) + align |= Qt::WordBreak | Qt::BreakAnywhere; + p->drawText(textRect(false), align, view->d->wordWrapIconText ? itemText : tmpText); + p->restore(); + return; + } +#endif + bool textOnBottom = (view->itemTextPos() == Q3IconView::Bottom); + int dim; + if (textOnBottom) + dim = (pixmap() ? pixmap() : unknown_icon)->width(); + else + dim = (pixmap() ? pixmap() : unknown_icon)->height(); + if (isSelected()) { + QPixmap *pix = pixmap() ? pixmap() : unknown_icon; + if (pix && !pix->isNull()) { + QPixmap *buffer = get_qiv_buffer_pixmap(pix->size()); + QBitmap mask = view->mask(pix); + + QPainter p2(buffer); + p2.fillRect(pix->rect(), Qt::white); + p2.drawPixmap(0, 0, *pix); + p2.end(); + + p2.begin(buffer); + p2.fillRect(pix->rect(), QBrush(cg.highlight(), Qt::Dense4Pattern)); + p2.end(); + buffer->setMask(mask); + + QRect cr = pix->rect(); + if (textOnBottom) + p->drawPixmap(x() + (width() - dim) / 2, y(), *buffer, 0, 0, + cr.width(), cr.height()); + else + p->drawPixmap(x() , y() + (height() - dim) / 2, *buffer, 0, 0, + cr.width(), cr.height()); + } + } else { + if (textOnBottom) + p->drawPixmap(x() + (width() - dim) / 2, y(), + *(pixmap() ? pixmap() : unknown_icon)); + else + p->drawPixmap(x() , y() + (height() - dim) / 2, + *(pixmap() ? pixmap() : unknown_icon)); + } + + p->save(); + if (isSelected()) { + p->fillRect(textRect(false), cg.highlight()); + p->setPen(QPen(cg.highlightedText())); + } else if (view->d->itemTextBrush != QBrush(Qt::NoBrush)) + p->fillRect(textRect(false), view->d->itemTextBrush); + + int align = Qt::AlignHCenter; + if (view->d->wordWrapIconText) + align |= Qt::WordBreak | Qt::BreakAnywhere; + p->drawText(textRect(false), align, + view->d->wordWrapIconText ? itemText : tmpText); + + p->restore(); + + p->restore(); +} + +/*! + Paints the focus rectangle of the item using the painter \a p and + the color group \a cg. +*/ + +void Q3IconViewItem::paintFocus(QPainter *p, const QColorGroup &cg) +{ + if (!view) + return; + + QStyleOptionFocusRect opt; + opt.rect = textRect(false); + opt.palette = cg; + if (isSelected()) { + opt.state = QStyle::State_FocusAtBorder; + opt.backgroundColor = cg.highlight(); + } else { + opt.state = QStyle::State_None; + opt.backgroundColor = cg.base(); + } + view->style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p); + + if (this != view->d->currentItem) { + opt.rect = pixmapRect(false); + opt.backgroundColor = cg.base(); + opt.state = QStyle::State_None; + view->style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p); + } +} + +#ifndef QT_NO_DRAGANDDROP +/*! + \fn void Q3IconViewItem::dropped(QDropEvent *e, const Q3ValueList<Q3IconDragItem> &lst) + + This function is called when something is dropped on the item. \a + e provides all the information about the drop. If the drag object + of the drop was a Q3IconDrag, \a lst contains the list of the + dropped items. You can get the data by calling + Q3IconDragItem::data() on each item. If the \a lst is empty, i.e. + the drag was not a Q3IconDrag, you must decode the data in \a e and + work with that. + + The default implementation does nothing; subclasses may + reimplement this function. +*/ + +void Q3IconViewItem::dropped(QDropEvent *, const Q3ValueList<Q3IconDragItem> &) +{ +} +#endif + +/*! + This function is called when a drag enters the item's bounding + rectangle. + + The default implementation does nothing; subclasses may + reimplement this function. +*/ + +void Q3IconViewItem::dragEntered() +{ +} + +/*! + This function is called when a drag leaves the item's bounding + rectangle. + + The default implementation does nothing; subclasses may + reimplement this function. +*/ + +void Q3IconViewItem::dragLeft() +{ +} + +/*! + Sets the bounding rectangle of the whole item to \a r. This + function is provided for subclasses which reimplement calcRect(), + so that they can set the calculated rectangle. \e{Any other use is + discouraged.} + + \sa calcRect() textRect() setTextRect() pixmapRect() setPixmapRect() +*/ + +void Q3IconViewItem::setItemRect(const QRect &r) +{ + itemRect = r; + checkRect(); + if (view) + view->updateItemContainer(this); +} + +/*! + Sets the bounding rectangle of the item's text to \a r. This + function is provided for subclasses which reimplement calcRect(), + so that they can set the calculated rectangle. \e{Any other use is + discouraged.} + + \sa calcRect() textRect() setItemRect() setPixmapRect() +*/ + +void Q3IconViewItem::setTextRect(const QRect &r) +{ + itemTextRect = r; + if (view) + view->updateItemContainer(this); +} + +/*! + Sets the bounding rectangle of the item's icon to \a r. This + function is provided for subclasses which reimplement calcRect(), + so that they can set the calculated rectangle. \e{Any other use is + discouraged.} + + \sa calcRect() pixmapRect() setItemRect() setTextRect() +*/ + +void Q3IconViewItem::setPixmapRect(const QRect &r) +{ + itemIconRect = r; + if (view) + view->updateItemContainer(this); +} + +/*! + \internal +*/ + +void Q3IconViewItem::calcTmpText() +{ + if (!view || view->d->wordWrapIconText || !wordWrapDirty) + return; + wordWrapDirty = false; + + int w = iconView()->maxItemWidth() - (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 : + pixmapRect().width()); + if (view->d->fm->width(itemText) < w) { + tmpText = itemText; + return; + } + + tmpText = QLatin1String("..."); + int i = 0; + while (view->d->fm->width(tmpText + itemText[i]) < w) + tmpText += itemText[i++]; + tmpText.remove((uint)0, 3); + tmpText += QLatin1String("..."); +} + +/*! \internal */ + +QString Q3IconViewItem::tempText() const +{ + return tmpText; +} + +void Q3IconViewItem::checkRect() +{ + int x = itemRect.x(); + int y = itemRect.y(); + int w = itemRect.width(); + int h = itemRect.height(); + + bool changed = false; + if (x < 0) { + x = 0; + changed = true; + } + if (y < 0) { + y = 0; + changed = true; + } + + if (changed) + itemRect.setRect(x, y, w, h); +} + + +/*! + \class Q3IconView + \brief The Q3IconView class provides an area with movable labelled icons. + + \compat + + A Q3IconView can display and manage a grid or other 2D layout of + labelled icons. Each labelled icon is a Q3IconViewItem. Items + (Q3IconViewItems) can be added or deleted at any time; items can be + moved within the Q3IconView. Single or multiple items can be + selected. Items can be renamed in-place. Q3IconView also supports + \link #draganddrop drag and drop\endlink. + + Each item contains a label string, a pixmap or picture (the icon + itself) and optionally a sort key. The sort key is used for + sorting the items and defaults to the label string. The label + string can be displayed below or to the right of the icon (see \l + ItemTextPos). + + The simplest way to create a Q3IconView is to create a Q3IconView + object and create some Q3IconViewItems with the Q3IconView as their + parent, set the icon view's geometry and show it. + For example: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 4 + + The Q3IconViewItem call passes a pointer to the Q3IconView we wish to + populate, along with the label text and a QPixmap. + + When an item is inserted the Q3IconView allocates a position for it. + Existing items are rearranged if autoArrange() is true. The + default arrangement is \l LeftToRight -- the Q3IconView fills up + the \e left-most column from top to bottom, then moves one column + \e right and fills that from top to bottom and so on. The + arrangement can be modified with any of the following approaches: + \list + \i Call setArrangement(), e.g. with \l TopToBottom which will fill + the \e top-most row from left to right, then moves one row \e down + and fills that row from left to right and so on. + \i Construct each Q3IconViewItem using a constructor which allows + you to specify which item the new one is to follow. + \i Call setSorting() or sort() to sort the items. + \endlist + + The spacing between items is set with setSpacing(). Items can be + laid out using a fixed grid using setGridX() and setGridY(); by + default the Q3IconView calculates a grid dynamically. The position + of items' label text is set with setItemTextPos(). The text's + background can be set with setItemTextBackground(). The maximum + width of an item and of its text are set with setMaxItemWidth() + and setMaxItemTextLength(). The label text will be word-wrapped if + it is too long; this is controlled by setWordWrapIconText(). If + the label text is truncated, the user can still see the entire + text in a tool tip if they hover the mouse over the item. This is + controlled with setShowToolTips(). + + Items which are \link Q3IconViewItem::isSelectable() + selectable\endlink may be selected depending on the SelectionMode; + the default is \l Single. Because Q3IconView offers multiple + selection it must display keyboard focus and selection state + separately. Therefore there are functions to set the selection + state of an item (setSelected()) and to select which item displays + keyboard focus (setCurrentItem()). When multiple items may be + selected the icon view provides a rubberband, too. + + When in-place renaming is enabled (it is disabled by default), the + user may change the item's label. They do this by selecting the item + (single clicking it or navigating to it with the arrow keys), then + single clicking it (or pressing F2), and entering their text. If no + key has been set with Q3IconViewItem::setKey() the new text will also + serve as the key. (See Q3IconViewItem::setRenameEnabled().) + + You can control whether users can move items themselves with + setItemsMovable(). + + Because the internal structure used to store the icon view items is + linear, no iterator class is needed to iterate over all the items. + Instead we iterate by getting the first item from the \e{icon view} + and then each subsequent (\l Q3IconViewItem::nextItem()) from each + \e item in turn: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 5 + Q3IconView also provides currentItem(). You can search for an item + using findItem() (searching by position or for label text) and + with findFirstVisibleItem() and findLastVisibleItem(). The number + of items is returned by count(). An item can be removed from an + icon view using takeItem(); to delete an item use \c delete. All + the items can be deleted with clear(). + + The Q3IconView emits a wide range of useful signals, including + selectionChanged(), currentChanged(), clicked(), moved() and + itemRenamed(). + + \target draganddrop + \section1 Drag and Drop + + Q3IconView supports the drag and drop of items within the Q3IconView + itself. It also supports the drag and drop of items out of or into + the Q3IconView and drag and drop onto items themselves. The drag and + drop of items outside the Q3IconView can be achieved in a simple way + with basic functionality, or in a more sophisticated way which + provides more power and control. + + The simple approach to dragging items out of the icon view is to + subclass Q3IconView and reimplement Q3IconView::dragObject(). + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 6 + + In this example we create a Q3TextDrag object, (derived from + Q3DragObject), containing the item's label and return it as the drag + object. We could just as easily have created a Q3ImageDrag from the + item's pixmap and returned that instead. + + Q3IconViews and their Q3IconViewItems can also be the targets of drag + and drops. To make the Q3IconView itself able to accept drops connect + to the dropped() signal. When a drop occurs this signal will be + emitted with a QDragEvent and a QLinkedList of Q3IconDragItems. To + make a Q3IconViewItem into a drop target subclass Q3IconViewItem and + reimplement Q3IconViewItem::acceptDrop() and + Q3IconViewItem::dropped(). + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 7 + + If you want to use extended drag-and-drop or have drag shapes drawn + you must take a more sophisticated approach. + + The first part is starting drags -- you should use a Q3IconDrag (or a + class derived from it) for the drag object. In dragObject() create the + drag object, populate it with Q3IconDragItems and return it. Normally + such a drag should offer each selected item's data. So in dragObject() + you should iterate over all the items, and create a Q3IconDragItem for + each selected item, and append these items with Q3IconDrag::append() to + the Q3IconDrag object. You can use Q3IconDragItem::setData() to set the + data of each item that should be dragged. If you want to offer the + data in additional mime-types, it's best to use a class derived from + Q3IconDrag, which implements additional encoding and decoding + functions. + + When a drag enters the icon view, there is little to do. Simply + connect to the dropped() signal and reimplement + Q3IconViewItem::acceptDrop() and Q3IconViewItem::dropped(). If you've + used a Q3IconDrag (or a subclass of it) the second argument to the + dropped signal contains a QLinkedList of Q3IconDragItems -- you can + access their data by calling Q3IconDragItem::data() on each one. + + For an example implementation of complex drag-and-drop look at the + fileiconview example (qt/examples/fileiconview). + + \sa Q3IconViewItem::setDragEnabled(), Q3IconViewItem::setDropEnabled(), + Q3IconViewItem::acceptDrop(), Q3IconViewItem::dropped() +*/ + +/*! \enum Q3IconView::ResizeMode + + This enum type is used to tell Q3IconView how it should treat the + positions of its icons when the widget is resized. The modes are: + + \value Fixed The icons' positions are not changed. + \value Adjust The icons' positions are adjusted to be within the + new geometry, if possible. +*/ + +/*! + \enum Q3IconView::SelectionMode + + This enumerated type is used by Q3IconView to indicate how it + reacts to selection by the user. It has four values: + + \value Single When the user selects an item, any already-selected + item becomes unselected and the user cannot unselect the selected + item. This means that the user can never clear the selection. (The + application programmer can, using Q3IconView::clearSelection().) + + \value Multi When the user selects an item, e.g. by navigating to + it with the keyboard arrow keys or by clicking it, the selection + status of that item is toggled and the other items are left alone. + + \value Extended When the user selects an item the selection is + cleared and the new item selected. However, if the user presses + the Ctrl key when clicking on an item, the clicked item gets + toggled and all other items are left untouched. If the user + presses the Shift key while clicking on an item, all items between + the current item and the clicked item get selected or unselected, + depending on the state of the clicked item. Also, multiple items + can be selected by dragging the mouse while the left mouse button + stays pressed. + + \value NoSelection Items cannot be selected. + + To summarize: \c Single is a real single-selection icon view; \c + Multi a real multi-selection icon view; \c Extended is an icon + view in which users can select multiple items but usually want to + select either just one or a range of contiguous items; and \c + NoSelection mode is for an icon view where the user can look but + not touch. +*/ + +/*! + \enum Q3IconView::Arrangement + + This enum type determines in which direction the items flow when + the view runs out of space. + + \value LeftToRight Items which don't fit into the view go further + down (you get a vertical scroll bar) + + \value TopToBottom Items which don't fit into the view go further + right (you get a horizontal scroll bar) +*/ + +/*! + \enum Q3IconView::ItemTextPos + + This enum type specifies the position of the item text in relation + to the icon. + + \value Bottom The text is drawn below the icon. + \value Right The text is drawn to the right of the icon. +*/ + +/*! + \fn void Q3IconView::dropped(QDropEvent *e, const Q3ValueList<Q3IconDragItem> &lst) + + This signal is emitted when a drop event occurs in the viewport + (but not on any icon) which the icon view itself can't handle. + + \a e provides all the information about the drop. If the drag + object of the drop was a Q3IconDrag, \a lst contains the list of + the dropped items. You can get the data using + Q3IconDragItem::data() on each item. If the \a lst is empty, i.e. + the drag was not a Q3IconDrag, you have to decode the data in \a e + and work with that. + + Note Q3IconViewItems may be drop targets; if a drop event occurs on + an item the item handles the drop. +*/ + +/*! + \fn void Q3IconView::moved() + + This signal is emitted after successfully dropping one (or more) + items of the icon view. If the items should be removed, it's best + to do so in a slot connected to this signal. +*/ + +/*! + \fn void Q3IconView::doubleClicked(Q3IconViewItem * item) + + This signal is emitted when the user double-clicks on \a item. +*/ + +/*! + \fn void Q3IconView::returnPressed (Q3IconViewItem * item) + + This signal is emitted if the user presses the Return or Enter + key. \a item is the currentItem() at the time of the keypress. +*/ + +/*! + \fn void Q3IconView::selectionChanged() + + This signal is emitted when the selection has been changed. It's + emitted in each selection mode. +*/ + +/*! + \fn void Q3IconView::selectionChanged(Q3IconViewItem *item) + \overload + + This signal is emitted when the selection changes. \a item is the + newly selected item. This signal is emitted only in single + selection mode. +*/ + +/*! + \fn void Q3IconView::currentChanged(Q3IconViewItem *item) + + This signal is emitted when a new item becomes current. \a item is + the new current item (or 0 if no item is now current). + + \sa currentItem() +*/ + +/*! + \fn void Q3IconView::onItem(Q3IconViewItem *item) + + This signal is emitted when the user moves the mouse cursor onto + an \a item, similar to the QWidget::enterEvent() function. +*/ + +// ### bug here - enter/leave event aren't considered. move the mouse +// out of the window and back in, to the same item. + +/*! + \fn void Q3IconView::onViewport() + + This signal is emitted when the user moves the mouse cursor from + an item to an empty part of the icon view. + + \sa onItem() +*/ + +/*! + \fn void Q3IconView::itemRenamed (Q3IconViewItem * item) + \overload + + This signal is emitted when \a item has been renamed, usually by + in-place renaming. + + \sa Q3IconViewItem::setRenameEnabled() Q3IconViewItem::rename() +*/ + +/*! + \fn void Q3IconView::itemRenamed (Q3IconViewItem * item, const QString &name) + + This signal is emitted when \a item has been renamed to \a name, + usually by in-place renaming. + + \sa Q3IconViewItem::setRenameEnabled() Q3IconViewItem::rename() +*/ + +/*! + \fn void Q3IconView::rightButtonClicked (Q3IconViewItem * item, const QPoint & pos) + + This signal is emitted when the user clicks the right mouse + button. If \a item is non-null, the cursor is on \a item. If \a + item is null, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). (If the click's + press and release differ by a pixel or two, \a pos is the + position at release time.) + + \sa rightButtonPressed() mouseButtonClicked() clicked() +*/ + +/*! + \fn void Q3IconView::contextMenuRequested(Q3IconViewItem *item, const QPoint & pos) + + This signal is emitted when the user invokes a context menu with + the right mouse button or with special system keys, with \a item + being the item under the mouse cursor or the current item, + respectively. + + \a pos is the position for the context menu in the global + coordinate system. +*/ + +/*! + \fn void Q3IconView::mouseButtonPressed(int button, Q3IconViewItem *item, const QPoint &pos) + + This signal is emitted when the user presses mouse button \a + button. If \a item is non-null, the cursor is on \a item. If \a + item is null, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). + + \sa rightButtonClicked() mouseButtonClicked() pressed() +*/ + +/*! + \fn void Q3IconView::mouseButtonClicked (int button, Q3IconViewItem * item, const QPoint & pos) + + This signal is emitted when the user clicks mouse button \a + button. If \a item is non-null, the cursor is on \a item. If \a + item is null, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). (If the click's + press and release differ by a pixel or two, \a pos is the + position at release time.) + + \sa mouseButtonPressed() rightButtonClicked() clicked() +*/ + +/*! + \fn void Q3IconView::clicked (Q3IconViewItem * item, const QPoint & pos) + \overload + + This signal is emitted when the user clicks any mouse button on an + icon view item. \a item is a pointer to the item that has been + clicked. + + \a pos is the position of the mouse cursor in the global coordinate + system (QMouseEvent::globalPos()). (If the click's press and release + differ by a pixel or two, \a pos is the position at release time.) + + \sa mouseButtonClicked() rightButtonClicked() pressed() +*/ + +/*! + \fn void Q3IconView::pressed (Q3IconViewItem * item, const QPoint & pos) + \overload + + This signal is emitted when the user presses any mouse button. If + \a item is non-null, the cursor is on \a item. If \a item is null, + the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). (If the click's + press and release differ by a pixel or two, \a pos is the + position at release time.) + + \sa mouseButtonPressed() rightButtonPressed() clicked() +*/ + +/*! + \fn void Q3IconView::clicked (Q3IconViewItem * item) + + This signal is emitted when the user clicks any mouse button. If + \a item is non-null, the cursor is on \a item. If \a item is null, + the mouse cursor isn't on any item. + + \sa mouseButtonClicked() rightButtonClicked() pressed() +*/ + +/*! + \fn void Q3IconView::pressed (Q3IconViewItem * item) + + This signal is emitted when the user presses any mouse button. If + \a item is non-null, the cursor is on \a item. If \a item is null, + the mouse cursor isn't on any item. + + \sa mouseButtonPressed() rightButtonPressed() clicked() +*/ + +/*! + \fn void Q3IconView::rightButtonPressed(Q3IconViewItem * item, const QPoint & pos) + + This signal is emitted when the user presses the right mouse + button. If \a item is non-null, the cursor is on \a item. If \a + item is null, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). +*/ + +/*! + Constructs an empty icon view called \a name, with parent \a + parent and using the widget flags \a f. +*/ + +Q3IconView::Q3IconView(QWidget *parent, const char *name, Qt::WindowFlags f) + : Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase | f) +{ + if (!unknown_icon) { + unknown_icon = new QPixmap((const char **)unknown_xpm); + qiv_cleanup_pixmap.add(&unknown_icon); + } + + d = new Q3IconViewPrivate; + d->dragging = false; + d->firstItem = 0; + d->lastItem = 0; + d->count = 0; + d->mousePressed = false; + d->selectionMode = Single; + d->currentItem = 0; + d->highlightedItem = 0; + d->rubber = 0; + d->scrollTimer = 0; + d->startDragItem = 0; + d->tmpCurrentItem = 0; + d->rastX = d->rastY = -1; + d->spacing = 5; + d->cleared = false; + d->arrangement = LeftToRight; + d->resizeMode = Fixed; + d->dropped = false; + d->adjustTimer = new QTimer(this, "iconview adjust timer"); + d->isIconDrag = false; + d->inMenuMode = false; +#ifndef QT_NO_DRAGANDDROP + d->iconDragData.clear(); +#endif + d->numDragItems = 0; + d->updateTimer = new QTimer(this, "iconview update timer"); + d->cachedW = d->cachedH = 0; + d->maxItemWidth = 100; + d->maxItemTextLength = 255; + d->inputTimer = new QTimer(this, "iconview input timer"); + d->currInputString.clear(); + d->dirty = false; + d->rearrangeEnabled = true; + d->itemTextPos = Bottom; + d->reorderItemsWhenInsert = true; +#ifndef QT_NO_CURSOR + d->oldCursor = Qt::ArrowCursor; +#endif + d->resortItemsWhenInsert = false; + d->sortDirection = true; + d->wordWrapIconText = true; + d->cachedContentsX = d->cachedContentsY = -1; + d->clearing = false; + d->fullRedrawTimer = new QTimer(this, "iconview full redraw timer"); + d->itemTextBrush = Qt::NoBrush; + d->drawAllBack = true; + d->fm = new QFontMetrics(font()); + d->minLeftBearing = d->fm->minLeftBearing(); + d->minRightBearing = d->fm->minRightBearing(); + d->firstContainer = d->lastContainer = 0; + d->containerUpdateLocked = false; + d->firstSizeHint = false; + d->selectAnchor = 0; + d->renamingItem = 0; + d->drawActiveSelection = true; + d->drawDragShapes = false; + + connect(d->adjustTimer, SIGNAL(timeout()), + this, SLOT(adjustItems())); + connect(d->updateTimer, SIGNAL(timeout()), + this, SLOT(slotUpdate())); + connect(d->fullRedrawTimer, SIGNAL(timeout()), + this, SLOT(updateContents())); + connect(this, SIGNAL(contentsMoving(int,int)), + this, SLOT(movedContents(int,int))); + + setAcceptDrops(true); + viewport()->setAcceptDrops(true); + + setMouseTracking(true); + viewport()->setMouseTracking(true); + + viewport()->setBackgroundRole(QPalette::Base); + viewport()->setFocusProxy(this); + viewport()->setFocusPolicy(Qt::WheelFocus); + setFocusPolicy(Qt::WheelFocus); + + d->showTips = true; +} + +/*! + \reimp +*/ +void Q3IconView::changeEvent(QEvent *ev) +{ + if(ev->type() == QEvent::StyleChange) { + *d->fm = QFontMetrics(font()); + d->minLeftBearing = d->fm->minLeftBearing(); + d->minRightBearing = d->fm->minRightBearing(); + + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) { + item->wordWrapDirty = true; + item->calcRect(); + } + +#if !defined(Q_WS_X11) + delete qiv_selection; + qiv_selection = 0; +#endif + } else if(ev->type() == QEvent::ActivationChange) { + if (!isActiveWindow() && d->scrollTimer) + d->scrollTimer->stop(); + if(isVisible() && !palette().isEqual(QPalette::Active, QPalette::Inactive)) + repaintSelectedItems(); + } + + Q3ScrollView::changeEvent(ev); + + if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange) { + *d->fm = QFontMetrics(font()); + d->minLeftBearing = d->fm->minLeftBearing(); + d->minRightBearing = d->fm->minRightBearing(); + + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) { + item->wordWrapDirty = true; + item->calcRect(); + } + } +} + +/*! + Destroys the icon view and deletes all items. +*/ + +Q3IconView::~Q3IconView() +{ + Q3IconViewItem *tmp, *item = d->firstItem; + d->clearing = true; + Q3IconViewPrivate::ItemContainer *c = d->firstContainer, *tmpc; + while (c) { + tmpc = c->n; + delete c; + c = tmpc; + } + while (item) { + tmp = item->next; + delete item; + item = tmp; + } + delete d->fm; + d->fm = 0; + delete d; +} + +/*! + Inserts the icon view item \a item after \a after. If \a after is + 0, \a item is appended after the last item. + + \e{You should never need to call this function.} Instead create + Q3IconViewItem's and associate them with your icon view like this: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 8 +*/ + +void Q3IconView::insertItem(Q3IconViewItem *item, Q3IconViewItem *after) +{ + if (!item) + return; + + if (d->firstItem == item || item->prev || item->next) + return; + + if (!item->view) + item->view = this; + + if (!d->firstItem) { + d->firstItem = d->lastItem = item; + item->prev = 0; + item->next = 0; + } else { + if (!after || after == d->lastItem) { + d->lastItem->next = item; + item->prev = d->lastItem; + item->next = 0; + d->lastItem = item; + } else { + Q3IconViewItem *i = d->firstItem; + while (i != after) + i = i->next; + + if (i) { + Q3IconViewItem *next = i->next; + item->next = next; + item->prev = i; + i->next = item; + next->prev = item; + } + } + } + + if (isVisible()) { + if (d->reorderItemsWhenInsert) { + if (d->updateTimer->isActive()) + d->updateTimer->stop(); + d->fullRedrawTimer->stop(); + // #### uncomment this ASA insertInGrid uses cached values and is efficient + //insertInGrid(item); + + d->cachedW = qMax(d->cachedW, item->x() + item->width()); + d->cachedH= qMax(d->cachedH, item->y() + item->height()); + + d->updateTimer->start(0, true); + } else { + insertInGrid(item); + + viewport()->update(item->x() - contentsX(), + item->y() - contentsY(), + item->width(), item->height()); + } + } else if (!autoArrange()) { + item->dirty = false; + } + + d->count++; + d->dirty = true; +} + +/*! + This slot is used for a slightly-delayed update. + + The icon view is not redrawn immediately after inserting a new item + but after a very small delay using a QTimer. This means that when + many items are inserted in a loop the icon view is probably redrawn + only once at the end of the loop. This makes the insertions both + flicker-free and faster. +*/ + +void Q3IconView::slotUpdate() +{ + d->updateTimer->stop(); + d->fullRedrawTimer->stop(); + + if (!d->firstItem || !d->lastItem) + return; + + // #### remove that ASA insertInGrid uses cached values and is efficient + if (d->resortItemsWhenInsert) + sort(d->sortDirection); + else { + int y = d->spacing; + Q3IconViewItem *item = d->firstItem; + int w = 0, h = 0; + while (item) { + bool changed; + Q3IconViewItem *next = makeRowLayout(item, y, changed); + if (!next || !next->next) + break; + + if(!QApplication::reverseLayout()) + item = next; + w = qMax(w, item->x() + item->width()); + h = qMax(h, item->y() + item->height()); + item = next; + if (d->arrangement == LeftToRight) + h = qMax(h, y); + + item = item->next; + } + + if (d->lastItem && d->arrangement == TopToBottom) { + item = d->lastItem; + int x = item->x(); + while (item && item->x() >= x) { + w = qMax(w, item->x() + item->width()); + h = qMax(h, item->y() + item->height()); + item = item->prev; + } + } + + w = qMax(qMax(d->cachedW, w), d->lastItem->x() + d->lastItem->width()); + h = qMax(qMax(d->cachedH, h), d->lastItem->y() + d->lastItem->height()); + + if (d->arrangement == TopToBottom) + w += d->spacing; + else + h += d->spacing; + viewport()->setUpdatesEnabled(false); + resizeContents(w, h); + viewport()->setUpdatesEnabled(true); + viewport()->repaint(); + } + + int cx = d->cachedContentsX == -1 ? contentsX() : d->cachedContentsX; + int cy = d->cachedContentsY == -1 ? contentsY() : d->cachedContentsY; + + if (cx != contentsX() || cy != contentsY()) + setContentsPos(cx, cy); + + d->cachedContentsX = d->cachedContentsY = -1; + d->cachedW = d->cachedH = 0; +} + +/*! + Takes the icon view item \a item out of the icon view and causes + an update of the screen display. The item is not deleted. You + should normally not need to call this function because + Q3IconViewItem::~Q3IconViewItem() calls it. The normal way to delete + an item is to delete it. +*/ + +void Q3IconView::takeItem(Q3IconViewItem *item) +{ + if (!item) + return; + + if (item->d->container1) + item->d->container1->items.removeAll(item); + if (item->d->container2) + item->d->container2->items.removeAll(item); + item->d->container2 = 0; + item->d->container1 = 0; + + bool block = signalsBlocked(); + blockSignals(d->clearing); + + QRect r = item->rect(); + + if (d->currentItem == item) { + if (item->prev) { + d->currentItem = item->prev; + emit currentChanged(d->currentItem); + repaintItem(d->currentItem); + } else if (item->next) { + d->currentItem = item->next; + emit currentChanged(d->currentItem); + repaintItem(d->currentItem); + } else { + d->currentItem = 0; + emit currentChanged(d->currentItem); + } + } + if (item->isSelected()) { + item->selected = false; + emit selectionChanged(); + } + + if (item == d->firstItem) { + d->firstItem = d->firstItem->next; + if (d->firstItem) + d->firstItem->prev = 0; + } else if (item == d->lastItem) { + d->lastItem = d->lastItem->prev; + if (d->lastItem) + d->lastItem->next = 0; + } else { + Q3IconViewItem *i = item; + if (i) { + if (i->prev) + i->prev->next = i->next; + if (i->next) + i->next->prev = i->prev; + } + } + + if (d->selectAnchor == item) + d->selectAnchor = d->currentItem; + + if (!d->clearing) + repaintContents(r.x(), r.y(), r.width(), r.height()); + + item->view = 0; + item->prev = 0; + item->next = 0; + d->count--; + + blockSignals(block); +} + +/*! + Returns the index of \a item, or -1 if \a item doesn't exist in + this icon view. +*/ + +int Q3IconView::index(const Q3IconViewItem *item) const +{ + if (!item) + return -1; + + if (item == d->firstItem) + return 0; + else if (item == d->lastItem) + return d->count - 1; + else { + Q3IconViewItem *i = d->firstItem; + int j = 0; + while (i && i != item) { + i = i->next; + ++j; + } + + return i ? j : -1; + } +} + +/*! + Returns a pointer to the first item of the icon view, or 0 if + there are no items in the icon view. + + \sa lastItem() currentItem() +*/ + +Q3IconViewItem *Q3IconView::firstItem() const +{ + return d->firstItem; +} + +/*! + Returns a pointer to the last item of the icon view, or 0 if there + are no items in the icon view. + + \sa firstItem() currentItem() +*/ + +Q3IconViewItem *Q3IconView::lastItem() const +{ + return d->lastItem; +} + +/*! + Returns a pointer to the current item of the icon view, or 0 if no + item is current. + + \sa setCurrentItem() firstItem() lastItem() +*/ + +Q3IconViewItem *Q3IconView::currentItem() const +{ + return d->currentItem; +} + +/*! + \reimp +*/ +QVariant Q3IconView::inputMethodQuery(Qt::InputMethodQuery query) const +{ + if (query == Qt::ImMicroFocus) { + return d->currentItem ? d->currentItem->rect() : QRect(); + } + return QWidget::inputMethodQuery(query); +} + +/*! + Makes \a item the new current item of the icon view. +*/ + +void Q3IconView::setCurrentItem(Q3IconViewItem *item) +{ + if (!item || item == d->currentItem) + return; + + Q3IconViewItem *old = d->currentItem; + d->currentItem = item; + emit currentChanged(d->currentItem); + if (d->selectionMode == Single) { + bool changed = false; + if (old && old->selected) { + old->selected = false; + changed = true; + } + if (item && !item->selected && item->isSelectable() && d->selectionMode != NoSelection) { + item->selected = true; + changed = true; + emit selectionChanged(item); + } + if (changed) + emit selectionChanged(); + } + + if (old) + repaintItem(old); + repaintItem(d->currentItem); +} + +/*! + Selects or unselects \a item depending on \a s, and may also + unselect other items, depending on Q3IconView::selectionMode() and + \a cb. + + If \a s is false, \a item is unselected. + + If \a s is true and Q3IconView::selectionMode() is \l Single, \a + item is selected, and the item which was selected is unselected. + + If \a s is true and Q3IconView::selectionMode() is \l Extended, \a + item is selected. If \a cb is true, the selection state of the + icon view's other items is left unchanged. If \a cb is false (the + default) all other items are unselected. + + If \a s is true and Q3IconView::selectionMode() is \l Multi \a item + is selected. + + Note that \a cb is used only if Q3IconView::selectionMode() is \l + Extended. \a cb defaults to false. + + All items whose selection status is changed repaint themselves. +*/ + +void Q3IconView::setSelected(Q3IconViewItem *item, bool s, bool cb) +{ + if (!item) + return; + item->setSelected(s, cb); +} + +/*! + \property Q3IconView::count + \brief the number of items in the icon view +*/ + +uint Q3IconView::count() const +{ + return d->count; +} + +/*! + Performs autoscrolling when selecting multiple icons with the + rubber band. +*/ + +void Q3IconView::doAutoScroll() +{ + QRect oldRubber = QRect(*d->rubber); + + QPoint vp = viewport()->mapFromGlobal(QCursor::pos()); + QPoint pos = viewportToContents(vp); + + if (pos == d->rubber->bottomRight()) + return; + + d->rubber->setRight(pos.x()); + d->rubber->setBottom(pos.y()); + + int minx = contentsWidth(), miny = contentsHeight(); + int maxx = 0, maxy = 0; + bool changed = false; + bool block = signalsBlocked(); + + QRect rr; + QRegion region(0, 0, visibleWidth(), visibleHeight()); + + blockSignals(true); + viewport()->setUpdatesEnabled(false); + bool alreadyIntersected = false; + QRect nr = d->rubber->normalized(); + QRect rubberUnion = nr.united(oldRubber.normalized()); + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + for (; c; c = c->n) { + if (c->rect.intersects(rubberUnion)) { + alreadyIntersected = true; + for (int i = 0; i < c->items.size(); ++i) { + Q3IconViewItem *item = c->items.at(i); + if (d->selectedItems.contains(item)) + continue; + if (!item->intersects(nr)) { + if (item->isSelected()) { + item->setSelected(false); + changed = true; + rr = rr.united(item->rect()); + } + } else if (item->intersects(nr)) { + if (!item->isSelected() && item->isSelectable()) { + item->setSelected(true, true); + changed = true; + rr = rr.united(item->rect()); + } else { + region = region.subtracted(QRect(contentsToViewport(item->pos()), + item->size())); + } + + minx = qMin(minx, item->x() - 1); + miny = qMin(miny, item->y() - 1); + maxx = qMax(maxx, item->x() + item->width() + 1); + maxy = qMax(maxy, item->y() + item->height() + 1); + } + } + } else { + if (alreadyIntersected) + break; + } + } + viewport()->setUpdatesEnabled(true); + blockSignals(block); + + QRect r = *d->rubber; + *d->rubber = oldRubber; + d->dragging = false; + *d->rubber = r; + if (changed) { + d->drawAllBack = false; + d->clipRegion = region; + repaintContents(rr); + d->drawAllBack = true; + } + ensureVisible(pos.x(), pos.y()); + d->dragging = true; + + if (changed) { + emit selectionChanged(); + if (d->selectionMode == Single) + emit selectionChanged(d->currentItem); + } + + if (!QRect(50, 50, viewport()->width()-100, viewport()->height()-100).contains(vp) && + !d->scrollTimer) { + d->scrollTimer = new QTimer(this); + + connect(d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll())); + d->scrollTimer->start(100, false); + } else if (QRect(50, 50, viewport()->width()-100, viewport()->height()-100).contains(vp) && + d->scrollTimer) { + disconnect(d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll())); + d->scrollTimer->stop(); + delete d->scrollTimer; + d->scrollTimer = 0; + } + +} + +/*! + \reimp +*/ + +void Q3IconView::drawContents(QPainter *p, int cx, int cy, int cw, int ch) +{ + if (d->dragging && d->rubber) + drawRubber(p); + + QRect r = QRect(cx, cy, cw, ch); + + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + QRegion remaining(QRect(cx, cy, cw, ch)); + bool alreadyIntersected = false; + while (c) { + if (c->rect.intersects(r)) { + p->save(); + p->resetXForm(); + QRect r2 = c->rect; + r2 = r2.intersected(r); + QRect r3(contentsToViewport(QPoint(r2.x(), r2.y())), QSize(r2.width(), r2.height())); + if (d->drawAllBack) { + p->setClipRect(r3); + } else { + QRegion reg = d->clipRegion.intersected(r3); + p->setClipRegion(reg); + } + drawBackground(p, r3); + remaining = remaining.subtracted(r3); + p->restore(); + + QPalette pal = palette(); + d->drawActiveSelection = hasFocus() || d->inMenuMode + || !style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this); + if (!d->drawActiveSelection) + pal.setCurrentColorGroup(QPalette::Inactive); + + // clip items to the container rect by default... this + // prevents icons with alpha channels from being painted + // twice when they are in 2 containers + // + // NOTE: the item could override this cliprect in its + // paintItem() implementation, which makes this useless + p->setClipRect(r2); + for (int i = 0; i < c->items.size(); ++i) { + Q3IconViewItem *item = c->items.at(i); + if (item->rect().intersects(r) && !item->dirty) { + p->save(); + p->setFont(font()); + item->paintItem(p, pal); + p->restore(); + } + } + alreadyIntersected = true; + } else { + if (alreadyIntersected) + break; + } + c = c->n; + } + + if (!remaining.isEmpty()) { + p->save(); + p->resetXForm(); + if (d->drawAllBack) { + p->setClipRegion(remaining); + } else { + remaining = d->clipRegion.intersected(remaining); + p->setClipRegion(remaining); + } + drawBackground(p, remaining.boundingRect()); + p->restore(); + } + + if ((hasFocus() || viewport()->hasFocus()) && d->currentItem && + d->currentItem->rect().intersects(r)) { + d->currentItem->paintFocus(p, palette()); + } + + if (d->dragging && d->rubber) + drawRubber(p); +} + +/*! + \overload + + Arranges all the items in the grid specified by gridX() and gridY(). + + Even if sorting() is enabled, the items are not sorted by this + function. If you want to sort or rearrange the items, use + iconview->sort(iconview->sortDirection()). + + If \a update is true (the default), the viewport is repainted as + well. + + \sa Q3IconView::setGridX(), Q3IconView::setGridY(), Q3IconView::sort() +*/ + +void Q3IconView::arrangeItemsInGrid(bool update) +{ + if (!d->firstItem || !d->lastItem) + return; + + d->containerUpdateLocked = true; + + int w = 0, h = 0, y = d->spacing; + + Q3IconViewItem *item = d->firstItem; + bool changedLayout = false; + while (item) { + bool changed; + Q3IconViewItem *next = makeRowLayout(item, y, changed); + changedLayout = changed || changedLayout; + if(!QApplication::reverseLayout()) + item = next; + w = qMax(w, item->x() + item->width()); + h = qMax(h, item->y() + item->height()); + item = next; + if (d->arrangement == LeftToRight) + h = qMax(h, y); + + if (!item || !item->next) + break; + + item = item->next; + } + + if (d->lastItem && d->arrangement == TopToBottom) { + item = d->lastItem; + int x = item->x(); + while (item && item->x() >= x) { + w = qMax(w, item->x() + item->width()); + h = qMax(h, item->y() + item->height()); + item = item->prev; + } + } + d->containerUpdateLocked = false; + + w = qMax(qMax(d->cachedW, w), d->lastItem->x() + d->lastItem->width()); + h = qMax(qMax(d->cachedH, h), d->lastItem->y() + d->lastItem->height()); + + if (d->arrangement == TopToBottom) + w += d->spacing; + else + h += d->spacing; + + bool ue = updatesEnabled(); + if (ue) + viewport()->setUpdatesEnabled(false); + int vw = visibleWidth(); + int vh = visibleHeight(); + resizeContents(w, h); + bool doAgain = false; + if (d->arrangement == LeftToRight) + doAgain = visibleWidth() != vw; + if (d->arrangement == TopToBottom) + doAgain = visibleHeight() != vh; + if (doAgain) // in the case that the visibleExtend changed because of the resizeContents (scroll bar show/hide), redo layout again + arrangeItemsInGrid(false); + if (ue) + viewport()->setUpdatesEnabled(true); + d->dirty = !isVisible(); + rebuildContainers(); + if (update && (!optimize_layout || changedLayout)) + repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height()); +} + +/*! + This variant uses \a grid instead of (gridX(), gridY()). If \a + grid is invalid (see QSize::isValid()), arrangeItemsInGrid() + calculates a valid grid itself and uses that. + + If \a update is true (the default) the viewport is repainted. +*/ + +void Q3IconView::arrangeItemsInGrid(const QSize &grid, bool update) +{ + d->containerUpdateLocked = true; + QSize grid_(grid); + if (!grid_.isValid()) { + int w = 0, h = 0; + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) { + w = qMax(w, item->width()); + h = qMax(h, item->height()); + } + + grid_ = QSize(qMax(d->rastX + d->spacing, w), + qMax(d->rastY + d->spacing, h)); + } + + int w = 0, h = 0; + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) { + int nx = item->x() / grid_.width(); + int ny = item->y() / grid_.height(); + item->move(nx * grid_.width(), + ny * grid_.height()); + w = qMax(w, item->x() + item->width()); + h = qMax(h, item->y() + item->height()); + item->dirty = false; + } + d->containerUpdateLocked = false; + + resizeContents(w, h); + rebuildContainers(); + if (update) + repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height()); +} + +/*! + \reimp +*/ + +void Q3IconView::setContentsPos(int x, int y) +{ + if (d->updateTimer->isActive()) { + d->cachedContentsX = x; + d->cachedContentsY = y; + } else { + d->cachedContentsY = d->cachedContentsX = -1; + Q3ScrollView::setContentsPos(x, y); + } +} + +/*! + \reimp +*/ + +void Q3IconView::showEvent(QShowEvent *) +{ + if (d->dirty) { + resizeContents(qMax(contentsWidth(), viewport()->width()), + qMax(contentsHeight(), viewport()->height())); + if (d->resortItemsWhenInsert) + sort(d->sortDirection); + if (autoArrange()) + arrangeItemsInGrid(false); + } + Q3ScrollView::show(); +} + +/*! + \property Q3IconView::selectionMode + \brief the selection mode of the icon view + + This can be \l Single (the default), \l Extended, \l Multi or \l + NoSelection. +*/ + +void Q3IconView::setSelectionMode(SelectionMode m) +{ + d->selectionMode = m; +} + +Q3IconView::SelectionMode Q3IconView::selectionMode() const +{ + return d->selectionMode; +} + +/*! + Returns a pointer to the item that contains point \a pos, which is + given in contents coordinates, or 0 if no item contains point \a + pos. +*/ + +Q3IconViewItem *Q3IconView::findItem(const QPoint &pos) const +{ + if (!d->firstItem) + return 0; + + Q3IconViewPrivate::ItemContainer *c = d->lastContainer; + for (; c; c = c->p) { + if (c->rect.contains(pos)) + for (int i = c->items.size()-1; i >= 0; --i) + if (c->items.at(i)->contains(pos)) + return c->items.at(i); + } + + return 0; +} + +/*! + \overload + + Returns a pointer to the first item whose text begins with \a + text, or 0 if no such item could be found. Use the \a compare flag + to control the comparison behavior. +*/ + +Q3IconViewItem *Q3IconView::findItem(const QString &text, ComparisonFlags compare) const +{ + if (!d->firstItem) + return 0; + + if (compare == CaseSensitive || compare == 0) + compare |= ExactMatch; + + QString itmtxt; + QString comtxt = text; + if (! (compare & CaseSensitive)) + comtxt = text.toLower(); + + Q3IconViewItem *item; + if (d->currentItem) + item = d->currentItem; + else + item = d->firstItem; + + Q3IconViewItem *beginsWithItem = 0; + Q3IconViewItem *endsWithItem = 0; + Q3IconViewItem *containsItem = 0; + + if (item) { + for (; item; item = item->next) { + if (!(compare & CaseSensitive)) + itmtxt = item->text().toLower(); + else + itmtxt = item->text(); + + if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt) + return item; + if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt)) + beginsWithItem = containsItem = item; + if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt)) + endsWithItem = containsItem = item; + if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt)) + containsItem = item; + } + + if (d->currentItem && d->firstItem) { + item = d->firstItem; + for (; item && item != d->currentItem; item = item->next) { + if (!(compare & CaseSensitive)) + itmtxt = item->text().toLower(); + else + itmtxt = item->text(); + + if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt) + return item; + if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt)) + beginsWithItem = containsItem = item; + if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt)) + endsWithItem = containsItem = item; + if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt)) + containsItem = item; + } + } + } + + // Obey the priorities + if (beginsWithItem) + return beginsWithItem; + else if (endsWithItem) + return endsWithItem; + else if (containsItem) + return containsItem; + return 0; +} + +/*! + Unselects all the items. +*/ + +void Q3IconView::clearSelection() +{ + selectAll(false); +} + +/*! + In Multi and Extended modes, this function sets all items to be + selected if \a select is true, and to be unselected if \a select + is false. + + In Single and NoSelection modes, this function only changes the + selection status of currentItem(). +*/ + +void Q3IconView::selectAll(bool select) +{ + if (d->selectionMode == NoSelection) + return; + + if (d->selectionMode == Single) { + if (d->currentItem) + d->currentItem->setSelected(select); + return; + } + + bool b = signalsBlocked(); + blockSignals(true); + Q3IconViewItem *item = d->firstItem; + Q3IconViewItem *i = d->currentItem; + bool changed = false; + bool ue = viewport()->updatesEnabled(); + if (ue) + viewport()->setUpdatesEnabled(false); + QRect rr; + for (; item; item = item->next) { + if (select != item->isSelected()) { + item->setSelected(select, true); + rr = rr.united(item->rect()); + changed = true; + } + } + if (ue) + viewport()->setUpdatesEnabled(true); + // we call updateContents not repaintContents because of possible previous updateContents + Q3ScrollView::updateContents(rr); + QApplication::sendPostedEvents(viewport(), QEvent::Paint); + if (i) + setCurrentItem(i); + blockSignals(b); + if (changed) { + emit selectionChanged(); + } +} + +/*! + Inverts the selection. Works only in Multi and Extended selection + mode. +*/ + +void Q3IconView::invertSelection() +{ + if (d->selectionMode == Single || + d->selectionMode == NoSelection) + return; + + bool b = signalsBlocked(); + blockSignals(true); + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) + item->setSelected(!item->isSelected(), true); + blockSignals(b); + emit selectionChanged(); +} + +/*! + Repaints the \a item. +*/ + +void Q3IconView::repaintItem(Q3IconViewItem *item) +{ + if (!item || item->dirty) + return; + + if (QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()). + intersects(QRect(item->x() - 1, item->y() - 1, item->width() + 2, item->height() + 2))) + repaintContents(item->x() - 1, item->y() - 1, item->width() + 2, item->height() + 2); +} + +/*! + Repaints the selected items. +*/ +void Q3IconView::repaintSelectedItems() +{ + if (selectionMode() == NoSelection) + return; + + if (selectionMode() == Single) { + if (!currentItem() || !currentItem()->isSelected()) + return; + QRect itemRect = currentItem()->rect(); //rect in contents coordinates + itemRect.moveBy(-contentsX(), -contentsY()); + viewport()->update(itemRect); + } else { + // check if any selected items are visible + Q3IconViewItem *item = firstItem(); + const QRect vr = QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); + + while (item) { + if (item->isSelected() && item->rect().intersects(vr)) + repaintItem(item); + item = item->nextItem(); + } + } +} + +/*! + Makes sure that \a item is entirely visible. If necessary, + ensureItemVisible() scrolls the icon view. + + \sa ensureVisible() +*/ + +void Q3IconView::ensureItemVisible(Q3IconViewItem *item) +{ + if (!item) + return; + + if ((d->updateTimer && d->updateTimer->isActive()) + || (d->fullRedrawTimer && d->fullRedrawTimer->isActive())) + slotUpdate(); + + int w = item->width(); + int h = item->height(); + ensureVisible(item->x() + w / 2, item->y() + h / 2, + w / 2 + 1, h / 2 + 1); +} + +/*! + Finds the first item whose bounding rectangle overlaps \a r and + returns a pointer to that item. \a r is given in content + coordinates. Returns 0 if no item overlaps \a r. + + If you want to find all items that touch \a r, you will need to + use this function and nextItem() in a loop ending at + findLastVisibleItem() and test Q3IconViewItem::rect() for each of + these items. + + \sa findLastVisibleItem() Q3IconViewItem::rect() +*/ + +Q3IconViewItem* Q3IconView::findFirstVisibleItem(const QRect &r) const +{ + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + Q3IconViewItem *i = 0; + bool alreadyIntersected = false; + for (; c; c = c->n) { + if (c->rect.intersects(r)) { + alreadyIntersected = true; + for (int j = 0; j < c->items.size(); ++j) { + Q3IconViewItem *item = c->items.at(j); + if (r.intersects(item->rect())) { + if (!i) { + i = item; + } else { + QRect r2 = item->rect(); + QRect r3 = i->rect(); + if (r2.y() < r3.y()) + i = item; + else if (r2.y() == r3.y() && + r2.x() < r3.x()) + i = item; + } + } + } + } else { + if (alreadyIntersected) + break; + } + } + + return i; +} + +/*! + Finds the last item whose bounding rectangle overlaps \a r and + returns a pointer to that item. \a r is given in content + coordinates. Returns 0 if no item overlaps \a r. + + \sa findFirstVisibleItem() +*/ + +Q3IconViewItem* Q3IconView::findLastVisibleItem(const QRect &r) const +{ + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + Q3IconViewItem *i = 0; + bool alreadyIntersected = false; + for (; c; c = c->n) { + if (c->rect.intersects(r)) { + alreadyIntersected = true; + for (int j = 0; j < c->items.size(); ++j) { + Q3IconViewItem *item = c->items.at(j); + if (r.intersects(item->rect())) { + if (!i) { + i = item; + } else { + QRect r2 = item->rect(); + QRect r3 = i->rect(); + if (r2.y() > r3.y()) + i = item; + else if (r2.y() == r3.y() && + r2.x() > r3.x()) + i = item; + } + } + } + } else { + if (alreadyIntersected) + break; + } + } + + return i; +} + +/*! + Clears the icon view. All items are deleted. +*/ + +void Q3IconView::clear() +{ + setContentsPos(0, 0); + d->clearing = true; + bool block = signalsBlocked(); + blockSignals(true); + clearSelection(); + blockSignals(block); + setContentsPos(0, 0); + d->currentItem = 0; + + if (!d->firstItem) { + d->clearing = false; + return; + } + + Q3IconViewItem *item = d->firstItem, *tmp; + d->firstItem = 0; + while (item) { + tmp = item->next; + delete item; + item = tmp; + } + Q3IconViewPrivate::ItemContainer *c = d->firstContainer, *tmpc; + while (c) { + tmpc = c->n; + delete c; + c = tmpc; + } + d->firstContainer = d->lastContainer = 0; + + d->count = 0; + d->lastItem = 0; + setCurrentItem(0); + d->highlightedItem = 0; + d->tmpCurrentItem = 0; + d->drawDragShapes = false; + + resizeContents(0, 0); + // maybe we don't need this update, so delay it + d->fullRedrawTimer->start(0, true); + + d->cleared = true; + d->clearing = false; +} + +/*! + \property Q3IconView::gridX + \brief the horizontal grid of the icon view + + If the value is -1, (the default), Q3IconView computes suitable + column widths based on the icon view's contents. + + Note that setting a grid width overrides setMaxItemWidth(). +*/ + +void Q3IconView::setGridX(int rx) +{ + d->rastX = rx >= 0 ? rx : -1; +} + +/*! + \property Q3IconView::gridY + \brief the vertical grid of the icon view + + If the value is -1, (the default), Q3IconView computes suitable + column heights based on the icon view's contents. +*/ + +void Q3IconView::setGridY(int ry) +{ + d->rastY = ry >= 0 ? ry : -1; +} + +int Q3IconView::gridX() const +{ + return d->rastX; +} + +int Q3IconView::gridY() const +{ + return d->rastY; +} + +/*! + \property Q3IconView::spacing + \brief the space in pixels between icon view items + + The default is 5 pixels. + + Negative values for spacing are illegal. +*/ + +void Q3IconView::setSpacing(int sp) +{ + d->spacing = sp; +} + +int Q3IconView::spacing() const +{ + return d->spacing; +} + +/*! + \property Q3IconView::itemTextPos + \brief the position where the text of each item is drawn. + + Valid values are \l Bottom or \l Right. The default is \l Bottom. +*/ + +void Q3IconView::setItemTextPos(ItemTextPos pos) +{ + if (pos == d->itemTextPos || (pos != Bottom && pos != Right)) + return; + + d->itemTextPos = pos; + + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) { + item->wordWrapDirty = true; + item->calcRect(); + } + + arrangeItemsInGrid(true); +} + +Q3IconView::ItemTextPos Q3IconView::itemTextPos() const +{ + return d->itemTextPos; +} + +/*! + \property Q3IconView::itemTextBackground + \brief the brush to use when drawing the background of an item's text. + + By default this brush is set to Qt::NoBrush, meaning that only the + normal icon view background is used. +*/ + +void Q3IconView::setItemTextBackground(const QBrush &brush) +{ + d->itemTextBrush = brush; +} + +QBrush Q3IconView::itemTextBackground() const +{ + return d->itemTextBrush; +} + +/*! + \property Q3IconView::arrangement + \brief the arrangement mode of the icon view + + This can be \l LeftToRight or \l TopToBottom. The default is \l + LeftToRight. +*/ + +void Q3IconView::setArrangement(Arrangement am) +{ + if (d->arrangement == am) + return; + + d->arrangement = am; + + viewport()->setUpdatesEnabled(false); + resizeContents(viewport()->width(), viewport()->height()); + viewport()->setUpdatesEnabled(true); + arrangeItemsInGrid(true); +} + +Q3IconView::Arrangement Q3IconView::arrangement() const +{ + return d->arrangement; +} + +/*! + \property Q3IconView::resizeMode + \brief the resize mode of the icon view + + This can be \l Fixed or \l Adjust. The default is \l Fixed. + See \l ResizeMode. +*/ + +void Q3IconView::setResizeMode(ResizeMode rm) +{ + if (d->resizeMode == rm) + return; + + d->resizeMode = rm; +} + +Q3IconView::ResizeMode Q3IconView::resizeMode() const +{ + return d->resizeMode; +} + +/*! + \property Q3IconView::maxItemWidth + \brief the maximum width that an item may have. + + The default is 100 pixels. + + Note that if the gridX() value is set Q3IconView will ignore + this property. +*/ + +void Q3IconView::setMaxItemWidth(int w) +{ + d->maxItemWidth = w; +} + +/*! + \property Q3IconView::maxItemTextLength + \brief the maximum length (in characters) that an item's text may have. + + The default is 255 characters. +*/ + +void Q3IconView::setMaxItemTextLength(int w) +{ + d->maxItemTextLength = w; +} + +int Q3IconView::maxItemWidth() const +{ + if (d->rastX != -1) + return d->rastX - 2; + else + return d->maxItemWidth; +} + +int Q3IconView::maxItemTextLength() const +{ + return d->maxItemTextLength; +} + +/*! + \property Q3IconView::itemsMovable + \brief whether the user is allowed to move items around in the icon view + + The default is true. +*/ + +void Q3IconView::setItemsMovable(bool b) +{ + d->rearrangeEnabled = b; +} + +bool Q3IconView::itemsMovable() const +{ + return d->rearrangeEnabled; +} + +/*! + \property Q3IconView::autoArrange + \brief whether the icon view rearranges its items when a new item is inserted. + + The default is true. + + Note that if the icon view is not visible at the time of + insertion, Q3IconView defers all position-related work until it is + shown and then calls arrangeItemsInGrid(). +*/ + +void Q3IconView::setAutoArrange(bool b) +{ + d->reorderItemsWhenInsert = b; +} + +bool Q3IconView::autoArrange() const +{ + return d->reorderItemsWhenInsert; +} + +/*! + If \a sort is true, this function sets the icon view to sort items + when a new item is inserted. If \a sort is false, the icon view + will not be sorted. + + Note that autoArrange() must be true for sorting to take place. + + If \a ascending is true (the default), items are sorted in + ascending order. If \a ascending is false, items are sorted in + descending order. + + Q3IconViewItem::compare() is used to compare pairs of items. The + sorting is based on the items' keys; these default to the items' + text unless specifically set to something else. + + \sa Q3IconView::setAutoArrange(), Q3IconView::autoArrange(), + sortDirection(), sort(), Q3IconViewItem::setKey() +*/ + +void Q3IconView::setSorting(bool sort, bool ascending) +{ + d->resortItemsWhenInsert = sort; + d->sortDirection = ascending; +} + +/*! + \property Q3IconView::sorting + \brief whether the icon view sorts on insertion + + The default is false, i.e. no sorting on insertion. + + To set the sorting, use setSorting(). +*/ + +bool Q3IconView::sorting() const +{ + return d->resortItemsWhenInsert; +} + +/*! + \property Q3IconView::sortDirection + \brief whether the sort direction for inserting new items is ascending; + + The default is true (i.e. ascending). This sort direction is only + meaningful if both sorting() and autoArrange() are true. + + To set the sort direction, use setSorting() +*/ + +bool Q3IconView::sortDirection() const +{ + return d->sortDirection; +} + +/*! + \property Q3IconView::wordWrapIconText + \brief whether the item text will be word-wrapped if it is too long + + The default is true. + + If this property is false, icon text that is too long is + truncated, and an ellipsis (...) appended to indicate that + truncation has occurred. The full text can still be seen by the + user if they hover the mouse because the full text is shown in a + tooltip; see setShowToolTips(). +*/ + +void Q3IconView::setWordWrapIconText(bool b) +{ + if (d->wordWrapIconText == (uint)b) + return; + + d->wordWrapIconText = b; + for (Q3IconViewItem *item = d->firstItem; item; item = item->next) { + item->wordWrapDirty = true; + item->calcRect(); + } + arrangeItemsInGrid(true); +} + +bool Q3IconView::wordWrapIconText() const +{ + return d->wordWrapIconText; +} + +/*! + \property Q3IconView::showToolTips + \brief whether the icon view will display a tool tip with the complete text for any truncated item text + + The default is true. Note that this has no effect if + setWordWrapIconText() is true, as it is by default. +*/ + +void Q3IconView::setShowToolTips(bool b) +{ + d->showTips = b; +} + +bool Q3IconView::showToolTips() const +{ + return d->showTips; +} + +/*! + \reimp +*/ +void Q3IconView::contentsMousePressEvent(QMouseEvent *e) +{ + contentsMousePressEventEx(e); +} + +void Q3IconView::contentsMousePressEventEx(QMouseEvent *e) +{ + if (d->rubber) { + d->dragging = false; + delete d->rubber; + d->rubber = 0; + viewport()->update(); + + if (d->scrollTimer) { + disconnect(d->scrollTimer, SIGNAL(timeout()), this, SLOT(doAutoScroll())); + d->scrollTimer->stop(); + delete d->scrollTimer; + d->scrollTimer = 0; + } + } + + d->dragStartPos = e->pos(); + Q3IconViewItem *item = findItem(e->pos()); + d->pressedItem = item; + + if (item) + d->selectAnchor = item; + +#ifndef QT_NO_TEXTEDIT + if (d->renamingItem) + d->renamingItem->renameItem(); +#endif + + if (!d->currentItem && !item && d->firstItem) { + d->currentItem = d->firstItem; + repaintItem(d->firstItem); + } + + if (item && item->dragEnabled()) + d->startDragItem = item; + else + d->startDragItem = 0; + + if (e->button() == Qt::LeftButton && !(e->state() & Qt::ShiftButton) && + !(e->state() & Qt::ControlButton) && item && item->isSelected() && + item->textRect(false).contains(e->pos())) { + + if (!item->renameEnabled()) { + d->mousePressed = true; +#ifndef QT_NO_TEXTEDIT + } else { + ensureItemVisible(item); + setCurrentItem(item); + item->rename(); + goto emit_signals; +#endif + } + } + + d->pressedSelected = item && item->isSelected(); + + if (item && item->isSelectable()) { + if (d->selectionMode == Single) + item->setSelected(true, e->state() & Qt::ControlButton); + else if (d->selectionMode == Multi && !item->isSelected()) + item->setSelected(true, e->state() & Qt::ControlButton); + else if (d->selectionMode == Extended) { + if (e->state() & Qt::ShiftButton) { + d->pressedSelected = false; + bool block = signalsBlocked(); + blockSignals(true); + viewport()->setUpdatesEnabled(false); + QRect r; + bool select = true; + if (d->currentItem) + r = QRect(qMin(d->currentItem->x(), item->x()), + qMin(d->currentItem->y(), item->y()), + 0, 0); + else + r = QRect(0, 0, 0, 0); + if (d->currentItem) { + if (d->currentItem->x() < item->x()) + r.setWidth(item->x() - d->currentItem->x() + item->width()); + else + r.setWidth(d->currentItem->x() - item->x() + d->currentItem->width()); + if (d->currentItem->y() < item->y()) + r.setHeight(item->y() - d->currentItem->y() + item->height()); + else + r.setHeight(d->currentItem->y() - item->y() + d->currentItem->height()); + r = r.normalized(); + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + bool alreadyIntersected = false; + QRect redraw; + for (; c; c = c->n) { + if (c->rect.intersects(r)) { + alreadyIntersected = true; + for (int i = 0; i < c->items.size(); ++i) { + Q3IconViewItem *item = c->items.at(i); + if (r.intersects(item->rect())) { + redraw = redraw.united(item->rect()); + item->setSelected(select, true); + } + } + } else { + if (alreadyIntersected) + break; + } + } + redraw = redraw.united(item->rect()); + viewport()->setUpdatesEnabled(true); + repaintContents(redraw); + } + blockSignals(block); + viewport()->setUpdatesEnabled(true); + item->setSelected(select, true); + emit selectionChanged(); + } else if (e->state() & Qt::ControlButton) { + d->pressedSelected = false; + item->setSelected(!item->isSelected(), e->state() & Qt::ControlButton); + } else { + item->setSelected(true, e->state() & Qt::ControlButton); + } + } + } else if ((d->selectionMode != Single || e->button() == Qt::RightButton) + && !(e->state() & Qt::ControlButton)) + selectAll(false); + + setCurrentItem(item); + + if (e->button() == Qt::LeftButton) { + if (!item && (d->selectionMode == Multi || + d->selectionMode == Extended)) { + d->tmpCurrentItem = d->currentItem; + d->currentItem = 0; + repaintItem(d->tmpCurrentItem); + if (d->rubber) + delete d->rubber; + d->rubber = 0; + d->rubber = new QRect(e->x(), e->y(), 0, 0); + d->selectedItems.clear(); + if ((e->state() & Qt::ControlButton) == Qt::ControlButton) { + for (Q3IconViewItem *i = firstItem(); i; i = i->nextItem()) + if (i->isSelected()) + d->selectedItems.insert(i, i); + } + } + + d->mousePressed = true; + } + + emit_signals: + if (!d->rubber) { + emit mouseButtonPressed(e->button(), item, e->globalPos()); + emit pressed(item); + emit pressed(item, e->globalPos()); + + if (e->button() == Qt::RightButton) + emit rightButtonPressed(item, e->globalPos()); + } +} + +/*! + \reimp +*/ + +void Q3IconView::contentsContextMenuEvent(QContextMenuEvent *e) +{ + if (!receivers(SIGNAL(contextMenuRequested(Q3IconViewItem*,QPoint)))) { + e->ignore(); + return; + } + if (e->reason() == QContextMenuEvent::Keyboard) { + Q3IconViewItem *item = currentItem(); + QRect r = item ? item->rect() : QRect(0, 0, visibleWidth(), visibleHeight()); + emit contextMenuRequested(item, viewport()->mapToGlobal(contentsToViewport(r.center()))); + } else { + d->mousePressed = false; + Q3IconViewItem *item = findItem(e->pos()); + emit contextMenuRequested(item, e->globalPos()); + } +} + +/*! + \reimp +*/ + +void Q3IconView::contentsMouseReleaseEvent(QMouseEvent *e) +{ + Q3IconViewItem *item = findItem(e->pos()); + d->selectedItems.clear(); + + bool emitClicked = true; + d->mousePressed = false; + d->startDragItem = 0; + + if (d->rubber) { + d->dragging = false; + viewport()->update(); + + if ((d->rubber->topLeft() - d->rubber->bottomRight()).manhattanLength() > + QApplication::startDragDistance()) + emitClicked = false; + delete d->rubber; + d->rubber = 0; + d->currentItem = d->tmpCurrentItem; + d->tmpCurrentItem = 0; + if (d->currentItem) + repaintItem(d->currentItem); + } + + if (d->scrollTimer) { + disconnect(d->scrollTimer, SIGNAL(timeout()), this, SLOT(doAutoScroll())); + d->scrollTimer->stop(); + delete d->scrollTimer; + d->scrollTimer = 0; + } + + if ((d->selectionMode == Extended || d->selectionMode == Multi) && + d->currentItem == d->pressedItem && + d->pressedSelected && d->currentItem) { + if (d->selectionMode == Extended) { + bool block = signalsBlocked(); + blockSignals(true); + clearSelection(); + blockSignals(block); + } + if (d->currentItem->isSelectable()) { + d->currentItem->selected = (d->selectionMode == Extended); + repaintItem(d->currentItem); + } + emit selectionChanged(); + } + d->pressedItem = 0; + + if (emitClicked) { + emit mouseButtonClicked(e->button(), item, e->globalPos()); + emit clicked(item); + emit clicked(item, e->globalPos()); + if (e->button() == Qt::RightButton) + emit rightButtonClicked(item, e->globalPos()); + } +} + +/*! + \reimp +*/ + +void Q3IconView::contentsMouseMoveEvent(QMouseEvent *e) +{ + Q3IconViewItem *item = findItem(e->pos()); + if (d->highlightedItem != item) { + if (item) + emit onItem(item); + else + emit onViewport(); + d->highlightedItem = item; + } + + if (d->mousePressed && e->state() == Qt::NoButton) + d->mousePressed = false; + + if (d->startDragItem) + item = d->startDragItem; + + if (d->mousePressed && d->startDragItem && item && item == d->currentItem && + (item->isSelected() || d->selectionMode == NoSelection) && item->dragEnabled()) { + if ((d->dragStartPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) { + d->mousePressed = false; + d->cleared = false; +#ifndef QT_NO_DRAGANDDROP + startDrag(); +#endif + if (d->tmpCurrentItem) + repaintItem(d->tmpCurrentItem); + } + } else if (d->mousePressed && !d->currentItem && d->rubber) { + doAutoScroll(); + } +} + +/*! + \reimp +*/ + +void Q3IconView::contentsMouseDoubleClickEvent(QMouseEvent *e) +{ + Q3IconViewItem *item = findItem(e->pos()); + if (item) { + selectAll(false); + item->setSelected(true, true); + emit doubleClicked(item); + } +} + +/*! + \reimp +*/ + +#ifndef QT_NO_DRAGANDDROP +void Q3IconView::contentsDragEnterEvent(QDragEnterEvent *e) +{ + d->dragging = true; + d->drawDragShapes = true; + d->tmpCurrentItem = 0; + initDragEnter(e); + d->oldDragPos = e->pos(); + d->oldDragAcceptAction = false; + drawDragShapes(e->pos()); + d->dropped = false; + e->accept(); +} + +/*! + \reimp +*/ + +void Q3IconView::contentsDragMoveEvent(QDragMoveEvent *e) +{ + if (e->pos() == d->oldDragPos) { + if (d->oldDragAcceptAction) + e->acceptAction(); + else + e->ignore(); + return; + } + + drawDragShapes(d->oldDragPos); + d->dragging = false; + + Q3IconViewItem *old = d->tmpCurrentItem; + d->tmpCurrentItem = 0; + + Q3IconViewItem *item = findItem(e->pos()); + + if (item) { + if (old && + old->rect().contains(d->oldDragPos) && + !old->rect().contains(e->pos())) { + old->dragLeft(); + repaintItem(old); + } + if (!item->rect().contains(d->oldDragPos)) + item->dragEntered(); + if (item->acceptDrop(e) || (item->isSelected() && e->source() == viewport())) { + d->oldDragAcceptAction = true; + e->acceptAction(); + } else { + d->oldDragAcceptAction = false; + e->ignore(); + } + + d->tmpCurrentItem = item; + viewport()->update(); + } else { + e->acceptAction(); + d->oldDragAcceptAction = true; + if (old) { + old->dragLeft(); + repaintItem(old); + } + } + + d->oldDragPos = e->pos(); + drawDragShapes(e->pos()); + d->dragging = true; +} + +/*! + \reimp +*/ + +void Q3IconView::contentsDragLeaveEvent(QDragLeaveEvent *) +{ + if (!d->dropped) + drawDragShapes(d->oldDragPos); + d->dragging = false; + + if (d->tmpCurrentItem) { + repaintItem(d->tmpCurrentItem); + d->tmpCurrentItem->dragLeft(); + } + + d->tmpCurrentItem = 0; + d->isIconDrag = false; + d->iconDragData.clear(); +} + +/*! + \reimp +*/ + +void Q3IconView::contentsDropEvent(QDropEvent *e) +{ + d->dropped = true; + d->dragging = false; + drawDragShapes(d->oldDragPos); + + if (d->tmpCurrentItem) + repaintItem(d->tmpCurrentItem); + + Q3IconViewItem *i = findItem(e->pos()); + + if ((!i || i->isSelected()) && e->source() == viewport() && d->currentItem && !d->cleared) { + if (!d->rearrangeEnabled) + return; + QRect r = d->currentItem->rect(); + + d->currentItem->move(e->pos() - d->dragStart); + + int w = d->currentItem->x() + d->currentItem->width() + 1; + int h = d->currentItem->y() + d->currentItem->height() + 1; + + repaintItem(d->currentItem); + repaintContents(r.x(), r.y(), r.width(), r.height()); + + int dx = d->currentItem->x() - r.x(); + int dy = d->currentItem->y() - r.y(); + + Q3IconViewItem *item = d->firstItem; + QRect rr; + for (; item; item = item->next) { + if (item->isSelected() && item != d->currentItem) { + rr = rr.united(item->rect()); + item->moveBy(dx, dy); + rr = rr.united(item->rect()); + } + w = qMax(w, item->x() + item->width() + 1); + h = qMax(h, item->y() + item->height() + 1); + } + repaintContents(rr); + bool fullRepaint = false; + if (w > contentsWidth() || + h > contentsHeight()) + fullRepaint = true; + + int oldw = contentsWidth(); + int oldh = contentsHeight(); + + resizeContents(w, h); + + + if (fullRepaint) { + repaintContents(oldw, 0, contentsWidth() - oldw, contentsHeight()); + repaintContents(0, oldh, contentsWidth(), contentsHeight() - oldh); + } + e->acceptAction(); + } else if (!i && (e->source() != viewport() || d->cleared)) { + QLinkedList<Q3IconDragItem> lst; + if (Q3IconDrag::canDecode(e)) { + QLinkedList<Q3IconDragDataItem> l; + Q3IconDragPrivate::decode(e, l); + QLinkedList<Q3IconDragDataItem>::Iterator it = l.begin(); + for (; it != l.end(); ++it) + lst << (*it).data; + } + emit dropped(e, lst); + } else if (i) { + QLinkedList<Q3IconDragItem> lst; + if (Q3IconDrag::canDecode(e)) { + QLinkedList<Q3IconDragDataItem> l; + Q3IconDragPrivate::decode(e, l); + QLinkedList<Q3IconDragDataItem>::Iterator it = l.begin(); + for (; it != l.end(); ++it) + lst << (*it).data; + } + i->dropped(e, lst); + } + d->isIconDrag = false; +} +#endif + +/*! + \reimp +*/ + +void Q3IconView::resizeEvent(QResizeEvent* e) +{ + Q3ScrollView::resizeEvent(e); + if (d->resizeMode == Adjust) { + optimize_layout = true; + adjustItems(); + optimize_layout = false; +#if 0 // no need for timer delay anymore + d->oldSize = e->oldSize(); + if (d->adjustTimer->isActive()) + d->adjustTimer->stop(); + d->adjustTimer->start(0, true); +#endif + } +} + +/*! + Adjusts the positions of the items to the geometry of the icon + view. +*/ + +void Q3IconView::adjustItems() +{ + d->adjustTimer->stop(); + if (d->resizeMode == Adjust) + arrangeItemsInGrid(true); +} + +/*! + \reimp +*/ + +void Q3IconView::keyPressEvent(QKeyEvent *e) +{ + if (!d->firstItem) + return; + + if (!d->currentItem) { + setCurrentItem(d->firstItem); + if (d->selectionMode == Single) + d->currentItem->setSelected(true, true); + return; + } + + bool selectCurrent = true; + + switch (e->key()) { + case Qt::Key_Escape: + e->ignore(); + break; +#ifndef QT_NO_TEXTEDIT + case Qt::Key_F2: { + if (d->currentItem->renameEnabled()) { + d->currentItem->renameItem(); + d->currentItem->rename(); + return; + } + } break; +#endif + case Qt::Key_Home: { + d->currInputString.clear(); + if (!d->firstItem) + break; + + selectCurrent = false; + + Q3IconViewItem *item = 0; + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + while (!item && c) { + QList<Q3IconViewItem*> &list = c->items; + for (int j = 0; j < list.size(); ++j) { + Q3IconViewItem *i = list.at(j); + if (!item) { + item = i; + } else { + if (d->arrangement == LeftToRight) { + // we use pixmap so the items textlength are ignored + // find topmost, leftmost item + if (i->pixmapRect(false).y() < item->pixmapRect(false).y() || + (i->pixmapRect(false).y() == item->pixmapRect(false).y() && + i->pixmapRect(false).x() < item->pixmapRect(false).x())) + item = i; + } else { + // find leftmost, topmost item + if (i->pixmapRect(false).x() < item->pixmapRect(false).x() || + (i->pixmapRect(false).x() == item->pixmapRect(false).x() && + i->pixmapRect(false).y() < item->pixmapRect(false).y())) + item = i; + } + } + } + c = c->n; + } + + if (item) { + Q3IconViewItem *old = d->currentItem; + setCurrentItem(item); + ensureItemVisible(item); + handleItemChange(old, e->state() & Qt::ShiftButton, + e->state() & Qt::ControlButton, true); + } + } break; + case Qt::Key_End: { + d->currInputString.clear(); + if (!d->lastItem) + break; + + selectCurrent = false; + + Q3IconViewItem *item = 0; + Q3IconViewPrivate::ItemContainer *c = d->lastContainer; + while (!item && c) { + QList<Q3IconViewItem*> &list = c->items; + for (int j = 0; j < list.size(); ++j) { + Q3IconViewItem *i = list.at(j); + if (!item) { + item = i; + } else { + if (d->arrangement == LeftToRight) { + // find bottommost, rightmost item + if (i->pixmapRect(false).bottom() > item->pixmapRect(false).bottom() || + (i->pixmapRect(false).bottom() == item->pixmapRect(false).bottom() && + i->pixmapRect(false).right() > item->pixmapRect(false).right())) + item = i; + } else { + // find rightmost, bottommost item + if (i->pixmapRect(false).right() > item->pixmapRect(false).right() || + (i->pixmapRect(false).right() == item->pixmapRect(false).right() && + i->pixmapRect(false).bottom() > item->pixmapRect(false).bottom())) + item = i; + } + } + } + c = c->p; + } + + if (item) { + Q3IconViewItem *old = d->currentItem; + setCurrentItem(item); + ensureItemVisible(item); + handleItemChange(old, e->state() & Qt::ShiftButton, + e->state() & Qt::ControlButton, true); + } + } break; + case Qt::Key_Right: { + d->currInputString.clear(); + Q3IconViewItem *item; + selectCurrent = false; + Direction dir = DirRight; + + QRect r(0, d->currentItem->y(), contentsWidth(), d->currentItem->height()); + item = findItem(dir, d->currentItem->rect().center(), r); + + // search the row below from the right + while (!item && r.y() < contentsHeight()) { + r.moveBy(0, d->currentItem->height()); + item = findItem(dir, QPoint(0, r.center().y()), r); + } + + if (item) { + Q3IconViewItem *old = d->currentItem; + setCurrentItem(item); + ensureItemVisible(item); + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + } break; + case Qt::Key_Left: { + d->currInputString.clear(); + Q3IconViewItem *item; + selectCurrent = false; + Direction dir = DirLeft; + + QRect r(0, d->currentItem->y(), contentsWidth(), d->currentItem->height()); + item = findItem(dir, d->currentItem->rect().center(), r); + + // search the row above from the left + while (!item && r.y() >= 0) { + r.moveBy(0, - d->currentItem->height()); + item = findItem(dir, QPoint(contentsWidth(), r.center().y()), r); + } + + if (item) { + Q3IconViewItem *old = d->currentItem; + setCurrentItem(item); + ensureItemVisible(item); + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + } break; + case Qt::Key_Space: { + d->currInputString.clear(); + if (d->selectionMode == Single) + break; + + d->currentItem->setSelected(!d->currentItem->isSelected(), true); + } break; + case Qt::Key_Enter: case Qt::Key_Return: + d->currInputString.clear(); + emit returnPressed(d->currentItem); + break; + case Qt::Key_Down: { + d->currInputString.clear(); + Q3IconViewItem *item; + selectCurrent = false; + Direction dir = DirDown; + + QRect r(d->currentItem->x(), 0, d->currentItem->width(), contentsHeight()); + item = findItem(dir, d->currentItem->rect().center(), r); + + // finding the closest item below and to the right + while (!item && r.x() < contentsWidth()) { + r.moveBy(r.width() , 0); + item = findItem(dir, QPoint(r.center().x(), 0), r); + } + + + Q3IconViewItem *i = d->currentItem; + setCurrentItem(item); + item = i; + handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } break; + case Qt::Key_Up: { + d->currInputString.clear(); + Q3IconViewItem *item; + selectCurrent = false; + Direction dir = DirUp; + + QRect r(d->currentItem->x(), 0, d->currentItem->width(), contentsHeight()); + item = findItem(dir, d->currentItem->rect().center(), r); + + // finding the closest item above and to the left + while (!item && r.x() >= 0) { + r.moveBy(- r.width(), 0); + item = findItem(dir, QPoint(r.center().x(), contentsHeight()), r); + } + + Q3IconViewItem *i = d->currentItem; + setCurrentItem(item); + item = i; + handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } break; + case Qt::Key_Next: { + d->currInputString.clear(); + selectCurrent = false; + QRect r; + if (d->arrangement == LeftToRight) + r = QRect(0, d->currentItem->y() + visibleHeight(), contentsWidth(), visibleHeight()); + else + r = QRect(d->currentItem->x() + visibleWidth(), 0, visibleWidth(), contentsHeight()); + Q3IconViewItem *item = d->currentItem; + Q3IconViewItem *ni = findFirstVisibleItem(r ); + if (!ni) { + if (d->arrangement == LeftToRight) + r = QRect(0, d->currentItem->y() + d->currentItem->height(), contentsWidth(), contentsHeight()); + else + r = QRect(d->currentItem->x() + d->currentItem->width(), 0, contentsWidth(), contentsHeight()); + ni = findLastVisibleItem(r ); + } + if (ni) { + setCurrentItem(ni); + handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + } break; + case Qt::Key_Prior: { + d->currInputString.clear(); + selectCurrent = false; + QRect r; + if (d->arrangement == LeftToRight) + r = QRect(0, d->currentItem->y() - visibleHeight(), contentsWidth(), visibleHeight()); + else + r = QRect(d->currentItem->x() - visibleWidth(), 0, visibleWidth(), contentsHeight()); + Q3IconViewItem *item = d->currentItem; + Q3IconViewItem *ni = findFirstVisibleItem(r ); + if (!ni) { + if (d->arrangement == LeftToRight) + r = QRect(0, 0, contentsWidth(), d->currentItem->y()); + else + r = QRect(0, 0, d->currentItem->x(), contentsHeight()); + ni = findFirstVisibleItem(r ); + } + if (ni) { + setCurrentItem(ni); + handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + } break; + default: + if (!e->text().isEmpty() && e->text()[0].isPrint()) { + selectCurrent = false; + Q3IconViewItem *i = d->currentItem; + if (!i) + i = d->firstItem; + if (!d->inputTimer->isActive()) { + d->currInputString = e->text(); + i = i->next; + if (!i) + i = d->firstItem; + i = findItemByName(i); + } else { + d->inputTimer->stop(); + d->currInputString += e->text(); + i = findItemByName(i); + if (!i) { + d->currInputString = e->text(); + if (d->currentItem && d->currentItem->next) + i = d->currentItem->next; + else + i = d->firstItem; + i = findItemByName(i); + } + } + if (i) { + setCurrentItem(i); + if (d->selectionMode == Extended) { + bool changed = false; + bool block = signalsBlocked(); + blockSignals(true); + selectAll(false); + blockSignals(block); + if (!i->selected && i->isSelectable()) { + changed = true; + i->selected = true; + repaintItem(i); + } + if (changed) + emit selectionChanged(); + } + } + d->inputTimer->start(400, true); + } else { + selectCurrent = false; + d->currInputString.clear(); + if (e->state() & Qt::ControlButton) { + switch (e->key()) { + case Qt::Key_A: + selectAll(true); + break; + } + } + e->ignore(); + return; + } + } + + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = d->currentItem; + + if (d->currentItem && !d->currentItem->isSelected() && + d->selectionMode == Single && selectCurrent) { + d->currentItem->setSelected(true); + } + + ensureItemVisible(d->currentItem); +} + +/* + Finds the closest item in the Direction \a dir relative from the point \a relativeTo + which intersects with the searchRect. + + The function chooses the closest item with its center in the \a searchRect. +*/ +Q3IconViewItem* Q3IconView::findItem(Direction dir, + const QPoint &relativeTo, + const QRect &searchRect) const +{ + Q3IconViewItem *centerMatch = 0; + int centerMatchML = 0; + + // gets list of containers with potential items + QList<Q3IconViewPrivate::ItemContainer * >* cList = + d->findContainers(dir, relativeTo, searchRect); + + for (int i = 0; i < cList->size() && !centerMatch; ++i) { + QList<Q3IconViewItem *> &list = (cList->at(i))->items; + for (int j = 0; j < list.size(); ++j) { + Q3IconViewItem *item = list.at(j); + if (neighbourItem(dir, relativeTo, item) && + searchRect.contains(item->rect().center()) && item != currentItem()) { + int ml = (relativeTo - item->rect().center()).manhattanLength(); + if (centerMatch) { + if (ml < centerMatchML) { + centerMatch = item; + centerMatchML = ml; + } + } else { + centerMatch = item; + centerMatchML = ml; + } + } + } + } + return centerMatch; +} + + +/* + Returns true if the items orientation compared to + the point \a relativeTo is correct. +*/ +bool Q3IconView::neighbourItem(Direction dir, + const QPoint &relativeTo, + const Q3IconViewItem *item) const +{ + switch (dir) { + case DirUp: + if (item->rect().center().y() < relativeTo.y()) + return true; + break; + case DirDown: + if (item->rect().center().y() > relativeTo.y()) + return true; + break; + case DirLeft: + if (item->rect().center().x() < relativeTo.x()) + return true; + break; + case DirRight: + if (item->rect().center().x() > relativeTo.x()) + return true; + break; + default: + // nothing + break; + } + return false; +} + +/*! + \reimp +*/ + +void Q3IconView::focusInEvent(QFocusEvent *e) +{ + d->mousePressed = false; + d->inMenuMode = false; + if (d->currentItem) { + repaintItem(d->currentItem); + } else if (d->firstItem && e->reason() != Qt::MouseFocusReason) { + d->currentItem = d->firstItem; + emit currentChanged(d->currentItem); + repaintItem(d->currentItem); + } + + if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) + repaintSelectedItems(); +} + +/*! + \reimp +*/ + +void Q3IconView::focusOutEvent(QFocusEvent *e) +{ + if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) { + d->inMenuMode = + e->reason() == Qt::PopupFocusReason || + (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar")); + if (!d->inMenuMode) + repaintSelectedItems(); + } + if (d->currentItem) + repaintItem(d->currentItem); +} + +/*! + Draws the rubber band using the painter \a p. +*/ + +void Q3IconView::drawRubber(QPainter *p) +{ + if (!p || !d->rubber) + return; + QStyleOptionRubberBand opt; + opt.rect = d->rubber->normalized(); + opt.shape = QRubberBand::Rectangle; + opt.palette = palette(); + opt.state = QStyle::State_None; + style()->drawControl(QStyle::CE_RubberBand, &opt, p, this); +} + +/*! + Returns the Q3DragObject that should be used for drag-and-drop. + This function is called by the icon view when starting a drag to + get the dragobject that should be used for the drag. Subclasses + may reimplement this. + + \sa Q3IconDrag +*/ + +#ifndef QT_NO_DRAGANDDROP +Q3DragObject *Q3IconView::dragObject() +{ + if (!d->currentItem) + return 0; + + QPoint orig = d->dragStartPos; + + Q3IconDrag *drag = new Q3IconDrag(viewport()); + drag->setPixmap((d->currentItem->pixmap() ? + *d->currentItem->pixmap() : QPixmap()), // ### QPicture + QPoint(d->currentItem->pixmapRect().width() / 2, + d->currentItem->pixmapRect().height() / 2)); + + if (d->selectionMode == NoSelection) { + Q3IconViewItem *item = d->currentItem; + drag->append(Q3IconDragItem(), + QRect(item->pixmapRect(false).x() - orig.x(), + item->pixmapRect(false).y() - orig.y(), + item->pixmapRect().width(), item->pixmapRect().height()), + QRect(item->textRect(false).x() - orig.x(), + item->textRect(false).y() - orig.y(), + item->textRect().width(), item->textRect().height())); + } else { + for (Q3IconViewItem *item = d->firstItem; item; item = item->next) { + if (item->isSelected()) { + drag->append(Q3IconDragItem(), + QRect(item->pixmapRect(false).x() - orig.x(), + item->pixmapRect(false).y() - orig.y(), + item->pixmapRect().width(), item->pixmapRect().height()), + QRect(item->textRect(false).x() - orig.x(), + item->textRect(false).y() - orig.y(), + item->textRect().width(), item->textRect().height())); + } + } + } + + return drag; +} + +/*! + Starts a drag. +*/ + +void Q3IconView::startDrag() +{ + if (!d->startDragItem) + return; + + QPoint orig = d->dragStartPos; + d->dragStart = QPoint(orig.x() - d->startDragItem->x(), + orig.y() - d->startDragItem->y()); + d->startDragItem = 0; + d->mousePressed = false; + d->pressedItem = 0; + d->pressedSelected = 0; + + Q3DragObject *drag = dragObject(); + if (!drag) + return; + + if (drag->drag()) + if (drag->target() != viewport()) + emit moved(); +} + +#endif + +/*! + Inserts the Q3IconViewItem \a item in the icon view's grid. \e{You + should never need to call this function.} Instead, insert + Q3IconViewItems by creating them with a pointer to the Q3IconView + that they are to be inserted into. +*/ + +void Q3IconView::insertInGrid(Q3IconViewItem *item) +{ + if (!item) + return; + + if (d->reorderItemsWhenInsert) { + // #### make this efficient - but it's not too dramatic + int y = d->spacing; + + item->dirty = false; + if (item == d->firstItem) { + bool dummy; + makeRowLayout(item, y, dummy); + return; + } + + Q3IconViewItem *begin = rowBegin(item); + y = begin->y(); + while (begin) { + bool dummy; + begin = makeRowLayout(begin, y, dummy); + + if (!begin || !begin->next) + break; + + begin = begin->next; + } + item->dirty = false; + } else { + QRegion r(QRect(0, 0, qMax(contentsWidth(), visibleWidth()), + qMax(contentsHeight(), visibleHeight()))); + + int y = -1; + for (Q3IconViewItem *i = d->firstItem; i; i = i->next) { + r = r.subtracted(i->rect()); + y = qMax(y, i->y() + i->height()); + } + + QVector<QRect> rects = r.rects(); + bool foundPlace = false; + for (int j = 0; j < rects.size(); ++j) { + const QRect rect = rects.at(j); + if (rect.width() >= item->width() && + rect.height() >= item->height()) { + int sx = 0, sy = 0; + if (rect.width() >= item->width() + d->spacing) + sx = d->spacing; + if (rect.height() >= item->height() + d->spacing) + sy = d->spacing; + item->move(rect.x() + sx, rect.y() + sy); + foundPlace = true; + break; + } + } + + if (!foundPlace) + item->move(d->spacing, y + d->spacing); + + resizeContents(qMax(contentsWidth(), item->x() + item->width()), + qMax(contentsHeight(), item->y() + item->height())); + item->dirty = false; + } +} + +/*! + Emits a signal to indicate selection changes. \a i is the + Q3IconViewItem that was selected or de-selected. + + \e{You should never need to call this function.} +*/ + +void Q3IconView::emitSelectionChanged(Q3IconViewItem *i) +{ + emit selectionChanged(); + if (d->selectionMode == Single) + emit selectionChanged(i ? i : d->currentItem); +} + +/*! + \internal +*/ + +void Q3IconView::emitRenamed(Q3IconViewItem *item) +{ + if (!item) + return; + + emit itemRenamed(item, item->text()); + emit itemRenamed(item); +} + +/*! + If a drag enters the icon view the shapes of the objects which the + drag contains are drawn, usnig \a pos as origin. +*/ + +void Q3IconView::drawDragShapes(const QPoint &pos) +{ +#ifndef QT_NO_DRAGANDDROP + if (pos == QPoint(-1, -1)) + return; + + if (!d->drawDragShapes) { + d->drawDragShapes = true; + return; + } + + d->dragPos = pos; + viewport()->update(); +#endif +} + +/*! + When a drag enters the icon view, this function is called to + initialize it. Initializing in this context means getting + information about the drag, for example so that the icon view + knows enough about the drag to be able to draw drag shapes for the + drag data (e.g. shapes of icons which are dragged), etc. +*/ + +#ifndef QT_NO_DRAGANDDROP +void Q3IconView::initDragEnter(QDropEvent *e) +{ + if (Q3IconDrag::canDecode(e)) { + Q3IconDragPrivate::decode(e, d->iconDragData); + d->isIconDrag = true; + } else if (Q3UriDrag::canDecode(e)) { + Q3StrList lst; + Q3UriDrag::decode(e, lst); + d->numDragItems = lst.count(); + } else { + d->numDragItems = 0; + } + +} +#endif + +/*! + This function is called to draw the rectangle \a r of the + background using the painter \a p. + + The default implementation fills \a r with the viewport's + backgroundBrush(). Subclasses can reimplement this to draw custom + backgrounds. + + \sa drawContents() +*/ + +void Q3IconView::drawBackground(QPainter *p, const QRect &r) +{ + p->fillRect(r, viewport()->backgroundBrush()); +} + +/*! + \reimp +*/ + +bool Q3IconView::eventFilter(QObject * o, QEvent * e) +{ + if (o == viewport()) { + switch(e->type()) { + case QEvent::FocusIn: + focusInEvent((QFocusEvent*)e); + return true; + case QEvent::FocusOut: + focusOutEvent((QFocusEvent*)e); + return true; + case QEvent::Enter: + enterEvent(e); + return true; + case QEvent::Paint: + if (o == viewport()) { + viewportPaintEvent((QPaintEvent*)e); + QPainter p(viewport()); + if (d->dragging) { + if (!d->rubber && d->drawDragShapes) { + p.setPen(QPen(Qt::color0)); + QStyleOptionFocusRect opt; + opt.palette = palette(); + opt.state = QStyle::State_KeyboardFocusChange; + opt.backgroundColor = palette().base().color(); + if (d->isIconDrag) { + d->dragPos = contentsToViewport(d->dragPos); + QLinkedList<Q3IconDragDataItem>::Iterator it = d->iconDragData.begin(); + for (; it != d->iconDragData.end(); ++it) { + QRect ir = (*it).item.pixmapRect(); + QRect tr = (*it).item.textRect(); + tr.moveBy(d->dragPos.x(), d->dragPos.y()); + ir.moveBy(d->dragPos.x(), d->dragPos.y()); + if (!ir.intersects(QRect(0, 0, visibleWidth(), visibleHeight()))) + continue; + opt.rect = ir; + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this); + opt.rect = tr; + p.drawRect(tr); + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this); + } + } else if (d->numDragItems > 0) { + for (int i = 0; i < d->numDragItems; ++i) { + opt.rect.setRect(d->dragPos.x() + i * 40, d->dragPos.y(), 35, 35); + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this); + } + + } + p.end(); + } + } else { + p.translate(-contentsX(), -contentsY()); + drawRubber(&p); + } + } + return true; +#ifndef QT_NO_TOOLTIP + case QHelpEvent::ToolTip: + { + if (wordWrapIconText() || !showToolTips()) + return false; + + QHelpEvent *he = static_cast<QHelpEvent *>(e); + Q3IconViewItem *item = findItem(viewportToContents(he->pos())); + if (!item || item->tmpText == item->itemText) { + QToolTip::showText(he->globalPos(), QString(), viewport()); + return true; + } + + QToolTip::showText(he->globalPos(), item->itemText, viewport()); + return true; + } +#endif + default: + // nothing + break; + } + } + + return Q3ScrollView::eventFilter(o, e); +} + + +/*! + \reimp +*/ + +QSize Q3IconView::minimumSizeHint() const +{ + return Q3ScrollView::minimumSizeHint(); +} + +/*! + \internal + Finds the next item after the start item beginning + with \a text. +*/ + +Q3IconViewItem* Q3IconView::findItemByName(Q3IconViewItem *start) +{ + if (!start) + return 0; + QString match = d->currInputString.toLower(); + if (match.length() < 1) + return start; + QString curText; + Q3IconViewItem *i = start; + do { + curText = i->text().toLower(); + if (curText.startsWith(match)) + return i; + i = i->next; + if (!i) + i = d->firstItem; + } while (i != start); + return 0; +} + +/*! + Lays out a row of icons (if Arrangement == \l TopToBottom this is + a column). Starts laying out with the item \a begin. \a y is the + starting coordinate. Returns the last item of the row (column) and + sets the new starting coordinate to \a y. The \a changed parameter + is used internally. + + \warning This function may be made private in a future version of + Qt. We do not recommend calling it. +*/ + +Q3IconViewItem *Q3IconView::makeRowLayout(Q3IconViewItem *begin, int &y, bool &changed) +{ + Q3IconViewItem *end = 0; + + bool reverse = QApplication::reverseLayout(); + changed = false; + + if (d->arrangement == LeftToRight) { + + if (d->rastX == -1) { + // first calculate the row height + int h = 0; + int x = 0; + int ih = 0; + Q3IconViewItem *item = begin; + for (;;) { + x += d->spacing + item->width(); + if (x > visibleWidth() && item != begin) { + item = item->prev; + break; + } + h = qMax(h, item->height()); + ih = qMax(ih, item->pixmapRect().height()); + Q3IconViewItem *old = item; + item = item->next; + if (!item) { + item = old; + break; + } + } + end = item; + + if (d->rastY != -1) + h = qMax(h, d->rastY); + + // now move the items + item = begin; + for (;;) { + item->dirty = false; + int x; + if (item == begin) { + if (reverse) + x = visibleWidth() - d->spacing - item->width(); + else + x = d->spacing; + } else { + if (reverse) + x = item->prev->x() - item->width() - d->spacing; + else + x = item->prev->x() + item->prev->width() + d->spacing; + } + changed = item->move(x, y + ih - item->pixmapRect().height()) || changed; + if (y + h < item->y() + item->height()) + h = qMax(h, ih + item->textRect().height()); + if (item == end) + break; + item = item->next; + } + y += h + d->spacing; + } else { + // first calculate the row height + int h = begin->height(); + int x = d->spacing; + int ih = begin->pixmapRect().height(); + Q3IconViewItem *item = begin; + int i = 0; + int sp = 0; + for (;;) { + int r = calcGridNum(item->width(), d->rastX); + if (item == begin) { + i += r; + sp += r; + x = d->spacing + d->rastX * r; + } else { + sp += r; + i += r; + x = i * d->rastX + sp * d->spacing; + } + if (x > visibleWidth() && item != begin) { + item = item->prev; + break; + } + h = qMax(h, item->height()); + ih = qMax(ih, item->pixmapRect().height()); + Q3IconViewItem *old = item; + item = item->next; + if (!item) { + item = old; + break; + } + } + end = item; + + if (d->rastY != -1) + h = qMax(h, d->rastY); + + // now move the items + item = begin; + i = 0; + sp = 0; + for (;;) { + item->dirty = false; + int r = calcGridNum(item->width(), d->rastX); + if (item == begin) { + if (d->itemTextPos == Bottom) + changed = item->move(d->spacing + (r * d->rastX - item->width()) / 2, + y + ih - item->pixmapRect().height()) || changed; + else + changed = item->move(d->spacing, y + ih - item->pixmapRect().height()) || changed; + i += r; + sp += r; + } else { + sp += r; + int x = i * d->rastX + sp * d->spacing; + if (d->itemTextPos == Bottom) + changed = item->move(x + (r * d->rastX - item->width()) / 2, + y + ih - item->pixmapRect().height()) || changed; + else + changed = item->move(x, y + ih - item->pixmapRect().height()) || changed; + i += r; + } + if (y + h < item->y() + item->height()) + h = qMax(h, ih + item->textRect().height()); + if (item == end) + break; + item = item->next; + } + y += h + d->spacing; + } + + + } else { // -------------------------------- SOUTH ------------------------------ + + int x = y; + + { + int w = 0; + int y = 0; + Q3IconViewItem *item = begin; + for (;;) { + y += d->spacing + item->height(); + if (y > visibleHeight() && item != begin) { + item = item->prev; + break; + } + w = qMax(w, item->width()); + Q3IconViewItem *old = item; + item = item->next; + if (!item) { + item = old; + break; + } + } + end = item; + + if (d->rastX != -1) + w = qMax(w, d->rastX); + + // now move the items + item = begin; + for (;;) { + item->dirty = false; + if (d->itemTextPos == Bottom) { + if (item == begin) + changed = item->move(x + (w - item->width()) / 2, d->spacing) || changed; + else + changed = item->move(x + (w - item->width()) / 2, + item->prev->y() + item->prev->height() + d->spacing) || changed; + } else { + if (item == begin) + changed = item->move(x, d->spacing) || changed; + else + changed = item->move(x, item->prev->y() + item->prev->height() + d->spacing) || changed; + } + if (item == end) + break; + item = item->next; + } + x += w + d->spacing; + } + + y = x; + } + + return end; +} + +/*! + \internal + Calculates how many cells an item of width \a w needs in a grid with of + \a x and returns the result. +*/ + +int Q3IconView::calcGridNum(int w, int x) const +{ + float r = (float)w / (float)x; + if ((w / x) * x != w) + r += 1.0; + return (int)r; +} + +/*! + \internal + Returns the first item of the row which contains \a item. +*/ + +Q3IconViewItem *Q3IconView::rowBegin(Q3IconViewItem *) const +{ + // #### todo + return d->firstItem; +} + +/*! + Sorts and rearranges all the items in the icon view. If \a + ascending is true, the items are sorted in increasing order, + otherwise they are sorted in decreasing order. + + Q3IconViewItem::compare() is used to compare pairs of items. The + sorting is based on the items' keys; these default to the items' + text unless specifically set to something else. + + Note that this function sets the sort order to \a ascending. + + \sa Q3IconViewItem::key(), Q3IconViewItem::setKey(), + Q3IconViewItem::compare(), Q3IconView::setSorting(), + Q3IconView::sortDirection() +*/ + +void Q3IconView::sort(bool ascending) +{ + if (count() == 0) + return; + + d->sortDirection = ascending; + Q3IconViewPrivate::SortableItem *items = new Q3IconViewPrivate::SortableItem[count()]; + + Q3IconViewItem *item = d->firstItem; + int i = 0; + for (; item; item = item->next) + items[i++].item = item; + + qsort(items, count(), sizeof(Q3IconViewPrivate::SortableItem), cmpIconViewItems); + + Q3IconViewItem *prev = 0; + item = 0; + if (ascending) { + for (i = 0; i < (int)count(); ++i) { + item = items[i].item; + if (item) { + item->prev = prev; + if (item->prev) + item->prev->next = item; + item->next = 0; + } + if (i == 0) + d->firstItem = item; + if (i == (int)count() - 1) + d->lastItem = item; + prev = item; + } + } else { + for (i = (int)count() - 1; i >= 0 ; --i) { + item = items[i].item; + if (item) { + item->prev = prev; + if (item->prev) + item->prev->next = item; + item->next = 0; + } + if (i == (int)count() - 1) + d->firstItem = item; + if (i == 0) + d->lastItem = item; + prev = item; + } + } + + delete [] items; + + arrangeItemsInGrid(true); +} + +/*! + \reimp +*/ + +QSize Q3IconView::sizeHint() const +{ + ensurePolished(); + + if (!d->firstItem) + return Q3ScrollView::sizeHint(); + + if (d->dirty && d->firstSizeHint) { + ((Q3IconView*)this)->resizeContents(qMax(400, contentsWidth()), + qMax(400, contentsHeight())); + if (autoArrange()) + ((Q3IconView*)this)->arrangeItemsInGrid(false); + d->firstSizeHint = false; + } + + d->dirty = true; + const QScrollBar *sb = verticalScrollBar(); + QStyleOptionSlider opt; + opt.init(sb); + opt.orientation = sb->orientation(); + int extra = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt, sb) + 2 * frameWidth(); + QSize s(qMin(400, contentsWidth() + extra), + qMin(400, contentsHeight() + extra)); + return s; +} + +/*! + \internal +*/ + +void Q3IconView::updateContents() +{ + viewport()->update(); +} + +/*! + \reimp +*/ + +void Q3IconView::enterEvent(QEvent *e) +{ + Q3ScrollView::enterEvent(e); + emit onViewport(); +} + +/*! + \internal + This function is always called when the geometry of an item changes. + This function moves the item into the correct area in the internal + data structure. +*/ + +void Q3IconView::updateItemContainer(Q3IconViewItem *item) +{ + if (!item || d->containerUpdateLocked || (!isVisible() && autoArrange())) + return; + + if (item->d->container1 && d->firstContainer) { + //Special-case to check if we can use removeLast otherwise use removeAll (slower) + if (item->d->container1->items.last() == item) + item->d->container1->items.removeLast(); + else + item->d->container1->items.removeAll(item); + } + item->d->container1 = 0; + if (item->d->container2 && d->firstContainer) { + //Special-case to check if we can use removeLast otherwise use removeAll (slower) + if (item->d->container2->items.last() == item) + item->d->container2->items.removeLast(); + else + item->d->container2->items.removeAll(item); + } + item->d->container2 = 0; + + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + if (!c) { + appendItemContainer(); + c = d->firstContainer; + } + + const QRect irect = item->rect(); + bool contains = false; + for (;;) { + if (c->rect.intersects(irect)) { + contains = c->rect.contains(irect); + break; + } + + c = c->n; + if (!c) { + appendItemContainer(); + c = d->lastContainer; + } + } + + if (!c) { + qWarning("Q3IconViewItem::updateItemContainer(): No fitting container found!"); + return; + } + + c->items.append(item); + item->d->container1 = c; + + if (!contains) { + c = c->n; + if (!c) { + appendItemContainer(); + c = d->lastContainer; + } + c->items.append(item); + item->d->container2 = c; + } + if (contentsWidth() < irect.right() || contentsHeight() < irect.bottom()) + resizeContents(qMax(contentsWidth(), irect.right()), qMax(contentsHeight(), irect.bottom())); +} + +/*! + \internal + Appends a new rect area to the internal data structure of the items. +*/ + +void Q3IconView::appendItemContainer() +{ + QSize s; + // #### We have to find out which value is best here + if (d->arrangement == LeftToRight) + s = QSize(INT_MAX - 1, RECT_EXTENSION); + else + s = QSize(RECT_EXTENSION, INT_MAX - 1); + + if (!d->firstContainer) { + d->firstContainer = new Q3IconViewPrivate::ItemContainer(0, 0, QRect(QPoint(0, 0), s)); + d->lastContainer = d->firstContainer; + } else { + if (d->arrangement == LeftToRight) + d->lastContainer = new Q3IconViewPrivate::ItemContainer( + d->lastContainer, 0, QRect(d->lastContainer->rect.bottomLeft(), s)); + else + d->lastContainer = new Q3IconViewPrivate::ItemContainer( + d->lastContainer, 0, QRect(d->lastContainer->rect.topRight(), s)); + } +} + +/*! \internal + + Rebuilds the whole internal data structure. This is done when it's + likely that most/all items change their geometry (e.g. in + arrangeItemsInGrid()), because calling this is then more efficient + than calling updateItemContainer() for each item. +*/ + +void Q3IconView::rebuildContainers() +{ + Q3IconViewPrivate::ItemContainer *c = d->firstContainer, *tmpc; + while (c) { + tmpc = c->n; + delete c; + c = tmpc; + } + d->firstContainer = d->lastContainer = 0; + + Q3IconViewItem *item = d->firstItem; + appendItemContainer(); + c = d->lastContainer; + while (item) { + if (c->rect.contains(item->rect())) { + item->d->container1 = c; + item->d->container2 = 0; + c->items.append(item); + item = item->next; + } else if (c->rect.intersects(item->rect())) { + item->d->container1 = c; + c->items.append(item); + c = c->n; + if (!c) { + appendItemContainer(); + c = d->lastContainer; + } + c->items.append(item); + item->d->container2 = c; + item = item->next; + c = c->p; + } else { + if (d->arrangement == LeftToRight) { + if (item->y() < c->rect.y() && c->p) { + c = c->p; + continue; + } + } else { + if (item->x() < c->rect.x() && c->p) { + c = c->p; + continue; + } + } + + c = c->n; + if (!c) { + appendItemContainer(); + c = d->lastContainer; + } + } + } +} + +/*! + \internal +*/ + +void Q3IconView::movedContents(int, int) +{ + if (d->drawDragShapes) { + drawDragShapes(d->oldDragPos); + d->oldDragPos = QPoint(-1, -1); + } +} + +void Q3IconView::handleItemChange(Q3IconViewItem *old, bool shift, + bool control, bool homeend) +{ + if (d->selectionMode == Single) { + bool block = signalsBlocked(); + blockSignals(true); + if (old) + old->setSelected(false); + blockSignals(block); + d->currentItem->setSelected(true, true); + } else if (d->selectionMode == Extended) { + if (shift) { + if (!d->selectAnchor) { + if (old && !old->selected && old->isSelectable()) { + old->selected = true; + repaintItem(old); + } + d->currentItem->setSelected(true, true); + } else { + Q3IconViewItem *from = d->selectAnchor, *to = d->currentItem; + if (!from || !to) + return; + + // checking if it's downwards and if we span rows + bool downwards = false; + bool spanning = false; + if (d->arrangement == LeftToRight) { + if (from->rect().center().y() < to->rect().center().y()) + downwards = true; + } else { + if (from->rect().center().x() < to->rect().center().x()) + downwards = true; + } + + QRect fr = from->rect(); + QRect tr = to->rect(); + if (d->arrangement == LeftToRight) { + fr.moveTopLeft(QPoint(tr.x(), fr.y())); + if (!tr.intersects(fr)) + spanning = true; + } else { + fr.moveTopLeft(QPoint(fr.x(), tr.y())); + if (!tr.intersects(fr)) + spanning = true; + } + + + // finding the rectangles + QRect topRect, bottomRect, midRect; + if (!spanning) { + midRect = from->rect().united(to->rect()); + } else { + if (downwards) { + topRect = from->rect(); + bottomRect = to->rect(); + } else { + topRect = to->rect(); + bottomRect = from->rect(); + } + if (d->arrangement == LeftToRight) { + topRect.setRight(contentsWidth()); + bottomRect.setLeft(0); + midRect.setRect(0, topRect.bottom(), + contentsWidth(), + bottomRect.top() - topRect.bottom()); + } else { + topRect.setBottom(contentsHeight()); + bottomRect.setTop(0); + midRect.setRect(topRect.right(), + 0, + bottomRect.left() - topRect.right(), + contentsHeight()); + } + } + + // finding contained items and selecting them + Q3IconViewItem *item = 0; + bool changed = false; + bool midValid = midRect.isValid(); + bool topValid = topRect.isValid(); + bool bottomValid = bottomRect.isValid(); + QRect selectedRect, unselectedRect; + for (item = d->firstItem; item; item = item->next) { + bool contained = false; + QPoint itemCenter = item->rect().center(); + if (midValid && midRect.contains(itemCenter)) + contained = true; + if (!contained && topValid && topRect.contains(itemCenter)) + contained = true; + if (!contained && bottomValid && bottomRect.contains(itemCenter)) + contained = true; + + if (contained) { + if (!item->selected && item->isSelectable()) { + changed = true; + item->selected = true; + selectedRect = selectedRect.united(item->rect()); + } + } else if (item->selected && !control) { + item->selected = false; + unselectedRect = unselectedRect.united(item->rect()); + changed = true; + } + } + + QRect viewRect(contentsX(), contentsY(), + visibleWidth(), visibleHeight()); + + if (viewRect.intersects(selectedRect)) { + if (homeend) + Q3ScrollView::updateContents(viewRect.intersected(selectedRect)); + else + repaintContents(viewRect.intersected(selectedRect)); + } + if (viewRect.intersects(unselectedRect)) { + if (homeend) + Q3ScrollView::updateContents(viewRect.intersected(unselectedRect)); + else + repaintContents(viewRect.intersected(unselectedRect)); + } + + if (changed) + emit selectionChanged(); + } + } else if (!control) { + blockSignals(true); + selectAll(false); + blockSignals(false); + d->currentItem->setSelected(true, true); + } + } else { + if (shift) + d->currentItem->setSelected(!d->currentItem->isSelected(), true); + } +} + +QBitmap Q3IconView::mask(QPixmap *pix) const +{ + QBitmap m; + if (d->maskCache.find(QString::number(pix->serialNumber()), m)) + return m; + if (pix->hasAlphaChannel()) + m = pix->mask(); + else + m = pix->createHeuristicMask(); + d->maskCache.insert(QString::number(pix->serialNumber()), m); + return m; +} + +/*! + Returns true if an iconview item is being renamed; otherwise + returns false. +*/ + +bool Q3IconView::isRenaming() const +{ +#ifndef QT_NO_TEXTEDIT + return d->renamingItem && d->renamingItem->renameBox; +#else + return false; +#endif +} + +/*! + \enum Q3IconView::StringComparisonMode + + This enum type is used to set the string comparison mode when + searching for an item. We'll refer to the string being searched + as the 'target' string. + + \value CaseSensitive The strings must match case sensitively. + \value ExactMatch The target and search strings must match exactly. + \value BeginsWith The target string begins with the search string. + \value EndsWith The target string ends with the search string. + \value Contains The target string contains the search string. + + If you OR these flags together (excluding \c CaseSensitive), the + search criteria be applied in the following order: \c ExactMatch, + \c BeginsWith, \c EndsWith, \c Contains. + + Matching is case-insensitive unless \c CaseSensitive is set. \c + CaseSensitive can be OR-ed with any combination of the other + flags. + + \sa ComparisonFlags +*/ + +/*! + \typedef Q3IconView::ComparisonFlags + + This typedef is used in Q3IconView's API for values that are OR'd + combinations of \l StringComparisonMode values. + + \sa StringComparisonMode +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_ICONVIEW diff --git a/src/qt3support/itemviews/q3iconview.h b/src/qt3support/itemviews/q3iconview.h new file mode 100644 index 0000000..d35a895 --- /dev/null +++ b/src/qt3support/itemviews/q3iconview.h @@ -0,0 +1,519 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3ICONVIEW_H +#define Q3ICONVIEW_H + +#include <Qt3Support/q3scrollview.h> +#include <QtCore/qstring.h> +#include <QtCore/qrect.h> +#include <QtCore/qpoint.h> +#include <QtCore/qsize.h> +#include <QtGui/qfont.h> // QString->QFont conversion +#include <Qt3Support/q3dragobject.h> +#include <QtGui/qbitmap.h> +#include <QtGui/qpicture.h> +#include <Qt3Support/q3valuelist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_ICONVIEW + +class Q3IconView; +class QPainter; +class QMimeSource; +class QMouseEvent; +class QDragEnterEvent; +class QDragMoveEvent; +class QDragLeaveEvent; +class QKeyEvent; +class QFocusEvent; +class QShowEvent; +class Q3IconViewItem; +class Q3IconViewItemLineEdit; +class QStringList; +class Q3IconDragPrivate; +class QColorGroup; + +#ifndef QT_NO_DRAGANDDROP + +class Q_COMPAT_EXPORT Q3IconDragItem +{ +public: + Q3IconDragItem(); + virtual ~Q3IconDragItem(); + virtual QByteArray data() const; + virtual void setData(const QByteArray &d); + bool operator== (const Q3IconDragItem&) const; + +private: + QByteArray ba; + +}; + +class Q_COMPAT_EXPORT Q3IconDrag : public Q3DragObject +{ + Q_OBJECT +public: + Q3IconDrag(QWidget * dragSource, const char* name = 0); + virtual ~Q3IconDrag(); + + void append(const Q3IconDragItem &item, const QRect &pr, const QRect &tr); + + virtual const char* format(int i) const; + static bool canDecode(QMimeSource* e); + virtual QByteArray encodedData(const char* mime) const; + +private: + Q_DISABLE_COPY(Q3IconDrag) + + Q3IconDragPrivate *d; + QChar endMark; + + friend class Q3IconView; + friend class Q3IconViewPrivate; +}; + +#endif + +class Q3IconViewToolTip; +class Q3IconViewItemPrivate; + +class Q_COMPAT_EXPORT Q3IconViewItem +{ + friend class Q3IconView; + friend class Q3IconViewToolTip; + friend class Q3IconViewItemLineEdit; + +public: + Q3IconViewItem(Q3IconView *parent); + Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after); + Q3IconViewItem(Q3IconView *parent, const QString &text); + Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, const QString &text); + Q3IconViewItem(Q3IconView *parent, const QString &text, const QPixmap &icon); + Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, const QString &text, const QPixmap &icon); +#ifndef QT_NO_PICTURE + Q3IconViewItem(Q3IconView *parent, const QString &text, const QPicture &picture); + Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, const QString &text, const QPicture &picture); +#endif + virtual ~Q3IconViewItem(); + + virtual void setRenameEnabled(bool allow); + virtual void setDragEnabled(bool allow); + virtual void setDropEnabled(bool allow); + + virtual QString text() const; + virtual QPixmap *pixmap() const; +#ifndef QT_NO_PICTURE + virtual QPicture *picture() const; +#endif + virtual QString key() const; + + bool renameEnabled() const; + bool dragEnabled() const; + bool dropEnabled() const; + + Q3IconView *iconView() const; + Q3IconViewItem *prevItem() const; + Q3IconViewItem *nextItem() const; + + int index() const; + + virtual void setSelected(bool s, bool cb); + virtual void setSelected(bool s); + virtual void setSelectable(bool s); + + bool isSelected() const; + bool isSelectable() const; + + virtual void repaint(); + + virtual bool move(int x, int y); + virtual void moveBy(int dx, int dy); + virtual bool move(const QPoint &pnt); + virtual void moveBy(const QPoint &pnt); + + QRect rect() const; + int x() const; + int y() const; + int width() const; + int height() const; + QSize size() const; + QPoint pos() const; + QRect textRect(bool relative = true) const; + QRect pixmapRect(bool relative = true) const; + bool contains(const QPoint& pnt) const; + bool intersects(const QRect& r) const; + + virtual bool acceptDrop(const QMimeSource *mime) const; + +#ifndef QT_NO_TEXTEDIT + void rename(); +#endif + + virtual int compare(Q3IconViewItem *i) const; + + virtual void setText(const QString &text); + virtual void setPixmap(const QPixmap &icon); +#ifndef QT_NO_PICTURE + virtual void setPicture(const QPicture &icon); +#endif + virtual void setText(const QString &text, bool recalc, bool redraw = true); + virtual void setPixmap(const QPixmap &icon, bool recalc, bool redraw = true); + virtual void setKey(const QString &k); + + virtual int rtti() const; + static int RTTI; + +protected: +#ifndef QT_NO_TEXTEDIT + virtual void removeRenameBox(); +#endif + virtual void calcRect(const QString &text_ = QString()); + virtual void paintItem(QPainter *p, const QColorGroup &cg); + virtual void paintFocus(QPainter *p, const QColorGroup &cg); +#ifndef QT_NO_DRAGANDDROP + virtual void dropped(QDropEvent *e, const Q3ValueList<Q3IconDragItem> &lst); +#endif + virtual void dragEntered(); + virtual void dragLeft(); + void setItemRect(const QRect &r); + void setTextRect(const QRect &r); + void setPixmapRect(const QRect &r); + void calcTmpText(); + QString tempText() const; + +private: + void init(Q3IconViewItem *after = 0 +#ifndef QT_NO_PICTURE + , QPicture *pic = 0 +#endif + ); +#ifndef QT_NO_TEXTEDIT + void renameItem(); + void cancelRenameItem(); +#endif + void checkRect(); + + Q3IconView *view; + QString itemText, itemKey; + QString tmpText; + QPixmap *itemIcon; +#ifndef QT_NO_PICTURE + QPicture *itemPic; +#endif + Q3IconViewItem *prev, *next; + uint allow_rename : 1; + uint allow_drag : 1; + uint allow_drop : 1; + uint selected : 1; + uint selectable : 1; + uint dirty : 1; + uint wordWrapDirty : 1; + QRect itemRect, itemTextRect, itemIconRect; +#ifndef QT_NO_TEXTEDIT + Q3IconViewItemLineEdit *renameBox; +#endif + QRect oldRect; + + Q3IconViewItemPrivate *d; + +}; + +class Q3IconViewPrivate; + +class Q_COMPAT_EXPORT Q3IconView : public Q3ScrollView +{ + friend class Q3IconViewItem; + friend class Q3IconViewPrivate; + friend class Q3IconViewToolTip; + + Q_OBJECT + // #### sorting and sort direction do not work + Q_ENUMS(SelectionMode ItemTextPos Arrangement ResizeMode) + Q_PROPERTY(bool sorting READ sorting) + Q_PROPERTY(bool sortDirection READ sortDirection) + Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) + Q_PROPERTY(int gridX READ gridX WRITE setGridX) + Q_PROPERTY(int gridY READ gridY WRITE setGridY) + Q_PROPERTY(int spacing READ spacing WRITE setSpacing) + Q_PROPERTY(ItemTextPos itemTextPos READ itemTextPos WRITE setItemTextPos) + Q_PROPERTY(QBrush itemTextBackground READ itemTextBackground WRITE setItemTextBackground) + Q_PROPERTY(Arrangement arrangement READ arrangement WRITE setArrangement) + Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) + Q_PROPERTY(int maxItemWidth READ maxItemWidth WRITE setMaxItemWidth) + Q_PROPERTY(int maxItemTextLength READ maxItemTextLength WRITE setMaxItemTextLength) + Q_PROPERTY(bool autoArrange READ autoArrange WRITE setAutoArrange) + Q_PROPERTY(bool itemsMovable READ itemsMovable WRITE setItemsMovable) + Q_PROPERTY(bool wordWrapIconText READ wordWrapIconText WRITE setWordWrapIconText) + Q_PROPERTY(bool showToolTips READ showToolTips WRITE setShowToolTips) + Q_PROPERTY(uint count READ count) + +public: + enum SelectionMode { + Single = 0, + Multi, + Extended, + NoSelection + }; + enum Arrangement { + LeftToRight = 0, + TopToBottom + }; + enum ResizeMode { + Fixed = 0, + Adjust + }; + enum ItemTextPos { + Bottom = 0, + Right + }; + + Q3IconView(QWidget* parent=0, const char* name=0, Qt::WindowFlags f = 0); + virtual ~Q3IconView(); + + virtual void insertItem(Q3IconViewItem *item, Q3IconViewItem *after = 0L); + virtual void takeItem(Q3IconViewItem *item); + + int index(const Q3IconViewItem *item) const; + + Q3IconViewItem *firstItem() const; + Q3IconViewItem *lastItem() const; + Q3IconViewItem *currentItem() const; + virtual void setCurrentItem(Q3IconViewItem *item); + virtual void setSelected(Q3IconViewItem *item, bool s, bool cb = false); + + uint count() const; + +public: + virtual void showEvent(QShowEvent *); + + virtual void setSelectionMode(SelectionMode m); + SelectionMode selectionMode() const; + + Q3IconViewItem *findItem(const QPoint &pos) const; + + enum StringComparisonMode { + CaseSensitive = 0x00001, // 0 0001 + BeginsWith = 0x00002, // 0 0010 + EndsWith = 0x00004, // 0 0100 + Contains = 0x00008, // 0 1000 + ExactMatch = 0x00010 // 1 0000 + }; + typedef uint ComparisonFlags; + Q3IconViewItem *findItem(const QString &text, ComparisonFlags = BeginsWith | Qt::CaseSensitive) const; + virtual void selectAll(bool select); + virtual void clearSelection(); + virtual void invertSelection(); + + virtual void repaintItem(Q3IconViewItem *item); + void repaintSelectedItems(); + + void ensureItemVisible(Q3IconViewItem *item); + Q3IconViewItem* findFirstVisibleItem(const QRect &r) const; + Q3IconViewItem* findLastVisibleItem(const QRect &r) const; + + virtual void clear(); + + virtual void setGridX(int rx); + virtual void setGridY(int ry); + int gridX() const; + int gridY() const; + virtual void setSpacing(int sp); + int spacing() const; + virtual void setItemTextPos(ItemTextPos pos); + ItemTextPos itemTextPos() const; + virtual void setItemTextBackground(const QBrush &b); + QBrush itemTextBackground() const; + virtual void setArrangement(Arrangement am); + Arrangement arrangement() const; + virtual void setResizeMode(ResizeMode am); + ResizeMode resizeMode() const; + virtual void setMaxItemWidth(int w); + int maxItemWidth() const; + virtual void setMaxItemTextLength(int w); + int maxItemTextLength() const; + virtual void setAutoArrange(bool b); + bool autoArrange() const; + virtual void setShowToolTips(bool b); + bool showToolTips() const; + + void setSorting(bool sort, bool ascending = true); + bool sorting() const; + bool sortDirection() const; + + virtual void setItemsMovable(bool b); + bool itemsMovable() const; + virtual void setWordWrapIconText(bool b); + bool wordWrapIconText() const; + + bool eventFilter(QObject * o, QEvent *); + + QSize minimumSizeHint() const; + QSize sizeHint() const; + + virtual void sort(bool ascending = true); + + bool isRenaming() const; + + QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + +public Q_SLOTS: + virtual void arrangeItemsInGrid(const QSize &grid, bool update = true); + virtual void arrangeItemsInGrid(bool update = true); + virtual void setContentsPos(int x, int y); + virtual void updateContents(); + +Q_SIGNALS: + void selectionChanged(); + void selectionChanged(Q3IconViewItem *item); + void currentChanged(Q3IconViewItem *item); + void clicked(Q3IconViewItem *); + void clicked(Q3IconViewItem *, const QPoint &); + void pressed(Q3IconViewItem *); + void pressed(Q3IconViewItem *, const QPoint &); + + void doubleClicked(Q3IconViewItem *item); + void returnPressed(Q3IconViewItem *item); + void rightButtonClicked(Q3IconViewItem* item, const QPoint& pos); + void rightButtonPressed(Q3IconViewItem* item, const QPoint& pos); + void mouseButtonPressed(int button, Q3IconViewItem* item, const QPoint& pos); + void mouseButtonClicked(int button, Q3IconViewItem* item, const QPoint& pos); + void contextMenuRequested(Q3IconViewItem* item, const QPoint &pos); + +#ifndef QT_NO_DRAGANDDROP + void dropped(QDropEvent *e, const Q3ValueList<Q3IconDragItem> &lst); +#endif + void moved(); + void onItem(Q3IconViewItem *item); + void onViewport(); + void itemRenamed(Q3IconViewItem *item, const QString &); + void itemRenamed(Q3IconViewItem *item); + +protected Q_SLOTS: + virtual void doAutoScroll(); + virtual void adjustItems(); + virtual void slotUpdate(); + +private Q_SLOTS: + void movedContents(int dx, int dy); + +protected: + void drawContents(QPainter *p, int cx, int cy, int cw, int ch); + void contentsMousePressEvent(QMouseEvent *e); + void contentsMouseReleaseEvent(QMouseEvent *e); + void contentsMouseMoveEvent(QMouseEvent *e); + void contentsMouseDoubleClickEvent(QMouseEvent *e); + void contentsContextMenuEvent(QContextMenuEvent *e); + +#ifndef QT_NO_DRAGANDDROP + void contentsDragEnterEvent(QDragEnterEvent *e); + void contentsDragMoveEvent(QDragMoveEvent *e); + void contentsDragLeaveEvent(QDragLeaveEvent *e); + void contentsDropEvent(QDropEvent *e); +#endif + + void resizeEvent(QResizeEvent* e); + void keyPressEvent(QKeyEvent *e); + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); + void enterEvent(QEvent *e); + + virtual void drawRubber(QPainter *p); +#ifndef QT_NO_DRAGANDDROP + virtual Q3DragObject *dragObject(); + virtual void startDrag(); +#endif + virtual void insertInGrid(Q3IconViewItem *item); + virtual void drawBackground(QPainter *p, const QRect &r); + + void emitSelectionChanged(Q3IconViewItem * i = 0); + void emitRenamed(Q3IconViewItem *item); + + Q3IconViewItem *makeRowLayout(Q3IconViewItem *begin, int &y, bool &changed); + + void changeEvent(QEvent *); + +private: + Q_DISABLE_COPY(Q3IconView) + + void contentsMousePressEventEx(QMouseEvent *e); + virtual void drawDragShapes(const QPoint &pnt); +#ifndef QT_NO_DRAGANDDROP + virtual void initDragEnter(QDropEvent *e); +#endif + Q3IconViewItem* findItemByName(Q3IconViewItem *start); + void handleItemChange(Q3IconViewItem *old, bool shift, + bool control, bool homeend = false); + + int calcGridNum(int w, int x) const; + Q3IconViewItem *rowBegin(Q3IconViewItem *item) const; + void updateItemContainer(Q3IconViewItem *item); + void appendItemContainer(); + void rebuildContainers(); + enum Direction { + DirUp = 0, + DirDown, + DirLeft, + DirRight + }; + Q3IconViewItem* findItem(Direction dir, + const QPoint &relativeTo, + const QRect &searchRect) const; + bool neighbourItem(Direction dir, + const QPoint &relativeTo, + const Q3IconViewItem *item) const; + QBitmap mask(QPixmap *pix) const; + + Q3IconViewPrivate *d; +}; + +#endif // QT_NO_ICONVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QICONVIEW_H diff --git a/src/qt3support/itemviews/q3listbox.cpp b/src/qt3support/itemviews/q3listbox.cpp new file mode 100644 index 0000000..da1b6f9 --- /dev/null +++ b/src/qt3support/itemviews/q3listbox.cpp @@ -0,0 +1,4687 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglobal.h" +#if defined(Q_CC_BOR) +// needed for qsort() because of a std namespace problem on Borland +#include "qplatformdefs.h" +#endif + +#include "q3listbox.h" +#ifndef QT_NO_LISTBOX +#include "qapplication.h" +#include "qevent.h" +#include "qfontmetrics.h" +#include "qpainter.h" +#include "qpixmap.h" +#include "qstringlist.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtimer.h" +#include "qvector.h" +#include "qpointer.h" +#ifndef QT_NO_ACCESSIBILITY +#include "qaccessible.h" +#endif + +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +class Q3ListBoxPrivate +{ +public: + Q3ListBoxPrivate(Q3ListBox *lb): + head(0), last(0), cache(0), cacheIndex(-1), current(0), + highlighted(0), tmpCurrent(0), columnPos(1), rowPos(1), rowPosCache(0), + columnPosOne(0), rowMode(Q3ListBox::FixedNumber), + columnMode(Q3ListBox::FixedNumber), numRows(1), numColumns(1), + currentRow(0), currentColumn(0), + mousePressRow(-1), mousePressColumn(-1), + mouseMoveRow(-1), mouseMoveColumn(-1), mouseInternalPress(false), + scrollTimer(0), updateTimer(0), visibleTimer(0), + selectionMode(Q3ListBox::Single), + count(0), + listBox(lb), currInputString(QString()), + rowModeWins(false), + ignoreMoves(false), + layoutDirty(true), + mustPaintAll(true), + dragging(false), + dirtyDrag (false), + variableHeight(true /* !!! ### false */), + variableWidth(false), + inMenuMode(false) + {} + int findItemByName(int item, const QString &text); + ~Q3ListBoxPrivate(); + + Q3ListBoxItem * head, *last, *cache; + int cacheIndex; + Q3ListBoxItem * current, *highlighted, *tmpCurrent; + + QVector<int> columnPos; + QVector<int> rowPos; + int rowPosCache; + int columnPosOne; + + Q3ListBox::LayoutMode rowMode; + Q3ListBox::LayoutMode columnMode; + int numRows; + int numColumns; + + int currentRow; + int currentColumn; + int mousePressRow; + int mousePressColumn; + int mouseMoveRow; + int mouseMoveColumn; + bool mouseInternalPress; + + QTimer * scrollTimer; + QTimer * updateTimer; + QTimer * visibleTimer; + QTimer * resizeTimer; + + QPoint scrollPos; + + Q3ListBox::SelectionMode selectionMode; + + int count; + + + Q3ListBox *listBox; + QString currInputString; + QTimer *inputTimer; + + Q3ListBoxItem *pressedItem, *selectAnchor; + + uint select :1; + uint pressedSelected :1; + uint rowModeWins :1; + uint ignoreMoves :1; + uint clearing :1; + uint layoutDirty :1; + uint mustPaintAll :1; + uint dragging :1; + uint dirtyDrag :1; + uint variableHeight :1; + uint variableWidth :1; + uint inMenuMode :1; + + QRect *rubber; + + struct SortableItem { + Q3ListBoxItem *item; + }; +}; + + +Q3ListBoxPrivate::~Q3ListBoxPrivate() +{ + Q_ASSERT(!head); +} + + +/*! + \class Q3ListBoxItem + \brief The Q3ListBoxItem class is the base class of all list box items. + + \compat + + This class is an abstract base class used for all list box items. + If you need to insert customized items into a Q3ListBox you must + inherit this class and reimplement paint(), height() and width(). + + \sa Q3ListBox +*/ + +/*! + Constructs an empty list box item in the list box \a listbox. +*/ + +Q3ListBoxItem::Q3ListBoxItem(Q3ListBox* listbox) +{ + lbox = listbox; + s = false; + dirty = true; + custom_highlight = false; + selectable = true; + p = n = 0; + + if (listbox) + listbox->insertItem(this); +} + +/*! + Constructs an empty list box item in the list box \a listbox and + inserts it after the item \a after or at the beginning if \a after + is 0. +*/ + +Q3ListBoxItem::Q3ListBoxItem(Q3ListBox* listbox, Q3ListBoxItem *after) +{ + lbox = listbox; + s = false; + dirty = true; + custom_highlight = false; + selectable = true; + p = n = 0; + + if (listbox) + listbox->insertItem(this, after); +} + + +/*! + Destroys the list box item. +*/ + +Q3ListBoxItem::~Q3ListBoxItem() +{ + if (lbox) + lbox->takeItem(this); +} + + +/*! + Defines whether the list box item is responsible for drawing + itself in a highlighted state when being selected. + + If \a b is false (the default), the list box will draw some + default highlight indicator before calling paint(). + + \sa isSelected(), paint() +*/ +void Q3ListBoxItem::setCustomHighlighting(bool b) +{ + custom_highlight = b; +} + +/*! + \fn void Q3ListBoxItem::paint(QPainter *p) + + Implement this function to draw your item. The painter, \a p, is + already open for painting. + + \sa height(), width() +*/ + +/*! + \fn int Q3ListBoxItem::width(const Q3ListBox* lb) const + + Reimplement this function to return the width of your item. The \a + lb parameter is the same as listBox() and is provided for + convenience and compatibility. + + The default implementation returns + \l{QApplication::globalStrut()}'s width. + + \sa paint(), height() +*/ +int Q3ListBoxItem::width(const Q3ListBox*) const +{ + return QApplication::globalStrut().width(); +} + +/*! + \fn int Q3ListBoxItem::height(const Q3ListBox* lb) const + + Implement this function to return the height of your item. The \a + lb parameter is the same as listBox() and is provided for + convenience and compatibility. + + The default implementation returns + \l{QApplication::globalStrut()}'s height. + + \sa paint(), width() +*/ +int Q3ListBoxItem::height(const Q3ListBox*) const +{ + return QApplication::globalStrut().height(); +} + + +/*! + Returns the text of the item. This text is also used for sorting. + + \sa setText() +*/ +QString Q3ListBoxItem::text() const +{ + return txt; +} + +/*! + Returns the pixmap associated with the item, or 0 if there isn't + one. + + The default implementation returns 0. +*/ +const QPixmap *Q3ListBoxItem::pixmap() const +{ + return 0; +} + +/*! \fn void Q3ListBoxItem::setSelectable(bool b) + + If \a b is true (the default) then this item can be selected by + the user; otherwise this item cannot be selected by the user. + + \sa isSelectable() +*/ + +/*! \fn bool Q3ListBoxItem::isSelectable() const + + Returns true if this item is selectable (the default); otherwise + returns false. + + \sa setSelectable() +*/ + + +/*! + \fn void Q3ListBoxItem::setText(const QString &text) + + Sets the text of the Q3ListBoxItem to \a text. This \a text is also + used for sorting. The text is not shown unless explicitly drawn in + paint(). + + \sa text() +*/ + + +/*! + \class Q3ListBoxText + \brief The Q3ListBoxText class provides list box items that display text. + + \compat + + The text is drawn in the widget's current font. If you need + several different fonts, you must implement your own subclass of + Q3ListBoxItem. + + \sa Q3ListBox, Q3ListBoxItem +*/ + + +/*! + Constructs a list box item in list box \a listbox showing the text + \a text. +*/ +Q3ListBoxText::Q3ListBoxText(Q3ListBox *listbox, const QString &text) + :Q3ListBoxItem(listbox) +{ + setText(text); +} + +/*! + Constructs a list box item showing the text \a text. +*/ + +Q3ListBoxText::Q3ListBoxText(const QString &text) + :Q3ListBoxItem() +{ + setText(text); +} + +/*! + Constructs a list box item in list box \a listbox showing the text + \a text. The item is inserted after the item \a after, or at the + beginning if \a after is 0. +*/ + +Q3ListBoxText::Q3ListBoxText(Q3ListBox* listbox, const QString &text, Q3ListBoxItem *after) + : Q3ListBoxItem(listbox, after) +{ + setText(text); +} + +/*! + Destroys the item. +*/ + +Q3ListBoxText::~Q3ListBoxText() +{ +} + +/*! + Draws the text using \a painter. +*/ + +void Q3ListBoxText::paint(QPainter *painter) +{ + int itemHeight = height(listBox()); + QFontMetrics fm = painter->fontMetrics(); + int yPos = ((itemHeight - fm.height()) / 2) + fm.ascent(); + painter->drawText(3, yPos, text()); +} + +/*! + Returns the height of a line of text in list box \a lb. + + \sa paint(), width() +*/ + +int Q3ListBoxText::height(const Q3ListBox* lb) const +{ + int h = lb ? lb->fontMetrics().lineSpacing() + 2 : 0; + return qMax(h, QApplication::globalStrut().height()); +} + +/*! + Returns the width of this line in list box \a lb. + + \sa paint(), height() +*/ + +int Q3ListBoxText::width(const Q3ListBox* lb) const +{ + int w = lb ? lb->fontMetrics().width(text()) + 6 : 0; + return qMax(w, QApplication::globalStrut().width()); +} + +/*! + \fn int Q3ListBoxText::rtti() const + + \reimp + + Returns 1. + + Make your derived classes return their own values for rtti(), and + you can distinguish between listbox items. You should use values + greater than 1000 preferably a large random number, to allow for + extensions to this class. +*/ + +int Q3ListBoxText::rtti() const +{ + return RTTI; +} + +/*! + \class Q3ListBoxPixmap + \brief The Q3ListBoxPixmap class provides list box items with a + pixmap and optional text. + + \compat + + Items of this class are drawn with the pixmap on the left with the + optional text to the right of the pixmap. + + \sa Q3ListBox, Q3ListBoxItem +*/ + + +/*! + Constructs a new list box item in list box \a listbox showing the + pixmap \a pixmap. +*/ + +Q3ListBoxPixmap::Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap &pixmap) + : Q3ListBoxItem(listbox) +{ + pm = pixmap; +} + +/*! + Constructs a new list box item showing the pixmap \a pixmap. +*/ + +Q3ListBoxPixmap::Q3ListBoxPixmap(const QPixmap &pixmap) + : Q3ListBoxItem() +{ + pm = pixmap; +} + +/*! + Constructs a new list box item in list box \a listbox showing the + pixmap \a pixmap. The item gets inserted after the item \a after, + or at the beginning if \a after is 0. +*/ + +Q3ListBoxPixmap::Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap &pixmap, Q3ListBoxItem *after) + : Q3ListBoxItem(listbox, after) +{ + pm = pixmap; +} + + +/*! + Destroys the item. +*/ + +Q3ListBoxPixmap::~Q3ListBoxPixmap() +{ +} + + +/*! + Constructs a new list box item in list box \a listbox showing the + pixmap \a pix and the text \a text. +*/ +Q3ListBoxPixmap::Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap &pix, const QString& text) + : Q3ListBoxItem(listbox) +{ + pm = pix; + setText(text); +} + +/*! + Constructs a new list box item showing the pixmap \a pix and the + text to \a text. +*/ +Q3ListBoxPixmap::Q3ListBoxPixmap(const QPixmap & pix, const QString& text) + : Q3ListBoxItem() +{ + pm = pix; + setText(text); +} + +/*! + Constructs a new list box item in list box \a listbox showing the + pixmap \a pix and the string \a text. The item gets inserted after + the item \a after, or at the beginning if \a after is 0. +*/ +Q3ListBoxPixmap::Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap & pix, const QString& text, + Q3ListBoxItem *after) + : Q3ListBoxItem(listbox, after) +{ + pm = pix; + setText(text); +} + +/*! + \fn const QPixmap *Q3ListBoxPixmap::pixmap() const + + Returns the pixmap associated with the item. +*/ + + +/*! + Draws the pixmap using \a painter. +*/ + +void Q3ListBoxPixmap::paint(QPainter *painter) +{ + int itemHeight = height(listBox()); + int yPos; + + const QPixmap *pm = pixmap(); + if (pm && ! pm->isNull()) { + yPos = (itemHeight - pm->height()) / 2; + painter->drawPixmap(3, yPos, *pm); + } + + if (!text().isEmpty()) { + QFontMetrics fm = painter->fontMetrics(); + yPos = ((itemHeight - fm.height()) / 2) + fm.ascent(); + painter->drawText(pm->width() + 5, yPos, text()); + } +} + +/*! + Returns the height of the pixmap in list box \a lb. + + \sa paint(), width() +*/ + +int Q3ListBoxPixmap::height(const Q3ListBox* lb) const +{ + int h; + if (text().isEmpty()) + h = pm.height(); + else + h = qMax(pm.height(), lb->fontMetrics().lineSpacing() + 2); + return qMax(h, QApplication::globalStrut().height()); +} + +/*! + Returns the width of the pixmap plus some margin in list box \a lb. + + \sa paint(), height() +*/ + +int Q3ListBoxPixmap::width(const Q3ListBox* lb) const +{ + if (text().isEmpty()) + return qMax(pm.width() + 6, QApplication::globalStrut().width()); + return qMax(pm.width() + lb->fontMetrics().width(text()) + 6, + QApplication::globalStrut().width()); +} + +/*! + \fn int Q3ListBoxPixmap::rtti() const + + \reimp + + Returns 2. + + Make your derived classes return their own values for rtti(), and + you can distinguish between listbox items. You should use values + greater than 1000 preferably a large random number, to allow for + extensions to this class. +*/ + +int Q3ListBoxPixmap::rtti() const +{ + return RTTI; +} + +/*! + \class Q3ListBox + \brief The Q3ListBox widget provides a list of selectable, read-only items. + + \compat + + This is typically a single-column list in which either no item or + one item is selected, but it can also be used in many other ways. + + Q3ListBox will add scroll bars as necessary, but it isn't intended + for \e really big lists. If you want more than a few thousand + items, it's probably better to use a different widget mainly + because the scroll bars won't provide very good navigation, but + also because Q3ListBox may become slow with huge lists. (See + Q3ListView and Q3Table for possible alternatives.) + + There are a variety of selection modes described in the + Q3ListBox::SelectionMode documentation. The default is \l Single + selection mode, but you can change it using setSelectionMode(). + (setMultiSelection() is still provided for compatibility with Qt + 1.x. We recommend using setSelectionMode() in all code.) + + Because Q3ListBox offers multiple selection it must display + keyboard focus and selection state separately. Therefore there are + functions both to set the selection state of an item, i.e. + setSelected(), and to set which item displays keyboard focus, i.e. + setCurrentItem(). + + The list box normally arranges its items in a single column and + adds a vertical scroll bar if required. It is possible to have a + different fixed number of columns (setColumnMode()), or as many + columns as will fit in the list box's assigned screen space + (setColumnMode(FitToWidth)), or to have a fixed number of rows + (setRowMode()) or as many rows as will fit in the list box's + assigned screen space (setRowMode(FitToHeight)). In all these + cases Q3ListBox will add scroll bars, as appropriate, in at least + one direction. + + If multiple rows are used, each row can be as high as necessary + (the normal setting), or you can request that all items will have + the same height by calling setVariableHeight(false). The same + applies to a column's width, see setVariableWidth(). + + The Q3ListBox's items are Q3ListBoxItem objects. Q3ListBox provides + methods to insert new items as strings, as pixmaps, and as + Q3ListBoxItem * (insertItem() with various arguments), and to + replace an existing item with a new string, pixmap or Q3ListBoxItem + (changeItem() with various arguments). You can also remove items + singly with removeItem() or clear() the entire list box. Note that + if you create a Q3ListBoxItem yourself and insert it, Q3ListBox + takes ownership of the item. + + You can also create a Q3ListBoxItem, such as Q3ListBoxText or + Q3ListBoxPixmap, with the list box as first parameter. The item + will then append itself. When you delete an item it is + automatically removed from the list box. + + The list of items can be arbitrarily large; Q3ListBox will add + scroll bars if necessary. Q3ListBox can display a single-column + (the common case) or multiple-columns, and offers both single and + multiple selection. Q3ListBox does not support multiple-column + items (but Q3ListView and Q3Table do), or tree hierarchies (but + Q3ListView does). + + The list box items can be accessed both as Q3ListBoxItem objects + (recommended) and using integer indexes (the original Q3ListBox + implementation used an array of strings internally, and the API + still supports this mode of operation). Everything can be done + using the new objects, and most things can be done using indexes. + + Each item in a Q3ListBox contains a Q3ListBoxItem. One of the items + can be the current item. The currentChanged() signal and the + highlighted() signal are emitted when a new item becomes current, + e.g. because the user clicks on it or Q3ListBox::setCurrentItem() + is called. The selected() signal is emitted when the user + double-clicks on an item or presses Enter on the current item. + + If the user does not select anything, no signals are emitted and + currentItem() returns -1. + + A list box has Qt::WheelFocus as a default focusPolicy(), i.e. it + can get keyboard focus by tabbing, clicking and through the use of + the mouse wheel. + + New items can be inserted using insertItem(), insertStrList() or + insertStringList(). + + By default, vertical and horizontal scroll bars are added and + removed as necessary. setHScrollBarMode() and setVScrollBarMode() + can be used to change this policy. + + If you need to insert types other than strings and pixmaps, you + must define new classes which inherit Q3ListBoxItem. + + \warning The list box assumes ownership of all list box items and + will delete them when it does not need them any more. + + \inlineimage qlistbox-m.png Screenshot in Motif style + \inlineimage qlistbox-w.png Screenshot in Windows style + + \sa Q3ListView, QComboBox, QButtonGroup +*/ + +/*! + \enum Q3ListBox::SelectionMode + + This enumerated type is used by Q3ListBox to indicate how it reacts + to selection by the user. + + \value Single When the user selects an item, any already-selected + item becomes unselected and the user cannot unselect the selected + item. This means that the user can never clear the selection, even + though the selection may be cleared by the application programmer + using Q3ListBox::clearSelection(). + + \value Multi When the user selects an item the selection status + of that item is toggled and the other items are left alone. + + \value Extended When the user selects an item the selection is + cleared and the new item selected. However, if the user presses + the Ctrl key when clicking on an item, the clicked item gets + toggled and all other items are left untouched. And if the user + presses the Shift key while clicking on an item, all items between + the current item and the clicked item get selected or unselected, + depending on the state of the clicked item. Also, multiple items + can be selected by dragging the mouse while the left mouse button + is kept pressed. + + \value NoSelection Items cannot be selected. + + In other words, \c Single is a real single-selection list box, \c + Multi is a real multi-selection list box, \c Extended is a list + box in which users can select multiple items but usually want to + select either just one or a range of contiguous items, and \c + NoSelection is for a list box where the user can look but not + touch. +*/ + + +/*! + \enum Q3ListBox::LayoutMode + + This enum type is used to specify how Q3ListBox lays out its rows + and columns. + + \value FixedNumber There is a fixed number of rows (or columns). + + \value FitToWidth There are as many columns as will fit + on-screen. + + \value FitToHeight There are as many rows as will fit on-screen. + + \value Variable There are as many rows as are required by the + column mode. (Or as many columns as required by the row mode.) + + Example: When you call setRowMode(FitToHeight), columnMode() + automatically becomes \c Variable to accommodate the row mode + you've set. +*/ + +/*! + \fn void Q3ListBox::onItem(Q3ListBoxItem *i) + + This signal is emitted when the user moves the mouse cursor onto + an item, similar to the QWidget::enterEvent() function. \a i is + the Q3ListBoxItem that the mouse has moved on. +*/ + +// ### bug here too? enter/leave event may noit considered. move the +// mouse out of the window and back in, to the same item - does it +// work? + +/*! + \fn void Q3ListBox::onViewport() + + This signal is emitted when the user moves the mouse cursor from + an item to an empty part of the list box. +*/ + + +/*! + Constructs a new empty list box called \a name and with parent \a + parent and widget attributes \a f. + + This constructor sets the Qt::WA_StaticContent and the + Qt::WA_NoBackground attributes to boost performance when drawing + Q3ListBoxItems. This may be unsuitable for custom Q3ListBoxItem + classes, in which case Qt::WA_StaticContents and Qt::WA_NoBackground + should be cleared on the viewport() after construction. +*/ + +Q3ListBox::Q3ListBox(QWidget *parent, const char *name, Qt::WindowFlags f) + : Q3ScrollView(parent, name, f | Qt::WStaticContents | Qt::WNoAutoErase) +{ + d = new Q3ListBoxPrivate(this); + d->updateTimer = new QTimer(this, "listbox update timer"); + d->visibleTimer = new QTimer(this, "listbox visible timer"); + d->inputTimer = new QTimer(this, "listbox input timer"); + d->resizeTimer = new QTimer(this, "listbox resize timer"); + d->clearing = false; + d->pressedItem = 0; + d->selectAnchor = 0; + d->select = false; + d->rubber = 0; + + setMouseTracking(true); + viewport()->setMouseTracking(true); + + connect(d->updateTimer, SIGNAL(timeout()), + this, SLOT(refreshSlot())); + connect(d->visibleTimer, SIGNAL(timeout()), + this, SLOT(ensureCurrentVisible())); + connect(d->resizeTimer, SIGNAL(timeout()), + this, SLOT(adjustItems())); + viewport()->setBackgroundRole(QPalette::Base); + viewport()->setFocusProxy(this); + viewport()->setFocusPolicy(Qt::WheelFocus); + setFocusPolicy(Qt::WheelFocus); + setAttribute(Qt::WA_MacShowFocusRect); +} + + +Q3ListBox * Q3ListBox::changedListBox = 0; + +/*! + Destroys the list box. Deletes all list box items. +*/ + +Q3ListBox::~Q3ListBox() +{ + if (changedListBox == this) + changedListBox = 0; + clear(); + delete d; + d = 0; +} + +/*! + \fn void Q3ListBox::pressed(Q3ListBoxItem *item) + + This signal is emitted when the user presses any mouse button. If + \a item is not 0, the cursor is on \a item. If \a item is 0, the + mouse cursor isn't on any item. + + Note that you must not delete any Q3ListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListBox::pressed(Q3ListBoxItem *item, const QPoint &pnt) + \overload + + This signal is emitted when the user presses any mouse button. If + \a item is not 0, the cursor is on \a item. If \a item is 0, the + mouse cursor isn't on any item. + + \a pnt is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). + + Note that you must not delete any Q3ListBoxItem objects in slots + connected to this signal. + + \sa mouseButtonPressed() rightButtonPressed() clicked() +*/ + +/*! + \fn void Q3ListBox::clicked(Q3ListBoxItem *item) + + This signal is emitted when the user clicks any mouse button. If + \a item is not 0, the cursor is on \a item. If \a item is 0, the + mouse cursor isn't on any item. + + Note that you must not delete any Q3ListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListBox::clicked(Q3ListBoxItem *item, const QPoint &pnt) + \overload + + This signal is emitted when the user clicks any mouse button. If + \a item is not 0, the cursor is on \a item. If \a item is 0, the + mouse cursor isn't on any item. + + \a pnt is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). (If the click's + press and release differs by a pixel or two, \a pnt is the + position at release time.) + + Note that you must not delete any Q3ListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListBox::mouseButtonClicked (int button, Q3ListBoxItem * item, const QPoint & pos) + + This signal is emitted when the user clicks mouse button \a + button. If \a item is not 0, the cursor is on \a item. If \a item + is 0, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). (If the click's + press and release differs by a pixel or two, \a pos is the + position at release time.) + + Note that you must not delete any Q3ListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListBox::mouseButtonPressed (int button, Q3ListBoxItem * item, const QPoint & pos) + + This signal is emitted when the user presses mouse button \a + button. If \a item is not 0, the cursor is on \a item. If \a item + is 0, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). + + Note that you must not delete any Q3ListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListBox::doubleClicked(Q3ListBoxItem *item) + + This signal is emitted whenever an item is double-clicked. It's + emitted on the second button press, not the second button release. + If \a item is not 0, the cursor is on \a item. If \a item is 0, + the mouse cursor isn't on any item. +*/ + + +/*! + \fn void Q3ListBox::returnPressed(Q3ListBoxItem *item) + + This signal is emitted when Enter or Return is pressed. The + \a item passed in the argument is currentItem(). +*/ + +/*! + \fn void Q3ListBox::rightButtonClicked(Q3ListBoxItem *item, const QPoint& point) + + This signal is emitted when the right button is clicked. The \a + item is the item that the button was clicked on (which could be + 0 if no item was clicked on), and the \a point is where the + click took place in global coordinates. +*/ + + +/*! + \fn void Q3ListBox::rightButtonPressed (Q3ListBoxItem *item, const QPoint &point) + + This signal is emitted when the right button is pressed. The \a + item is the item that the button was pressed over (which could be + 0 if no item was pressed over), and the \a point is where the + press took place in global coordinates. +*/ + +/*! + \fn void Q3ListBox::contextMenuRequested(Q3ListBoxItem *item, const QPoint & pos) + + This signal is emitted when the user invokes a context menu with + the right mouse button or with special system keys, with \a item + being the item under the mouse cursor or the current item, + respectively. + + \a pos is the position for the context menu in the global + coordinate system. +*/ + +/*! + \fn void Q3ListBox::selectionChanged() + + This signal is emitted when the selection set of a list box + changes. This signal is emitted in each selection mode. If the + user selects five items by drag-selecting, Q3ListBox tries to emit + just one selectionChanged() signal so the signal can be connected + to computationally expensive slots. + + \sa selected() currentItem() +*/ + +/*! + \fn void Q3ListBox::selectionChanged(Q3ListBoxItem *item) + \overload + + This signal is emitted when the selection in a \l Single selection + list box changes. \a item is the newly selected list box item. + + \sa selected() currentItem() +*/ + +/*! + \fn void Q3ListBox::currentChanged(Q3ListBoxItem *item) + + This signal is emitted when the user makes a new item the current + item. \a item is the new current list box item. + + \sa setCurrentItem() currentItem() +*/ + +/*! + \fn void Q3ListBox::highlighted(int index) + + This signal is emitted when the user makes a new item the current + item. \a index is the index of the new current item. + + \sa currentChanged() selected() currentItem() selectionChanged() +*/ + +/*! + \fn void Q3ListBox::highlighted(Q3ListBoxItem *item) + + \overload + + This signal is emitted when the user makes a new \a item the current + \a item. + + \sa currentChanged() selected() currentItem() selectionChanged() +*/ + +/*! + \fn void Q3ListBox::highlighted(const QString & text) + + \overload + + This signal is emitted when the user makes a new item the current + item and the item is (or has) as string. The argument is the new + current item's \a text. + + \sa currentChanged() selected() currentItem() selectionChanged() +*/ + +/*! + \fn void Q3ListBox::selected(int index) + + This signal is emitted when the user double-clicks on an item or + presses Enter on the current item. \a index is the index of the + selected item. + + \sa currentChanged() highlighted() selectionChanged() +*/ + +/*! + \fn void Q3ListBox::selected(Q3ListBoxItem *item) + + \overload + + This signal is emitted when the user double-clicks on an \a item or + presses Enter on the current \a item. + + \sa currentChanged() highlighted() selectionChanged() +*/ + +/*! + \fn void Q3ListBox::selected(const QString &text) + + \overload + + This signal is emitted when the user double-clicks on an item or + presses Enter on the current item, and the item is (or has) a + string. The argument is the \a text of the selected item. + + \sa currentChanged() highlighted() selectionChanged() +*/ + +/*! + \property Q3ListBox::count + \brief the number of items in the list box +*/ + +uint Q3ListBox::count() const +{ + return d->count; +} + +#if 0 +/*! + Inserts the string list \a list into the list at position \a + index. + + If \a index is negative, \a list is inserted at the end of the + list. If \a index is too large, the operation is ignored. + + \warning This function uses \c{const char *} rather than QString, + so we recommend against using it. It is provided so that legacy + code will continue to work, and so that programs that certainly + will not need to handle code outside a single 8-bit locale can use + it. See insertStringList() which uses real QStrings. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStringList() +*/ + +void Q3ListBox::insertStrList(const QStrList *list, int index) +{ + if (!list) { + Q_ASSERT(list != 0); + return; + } + insertStrList(*list, index); +} +#endif + + +/*! + Inserts the string list \a list into the list at position \a + index. + + If \a index is negative, \a list is inserted at the end of the + list. If \a index is too large, the operation is ignored. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStrList() +*/ + +void Q3ListBox::insertStringList(const QStringList & list, int index) +{ + if (index < 0) + index = count(); + for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + insertItem(new Q3ListBoxText(*it), index++); +} + + +#if 0 +/*! + \overload + + Inserts the string list \a list into the list at position \a + index. + + If \a index is negative, \a list is inserted at the end of the + list. If \a index is too large, the operation is ignored. + + \warning This function uses \c{const char *} rather than QString, + so we recommend against using it. It is provided so that legacy + code will continue to work, and so that programs that certainly + will not need to handle code outside a single 8-bit locale can use + it. See insertStringList() which uses real QStrings. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStringList() +*/ +void Q3ListBox::insertStrList(const QStrList & list, int index) +{ + QStrListIterator it(list); + const char* txt; + if (index < 0) + index = count(); + while ((txt=it.current())) { + ++it; + insertItem(new Q3ListBoxText(QString::fromLatin1(txt)), + index++); + } + if (hasFocus() && !d->current) + setCurrentItem(d->head); +} +#endif + + +/*! + Inserts the \a numStrings strings of the array \a strings into the + list at position \a index. + + If \a index is negative, insertStrList() inserts \a strings at the + end of the list. If \a index is too large, the operation is + ignored. + + \warning This function uses \c{const char *} rather than QString, + so we recommend against using it. It is provided so that legacy + code will continue to work, and so that programs that certainly + will not need to handle code outside a single 8-bit locale can use + it. See insertStringList() which uses real QStrings. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStringList() +*/ + +void Q3ListBox::insertStrList(const char **strings, int numStrings, int index) +{ + if (!strings) { + Q_ASSERT(strings != 0); + return; + } + if (index < 0) + index = count(); + int i = 0; + while ((numStrings<0 && strings[i]!=0) || i<numStrings) { + insertItem(new Q3ListBoxText(QString::fromLatin1(strings[i])), + index + i); + i++; + } + if (hasFocus() && !d->current) + setCurrentItem(d->head); +} + +/*! + Inserts the item \a lbi into the list at position \a index. + + If \a index is negative or larger than the number of items in the + list box, \a lbi is inserted at the end of the list. + + \sa insertStrList() +*/ + +void Q3ListBox::insertItem(const Q3ListBoxItem *lbi, int index) +{ + if (!lbi) + return; + + if (index < 0) + index = d->count; + + if (index >= d->count) { + insertItem(lbi, d->last); + return; + } + + Q3ListBoxItem * item = (Q3ListBoxItem *)lbi; + d->count++; + d->cache = 0; + + item->lbox = this; + if (!d->head || index == 0) { + item->n = d->head; + item->p = 0; + d->head = item; + item->dirty = true; + if (item->n) + item->n->p = item; + } else { + Q3ListBoxItem * i = d->head; + while (i->n && index > 1) { + i = i->n; + index--; + } + if (i->n) { + item->n = i->n; + item->p = i; + item->n->p = item; + item->p->n = item; + } else { + i->n = item; + item->p = i; + item->n = 0; + } + } + + if (hasFocus() && !d->current) { + d->current = d->head; + updateItem(d->current); + emit highlighted(d->current); + emit highlighted(d->current->text()); + emit highlighted(index); + } + + triggerUpdate(true); +} + +/*! + \overload + + Inserts the item \a lbi into the list after the item \a after, or + at the beginning if \a after is 0. + + \sa insertStrList() +*/ + +void Q3ListBox::insertItem(const Q3ListBoxItem *lbi, const Q3ListBoxItem *after) +{ + if (!lbi) + return; + + Q3ListBoxItem * item = (Q3ListBoxItem*)lbi; + d->count++; + d->cache = 0; + + item->lbox = this; + if (!d->head || !after) { + item->n = d->head; + item->p = 0; + d->head = item; + item->dirty = true; + if (item->n) + item->n->p = item; + } else { + Q3ListBoxItem * i = (Q3ListBoxItem*) after; + if (i) { + item->n = i->n; + item->p = i; + if (item->n) + item->n->p = item; + if (item->p) + item->p->n = item; + } + } + + if (after == d->last) + d->last = (Q3ListBoxItem*) lbi; + + if (hasFocus() && !d->current) { + d->current = d->head; + updateItem(d->current); + emit highlighted(d->current); + emit highlighted(d->current->text()); + emit highlighted(index(d->current)); + } + + triggerUpdate(true); +} + +/*! + \overload + + Inserts a new list box text item with the text \a text into the + list at position \a index. + + If \a index is negative, \a text is inserted at the end of the + list. + + \sa insertStrList() +*/ + +void Q3ListBox::insertItem(const QString &text, int index) +{ + insertItem(new Q3ListBoxText(text), index); +} + +/*! + \overload + + Inserts a new list box pixmap item with the pixmap \a pixmap into + the list at position \a index. + + If \a index is negative, \a pixmap is inserted at the end of the + list. + + \sa insertStrList() +*/ + +void Q3ListBox::insertItem(const QPixmap &pixmap, int index) +{ + insertItem(new Q3ListBoxPixmap(pixmap), index); +} + +/*! + \overload + + Inserts a new list box pixmap item with the pixmap \a pixmap and + the text \a text into the list at position \a index. + + If \a index is negative, \a pixmap is inserted at the end of the + list. + + \sa insertStrList() +*/ + +void Q3ListBox::insertItem(const QPixmap &pixmap, const QString &text, int index) +{ + insertItem(new Q3ListBoxPixmap(pixmap, text), index); +} + +/*! + Removes and deletes the item at position \a index. If \a index is + equal to currentItem(), a new item becomes current and the + currentChanged() and highlighted() signals are emitted. + + \sa insertItem(), clear() +*/ + +void Q3ListBox::removeItem(int index) +{ + bool wasVisible = itemVisible(currentItem()); + delete item(index); + triggerUpdate(true); + if (wasVisible) + ensureCurrentVisible(); +} + + +/*! + Deletes all the items in the list. + + \sa removeItem() +*/ + +void Q3ListBox::clear() +{ + setContentsPos(0, 0); + bool blocked = signalsBlocked(); + blockSignals(true); + d->clearing = true; + d->current = 0; + d->tmpCurrent = 0; + Q3ListBoxItem * i = d->head; + d->head = 0; + while (i) { + Q3ListBoxItem * n = i->n; + i->n = i->p = 0; + delete i; + i = n; + } + d->count = 0; + d->numRows = 1; + d->numColumns = 1; + d->currentRow = 0; + d->currentColumn = 0; + d->mousePressRow = -1; + d->mousePressColumn = -1; + d->mouseMoveRow = -1; + d->mouseMoveColumn = -1; + clearSelection(); + d->selectAnchor = 0; + blockSignals(blocked); + triggerUpdate(true); + d->last = 0; + d->clearing = false; +} + + +/*! + Returns the text at position \a index, or an empty string if there + is no text at that position. + + \sa pixmap() +*/ + +QString Q3ListBox::text(int index) const +{ + Q3ListBoxItem * i = item(index); + if (i) + return i->text(); + return QString(); +} + + +/*! + Returns a pointer to the pixmap at position \a index, or 0 if + there is no pixmap there. + + \sa text() +*/ + +const QPixmap *Q3ListBox::pixmap(int index) const +{ + Q3ListBoxItem * i = item(index); + if (i) + return i->pixmap(); + return 0; +} + +/*! + \overload + + Replaces the item at position \a index with a new list box text + item with text \a text. + + The operation is ignored if \a index is out of range. + + \sa insertItem(), removeItem() +*/ + +void Q3ListBox::changeItem(const QString &text, int index) +{ + if(index >= 0 && index < (int)count()) + changeItem(new Q3ListBoxText(text), index); +} + +/*! + \overload + + Replaces the item at position \a index with a new list box pixmap + item with pixmap \a pixmap. + + The operation is ignored if \a index is out of range. + + \sa insertItem(), removeItem() +*/ + +void Q3ListBox::changeItem(const QPixmap &pixmap, int index) +{ + if(index >= 0 && index < (int)count()) + changeItem(new Q3ListBoxPixmap(pixmap), index); +} + +/*! + \overload + + Replaces the item at position \a index with a new list box pixmap + item with pixmap \a pixmap and text \a text. + + The operation is ignored if \a index is out of range. + + \sa insertItem(), removeItem() +*/ + +void Q3ListBox::changeItem(const QPixmap &pixmap, const QString &text, int index) +{ + if(index >= 0 && index < (int)count()) + changeItem(new Q3ListBoxPixmap(pixmap, text), index); +} + + + +/*! + Replaces the item at position \a index with \a lbi. If \a index is + negative or too large, changeItem() does nothing. + + The item that has been changed will become selected. + + \sa insertItem(), removeItem() +*/ + +void Q3ListBox::changeItem(const Q3ListBoxItem *lbi, int index) +{ + if (!lbi || index < 0 || index >= (int)count()) + return; + + removeItem(index); + insertItem(lbi, index); + setCurrentItem(index); +} + + +/*! + \property Q3ListBox::numItemsVisible + \brief the number of visible items. + + Both partially and entirely visible items are counted. +*/ + +int Q3ListBox::numItemsVisible() const +{ + doLayout(); + + int columns = 0; + + int x = contentsX(); + int i=0; + while (i < (int)d->columnPos.size()-1 && + d->columnPos[i] < x) + i++; + if (i < (int)d->columnPos.size()-1 && + d->columnPos[i] > x) + columns++; + x += visibleWidth(); + while (i < (int)d->columnPos.size()-1 && + d->columnPos[i] < x) { + i++; + columns++; + } + + int y = contentsY(); + int rows = 0; + while (i < (int)d->rowPos.size()-1 && + d->rowPos[i] < y) + i++; + if (i < (int)d->rowPos.size()-1 && + d->rowPos[i] > y) + rows++; + y += visibleHeight(); + while (i < (int)d->rowPos.size()-1 && + d->rowPos[i] < y) { + i++; + rows++; + } + + return rows*columns; +} + +int Q3ListBox::currentItem() const +{ + if (!d->current || !d->head) + return -1; + + return index(d->current); +} + + +/*! + \property Q3ListBox::currentText + \brief the text of the current item. + + This is equivalent to text(currentItem()). +*/ + + +/*! + \property Q3ListBox::currentItem + \brief the current highlighted item + + When setting this property, the highlighting is moved to the item + and the list box scrolled as necessary. + + If no item is current, currentItem() returns -1. +*/ + +void Q3ListBox::setCurrentItem(int index) +{ + setCurrentItem(item(index)); +} + +/*! + \reimp +*/ +QVariant Q3ListBox::inputMethodQuery(Qt::InputMethodQuery query) const +{ + if (query == Qt::ImMicroFocus) + return d->current ? itemRect(d->current) : QRect(); + return QWidget::inputMethodQuery(query); +} + +/*! + \overload + + Sets the current item to the Q3ListBoxItem \a i. +*/ +void Q3ListBox::setCurrentItem(Q3ListBoxItem * i) +{ + if (!i || d->current == i) + return; + + Q3ListBoxItem * o = d->current; + d->current = i; + int ind = index(i); + + if (i && selectionMode() == Single) { + bool changed = false; + if (o && o->s) { + changed = true; + o->s = false; + } + if (i && !i->s && d->selectionMode != NoSelection && i->isSelectable()) { + i->s = true; + changed = true; + emit selectionChanged(i); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::StateChanged); +#endif + } + if (changed) { + emit selectionChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection); +#endif + } + } + + d->currentColumn = ind / numRows(); + d->currentRow = ind % numRows(); + if (o) + updateItem(o); + if (i) + updateItem(i); + // scroll after the items are redrawn + d->visibleTimer->start(1, true); + + QString tmp; + if (i) + tmp = i->text(); + emit highlighted(i); + if (!tmp.isNull()) + emit highlighted(tmp); + emit highlighted(ind); + emit currentChanged(i); + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::Focus); +#endif +} + + +/*! + Returns a pointer to the item at position \a index, or 0 if \a + index is out of bounds. + + \sa index() +*/ + +Q3ListBoxItem *Q3ListBox::item(int index) const +{ + if (index < 0 || index > d->count -1) + return 0; + + Q3ListBoxItem * i = d->head; + + if (d->cache && index > 0) { + i = d->cache; + int idx = d->cacheIndex; + while (i && idx < index) { + idx++; + i = i->n; + } + while (i && idx > index) { + idx--; + i = i->p; + } + } else { + int idx = index; + while (i && idx > 0) { + idx--; + i = i->n; + } + } + + if (index > 0) { + d->cache = i; + d->cacheIndex = index; + } + + return i; +} + + +/*! + Returns the index of \a lbi, or -1 if the item is not in this list + box or \a lbi is 0. + + \sa item() +*/ + +int Q3ListBox::index(const Q3ListBoxItem * lbi) const +{ + if (!lbi) + return -1; + Q3ListBoxItem * i_n = d->head; + int c_n = 0; + if (d->cache) { + i_n = d->cache; + c_n = d->cacheIndex; + } + Q3ListBoxItem* i_p = i_n; + int c_p = c_n; + while ((i_n != 0 || i_p != 0) && i_n != lbi && i_p != lbi) { + if (i_n) { + c_n++; + i_n = i_n->n; + } + if (i_p) { + c_p--; + i_p = i_p->p; + } + } + if (i_p == lbi) + return c_p; + if (i_n == lbi) + return c_n; + return -1; +} + + + +/*! + Returns true if the item at position \a index is at least partly + visible; otherwise returns false. +*/ + +bool Q3ListBox::itemVisible(int index) +{ + Q3ListBoxItem * i = item(index); + return i ? itemVisible(i) : false; +} + + +/*! + \overload + + Returns true if \a item is at least partly visible; otherwise + returns false. +*/ + +bool Q3ListBox::itemVisible(const Q3ListBoxItem * item) +{ + if (d->layoutDirty) + doLayout(); + + int i = index(item); + int col = i / numRows(); + int row = i % numRows(); + return (d->columnPos[col] < contentsX()+visibleWidth() && + d->rowPos[row] < contentsY()+visibleHeight() && + d->columnPos[col+1] > contentsX() && + d->rowPos[row+1] > contentsY()); +} + + +/*! \reimp */ + +void Q3ListBox::mousePressEvent(QMouseEvent *e) +{ + mousePressEventEx(e); +} + +void Q3ListBox::mousePressEventEx(QMouseEvent *e) +{ + d->mouseInternalPress = true; + Q3ListBoxItem * i = itemAt(e->pos()); + + if (!i && !d->current && d->head) { + d->current = d->head; + updateItem(d->head); + } + + if (!i && (d->selectionMode != Single || e->button() == Qt::RightButton) + && !(e->state() & Qt::ControlButton)) + clearSelection(); + + d->select = d->selectionMode == Multi ? (i ? !i->isSelected() : false) : true; + d->pressedSelected = i && i->s; + + if (i) + d->selectAnchor = i; + if (i) { + switch(selectionMode()) { + default: + case Single: + if (!i->s || i != d->current) { + if (i->isSelectable()) + setSelected(i, true); + else + setCurrentItem(i); + } + break; + case Extended: + if (i) { + bool shouldBlock = false; + if (!(e->state() & Qt::ShiftButton) && + !(e->state() & Qt::ControlButton)) { + if (!i->isSelected()) { + bool b = signalsBlocked(); + blockSignals(true); + clearSelection(); + blockSignals(b); + } + setSelected(i, true); + d->dragging = true; // always assume dragging + shouldBlock = true; + } else if (e->state() & Qt::ShiftButton) { + d->pressedSelected = false; + Q3ListBoxItem *oldCurrent = item(currentItem()); + bool down = index(oldCurrent) < index(i); + + Q3ListBoxItem *lit = down ? oldCurrent : i; + bool select = d->select; + bool blocked = signalsBlocked(); + blockSignals(true); + for (;; lit = lit->n) { + if (!lit) { + triggerUpdate(false); + break; + } + if (down && lit == i) { + setSelected(i, select); + triggerUpdate(false); + break; + } + if (!down && lit == oldCurrent) { + setSelected(oldCurrent, select); + triggerUpdate(false); + break; + } + setSelected(lit, select); + } + blockSignals(blocked); + emit selectionChanged(); + } else if (e->state() & Qt::ControlButton) { + setSelected(i, !i->isSelected()); + shouldBlock = true; + d->pressedSelected = false; + } + bool blocked = signalsBlocked(); + blockSignals(shouldBlock); + setCurrentItem(i); + blockSignals(blocked); + } + break; + case Multi: + { + setSelected(i, !i->s); + bool b = signalsBlocked(); + blockSignals(true); + setCurrentItem(i); + blockSignals(b); + break; + } + case NoSelection: + setCurrentItem(i); + break; + } + } else { + bool unselect = true; + if (e->button() == Qt::LeftButton) { + if (d->selectionMode == Multi || + d->selectionMode == Extended) { + d->tmpCurrent = d->current; + d->current = 0; + updateItem(d->tmpCurrent); + if (d->rubber) + delete d->rubber; + d->rubber = 0; + d->rubber = new QRect(e->x(), e->y(), 0, 0); + + if (d->selectionMode == Extended && !(e->state() & Qt::ControlButton)) + selectAll(false); + unselect = false; + } + if (unselect && (e->button() == Qt::RightButton || + (selectionMode() == Multi || selectionMode() == Extended))) + clearSelection(); + } + } + + // for sanity, in case people are event-filtering or whatnot + delete d->scrollTimer; + d->scrollTimer = 0; + if (i) { + d->mousePressColumn = d->currentColumn; + d->mousePressRow = d->currentRow; + } else { + d->mousePressColumn = -1; + d->mousePressRow = -1; + } + d->ignoreMoves = false; + + d->pressedItem = i; + + emit pressed(i); + emit pressed(i, e->globalPos()); + emit mouseButtonPressed(e->button(), i, e->globalPos()); + if (e->button() == Qt::RightButton) + emit rightButtonPressed(i, e->globalPos()); +} + + +/*! \reimp */ + +void Q3ListBox::mouseReleaseEvent(QMouseEvent *e) +{ + if (d->selectionMode == Extended && + d->dragging) { + d->dragging = false; + if (d->current != d->pressedItem) { + updateSelection(); // when we drag, we get an update after we release + } + } + + if (d->rubber) { + drawRubber(); + delete d->rubber; + d->rubber = 0; + d->current = d->tmpCurrent; + updateItem(d->current); + } + if (d->scrollTimer) + mouseMoveEvent(e); + delete d->scrollTimer; + d->scrollTimer = 0; + d->ignoreMoves = false; + + if (d->selectionMode == Extended && + d->current == d->pressedItem && + d->pressedSelected && d->current) { + bool block = signalsBlocked(); + blockSignals(true); + clearSelection(); + blockSignals(block); + d->current->s = true; + emit selectionChanged(); + } + + Q3ListBoxItem * i = itemAt(e->pos()); + bool emitClicked = (d->mousePressColumn != -1 && d->mousePressRow != -1) || !d->pressedItem; + emitClicked = emitClicked && d->pressedItem == i; + d->pressedItem = 0; + d->mousePressRow = -1; + d->mousePressColumn = -1; + d->mouseInternalPress = false; + if (emitClicked) { + emit clicked(i); + emit clicked(i, e->globalPos()); + emit mouseButtonClicked(e->button(), i, e->globalPos()); + if (e->button() == Qt::RightButton) + emit rightButtonClicked(i, e->globalPos()); + } +} + + +/*! \reimp */ + +void Q3ListBox::mouseDoubleClickEvent(QMouseEvent *e) +{ + bool ok = true; + Q3ListBoxItem *i = itemAt(e->pos()); + if (!i || selectionMode() == NoSelection) + ok = false; + + d->ignoreMoves = true; + + if (d->current && ok) { + Q3ListBoxItem * i = d->current; + QString tmp = d->current->text(); + emit selected(currentItem()); + emit selected(i); + if (!tmp.isNull()) + emit selected(tmp); + emit doubleClicked(i); + } +} + + +/*! \reimp */ + +void Q3ListBox::mouseMoveEvent(QMouseEvent *e) +{ + Q3ListBoxItem * i = itemAt(e->pos()); + if (i != d->highlighted) { + if (i) { + emit onItem(i); + } else { + emit onViewport(); + } + d->highlighted = i; + } + + if (d->rubber) { + QRect r = d->rubber->normalized(); + drawRubber(); + d->rubber->setCoords(d->rubber->x(), d->rubber->y(), e->x(), e->y()); + doRubberSelection(r, d->rubber->normalized()); + drawRubber(); + return; + } + + if (((e->state() & (Qt::RightButton | Qt::LeftButton | Qt::MidButton)) == 0) || + d->ignoreMoves) + return; + + // hack to keep the combo (and what else?) working: if we get a + // move outside the listbox without having seen a press, discard + // it. + if (!QRect(0, 0, visibleWidth(), visibleHeight()).contains(e->pos()) && + ((d->mousePressColumn < 0 && d->mousePressRow < 0) + || (e->state() == Qt::NoButton && !d->pressedItem))) + return; + + // figure out in what direction to drag-select and perhaps scroll + int dx = 0; + int x = e->x(); + if (x >= visibleWidth()) { + x = visibleWidth()-1; + dx = 1; + } else if (x < 0) { + x = 0; + dx = -1; + } + d->mouseMoveColumn = columnAt(x + contentsX()); + + // sanitize mousePressColumn, if we got here without a mouse press event + if (d->mousePressColumn < 0 && d->mouseMoveColumn >= 0) + d->mousePressColumn = d->mouseMoveColumn; + if (d->mousePressColumn < 0 && d->currentColumn >= 0) + d->mousePressColumn = d->currentColumn; + + // if it's beyond the last column, use the last one + if (d->mouseMoveColumn < 0) + d->mouseMoveColumn = dx >= 0 ? numColumns()-1 : 0; + + // repeat for y + int dy = 0; + int y = e->y(); + if (y >= visibleHeight()) { + y = visibleHeight()-1; + dy = 1; + } else if (y < 0) { + y = 0; + dy = -1; + } + d->mouseMoveRow = rowAt(y + contentsY()); + + if (d->mousePressRow < 0 && d->mouseMoveRow >= 0) + d->mousePressRow = d->mouseMoveRow; + if (d->mousePressRow < 0 && d->currentRow >= 0) + d->mousePressRow = d->currentRow; + + if (d->mousePressRow < 0) + d->mousePressRow = rowAt(x + contentsX()); + + d->scrollPos = QPoint(dx, dy); + + if ((dx || dy) && !d->scrollTimer && e->state() == Qt::LeftButton && e->button() != Qt::LeftButton) { + // start autoscrolling if necessary + d->scrollTimer = new QTimer(this); + connect(d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll())); + d->scrollTimer->start(100, false); + doAutoScroll(); + } else if (!d->scrollTimer) { + // or just select the required bits + updateSelection(); + } +} + + + +void Q3ListBox::updateSelection() +{ + if (d->mouseMoveColumn >= 0 && d->mouseMoveRow >= 0 && + d->mousePressColumn >= 0 && d->mousePressRow >= 0) { + Q3ListBoxItem * i = item(d->mouseMoveColumn * numRows() + + d->mouseMoveRow); +#ifndef QT_NO_ACCESSIBILITY + int ind = index(i); +#endif + if (selectionMode() == Single || selectionMode() == NoSelection) { + if (i && (d->mouseInternalPress || (windowType() == Qt::Popup))) + setCurrentItem(i); + } else { + if (d->selectionMode == Extended && ( + (d->current == d->pressedItem && d->pressedSelected) || + (d->dirtyDrag && !d->dragging))) { + if (d->dirtyDrag && !d->dragging) // emit after dragging stops + d->dirtyDrag = false; + else + clearSelection(); // don't reset drag-selected items + d->pressedItem = 0; + if (i && i->isSelectable()) { + bool block = signalsBlocked(); + blockSignals(true); + i->s = true; + blockSignals(block); + emit selectionChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::StateChanged); + QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection); + QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::SelectionAdd); +#endif + } + triggerUpdate(false); + } else { + int c = qMin(d->mouseMoveColumn, d->mousePressColumn); + int r = qMin(d->mouseMoveRow, d->mousePressRow); + int c2 = qMax(d->mouseMoveColumn, d->mousePressColumn); + int r2 = qMax(d->mouseMoveRow, d->mousePressRow); + bool changed = false; + while(c <= c2) { + Q3ListBoxItem * i = item(c*numRows()+r); + int rtmp = r; + while(i && rtmp <= r2) { + if ((bool)i->s != (bool)d->select && i->isSelectable()) { + i->s = d->select; +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::StateChanged); + QAccessible::updateAccessibility(viewport(), ind+1, d->select ? QAccessible::SelectionAdd : QAccessible::SelectionRemove); +#endif + i->dirty = true; + d->dirtyDrag = changed = true; + } + i = i->n; + rtmp++; + } + c++; + } + if (changed) { + if (!d->dragging) // emit after dragging stops instead + emit selectionChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection); +#endif + triggerUpdate(false); + } + } + if (i) + setCurrentItem(i); + } + } +} + +void Q3ListBox::repaintSelection() +{ + if (d->numColumns == 1) { + for (uint i = topItem(); itemVisible(i) && i < count(); ++i) { + Q3ListBoxItem *it = item(i); + if (!it) + break; + if (it->isSelected()) + updateItem(it); + } + } else { + for (uint i = 0; i < count(); ++i) { + Q3ListBoxItem *it = item(i); + if (!it) + break; + if (it->isSelected()) + updateItem(it); + } + } +} + +/*! \reimp +*/ + +void Q3ListBox::contentsContextMenuEvent(QContextMenuEvent *e) +{ + if (!receivers(SIGNAL(contextMenuRequested(Q3ListBoxItem*,QPoint)))) { + e->ignore(); + return; + } + if (e->reason() == QContextMenuEvent::Keyboard) { + Q3ListBoxItem *i = item(currentItem()); + if (i) { + QRect r = itemRect(i); + emit contextMenuRequested(i, mapToGlobal(r.topLeft() + QPoint(width() / 2, r.height() / 2))); + } + } else { + Q3ListBoxItem * i = itemAt(contentsToViewport(e->pos())); + emit contextMenuRequested(i, e->globalPos()); + } +} + +/*!\reimp +*/ +void Q3ListBox::keyPressEvent(QKeyEvent *e) +{ + if ((e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab) + && e->state() & Qt::ControlButton) + e->ignore(); + + if (count() == 0) { + e->ignore(); + return; + } + + QPointer<Q3ListBox> selfCheck = this; + + Q3ListBoxItem *old = d->current; + if (!old) { + setCurrentItem(d->head); + if (d->selectionMode == Single) + setSelected(d->head, true); + e->ignore(); + return; + } + + bool selectCurrent = false; + switch (e->key()) { + case Qt::Key_Up: + { + d->currInputString.clear(); + if (currentItem() > 0) { + setCurrentItem(currentItem() - 1); + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = d->current; + } + break; + case Qt::Key_Down: + { + d->currInputString.clear(); + if (currentItem() < (int)count() - 1) { + setCurrentItem(currentItem() + 1); + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = d->current; + } + break; + case Qt::Key_Left: + { + d->currInputString.clear(); + if (currentColumn() > 0) { + setCurrentItem(currentItem() - numRows()); + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } else if (numColumns() > 1 && currentItem() > 0) { + int row = currentRow(); + setCurrentItem(currentRow() - 1 + (numColumns() - 1) * numRows()); + + if (currentItem() == -1) + setCurrentItem(row - 1 + (numColumns() - 2) * numRows()); + + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } else { + QApplication::sendEvent(horizontalScrollBar(), e); + } + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = d->current; + } + break; + case Qt::Key_Right: + { + d->currInputString.clear(); + if (currentColumn() < numColumns()-1) { + int row = currentRow(); + int i = currentItem(); + Q3ListBoxItem *it = item(i + numRows()); + if (!it) + it = item(count()-1); + setCurrentItem(it); + + if (currentItem() == -1) { + if (row < numRows() - 1) + setCurrentItem(row + 1); + else + setCurrentItem(i); + } + + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } else if (numColumns() > 1 && currentRow() < numRows()) { + if (currentRow() + 1 < numRows()) { + setCurrentItem(currentRow() + 1); + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + } else { + QApplication::sendEvent(horizontalScrollBar(), e); + } + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = d->current; + } + break; + case Qt::Key_Next: + { + d->currInputString.clear(); + int i = 0; + if (numColumns() == 1) { + i = currentItem() + numItemsVisible(); + i = i > (int)count() - 1 ? (int)count() - 1 : i; + setCurrentItem(i); + setBottomItem(i); + } else { + // I'm not sure about this behavior... + if (currentRow() == numRows() - 1) + i = currentItem() + numRows(); + else + i = currentItem() + numRows() - currentRow() - 1; + i = i > (int)count() - 1 ? (int)count() - 1 : i; + setCurrentItem(i); + } + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = d->current; + } + break; + case Qt::Key_Prior: + { + selectCurrent = true; + d->currInputString.clear(); + int i; + if (numColumns() == 1) { + i = currentItem() - numItemsVisible(); + i = i < 0 ? 0 : i; + setCurrentItem(i); + setTopItem(i); + } else { + // I'm not sure about this behavior... + if (currentRow() == 0) + i = currentItem() - numRows(); + else + i = currentItem() - currentRow(); + i = i < 0 ? 0 : i; + setCurrentItem(i); + } + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = d->current; + } + break; + case Qt::Key_Space: + { + selectCurrent = true; + d->currInputString.clear(); + toggleCurrentItem(); + if (selectionMode() == Extended && d->current->isSelected()) + emit highlighted(currentItem()); + if (selfCheck && (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)) + d->selectAnchor = d->current; + } + break; + case Qt::Key_Return: + case Qt::Key_Enter: + { + selectCurrent = true; + d->currInputString.clear(); + if (currentItem() >= 0 && selectionMode() != NoSelection) { + QString tmp = item(currentItem())->text(); + emit selected(currentItem()); + emit selected(item(currentItem())); + if (!tmp.isEmpty()) + emit selected(tmp); + emit returnPressed(item(currentItem())); + } + if (selfCheck && (!(e->state() & Qt::ShiftButton) || !d->selectAnchor)) + d->selectAnchor = d->current; + } + break; + case Qt::Key_Home: + { + selectCurrent = true; + d->currInputString.clear(); + setCurrentItem(0); + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = d->current; + } + break; + case Qt::Key_End: + { + selectCurrent = true; + d->currInputString.clear(); + int i = (int)count() - 1; + setCurrentItem(i); + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = d->current; + } + break; + default: + { + if (!e->text().isEmpty() && e->text()[0].isPrint() && count()) { + int curItem = currentItem(); + if (curItem == -1) + curItem = 0; + if (!d->inputTimer->isActive()) { + d->currInputString = e->text(); + curItem = d->findItemByName(++curItem, d->currInputString); + } else { + d->inputTimer->stop(); + d->currInputString += e->text(); + int oldCurItem = curItem; + curItem = d->findItemByName(curItem, d->currInputString); + if (curItem < 0) { + curItem = d->findItemByName(++oldCurItem, e->text()); + d->currInputString = e->text(); + } + } + if (curItem >= 0) + setCurrentItem(curItem); + if (curItem >= 0 && selectionMode() == Q3ListBox::Extended) { + bool changed = false; + bool block = signalsBlocked(); + blockSignals(true); + selectAll(false); + blockSignals(block); + Q3ListBoxItem *i = item(curItem); + if (!i->s && i->isSelectable()) { + changed = true; + i->s = true; + updateItem(i); + } + if (changed) + emit selectionChanged(); + } + d->inputTimer->start(400, true); + } else { + d->currInputString.clear(); + if (e->state() & Qt::ControlButton) { + switch (e->key()) { + case Qt::Key_A: + selectAll(true); + break; + } + } else { + e->ignore(); + } + } + } + } + + if (selfCheck && selectCurrent && selectionMode() == Single && + d->current && !d->current->s) { + updateItem(d->current); + setSelected(d->current, true); + } +} + + +/*!\reimp +*/ +void Q3ListBox::focusInEvent(QFocusEvent *e) +{ + d->mousePressRow = -1; + d->mousePressColumn = -1; + d->inMenuMode = false; + if (e->reason() != Qt::MouseFocusReason && !d->current && d->head) { + d->current = d->head; + Q3ListBoxItem *i = d->current; + QString tmp; + if (i) + tmp = i->text(); + int tmp2 = index(i); + emit highlighted(i); + if (!tmp.isNull()) + emit highlighted(tmp); + emit highlighted(tmp2); + emit currentChanged(i); + } + if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) + repaintSelection(); + + if (d->current) + updateItem(currentItem()); +} + + +/*!\reimp +*/ +void Q3ListBox::focusOutEvent(QFocusEvent *e) +{ + if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) { + d->inMenuMode = + e->reason() == Qt::PopupFocusReason || + (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar")); + if (!d->inMenuMode) + repaintSelection(); + } + + if (d->current) + updateItem(currentItem()); +} + +/*!\reimp +*/ +bool Q3ListBox::eventFilter(QObject *o, QEvent *e) +{ + return Q3ScrollView::eventFilter(o, e); +} + +/*! + Repaints the item at position \a index in the list. +*/ + +void Q3ListBox::updateItem(int index) +{ + if (index >= 0) + updateItem(item(index)); +} + + +/*! + \overload + + Repaints the Q3ListBoxItem \a i. +*/ + +void Q3ListBox::updateItem(Q3ListBoxItem * i) +{ + if (!i) + return; + i->dirty = true; + d->updateTimer->start(0, true); +} + + +/*! + \property Q3ListBox::selectionMode + \brief the selection mode of the list box + + Sets the list box's selection mode, which may be one of \c Single + (the default), \c Extended, \c Multi or \c NoSelection. + + \sa SelectionMode +*/ + +void Q3ListBox::setSelectionMode(SelectionMode mode) +{ + if (d->selectionMode == mode) + return; + + if ((selectionMode() == Multi || selectionMode() == Extended) + && (mode == Q3ListBox::Single || mode == Q3ListBox::NoSelection)){ + clearSelection(); + if ((mode == Q3ListBox::Single) && currentItem()) + setSelected(currentItem(), true); + } + + d->selectionMode = mode; + triggerUpdate(true); +} + + +Q3ListBox::SelectionMode Q3ListBox::selectionMode() const +{ + return d->selectionMode; +} + + +/*! + \property Q3ListBox::multiSelection + \brief whether or not the list box is in Multi selection mode + + Consider using the \l Q3ListBox::selectionMode property instead of + this property. + + When setting this property, Multi selection mode is used if set to true and + to Single selection mode if set to false. + + When getting this property, true is returned if the list box is in + Multi selection mode or Extended selection mode, and false if it is + in Single selection mode or NoSelection mode. + + \sa selectionMode +*/ + +bool Q3ListBox::isMultiSelection() const +{ + return selectionMode() == Multi || selectionMode() == Extended; +} + +void Q3ListBox::setMultiSelection(bool enable) +{ + setSelectionMode(enable ? Multi : Single); +} + + +/*! + Toggles the selection status of currentItem() and repaints if the + list box is a \c Multi selection list box. + + \sa setMultiSelection() +*/ + +void Q3ListBox::toggleCurrentItem() +{ + if (selectionMode() == Single || + selectionMode() == NoSelection || + !d->current) + return; + + if (d->current->s || d->current->isSelectable()) { + d->current->s = !d->current->s; + emit selectionChanged(); +#ifndef QT_NO_ACCESSIBILITY + int ind = index(d->current); + QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection); + QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::StateChanged); + QAccessible::updateAccessibility(viewport(), ind+1, d->current->s ? QAccessible::SelectionAdd : QAccessible::SelectionRemove); +#endif + } + updateItem(d->current); +} + + +/*! + \overload + + If \a select is true the item at position \a index is selected; + otherwise the item is deselected. +*/ + +void Q3ListBox::setSelected(int index, bool select) +{ + setSelected(item(index), select); +} + + +/*! + Selects \a item if \a select is true or unselects it if \a select + is false, and repaints the item appropriately. + + If the list box is a \c Single selection list box and \a select is + true, setSelected() calls setCurrentItem(). + + If the list box is a \c Single selection list box, \a select is + false, setSelected() calls clearSelection(). + + \sa setMultiSelection(), setCurrentItem(), clearSelection(), currentItem() +*/ + +void Q3ListBox::setSelected(Q3ListBoxItem * item, bool select) +{ + if (!item || !item->isSelectable() || + (bool)item->s == select || d->selectionMode == NoSelection) + return; + + int ind = index(item); + bool emitHighlighted = (d->current != item) || ( select && (item->s != (uint) select) ); + if (selectionMode() == Single) { + if (d->current != item) { + Q3ListBoxItem *o = d->current; + if (d->current && d->current->s) + d->current->s = false; + d->current = item; +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::Focus); +#endif + d->currentColumn = ind / numRows(); + d->currentRow = ind % numRows(); + + if (o) + updateItem(o); + } + } + + item->s = (uint)select; + updateItem(item); + + if (d->selectionMode == Single && select) { + emit selectionChanged(item); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), ind+1, QAccessible::StateChanged); +#endif + } + emit selectionChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection); + if (d->selectionMode != Single) + QAccessible::updateAccessibility(viewport(), ind+1, select ? QAccessible::SelectionAdd : QAccessible::SelectionRemove); +#endif + + if (emitHighlighted) { + QString tmp; + if (item) + tmp = item->text(); + int tmp2 = index(item); + emit highlighted(item); + if (!tmp.isNull()) + emit highlighted(tmp); + emit highlighted(tmp2); + emit currentChanged(item); + } +} + + +/*! + Returns true if item \a i is selected; otherwise returns false. +*/ + +bool Q3ListBox::isSelected(int i) const +{ + if (selectionMode() == Single && i != currentItem()) + return false; + + Q3ListBoxItem * lbi = item(i); + if (!lbi) + return false; // should not happen + return lbi->s; +} + + +/*! + \overload + + Returns true if item \a i is selected; otherwise returns false. +*/ + +bool Q3ListBox::isSelected(const Q3ListBoxItem * i) const +{ + if (!i) + return false; + + return i->s; +} + +/*! Returns the selected item if the list box is in +single-selection mode and an item is selected. + +If no items are selected or the list box is in another selection mode +this function returns 0. + +\sa setSelected() setMultiSelection() +*/ + +Q3ListBoxItem* Q3ListBox::selectedItem() const +{ + if (d->selectionMode != Single) + return 0; + if (isSelected(currentItem())) + return d->current; + return 0; +} + + +/*! + Deselects all items, if possible. + + Note that a \c Single selection list box will automatically select + an item if it has keyboard focus. +*/ + +void Q3ListBox::clearSelection() +{ + selectAll(false); +} + +/*! + In \c Multi and \c Extended modes, this function sets all items to + be selected if \a select is true, and to be unselected if \a + select is false. + + In \c Single and \c NoSelection modes, this function only changes + the selection status of currentItem(). +*/ + +void Q3ListBox::selectAll(bool select) +{ + if (selectionMode() == Multi || selectionMode() == Extended) { + bool b = signalsBlocked(); + blockSignals(true); + for (int i = 0; i < (int)count(); i++) + setSelected(i, select); + blockSignals(b); + emit selectionChanged(); + } else if (d->current) { + Q3ListBoxItem * i = d->current; + setSelected(i, select); + } +} + +/*! + Inverts the selection. Only works in \c Multi and \c Extended + selection mode. +*/ + +void Q3ListBox::invertSelection() +{ + if (d->selectionMode == Single || + d->selectionMode == NoSelection) + return; + + bool b = signalsBlocked(); + blockSignals(true); + for (int i = 0; i < (int)count(); i++) + setSelected(i, !item(i)->isSelected()); + blockSignals(b); + emit selectionChanged(); +} + + +/*! + Not used anymore; provided for compatibility. +*/ + +void Q3ListBox::emitChangedSignal(bool) +{ +} + + +/*! \reimp */ + +QSize Q3ListBox::sizeHint() const +{ + if (cachedSizeHint().isValid()) + return cachedSizeHint(); + + ensurePolished(); + doLayout(); + + int i=0; + while(i < 10 && + i < (int)d->columnPos.size()-1 && + d->columnPos[i] < 200) + i++; + int x; + x = qMin(200, d->columnPos[i] + + 2 * style()->pixelMetric(QStyle::PM_DefaultFrameWidth)); + x = qMax(40, x); + + i = 0; + while(i < 10 && + i < (int)d->rowPos.size()-1 && + d->rowPos[i] < 200) + i++; + int y; + y = qMin(200, d->rowPos[i] + + 2 * style()->pixelMetric(QStyle::PM_DefaultFrameWidth)); + y = qMax(40, y); + + QSize s(x, y); + setCachedSizeHint(s); + return s; +} + +/*! + \reimp +*/ + +QSize Q3ListBox::minimumSizeHint() const +{ + return Q3ScrollView::minimumSizeHint(); +} + + +/*! + Ensures that a single paint event will occur at the end of the + current event loop iteration. If \a doLayout is true, the layout + is also redone. +*/ + +void Q3ListBox::triggerUpdate(bool doLayout) +{ + if (doLayout) + d->layoutDirty = d->mustPaintAll = true; + d->updateTimer->start(0, true); +} + + +void Q3ListBox::setColumnMode(LayoutMode mode) +{ + if (mode == Variable) + return; + d->rowModeWins = false; + d->columnMode = mode; + triggerUpdate(true); +} + + +void Q3ListBox::setColumnMode(int columns) +{ + if (columns < 1) + columns = 1; + d->columnMode = FixedNumber; + d->numColumns = columns; + d->rowModeWins = false; + triggerUpdate(true); +} + +void Q3ListBox::setRowMode(LayoutMode mode) +{ + if (mode == Variable) + return; + d->rowModeWins = true; + d->rowMode = mode; + triggerUpdate(true); +} + + +void Q3ListBox::setRowMode(int rows) +{ + if (rows < 1) + rows = 1; + d->rowMode = FixedNumber; + d->numRows = rows; + d->rowModeWins = true; + triggerUpdate(true); +} + +/*! + \property Q3ListBox::columnMode + \brief the column layout mode for this list box. + + setColumnMode() sets the layout mode and adjusts the number of + displayed columns. The row layout mode automatically becomes \c + Variable, unless the column mode is \c Variable. + + \sa setRowMode() rowMode numColumns +*/ + + +Q3ListBox::LayoutMode Q3ListBox::columnMode() const +{ + if (d->rowModeWins) + return Variable; + else + return d->columnMode; +} + + +/*! + \property Q3ListBox::rowMode + \brief the row layout mode for this list box + + This property is normally \c Variable. + + setRowMode() sets the layout mode and adjusts the number of + displayed rows. The column layout mode automatically becomes \c + Variable, unless the row mode is \c Variable. + + \sa columnMode +*/ + + +Q3ListBox::LayoutMode Q3ListBox::rowMode() const +{ + if (d->rowModeWins) + return d->rowMode; + else + return Variable; +} + + +/*! + \property Q3ListBox::numColumns + \brief the number of columns in the list box + + This is normally 1, but can be different if \l + Q3ListBox::columnMode or \l Q3ListBox::rowMode has been set. + + \sa columnMode rowMode numRows +*/ + +int Q3ListBox::numColumns() const +{ + if (count() == 0) + return 0; + if (!d->rowModeWins && d->columnMode == FixedNumber) + return d->numColumns; + doLayout(); + return d->columnPos.size()-1; +} + + +/*! + \property Q3ListBox::numRows + \brief the number of rows in the list box. + + This is equal to the number of items in the default single-column + layout, but can be different. + + \sa columnMode rowMode numColumns +*/ + +int Q3ListBox::numRows() const +{ + if (count() == 0) + return 0; + if (d->rowModeWins && d->rowMode == FixedNumber) + return d->numRows; + doLayout(); + return d->rowPos.size()-1; +} + + +/*! + This function does the hard layout work. You should never need to + call it. +*/ + +void Q3ListBox::doLayout() const +{ + if (!d->layoutDirty || d->resizeTimer->isActive()) + return; + ensurePolished(); + int c = count(); + switch(rowMode()) { + case FixedNumber: + // columnMode() is known to be Variable + tryGeometry(d->numRows, (c+d->numRows-1)/d->numRows); + break; + case FitToHeight: + // columnMode() is known to be Variable + if (d->head) { + // this is basically the FitToWidth code, but edited to use rows. + int maxh = 0; + Q3ListBoxItem * i = d->head; + while (i) { + int h = i->height(this); + if (maxh < h) + maxh = h; + i = i->n; + } + int vh = viewportSize(1, 1).height(); + do { + int rows = vh / maxh; + if (rows > c) + rows = c; + if (rows < 1) + rows = 1; + if (variableHeight() && rows < c) { + do { + ++rows; + tryGeometry(rows, (c+rows-1)/rows); + } while (rows <= c && + d->rowPos[(int)d->rowPos.size()-1] <= vh); + --rows; + } + tryGeometry(rows, (c+rows-1)/rows); + int nvh = viewportSize(d->columnPos[(int)d->columnPos.size()-1], + d->rowPos[(int)d->rowPos.size()-1]).height(); + if (nvh < vh) + vh = nvh; + } while (d->rowPos.size() > 2 && + vh < d->rowPos[(int)d->rowPos.size()-1]); + } else { + tryGeometry(1, 1); + } + break; + case Variable: + if (columnMode() == FixedNumber) { + tryGeometry((count()+d->numColumns-1)/d->numColumns, + d->numColumns); + } else if (d->head) { // FitToWidth, at least one item + int maxw = 0; + Q3ListBoxItem * i = d->head; + while (i) { + int w = i->width(this); + if (maxw < w) + maxw = w; + i = i->n; + } + int vw = viewportSize(1, 1).width(); + do { + int cols = vw / maxw; + if (cols > c) + cols = c; + if (cols < 1) + cols = 1; + if (variableWidth() && cols < c) { + do { + ++cols; + tryGeometry((c+cols-1)/cols, cols); + } while (cols <= c && + d->columnPos[(int)d->columnPos.size()-1] <= vw); + --cols; + } + tryGeometry((c+cols-1)/cols, cols); + int nvw = viewportSize(d->columnPos[(int)d->columnPos.size()-1], + d->rowPos[(int)d->rowPos.size()-1]).width(); + if (nvw < vw) + vw = nvw; + } while (d->columnPos.size() > 2 && + vw < d->columnPos[(int)d->columnPos.size()-1]); + } else { + tryGeometry(1, 1); + } + break; + } + + d->layoutDirty = false; + int w = d->columnPos[(int)d->columnPos.size()-1]; + int h = d->rowPos[(int)d->rowPos.size()-1]; + QSize s(viewportSize(w, h)); + w = qMax(w, s.width()); + + d->columnPosOne = d->columnPos[1]; + // extend the column for simple single-column listboxes + if (columnMode() == FixedNumber && d->numColumns == 1 && + d->columnPos[1] < w) + d->columnPos[1] = w; + ((Q3ListBox *)this)->resizeContents(w, h); +} + + +/*! + Lay the items out in a \a columns by \a rows array. The array may + be too big: doLayout() is expected to call this with the right + values. +*/ + +void Q3ListBox::tryGeometry(int rows, int columns) const +{ + if (columns < 1) + columns = 1; + d->columnPos.resize(columns+1); + + if (rows < 1) + rows = 1; + d->rowPos.resize(rows+1); + + // funky hack I: dump the height/width of each column/row in + // {column,row}Pos for later conversion to positions. + int c; + for(c=0; c<=columns; c++) + d->columnPos[c] = 0; + int r; + for(r=0; r<=rows; r++) + d->rowPos[r] = 0; + r = c = 0; + Q3ListBoxItem * i = d->head; + while (i && c < columns) { + if (i == d->current) { + d->currentRow = r; + d->currentColumn = c; + } + + int w = i->width(this); + if (d->columnPos[c] < w) + d->columnPos[c] = w; + int h = i->height(this); + if (d->rowPos[r] < h) + d->rowPos[r] = h; + i = i->n; + r++; + if (r == rows) { + r = 0; + c++; + } + } + // funky hack II: if not variable {width,height}, unvariablify it. + if (!variableWidth()) { + int w = 0; + for(c=0; c<columns; c++) + if (w < d->columnPos[c]) + w = d->columnPos[c]; + for(c=0; c<columns; c++) + d->columnPos[c] = w; + } + if (!variableHeight()) { + int h = 0; + for(r=0; r<rows; r++) + if (h < d->rowPos[r]) + h = d->rowPos[r]; + for(r=0; r<rows; r++) + d->rowPos[r] = h; + } + // repair the hacking. + int x = 0; + for(c=0; c<=columns; c++) { + int w = d->columnPos[c]; + d->columnPos[c] = x; + x += w; + } + int y = 0; + for(r=0; r<=rows; r++) { + int h = d->rowPos[r]; + d->rowPos[r] = y; + y += h; + } +} + + +/*! + Returns the row index of the current item, or -1 if no item is the + current item. +*/ + +int Q3ListBox::currentRow() const +{ + if (!d->current) + return -1; + if (d->currentRow < 0) + d->layoutDirty = true; + if (d->layoutDirty) + doLayout(); + return d->currentRow; +} + + +/*! + Returns the column index of the current item, or -1 if no item is + the current item. +*/ + +int Q3ListBox::currentColumn() const +{ + if (!d->current) + return -1; + if (d->currentColumn < 0) + d->layoutDirty = true; + if (d->layoutDirty) + doLayout(); + return d->currentColumn; +} + + +void Q3ListBox::setTopItem(int index) +{ + if (index >= (int)count() || count() == 0) + return; + int col = index / numRows(); + int y = d->rowPos[index-col*numRows()]; + if (d->columnPos[col] >= contentsX() && + d->columnPos[col+1] <= contentsX() + visibleWidth()) + setContentsPos(contentsX(), y); + else + setContentsPos(d->columnPos[col], y); +} + +/*! + Scrolls the list box so the item at position \a index in the list + is displayed in the bottom row of the list box. + + \sa setTopItem() +*/ + +void Q3ListBox::setBottomItem(int index) +{ + if (index >= (int)count() || count() == 0) + return; + int col = index / numRows(); + int y = d->rowPos[1+index-col*numRows()] - visibleHeight(); + if (y < 0) + y = 0; + if (d->columnPos[col] >= contentsX() && + d->columnPos[col+1] <= contentsX() + visibleWidth()) + setContentsPos(contentsX(), y); + else + setContentsPos(d->columnPos[col], y); +} + + +/*! + Returns the item at point \a p, specified in viewport coordinates, + or a 0 if there is no item at \a p. + + Use contentsToViewport() to convert between widget coordinates and + viewport coordinates. +*/ + +Q3ListBoxItem * Q3ListBox::itemAt(const QPoint& p) const +{ + if (d->layoutDirty) + doLayout(); + QPoint np = p; + + np -= viewport()->pos(); + if (!viewport()->rect().contains(np)) + return 0; + + // take into account contents position + np = viewportToContents(np); + + int x = np.x(); + int y = np.y(); + + // return 0 when y is below the last row + if (y > d->rowPos[numRows()]) + return 0; + + int col = columnAt(x); + int row = rowAt(y); + + Q3ListBoxItem *i = item(col * numRows() + row); + if (i && numColumns() > 1) { + if (d->columnPos[col] + i->width(this) >= x) + return i; + } else { + if (d->columnPos[col + 1] >= x) + return i; + } + return 0; +} + + +/*! + Ensures that the current item is visible. +*/ + +void Q3ListBox::ensureCurrentVisible() +{ + if (!d->current) + return; + + doLayout(); + + int row = currentRow(); + int column = currentColumn(); + int w = (d->columnPos[column+1] - d->columnPos[column]) / 2; + int h = (d->rowPos[row+1] - d->rowPos[row]) / 2; + // next four lines are Bad. they mean that for pure left-to-right + // languages, textual list box items are displayed better than + // before when there is little space. for non-textual items, or + // other languages, it means... that you really should have enough + // space in the first place :) + if (numColumns() == 1) + w = 0; + if (w*2 > viewport()->width()) + w = viewport()->width()/2; + + ensureVisible(d->columnPos[column] + w, d->rowPos[row] + h, w, h); +} + + +/*! \internal */ + +void Q3ListBox::doAutoScroll() +{ + if (d->scrollPos.x() < 0) { + // scroll left + int x = contentsX() - horizontalScrollBar()->singleStep(); + if (x < 0) + x = 0; + if (x != contentsX()) { + d->mouseMoveColumn = columnAt(x); + updateSelection(); + if (x < contentsX()) + setContentsPos(x, contentsY()); + } + } else if (d->scrollPos.x() > 0) { + // scroll right + int x = contentsX() + horizontalScrollBar()->singleStep(); + if (x + visibleWidth() > contentsWidth()) + x = contentsWidth() - visibleWidth(); + if (x != contentsX()) { + d->mouseMoveColumn = columnAt(x + visibleWidth() - 1); + updateSelection(); + if (x > contentsX()) + setContentsPos(x, contentsY()); + } + } + + if (d->scrollPos.y() < 0) { + // scroll up + int y = contentsY() - verticalScrollBar()->singleStep(); + if (y < 0) + y = 0; + if (y != contentsY()) { + y = contentsY() - verticalScrollBar()->singleStep(); + d->mouseMoveRow = rowAt(y); + updateSelection(); + } + } else if (d->scrollPos.y() > 0) { + // scroll down + int y = contentsY() + verticalScrollBar()->singleStep(); + if (y + visibleHeight() > contentsHeight()) + y = contentsHeight() - visibleHeight(); + if (y != contentsY()) { + y = contentsY() + verticalScrollBar()->singleStep(); + d->mouseMoveRow = rowAt(y + visibleHeight() - 1); + updateSelection(); + } + } + + if (d->scrollPos == QPoint(0, 0)) { + delete d->scrollTimer; + d->scrollTimer = 0; + } +} + + +/*! + \property Q3ListBox::topItem + \brief the index of an item at the top of the screen. + + When getting this property and the listbox has multiple columns, + an arbitrary item is selected and returned. + + When setting this property, the list box is scrolled so the item + at position \e index in the list is displayed in the top row of + the list box. +*/ + +int Q3ListBox::topItem() const +{ + doLayout(); + + // move rightwards to the best column + int col = columnAt(contentsX()); + int row = rowAt(contentsY()); + return col * numRows() + row; +} + + +/*! + \property Q3ListBox::variableHeight + \brief whether this list box has variable-height rows + + When the list box has variable-height rows (the default), each row + is as high as the highest item in that row. When it has same-sized + rows, all rows are as high as the highest item in the list box. + + \sa variableWidth +*/ + +bool Q3ListBox::variableHeight() const +{ + return d->variableHeight; +} + + +void Q3ListBox::setVariableHeight(bool enable) +{ + if ((bool)d->variableHeight == enable) + return; + + d->variableHeight = enable; + triggerUpdate(true); +} + + +/*! + \property Q3ListBox::variableWidth + \brief whether this list box has variable-width columns + + When the list box has variable-width columns, each column is as + wide as the widest item in that column. When it has same-sized + columns (the default), all columns are as wide as the widest item + in the list box. + + \sa variableHeight +*/ + +bool Q3ListBox::variableWidth() const +{ + return d->variableWidth; +} + + +void Q3ListBox::setVariableWidth(bool enable) +{ + if ((bool)d->variableWidth == enable) + return; + + d->variableWidth = enable; + triggerUpdate(true); +} + + +/*! + Repaints only what really needs to be repainted. +*/ +void Q3ListBox::refreshSlot() +{ + if (d->mustPaintAll || + d->layoutDirty) { + d->mustPaintAll = false; + bool currentItemVisible = itemVisible(currentItem()); + doLayout(); + if (hasFocus() && + currentItemVisible && + d->currentColumn >= 0 && + d->currentRow >= 0 && + (d->columnPos[d->currentColumn] < contentsX() || + d->columnPos[d->currentColumn+1]>contentsX()+visibleWidth() || + d->rowPos[d->currentRow] < contentsY() || + d->rowPos[d->currentRow+1] > contentsY()+visibleHeight())) + ensureCurrentVisible(); + viewport()->repaint(); + return; + } + + QRegion r; + int x = contentsX(); + int y = contentsY(); + int col = columnAt(x); + int row = rowAt(y); + int top = row; + while(col < (int)d->columnPos.size()-1 && d->columnPos[col+1] < x) + col++; + while(top < (int)d->rowPos.size()-1 && d->rowPos[top+1] < y) + top++; + Q3ListBoxItem * i = item(col * numRows() + row); + + while (i && (int)col < numColumns() && + d->columnPos[col] < x + visibleWidth() ) { + int cw = d->columnPos[col+1] - d->columnPos[col]; + while (i && row < numRows() && d->rowPos[row] < + y + visibleHeight()) { + if (i->dirty) + r = r.united(QRect(d->columnPos[col] - x, d->rowPos[row] - y, + cw, d->rowPos[row+1] - d->rowPos[row])); + row++; + i = i->n; + } + col++; + if (numColumns() > 1) { + row = top; + i = item(col * numRows() + row); + } + } + + if (r.isEmpty()) + viewport()->repaint(); + else + viewport()->repaint(r); +} + + +/*! \reimp */ + +void Q3ListBox::viewportPaintEvent(QPaintEvent * e) +{ + doLayout(); + QWidget* vp = viewport(); + QPainter p(vp); + QRegion r = e->region(); + +#if 0 + { + // this stuff has been useful enough times that from now I'm + // leaving it in the source. + uint i = 0; + qDebug("%s/%s: %i rects", className(), name(), r.rects().size()); + while(i < r.rects().size()) { + qDebug("rect %d: %d, %d, %d, %d", i, + r.rects()[i].left(), r.rects()[i].top(), + r.rects()[i].width(), r.rects()[i].height()); + i++; + } + qDebug(""); + } +#endif + + int x = contentsX(); + int y = contentsY(); + int w = vp->width(); + int h = vp->height(); + + int col = columnAt(x); + int top = rowAt(y); + int row = top; + + Q3ListBoxItem * i = item(col*numRows() + row); + + const QPalette &pal = palette(); + p.setPen(pal.text().color()); + p.setBackground(palette().brush(backgroundRole()).color()); + while (i && (int)col < numColumns() && d->columnPos[col] < x + w) { + int cw = d->columnPos[col+1] - d->columnPos[col]; + while (i && (int)row < numRows() && d->rowPos[row] < y + h) { + int ch = d->rowPos[row+1] - d->rowPos[row]; + QRect itemRect(d->columnPos[col]-x, d->rowPos[row]-y, cw, ch); + QRegion tempRegion(itemRect); + QRegion itemPaintRegion(tempRegion.intersected(r )); + if (!itemPaintRegion.isEmpty()) { + p.save(); + p.setClipRegion(itemPaintRegion); + p.translate(d->columnPos[col]-x, d->rowPos[row]-y); + paintCell(&p, row, col); + p.restore(); + r = r.subtracted(itemPaintRegion); + } + row++; + if (i->dirty) { + // reset dirty flag only if the entire item was painted + if (itemPaintRegion == QRegion(itemRect)) + i->dirty = false; + } + i = i->n; + } + col++; + if (numColumns() > 1) { + row = top; + i = item(col * numRows() + row); + } + } + + if (r.isEmpty()) + return; + p.setClipRegion(r); + p.fillRect(0, 0, w, h, viewport()->palette().brush(viewport()->backgroundRole())); + + if(d->rubber && d->rubber->width() && d->rubber->height()) { + p.save(); + p.setClipping(false); + // p.setRasterOp(NotROP); // ### fix - use qrubberband instead + QStyleOptionRubberBand opt; + opt.rect = d->rubber->normalized(); + opt.palette = palette(); + opt.shape = QRubberBand::Rectangle; + opt.opaque = false; + style()->drawControl(QStyle::CE_RubberBand, &opt, &p, this); + p.restore(); + } +} + + +/*! + Returns the height in pixels of the item with index \a index. \a + index defaults to 0. + + If \a index is too large, this function returns 0. +*/ + +int Q3ListBox::itemHeight(int index) const +{ + if (index >= (int)count() || index < 0) + return 0; + int r = index % numRows(); + return d->rowPos[r+1] - d->rowPos[r]; +} + + +/*! + Returns the index of the column at \a x, which is in the listbox's + coordinates, not in on-screen coordinates. + + If there is no column that spans \a x, columnAt() returns -1. +*/ + +int Q3ListBox::columnAt(int x) const +{ + if (x < 0) + return -1; + if (!d->columnPos.size()) + return -1; + if (x >= d->columnPos[(int)d->columnPos.size()-1]) + return numColumns() - 1; + + int col = 0; + while(col < (int)d->columnPos.size()-1 && d->columnPos[col+1] < x) + col++; + return col; +} + + +/*! + Returns the index of the row at \a y, which is in the listbox's + coordinates, not in on-screen coordinates. + + If there is no row that spans \a y, rowAt() returns -1. +*/ + +int Q3ListBox::rowAt(int y) const +{ + if (y < 0) + return -1; + + // find the top item, use bsearch for speed + int l = 0; + int r = d->rowPos.size() - 2; + if (r < 0) + return -1; + if (l <= d->rowPosCache && d->rowPosCache <= r) { + if (d->rowPos[qMax(l, d->rowPosCache - 10)] <= y + && y <= d->rowPos[qMin(r, d->rowPosCache + 10)]) { + l = qMax(l, d->rowPosCache - 10); + r = qMin(r, d->rowPosCache + 10); + } + } + int i = ((l+r+1) / 2); + while (r - l) { + if (d->rowPos[i] > y) + r = i -1; + else + l = i; + i = ((l+r+1) / 2); + } + d->rowPosCache = i; + if (d->rowPos[i] <= y && y <= d->rowPos[i+1] ) + return i; + + return d->count - 1; +} + + +/*! + Returns the rectangle on the screen that \a item occupies in + viewport()'s coordinates, or an invalid rectangle if \a item is 0 + or is not currently visible. +*/ + +QRect Q3ListBox::itemRect(Q3ListBoxItem *item) const +{ + if (d->resizeTimer->isActive()) + return QRect(0, 0, -1, -1); + if (!item) + return QRect(0, 0, -1, -1); + + int i = index(item); + if (i == -1) + return QRect(0, 0, -1, -1); + + int col = i / numRows(); + int row = i % numRows(); + + int x = d->columnPos[col] - contentsX(); + int y = d->rowPos[row] - contentsY(); + + QRect r(x, y, d->columnPos[col + 1] - d->columnPos[col], + d->rowPos[row + 1] - d->rowPos[row]); + if (r.intersects(QRect(0, 0, visibleWidth(), visibleHeight()))) + return r; + return QRect(0, 0, -1, -1); +} + + +/*! + Using this method is quite inefficient. We suggest to use insertItem() + for inserting and sort() afterwards. + + Inserts \a lbi at its sorted position in the list box and returns the + position. + + All items must be inserted with inSort() to maintain the sorting + order. inSort() treats any pixmap (or user-defined type) as + lexicographically less than any string. + + \sa insertItem(), sort() +*/ +int Q3ListBox::inSort(const Q3ListBoxItem * lbi) +{ + if (!lbi) + return -1; + + Q3ListBoxItem * i = d->head; + int c = 0; + + while(i && i->text() < lbi->text()) { + i = i->n; + c++; + } + insertItem(lbi, c); + return c; +} + +/*! + \overload + Using this method is quite inefficient. We suggest to use insertItem() + for inserting and sort() afterwards. + + Inserts a new item of \a text at its sorted position in the list box and + returns the position. + + All items must be inserted with inSort() to maintain the sorting + order. inSort() treats any pixmap (or user-defined type) as + lexicographically less than any string. + + \sa insertItem(), sort() +*/ +int Q3ListBox::inSort(const QString& text) +{ + Q3ListBoxItem *lbi = new Q3ListBoxText(text); + + Q3ListBoxItem * i = d->head; + int c = 0; + + while(i && i->text() < lbi->text()) { + i = i->n; + c++; + } + insertItem(lbi, c); + return c; +} + + +/*! \reimp */ + +void Q3ListBox::resizeEvent(QResizeEvent *e) +{ + d->layoutDirty = (d->layoutDirty || + rowMode() == FitToHeight || + columnMode() == FitToWidth); + + if (!d->layoutDirty && columnMode() == FixedNumber && + d->numColumns == 1) { + int w = d->columnPosOne; + QSize s(viewportSize(w, contentsHeight())); + w = qMax(w, s.width()); + d->columnPos[1] = qMax(w, d->columnPosOne); + resizeContents(d->columnPos[1], contentsHeight()); + } + + if (d->resizeTimer->isActive()) + d->resizeTimer->stop(); + if (d->rowMode == FixedNumber && d->columnMode == FixedNumber) { + bool currentItemVisible = itemVisible(currentItem()); + doLayout(); + Q3ScrollView::resizeEvent(e); + if (currentItemVisible) + ensureCurrentVisible(); + if (d->current) + viewport()->repaint(itemRect(d->current)); + } else if ((d->columnMode == FitToWidth || d->rowMode == FitToHeight) && !(isVisible())) { + Q3ScrollView::resizeEvent(e); + } else if (d->layoutDirty) { + d->resizeTimer->start(100, true); + resizeContents(contentsWidth() - (e->oldSize().width() - e->size().width()), + contentsHeight() - (e->oldSize().height() - e->size().height())); + Q3ScrollView::resizeEvent(e); + } else { + Q3ScrollView::resizeEvent(e); + } +} + +/*! + \internal +*/ + +void Q3ListBox::adjustItems() +{ + triggerUpdate(true); + ensureCurrentVisible(); +} + + +/*! + Provided for compatibility with the old Q3ListBox. We recommend + using Q3ListBoxItem::paint() instead. + + Repaints the cell at \a row, \a col using painter \a p. +*/ + +void Q3ListBox::paintCell(QPainter * p, int row, int col) +{ + bool drawActiveSelection = hasFocus() || d->inMenuMode || + !style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this); + QPalette pal = palette(); + if(!drawActiveSelection) + pal.setCurrentColorGroup(QPalette::Inactive); + + int cw = d->columnPos[col+1] - d->columnPos[col]; + int ch = d->rowPos[row+1] - d->rowPos[row]; + Q3ListBoxItem * i = item(col*numRows()+row); + p->save(); + if (i->s) { + if (i->custom_highlight) { + p->fillRect(0, 0, cw, ch, pal.brush(viewport()->foregroundRole())); + p->setPen(pal.highlightedText().color()); + p->setBackground(pal.highlight()); + } else if (numColumns() == 1) { + p->fillRect(0, 0, cw, ch, pal.brush(QPalette::Highlight)); + p->setPen(pal.highlightedText().color()); + p->setBackground(pal.highlight()); + } else { + int iw = i->width(this); + p->fillRect(0, 0, iw, ch, pal.brush(QPalette::Highlight)); + p->fillRect(iw, 0, cw - iw + 1, ch, viewport()->palette().brush(viewport()->backgroundRole())); + p->setPen(pal.highlightedText().color()); + p->setBackground(pal.highlight()); + } + } else { + p->fillRect(0, 0, cw, ch, viewport()->palette().brush(viewport()->backgroundRole())); + } + + i->paint(p); + + if (d->current == i && hasFocus() && !i->custom_highlight) { + if (numColumns() > 1) + cw = i->width(this); + QStyleOptionFocusRect opt; + opt.rect.setRect(0, 0, cw, ch); + opt.palette = pal; + opt.state = QStyle::State_FocusAtBorder; + if (i->isSelected()) + opt.backgroundColor = pal.highlight().color(); + else + opt.backgroundColor = pal.base().color(); + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, this); + } + + p->restore(); +} + +/*! + Returns the width of the widest item in the list box. +*/ + +long Q3ListBox::maxItemWidth() const +{ + if (d->layoutDirty) + doLayout(); + long m = 0; + int i = d->columnPos.size(); + while(i--) + if (m < d->columnPos[i]) + m = d->columnPos[i]; + return m; +} + + +/*! \reimp */ + +void Q3ListBox::showEvent(QShowEvent *) +{ + d->ignoreMoves = false; + d->mousePressRow = -1; + d->mousePressColumn = -1; + d->mustPaintAll = false; + ensureCurrentVisible(); +} + +/*! + \fn bool Q3ListBoxItem::isSelected() const + + Returns true if the item is selected; otherwise returns false. + + \sa Q3ListBox::isSelected(), isCurrent() +*/ + +/*! + Returns true if the item is the current item; otherwise returns + false. + + \sa Q3ListBox::currentItem(), Q3ListBox::item(), isSelected() +*/ +bool Q3ListBoxItem::isCurrent() const +{ + return listBox() && listBox()->hasFocus() && + listBox()->item(listBox()->currentItem()) == this; +} + +/*! + \fn void Q3ListBox::centerCurrentItem() + + If there is a current item, the list box is scrolled so that this + item is displayed centered. + + \sa Q3ListBox::ensureCurrentVisible() +*/ + +/*! + Returns a pointer to the list box containing this item. +*/ + +Q3ListBox * Q3ListBoxItem::listBox() const +{ + return lbox; +} + + +/*! + Removes \a item from the list box and causes an update of the + screen display. The item is not deleted. You should normally not + need to call this function because Q3ListBoxItem::~Q3ListBoxItem() + calls it. The normal way to delete an item is with \c delete. + + \sa Q3ListBox::insertItem() +*/ +void Q3ListBox::takeItem(const Q3ListBoxItem * item) +{ + if (!item || d->clearing) + return; + d->cache = 0; + d->count--; + if (item == d->last) + d->last = d->last->p; + if (item->p && item->p->n == item) + item->p->n = item->n; + if (item->n && item->n->p == item) + item->n->p = item->p; + if (d->head == item) { + d->head = item->n; + d->currentColumn = d->currentRow = -1; + } + + if (d->current == item) { + d->current = item->n ? item->n : item->p; + Q3ListBoxItem *i = d->current; + QString tmp; + if (i) + tmp = i->text(); + int tmp2 = index(i); + emit highlighted(i); + if (!tmp.isNull()) + emit highlighted(tmp); + emit highlighted(tmp2); + emit currentChanged(i); + } + if (d->tmpCurrent == item) + d->tmpCurrent = d->current; + if (d->selectAnchor == item) + d->selectAnchor = d->current; + + if (item->s) + emit selectionChanged(); + ((Q3ListBoxItem *)item)->lbox = 0; + triggerUpdate(true); +} + +/*! + \internal + Finds the next item after start beginning with \a text. +*/ + +int Q3ListBoxPrivate::findItemByName(int start, const QString &text) +{ + if (start < 0 || (uint)start >= listBox->count()) + start = 0; + QString match = text.toLower(); + if (match.length() < 1) + return start; + QString curText; + int item = start; + do { + curText = listBox->text(item).toLower(); + if (curText.startsWith(match)) + return item; + item++; + if ((uint)item == listBox->count()) + item = 0; + } while (item != start); + return -1; +} + +/*! + \internal +*/ + +void Q3ListBox::clearInputString() +{ + d->currInputString.clear(); +} + +/*! + Finds the first list box item that has the text \a text and + returns it, or returns 0 of no such item could be found. If \c + ComparisonFlags are specified in \a compare then these flags + are used, otherwise the default is a case-insensitive, "begins + with" search. +*/ + +Q3ListBoxItem *Q3ListBox::findItem(const QString &text, ComparisonFlags compare) const +{ + if (text.isEmpty()) + return 0; + + if (compare == CaseSensitive || compare == 0) + compare |= ExactMatch; + + QString itmtxt; + QString comtxt = text; + if (!(compare & CaseSensitive)) + comtxt = text.toLower(); + + Q3ListBoxItem *item; + if (d->current) + item = d->current; + else + item = d->head; + + Q3ListBoxItem *beginsWithItem = 0; + Q3ListBoxItem *endsWithItem = 0; + Q3ListBoxItem *containsItem = 0; + + if (item) { + for (; item; item = item->n) { + if (!(compare & CaseSensitive)) + itmtxt = item->text().toLower(); + else + itmtxt = item->text(); + + if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt) + return item; + if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt)) + beginsWithItem = containsItem = item; + if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt)) + endsWithItem = containsItem = item; + if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt)) + containsItem = item; + } + + if (d->current && d->head) { + item = d->head; + for (; item && item != d->current; item = item->n) { + if (!(compare & CaseSensitive)) + itmtxt = item->text().toLower(); + else + itmtxt = item->text(); + + if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt) + return item; + if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt)) + beginsWithItem = containsItem = item; + if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt)) + endsWithItem = containsItem = item; + if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt)) + containsItem = item; + } + } + } + + // Obey the priorities + if (beginsWithItem) + return beginsWithItem; + else if (endsWithItem) + return endsWithItem; + else if (containsItem) + return containsItem; + return 0; +} + +/*! + \internal +*/ + +void Q3ListBox::drawRubber() +{ + if (!d->rubber) + return; + if (!d->rubber->width() && !d->rubber->height()) + return; + update(); +} + +/*! + \internal +*/ + +void Q3ListBox::doRubberSelection(const QRect &old, const QRect &rubber) +{ + Q3ListBoxItem *i = d->head; + QRect ir, pr; + bool changed = false; + for (; i; i = i->n) { + ir = itemRect(i); + if (ir == QRect(0, 0, -1, -1)) + continue; + if (i->isSelected() && !ir.intersects(rubber) && ir.intersects(old)) { + i->s = false; + pr = pr.united(ir); + changed = true; + } else if (!i->isSelected() && ir.intersects(rubber)) { + if (i->isSelectable()) { + i->s = true; + pr = pr.united(ir); + changed = true; + } + } + } + if (changed) { + emit selectionChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection); +#endif + } + viewport()->repaint(pr); +} + + +/*! + Returns true if the user is selecting items using a rubber band + rectangle; otherwise returns false. +*/ + +bool Q3ListBox::isRubberSelecting() const +{ + return d->rubber != 0; +} + + +/*! + Returns the item that comes after this in the list box. If this is + the last item, 0 is returned. + + \sa prev() +*/ + +Q3ListBoxItem *Q3ListBoxItem::next() const +{ + return n; +} + +/*! + Returns the item which comes before this in the list box. If this + is the first item, 0 is returned. + + \sa next() +*/ + +Q3ListBoxItem *Q3ListBoxItem::prev() const +{ + return p; +} + +/*! + Returns the first item in this list box. If the list box is empty, + returns 0. +*/ + +Q3ListBoxItem *Q3ListBox::firstItem() const +{ + return d->head; +} + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +#ifdef Q_OS_WINCE +static int _cdecl cmpListBoxItems(const void *n1, const void *n2) +#else +static int cmpListBoxItems(const void *n1, const void *n2) +#endif +{ + if (!n1 || !n2) + return 0; + + Q3ListBoxPrivate::SortableItem *i1 = (Q3ListBoxPrivate::SortableItem *)n1; + Q3ListBoxPrivate::SortableItem *i2 = (Q3ListBoxPrivate::SortableItem *)n2; + + return i1->item->text().localeAwareCompare(i2->item->text()); +} + +#if defined(Q_C_CALLBACKS) +} +#endif + +/*! + If \a ascending is true sorts the items in ascending order; + otherwise sorts in descending order. + + To compare the items, the text (Q3ListBoxItem::text()) of the items + is used. +*/ + +void Q3ListBox::sort(bool ascending) +{ + if (count() == 0) + return; + + d->cache = 0; + + Q3ListBoxPrivate::SortableItem *items = new Q3ListBoxPrivate::SortableItem[count()]; + + Q3ListBoxItem *item = d->head; + int i = 0; + for (; item; item = item->n) + items[i++].item = item; + + qsort(items, count(), sizeof(Q3ListBoxPrivate::SortableItem), cmpListBoxItems); + + Q3ListBoxItem *prev = 0; + item = 0; + if (ascending) { + for (i = 0; i < (int)count(); ++i) { + item = items[i].item; + if (item) { + item->p = prev; + item->dirty = true; + if (item->p) + item->p->n = item; + item->n = 0; + } + if (i == 0) + d->head = item; + prev = item; + } + } else { + for (i = (int)count() - 1; i >= 0 ; --i) { + item = items[i].item; + if (item) { + item->p = prev; + item->dirty = true; + if (item->p) + item->p->n = item; + item->n = 0; + } + if (i == (int)count() - 1) + d->head = item; + prev = item; + } + } + d->last = item; + + delete [] items; + + // We have to update explicitly in case the current "vieport" overlaps the + // new viewport we set (starting at (0,0)). + bool haveToUpdate = contentsX() < visibleWidth() || contentsY() < visibleHeight(); + setContentsPos(0, 0); + if (haveToUpdate) + updateContents(0, 0, visibleWidth(), visibleHeight()); +} + +void Q3ListBox::handleItemChange(Q3ListBoxItem *old, bool shift, bool control) +{ + if (d->selectionMode == Single) { + // nothing + } else if (d->selectionMode == Extended) { + if (shift) { + selectRange(d->selectAnchor ? d->selectAnchor : old, + d->current, false, true, (d->selectAnchor && !control) ? true : false); + } else if (!control) { + bool block = signalsBlocked(); + blockSignals(true); + selectAll(false); + blockSignals(block); + setSelected(d->current, true); + } + } else if (d->selectionMode == Multi) { + if (shift) + selectRange(old, d->current, true, false); + } +} + +void Q3ListBox::selectRange(Q3ListBoxItem *from, Q3ListBoxItem *to, bool invert, bool includeFirst, bool clearSel) +{ + if (!from || !to) + return; + if (from == to && !includeFirst) + return; + Q3ListBoxItem *i = 0; + int index =0; + int f_idx = -1, t_idx = -1; + for (i = d->head; i; i = i->n, index++) { + if (i == from) + f_idx = index; + if (i == to) + t_idx = index; + if (f_idx != -1 && t_idx != -1) + break; + } + if (f_idx > t_idx) { + i = from; + from = to; + to = i; + if (!includeFirst) + to = to->prev(); + } else { + if (!includeFirst) + from = from->next(); + } + + bool changed = false; + if (clearSel) { + for (i = d->head; i && i != from; i = i->n) { + if (i->s) { + i->s = false; + changed = true; + updateItem(i); + } + } + for (i = to->n; i; i = i->n) { + if (i->s) { + i->s = false; + changed = true; + updateItem(i); + } + } + } + + for (i = from; i; i = i->next()) { + if (!invert) { + if (!i->s && i->isSelectable()) { + i->s = true; + changed = true; + updateItem(i); + } + } else { + bool sel = !i->s; + if (((bool)i->s != sel && sel && i->isSelectable()) || !sel) { + i->s = sel; + changed = true; + updateItem(i); + } + } + if (i == to) + break; + } + if (changed) { + emit selectionChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection); +#endif + } +} + +/*! \reimp */ +void Q3ListBox::changeEvent(QEvent *ev) +{ + if (ev->type() == QEvent::ActivationChange) { + if (!isActiveWindow() && d->scrollTimer) + d->scrollTimer->stop(); + if (!palette().isEqual(QPalette::Active, QPalette::Inactive)) + viewport()->update(); + } + Q3ScrollView::changeEvent(ev); + + if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange) + triggerUpdate(true); +} + +/*! + Returns 0. + + Make your derived classes return their own values for rtti(), and + you can distinguish between listbox items. You should use values + greater than 1000 preferably a large random number, to allow for + extensions to this class. +*/ + +int Q3ListBoxItem::rtti() const +{ + return RTTI; +} + +/*! + \fn bool Q3ListBox::dragSelect() const + + Returns true. Dragging always selects. +*/ + +/*! + \fn void Q3ListBox::setDragSelect(bool b) + + Does nothing. Dragging always selects. The \a b parameter is ignored. +*/ + +/*! + \fn bool Q3ListBox::autoScroll() const + + Use dragAutoScroll() instead. This function always returns true. +*/ + +/*! + \fn void Q3ListBox::setAutoScroll(bool b) + + Use setDragAutoScroll(\a b) instead. +*/ + +/*! + \fn bool Q3ListBox::autoScrollBar() const + + Use vScrollBarMode() instead. Returns true if the vertical + scrollbar mode is \c Auto. +*/ + +/*! + \fn void Q3ListBox::setAutoScrollBar(bool enable) + + Use setVScrollBarMode() instead. + + If \a enable is true, pass \c Auto as the argument to + setVScrollBarMode(); otherwise, pass \c AlwaysOff. +*/ + +/*! + \fn bool Q3ListBox::scrollBar() const + + Use vScrollBarMode() instead. Returns true if the vertical + scrollbar mode is not \c AlwaysOff. +*/ + +/*! + \fn void Q3ListBox::setScrollBar(bool enable) + + Use setVScrollBarMode() instead. + + If \a enable is true, pass \c AlwaysOn as the argument to + setVScrollBarMode(); otherwise, pass \c AlwaysOff. +*/ + +/*! + \fn bool Q3ListBox::autoBottomScrollBar() const + + Use hScrollBarMode() instead. Returns true if the horizontal + scrollbar mode is set to \c Auto. +*/ + +/*! + \fn void Q3ListBox::setAutoBottomScrollBar(bool enable) + + Use setHScrollBarMode() instead. + + If \a enable is true, pass \c Auto as the argument to + setHScrollBarMode(); otherwise, pass \c AlwaysOff. +*/ + +/*! + \fn bool Q3ListBox::bottomScrollBar() const + + Use hScrollBarMode() instead. Returns true if the horizontal + scrollbar mode is not \c AlwaysOff. +*/ + +/*! + \fn void Q3ListBox::setBottomScrollBar(bool enable) + + Use setHScrollBarMode() instead. + + If \a enable is true, pass \c AlwaysOn as the argument to + setHScrollBarMode(); otherwise, pass \c AlwaysOff. +*/ + +/*! + \fn bool Q3ListBox::smoothScrolling() const + + Returns false. Qt always scrolls smoothly. +*/ + +/*! + \fn void Q3ListBox::setSmoothScrolling(bool b) + + Does nothing. Qt always scrolls smoothly. The \a b parameter is + ignored. +*/ + +/*! + \fn bool Q3ListBox::autoUpdate() const + + Returns true. Qt always updates automatically. +*/ + +/*! + \fn void Q3ListBox::setAutoUpdate(bool b) + + Does nothing. Qt always updates automatically. The \a b parameter + is ignored. +*/ + +/*! + \fn void Q3ListBox::setFixedVisibleLines(int lines) + + Use setRowMode(\a lines) instead. +*/ + +/*! + \fn int Q3ListBox::cellHeight(int i) const + + Use itemHeight(\a i) instead. +*/ + +/*! + \fn int Q3ListBox::cellHeight() const + + Use itemHeight() instead. +*/ + +/*! + \fn int Q3ListBox::cellWidth() const + + Use maxItemWidth() instead. +*/ + +/*! + \fn int Q3ListBox::cellWidth(int i) const + + Use maxItemWidth(\a i) instead. +*/ + +/*! + \fn int Q3ListBox::numCols() const + + Use numColumns() instead. +*/ + +/*! + \fn void Q3ListBox::updateCellWidth() + + Does nothing. Qt automatically updates. +*/ + +/*! + \fn int Q3ListBox::totalWidth() const + + Use contentsWidth() instead. +*/ + +/*! + \fn int Q3ListBox::totalHeight() const + + Use contentsHeight() instead. +*/ + +/*! + \fn int Q3ListBox::findItem(int yPos) const + + Use index(itemAt(\a yPos)) instead. +*/ + +/*! + \fn bool Q3ListBoxItem::selected() const + + Use isSelected() instead. Returns true if isSelected() + returns true. +*/ + +/*! + \fn bool Q3ListBoxItem::current() const + + Use isCurrent() instead. Returns true if isCurrent() + returns true. +*/ + +/*! + \enum Q3ListBox::StringComparisonMode + + This enum type is used to set the string comparison mode when + searching for an item. We'll refer to the string being searched + as the 'target' string. + + \value CaseSensitive The strings must match case sensitively. + \value ExactMatch The target and search strings must match exactly. + \value BeginsWith The target string begins with the search string. + \value EndsWith The target string ends with the search string. + \value Contains The target string contains the search string. + + If you OR these flags together (excluding \c CaseSensitive), the + search criteria be applied in the following order: \c ExactMatch, + \c BeginsWith, \c EndsWith, \c Contains. + + Matching is case-insensitive unless \c CaseSensitive is set. \c + CaseSensitive can be OR-ed with any combination of the other + flags. + + \sa ComparisonFlags +*/ + +/*! + \typedef Q3ListBox::ComparisonFlags + + This typedef is used in Q3IconView's API for values that are OR'd + combinations of \l StringComparisonMode values. + + \sa StringComparisonMode +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_LISTBOX diff --git a/src/qt3support/itemviews/q3listbox.h b/src/qt3support/itemviews/q3listbox.h new file mode 100644 index 0000000..376dd65 --- /dev/null +++ b/src/qt3support/itemviews/q3listbox.h @@ -0,0 +1,429 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3LISTBOX_H +#define Q3LISTBOX_H + +#include <Qt3Support/q3scrollview.h> +#include <QtGui/qpixmap.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_LISTBOX + +class Q3ListBoxPrivate; +class Q3ListBoxItem; +class QString; +class QStringList; + +class Q_COMPAT_EXPORT Q3ListBox : public Q3ScrollView +{ + friend class Q3ListBoxItem; + friend class Q3ListBoxPrivate; + + Q_OBJECT + Q_ENUMS(SelectionMode LayoutMode) + Q_PROPERTY(uint count READ count) + Q_PROPERTY(int numItemsVisible READ numItemsVisible) + Q_PROPERTY(int currentItem READ currentItem WRITE setCurrentItem USER true) + Q_PROPERTY(QString currentText READ currentText) + Q_PROPERTY(int topItem READ topItem WRITE setTopItem DESIGNABLE false) + Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) + Q_PROPERTY(bool multiSelection READ isMultiSelection WRITE setMultiSelection DESIGNABLE false) + Q_PROPERTY(LayoutMode columnMode READ columnMode WRITE setColumnMode) + Q_PROPERTY(LayoutMode rowMode READ rowMode WRITE setRowMode) + Q_PROPERTY(int numColumns READ numColumns) + Q_PROPERTY(int numRows READ numRows) + Q_PROPERTY(bool variableWidth READ variableWidth WRITE setVariableWidth) + Q_PROPERTY(bool variableHeight READ variableHeight WRITE setVariableHeight) + +public: + Q3ListBox(QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0 ); + ~Q3ListBox(); + + uint count() const; + + void insertStringList(const QStringList&, int index=-1); +// ### fix before Qt 4.0 +#if 0 + void insertStrList(const QStrList *, int index=-1); + void insertStrList(const QStrList &, int index=-1); +#endif + void insertStrList(const char **, + int numStrings=-1, int index=-1); + + void insertItem(const Q3ListBoxItem *, int index=-1); + void insertItem(const Q3ListBoxItem *, const Q3ListBoxItem *after); + void insertItem(const QString &text, int index=-1); + void insertItem(const QPixmap &pixmap, int index=-1); + void insertItem(const QPixmap &pixmap, const QString &text, int index=-1); + + void removeItem(int index); + + QString text(int index) const; + const QPixmap *pixmap(int index) const; + + void changeItem(const Q3ListBoxItem *, int index); + void changeItem(const QString &text, int index); + void changeItem(const QPixmap &pixmap, int index); + void changeItem(const QPixmap &pixmap, const QString &text, int index); + + void takeItem(const Q3ListBoxItem *); + + int numItemsVisible() const; + + int currentItem() const; + QString currentText() const { return text(currentItem()); } + virtual void setCurrentItem(int index); + virtual void setCurrentItem(Q3ListBoxItem *); + void centerCurrentItem() { ensureCurrentVisible(); } + int topItem() const; + virtual void setTopItem(int index); + virtual void setBottomItem(int index); + + long maxItemWidth() const; + + enum SelectionMode { Single, Multi, Extended, NoSelection }; + virtual void setSelectionMode(SelectionMode); + SelectionMode selectionMode() const; + + void setMultiSelection(bool multi); + bool isMultiSelection() const; + + QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + virtual void setSelected(Q3ListBoxItem *, bool); + void setSelected(int, bool); + bool isSelected(int) const; + bool isSelected(const Q3ListBoxItem *) const; + Q3ListBoxItem* selectedItem() const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + Q3ListBoxItem *item(int index) const; + int index(const Q3ListBoxItem *) const; + + enum StringComparisonMode { + CaseSensitive = 0x00001, // 0 0001 + BeginsWith = 0x00002, // 0 0010 + EndsWith = 0x00004, // 0 0100 + Contains = 0x00008, // 0 1000 + ExactMatch = 0x00010 // 1 0000 + }; + typedef uint ComparisonFlags; + Q3ListBoxItem *findItem(const QString &text, ComparisonFlags compare = BeginsWith) const; + + void triggerUpdate(bool doLayout); + + bool itemVisible(int index); + bool itemVisible(const Q3ListBoxItem *); + + enum LayoutMode { FixedNumber, + FitToWidth, FitToHeight = FitToWidth, + Variable }; + virtual void setColumnMode(LayoutMode); + virtual void setColumnMode(int); + virtual void setRowMode(LayoutMode); + virtual void setRowMode(int); + + LayoutMode columnMode() const; + LayoutMode rowMode() const; + + int numColumns() const; + int numRows() const; + + bool variableWidth() const; + virtual void setVariableWidth(bool); + + bool variableHeight() const; + virtual void setVariableHeight(bool); + + void viewportPaintEvent(QPaintEvent *); + + bool dragSelect() const { return true; } + void setDragSelect(bool) {} + bool autoScroll() const { return true; } + void setAutoScroll(bool) {} + bool autoScrollBar() const { return vScrollBarMode() == Auto; } + void setAutoScrollBar(bool enable) { setVScrollBarMode(enable ? Auto : AlwaysOff); } + bool scrollBar() const { return vScrollBarMode() != AlwaysOff; } + void setScrollBar(bool enable) { setVScrollBarMode(enable ? AlwaysOn : AlwaysOff); } + bool autoBottomScrollBar() const { return hScrollBarMode() == Auto; } + void setAutoBottomScrollBar(bool enable) { setHScrollBarMode(enable ? Auto : AlwaysOff); } + bool bottomScrollBar() const { return hScrollBarMode() != AlwaysOff; } + void setBottomScrollBar(bool enable) { setHScrollBarMode(enable ? AlwaysOn : AlwaysOff); } + bool smoothScrolling() const { return false; } + void setSmoothScrolling(bool) {} + bool autoUpdate() const { return true; } + void setAutoUpdate(bool) {} + void setFixedVisibleLines(int lines) { setRowMode(lines); } + int inSort(const Q3ListBoxItem *); + int inSort(const QString& text); + int cellHeight(int i) const { return itemHeight(i); } + int cellHeight() const { return itemHeight(); } + int cellWidth() const { return maxItemWidth(); } + int cellWidth(int i) const { Q_ASSERT(i==0); Q_UNUSED(i) return maxItemWidth(); } + int numCols() const { return numColumns(); } + + int itemHeight(int index = 0) const; + Q3ListBoxItem * itemAt(const QPoint &) const; + + QRect itemRect(Q3ListBoxItem *item) const; + + Q3ListBoxItem *firstItem() const; + + void sort(bool ascending = true); + +public Q_SLOTS: + void clear(); + virtual void ensureCurrentVisible(); + virtual void clearSelection(); + virtual void selectAll(bool select); + virtual void invertSelection(); + +Q_SIGNALS: + void highlighted(int index); + void selected(int index); + void highlighted(const QString &); + void selected(const QString &); + void highlighted(Q3ListBoxItem *); + void selected(Q3ListBoxItem *); + + void selectionChanged(); + void selectionChanged(Q3ListBoxItem *); + void currentChanged(Q3ListBoxItem *); + void clicked(Q3ListBoxItem *); + void clicked(Q3ListBoxItem *, const QPoint &); + void pressed(Q3ListBoxItem *); + void pressed(Q3ListBoxItem *, const QPoint &); + + void doubleClicked(Q3ListBoxItem *); + void returnPressed(Q3ListBoxItem *); + void rightButtonClicked(Q3ListBoxItem *, const QPoint &); + void rightButtonPressed(Q3ListBoxItem *, const QPoint &); + void mouseButtonPressed(int, Q3ListBoxItem*, const QPoint&); + void mouseButtonClicked(int, Q3ListBoxItem*, const QPoint&); + + void contextMenuRequested(Q3ListBoxItem *, const QPoint &); + + void onItem(Q3ListBoxItem *item); + void onViewport(); + +protected: + void changeEvent(QEvent *); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void mouseDoubleClickEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + void contentsContextMenuEvent(QContextMenuEvent *); + + void keyPressEvent(QKeyEvent *e); + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); + void resizeEvent(QResizeEvent *); + void showEvent(QShowEvent *); + + bool eventFilter(QObject *o, QEvent *e); + + void updateItem(int index); + void updateItem(Q3ListBoxItem *); + + void updateCellWidth() { } + int totalWidth() const { return contentsWidth(); } + int totalHeight() const { return contentsHeight(); } + + virtual void paintCell(QPainter *, int row, int col); + + void toggleCurrentItem(); + bool isRubberSelecting() const; + + void doLayout() const; + + int findItem(int yPos) const { return index(itemAt(QPoint(0,yPos))); } + +protected Q_SLOTS: + void clearInputString(); + +private Q_SLOTS: + void refreshSlot(); + void doAutoScroll(); + void adjustItems(); + +private: + Q_DISABLE_COPY(Q3ListBox) + + void mousePressEventEx(QMouseEvent *); + void tryGeometry(int, int) const; + int currentRow() const; + int currentColumn() const; + void updateSelection(); + void repaintSelection(); + void drawRubber(); + void doRubberSelection(const QRect &old, const QRect &rubber); + void handleItemChange(Q3ListBoxItem *old, bool shift, bool control); + void selectRange(Q3ListBoxItem *from, Q3ListBoxItem *to, bool invert, bool includeFirst, bool clearSel = false); + + void emitChangedSignal(bool); + + int columnAt(int) const; + int rowAt(int) const; + + Q3ListBoxPrivate * d; + + static Q3ListBox * changedListBox; +}; + + +class Q_COMPAT_EXPORT Q3ListBoxItem +{ +public: + Q3ListBoxItem(Q3ListBox* listbox = 0); + Q3ListBoxItem(Q3ListBox* listbox, Q3ListBoxItem *after); + virtual ~Q3ListBoxItem(); + + virtual QString text() const; + virtual const QPixmap *pixmap() const; + + virtual int height(const Q3ListBox *) const; + virtual int width(const Q3ListBox *) const; + + bool isSelected() const { return s; } + bool isCurrent() const; + + bool selected() const { return isSelected(); } + bool current() const { return isCurrent(); } + + Q3ListBox *listBox() const; + + void setSelectable(bool b) { selectable = b; } + bool isSelectable() const { return selectable; } + + Q3ListBoxItem *next() const; + Q3ListBoxItem *prev() const; + + virtual int rtti() const; + enum { RTTI = 0 }; + +protected: + virtual void paint(QPainter *) = 0; + virtual void setText(const QString &text) { txt = text; } + void setCustomHighlighting(bool); + +private: + Q_DISABLE_COPY(Q3ListBoxItem) + + QString txt; + uint selectable : 1; + uint s : 1; + uint dirty:1; + uint custom_highlight : 1; + uint unused : 28; + Q3ListBoxItem * p, * n; + Q3ListBox* lbox; + friend class Q3ListBox; + friend class Q3ListBoxPrivate; + friend class Q3ComboBox; + friend class Q3ComboBoxPopupItem; +}; + + +class Q_COMPAT_EXPORT Q3ListBoxText : public Q3ListBoxItem +{ +public: + Q3ListBoxText(Q3ListBox* listbox, const QString & text=QString()); + Q3ListBoxText(const QString & text=QString()); + Q3ListBoxText(Q3ListBox* listbox, const QString & text, Q3ListBoxItem *after); + ~Q3ListBoxText(); + + int height(const Q3ListBox *) const; + int width(const Q3ListBox *) const; + + int rtti() const; + enum { RTTI = 1 }; + +protected: + void paint(QPainter *); + +private: + Q_DISABLE_COPY(Q3ListBoxText) +}; + + +class Q_COMPAT_EXPORT Q3ListBoxPixmap : public Q3ListBoxItem +{ +public: + Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap &); + Q3ListBoxPixmap(const QPixmap &); + Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap & pix, Q3ListBoxItem *after); + Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap &, const QString&); + Q3ListBoxPixmap(const QPixmap &, const QString&); + Q3ListBoxPixmap(Q3ListBox* listbox, const QPixmap & pix, const QString&, Q3ListBoxItem *after); + ~Q3ListBoxPixmap(); + + const QPixmap *pixmap() const { return ± } + + int height(const Q3ListBox *) const; + int width(const Q3ListBox *) const; + + int rtti() const; + enum { RTTI = 2 }; + +protected: + void paint(QPainter *); + +private: + Q_DISABLE_COPY(Q3ListBoxPixmap) + + QPixmap pm; +}; + +#endif // QT_NO_LISTBOX + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3LISTBOX_H diff --git a/src/qt3support/itemviews/q3listview.cpp b/src/qt3support/itemviews/q3listview.cpp new file mode 100644 index 0000000..1ebee42 --- /dev/null +++ b/src/qt3support/itemviews/q3listview.cpp @@ -0,0 +1,7948 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qplatformdefs.h> +#include "q3listview.h" +#ifndef QT_NO_LISTVIEW +#include "q3tl.h" +#include "qapplication.h" +#include "qbitmap.h" +#include "q3cleanuphandler.h" +#include "qcursor.h" +#include "qdatetime.h" +#include "q3dragobject.h" +#include "qevent.h" +#include "qhash.h" +#include "q3header.h" +#include "qicon.h" +#include "qlineedit.h" +#include "qpainter.h" +#include "qpixmapcache.h" +#include "qstack.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtimer.h" +#include "qtooltip.h" +#include "qdebug.h" +#ifndef QT_NO_ACCESSIBILITY +#include "qaccessible.h" +#endif + +QT_BEGIN_NAMESPACE + +const int Unsorted = 16383; + +static Q3CleanupHandler<QBitmap> qlv_cleanup_bitmap; + + +struct Q3ListViewPrivate +{ + // classes that are here to avoid polluting the global name space + + // the magical hidden mother of all items + class Root: public Q3ListViewItem { + public: + Root(Q3ListView * parent); + + void setHeight(int); + void invalidateHeight(); + void setup(); + Q3ListView * theListView() const; + + Q3ListView * lv; + }; + + // to remember what's on screen + class DrawableItem { + public: + DrawableItem() {} + DrawableItem(int level, int ypos, Q3ListViewItem * item) + : l(level), y(ypos), i(item) {}; + int l; + int y; + Q3ListViewItem * i; + }; + + // for sorting + class SortableItem { + public: + /* + We could be smarter and keep a pointer to the Q3ListView + item instead of numCols, col and asc. This would then allow + us to use the physical ordering of columns rather than the + logical. Microsoft uses the logical ordering, so there is + some virtue in doing so, although it prevents the user from + choosing the secondary key. + */ + Q3ListViewItem * item; + int numCols; + int col; + bool asc; + + int cmp(const SortableItem& i) const { + int diff = item->compare(i.item, col, asc); + if (diff == 0 && numCols != 1) { + for (int j = 0; j < numCols; j++) { + if (j != col) { + diff = item->compare(i.item, j, asc); + if (diff != 0) + break; + } + } + } + return diff; + } + bool operator<(const SortableItem& i) const { return cmp(i) < 0; } + bool operator<=(const SortableItem& i) const { return cmp(i) <= 0; } + bool operator>(const SortableItem& i) const { return cmp(i) > 0; } + }; + + class ItemColumnInfo { + public: + ItemColumnInfo(): pm(0), next(0), truncated(false), dirty(false), allow_rename(false), width(0) {} + ~ItemColumnInfo() { delete pm; delete next; } + QString text, tmpText; + QPixmap * pm; + ItemColumnInfo * next; + uint truncated : 1; + uint dirty : 1; + uint allow_rename : 1; + int width; + }; + + class ViewColumnInfo { + public: + ViewColumnInfo(): align(Qt::AlignAuto), sortable(true), next(0) {} + ~ViewColumnInfo() { delete next; } + int align; + bool sortable; + ViewColumnInfo * next; + }; + + // private variables used in Q3ListView + ViewColumnInfo * vci; + Q3Header * h; + Root * r; + uint rootIsExpandable : 1; + int margin; + + Q3ListViewItem * focusItem, *highlighted, *oldFocusItem; + + QTimer * timer; + QTimer * dirtyItemTimer; + QTimer * visibleTimer; + int levelWidth; + + // the list of drawables, and the range drawables covers entirely + // (it may also include a few items above topPixel) + QList<DrawableItem> drawables; + int topPixel; + int bottomPixel; + + QList<const Q3ListViewItem *> dirtyItems; + + Q3ListView::SelectionMode selectionMode; + + // Per-column structure for information not in the Q3Header + struct Column { + Q3ListView::WidthMode wmode; + }; + QVector<Column> column; + + // suggested height for the items + int fontMetricsHeight; + int minLeftBearing, minRightBearing; + int ellipsisWidth; + + // currently typed prefix for the keyboard interface, and the time + // of the last key-press + QString currentPrefix; + QTime currentPrefixTime; + + // holds a list of iterators + QList<Q3ListViewItemIterator *> iterators; + Q3ListViewItem *pressedItem, *selectAnchor; + + QTimer *scrollTimer; + QTimer *renameTimer; + QTimer *autoopenTimer; + + // sort column and order #### may need to move to Q3Header [subclass] + int sortcolumn; + bool ascending :1; + bool sortIndicator :1; + // whether to select or deselect during this mouse press. + bool allColumnsShowFocus :1; + bool select :1; + + // true if the widget should take notice of mouseReleaseEvent + bool buttonDown :1; + // true if the widget should ignore a double-click + bool ignoreDoubleClick :1; + + bool clearing :1; + bool pressedSelected :1; + bool pressedEmptyArea :1; + + bool toolTips :1; + bool fullRepaintOnComlumnChange:1; + bool updateHeader :1; + + bool startEdit : 1; + bool ignoreEditAfterFocus : 1; + bool inMenuMode :1; + + Q3ListView::RenameAction defRenameAction; + + Q3ListViewItem *startDragItem; + QPoint dragStartPos; + int pressedColumn; + Q3ListView::ResizeMode resizeMode; +}; + +Q_DECLARE_TYPEINFO(Q3ListViewPrivate::DrawableItem, Q_PRIMITIVE_TYPE); + +// these should probably be in Q3ListViewPrivate, for future thread safety +static bool activatedByClick; +static QPoint activatedP; + +#ifndef QT_NO_ACCESSIBILITY +static int indexOfItem(Q3ListViewItem *item) +{ + if (!QAccessible::isActive()) + return 0; + + static Q3ListViewItem *lastItem = 0; + static int lastIndex = 0; + + if (!item || !item->listView()) + return 0; + + if (item == lastItem) + return lastIndex; + + lastItem = item; + int index = 1; + + Q3ListViewItemIterator it(item->listView()); + while (it.current()) { + if (it.current() == item) { + lastIndex = index; + return index; + } + ++it; + ++index; + } + lastIndex = 0; + return 0; +} +#endif + +/*! + Creates a string with ... like "Trollte..." or "...olltech", depending on the alignment. +*/ +static QString qEllipsisText(const QString &org, const QFontMetrics &fm, int width, int align) +{ + int ellWidth = fm.width(QLatin1String("...")); + QString text = QString::fromLatin1(""); + int i = 0; + int len = org.length(); + int offset = (align & Qt::AlignRight) ? (len-1) - i : i; + while (i < len && fm.width(text + org[offset]) + ellWidth < width) { + if (align & Qt::AlignRight) + text.prepend(org[offset]); + else + text += org[offset]; + offset = (align & Qt::AlignRight) ? (len-1) - ++i : ++i; + } + if (text.isEmpty()) + text = (align & Qt::AlignRight) ? org.right(1) : text = org.left(1); + if (align & Qt::AlignRight) + text.prepend(QLatin1String("...")); + else + text += QLatin1String("..."); + return text; +} + +/*! + \class Q3ListViewItem + \brief The Q3ListViewItem class implements a list view item. + + \compat + + A list view item is a multi-column object capable of displaying + itself in a Q3ListView. + + The easiest way to use Q3ListViewItem is to construct one with a + few constant strings, and either a Q3ListView or another + Q3ListViewItem as parent. + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 0 + We've discarded the pointers to the items since we can still access + them via their parent \e listView. By default, Q3ListView sorts its + items; this can be switched off with Q3ListView::setSorting(-1). + + The parent must be another Q3ListViewItem or a Q3ListView. If the + parent is a Q3ListView, the item becomes a top-level item within + that Q3ListView. If the parent is another Q3ListViewItem, the item + becomes a child of that list view item. + + If you keep the pointer, you can set or change the texts using + setText(), add pixmaps using setPixmap(), change its mode using + setSelectable(), setSelected(), setOpen() and setExpandable(). + You'll also be able to change its height using setHeight(), and + traverse its sub-items. You don't have to keep the pointer since + you can get a pointer to any Q3ListViewItem in a Q3ListView using + Q3ListView::selectedItem(), Q3ListView::currentItem(), + Q3ListView::firstChild(), Q3ListView::lastItem() and + Q3ListView::findItem(). + + If you call \c delete on a list view item, it will be deleted as + expected, and as usual for \l{QObject}s, if it has any child items + (to any depth), all these will be deleted too. + + \l{Q3CheckListItem}s are list view items that have a checkbox or + radio button and can be used in place of plain Q3ListViewItems. + + You can traverse the tree as if it were a doubly-linked list using + itemAbove() and itemBelow(); they return pointers to the items + directly above and below this item on the screen (even if none of + them are actually visible at the moment). + + Here's how to traverse all of an item's children (but not its + children's children, etc.): + Example: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 1 + + If you want to iterate over every item, to any level of depth use + an iterator. To iterate over the entire tree, initialize the + iterator with the list view itself; to iterate over an item's + children (and children's children to any depth), initialize the + iterator with the item: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 2 + + Note that the order of the children will change when the sorting + order changes and is undefined if the items are not visible. You + can, however, call enforceSortOrder() at any time; Q3ListView will + always call it before it needs to show an item. + + Many programs will need to reimplement Q3ListViewItem. The most + commonly reimplemented functions are: + \table + \header \i Function \i Description + \row \i \l text() + \i Returns the text in a column. Many subclasses will compute + this on the fly. + \row \i \l key() + \i Used for sorting. The default key() simply calls + text(), but judicious use of key() can give you fine + control over sorting; for example, QFileDialog + reimplements key() to sort by date. + \row \i \l setup() + \i Called before showing the item and whenever the list + view's font changes, for example. + \row \i \l activate() + \i Called whenever the user clicks on the item or presses + Space when the item is the current item. + \endtable + + Some subclasses call setExpandable(true) even when they have no + children, and populate themselves when setup() or setOpen(true) is + called. The \c dirview/dirview.cpp example program uses this + technique to start up quickly: The files and subdirectories in a + directory aren't inserted into the tree until they're actually + needed. + + \img qlistviewitems.png List View Items + + \sa Q3CheckListItem Q3ListView +*/ + +/*! + \fn int Q3CheckListItem::rtti() const + + Returns 1. + + Make your derived classes return their own values for rtti(), and + you can distinguish between list view items. You should use values + greater than 1000, to allow for extensions to this class. +*/ + +/*! + Constructs a new top-level list view item in the Q3ListView \a + parent. +*/ + +Q3ListViewItem::Q3ListViewItem(Q3ListView * parent) +{ + init(); + parent->insertItem(this); +} + + +/*! + Constructs a new list view item that is a child of \a parent and + first in the parent's list of children. +*/ + +Q3ListViewItem::Q3ListViewItem(Q3ListViewItem * parent) +{ + init(); + parent->insertItem(this); +} + + + + +/*! + Constructs an empty list view item that is a child of \a parent + and is after item \a after in the parent's list of children. Since + \a parent is a Q3ListView the item will be a top-level item. +*/ + +Q3ListViewItem::Q3ListViewItem(Q3ListView * parent, Q3ListViewItem * after) +{ + init(); + parent->insertItem(this); + moveToJustAfter(after); +} + + +/*! + Constructs an empty list view item that is a child of \a parent + and is after item \a after in the parent's list of children. +*/ + +Q3ListViewItem::Q3ListViewItem(Q3ListViewItem * parent, Q3ListViewItem * after) +{ + init(); + parent->insertItem(this); + moveToJustAfter(after); +} + + + +/*! + Constructs a new top-level list view item in the Q3ListView \a + parent, with up to eight constant strings, \a label1, \a label2, \a + label3, \a label4, \a label5, \a label6, \a label7 and \a label8 + defining its columns' contents. + + \sa setText() +*/ + +Q3ListViewItem::Q3ListViewItem(Q3ListView * parent, + const QString &label1, + const QString &label2, + const QString &label3, + const QString &label4, + const QString &label5, + const QString &label6, + const QString &label7, + const QString &label8) +{ + init(); + parent->insertItem(this); + + setText(0, label1); + setText(1, label2); + setText(2, label3); + setText(3, label4); + setText(4, label5); + setText(5, label6); + setText(6, label7); + setText(7, label8); +} + + +/*! + Constructs a new list view item as a child of the Q3ListViewItem \a + parent with up to eight constant strings, \a label1, \a label2, \a + label3, \a label4, \a label5, \a label6, \a label7 and \a label8 + as columns' contents. + + \sa setText() +*/ + +Q3ListViewItem::Q3ListViewItem(Q3ListViewItem * parent, + const QString &label1, + const QString &label2, + const QString &label3, + const QString &label4, + const QString &label5, + const QString &label6, + const QString &label7, + const QString &label8) +{ + init(); + parent->insertItem(this); + + setText(0, label1); + setText(1, label2); + setText(2, label3); + setText(3, label4); + setText(4, label5); + setText(5, label6); + setText(6, label7); + setText(7, label8); +} + +/*! + Constructs a new list view item in the Q3ListView \a parent that is + included after item \a after and that has up to eight column + texts, \a label1, \a label2, \a label3, \a label4, \a label5, \a + label6, \a label7 and\a label8. + + Note that the order is changed according to Q3ListViewItem::key() + unless the list view's sorting is disabled using + Q3ListView::setSorting(-1). + + \sa setText() +*/ + +Q3ListViewItem::Q3ListViewItem(Q3ListView * parent, Q3ListViewItem * after, + const QString &label1, + const QString &label2, + const QString &label3, + const QString &label4, + const QString &label5, + const QString &label6, + const QString &label7, + const QString &label8) +{ + init(); + parent->insertItem(this); + moveToJustAfter(after); + + setText(0, label1); + setText(1, label2); + setText(2, label3); + setText(3, label4); + setText(4, label5); + setText(5, label6); + setText(6, label7); + setText(7, label8); +} + + +/*! + Constructs a new list view item as a child of the Q3ListViewItem \a + parent. It is inserted after item \a after and may contain up to + eight strings, \a label1, \a label2, \a label3, \a label4, \a + label5, \a label6, \a label7 and \a label8 as column entries. + + Note that the order is changed according to Q3ListViewItem::key() + unless the list view's sorting is disabled using + Q3ListView::setSorting(-1). + + \sa setText() +*/ + +Q3ListViewItem::Q3ListViewItem(Q3ListViewItem * parent, Q3ListViewItem * after, + const QString &label1, + const QString &label2, + const QString &label3, + const QString &label4, + const QString &label5, + const QString &label6, + const QString &label7, + const QString &label8) +{ + init(); + parent->insertItem(this); + moveToJustAfter(after); + + setText(0, label1); + setText(1, label2); + setText(2, label3); + setText(3, label4); + setText(4, label5); + setText(5, label6); + setText(6, label7); + setText(7, label8); +} + +/*! + Sorts all this item's child items using the current sorting + configuration (sort column and direction). + + \sa enforceSortOrder() +*/ + +void Q3ListViewItem::sort() +{ + if (!listView()) + return; + lsc = Unsorted; + enforceSortOrder(); + listView()->triggerUpdate(); +} + +/*! + Returns 0. + + Make your derived classes return their own values for rtti(), so + that you can distinguish between different kinds of list view + items. You should use values greater than 1000 to allow for + extensions to this class. +*/ + +int Q3ListViewItem::rtti() const +{ + return RTTI; +} + +/* + Performs the initializations that's common to the constructors. +*/ + +void Q3ListViewItem::init() +{ + ownHeight = 0; + maybeTotalHeight = -1; + open = false; + + nChildren = 0; + parentItem = 0; + siblingItem = childItem = 0; + + columns = 0; + + selected = 0; + selectable = true; + + lsc = Unsorted; + lso = true; // unsorted in ascending order :) + configured = false; + expandable = false; + selectable = true; + is_root = false; + allow_drag = false; + allow_drop = false; + visible = true; + renameBox = 0; + enabled = true; + mlenabled = false; +} + +/*! + If \a b is true, the item is made visible; otherwise it is hidden. + + If the item is not visible, itemAbove() and itemBelow() will never + return this item, although you still can reach it by using e.g. + Q3ListViewItemIterator. +*/ + +void Q3ListViewItem::setVisible(bool b) +{ + if (b == (bool)visible) + return; + Q3ListView *lv = listView(); + if (!lv) + return; + if (b && parent() && !parent()->isVisible()) + return; + visible = b; + configured = false; + setHeight(0); + invalidateHeight(); + if (parent()) + parent()->invalidateHeight(); + else + lv->d->r->invalidateHeight(); + for (Q3ListViewItem *i = childItem; i; i = i->siblingItem) + i->setVisible(b); + if (lv) + lv->triggerUpdate(); +} + +/*! + Returns true if the item is visible; otherwise returns false. + + \sa setVisible() +*/ + +bool Q3ListViewItem::isVisible() const +{ + return (bool)visible; +} + +/*! + If \a b is true, this item can be in-place renamed in the column + \a col by the user; otherwise it cannot be renamed in-place. +*/ + +void Q3ListViewItem::setRenameEnabled(int col, bool b) +{ + Q3ListViewPrivate::ItemColumnInfo * l = (Q3ListViewPrivate::ItemColumnInfo*)columns; + if (!l) { + l = new Q3ListViewPrivate::ItemColumnInfo; + columns = (void*)l; + } + for(int c = 0; c < col; c++) { + if (!l->next) + l->next = new Q3ListViewPrivate::ItemColumnInfo; + l = l->next; + } + + if (!l) + return; + l->allow_rename = b; +} + +/*! + Returns true if this item can be in-place renamed in column \a + col; otherwise returns false. +*/ + +bool Q3ListViewItem::renameEnabled(int col) const +{ + Q3ListViewPrivate::ItemColumnInfo * l = (Q3ListViewPrivate::ItemColumnInfo*)columns; + if (!l) + return false; + + while(col && l) { + l = l->next; + col--; + } + + if (!l) + return false; + return (bool)l->allow_rename; +} + +/*! + If \a b is true the item is enabled; otherwise it is disabled. + Disabled items are drawn differently (e.g. grayed-out) and are not + accessible by the user. +*/ + +void Q3ListViewItem::setEnabled(bool b) +{ + if ((bool)enabled == b) + return; + enabled = b; + if (!enabled) + selected = false; + Q3ListView *lv = listView(); + if (lv) { + lv->triggerUpdate(); + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(lv->viewport(), indexOfItem(this), QAccessible::StateChanged); +#endif + } +} + +/*! + Returns true if this item is enabled; otherwise returns false. + + \sa setEnabled() +*/ + +bool Q3ListViewItem::isEnabled() const +{ + return (bool)enabled; +} + +/*! + If in-place renaming of this item is enabled (see + renameEnabled()), this function starts renaming the item in column + \a col, by creating and initializing an edit box. +*/ + +void Q3ListViewItem::startRename(int col) +{ + if (!renameEnabled(col)) + return; + if (renameBox) + cancelRename(col); + Q3ListView *lv = listView(); + if (!lv) + return; + + if (lv->d->renameTimer) + lv->d->renameTimer->stop(); + + lv->ensureItemVisible(this); + + if (lv->d->timer->isActive()) { + // make sure that pending calculations get finished + lv->d->timer->stop(); + lv->updateContents(); + } + + if (lv->currentItem() && lv->currentItem()->renameBox) { + if (lv->d->defRenameAction == Q3ListView::Reject) + lv->currentItem()->cancelRename(lv->currentItem()->renameCol); + else + lv->currentItem()->okRename(lv->currentItem()->renameCol); + } + + if (this != lv->currentItem()) + lv->setCurrentItem(this); + + QRect r = lv->itemRect(this); + r = QRect(lv->viewportToContents(r.topLeft()), r.size()); + r.setLeft(lv->header()->sectionPos(col)); + r.setWidth(qMin(lv->header()->sectionSize(col) - 1, + lv->contentsX() + lv->visibleWidth() - r.left())); + if (col == 0) + r.setLeft(r.left() + lv->itemMargin() + (depth() + (lv->rootIsDecorated() ? 1 : 0)) * lv->treeStepSize() - 1); + if (pixmap(col)) + r.setLeft(r.left() + pixmap(col)->width()); + if (r.x() - lv->contentsX() < 0) { + lv->scrollBy(r.x() - lv->contentsX(), 0); + r.setX(lv->contentsX()); + } else if ((lv->contentsX() + lv->visibleWidth()) < (r.x() + r.width())) { + lv->scrollBy((r.x() + r.width()) - (lv->contentsX() + lv->visibleWidth()), 0); + } + if (r.width() > lv->visibleWidth()) + r.setWidth(lv->visibleWidth()); + renameBox = new QLineEdit(lv->viewport(), "qt_renamebox"); + renameBox->setFrame(false); + renameBox->setText(text(col)); + renameBox->selectAll(); + renameBox->installEventFilter(lv); + lv->addChild(renameBox, r.x(), r.y()); + renameBox->resize(r.size()); + lv->viewport()->setFocusProxy(renameBox); + renameBox->setFocus(); + renameBox->show(); + renameCol = col; +} + +/*! + This function removes the rename box. +*/ + +void Q3ListViewItem::removeRenameBox() +{ + // Sanity, it should be checked by the functions calling this first anyway + Q3ListView *lv = listView(); + if (!lv || !renameBox) + return; + const bool resetFocus = lv->viewport()->focusProxy() == renameBox; + delete renameBox; + renameBox = 0; + if (resetFocus) { + lv->viewport()->setFocusProxy(lv); + lv->setFocus(); + } +} + +/*! + This function is called if the user presses Enter during in-place + renaming of the item in column \a col. + + \sa cancelRename() +*/ + +void Q3ListViewItem::okRename(int col) +{ + Q3ListView *lv = listView(); + if (!lv || !renameBox) + return; + setText(col, renameBox->text()); + removeRenameBox(); + + // we set the parent lsc to Unsorted if that column is the sorted one + if (parent() && (int)parent()->lsc == col) + parent()->lsc = Unsorted; + + emit lv->itemRenamed(this, col); + emit lv->itemRenamed(this, col, text(col)); +} + +/*! + This function is called if the user cancels in-place renaming of + this item in column \a col (e.g. by pressing Esc). + + \sa okRename() +*/ + +void Q3ListViewItem::cancelRename(int) +{ + Q3ListView *lv = listView(); + if (!lv || !renameBox) + return; + removeRenameBox(); +} + +/*! + Destroys the item, deleting all its children and freeing up all + allocated resources. +*/ + +Q3ListViewItem::~Q3ListViewItem() +{ + if (renameBox) { + delete renameBox; + renameBox = 0; + } + + Q3ListView *lv = listView(); + + if (lv) { + if (lv->d->oldFocusItem == this) + lv->d->oldFocusItem = 0; + if (lv->d->focusItem == this) + lv->d->focusItem = 0; + if (lv->d->highlighted == this) + lv->d->highlighted = 0; + if (lv->d->pressedItem == this) + lv->d->pressedItem = 0; + if (lv->d->selectAnchor == this) + lv->d->selectAnchor = 0; + for (int j = 0; j < lv->d->iterators.size(); ++j) { + Q3ListViewItemIterator *i = lv->d->iterators.at(j); + if (i->current() == this) + i->currentRemoved(); + } + } + + if (parentItem) + parentItem->takeItem(this); + Q3ListViewItem * i = childItem; + childItem = 0; + while (i) { + i->parentItem = 0; + Q3ListViewItem * n = i->siblingItem; + delete i; + i = n; + } + delete (Q3ListViewPrivate::ItemColumnInfo *)columns; +} + + +/*! + If \a b is true each of the item's columns may contain multiple + lines of text; otherwise each of them may only contain a single + line. +*/ + +void Q3ListViewItem::setMultiLinesEnabled(bool b) +{ + mlenabled = b; +} + +/*! + Returns true if the item can display multiple lines of text in its + columns; otherwise returns false. +*/ + +bool Q3ListViewItem::multiLinesEnabled() const +{ + return mlenabled; +} + +/*! + If \a allow is true, the list view starts a drag (see + Q3ListView::dragObject()) when the user presses and moves the mouse + on this item. +*/ + + +void Q3ListViewItem::setDragEnabled(bool allow) +{ + allow_drag = (uint)allow; +} + +/*! + If \a allow is true, the list view accepts drops onto the item; + otherwise drops are not allowed. +*/ + +void Q3ListViewItem::setDropEnabled(bool allow) +{ + allow_drop = (uint)allow; +} + +/*! + Returns true if this item can be dragged; otherwise returns false. + + \sa setDragEnabled() +*/ + +bool Q3ListViewItem::dragEnabled() const +{ + return (bool)allow_drag; +} + +/*! + Returns true if this item accepts drops; otherwise returns false. + + \sa setDropEnabled(), acceptDrop() +*/ + +bool Q3ListViewItem::dropEnabled() const +{ + return (bool)allow_drop; +} + +/*! + Returns true if the item can accept drops of type QMimeSource \a + mime; otherwise returns false. + + The default implementation does nothing and returns false. A + subclass must reimplement this to accept drops. +*/ + +bool Q3ListViewItem::acceptDrop(const QMimeSource *) const +{ + return false; +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + This function is called when something was dropped on the item. \a e + contains all the information about the drop. + + The default implementation does nothing, subclasses may need to + reimplement this function. +*/ + +void Q3ListViewItem::dropped(QDropEvent *e) +{ + Q_UNUSED(e); +} + +#endif + +/*! + This function is called when a drag enters the item's bounding + rectangle. + + The default implementation does nothing, subclasses may need to + reimplement this function. +*/ + +void Q3ListViewItem::dragEntered() +{ +} + +/*! + This function is called when a drag leaves the item's bounding + rectangle. + + The default implementation does nothing, subclasses may need to + reimplement this function. +*/ + +void Q3ListViewItem::dragLeft() +{ +} + +/*! + Inserts \a newChild into this list view item's list of children. + You should not need to call this function; it is called + automatically by the constructor of \a newChild. + + \warning If you are using \c Single selection mode, then you + should only insert unselected items. +*/ + +void Q3ListViewItem::insertItem(Q3ListViewItem * newChild) +{ + Q3ListView *lv = listView(); + if (lv && lv->currentItem() && lv->currentItem()->renameBox) { + if (lv->d->defRenameAction == Q3ListView::Reject) + lv->currentItem()->cancelRename(lv->currentItem()->renameCol); + else + lv->currentItem()->okRename(lv->currentItem()->renameCol); + } + + if (!newChild || newChild->parentItem == this) + return; + if (newChild->parentItem) + newChild->parentItem->takeItem(newChild); + if (open) + invalidateHeight(); + newChild->siblingItem = childItem; + childItem = newChild; + nChildren++; + newChild->parentItem = this; + lsc = Unsorted; + newChild->ownHeight = 0; + newChild->configured = false; + + if (lv && !lv->d->focusItem) { + lv->d->focusItem = lv->firstChild(); + lv->d->selectAnchor = lv->d->focusItem; + lv->repaintItem(lv->d->focusItem); + } +} + + +/*! + \fn void Q3ListViewItem::removeItem(Q3ListViewItem *item) + + Removes the given \a item. Use takeItem() instead. +*/ + + +/*! + Removes \a item from this object's list of children and causes an + update of the screen display. The item is not deleted. You should + not normally need to call this function because + Q3ListViewItem::~Q3ListViewItem() calls it. + + The normal way to delete an item is to use \c delete. + + If you need to move an item from one place in the hierarchy to + another you can use takeItem() to remove the item from the list + view and then insertItem() to put the item back in its new + position. + + If a taken item is part of a selection in \c Single selection + mode, it is unselected and selectionChanged() is emitted. If a + taken item is part of a selection in \c Multi or \c Extended + selection mode, it remains selected. + + \warning This function leaves \a item and its children in a state + where most member functions are unsafe. Only a few functions work + correctly on an item in this state, most notably insertItem(). The + functions that work on taken items are explicitly documented as + such. + + \sa Q3ListViewItem::insertItem() +*/ + +void Q3ListViewItem::takeItem(Q3ListViewItem * item) +{ + if (!item) + return; + + Q3ListView *lv = listView(); + if (lv && lv->currentItem() && lv->currentItem()->renameBox) { + if (lv->d->defRenameAction == Q3ListView::Reject) + lv->currentItem()->cancelRename(lv->currentItem()->renameCol); + else + lv->currentItem()->okRename(lv->currentItem()->renameCol); + } + bool emit_changed = false; + if (lv && !lv->d->clearing) { + if (lv->d->oldFocusItem == this) + lv->d->oldFocusItem = 0; + + for (int j = 0; j < lv->d->iterators.size(); ++j) { + Q3ListViewItemIterator *i = lv->d->iterators.at(j); + if (i->current() == item) + i->currentRemoved(); + } + + invalidateHeight(); + + if (lv->d && !lv->d->drawables.isEmpty()) + lv->d->drawables.clear(); + + if (!lv->d->dirtyItems.isEmpty()) { + if (item->childItem) { + lv->d->dirtyItems.clear(); + lv->d->dirtyItemTimer->stop(); + lv->triggerUpdate(); + } else { + lv->d->dirtyItems.removeAll(item); + } + } + + if (lv->d->focusItem) { + const Q3ListViewItem * c = lv->d->focusItem; + while(c && c != item) + c = c->parentItem; + if (c == item) { + if (lv->selectedItem()) { + // for Single, setSelected(false) when selectedItem() is taken + lv->selectedItem()->setSelected(false); + // we don't emit selectionChanged(0) + emit lv->selectionChanged(); + } + if (item->nextSibling()) + lv->d->focusItem = item->nextSibling(); + else if (item->itemAbove()) + lv->d->focusItem = item->itemAbove(); + else + lv->d->focusItem = 0; + emit_changed = true; + } + } + + // reset anchors etc. if they are set to this or any child + // items + const Q3ListViewItem *ptr = lv->d->selectAnchor; + while (ptr && ptr != item) + ptr = ptr->parentItem; + if (ptr == item) + lv->d->selectAnchor = lv->d->focusItem; + + ptr = lv->d->startDragItem; + while (ptr && ptr != item) + ptr = ptr->parentItem; + if (ptr == item) + lv->d->startDragItem = 0; + + ptr = lv->d->pressedItem; + while (ptr && ptr != item) + ptr = ptr->parentItem; + if (ptr == item) + lv->d->pressedItem = 0; + + ptr = lv->d->highlighted; + while (ptr && ptr != item) + ptr = ptr->parentItem; + if (ptr == item) + lv->d->highlighted = 0; + } + + nChildren--; + + Q3ListViewItem ** nextChild = &childItem; + while(nextChild && *nextChild && item != *nextChild) + nextChild = &((*nextChild)->siblingItem); + + if (nextChild && item == *nextChild) + *nextChild = (*nextChild)->siblingItem; + item->parentItem = 0; + item->siblingItem = 0; + item->ownHeight = 0; + item->maybeTotalHeight = -1; + item->configured = false; + + if (emit_changed) { + emit lv->currentChanged(lv->d->focusItem); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(lv->viewport(), 0, QAccessible::Focus); +#endif + } +} + + +/*! + \fn QString Q3ListViewItem::key(int column, bool ascending) const + + Returns a key that can be used for sorting by column \a column. + The default implementation returns text(). Derived classes may + also incorporate the order indicated by \a ascending into this + key, although this is not recommended. + + If you want to sort on non-alphabetical data, e.g. dates, numbers, + etc., it is more efficient to reimplement compare(). + + \sa compare(), sortChildItems() +*/ + +QString Q3ListViewItem::key(int column, bool) const +{ + return text(column); +} + + +/*! + Compares this list view item to \a i using the column \a col in \a + ascending order. Returns \< 0 if this item is less than \a i, 0 if + they are equal and \> 0 if this item is greater than \a i. + + This function is used for sorting. + + The default implementation compares the item keys (key()) using + QString::localeAwareCompare(). A reimplementation can use + different values and a different comparison function. Here is a + reimplementation that uses plain Unicode comparison: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 3 + We don't recommend using \a ascending so your code can safely + ignore it. + + \sa key() QString::localeAwareCompare() QString::compare() +*/ + +int Q3ListViewItem::compare(Q3ListViewItem *i, int col, bool ascending) const +{ + return key(col, ascending).localeAwareCompare(i->key(col, ascending)); +} + +/*! + Sorts this item's children using column \a column. This is done in + ascending order if \a ascending is true and in descending order if + \a ascending is false. + + Asks some of the children to sort their children. (Q3ListView and + Q3ListViewItem ensure that all on-screen objects are properly + sorted but may avoid or defer sorting other objects in order to be + more responsive.) + + \sa key() compare() +*/ + +void Q3ListViewItem::sortChildItems(int column, bool ascending) +{ + // we try HARD not to sort. if we're already sorted, don't. + if (column == (int)lsc && ascending == (bool)lso) + return; + + if (column < 0) + return; + + lsc = column; + lso = ascending; + + const int nColumns = (listView() ? listView()->columns() : 0); + + // and don't sort if we already have the right sorting order + if (column > nColumns || childItem == 0 || childItem->siblingItem == 0) + return; + + // make an array for qHeapSort() + Q3ListViewPrivate::SortableItem * siblings + = new Q3ListViewPrivate::SortableItem[nChildren]; + Q3ListViewItem * s = childItem; + int i = 0; + while (s && i < nChildren) { + siblings[i].numCols = nColumns; + siblings[i].col = column; + siblings[i].asc = ascending; + siblings[i].item = s; + s = s->siblingItem; + i++; + } + + // and sort it. + qHeapSort(siblings, siblings + nChildren); + + // build the linked list of siblings, in the appropriate + // direction, and finally set this->childItem to the new top + // child. + if (ascending) { + for(i = 0; i < nChildren - 1; i++) + siblings[i].item->siblingItem = siblings[i+1].item; + siblings[nChildren-1].item->siblingItem = 0; + childItem = siblings[0].item; + } else { + for(i = nChildren - 1; i > 0; i--) + siblings[i].item->siblingItem = siblings[i-1].item; + siblings[0].item->siblingItem = 0; + childItem = siblings[nChildren-1].item; + } + for (i = 0; i < nChildren; i++) { + if (siblings[i].item->isOpen()) + siblings[i].item->sort(); + } + delete[] siblings; +} + + +/*! + Sets this item's height to \a height pixels. This implicitly + changes totalHeight(), too. + + Note that a font change causes this height to be overwritten + unless you reimplement setup(). + + For best results in Windows style we suggest using an even number + of pixels. + + \sa height() totalHeight() isOpen() +*/ + +void Q3ListViewItem::setHeight(int height) +{ + if (ownHeight != height) { + if (visible) + ownHeight = height; + else + ownHeight = 0; + invalidateHeight(); + } +} + + +/*! + Invalidates the cached total height of this item, including all + open children. + + \sa setHeight() height() totalHeight() +*/ + +void Q3ListViewItem::invalidateHeight() +{ + if (maybeTotalHeight < 0) + return; + maybeTotalHeight = -1; + if (parentItem && parentItem->isOpen()) + parentItem->invalidateHeight(); +} + + +/*! + Opens or closes an item, i.e. shows or hides an item's children. + + If \a o is true all child items are shown initially. The user can + hide them by clicking the \bold{-} icon to the left of the item. + If \a o is false, the children of this item are initially hidden. + The user can show them by clicking the \bold{+} icon to the left + of the item. + + \sa height() totalHeight() isOpen() +*/ + +void Q3ListViewItem::setOpen(bool o) +{ + if (o == (bool)open || !enabled) + return; + open = o; + + // If no children to show simply emit signals and return + if (!nChildren) { + Q3ListView *lv = listView(); + if (lv && this != lv->d->r) { + if (o) + emit lv->expanded(this); + else + emit lv->collapsed(this); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(lv->viewport(), indexOfItem(this), QAccessible::StateChanged); +#endif + } + return; + } + invalidateHeight(); + + if (!configured) { + Q3ListViewItem * l = this; + QStack<Q3ListViewItem *> s; + while(l) { + if (l->open && l->childItem) { + s.push(l->childItem); + } else if (l->childItem) { + // first invisible child is unconfigured + Q3ListViewItem * c = l->childItem; + while(c) { + c->configured = false; + c = c->siblingItem; + } + } + l->configured = true; + l->setup(); + l = (l == this) ? 0 : l->siblingItem; + if (!l && !s.isEmpty()) + l = s.pop(); + } + } + + Q3ListView *lv = listView(); + + if (open && lv) + enforceSortOrder(); + + if (isVisible() && lv && lv->d && !lv->d->drawables.isEmpty()) + lv->buildDrawableList(); + + if (lv && this != lv->d->r) { + if (o) + emit lv->expanded(this); + else + emit lv->collapsed(this); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(lv->viewport(), indexOfItem(this), QAccessible::StateChanged); +#endif + } +} + + +/*! + This virtual function is called before the first time Q3ListView + needs to know the height or any other graphical attribute of this + object, and whenever the font, GUI style, or colors of the list + view change. + + The default calls widthChanged() and sets the item's height to the + height of a single line of text in the list view's font. (If you + use icons, multi-line text, etc., you will probably need to call + setHeight() yourself or reimplement it.) +*/ + +void Q3ListViewItem::setup() +{ + widthChanged(); + Q3ListView *lv = listView(); + + int ph = 0; + int h = 0; + if (lv) { + for (int i = 0; i < lv->d->column.size(); ++i) { + if (pixmap(i)) + ph = qMax(ph, pixmap(i)->height()); + } + + if (mlenabled) { + h = ph; + for (int c = 0; c < lv->columns(); ++c) { + int lines = text(c).count(QLatin1Char('\n')) + 1; + int tmph = lv->d->fontMetricsHeight + + lv->fontMetrics().lineSpacing() * (lines - 1); + h = qMax(h, tmph); + } + h += 2*lv->itemMargin(); + } else { + h = qMax(lv->d->fontMetricsHeight, ph) + 2*lv->itemMargin(); + } + } + + h = qMax(h, QApplication::globalStrut().height()); + + if (h % 2 > 0) + h++; + setHeight(h); +} + + + + +/*! + This virtual function is called whenever the user presses the mouse + on this item or presses Space on it. + + \sa activatedPos() +*/ + +void Q3ListViewItem::activate() +{ +} + + +/*! + When called from a reimplementation of activate(), this function + gives information on how the item was activated. Otherwise the + behavior is undefined. + + If activate() was caused by a mouse press, the function sets \a + pos to where the user clicked and returns true; otherwise it + returns false and does not change \a pos. + + \a pos is relative to the top-left corner of this item. + + \sa activate() +*/ + +bool Q3ListViewItem::activatedPos(QPoint &pos) +{ + if (activatedByClick) + pos = activatedP; + return activatedByClick; +} + + +/*! + \fn bool Q3ListViewItem::isSelectable() const + + Returns true if the item is selectable (as it is by default); + otherwise returns false + + \sa setSelectable() +*/ + + +/*! + Sets this items to be selectable if \a enable is true (the + default) or not to be selectable if \a enable is false. + + The user is not able to select a non-selectable item using either + the keyboard or the mouse. The application programmer still can + though, e.g. using setSelected(). + + \sa isSelectable() +*/ + +void Q3ListViewItem::setSelectable(bool enable) +{ + selectable = enable; +} + + +/*! + \fn bool Q3ListViewItem::isExpandable() const + + Returns true if this item is expandable even when it has no + children; otherwise returns false. +*/ + +/*! + Sets this item to be expandable even if it has no children if \a + enable is true, and to be expandable only if it has children if \a + enable is false (the default). + + The dirview example uses this in the canonical fashion. It checks + whether the directory is empty in setup() and calls + setExpandable(true) if not; in setOpen() it reads the contents of + the directory and inserts items accordingly. This strategy means + that dirview can display the entire file system without reading + very much at startup. + + Note that root items are not expandable by the user unless + Q3ListView::setRootIsDecorated() is set to true. + + \sa setSelectable() +*/ + +void Q3ListViewItem::setExpandable(bool enable) +{ + expandable = enable; +} + + +/*! + Makes sure that this object's children are sorted appropriately. + + This only works if every item from the root item down to this item + is already sorted. + + \sa sortChildItems() +*/ + +void Q3ListViewItem::enforceSortOrder() const +{ + Q3ListView *lv = listView(); + if (!lv || (lv && (lv->d->clearing || lv->d->sortcolumn == Unsorted))) + return; + if (parentItem && + (parentItem->lsc != lsc || parentItem->lso != lso)) + ((Q3ListViewItem *)this)->sortChildItems((int)parentItem->lsc, + (bool)parentItem->lso); + else if (!parentItem && + ((int)lsc != lv->d->sortcolumn || (bool)lso != lv->d->ascending)) + ((Q3ListViewItem *)this)->sortChildItems(lv->d->sortcolumn, lv->d->ascending); +} + + +/*! + \fn bool Q3ListViewItem::isSelected() const + + Returns true if this item is selected; otherwise returns false. + + \sa setSelected() Q3ListView::setSelected() Q3ListView::selectionChanged() +*/ + + +/*! + If \a s is true this item is selected; otherwise it is deselected. + + This function does not maintain any invariants or repaint anything + -- Q3ListView::setSelected() does that. + + \sa height() totalHeight() +*/ + +void Q3ListViewItem::setSelected(bool s) +{ + bool old = selected; + + Q3ListView *lv = listView(); + if (lv && lv->selectionMode() != Q3ListView::NoSelection) { + if (s && isSelectable()) + selected = true; + else + selected = false; + +#ifndef QT_NO_ACCESSIBILITY + if (old != (bool)selected) { + int ind = indexOfItem(this); + QAccessible::updateAccessibility(lv->viewport(), ind, QAccessible::StateChanged); + QAccessible::updateAccessibility(lv->viewport(), ind, selected ? QAccessible::SelectionAdd : QAccessible::SelectionRemove); + } +#else + Q_UNUSED(old); +#endif + } +} + +/*! + Returns the total height of this object, including any visible + children. This height is recomputed lazily and cached for as long + as possible. + + Functions which can affect the total height are, setHeight() which + is used to set an item's height, setOpen() to show or hide an + item's children, and invalidateHeight() to invalidate the cached + height. + + \sa height() +*/ + +int Q3ListViewItem::totalHeight() const +{ + if (!visible) + return 0; + if (maybeTotalHeight >= 0) + return maybeTotalHeight; + Q3ListViewItem * that = (Q3ListViewItem *)this; + if (!that->configured) { + that->configured = true; + that->setup(); // ### virtual non-const function called in const + } + that->maybeTotalHeight = that->ownHeight; + + if (!that->isOpen() || !that->childCount()) + return that->ownHeight; + + Q3ListViewItem * child = that->childItem; + while (child != 0) { + that->maybeTotalHeight += child->totalHeight(); + child = child->siblingItem; + } + return that->maybeTotalHeight; +} + + +/*! + Returns the text in column \a column, or an empty string if there is + no text in that column. + + \sa key() paintCell() +*/ + +QString Q3ListViewItem::text(int column) const +{ + Q3ListViewPrivate::ItemColumnInfo * l + = (Q3ListViewPrivate::ItemColumnInfo*) columns; + + while(column && l) { + l = l->next; + column--; + } + + return l ? l->text : QString(); +} + + +/*! + Sets the text in column \a column to \a text, if \a column is a + valid column number and \a text is different from the existing + text. + + If the text() function has been reimplemented, this function may + be a no-op. + + \sa text() key() +*/ + +void Q3ListViewItem::setText(int column, const QString &text) +{ + if (column < 0) + return; + + Q3ListViewPrivate::ItemColumnInfo * l + = (Q3ListViewPrivate::ItemColumnInfo*) columns; + if (!l) { + l = new Q3ListViewPrivate::ItemColumnInfo; + columns = (void*)l; + } + for(int c = 0; c < column; c++) { + if (!l->next) + l->next = new Q3ListViewPrivate::ItemColumnInfo; + l = l->next; + } + if (l->text == text) + return; + + int oldLc = 0; + int newLc = 0; + if (mlenabled) { + if (!l->text.isEmpty()) + oldLc = l->text.count(QLatin1Char('\n')) + 1; + if (!text.isEmpty()) + newLc = text.count(QLatin1Char('\n')) + 1; + } + + l->dirty = true; + l->text = text; + if (column == (int)lsc) + lsc = Unsorted; + + if (mlenabled && oldLc != newLc) + setup(); + else + widthChanged(column); + + Q3ListView * lv = listView(); + if (lv) { + lv->triggerUpdate(); +#ifndef QT_NO_ACCESSIBILITY + if (lv->isVisible()) + QAccessible::updateAccessibility(lv->viewport(), indexOfItem(this), QAccessible::NameChanged); +#endif + } +} + + +/*! + Sets the pixmap in column \a column to \a pm, if \a pm is non-null + and different from the current pixmap, and if \a column is + non-negative. + + \sa pixmap() setText() +*/ + +void Q3ListViewItem::setPixmap(int column, const QPixmap & pm) +{ + if (column < 0) + return; + + int oldW = 0; + int oldH = 0; + if (pixmap(column)) { + oldW = pixmap(column)->width(); + oldH = pixmap(column)->height(); + } + + Q3ListViewPrivate::ItemColumnInfo * l + = (Q3ListViewPrivate::ItemColumnInfo*) columns; + if (!l) { + l = new Q3ListViewPrivate::ItemColumnInfo; + columns = (void*)l; + } + + for(int c = 0; c < column; c++) { + if (!l->next) + l->next = new Q3ListViewPrivate::ItemColumnInfo; + l = l->next; + } + + if ((pm.isNull() && (!l->pm || l->pm->isNull())) || + (l->pm && pm.serialNumber() == l->pm->serialNumber())) + return; + + if (pm.isNull()) { + delete l->pm; + l->pm = 0; + } else { + if (l->pm) + *(l->pm) = pm; + else + l->pm = new QPixmap(pm); + } + + int newW = 0; + int newH = 0; + if (pixmap(column)) { + newW = pixmap(column)->width(); + newH = pixmap(column)->height(); + } + + if (oldW != newW || oldH != newH) { + setup(); + widthChanged(column); + invalidateHeight(); + } + Q3ListView *lv = listView(); + if (lv) { + lv->triggerUpdate(); + } +} + + +/*! + Returns the pixmap for \a column, or 0 if there is no pixmap for + \a column. + + \sa setText() setPixmap() +*/ + +const QPixmap * Q3ListViewItem::pixmap(int column) const +{ + Q3ListViewPrivate::ItemColumnInfo * l + = (Q3ListViewPrivate::ItemColumnInfo*) columns; + + while(column && l) { + l = l->next; + column--; + } + + return (l && l->pm) ? l->pm : 0; +} + + +/* + This function paints the contents of one column of an item + and aligns it as described by \a align. + + \a p is a QPainter open on the relevant paint device. \a p is + translated so (0, 0) is the top-left pixel in the cell and \a + width-1, height()-1 is the bottom-right pixel \e in the cell. The + other properties of \a p (pen, brush, etc) are undefined. \a pal is + the color group to use. \a column is the logical column number + within the item that is to be painted; 0 is the column which may + contain a tree. + + This function may use Q3ListView::itemMargin() for readability + spacing on the left and right sides of data such as text, and + should honor isSelected() and Q3ListView::allColumnsShowFocus(). + + If you reimplement this function, you should also reimplement + width(). + + The rectangle to be painted is in an undefined state when this + function is called, so you \e must draw on all the pixels. The + painter \a p has the right font on entry. + + \sa paintBranches(), Q3ListView::drawContentsOffset() +*/ + +static QStyleOptionQ3ListView getStyleOption(const Q3ListView *lv, const Q3ListViewItem *item, + bool hierarchy = false) +{ + QStyleOptionQ3ListView opt; + opt.init(lv); + opt.subControls = QStyle::SC_None; + opt.activeSubControls = QStyle::SC_None; + QWidget *vp = lv->viewport(); + opt.viewportPalette = vp->palette(); + opt.viewportBGRole = vp->backgroundRole(); + opt.itemMargin = lv->itemMargin(); + opt.sortColumn = 0; + opt.treeStepSize = lv->treeStepSize(); + opt.rootIsDecorated = lv->rootIsDecorated(); + bool firstItem = true; + int y = item ? item->itemPos() : 0; + while (item) { + QStyleOptionQ3ListViewItem lvi; + lvi.height = item->height(); + lvi.totalHeight = item->totalHeight(); + lvi.itemY = y; + lvi.childCount = item->childCount(); + lvi.features = QStyleOptionQ3ListViewItem::None; + lvi.state = QStyle::State_None; + if (item->isEnabled()) + lvi.state |= QStyle::State_Enabled; + if (item->isOpen()) + lvi.state |= QStyle::State_Open; + if (item->isExpandable()) + lvi.features |= QStyleOptionQ3ListViewItem::Expandable; + if (item->multiLinesEnabled()) + lvi.features |= QStyleOptionQ3ListViewItem::MultiLine; + if (item->isVisible()) + lvi.features |= QStyleOptionQ3ListViewItem::Visible; + if (item->parent() && item->parent()->rtti() == 1 + && static_cast<Q3CheckListItem *>(item->parent())->type() == Q3CheckListItem::Controller) + lvi.features |= QStyleOptionQ3ListViewItem::ParentControl; + opt.items.append(lvi); + // we only care about the children when we are painting the branches + // this is only enabled by Q3ListViewItem::paintBranches + if (hierarchy) { + if (!firstItem) { + item = item->nextSibling(); + } else { + firstItem = false; + item = item->firstChild(); + } + y += lvi.height; + } else { + break; + } + } + return opt; +} + +/*! + \fn void Q3ListViewItem::paintCell(QPainter *painter, const QColorGroup & cg, int column, int width, int align) + + This virtual function paints the contents of one column of an item + and aligns it as described by \a align. + + The \a painter is a Q3Painter open on the relevant paint + device. It is translated so (0, 0) is the top-left pixel in the + cell and \a width - 1, height() - 1 is the bottom-right pixel \e + in the cell. The other properties of the \a painter (pen, brush, etc) are + undefined. \a cg is the color group to use. \a column is the + logical column number within the item that is to be painted; 0 is + the column which may contain a tree. + + This function may use Q3ListView::itemMargin() for readability + spacing on the left and right sides of data such as text, and + should honor \l isSelected() and + Q3ListView::allColumnsShowFocus(). + + If you reimplement this function, you should also reimplement \l + width(). + + The rectangle to be painted is in an undefined state when this + function is called, so you \e must draw on all the pixels. The + \a painter has the right font on entry. + + \sa paintBranches(), Q3ListView::drawContentsOffset() +*/ +void Q3ListViewItem::paintCell(QPainter * p, const QColorGroup & cg, + int column, int width, int align) +{ + // Change width() if you change this. + + QPalette pal = cg; + if (!p) + return; + + Q3ListView *lv = listView(); + if (!lv) + return; + QFontMetrics fm(p->fontMetrics()); + + // had, but we _need_ the column info for the ellipsis thingy!!! + if (!columns) { + for (int i = 0; i < lv->d->column.size(); ++i) { + setText(i, text(i)); + } + } + + QString t = text(column); + + if (columns) { + Q3ListViewPrivate::ItemColumnInfo *ci = 0; + // try until we have a column info.... + while (!ci) { + ci = (Q3ListViewPrivate::ItemColumnInfo*)columns; + for (int i = 0; ci && (i < column); ++i) + ci = ci->next; + + if (!ci) { + setText(column, t); + ci = 0; + } + } + + // if the column width changed and this item was not painted since this change + if (ci && (ci->width != width || ci->text != t || ci->dirty)) { + ci->text = t; + ci->dirty = false; + ci->width = width; + ci->truncated = false; + // if we have to do the ellipsis thingy calc the truncated text + int pw = lv->itemMargin()*2 - lv->d->minLeftBearing - lv->d->minRightBearing; + pw += pixmap(column) ? pixmap(column)->width() + lv->itemMargin() : 0; + if (!mlenabled && fm.width(t) + pw > width) { + // take care of arabic shaping in width calculation (lars) + ci->truncated = true; + ci->tmpText = qEllipsisText(t, fm, width - pw, align); + } else if (mlenabled && fm.width(t) + pw > width) { + QStringList list = t.split(QLatin1Char('\n')); + for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) { + QString z = *it; + if (fm.width(z) + pw > width) { + ci->truncated = true; + *it = qEllipsisText(z, fm, width - pw, align); + } + } + + if (ci->truncated) + ci->tmpText = list.join(QString(QLatin1Char('\n'))); + } + } + + // if we have to draw the ellipsis thingy, use the truncated text + if (ci && ci->truncated) + t = ci->tmpText; + } + + int marg = lv->itemMargin(); + int r = marg; + const QPixmap * icon = pixmap(column); + + const QPalette::ColorRole crole = lv->viewport()->backgroundRole(); + if (pal.brush(crole) != lv->palette().brush(pal.currentColorGroup(), crole)) + p->fillRect(0, 0, width, height(), pal.brush(crole)); + else + lv->paintEmptyArea(p, QRect(0, 0, width, height())); + + // (lars) what does this do??? +#if 0 // RS: #### + if (align != Qt::AlignLeft) + marg -= lv->d->minRightBearing; +#endif + if (isSelected() && + (column == 0 || lv->allColumnsShowFocus())) { + p->fillRect(r - marg, 0, qMax(0, width - r + marg), height(), + pal.brush(QPalette::Highlight)); + if (enabled || !lv) + p->setPen(pal.highlightedText().color()); + else if (!enabled && lv) + p->setPen(lv->palette().color(QPalette::Disabled, QPalette::HighlightedText)); + } else { + if (enabled || !lv) + p->setPen(pal.text().color()); + else if (!enabled && lv) + p->setPen(lv->palette().color(QPalette::Disabled, QPalette::Text)); + } + + +#if 0 + bool reverse = QApplication::reverseLayout(); +#else + bool reverse = false; +#endif + int iconWidth = 0; + + if (icon) { + iconWidth = icon->width() + lv->itemMargin(); + int xo = r; + // we default to Qt::AlignVCenter. + int yo = (height() - icon->height()) / 2; + + // I guess we may as well always respect vertical alignment. + if (align & Qt::AlignBottom) + yo = height() - icon->height(); + else if (align & Qt::AlignTop) + yo = 0; + + // respect horizontal alignment when there is no text for an item. + if (text(column).isEmpty()) { + if (align & Qt::AlignRight) + xo = width - 2 * marg - iconWidth; + else if (align & Qt::AlignHCenter) + xo = (width - iconWidth) / 2; + } + if (reverse) + xo = width - 2 * marg - iconWidth; + p->drawPixmap(xo, yo, *icon); + } + + if (!t.isEmpty()) { + if (!mlenabled) { + if (!(align & Qt::AlignTop || align & Qt::AlignBottom)) + align |= Qt::AlignVCenter; + } else { + if (!(align & Qt::AlignVCenter || align & Qt::AlignBottom)) + align |= Qt::AlignTop; + } + if (!reverse) + r += iconWidth; + + if (!mlenabled) { + p->drawText(r, 0, width-marg-r, height(), align, t); + } else { + p->drawText(r, marg, width-marg-r, height(), align, t); + } + } + + if (mlenabled && column == 0 && isOpen() && childCount()) { + int textheight = fm.size(align, t).height() + 2 * lv->itemMargin(); + textheight = qMax(textheight, QApplication::globalStrut().height()); + if (textheight % 2 > 0) + textheight++; + if (textheight < height()) { + int w = lv->treeStepSize() / 2; + QStyleOptionQ3ListView opt = getStyleOption(lv, this); + opt.rect.setRect(0, textheight, w + 1, height() - textheight + 1); + opt.palette = pal; + opt.subControls = QStyle::SC_Q3ListViewExpand; + opt.activeSubControls = QStyle::SC_All; + lv->style()->drawComplexControl(QStyle::CC_Q3ListView, &opt, p, lv); + } + } +} + +/*! + Returns the number of pixels of width required to draw column \a c + of list view \a lv, using the metrics \a fm without cropping. The + list view containing this item may use this information depending + on the Q3ListView::WidthMode settings for the column. + + The default implementation returns the width of the bounding + rectangle of the text of column \a c. + + \sa listView() widthChanged() Q3ListView::setColumnWidthMode() + Q3ListView::itemMargin() +*/ +int Q3ListViewItem::width(const QFontMetrics& fm, + const Q3ListView* lv, int c) const +{ + int w; + if (mlenabled) + w = fm.size(Qt::AlignVCenter, text(c)).width() + lv->itemMargin() * 2 + - lv->d->minLeftBearing - lv->d->minRightBearing; + else + w = fm.width(text(c)) + lv->itemMargin() * 2 + - lv->d->minLeftBearing - lv->d->minRightBearing; + const QPixmap * pm = pixmap(c); + if (pm) + w += pm->width() + lv->itemMargin(); // ### correct margin stuff? + return qMax(w, QApplication::globalStrut().width()); +} + + +/*! + Paints a focus indicator on the rectangle \a r using painter \a p + and colors \a cg. + + \a p is already clipped. + + \sa paintCell() paintBranches() Q3ListView::setAllColumnsShowFocus() +*/ + +void Q3ListViewItem::paintFocus(QPainter *p, const QColorGroup &cg, const QRect &r) +{ + QPalette pal = cg; + Q3ListView *lv = listView(); + if (lv) { + QStyleOptionFocusRect opt; + opt.init(lv); + opt.rect = r; + opt.palette = pal; + opt.state |= QStyle::State_KeyboardFocusChange; + if (isSelected()) { + opt.state |= QStyle::State_FocusAtBorder; + opt.backgroundColor = pal.highlight().color(); + } else { + opt.state |= QStyle::State_None; + opt.backgroundColor = pal.base().color(); + } + lv->style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, lv); + } +} + + +/*! + Paints a set of branches from this item to (some of) its children. + + Painter \a p is set up with clipping and translation so that you + can only draw in the rectangle that needs redrawing; \a cg is the + color group to use; the update rectangle is at (0, 0) and has size + width \a w by height \a h. The top of the rectangle you own is at + \a y (which is never greater than 0 but can be outside the window + system's allowed coordinate range). + + The update rectangle is in an undefined state when this function + is called; this function must draw on \e all of the pixels. + + \sa paintCell(), Q3ListView::drawContentsOffset() +*/ + +void Q3ListViewItem::paintBranches(QPainter * p, const QColorGroup & cg, + int w, int y, int h) +{ + Q3ListView *lv = listView(); + if (lv) + lv->paintEmptyArea(p, QRect(0, 0, w, h)); + if (!visible || !lv) + return; + QStyleOptionQ3ListView opt = getStyleOption(lv, this, true); + opt.rect.setRect(0, y, w, h); + opt.palette = cg; + opt.subControls = QStyle::SC_Q3ListViewBranch | QStyle::SC_Q3ListViewExpand; + opt.activeSubControls = QStyle::SC_None; + lv->style()->drawComplexControl(QStyle::CC_Q3ListView, &opt, p, lv); +} + + +Q3ListViewPrivate::Root::Root(Q3ListView * parent) + : Q3ListViewItem(parent) +{ + lv = parent; + setHeight(0); + setOpen(true); +} + + +void Q3ListViewPrivate::Root::setHeight(int) +{ + Q3ListViewItem::setHeight(0); +} + + +void Q3ListViewPrivate::Root::invalidateHeight() +{ + Q3ListViewItem::invalidateHeight(); + lv->triggerUpdate(); +} + + +Q3ListView * Q3ListViewPrivate::Root::theListView() const +{ + return lv; +} + + +void Q3ListViewPrivate::Root::setup() +{ + // explicitly nothing +} + + + +/*! +\internal +If called after a mouse click, tells the list view to ignore a +following double click. This state is reset after the next mouse click. +*/ + +void Q3ListViewItem::ignoreDoubleClick() +{ + Q3ListView *lv = listView(); + if (lv) + lv->d->ignoreDoubleClick = true; +} + + + +/*! + \fn void Q3ListView::onItem(Q3ListViewItem *i) + + This signal is emitted when the user moves the mouse cursor onto + item \a i, similar to the QWidget::enterEvent() function. +*/ + +// ### bug here too? see qiconview.cppp onItem/onViewport + +/*! + \fn void Q3ListView::onViewport() + + This signal is emitted when the user moves the mouse cursor from + an item to an empty part of the list view. +*/ + +/*! + \enum Q3ListView::SelectionMode + + This enumerated type is used by Q3ListView to indicate how it + reacts to selection by the user. + + \value Single When the user selects an item, any already-selected + item becomes unselected, and the user cannot unselect the selected + item. + + \value Multi When the user selects an item in the usual way, the + selection status of that item is toggled and the other items are + left alone. + + \value Extended When the user selects an item in the usual way, + the selection is cleared and the new item selected. However, if + the user presses the Ctrl key when clicking on an item, the + clicked item gets toggled and all other items are left untouched. + And if the user presses the Shift key while clicking on an item, + all items between the current item and the clicked item get + selected or unselected, depending on the state of the clicked + item. Also, multiple items can be selected by dragging the mouse + over them. + + \value NoSelection Items cannot be selected. + + In other words, \c Single is a real single-selection list view, \c + Multi a real multi-selection list view, \c Extended is a list view + where users can select multiple items but usually want to select + either just one or a range of contiguous items, and \c NoSelection + is a list view where the user can look but not touch. +*/ + +/*! + \enum Q3ListView::ResizeMode + + This enum describes how the list view's header adjusts to resize + events which affect the width of the list view. + + \value NoColumn The columns do not get resized in resize events. + + \value AllColumns All columns are resized equally to fit the width + of the list view. + + \value LastColumn The last column is resized to fit the width of + the list view. +*/ + +/*! + \enum Q3ListView::RenameAction + + This enum describes whether a rename operation is accepted if the + rename editor loses focus without the user pressing Enter. + + \value Accept Rename if Enter is pressed or focus is lost. + + \value Reject Discard the rename operation if focus is lost (and + Enter has not been pressed). +*/ + +/*! + \class Q3ListView + \brief The Q3ListView class implements a list/tree view. + + \compat + + It can display and control a hierarchy of multi-column items, and + provides the ability to add new items at any time. The user may + select one or many items (depending on the \c SelectionMode) and + sort the list in increasing or decreasing order by any column. + + The simplest pattern of use is to create a Q3ListView, add some + column headers using addColumn() and create one or more + Q3ListViewItem or Q3CheckListItem objects with the Q3ListView as + parent. + + Further nodes can be added to the list view object (the root of the + tree) or as child nodes to Q3ListViewItems. + + The main setup functions are: + \table + \header \i Function \i Action + \row \i \l addColumn() + \i Adds a column with a text label and perhaps width. Columns + are counted from the left starting with column 0. + \row \i \l setColumnWidthMode() + \i Sets the column to be resized automatically or not. + \row \i \l setAllColumnsShowFocus() + \i Sets whether items should show keyboard focus using all + columns or just column 0. The default is to show focus + just using column 0. + \row \i \l setRootIsDecorated() + \i Sets whether root items can be opened and closed by the + user and have open/close decoration to their left. The + default is false. + \row \i \l setTreeStepSize() + \i Sets how many pixels an item's children are indented + relative to their parent. The default is 20. This is + mostly a matter of taste. + \row \i \l setSorting() + \i Sets whether the items should be sorted, whether it should + be in ascending or descending order, and by what column + they should be sorted. By default the list view is sorted + by the first column; to switch this off call setSorting(-1). + \endtable + + There are several functions for mapping between items and + coordinates. itemAt() returns the item at a position on-screen, + itemRect() returns the rectangle an item occupies on the screen, + and itemPos() returns the position of any item (whether it is + on-screen or not). firstChild() returns the list view's first item + (not necessarily visible on-screen). + + You can iterate over visible items using + Q3ListViewItem::itemBelow(); over a list view's top-level items + using Q3ListViewItem::firstChild() and + Q3ListViewItem::nextSibling(); or every item using a + Q3ListViewItemIterator. See + the Q3ListViewItem documentation for examples of traversal. + + An item can be moved amongst its siblings using + Q3ListViewItem::moveItem(). To move an item in the hierarchy use + takeItem() and insertItem(). Item's (and all their child items) + are deleted with \c delete; to delete all the list view's items + use clear(). + + There are a variety of selection modes described in the + Q3ListView::SelectionMode documentation. The default is \c Single + selection, which you can change using setSelectionMode(). + + Because Q3ListView offers multiple selection it must display + keyboard focus and selection state separately. Therefore there are + functions both to set the selection state of an item + (setSelected()) and to set which item displays keyboard focus + (setCurrentItem()). + + Q3ListView emits two groups of signals; one group signals changes + in selection/focus state and one indicates selection. The first + group consists of selectionChanged() (applicable to all list + views), selectionChanged(Q3ListViewItem*) (applicable only to a + \c Single selection list view), and currentChanged(Q3ListViewItem*). + The second group consists of doubleClicked(Q3ListViewItem*), + returnPressed(Q3ListViewItem*), + rightButtonClicked(Q3ListViewItem*, const QPoint&, int), etc. + + Note that changing the state of the list view in a slot connected + to a list view signal may cause unexpected side effects. If you + need to change the list view's state in response to a signal, use + a \link QTimer::singleShot() single shot timer\endlink with a + time out of 0, and connect this timer to a slot that modifies the + list view's state. + + In Motif style, Q3ListView deviates fairly strongly from the look + and feel of the Motif hierarchical tree view. This is done mostly + to provide a usable keyboard interface and to make the list view + look better with a white background. + + If selectionMode() is \c Single (the default) the user can select + one item at a time, e.g. by clicking an item with the mouse, see + \l Q3ListView::SelectionMode for details. + + The list view can be navigated either using the mouse or the + keyboard. Clicking a \bold{-} icon closes an item (hides its + children) and clicking a \bold{+} icon opens an item (shows its + children). The keyboard controls are these: + \table + \header \i Keypress \i Action + \row \i Home + \i Make the first item current and visible. + \row \i End + \i Make the last item current and visible. + \row \i Page Up + \i Make the item above the top visible item current and visible. + \row \i Page Down + \i Make the item below the bottom visible item current and visible. + \row \i Up Arrow + \i Make the item above the current item current and visible. + \row \i Down Arrow + \i Make the item below the current item current and visible. + \row \i Left Arrow + \i If the current item is closed (\bold{+} icon) or has no + children, make its parent item current and visible. If the + current item is open (\bold{-} icon) close it, i.e. hide its + children. Exception: if the current item is the first item + and is closed and the horizontal scroll bar is offset to + the right the list view will be scrolled left. + \row \i Right Arrow + \i If the current item is closed (\bold{+} icon) and has + children, the item is opened. If the current item is + opened (\bold{-} icon) and has children the item's first + child is made current and visible. If the current item has + no children the list view is scrolled right. + \endtable + + If the user starts typing letters with the focus in the list view + an incremental search will occur. For example if the user types + 'd' the current item will change to the first item that begins + with the letter 'd'; if they then type 'a', the current item will + change to the first item that begins with 'da', and so on. If no + item begins with the letters they type the current item doesn't + change. + + Note that the list view's size hint is calculated taking into + account the height \e and width to produce a nice aspect ratio. + This may mean that you need to reimplement sizeHint() in some + cases. + + \warning The list view assumes ownership of all list view items + and will delete them when it does not need them any more. + + \sa Q3ListViewItem Q3CheckListItem +*/ + +/*! + \fn void Q3ListView::itemRenamed(Q3ListViewItem * item, int col) + + \overload + + This signal is emitted when \a item has been renamed, e.g. by + in-place renaming, in column \a col. + + \sa Q3ListViewItem::setRenameEnabled() +*/ + +/*! + \fn void Q3ListView::itemRenamed(Q3ListViewItem * item, int col, const QString &text) + + This signal is emitted when \a item has been renamed to \a text, + e.g. by in in-place renaming, in column \a col. + + \sa Q3ListViewItem::setRenameEnabled() +*/ + +/*! + Constructs a new empty list view called \a name with parent \a + parent and widget attributes \a f. + + This constructor sets the \c WA_StaticContent and the \c + Qt::WA_NoBackground attributes to boost performance when drawing + Q3ListViewItems. This may be unsuitable for custom Q3ListViewItem + classes, in which case Qt::WA_StaticContents and Qt::WA_NoBackground + should be cleared on the viewport() after construction. + + \sa QWidget::setAttribute() +*/ +Q3ListView::Q3ListView(QWidget * parent, const char *name, Qt::WindowFlags f) + : Q3ScrollView(parent, name, f | Qt::WStaticContents | Qt::WNoAutoErase) +{ + init(); +} + +void Q3ListView::init() +{ + d = new Q3ListViewPrivate; + d->vci = 0; + d->timer = new QTimer(this); + d->levelWidth = 20; + d->r = 0; + d->rootIsExpandable = 0; + d->h = new Q3Header(this, "list view header"); + d->h->installEventFilter(this); + d->focusItem = 0; + d->oldFocusItem = 0; + d->dirtyItemTimer = new QTimer(this); + d->visibleTimer = new QTimer(this); + d->renameTimer = new QTimer(this); + d->autoopenTimer = new QTimer(this); + d->margin = 1; + d->selectionMode = Q3ListView::Single; + d->sortcolumn = 0; + d->ascending = true; + d->allColumnsShowFocus = false; + d->fontMetricsHeight = fontMetrics().height(); + d->h->setTracking(true); + d->buttonDown = false; + d->ignoreDoubleClick = false; + d->scrollTimer = 0; + d->sortIndicator = false; + d->clearing = false; + d->minLeftBearing = fontMetrics().minLeftBearing(); + d->minRightBearing = fontMetrics().minRightBearing(); + d->ellipsisWidth = fontMetrics().width(QLatin1String("...")) * 2; + d->highlighted = 0; + d->pressedItem = 0; + d->selectAnchor = 0; + d->select = true; + d->startDragItem = 0; + d->toolTips = true; + d->updateHeader = false; + d->fullRepaintOnComlumnChange = false; + d->resizeMode = NoColumn; + d->defRenameAction = Reject; + d->pressedEmptyArea = false; + d->startEdit = true; + d->ignoreEditAfterFocus = false; + d->inMenuMode = false; + d->pressedSelected = false; + + setMouseTracking(true); + viewport()->setMouseTracking(true); + + connect(d->timer, SIGNAL(timeout()), + this, SLOT(updateContents())); + connect(d->dirtyItemTimer, SIGNAL(timeout()), + this, SLOT(updateDirtyItems())); + connect(d->visibleTimer, SIGNAL(timeout()), + this, SLOT(makeVisible())); + connect(d->renameTimer, SIGNAL(timeout()), + this, SLOT(startRename())); + connect(d->autoopenTimer, SIGNAL(timeout()), + this, SLOT(openFocusItem())); + + connect(d->h, SIGNAL(sizeChange(int,int,int)), + this, SLOT(handleSizeChange(int,int,int))); + connect(d->h, SIGNAL(indexChange(int,int,int)), + this, SLOT(handleIndexChange())); + connect(d->h, SIGNAL(sectionClicked(int)), + this, SLOT(changeSortColumn(int))); + connect(d->h, SIGNAL(sectionHandleDoubleClicked(int)), + this, SLOT(adjustColumn(int))); + connect(horizontalScrollBar(), SIGNAL(sliderMoved(int)), + d->h, SLOT(setOffset(int))); + connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), + d->h, SLOT(setOffset(int))); + + // will access d->r + Q3ListViewPrivate::Root * r = new Q3ListViewPrivate::Root(this); + r->is_root = true; + d->r = r; + d->r->setSelectable(false); + + viewport()->setFocusProxy(this); + viewport()->setFocusPolicy(Qt::WheelFocus); + setFocusPolicy(Qt::WheelFocus); + viewport()->setBackgroundRole(QPalette::Base); + setAttribute(Qt::WA_MacShowFocusRect); +} + +/*! + \property Q3ListView::showSortIndicator + \brief whether the list view header should display a sort indicator. + + If this property is true, an arrow is drawn in the header of the + list view to indicate the sort order of the list view contents. + The arrow will be drawn in the correct column and will point up or + down, depending on the current sort direction. The default is + false (don't show an indicator). + + \sa Q3Header::setSortIndicator() +*/ + +void Q3ListView::setShowSortIndicator(bool show) +{ + if (show == d->sortIndicator) + return; + + d->sortIndicator = show; + if (d->sortcolumn != Unsorted && d->sortIndicator) + d->h->setSortIndicator(d->sortcolumn, d->ascending); + else + d->h->setSortIndicator(-1); +} + +bool Q3ListView::showSortIndicator() const +{ + return d->sortIndicator; +} + +/*! + \property Q3ListView::showToolTips + \brief whether this list view should show tooltips for truncated column texts + + The default is true. +*/ + +void Q3ListView::setShowToolTips(bool b) +{ + d->toolTips = b; +} + +bool Q3ListView::showToolTips() const +{ + return d->toolTips; +} + +/*! + \property Q3ListView::resizeMode + \brief whether all, none or the only the last column should be resized + + Specifies whether all, none or only the last column should be + resized to fit the full width of the list view. The values for this + property can be one of: \c NoColumn (the default), \c AllColumns + or \c LastColumn. + + \warning Setting the resize mode should be done after all necessary + columns have been added to the list view, otherwise the behavior is + undefined. + + \sa Q3Header, header() +*/ + +void Q3ListView::setResizeMode(ResizeMode m) +{ + d->resizeMode = m; + if (m == NoColumn) + header()->setStretchEnabled(false); + else if (m == AllColumns) + header()->setStretchEnabled(true); + else + header()->setStretchEnabled(true, header()->count() - 1); +} + +Q3ListView::ResizeMode Q3ListView::resizeMode() const +{ + return d->resizeMode; +} + +/*! + Destroys the list view, deleting all its items, and frees up all + allocated resources. +*/ + +Q3ListView::~Q3ListView() +{ + for (int j = 0; j < d->iterators.size(); ++j) { + Q3ListViewItemIterator *i = d->iterators.at(j); + i->listView = 0; + } + + d->focusItem = 0; + delete d->r; + d->r = 0; + delete d->vci; + d->vci = 0; +#if 0 + delete d->toolTip; + d->toolTip = 0; +#endif + delete d; + d = 0; +} + + +/*! + Calls Q3ListViewItem::paintCell() and + Q3ListViewItem::paintBranches() as necessary for all list view + items that require repainting in the \a cw pixels wide and \a ch + pixels high bounding rectangle starting at position \a cx, \a cy + with offset \a ox, \a oy. Uses the painter \a p. +*/ + +void Q3ListView::drawContentsOffset(QPainter * p, int ox, int oy, + int cx, int cy, int cw, int ch) +{ + if (columns() == 0) { + paintEmptyArea(p, QRect(cx, cy, cw, ch)); + return; + } + + if (d->drawables.isEmpty() || + d->topPixel > cy || + d->bottomPixel < cy + ch - 1 || + d->r->maybeTotalHeight < 0) + buildDrawableList(); + + if (!d->dirtyItems.isEmpty()) { + QRect br(cx - ox, cy - oy, cw, ch); + for (int i = 0; i < d->dirtyItems.size(); ++i) { + const Q3ListViewItem * item = d->dirtyItems.at(i); + QRect ir = itemRect(item).intersected(viewport()->visibleRect()); + if (ir.isEmpty() || br.contains(ir)) + // we're painting this one, or it needs no painting: forget it + d->dirtyItems.removeAt(i); + } + if (d->dirtyItems.count()) { + // there are still items left that need repainting + d->dirtyItemTimer->start(0, true); + } else { + // we're painting all items that need to be painted + d->dirtyItems.clear(); + d->dirtyItemTimer->stop(); + } + } + + p->setFont(font()); + + QRect r; + int fx = -1, x, fc = 0, lc = 0; + int tx = -1; + + for (int i = 0; i < d->drawables.size(); ++i) { + Q3ListViewPrivate::DrawableItem current = d->drawables.at(i); + if (!current.i->isVisible()) + continue; + int ih = current.i->height(); + int ith = current.i->totalHeight(); + int c; + int cs; + + // need to paint current? + if (ih > 0 && current.y < cy+ch && current.y+ih > cy) { + if (fx < 0) { + // find first interesting column, once + x = 0; + c = 0; + cs = d->h->cellSize(0); + while (x + cs <= cx && c < d->h->count()) { + x += cs; + c++; + if (c < d->h->count()) + cs = d->h->cellSize(c); + } + fx = x; + fc = c; + while(x < cx + cw && c < d->h->count()) { + x += cs; + c++; + if (c < d->h->count()) + cs = d->h->cellSize(c); + } + lc = c; + } + + x = fx; + c = fc; + // draw to last interesting column + + bool drawActiveSelection = hasFocus() || d->inMenuMode || + !style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this) + || (currentItem() && currentItem()->renameBox + && currentItem()->renameBox->hasFocus()); + QPalette pal = palette(); + if(!drawActiveSelection) + pal.setCurrentColorGroup(QPalette::Inactive); + + while (c < lc) { + int i = d->h->mapToLogical(c); + cs = d->h->cellSize(c); + r.setRect(x - ox, current.y - oy, cs, ih); + if (i == 0 && current.i->parentItem) + r.setLeft(r.left() + current.l * treeStepSize()); + + p->save(); + // No need to paint if the cell isn't technically visible + if (!(r.width() == 0 || r.height() == 0)) { + p->translate(r.left(), r.top()); + int ac = d->h->mapToLogical(c); + // map to Left currently. This should change once we + // can really reverse the listview. + int align = columnAlignment(ac); + if (align == Qt::AlignAuto) align = Qt::AlignLeft; + current.i->paintCell(p, pal, ac, r.width(), align); + } + p->restore(); + x += cs; + c++; + } + + if (current.i == d->focusItem && hasFocus() && + !d->allColumnsShowFocus) { + p->save(); + int cell = d->h->mapToActual(0); + QRect r(d->h->cellPos(cell) - ox, current.y - oy, d->h->cellSize(cell), ih); + if (current.i->parentItem) + r.setLeft(r.left() + current.l * treeStepSize()); + if (r.left() < r.right()) + current.i->paintFocus(p, palette(), r); + p->restore(); + } + } + + const int cell = d->h->mapToActual(0); + + // does current need focus indication? + if (current.i == d->focusItem && hasFocus() && + d->allColumnsShowFocus) { + p->save(); + int x = -contentsX(); + int w = header()->cellPos(header()->count() - 1) + + header()->cellSize(header()->count() - 1); + + r.setRect(x, current.y - oy, w, ih); + if (d->h->mapToActual(0) == 0 || (current.l == 0 && !rootIsDecorated())) { + int offsetx = qMin(current.l * treeStepSize(), d->h->cellSize(cell)); + r.setLeft(r.left() + offsetx); + current.i->paintFocus(p, palette(), r); + } else { + int xdepth = qMin(treeStepSize() * (current.i->depth() + (rootIsDecorated() ? 1 : 0)) + + itemMargin(), d->h->cellSize(cell)); + xdepth += d->h->cellPos(cell); + QRect r1(r); + r1.setRight(d->h->cellPos(cell) - 1); + QRect r2(r); + r2.setLeft(xdepth - 1); + current.i->paintFocus(p, palette(), r1); + current.i->paintFocus(p, palette(), r2); + } + p->restore(); + } + + if (tx < 0) + tx = d->h->cellPos(cell); + + // do any children of current need to be painted? + if (ih != ith && + (current.i != d->r || d->rootIsExpandable) && + current.y + ith > cy && + current.y + ih < cy + ch && + tx + current.l * treeStepSize() < cx + cw && + tx + (current.l+1) * treeStepSize() > cx) { + // compute the clip rectangle the safe way + + int rtop = current.y + ih; + int rbottom = current.y + ith; + int rleft = tx + current.l*treeStepSize(); + int rright = rleft + treeStepSize(); + + int crtop = qMax(rtop, cy); + int crbottom = qMin(rbottom, cy+ch); + int crleft = qMax(rleft, cx); + int crright = qMin(rright, cx+cw); + + r.setRect(crleft-ox, crtop-oy, + crright-crleft, crbottom-crtop); + + if (r.isValid()) { + p->save(); + p->setClipRect(QRect(d->h->cellPos(cell), 0, d->h->cellSize(cell), height())); + p->translate(rleft-ox, crtop-oy); + + current.i->paintBranches(p, palette(), treeStepSize(), + rtop - crtop, r.height()); + p->restore(); + } + } + } + + if (d->r->totalHeight() < cy + ch) + paintEmptyArea(p, QRect(cx - ox, d->r->totalHeight() - oy, + cw, cy + ch - d->r->totalHeight())); + + int c = d->h->count()-1; + if (c >= 0 && + d->h->cellPos(c) + d->h->cellSize(c) < cx + cw) { + c = d->h->cellPos(c) + d->h->cellSize(c); + paintEmptyArea(p, QRect(c - ox, cy - oy, cx + cw - c, ch)); + } +} + + + +/*! + Paints \a rect so that it looks like empty background using + painter \a p. \a rect is in widget coordinates, ready to be fed to + \a p. + + The default function fills \a rect with the + viewport()->backgroundBrush(). +*/ + +void Q3ListView::paintEmptyArea(QPainter * p, const QRect & rect) +{ + QStyleOptionQ3ListView opt = getStyleOption(this, 0); + opt.rect = rect; + opt.sortColumn = d->sortcolumn; + opt.subControls = QStyle::SC_Q3ListView; + style()->drawComplexControl(QStyle::CC_Q3ListView, &opt, p, this); +} + + +/* + Rebuilds the list of drawable Q3ListViewItems. This function is + const so that const functions can call it without requiring + d->drawables to be mutable. +*/ + +void Q3ListView::buildDrawableList() const +{ + d->r->enforceSortOrder(); + + QStack<Q3ListViewPrivate::DrawableItem> stack; + Q3ListViewPrivate::DrawableItem di(((int)d->rootIsExpandable)-1, 0, d->r); + stack.push(di); + + Q3ListView *that = const_cast<Q3ListView *>(this); + + // could mess with cy and ch in order to speed up vertical + // scrolling + int cy = contentsY(); + int ch = that->visibleHeight(); + d->topPixel = cy + ch; // one below bottom + d->bottomPixel = cy - 1; // one above top + + that->d->drawables.clear(); + + while (!stack.isEmpty()) { + Q3ListViewPrivate::DrawableItem cur = stack.pop(); + + int ih = cur.i->height(); + int ith = cur.i->totalHeight(); + + // is this item, or its branch symbol, inside the viewport? + if (cur.y + ith >= cy && cur.y < cy + ch) { + that->d->drawables.append(cur); + // perhaps adjust topPixel up to this item? may be adjusted + // down again if any children are not to be painted + if (cur.y < d->topPixel) + d->topPixel = cur.y; + // bottompixel is easy: the bottom item drawn contains it + d->bottomPixel = cur.y + ih - 1; + } + + // push younger sibling of cur on the stack? + if (cur.y + ith < cy+ch && cur.i->siblingItem) + stack.push(Q3ListViewPrivate::DrawableItem(cur.l, cur.y + ith, cur.i->siblingItem)); + + // do any children of cur need to be painted? + if (cur.i->isOpen() && cur.i->childCount() && + cur.y + ith > cy && + cur.y + ih < cy + ch) { + cur.i->enforceSortOrder(); + + Q3ListViewItem * c = cur.i->childItem; + int y = cur.y + ih; + + // if any of the children are not to be painted, skip them + // and invalidate topPixel + while (c && y + c->totalHeight() <= cy) { + y += c->totalHeight(); + c = c->siblingItem; + d->topPixel = cy + ch; + } + + // push one child on the stack, if there is at least one + // needing to be painted + if (c && y < cy+ch) + stack.push(Q3ListViewPrivate::DrawableItem(cur.l + 1, y, c)); + } + } +} + +/*! + \property Q3ListView::treeStepSize + \brief the number of pixels a child is offset from its parent + + The default is 20 pixels. + + Of course, this property is only meaningful for hierarchical list + views. +*/ + +int Q3ListView::treeStepSize() const +{ + return d->levelWidth; +} + +void Q3ListView::setTreeStepSize(int size) +{ + if (size != d->levelWidth) { + d->levelWidth = size; + viewport()->repaint(); + } +} + +/*! + Inserts item \a i into the list view as a top-level item. You do + not need to call this unless you've called takeItem(\a i) or + Q3ListViewItem::takeItem(\a i) and need to reinsert \a i elsewhere. + + \sa Q3ListViewItem::takeItem() takeItem() +*/ + +void Q3ListView::insertItem(Q3ListViewItem * i) +{ + if (d->r) // not for d->r itself + d->r->insertItem(i); +} + + +/*! + Removes and deletes all the items in this list view and triggers + an update. + + \sa triggerUpdate() +*/ + +void Q3ListView::clear() +{ + bool wasUpdatesEnabled = viewport()->updatesEnabled(); + if (wasUpdatesEnabled) + viewport()->setUpdatesEnabled(false); + setContentsPos(0, 0); + viewport()->setUpdatesEnabled(wasUpdatesEnabled); + + bool block = signalsBlocked(); + blockSignals(true); + d->clearing = true; + clearSelection(); + for (int j = 0; j < d->iterators.size(); ++j) { + Q3ListViewItemIterator *i = d->iterators.at(j); + i->curr = 0; + } + + d->drawables.clear(); + d->dirtyItems.clear(); + d->dirtyItemTimer->stop(); + + d->highlighted = 0; + d->focusItem = 0; + d->selectAnchor = 0; + d->pressedItem = 0; + d->startDragItem = 0; + + // if it's down its downness makes no sense, so undown it + d->buttonDown = false; + + Q3ListViewItem *c = (Q3ListViewItem *)d->r->firstChild(); + Q3ListViewItem *n; + while(c) { + n = (Q3ListViewItem *)c->nextSibling(); + delete c; + c = n; + } + resizeContents(d->h->sizeHint().width(), contentsHeight()); + delete d->r; + d->r = 0; + Q3ListViewPrivate::Root * r = new Q3ListViewPrivate::Root(this); + r->is_root = true; + d->r = r; + d->r->setSelectable(false); + blockSignals(block); + triggerUpdate(); + d->clearing = false; +} + +/*! + \reimp +*/ + +void Q3ListView::setContentsPos(int x, int y) +{ + updateGeometries(); + Q3ScrollView::setContentsPos(x, y); +} + +/*! + Adds a \a width pixels wide column with the column header \a label + to the list view, and returns the index of the new column. + + All columns apart from the first one are inserted to the right of + the existing ones. + + If \a width is negative, the new column's \l WidthMode is set to + \c Maximum instead of \c Manual. + + \sa setColumnText() setColumnWidth() setColumnWidthMode() +*/ +int Q3ListView::addColumn(const QString &label, int width) +{ + int c = d->h->addLabel(label, width); + d->column.resize(c+1); + d->column[c].wmode = (width >= 0 ? Manual : Maximum); + updateGeometries(); + updateGeometry(); + return c; +} + +/*! + \overload + + Adds a \a width pixels wide new column with the header \a label + and the \a icon to the list view, and returns the index of the + column. + + If \a width is negative, the new column's \l WidthMode is set to + \c Maximum, and to \c Manual otherwise. + + \sa setColumnText() setColumnWidth() setColumnWidthMode() +*/ +int Q3ListView::addColumn(const QIcon& icon, const QString &label, int width) +{ + int c = d->h->addLabel(icon, label, width); + d->column.resize(c+1); + d->column[c].wmode = (width >= 0 ? Manual : Maximum); + updateGeometries(); + updateGeometry(); + return c; +} + +/*! + \property Q3ListView::columns + \brief the number of columns in this list view + + \sa addColumn(), removeColumn() +*/ + +int Q3ListView::columns() const +{ + return d->column.count(); +} + +/*! + Removes the column at position \a index. +*/ + +void Q3ListView::removeColumn(int index) +{ + if (index < 0 || index > (int)d->column.count() - 1) + return; + + if (d->vci) { + Q3ListViewPrivate::ViewColumnInfo *vi = d->vci, *prev = 0, *next = 0; + for (int i = 0; i < index; ++i) { + if (vi) { + prev = vi; + vi = vi->next; + } + } + if (vi) { + next = vi->next; + if (prev) + prev->next = next; + vi->next = 0; + delete vi; + if (index == 0) + d->vci = next; + } + } + + Q3ListViewItemIterator it(this); + for (; it.current(); ++it) { + Q3ListViewPrivate::ItemColumnInfo *ci = (Q3ListViewPrivate::ItemColumnInfo*)it.current()->columns; + if (ci) { + Q3ListViewPrivate::ItemColumnInfo *prev = 0, *next = 0; + for (int i = 0; i < index; ++i) { + if (ci) { + prev = ci; + ci = ci->next; + } + } + if (ci) { + next = ci->next; + if (prev) + prev->next = next; + ci->next = 0; + delete ci; + if (index == 0) + it.current()->columns = next; + } + } + } + + for (int i = index; i < (int)d->column.size() - 1; ++i) + d->column[i] = d->column[i + 1]; + d->column.resize(d->column.size() - 1); + + d->h->removeLabel(index); + if (d->resizeMode == LastColumn) + d->h->setStretchEnabled(true, d->h->count() - 1); + + updateGeometries(); + if (d->column.count() == 0) + clear(); + updateGeometry(); + viewport()->update(); +} + +/*! + Sets the heading of column \a column to \a label. + + \sa columnText() +*/ +void Q3ListView::setColumnText(int column, const QString &label) +{ + if (column < d->h->count()) { + d->h->setLabel(column, label); + updateGeometries(); + updateGeometry(); + } +} + +/*! + \overload + + Sets the heading of column \a column to \a icon and \a label. + + \sa columnText() +*/ +void Q3ListView::setColumnText(int column, const QIcon& icon, const QString &label) +{ + if (column < d->h->count()) { + d->h->setLabel(column, icon, label); + updateGeometries(); + } +} + +/*! + Sets the width of column \a column to \a w pixels. Note that if + the column has a \c WidthMode other than \c Manual, this width + setting may be subsequently overridden. + + \sa columnWidth() +*/ +void Q3ListView::setColumnWidth(int column, int w) +{ + int oldw = d->h->sectionSize(column); + if (column < d->h->count() && oldw != w) { + d->h->resizeSection(column, w); + disconnect(d->h, SIGNAL(sizeChange(int,int,int)), + this, SLOT(handleSizeChange(int,int,int))); + emit d->h->sizeChange(column, oldw, w); + connect(d->h, SIGNAL(sizeChange(int,int,int)), + this, SLOT(handleSizeChange(int,int,int))); + viewport()->update(); + } +} + + +/*! + Returns the text of column \a c. + + \sa setColumnText() +*/ + +QString Q3ListView::columnText(int c) const +{ + return d->h->label(c); +} + +/*! + Returns the width of column \a c. + + \sa setColumnWidth() +*/ + +int Q3ListView::columnWidth(int c) const +{ + int actual = d->h->mapToActual(c); + return d->h->cellSize(actual); +} + + +/*! + \enum Q3ListView::WidthMode + + This enum type describes how the width of a column in the view + changes. + + \value Manual the column width does not change automatically. + + \value Maximum the column is automatically sized according to the + widths of all items in the column. (Note: The column never shrinks + in this case.) This means that the column is always resized to the + width of the item with the largest width in the column. + + \sa setColumnWidth() setColumnWidthMode() columnWidth() +*/ + + +/*! + Sets column \a{c}'s width mode to \a mode. The default depends on + the original width argument to addColumn(). + + \sa Q3ListViewItem::width() +*/ + +void Q3ListView::setColumnWidthMode(int c, WidthMode mode) +{ + if (c >= 0 && c < d->h->count()) + d->column[c].wmode = mode; +} + + +/*! + Returns the \c WidthMode for column \a c. + + \sa setColumnWidthMode() +*/ + +Q3ListView::WidthMode Q3ListView::columnWidthMode(int c) const +{ + if (c >= 0 && c < d->h->count()) + return d->column[c].wmode; + else + return Manual; +} + + +/*! + Sets column \a{column}'s alignment to \a align. The alignment is + ultimately passed to Q3ListViewItem::paintCell() for each item in + the list view. For horizontally aligned text with Qt::AlignLeft or + Qt::AlignHCenter the ellipsis (...) will be to the right, for + Qt::AlignRight the ellipsis will be to the left. + + \sa Qt::Alignment +*/ + +void Q3ListView::setColumnAlignment(int column, int align) +{ + if (column < 0) + return; + if (!d->vci) + d->vci = new Q3ListViewPrivate::ViewColumnInfo; + Q3ListViewPrivate::ViewColumnInfo * l = d->vci; + while(column) { + if (!l->next) + l->next = new Q3ListViewPrivate::ViewColumnInfo; + l = l->next; + column--; + } + if (l->align == align) + return; + l->align = align; + triggerUpdate(); +} + + +/*! + Returns the alignment of column \a column. The default is \c + Qt::AlignAuto. + + \sa Qt::Alignment +*/ + +int Q3ListView::columnAlignment(int column) const +{ + if (column < 0 || !d->vci) + return Qt::AlignAuto; + Q3ListViewPrivate::ViewColumnInfo * l = d->vci; + while(column) { + if (!l->next) + l->next = new Q3ListViewPrivate::ViewColumnInfo; + l = l->next; + column--; + } + return l ? l->align : Qt::AlignAuto; +} + + + +/*! + \internal +*/ +void Q3ListView::show() +{ + // Reimplemented to setx the correct background mode and viewed + // area size. + if (!isVisible()) { + reconfigureItems(); + updateGeometries(); + } + Q3ScrollView::show(); +} + + +/*! + Updates the sizes of the viewport, header, scroll bars and so on. + + \warning Don't call this directly; call triggerUpdate() instead. +*/ + +void Q3ListView::updateContents() +{ + if (d->updateHeader) + header()->adjustHeaderSize(); + d->updateHeader = false; + if (!isVisible()) { + // Not in response to a setText/setPixmap any more. + return; + } + d->drawables.clear(); + viewport()->setUpdatesEnabled(false); + updateGeometries(); + viewport()->setUpdatesEnabled(true); + viewport()->repaint(); +} + + +void Q3ListView::updateGeometries() +{ + int th = d->r->totalHeight(); + int tw = d->h->headerWidth(); + if (d->h->offset() && + tw < d->h->offset() + d->h->width()) + horizontalScrollBar()->setValue(tw - Q3ListView::d->h->width()); +#if 0 + if (QApplication::reverseLayout() && d->h->offset() != horizontalScrollBar()->value()) + horizontalScrollBar()->setValue(d->h->offset()); +#endif + verticalScrollBar()->raise(); + resizeContents(tw, th); + d->drawables.clear(); + if (d->h->isHidden()) { + setMargins(0, 0, 0, 0); + } else { + QSize hs(d->h->sizeHint()); + setMargins(0, hs.height(), 0, 0); + d->h->setGeometry(viewport()->x(), viewport()->y()-hs.height(), + visibleWidth(), hs.height()); + } +} + + +/*! + Updates the display when the section \a section has changed size + from the old size, \a os, to the new size, \a ns. +*/ + +void Q3ListView::handleSizeChange(int section, int os, int ns) +{ + bool upe = viewport()->updatesEnabled(); + if (upe) + viewport()->setUpdatesEnabled(false); + viewport()->setAttribute(Qt::WA_UpdatesDisabled, true); + int sx = horizontalScrollBar()->value(); + bool sv = horizontalScrollBar()->isVisible(); + updateGeometries(); + bool fullRepaint = d->fullRepaintOnComlumnChange || sx != horizontalScrollBar()->value() + || sv != horizontalScrollBar()->isVisible(); + d->fullRepaintOnComlumnChange = false; + if (upe) + viewport()->setUpdatesEnabled(true); + + if (fullRepaint) { + viewport()->repaint(); + return; + } + + int actual = d->h->mapToActual(section); + int dx = ns - os; + int left = d->h->cellPos(actual) - contentsX() + d->h->cellSize(actual); + if (dx > 0) + left -= dx; + if (left < visibleWidth()) + viewport()->scroll(dx, 0, QRect(left, 0, visibleWidth() - left, visibleHeight())); + viewport()->repaint(left - 4 - d->ellipsisWidth, 0, 4 + d->ellipsisWidth, + visibleHeight()); // border between the items and ellipses width + + // map auto to left for now. Need to fix this once we support + // reverse layout on the listview. + int align = columnAlignment(section); + if (align == Qt::AlignAuto) align = Qt::AlignLeft; + if (align != Qt::AlignAuto && align != Qt::AlignLeft) + viewport()->repaint(d->h->cellPos(actual) - contentsX(), 0, + d->h->cellSize(actual), visibleHeight()); + + if (currentItem() && currentItem()->renameBox) { + QRect r = itemRect(currentItem()); + r = QRect(viewportToContents(r.topLeft()), r.size()); + r.setLeft(header()->sectionPos(currentItem()->renameCol)); + r.setWidth(header()->sectionSize(currentItem()->renameCol) - 1); + if (currentItem()->renameCol == 0) + r.setLeft(r.left() + itemMargin() + (currentItem()->depth() + + (rootIsDecorated() ? 1 : 0)) * treeStepSize() - 1); + if (currentItem()->pixmap(currentItem()->renameCol)) + r.setLeft(r.left() + currentItem()->pixmap(currentItem()->renameCol)->width()); + if (r.x() - contentsX() < 0) + r.setX(contentsX()); + if (r.width() > visibleWidth()) + r.setWidth(visibleWidth()); + addChild(currentItem()->renameBox, r.x(), r.y()); + currentItem()->renameBox->resize(r.size()); + } +} + + +/* + Very smart internal slot that repaints \e only the items that need + to be repainted. Don't use this directly; call repaintItem() + instead. +*/ + +void Q3ListView::updateDirtyItems() +{ + if (d->timer->isActive() || d->dirtyItems.isEmpty()) + return; + QRect ir; + for (int i = 0; i < d->dirtyItems.size(); ++i) { + const Q3ListViewItem *item = d->dirtyItems.at(i); + ir = ir.united(itemRect(item)); + } + d->dirtyItems.clear(); + if (!ir.isEmpty()) { // rectangle to be repainted + if (ir.x() < 0) + ir.moveBy(-ir.x(), 0); + viewport()->repaint(ir); + } +} + + +void Q3ListView::makeVisible() +{ + if (d->focusItem) + ensureItemVisible(d->focusItem); +} + + +/*! + Ensures that the header is correctly sized and positioned when the + resize event \a e occurs. +*/ + +void Q3ListView::resizeEvent(QResizeEvent *e) +{ + Q3ScrollView::resizeEvent(e); + d->fullRepaintOnComlumnChange = true; + d->h->resize(visibleWidth(), d->h->height()); + d->h->adjustHeaderSize(); +} + +/*! \reimp */ + +void Q3ListView::viewportResizeEvent(QResizeEvent *e) +{ + Q3ScrollView::viewportResizeEvent(e); + d->h->resize(visibleWidth(), d->h->height()); + if (resizeMode() != NoColumn && currentItem() && currentItem()->renameBox) { + QRect r = itemRect(currentItem()); + r = QRect(viewportToContents(r.topLeft()), r.size()); + r.setLeft(header()->sectionPos(currentItem()->renameCol)); + r.setWidth(header()->sectionSize(currentItem()->renameCol) - 1); + if (currentItem()->renameCol == 0) + r.setLeft(r.left() + itemMargin() + (currentItem()->depth() + + (rootIsDecorated() ? 1 : 0)) * treeStepSize() - 1); + if (currentItem()->pixmap(currentItem()->renameCol)) + r.setLeft(r.left() + currentItem()->pixmap(currentItem()->renameCol)->width()); + if (r.x() - contentsX() < 0) + r.setX(contentsX()); + if (r.width() > visibleWidth()) + r.setWidth(visibleWidth()); + addChild(currentItem()->renameBox, r.x(), r.y()); + currentItem()->renameBox->resize(r.size()); + } +} + +/*! + Triggers a size, geometry and content update during the next + iteration of the event loop. Ensures that there'll be just one + update to avoid flicker. +*/ + +void Q3ListView::triggerUpdate() +{ + if (!isVisible() || !updatesEnabled()) { + // Not in response to a setText/setPixmap any more. + return; // it will update when shown, or something. + } + + d->timer->start(0, true); +} + + +/*! + Redirects the event \a e relating to object \a o, for the viewport + to mousePressEvent(), keyPressEvent() and friends. +*/ + +bool Q3ListView::eventFilter(QObject * o, QEvent * e) +{ + if (o == d->h && + e->type() >= QEvent::MouseButtonPress && + e->type() <= QEvent::MouseMove) { + QMouseEvent * me = (QMouseEvent *)e; + QMouseEvent me2(me->type(), + QPoint(me->pos().x(), + me->pos().y() - d->h->height()), + me->button(), me->state()); + switch(me2.type()) { + case QEvent::MouseButtonDblClick: + if (me2.button() == Qt::RightButton) + return true; + break; + case QEvent::MouseMove: + if (me2.state() & Qt::RightButton) { + viewportMouseMoveEvent(&me2); + return true; + } + break; + default: + break; + } + } else if (o == viewport()) { + QFocusEvent * fe = (QFocusEvent *)e; + + switch(e->type()) { + case QEvent::FocusIn: + focusInEvent(fe); + return true; + case QEvent::FocusOut: + focusOutEvent(fe); + return true; +#ifndef QT_NO_TOOLTIP + case QEvent::ToolTip: + { + if (!showToolTips()) + return false; + + QHelpEvent *he = static_cast<QHelpEvent *>(e); + Q3ListViewItem *item = itemAt(he->pos()); + QPoint contentsPos = viewportToContents(he->pos()); + if (!item || !item->columns) { + QToolTip::showText(he->globalPos(), QString(), viewport()); + return true; + } + int col = header()->sectionAt(contentsPos.x()); + Q3ListViewPrivate::ItemColumnInfo *ci = (Q3ListViewPrivate::ItemColumnInfo*)item->columns; + for (int i = 0; ci && (i < col); ++i) + ci = ci->next; + + if (!ci || !ci->truncated) + QToolTip::showText(he->globalPos(), QString(), viewport()); + else + QToolTip::showText(he->globalPos(), item->text(col), viewport()); + return true; + } +#endif + default: + // nothing + break; + } + } else if (qobject_cast<QLineEdit*>(o)) { + if (currentItem() && currentItem()->renameBox) { + if (e->type() == QEvent::KeyPress) { + QKeyEvent *ke = (QKeyEvent*)e; + if (ke->key() == Qt::Key_Return || + ke->key() == Qt::Key_Enter) { + currentItem()->okRename(currentItem()->renameCol); + return true; + } else if (ke->key() == Qt::Key_Escape) { + currentItem()->cancelRename(currentItem()->renameCol); + return true; + } + } else if (e->type() == QEvent::FocusOut) { + if (((QFocusEvent*)e)->reason() != Qt::PopupFocusReason) { + QCustomEvent *e = new QCustomEvent(9999); + QApplication::postEvent(o, e); + return true; + } + } else if (e->type() == 9999) { + if (d->defRenameAction == Reject) + currentItem()->cancelRename(currentItem()->renameCol); + else + currentItem()->okRename(currentItem()->renameCol); + return true; + } + } + } + + return Q3ScrollView::eventFilter(o, e); +} + + +/*! + Returns a pointer to the list view containing this item. + + Note that this function traverses the items to the root to find the + listview. This function will return 0 for taken items - see + Q3ListViewItem::takeItem() +*/ + +Q3ListView * Q3ListViewItem::listView() const +{ + const Q3ListViewItem* c = this; + while (c && !c->is_root) + c = c->parentItem; + if (!c) + return 0; + return ((Q3ListViewPrivate::Root*)c)->theListView(); +} + + +/*! + Returns the depth of this item. +*/ +int Q3ListViewItem::depth() const +{ + return parentItem ? parentItem->depth()+1 : -1; // -1 == the hidden root +} + + +/*! + Returns a pointer to the item immediately above this item on the + screen. This is usually the item's closest older sibling, but it + may also be its parent or its next older sibling's youngest child, + or something else if anyoftheabove->height() returns 0. Returns 0 + if there is no item immediately above this item. + + This function assumes that all parents of this item are open (i.e. + that this item is visible, or can be made visible by scrolling). + + This function might be relatively slow because of the tree + traversions needed to find the correct item. + + \sa itemBelow() Q3ListView::itemRect() +*/ + +Q3ListViewItem * Q3ListViewItem::itemAbove() const +{ + if (!parentItem) + return 0; + + Q3ListViewItem * c = parentItem; + if (c->childItem != this) { + c = c->childItem; + while(c && c->siblingItem != this) + c = c->siblingItem; + if (!c) + return 0; + while(c->isOpen() && c->childItem) { + c = c->childItem; + while(c->siblingItem) + c = c->siblingItem; // assign c's sibling to c + } + } + if (c && (!c->height() || !c->isEnabled())) + return c->itemAbove(); + return c; +} + + +/*! + Returns a pointer to the item immediately below this item on the + screen. This is usually the item's eldest child, but it may also + be its next younger sibling, its parent's next younger sibling, + grandparent's, etc., or something else if anyoftheabove->height() + returns 0. Returns 0 if there is no item immediately below this + item. + + This function assumes that all parents of this item are open (i.e. + that this item is visible or can be made visible by scrolling). + + \sa itemAbove() Q3ListView::itemRect() +*/ + +Q3ListViewItem * Q3ListViewItem::itemBelow() const +{ + Q3ListViewItem * c = 0; + if (isOpen() && childItem) { + c = childItem; + } else if (siblingItem) { + c = siblingItem; + } else if (parentItem) { + c = const_cast<Q3ListViewItem*>(this); + do { + c = c->parentItem; + } while(c->parentItem && !c->siblingItem); + if (c) + c = c->siblingItem; + } + if (c && (!c->height() || !c->isEnabled())) + return c->itemBelow(); + return c; +} + + +/*! + \fn bool Q3ListViewItem::isOpen() const + + Returns true if this list view item has children \e and they are + not explicitly hidden; otherwise returns false. + + \sa setOpen() +*/ + +/*! + Returns the first (top) child of this item, or 0 if this item has + no children. + + Note that the children are not guaranteed to be sorted properly. + Q3ListView and Q3ListViewItem try to postpone or avoid sorting to + the greatest degree possible, in order to keep the user interface + snappy. + + \sa nextSibling() sortChildItems() +*/ + +Q3ListViewItem* Q3ListViewItem::firstChild() const +{ + enforceSortOrder(); + return childItem; +} + + +/*! + Returns the parent of this item, or 0 if this item has no parent. + + \sa firstChild(), nextSibling() +*/ + +Q3ListViewItem* Q3ListViewItem::parent() const +{ + if (!parentItem || parentItem->is_root) return 0; + return parentItem; +} + + +/*! + \fn Q3ListViewItem* Q3ListViewItem::nextSibling() const + + Returns the sibling item below this item, or 0 if there is no + sibling item after this item. + + Note that the siblings are not guaranteed to be sorted properly. + Q3ListView and Q3ListViewItem try to postpone or avoid sorting to + the greatest degree possible, in order to keep the user interface + snappy. + + \sa firstChild() sortChildItems() +*/ + +/*! + \fn int Q3ListViewItem::childCount () const + + Returns how many children this item has. The count only includes + the item's immediate children. +*/ + + +/*! + Returns the height of this item in pixels. This does not include + the height of any children; totalHeight() returns that. +*/ +int Q3ListViewItem::height() const +{ + Q3ListViewItem * that = (Q3ListViewItem *)this; + if (!that->configured) { + that->configured = true; + that->setup(); // ### virtual non-const function called in const + } + + return visible ? ownHeight : 0; +} + +/*! + Call this function when the value of width() may have changed for + column \a c. Normally, you should call this if text(c) changes. + Passing -1 for \a c indicates that all columns may have changed. + It is more efficient to pass -1 if two or more columns have + changed than to call widthChanged() separately for each one. + + \sa width() +*/ +void Q3ListViewItem::widthChanged(int c) const +{ + Q3ListView *lv = listView(); + if (lv) + lv->widthChanged(this, c); +} + +/*! + \fn void Q3ListView::dropped (QDropEvent * e) + + This signal is emitted, when a drop event occurred on the + viewport (not onto an item). + + \a e provides all the information about the drop. +*/ + +/*! + \fn void Q3ListView::selectionChanged() + + This signal is emitted whenever the set of selected items has + changed (normally before the screen update). It is available both + in \c Single selection and \c Multi selection mode but is most + useful in \c Multi selection mode. + + \warning Do not delete any Q3ListViewItem objects in slots + connected to this signal. + + \sa setSelected() Q3ListViewItem::setSelected() +*/ + + +/*! + \fn void Q3ListView::pressed(Q3ListViewItem *item) + + This signal is emitted whenever the user presses the mouse button + in a list view. \a item is the list view item on which the user + pressed the mouse button, or 0 if the user didn't press the mouse + on an item. + + \warning Do not delete any Q3ListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListView::pressed(Q3ListViewItem *item, const QPoint &pnt, int c) + + \overload + + This signal is emitted whenever the user presses the mouse button + in a list view. \a item is the list view item on which the user + pressed the mouse button, or 0 if the user didn't press the mouse + on an item. \a pnt is the position of the mouse cursor in global + coordinates, and \a c is the column where the mouse cursor was + when the user pressed the mouse button. + + \warning Do not delete any Q3ListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListView::clicked(Q3ListViewItem *item) + + This signal is emitted whenever the user clicks (mouse pressed \e + and mouse released) in the list view. \a item is the clicked list + view item, or 0 if the user didn't click on an item. + + \warning Do not delete any Q3ListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListView::mouseButtonClicked(int button, Q3ListViewItem * item, const QPoint & pos, int c) + + This signal is emitted whenever the user clicks (mouse pressed \e + and mouse released) in the list view at position \a pos. \a button + is the mouse button that the user pressed, \a item is the clicked + list view item or 0 if the user didn't click on an item. If \a + item is not 0, \a c is the list view column into which the user + pressed; if \a item is 0 \a{c}'s value is undefined. + + \warning Do not delete any Q3ListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListView::mouseButtonPressed(int button, Q3ListViewItem * item, const QPoint & pos, int c) + + This signal is emitted whenever the user pressed the mouse button + in the list view at position \a pos. \a button is the mouse button + which the user pressed, \a item is the pressed list view item or 0 + if the user didn't press on an item. If \a item is not 0, \a c is + the list view column into which the user pressed; if \a item is 0 + \a{c}'s value is undefined. + + \warning Do not delete any Q3ListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListView::clicked(Q3ListViewItem *item, const QPoint &pnt, int c) + + \overload + + This signal is emitted whenever the user clicks (mouse pressed \e + and mouse released) in the list view. \a item is the clicked list + view item, or 0 if the user didn't click on an item. \a pnt is the + position where the user has clicked in global coordinates. If \a + item is not 0, \a c is the list view column into which the user + pressed; if \a item is 0 \a{c}'s value is undefined. + + \warning Do not delete any Q3ListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void Q3ListView::selectionChanged(Q3ListViewItem *item) + + \overload + + This signal is emitted whenever the selected item has changed in + \c Single selection mode (normally after the screen update). The + argument is the newly selected \a item. + + In \c Multi selection mode, use the no argument overload of this + signal. + + \warning Do not delete any Q3ListViewItem objects in slots + connected to this signal. + + \sa setSelected() Q3ListViewItem::setSelected() currentChanged() +*/ + + +/*! + \fn void Q3ListView::currentChanged(Q3ListViewItem *item) + + This signal is emitted whenever the current item has changed + (normally after the screen update). The current item is the item + responsible for indicating keyboard focus. + + The argument is the newly current \a item, or 0 if the change made + no item current. This can happen, for example, if all the items in + the list view are deleted. + + \warning Do not delete any Q3ListViewItem objects in slots + connected to this signal. + + \sa setCurrentItem() currentItem() +*/ + + +/*! + \fn void Q3ListView::expanded(Q3ListViewItem *item) + + This signal is emitted when \a item has been expanded, i.e. when + the children of \a item are shown. + + \sa setOpen() collapsed() +*/ + +/*! + \fn void Q3ListView::collapsed(Q3ListViewItem *item) + + This signal is emitted when the \a item has been collapsed, i.e. + when the children of \a item are hidden. + + \sa setOpen() expanded() +*/ + +/*! + Processes the mouse press event \a e on behalf of the viewed widget. +*/ +void Q3ListView::contentsMousePressEvent(QMouseEvent * e) +{ + contentsMousePressEventEx(e); +} + +void Q3ListView::contentsMousePressEventEx(QMouseEvent * e) +{ + if (!e) + return; + + if (!d->ignoreEditAfterFocus) + d->startEdit = true; + d->ignoreEditAfterFocus = false; + + if (currentItem() && currentItem()->renameBox && + !itemRect(currentItem()).contains(e->pos())) { + d->startEdit = false; + if (d->defRenameAction == Reject) + currentItem()->cancelRename(currentItem()->renameCol); + else + currentItem()->okRename(currentItem()->renameCol); + } + + d->startDragItem = 0; + d->dragStartPos = e->pos(); + QPoint vp = contentsToViewport(e->pos()); + + d->ignoreDoubleClick = false; + d->buttonDown = true; + + Q3ListViewItem * i = itemAt(vp); + d->pressedEmptyArea = e->y() > contentsHeight(); + if (i && !i->isEnabled()) + return; + if (d->startEdit && (i != currentItem() || (i && !i->isSelected()))) + d->startEdit = false; + Q3ListViewItem *oldCurrent = currentItem(); + + if (e->button() == Qt::RightButton && (e->state() & Qt::ControlButton)) + goto emit_signals; + + if (!i) { + if (!(e->state() & Qt::ControlButton)) + clearSelection(); + goto emit_signals; + } else { + // No new anchor when using shift + if (!(e->state() & Qt::ShiftButton)) + d->selectAnchor = i; + } + + if ((i->isExpandable() || i->childCount()) && + d->h->mapToLogical(d->h->cellAt(vp.x())) == 0) { + int x1 = vp.x() + + d->h->offset() - + d->h->cellPos(d->h->mapToActual(0)); + int draw = 0; + for (; draw < d->drawables.size(); ++draw) + if (d->drawables.at(draw).i == i) + break; + + if (draw < d->drawables.size()) { + Q3ListViewPrivate::DrawableItem it = d->drawables.at(draw); + QStyleOptionQ3ListView opt = getStyleOption(this, i); + x1 -= treeStepSize() * (it.l - 1); + QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_Q3ListView, &opt, + QPoint(x1, e->pos().y()), this); + if (ctrl == QStyle::SC_Q3ListViewExpand && + e->type() == style()->styleHint(QStyle::SH_Q3ListViewExpand_SelectMouseType, 0, + this)) { + d->buttonDown = false; + if (e->button() == Qt::LeftButton) { + bool close = i->isOpen(); + setOpen(i, !close); + // ### Looks dangerous, removed because of reentrance problems + // qApp->processEvents(); + if (!d->focusItem) { + d->focusItem = i; + repaintItem(d->focusItem); + emit currentChanged(d->focusItem); + } + if (close) { + bool newCurrent = false; + Q3ListViewItem *ci = d->focusItem; + while (ci) { + if (ci->parent() && ci->parent() == i) { + newCurrent = true; + break; + } + ci = ci->parent(); + } + if (newCurrent) { + setCurrentItem(i); + } + } + } + d->ignoreDoubleClick = true; + d->buttonDown = false; + goto emit_signals; + } + } + } + + d->select = d->selectionMode == Multi ? !i->isSelected() : true; + + {// calculate activatedP + activatedByClick = true; + QPoint topLeft = itemRect(i).topLeft(); //### inefficient? + activatedP = vp - topLeft; + int xdepth = treeStepSize() * (i->depth() + (rootIsDecorated() ? 1 : 0)) + + itemMargin(); + xdepth += d->h->sectionPos(d->h->mapToSection(0)); + activatedP.rx() -= xdepth; + } + i->activate(); + activatedByClick = false; + + if (i != d->focusItem) + setCurrentItem(i); + else + repaintItem(i); + + d->pressedSelected = i && i->isSelected(); + + if (i->isSelectable() && selectionMode() != NoSelection) { + if (selectionMode() == Single) + setSelected(i, true); + else if (selectionMode() == Multi) + setSelected(i, d->select); + else if (selectionMode() == Extended) { + bool changed = false; + if (!(e->state() & (Qt::ControlButton | Qt::ShiftButton))) { + if (!i->isSelected()) { + bool blocked = signalsBlocked(); + blockSignals(true); + clearSelection(); + blockSignals(blocked); + i->setSelected(true); + changed = true; + } + } else { + if (e->state() & Qt::ShiftButton) + d->pressedSelected = false; + if ((e->state() & Qt::ControlButton) && !(e->state() & Qt::ShiftButton) && i) { + i->setSelected(!i->isSelected()); + changed = true; + d->pressedSelected = false; + } else if (!oldCurrent || !i || oldCurrent == i) { + if ((bool)i->selected != d->select) { + changed = true; + i->setSelected(d->select); + } + // Shift pressed in Extended mode --- + } else { + changed = selectRange(i, oldCurrent, d->selectAnchor); + } + } + if (changed) { + triggerUpdate(); + emit selectionChanged(); + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection); +#endif + } + } + } + + emit_signals: + + if (i && !d->buttonDown && + vp.x() + contentsX() < itemMargin() + (i->depth() + (rootIsDecorated() ? 1 : 0)) * treeStepSize()) + i = 0; + d->pressedItem = i; + + int c = i ? d->h->mapToLogical(d->h->cellAt(vp.x())) : -1; + if (!i || (i && i->isEnabled())) { + emit pressed(i); + emit pressed(i, viewport()->mapToGlobal(vp), c); + } + emit mouseButtonPressed(e->button(), i, viewport()->mapToGlobal(vp), c); + + if (e->button() == Qt::RightButton && i == d->pressedItem) { + if (!i && !(e->state() & Qt::ControlButton)) + clearSelection(); + + emit rightButtonPressed(i, viewport()->mapToGlobal(vp), c); + } +} + +/*! + \reimp +*/ + +void Q3ListView::contentsContextMenuEvent(QContextMenuEvent *e) +{ + if (!receivers(SIGNAL(contextMenuRequested(Q3ListViewItem*,QPoint,int)))) { + e->ignore(); + return; + } + if (e->reason() == QContextMenuEvent::Keyboard) { + Q3ListViewItem *item = currentItem(); + if (item) { + QRect r = itemRect(item); + QPoint p = r.topLeft(); + if (allColumnsShowFocus()) + p += QPoint(width() / 2, (r.height() / 2)); + else + p += QPoint(columnWidth(0) / 2, (r.height() / 2)); + p.rx() = qMax(0, p.x()); + p.rx() = qMin(visibleWidth(), p.x()); + emit contextMenuRequested(item, viewport()->mapToGlobal(p), -1); + } + } else { + QPoint vp = contentsToViewport(e->pos()); + Q3ListViewItem * i = itemAt(vp); + int c = i ? d->h->mapToLogical(d->h->cellAt(vp.x())) : -1; + emit contextMenuRequested(i, viewport()->mapToGlobal(vp), c); + } +} + +/*! + Processes the mouse release event \a e on behalf of the viewed widget. +*/ +void Q3ListView::contentsMouseReleaseEvent(QMouseEvent * e) +{ + contentsMouseReleaseEventEx(e); +} + +void Q3ListView::contentsMouseReleaseEventEx(QMouseEvent * e) +{ + d->startDragItem = 0; + bool emitClicked = !d->pressedItem || d->buttonDown; + d->buttonDown = false; + // delete and disconnect autoscroll timer, if we have one + if (d->scrollTimer) { + disconnect(d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll())); + d->scrollTimer->stop(); + delete d->scrollTimer; + d->scrollTimer = 0; + } + + if (!e) + return; + + if (d->selectionMode == Extended && + d->focusItem == d->pressedItem && + d->pressedSelected && d->focusItem && + e->button() == Qt::LeftButton) { + bool block = signalsBlocked(); + blockSignals(true); + clearSelection(); + blockSignals(block); + d->focusItem->setSelected(true); + emit selectionChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), 0, QAccessible::Selection); +#endif + } + + QPoint vp = contentsToViewport(e->pos()); + Q3ListViewItem *i = itemAt(vp); + if (i && !i->isEnabled()) + return; + + if (i && i == d->pressedItem && (i->isExpandable() || i->childCount()) && + !d->h->mapToLogical(d->h->cellAt(vp.x())) && e->button() == Qt::LeftButton && + e->type() == style()->styleHint(QStyle::SH_Q3ListViewExpand_SelectMouseType, 0, this)) { + int draw = 0; + for (; draw < d->drawables.size(); ++draw) + if (d->drawables.at(draw).i == i) + break; + if (draw < d->drawables.size()) { + int x1 = vp.x() + d->h->offset() - d->h->cellPos(d->h->mapToActual(0)) - + (treeStepSize() * (d->drawables.at(draw).l - 1)); + QStyleOptionQ3ListView opt = getStyleOption(this, i); + QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_Q3ListView, &opt, + QPoint(x1, e->pos().y()), this); + if (ctrl == QStyle::SC_Q3ListViewExpand) { + bool close = i->isOpen(); + setOpen(i, !close); + // ### Looks dangerous, removed because of reentrance problems + // qApp->processEvents(); + if (!d->focusItem) { + d->focusItem = i; + repaintItem(d->focusItem); + emit currentChanged(d->focusItem); + } + if (close) { + bool newCurrent = false; + Q3ListViewItem *ci = d->focusItem; + while (ci) { + if (ci->parent() && ci->parent() == i) { + newCurrent = true; + break; + } + ci = ci->parent(); + } + if (newCurrent) + setCurrentItem(i); + d->ignoreDoubleClick = true; + } + } + } + } + + if (i == d->pressedItem && i && i->isSelected() && e->button() == Qt::LeftButton && d->startEdit) { + QRect r = itemRect(currentItem()); + r = QRect(viewportToContents(r.topLeft()), r.size()); + d->pressedColumn = header()->sectionAt( e->pos().x()); + r.setLeft(header()->sectionPos(d->pressedColumn)); + r.setWidth(header()->sectionSize(d->pressedColumn) - 1); + if (d->pressedColumn == 0) + r.setLeft(r.left() + itemMargin() + (currentItem()->depth() + + (rootIsDecorated() ? 1 : 0)) * treeStepSize() - 1); + if (r.contains(e->pos()) && + !(e->state() & (Qt::ShiftButton | Qt::ControlButton))) + d->renameTimer->start(QApplication::doubleClickInterval(), true); + } + if (i && vp.x() + contentsX() < itemMargin() + (i->depth() + (rootIsDecorated() ? 1 : 0)) * treeStepSize()) + i = 0; + emitClicked = emitClicked && d->pressedItem == i; + d->pressedItem = 0; + + if (emitClicked) { + if (!i || (i && i->isEnabled())) { + emit clicked(i); + emit clicked(i, viewport()->mapToGlobal(vp), d->h->mapToLogical(d->h->cellAt(vp.x()))); + } + emit mouseButtonClicked(e->button(), i, viewport()->mapToGlobal(vp), + i ? d->h->mapToLogical(d->h->cellAt(vp.x())) : -1); + + if (e->button() == Qt::RightButton) { + if (!i) { + if (!(e->state() & Qt::ControlButton)) + clearSelection(); + emit rightButtonClicked(0, viewport()->mapToGlobal(vp), -1); + return; + } + + int c = d->h->mapToLogical(d->h->cellAt(vp.x())); + emit rightButtonClicked(i, viewport()->mapToGlobal(vp), c); + } + } +} + + +/*! + Processes the mouse double-click event \a e on behalf of the viewed widget. +*/ +void Q3ListView::contentsMouseDoubleClickEvent(QMouseEvent * e) +{ + d->renameTimer->stop(); + d->startEdit = false; + if (!e || e->button() != Qt::LeftButton) + return; + + // ensure that the following mouse moves and eventual release is + // ignored. + d->buttonDown = false; + + if (d->ignoreDoubleClick) { + d->ignoreDoubleClick = false; + return; + } + + QPoint vp = contentsToViewport(e->pos()); + + Q3ListViewItem * i = itemAt(vp); + + // we emit doubleClicked when the item is null (or enabled) to be consistent with + // rightButtonClicked etc. + if (!i || i->isEnabled()) { + int c = d->h->mapToLogical(d->h->cellAt(vp.x())); + emit doubleClicked(i, viewport()->mapToGlobal(vp), c); + } + + if (!i || !i->isEnabled()) + return; + + if (!i->isOpen()) { + if (i->isExpandable() || i->childCount()) + setOpen(i, true); + } else { + setOpen(i, false); + } + + // we emit the 'old' obsolete doubleClicked only if the item is not null and enabled + emit doubleClicked(i); +} + + +/*! + Processes the mouse move event \a e on behalf of the viewed widget. +*/ +void Q3ListView::contentsMouseMoveEvent(QMouseEvent * e) +{ + if (!e) + return; + + bool needAutoScroll = false; + + QPoint vp = contentsToViewport(e->pos()); + + Q3ListViewItem * i = itemAt(vp); + if (i && !i->isEnabled()) + return; + if (i != d->highlighted && + !(d->pressedItem && + (d->pressedItem->isSelected() || d->selectionMode == NoSelection) && + d->pressedItem->dragEnabled())) { + + if (i) { + emit onItem(i); + } else { + emit onViewport(); + } + d->highlighted = i; + } + + if (d->startDragItem) + i = d->startDragItem; + + if (!d->buttonDown || + ((e->state() & Qt::LeftButton) != Qt::LeftButton && + (e->state() & Qt::MidButton) != Qt::MidButton && + (e->state() & Qt::RightButton) != Qt::RightButton)) + return; + + if (d->pressedItem && + (d->pressedItem->isSelected() || d->selectionMode == NoSelection) && + d->pressedItem->dragEnabled()) { + + if (!d->startDragItem) { + setSelected(d->pressedItem, true); + d->startDragItem = d->pressedItem; + } + if ((d->dragStartPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) { + d->buttonDown = false; +#ifndef QT_NO_DRAGANDDROP + startDrag(); +#endif + } + return; + } + + // check, if we need to scroll + if (vp.y() > visibleHeight() || vp.y() < 0) + needAutoScroll = true; + + // if we need to scroll and no autoscroll timer is started, + // connect the timer + if (needAutoScroll && !d->scrollTimer) { + d->scrollTimer = new QTimer(this); + connect(d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll())); + d->scrollTimer->start(100, false); + // call it once manually + doAutoScroll(vp); + } + + // if we don't need to autoscroll + if (!needAutoScroll) { + // if there is a autoscroll timer, delete it + if (d->scrollTimer) { + disconnect(d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll())); + d->scrollTimer->stop(); + delete d->scrollTimer; + d->scrollTimer = 0; + } + // call this to select an item (using the pos from the event) + doAutoScroll(vp); + } +} + + +/*! + This slot handles auto-scrolling when the mouse button is pressed + and the mouse is outside the widget. +*/ +void Q3ListView::doAutoScroll() +{ + doAutoScroll(QPoint()); +} + +/* + Handles auto-scrolling when the mouse button is pressed + and the mouse is outside the widget. + + If cursorPos is (0,0) (isNull == true) it uses the current QCursor::pos, otherwise it uses cursorPos +*/ +void Q3ListView::doAutoScroll(const QPoint &cursorPos) +{ + QPoint pos = cursorPos.isNull() ? viewport()->mapFromGlobal(QCursor::pos()) : cursorPos; + if (!d->focusItem || (d->pressedEmptyArea && pos.y() > contentsHeight())) + return; + + bool down = pos.y() > itemRect(d->focusItem).y(); + + int g = pos.y() + contentsY(); + + if (down && pos.y() > height() ) + g = height() + contentsY(); + else if (pos.y() < 0) + g = contentsY(); + + Q3ListViewItem *c = d->focusItem, *old = 0; + Q3ListViewItem *oldCurrent = c; + if (down) { + int y = itemRect(d->focusItem).y() + contentsY(); + while(c && y + c->height() <= g) { + y += c->height(); + old = c; + c = c->itemBelow(); + } + if (!c && old) + c = old; + } else { + int y = itemRect(d->focusItem).y() + contentsY(); + while(c && y >= g) { + old = c; + c = c->itemAbove(); + if (c) + y -= c->height(); + } + if (!c && old) + c = old; + } + + if (!c || c == d->focusItem) + return; + + if (d->focusItem) { + if (d->selectionMode == Multi) { + // also (de)select the ones in between + Q3ListViewItem * b = d->focusItem; + bool down = (itemPos(c) > itemPos(b)); + while(b && b != c) { + if (b->isSelectable()) + setSelected(b, d->select); + b = down ? b->itemBelow() : b->itemAbove(); + } + if (c->isSelectable()) + setSelected(c, d->select); + } else if (d->selectionMode == Extended) { + if (selectRange(c, oldCurrent, d->selectAnchor)) { + triggerUpdate(); + emit selectionChanged(); + } + } + } + + setCurrentItem(c); + d->visibleTimer->start(1, true); +} + +/*! + \reimp +*/ + +void Q3ListView::focusInEvent(QFocusEvent *e) +{ + d->inMenuMode = false; + if (d->focusItem) { + repaintItem(d->focusItem); + } else if (firstChild() && e->reason() != Qt::MouseFocusReason) { + d->focusItem = firstChild(); + emit currentChanged(d->focusItem); + repaintItem(d->focusItem); + } + if (e->reason() == Qt::MouseFocusReason) { + d->ignoreEditAfterFocus = true; + d->startEdit = false; + } + if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) { + viewport()->repaint(); + } +} + +/*! + \reimp +*/ +QVariant Q3ListView::inputMethodQuery(Qt::InputMethodQuery query) const +{ + if (query == Qt::ImMicroFocus) { + QRect mfrect = itemRect(d->focusItem); + if (mfrect.isValid() && header() && header()->isVisible()) + mfrect.moveBy(0, header()->height()); + return mfrect; + } + return QWidget::inputMethodQuery(query); +} + +/*! + \reimp +*/ + +void Q3ListView::focusOutEvent(QFocusEvent *e) +{ + if (e->reason() == Qt::PopupFocusReason && d->buttonDown) + d->buttonDown = false; + if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) { + d->inMenuMode = + e->reason() == Qt::PopupFocusReason + || (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar")); + if (!d->inMenuMode) { + viewport()->repaint(); + } + } + + if (d->focusItem) + repaintItem(d->focusItem); +} + + +/*! + \reimp +*/ + +void Q3ListView::keyPressEvent(QKeyEvent * e) +{ + if (currentItem() && currentItem()->renameBox) + return; + if (!firstChild()) { + e->ignore(); + return; // subclass bug + } + + Q3ListViewItem* oldCurrent = currentItem(); + if (!oldCurrent) { + setCurrentItem(firstChild()); + if (d->selectionMode == Single) + setSelected(firstChild(), true); + return; + } + + Q3ListViewItem * i = currentItem(); + Q3ListViewItem *old = i; + + QRect r(itemRect(i)); + Q3ListViewItem * i2; + + bool singleStep = false; + bool selectCurrent = true; + bool wasNavigation = true; + + switch(e->key()) { + case Qt::Key_Backspace: + case Qt::Key_Delete: + d->currentPrefix.truncate(0); + break; + case Qt::Key_Enter: + case Qt::Key_Return: + d->currentPrefix.truncate(0); + if (i && !i->isSelectable() && i->isEnabled() && + (i->childCount() || i->isExpandable() || i->isOpen())) { + i->setOpen(!i->isOpen()); + return; + } + e->ignore(); + if (currentItem() && !currentItem()->isEnabled()) + break; + emit returnPressed(currentItem()); + // do NOT accept. QDialog. + return; + case Qt::Key_Down: + selectCurrent = false; + i = i->itemBelow(); + d->currentPrefix.truncate(0); + singleStep = true; + break; + case Qt::Key_Up: + selectCurrent = false; + i = i->itemAbove(); + d->currentPrefix.truncate(0); + singleStep = true; + break; + case Qt::Key_Home: + selectCurrent = false; + i = firstChild(); + if (!i->height() || !i->isEnabled()) + i = i->itemBelow(); + d->currentPrefix.truncate(0); + break; + case Qt::Key_End: + selectCurrent = false; + i = firstChild(); + while (i->nextSibling() && i->nextSibling()->height() && i->nextSibling()->isEnabled()) + i = i->nextSibling(); + while (i->itemBelow()) + i = i->itemBelow(); + d->currentPrefix.truncate(0); + break; + case Qt::Key_Next: + selectCurrent = false; + i2 = itemAt(QPoint(0, visibleHeight()-1)); + if (i2 == i || !r.isValid() || + visibleHeight() <= itemRect(i).bottom()) { + if (i2) + i = i2; + int left = visibleHeight(); + while((i2 = i->itemBelow()) != 0 && left > i2->height()) { + left -= i2->height(); + i = i2; + } + } else { + if (!i2) { + // list is shorter than the view, goto last item + while((i2 = i->itemBelow()) != 0) + i = i2; + } else { + i = i2; + } + } + d->currentPrefix.truncate(0); + break; + case Qt::Key_Prior: + selectCurrent = false; + i2 = itemAt(QPoint(0, 0)); + if (i == i2 || !r.isValid() || r.top() <= 0) { + if (i2) + i = i2; + int left = visibleHeight(); + while((i2 = i->itemAbove()) != 0 && left > i2->height()) { + left -= i2->height(); + i = i2; + } + } else { + i = i2; + } + d->currentPrefix.truncate(0); + break; + case Qt::Key_Plus: + d->currentPrefix.truncate(0); + if ( !i->isOpen() && (i->isExpandable() || i->childCount())) + setOpen(i, true); + else + return; + break; + case Qt::Key_Right: + d->currentPrefix.truncate(0); + if (i->isOpen() && i->childItem) { + i = i->childItem; + } else if (!i->isOpen() && (i->isExpandable() || i->childCount())) { + setOpen(i, true); + } else if (contentsX() + visibleWidth() < contentsWidth()) { + horizontalScrollBar()->triggerAction(QScrollBar::SliderSingleStepAdd); + return; + } else { + return; + } + break; + case Qt::Key_Minus: + d->currentPrefix.truncate(0); + if (i->isOpen()) + setOpen(i, false); + else + return; + break; + case Qt::Key_Left: + d->currentPrefix.truncate(0); + if (i->isOpen()) { + setOpen(i, false); + } else if (i->parentItem && i->parentItem != d->r) { + i = i->parentItem; + } else if (contentsX()) { + horizontalScrollBar()->triggerAction(QScrollBar::SliderSingleStepSub); + return; + } else { + return; + } + break; + case Qt::Key_Space: + activatedByClick = false; + d->currentPrefix.truncate(0); + if (currentItem() && !currentItem()->isEnabled()) + break; + i->activate(); + if (i->isSelectable() && (d->selectionMode == Multi || d->selectionMode == Extended)) { + setSelected(i, !i->isSelected()); + d->currentPrefix.truncate(0); + } + emit spacePressed(currentItem()); + break; + case Qt::Key_Escape: + e->ignore(); // For QDialog + return; + case Qt::Key_F2: + if (currentItem() && currentItem()->renameEnabled(0)) + currentItem()->startRename(0); + default: + if (e->text().length() > 0 && e->text()[0].isPrint()) { + selectCurrent = false; + wasNavigation = false; + QString input(d->currentPrefix); + Q3ListViewItem * keyItem = i; + QTime now(QTime::currentTime()); + bool tryFirst = true; + while(keyItem) { + // try twice, first with the previous string and this char + if (d->currentPrefixTime.msecsTo(now) <= 400) + input = input + e->text().toLower(); + else + input = e->text().toLower(); + if (input.length() == e->text().length()) { + if (keyItem->itemBelow()) { + keyItem = keyItem->itemBelow(); + tryFirst = true; + } else { + keyItem = firstChild(); + tryFirst = false; + } + } + QString keyItemKey; + QString prefix; + while(keyItem) { + keyItemKey = QString::null; + // Look first in the sort column, then left to right + if (d->sortcolumn != Unsorted) + keyItemKey = keyItem->text(d->sortcolumn); + for (int col = 0; col < d->h->count() && keyItemKey.isNull(); ++col) + keyItemKey = keyItem->text(d->h->mapToSection(col)); + if (!keyItemKey.isEmpty()) { + prefix = keyItemKey; + prefix.truncate(input.length()); + prefix = prefix.toLower(); + if (prefix == input) { + d->currentPrefix = input; + d->currentPrefixTime = now; + i = keyItem; + // nonoptimal double-break... + keyItem = 0; + input.truncate(0); + tryFirst = false; + } + } + if (keyItem) + keyItem = keyItem->itemBelow(); + if (!keyItem && tryFirst) { + keyItem = firstChild(); + tryFirst = false; + } + } + // then, if appropriate, with just this character + if (input.length() > e->text().length()) { + input.truncate(0); + keyItem = i; + } + } + } else { + d->currentPrefix.truncate(0); + if (e->state() & Qt::ControlButton) { + d->currentPrefix.clear(); + switch (e->key()) { + case Qt::Key_A: + selectAll(true); + break; + } + } + e->ignore(); + return; + } + } + + if (!i) + return; + + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = i; + + setCurrentItem(i); + if (i->isSelectable()) + handleItemChange(old, wasNavigation && (e->state() & Qt::ShiftButton), + wasNavigation && (e->state() & Qt::ControlButton)); + + if (d->focusItem && !d->focusItem->isSelected() && d->selectionMode == Single && selectCurrent) + setSelected(d->focusItem, true); + + if (singleStep) + d->visibleTimer->start(1, true); + else + ensureItemVisible(i); +} + + +/*! + Returns the list view item at \a viewPos. Note that \a viewPos is + in the viewport()'s coordinate system, not in the list view's own, + much larger, coordinate system. + + itemAt() returns 0 if there is no such item. + + Note that you also get the pointer to the item if \a viewPos + points to the root decoration (see setRootIsDecorated()) of the + item. To check whether or not \a viewPos is on the root decoration + of the item, you can do something like this: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 4 + + This might be interesting if you use this function to find out + where the user clicked and if you want to start a drag (which you + do not want to do if the user clicked onto the root decoration of + an item). + + \sa itemPos() itemRect() viewportToContents() +*/ + +Q3ListViewItem * Q3ListView::itemAt(const QPoint & viewPos) const +{ + if (viewPos.x() > contentsWidth() - contentsX()) + return 0; + + if (d->drawables.isEmpty()) + buildDrawableList(); + + int g = viewPos.y() + contentsY(); + + for (int i = 0; i < d->drawables.size(); ++i) { + Q3ListViewPrivate::DrawableItem c = d->drawables.at(i); + if (c.y + c.i->height() > g + && c.i->isVisible() && (!c.i->parent() || c.i->parent()->isVisible())) + return c.y <= g ? c.i : 0; + } + return 0; +} + + +/*! + Returns the y-coordinate of \a item in the list view's coordinate + system. This function is normally much slower than itemAt() but it + works for all items, whereas itemAt() normally works only for + items on the screen. + + This is a thin wrapper around Q3ListViewItem::itemPos(). + + \sa itemAt() itemRect() +*/ + +int Q3ListView::itemPos(const Q3ListViewItem * item) +{ + return item ? item->itemPos() : 0; +} + + +/*! + \property Q3ListView::multiSelection + \brief whether the list view is in multi-selection or extended-selection mode + + If you enable multi-selection, \c Multi, mode, it is possible to + specify whether or not this mode should be extended. \c Extended + means that the user can select multiple items only when pressing + the Shift or Ctrl key at the same time. + + The default selection mode is \c Single. + + \sa selectionMode() +*/ + +void Q3ListView::setMultiSelection(bool enable) +{ + if (!enable) + d->selectionMode = Q3ListView::Single; + else if ( d->selectionMode != Multi && d->selectionMode != Extended) + d->selectionMode = Q3ListView::Multi; +} + +bool Q3ListView::isMultiSelection() const +{ + return d->selectionMode == Q3ListView::Extended || d->selectionMode == Q3ListView::Multi; +} + +/*! + \property Q3ListView::selectionMode + \brief the list view's selection mode + + The mode can be \c Single (the default), \c Extended, \c Multi or + \c NoSelection. + + \sa multiSelection +*/ + +void Q3ListView::setSelectionMode(SelectionMode mode) +{ + if (d->selectionMode == mode) + return; + + if ((d->selectionMode == Multi || d->selectionMode == Extended) && + (mode == Q3ListView::Single || mode == Q3ListView::NoSelection)){ + clearSelection(); + if ((mode == Q3ListView::Single) && currentItem()) + currentItem()->selected = true; + } + + d->selectionMode = mode; +} + +Q3ListView::SelectionMode Q3ListView::selectionMode() const +{ + return d->selectionMode; +} + + +/*! + If \a selected is true the \a item is selected; otherwise it is + unselected. + + If the list view is in \c Single selection mode and \a selected is + true, the currently selected item is unselected and \a item is + made current. Unlike Q3ListViewItem::setSelected(), this function + updates the list view as necessary and emits the + selectionChanged() signals. + + \sa isSelected() setMultiSelection() isMultiSelection() + setCurrentItem(), setSelectionAnchor() +*/ + +void Q3ListView::setSelected(Q3ListViewItem * item, bool selected) +{ + if (!item || item->isSelected() == selected || + !item->isSelectable() || selectionMode() == NoSelection) + return; + + bool emitHighlighted = false; + if (selectionMode() == Single && d->focusItem != item) { + Q3ListViewItem *o = d->focusItem; + if (d->focusItem && d->focusItem->selected) + d->focusItem->setSelected(false); + d->focusItem = item; + if (o) + repaintItem(o); + emitHighlighted = true; + } + + item->setSelected(selected); + + repaintItem(item); + + if (d->selectionMode == Single && selected) + emit selectionChanged(item); + emit selectionChanged(); + + if (emitHighlighted) + emit currentChanged(d->focusItem); +} + +/*! + Sets the selection anchor to \a item, if \a item is selectable. + + The selection anchor is the item that remains selected when + Shift-selecting with either mouse or keyboard in \c Extended + selection mode. + + \sa setSelected() +*/ + +void Q3ListView::setSelectionAnchor(Q3ListViewItem *item) +{ + if (item && item->isSelectable()) + d->selectAnchor = item; +} + +/*! + Sets all the items to be not selected, updates the list view as + necessary, and emits the selectionChanged() signals. Note that for + \c Multi selection list views this function needs to iterate over + \e all items. + + \sa setSelected(), setMultiSelection() +*/ + +void Q3ListView::clearSelection() +{ + selectAll(false); +} + +/*! + If \a select is true, all the items get selected; otherwise all + the items get unselected. This only works in the selection modes \c + Multi and \c Extended. In \c Single and \c NoSelection mode the + selection of the current item is just set to \a select. +*/ + +void Q3ListView::selectAll(bool select) +{ + if (d->selectionMode == Multi || d->selectionMode == Extended) { + bool b = signalsBlocked(); + blockSignals(true); + bool anything = false; + Q3ListViewItemIterator it(this); + while (it.current()) { + Q3ListViewItem *i = it.current(); + if ((bool)i->selected != select) { + i->setSelected(select); + anything = true; + } + ++it; + } + blockSignals(b); + if (anything) { + emit selectionChanged(); + triggerUpdate(); + } + } else if (d->focusItem) { + Q3ListViewItem * i = d->focusItem; + setSelected(i, select); + } +} + +/*! + Inverts the selection. Only works in \c Multi and \c Extended + selection modes. +*/ + +void Q3ListView::invertSelection() +{ + if (d->selectionMode == Single || + d->selectionMode == NoSelection) + return; + + bool b = signalsBlocked(); + blockSignals(true); + Q3ListViewItemIterator it(this); + for (; it.current(); ++it) + it.current()->setSelected(!it.current()->isSelected()); + blockSignals(b); + emit selectionChanged(); + triggerUpdate(); +} + + +/*! + Returns true if the list view item \a i is selected; otherwise + returns false. + + \sa Q3ListViewItem::isSelected() +*/ + +bool Q3ListView::isSelected(const Q3ListViewItem * i) const +{ + return i ? i->isSelected() : false; +} + + +/*! + Returns the selected item if the list view is in \c Single + selection mode and an item is selected. + + If no items are selected or the list view is not in \c Single + selection mode this function returns 0. + + \sa setSelected() setMultiSelection() +*/ + +Q3ListViewItem * Q3ListView::selectedItem() const +{ + if (d->selectionMode != Single) + return 0; + if (d->focusItem && d->focusItem->isSelected()) + return d->focusItem; + return 0; +} + + +/*! + Sets item \a i to be the current item and repaints appropriately + (i.e. highlights the item). The current item is used for keyboard + navigation and focus indication; it is independent of any selected + items, although a selected item can also be the current item. + + \sa currentItem() setSelected() +*/ + +void Q3ListView::setCurrentItem(Q3ListViewItem * i) +{ + if (!i || d->focusItem == i || !i->isEnabled()) + return; + + if (currentItem() && currentItem()->renameBox) { + if (d->defRenameAction == Reject) + currentItem()->cancelRename(currentItem()->renameCol); + else + currentItem()->okRename(currentItem()->renameCol); + } + + Q3ListViewItem * prev = d->focusItem; + d->focusItem = i; + + if (i != prev) { + if (i && d->selectionMode == Single) { + bool changed = false; + if (prev && prev->selected) { + changed = true; + prev->setSelected(false); + } + if (i && !i->selected && d->selectionMode != NoSelection && i->isSelectable()) { + i->setSelected(true); + changed = true; + emit selectionChanged(i); + } + if (changed) + emit selectionChanged(); + } + + if (i) + repaintItem(i); + if (prev) + repaintItem(prev); + emit currentChanged(i); + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), indexOfItem(i), QAccessible::Focus); +#endif + } +} + + +/*! + Returns the current item, or 0 if there isn't one. + + \sa setCurrentItem() +*/ + +Q3ListViewItem * Q3ListView::currentItem() const +{ + return d->focusItem; +} + + +/*! + Returns the rectangle on the screen that item \a item occupies in + viewport()'s coordinates, or an invalid rectangle if \a item is 0 or + is not currently visible. + + The rectangle returned does not include any children of the + rectangle (i.e. it uses Q3ListViewItem::height(), rather than + Q3ListViewItem::totalHeight()). If you want the rectangle to + include children you can use something like this: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 5 + + Note the way it avoids too-high rectangles. totalHeight() can be + much larger than the window system's coordinate system allows. + + itemRect() is comparatively slow. It's best to call it only for + items that are probably on-screen. +*/ + +QRect Q3ListView::itemRect(const Q3ListViewItem * item) const +{ + if (d->drawables.isEmpty()) + buildDrawableList(); + + for (int i = 0; i < d->drawables.size(); ++i) { + const Q3ListViewPrivate::DrawableItem &c = d->drawables.at(i); + if (c.i == item) { + int y = c.y - contentsY(); + if (y + c.i->height() >= 0 && y < ((Q3ListView *)this)->visibleHeight()) { + return QRect(-contentsX(), y, d->h->width(), c.i->height());; + } + } + } + + return QRect(0, 0, -1, -1); +} + + +/*! + \fn void Q3ListView::doubleClicked(Q3ListViewItem *item) + + This signal is emitted whenever an item is double-clicked. It's + emitted on the second button press, not the second button release. + \a item is the list view item on which the user did the + double-click. +*/ + +/*! + \fn void Q3ListView::doubleClicked(Q3ListViewItem *item, const + QPoint& point, int column) + + This signal is emitted when a double-click occurs. It's emitted on + the second button press, not the second button release. The \a + item is the Q3ListViewItem the button was double-clicked on (which + could be 0 if it wasn't double-clicked on an item). The \a point + where the double-click occurred is given in global coordinates. If + an item was double-clicked on, \a column is the column within the + item that was double-clicked; otherwise \a column is -1. + + \warning Do not delete any Q3ListViewItem objects in slots + connected to this signal. +*/ + + +/*! + \fn void Q3ListView::returnPressed(Q3ListViewItem *item) + + This signal is emitted when Enter or Return is pressed. The + \a item parameter is the currentItem(). +*/ + +/*! + \fn void Q3ListView::spacePressed(Q3ListViewItem *item) + + This signal is emitted when Space is pressed. The \a item + parameter is the currentItem(). +*/ + + +/*! + Sets the list view to be sorted by column \a column in ascending + order if \a ascending is true or descending order if it is false. + + If \a column is -1, sorting is disabled and the user cannot sort + columns by clicking on the column headers. If \a column is larger + than the number of columns the user must click on a column + header to sort the list view. +*/ + +void Q3ListView::setSorting(int column, bool ascending) +{ + if (column == -1) + column = Unsorted; + + if (d->sortcolumn == column && d->ascending == ascending) + return; + + d->ascending = ascending; + d->sortcolumn = column; + if (d->sortcolumn != Unsorted && d->sortIndicator) + d->h->setSortIndicator(d->sortcolumn, d->ascending); + else + d->h->setSortIndicator(-1); + + triggerUpdate(); + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(viewport(), 0, QAccessible::ObjectReorder); +#endif +} + +/*! + Sets the \a column the list view is sorted by. + + Sorting is triggered by choosing a header section. +*/ + +void Q3ListView::changeSortColumn(int column) +{ + if (isRenaming()) { + if (d->defRenameAction == Q3ListView::Reject) { + currentItem()->cancelRename(currentItem()->renameCol); + } else { + currentItem()->okRename(currentItem()->renameCol); + } + } + if (d->sortcolumn != Unsorted) { + int lcol = d->h->mapToLogical(column); + setSorting(lcol, d->sortcolumn == lcol ? !d->ascending : true); + } +} + +/*! + \internal + Handles renaming when sections are being swapped by the user. +*/ + +void Q3ListView::handleIndexChange() +{ + if (isRenaming()) { + if (d->defRenameAction == Q3ListView::Reject) { + currentItem()->cancelRename(currentItem()->renameCol); + } else { + currentItem()->okRename(currentItem()->renameCol); + } + } + triggerUpdate(); +} + +/*! + Returns the column by which the list view is sorted, or -1 if + sorting is disabled. + + \sa sortOrder() +*/ + +int Q3ListView::sortColumn() const +{ + return d->sortcolumn; +} + +/*! + Sets the sorting column for the list view. + + If \a column is -1, sorting is disabled and the user cannot sort + columns by clicking on the column headers. If \a column is larger + than the number of columns the user must click on a column header + to sort the list view. + + \sa setSorting() +*/ +void Q3ListView::setSortColumn(int column) +{ + setSorting(column, d->ascending); +} + +/*! + Returns the sorting order of the list view items. + + \sa sortColumn() +*/ +Qt::SortOrder Q3ListView::sortOrder() const +{ + if (d->ascending) + return Qt::AscendingOrder; + return Qt::DescendingOrder; +} + +/*! + Sets the sort order for the items in the list view to \a order. + + \sa setSorting() +*/ +void Q3ListView::setSortOrder(Qt::SortOrder order) +{ + setSorting(d->sortcolumn, order == Qt::AscendingOrder ? true : false); +} + +/*! + Sorts the list view using the last sorting configuration (sort + column and ascending/descending). +*/ + +void Q3ListView::sort() +{ + if (d->r) + d->r->sort(); +} + +/*! + \property Q3ListView::itemMargin + \brief the advisory item margin that list items may use + + The item margin defaults to one pixel and is the margin between + the item's edges and the area where it draws its contents. + Q3ListViewItem::paintFocus() draws in the margin. + + \sa Q3ListViewItem::paintCell() +*/ + +void Q3ListView::setItemMargin(int m) +{ + if (d->margin == m) + return; + d->margin = m; + if (isVisible()) { + d->drawables.clear(); + triggerUpdate(); + } +} + +int Q3ListView::itemMargin() const +{ + return d->margin; +} + + +/*! + \fn void Q3ListView::rightButtonClicked(Q3ListViewItem *item, + const QPoint& point, int column) + + This signal is emitted when the right button is clicked. The \a + item is the Q3ListViewItem the button was clicked on (which could + be 0 if it wasn't clicked on an item). The \a point where the + click occurred is given in global coordinates. If an item was + clicked on, \a column is the column within the item that was + clicked; otherwise \a column is -1. +*/ + + +/*! + \fn void Q3ListView::rightButtonPressed (Q3ListViewItem *item, + const QPoint &point, int column) + + This signal is emitted when the right button is pressed. The \a + item is the Q3ListViewItem the button was pressed on (which could + be 0 if it wasn't pressed on an item). The \a point where the + press occurred is given in global coordinates. If an item was + pressed on, \a column is the column within the item that was + pressed; otherwise \a column is -1. +*/ + +/*! + \fn void Q3ListView::contextMenuRequested(Q3ListViewItem *item, const QPoint & pos, int col) + + This signal is emitted when the user invokes a context menu with + the right mouse button or with special system keys. If the + keyboard was used \a item is the current item; if the mouse was + used, \a item is the item under the mouse pointer or 0 if there is + no item under the mouse pointer. If no item is clicked, the column + index emitted is -1. + + \a pos is the position for the context menu in the global + coordinate system. + + \a col is the column on which the user pressed, or -1 if the + signal was triggered by a key event. +*/ + +/*! + \reimp +*/ +void Q3ListView::changeEvent(QEvent *ev) +{ + if(ev->type() == QEvent::StyleChange) { + reconfigureItems(); + } else if(ev->type() == QEvent::ActivationChange) { + if (!isActiveWindow() && d->scrollTimer) + d->scrollTimer->stop(); + if (!palette().isEqual(QPalette::Active, QPalette::Inactive)) + viewport()->update(); + } + Q3ScrollView::changeEvent(ev); + + if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange + || ev->type() == QEvent::ApplicationPaletteChange || ev->type() == QEvent::PaletteChange) + reconfigureItems(); +} + +/*! + Ensures that setup() is called for all currently visible items, + and that it will be called for currently invisible items as soon + as their parents are opened. + + (A visible item, here, is an item whose parents are all open. The + item may happen to be off-screen.) + + \sa Q3ListViewItem::setup() +*/ + +void Q3ListView::reconfigureItems() +{ + d->fontMetricsHeight = fontMetrics().height(); + d->minLeftBearing = fontMetrics().minLeftBearing(); + d->minRightBearing = fontMetrics().minRightBearing(); + d->ellipsisWidth = fontMetrics().width(QLatin1String("...")) * 2; + d->r->setOpen(false); + d->r->configured = false; + d->r->setOpen(true); +} + +/*! + Ensures that the width mode of column \a c is updated according to + the width of \a item. +*/ + +void Q3ListView::widthChanged(const Q3ListViewItem* item, int c) +{ + if (c >= d->h->count()) + return; + + + QFontMetrics fm = fontMetrics(); + int col = c < 0 ? 0 : c; + while (col == c || (c < 0 && col < d->h->count())) { + if (d->column[col].wmode == Maximum) { + int w = item->width(fm, this, col); + if (showSortIndicator()) { + int tw = d->h->sectionSizeHint( col, fm ).width(); + tw += 40; //add space for the sort indicator + w = qMax(w, tw); + } + if (col == 0) { + int indent = treeStepSize() * item->depth(); + if (rootIsDecorated()) + indent += treeStepSize(); + w += indent; + } + if (w > columnWidth(col) && !d->h->isStretchEnabled() && !d->h->isStretchEnabled(col)) { + d->updateHeader = true; + setColumnWidth(col, w); + } + } + col++; + } +} + +/*! + \property Q3ListView::allColumnsShowFocus + \brief whether items should show keyboard focus using all columns + + If this property is true all columns will show focus and selection + states, otherwise only column 0 will show focus. + + The default is false. + + Setting this to true if it's not necessary may cause noticeable + flicker. +*/ + +void Q3ListView::setAllColumnsShowFocus(bool enable) +{ + d->allColumnsShowFocus = enable; +} + +bool Q3ListView::allColumnsShowFocus() const +{ + return d->allColumnsShowFocus; +} + + +/*! + Returns the first item in this Q3ListView. Returns 0 if there is no + first item. + + A list view's items can be traversed using firstChild() + and nextSibling() or using a Q3ListViewItemIterator. + + \sa itemAt() Q3ListViewItem::itemBelow() Q3ListViewItem::itemAbove() +*/ + +Q3ListViewItem * Q3ListView::firstChild() const +{ + if (!d->r) + return 0; + + d->r->enforceSortOrder(); + return d->r->childItem; +} + +/*! + Returns the last item in the list view tree. Returns 0 if there + are no items in the Q3ListView. + + This function is slow because it traverses the entire tree to find + the last item. +*/ + +Q3ListViewItem* Q3ListView::lastItem() const +{ + Q3ListViewItem* item = firstChild(); + if (item) { + while (item->nextSibling() || item->firstChild()) { + if (item->nextSibling()) + item = item->nextSibling(); + else + item = item->firstChild(); + } + } + return item; +} + +/*! + Repaints this item on the screen if it is currently visible. +*/ + +void Q3ListViewItem::repaint() const +{ + Q3ListView *lv = listView(); + if (lv) + lv->repaintItem(this); +} + + +/*! + Repaints \a item on the screen if \a item is currently visible. + Takes care to avoid multiple repaints. +*/ + +void Q3ListView::repaintItem(const Q3ListViewItem * item) const +{ + if (!item) + return; + d->dirtyItemTimer->start(0, true); + d->dirtyItems.append(item); +} + + +struct Q3CheckListItemPrivate +{ + Q3CheckListItemPrivate(): + exclusive(0), + currentState(Q3CheckListItem::Off), + tristate(false) {} + + Q3CheckListItem *exclusive; + Q3CheckListItem::ToggleState currentState; + QHash<Q3CheckListItem *, Q3CheckListItem::ToggleState> statesDict; + bool tristate; +}; + + +/*! + \class Q3CheckListItem + \brief The Q3CheckListItem class provides checkable list view items. + + \compat + + Q3CheckListItems are used in \l{Q3ListView}s to provide + \l{Q3ListViewItem}s that are checkboxes, radio buttons or + controllers. + + Checkbox and controller check list items may be inserted at any + level in a list view. Radio button check list items must be + children of a controller check list item. + + The item can be checked or unchecked with setOn(). Its type can be + retrieved with type() and its text retrieved with text(). + + \img qlistviewitems.png List View Items + + \sa Q3ListViewItem Q3ListView +*/ + +/*! + \enum Q3CheckListItem::Type + + This enum type specifies a Q3CheckListItem's type: + + \value RadioButton + \value CheckBox + \value RadioButtonController + \value CheckBoxController + \omitvalue Controller +*/ + +/*! + \enum Q3CheckListItem::ToggleState + + This enum specifies a Q3CheckListItem's toggle state. + + \value Off + \value NoChange + \value On +*/ + + +/*! + Constructs a checkable item with parent \a parent, text \a text + and of type \a tt. Note that a \c RadioButton must be the child of a + \c RadioButtonController, otherwise it will not toggle. +*/ +Q3CheckListItem::Q3CheckListItem(Q3CheckListItem *parent, const QString &text, + Type tt) + : Q3ListViewItem(parent, text, QString()) +{ + myType = tt; + init(); + if (myType == RadioButton) { + if (parent->type() != RadioButtonController) + qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be " + "child of a controller"); + else + d->exclusive = parent; + } +} + +/*! + Constructs a checkable item with parent \a parent, which is after + \a after in the parent's list of children, and with text \a text + and of type \a tt. Note that a \c RadioButton must be the child of + a \c RadioButtonController, otherwise it will not toggle. +*/ +Q3CheckListItem::Q3CheckListItem(Q3CheckListItem *parent, Q3ListViewItem *after, + const QString &text, Type tt) + : Q3ListViewItem(parent, after, text) +{ + myType = tt; + init(); + if (myType == RadioButton) { + if (parent->type() != RadioButtonController) + qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be " + "child of a controller"); + else + d->exclusive = parent; + } +} + +/*! + Constructs a checkable item with parent \a parent, text \a text + and of type \a tt. Note that this item must \e not be a \c + RadioButton. Radio buttons must be children of a \c + RadioButtonController. +*/ +Q3CheckListItem::Q3CheckListItem(Q3ListViewItem *parent, const QString &text, + Type tt) + : Q3ListViewItem(parent, text, QString()) +{ + myType = tt; + if (myType == RadioButton) { + qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be " + "child of a Q3CheckListItem"); + } + init(); +} + +/*! + Constructs a checkable item with parent \a parent, which is after + \a after in the parent's list of children, with text \a text and + of type \a tt. Note that this item must \e not be a \c + RadioButton. Radio buttons must be children of a \c + RadioButtonController. +*/ +Q3CheckListItem::Q3CheckListItem(Q3ListViewItem *parent, Q3ListViewItem *after, + const QString &text, Type tt) + : Q3ListViewItem(parent, after, text) +{ + myType = tt; + if (myType == RadioButton) { + qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be " + "child of a Q3CheckListItem"); + } + init(); +} + + +/*! + Constructs a checkable item with parent \a parent, text \a text + and of type \a tt. Note that \a tt must \e not be \c RadioButton. + Radio buttons must be children of a \c RadioButtonController. +*/ +Q3CheckListItem::Q3CheckListItem(Q3ListView *parent, const QString &text, + Type tt) + : Q3ListViewItem(parent, text) +{ + myType = tt; + if (tt == RadioButton) + qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be " + "child of a Q3CheckListItem"); + init(); +} + +/*! + Constructs a checkable item with parent \a parent, which is after + \a after in the parent's list of children, with text \a text and + of type \a tt. Note that \a tt must \e not be \c RadioButton. + Radio buttons must be children of a \c RadioButtonController. +*/ +Q3CheckListItem::Q3CheckListItem(Q3ListView *parent, Q3ListViewItem *after, + const QString &text, Type tt) + : Q3ListViewItem(parent, after, text) +{ + myType = tt; + if (tt == RadioButton) + qWarning("Q3CheckListItem::Q3CheckListItem(), radio button must be " + "child of a Q3CheckListItem"); + init(); +} + + +/* \reimp */ + +int Q3CheckListItem::rtti() const +{ + return RTTI; +} + +/*! + Constructs a \c RadioButtonController item with parent \a parent, + text \a text and pixmap \a p. +*/ +Q3CheckListItem::Q3CheckListItem(Q3ListView *parent, const QString &text, + const QPixmap & p) + : Q3ListViewItem(parent, text) +{ + myType = RadioButtonController; + setPixmap(0, p); + init(); +} + +/*! + Constructs a \c RadioButtonController item with parent \a parent, + text \a text and pixmap \a p. +*/ +Q3CheckListItem::Q3CheckListItem(Q3ListViewItem *parent, const QString &text, + const QPixmap & p) + : Q3ListViewItem(parent, text) +{ + myType = RadioButtonController; + setPixmap(0, p); + init(); +} + +void Q3CheckListItem::init() +{ + d = new Q3CheckListItemPrivate(); + on = false; + // CheckBoxControllers by default have tristate set to true + if (myType == CheckBoxController) + setTristate(true); +} + +/*! + Destroys the item, and all its children to any depth, freeing up + all allocated resources. +*/ +Q3CheckListItem::~Q3CheckListItem() +{ + if (myType == RadioButton + && d->exclusive && d->exclusive->d + && d->exclusive->d->exclusive == this) + d->exclusive->turnOffChild(); + d->exclusive = 0; // so the children won't try to access us. + delete d; + d = 0; +} + +/*! + \fn Q3CheckListItem::Type Q3CheckListItem::type() const + + Returns the type of this item. +*/ + +/*! + \fn bool Q3CheckListItem::isOn() const + + Returns true if the item is toggled on; otherwise returns false. +*/ + +/*! + Sets tristate to \a b if the \c Type is either a \c CheckBoxController or + a \c CheckBox. + + \c CheckBoxControllers are tristate by default. + + \sa state() isTristate() +*/ +void Q3CheckListItem::setTristate(bool b) +{ + if ((myType != CheckBoxController) && (myType != CheckBox)) { + qWarning("Q3CheckListItem::setTristate(), has no effect on RadioButton " + "or RadioButtonController."); + return; + } + d->tristate = b; +} + +/*! + Returns true if the item is tristate; otherwise returns false. + + \sa setTristate() +*/ +bool Q3CheckListItem::isTristate() const +{ + return d->tristate; +} + +/*! + Returns the state of the item. + + \sa Q3CheckListItem::ToggleState +*/ +Q3CheckListItem::ToggleState Q3CheckListItem::state() const +{ + if (!isTristate() && internalState() == NoChange) + return Off; + else + return d->currentState; +} + +/* + Same as the public state() except this one does not mask NoChange into Off + when tristate is disabled. +*/ +Q3CheckListItem::ToggleState Q3CheckListItem::internalState() const +{ + return d->currentState; +} + + + + +/*! + Sets the toggle state of the checklistitem to \a s. \a s can be + \c Off, \c NoChange or \c On. + + Tristate can only be enabled for \c CheckBox or \c CheckBoxController, + therefore the \c NoChange only applies to them. + + Setting the state to \c On or \c Off on a \c CheckBoxController + will recursivly set the states of its children to the same state. + + Setting the state to \c NoChange on a \c CheckBoxController will + make it recursivly recall the previous stored state of its + children. If there was no previous stored state the children are + all set to \c On. +*/ +void Q3CheckListItem::setState(ToggleState s) +{ + if (myType == CheckBoxController && state() == NoChange) + updateStoredState(this); + setState(s, true, true); +} + +/* + Sets the toggle state of the checklistitems. \a update tells if the + controller / parent controller should be aware of these changes, \a store + tells if the parent should store its children if certain conditions arise +*/ +void Q3CheckListItem::setState(ToggleState s, bool update, bool store) +{ + + if (s == internalState()) + return; + + if (myType == CheckBox) { + setCurrentState(s); + stateChange(state()); + if (update && parent() && parent()->rtti() == 1 + && ((Q3CheckListItem*)parent())->type() == CheckBoxController) + ((Q3CheckListItem*)parent())->updateController(update, store); + } else if (myType == CheckBoxController) { + if (s == NoChange && childCount()) { + restoreState(this); + } else { + Q3ListViewItem *item = firstChild(); + int childCount = 0; + while(item) { + if (item->rtti() == 1 && + (((Q3CheckListItem*)item)->type() == CheckBox || + ((Q3CheckListItem*)item)->type() == CheckBoxController)) { + Q3CheckListItem *checkItem = (Q3CheckListItem*)item; + checkItem->setState(s, false, false); + childCount++; + } + item = item->nextSibling(); + } + if (update) { + if (childCount > 0) { + ToggleState oldState = internalState(); + updateController(false, false); + if (oldState != internalState() && + parent() && parent()->rtti() == 1 && + ((Q3CheckListItem*)parent())->type() == CheckBoxController) + ((Q3CheckListItem*)parent())->updateController(update, store); + + updateController(update, store); + } else { + // if there are no children we simply set the CheckBoxController and update its parent + setCurrentState(s); + stateChange(state()); + if (parent() && parent()->rtti() == 1 + && ((Q3CheckListItem*)parent())->type() == CheckBoxController) + ((Q3CheckListItem*)parent())->updateController(update, store); + } + } else { + setCurrentState(s); + stateChange(state()); + } + + } + } else if (myType == RadioButton) { + if (s == On) { + if (d->exclusive && d->exclusive->d->exclusive != this) + d->exclusive->turnOffChild(); + setCurrentState(s); + if (d->exclusive) + d->exclusive->d->exclusive = this; + } else { + if (d->exclusive && d->exclusive->d->exclusive == this) + d->exclusive->d->exclusive = 0; + setCurrentState(Off); + } + stateChange(state()); + } + repaint(); +} + +/* + this function is needed because we need to update "on" every time we + update d->currentState. In order to retain binary compatibility the + inline function isOn() needs the "on" bool ### should be changed in + ver 4 +*/ +void Q3CheckListItem::setCurrentState(ToggleState s) +{ + ToggleState old = d->currentState; + d->currentState = s; + if (d->currentState == On) + on = true; + else + on = false; + +#ifndef QT_NO_ACCESSIBILITY + if (old != d->currentState && listView()) + QAccessible::updateAccessibility(listView()->viewport(), indexOfItem(this), QAccessible::StateChanged); +#else + Q_UNUSED(old); +#endif +} + + + +/* + updates the internally stored state of this item for the parent (key) +*/ +void Q3CheckListItem::setStoredState(ToggleState newState, Q3CheckListItem *key) +{ + if (myType == CheckBox || myType == CheckBoxController) + d->statesDict[key] = newState; +} + +/* + Returns the stored state for this item for the given key. + If the key is not found it returns Off. +*/ +Q3CheckListItem::ToggleState Q3CheckListItem::storedState(Q3CheckListItem *key) const +{ + QHash<Q3CheckListItem *, Q3CheckListItem::ToggleState>::Iterator it = d->statesDict.find(key); + if (it != d->statesDict.end()) + return it.value(); + else + return Off; +} + + +/*! + \fn QString Q3CheckListItem::text() const + + Returns the item's text. +*/ + + +/*! + If this is a \c RadioButtonController that has \c RadioButton + children, turn off the child that is on. +*/ +void Q3CheckListItem::turnOffChild() +{ + if (myType == RadioButtonController && d->exclusive) + d->exclusive->setOn(false); +} + +/*! + Toggle check box or set radio button to on. +*/ +void Q3CheckListItem::activate() +{ + Q3ListView * lv = listView(); + + if ((lv && !lv->isEnabled()) || !isEnabled()) + return; + + QPoint pos; + int boxsize = lv->style()->pixelMetric(QStyle::PM_CheckListButtonSize, 0, lv); + if (activatedPos(pos)) { + bool parentControl = false; + if (parent() && parent()->rtti() == 1 && + ((Q3CheckListItem*) parent())->type() == RadioButtonController) + parentControl = true; + + int x = parentControl ? 0 : 3; + int align = lv->columnAlignment(0); + int marg = lv->itemMargin(); + int y = 0; + + if (align & Qt::AlignVCenter) + y = ((height() - boxsize) / 2) + marg; + else + y = (lv->fontMetrics().height() + 2 + marg - boxsize) / 2; + + QRect r(x, y, boxsize-3, boxsize-3); + // columns might have been swapped + r.moveBy(lv->header()->sectionPos(0), 0); + if (!r.contains(pos)) + return; + } + if ((myType == CheckBox) || (myType == CheckBoxController)) { + lv->d->startEdit = FALSE; + switch (internalState()) { + case On: + setState(Off); + break; + case Off: + if ( (!isTristate() && myType == CheckBox) || + (myType == CheckBoxController && !childCount()) ) { + setState(On); + } else { + setState(NoChange); + if (myType == CheckBoxController && internalState() != NoChange) + setState(On); + } + break; + case NoChange: + setState(On); + break; + } + ignoreDoubleClick(); + } else if (myType == RadioButton) { + setOn(true); + ignoreDoubleClick(); + } +} + +/*! + Sets the button on if \a b is true, otherwise sets it off. + Maintains radio button exclusivity. +*/ +void Q3CheckListItem::setOn(bool b ) +{ + if (b) + setState(On , true, true); + else + setState(Off , true, true); +} + + +/*! + \fn void Q3CheckListItem::stateChange(bool b) + + This virtual function is called when the item changes its state. + \a b is true if the state is \c On; otherwise the state is \c Off. + \c NoChange (if tristate is enabled and the type is either \c + CheckBox or \c CheckBoxController) reports the same as \c Off, so + use state() to determine if the state is actually \c Off or \c + NoChange. +*/ +void Q3CheckListItem::stateChange(bool) +{ +} + +/* + Calls the public virtual function if the state is changed to either On, NoChange or Off. + NoChange reports the same as Off - ### should be fixed in ver4 +*/ +void Q3CheckListItem::stateChange(ToggleState s) +{ + stateChange(s == On); +} + +/* + sets the state of the CheckBox and CheckBoxController back to + previous stored state +*/ +void Q3CheckListItem::restoreState(Q3CheckListItem *key, int depth) +{ + switch (type()) { + case CheckBox: + setCurrentState(storedState(key)); + stateChange(state()); + repaint(); + break; + case CheckBoxController: { + Q3ListViewItem *item = firstChild(); + int childCount = 0; + while (item) { + // recursively calling restoreState for children of type CheckBox and CheckBoxController + if (item->rtti() == 1 && + (((Q3CheckListItem*)item)->type() == CheckBox || + ((Q3CheckListItem*)item)->type() == CheckBoxController)) { + ((Q3CheckListItem*)item)->restoreState(key , depth+1); + childCount++; + } + item = item->nextSibling(); + } + if (childCount > 0) { + if (depth == 0) + updateController(true); + else + updateController(false); + } else { + // if there are no children we retrieve the CheckBoxController state directly. + setState(storedState(key), true, false); + } + } + break; + default: + break; + } +} + + +/* + Checks the childrens state and updates the controllers state + if necessary. If the controllers state change, then his parent again is + called to update itself. +*/ +void Q3CheckListItem::updateController(bool update , bool store) +{ + if (myType != CheckBoxController) + return; + + Q3CheckListItem *controller = 0; + // checks if this CheckBoxController has another CheckBoxController as parent + if (parent() && parent()->rtti() == 1 + && ((Q3CheckListItem*)parent())->type() == CheckBoxController) + controller = (Q3CheckListItem*)parent(); + + ToggleState theState = Off; + bool first = true; + Q3ListViewItem *item = firstChild(); + while(item && theState != NoChange) { + if (item->rtti() == 1 && + (((Q3CheckListItem*)item)->type() == CheckBox || + ((Q3CheckListItem*)item)->type() == CheckBoxController)) { + Q3CheckListItem *checkItem = (Q3CheckListItem*)item; + if (first) { + theState = checkItem->internalState(); + first = false; + } else { + if (checkItem->internalState() == NoChange || + theState != checkItem->internalState()) + theState = NoChange; + else + theState = checkItem->internalState(); + } + } + item = item->nextSibling(); + } + if (internalState() != theState) { + setCurrentState(theState); + if (store && (internalState() == On || internalState() == Off)) + updateStoredState(this); + stateChange(state()); + if (update && controller) { + controller->updateController(update, store); + } + repaint(); + } +} + + +/* + Makes all the children CheckBoxes update their storedState +*/ +void Q3CheckListItem::updateStoredState(Q3CheckListItem *key) +{ + if (myType != CheckBoxController) + return; + + Q3ListViewItem *item = firstChild(); + while(item) { + if (item->rtti() == 1) { + Q3CheckListItem *checkItem = (Q3CheckListItem*)item; + if (checkItem->type() == CheckBox) + checkItem->setStoredState(checkItem->internalState(), key); + else if (checkItem->type() == CheckBoxController) + checkItem->updateStoredState(key); + } + item = item->nextSibling(); + } + // this state is only needed if the CheckBoxController has no CheckBox / CheckBoxController children. + setStoredState(internalState() , key); +} + + +/*! + \reimp +*/ +void Q3CheckListItem::setup() +{ + Q3ListViewItem::setup(); + int h = height(); + Q3ListView *lv = listView(); + if (lv) + h = qMax(lv->style()->pixelMetric(QStyle::PM_CheckListButtonSize, 0, lv), + h); + h = qMax(h, QApplication::globalStrut().height()); + setHeight(h); +} + +/*! + \reimp +*/ + +int Q3CheckListItem::width(const QFontMetrics& fm, const Q3ListView* lv, int column) const +{ + int r = Q3ListViewItem::width(fm, lv, column); + if (column == 0) { + r += lv->itemMargin(); + if (myType == RadioButtonController && pixmap(0)) { + // r += 0; + } else { + r += lv->style()->pixelMetric(QStyle::PM_CheckListButtonSize, 0, lv) + 4; + } + } + return qMax(r, QApplication::globalStrut().width()); +} + +/*! + Paints the item using the painter \a p and the color group \a cg. + The item is in column \a column, has width \a width and has + alignment \a align. (See \l Qt::Alignment for valid alignments.) +*/ +void Q3CheckListItem::paintCell(QPainter * p, const QColorGroup & cg, + int column, int width, int align) +{ + if (!p) + return; + + Q3ListView *lv = listView(); + if (!lv) + return; + + const QPalette::ColorRole crole = lv->backgroundRole(); + if (cg.brush(crole) != lv->palette().brush(cg.currentColorGroup(), crole)) + p->fillRect(0, 0, width, height(), cg.brush(crole)); + else + lv->paintEmptyArea(p, QRect(0, 0, width, height())); + + if (column != 0) { + // The rest is text, or for subclasses to change. + Q3ListViewItem::paintCell(p, cg, column, width, align); + return; + } + + bool parentControl = false; + if (parent() && parent()->rtti() == 1 && + ((Q3CheckListItem*) parent())->type() == RadioButtonController) + parentControl = true; + + QFontMetrics fm(lv->fontMetrics()); + int boxsize = lv->style()->pixelMetric(myType == RadioButtonController ? QStyle::PM_CheckListControllerSize : + QStyle::PM_CheckListButtonSize, 0, lv); + int marg = lv->itemMargin(); + int r = marg; + + // Draw controller / check box / radio button --------------------- + QStyle::State styleflags = QStyle::State_None; + if (internalState() == On) { + styleflags |= QStyle::State_On; + } else if (internalState() == NoChange) { + if (myType == CheckBoxController && !isTristate()) + styleflags |= QStyle::State_Off; + else + styleflags |= QStyle::State_NoChange; + } else { + styleflags |= QStyle::State_Off; + } + if (isSelected()) + styleflags |= QStyle::State_Selected; + if (isEnabled() && lv->isEnabled()) + styleflags |= QStyle::State_Enabled; + if (lv->window()->isActiveWindow()) + styleflags |= QStyle::State_Active; + + if (myType == RadioButtonController) { + int x = 0; + if(!parentControl) + x += 3; + if (!pixmap(0)) { + QStyleOptionQ3ListView opt = getStyleOption(lv, this); + opt.rect.setRect(x, 0, boxsize, fm.height() + 2 + marg); + opt.palette = cg; + opt.state = styleflags; + lv->style()->drawPrimitive(QStyle::PE_Q3CheckListController, &opt, p, lv); + r += boxsize + 4; + } + } else { + Q_ASSERT(lv); //### + int x = 0; + int y = 0; + if (!parentControl) + x += 3; + if (align & Qt::AlignVCenter) + y = ((height() - boxsize) / 2) + marg; + else + y = (fm.height() + 2 + marg - boxsize) / 2; + + QStyleOptionQ3ListView opt = getStyleOption(lv, this); + opt.rect.setRect(x, y, boxsize, fm.height() + 2 + marg); + opt.palette = cg; + opt.state = styleflags; + lv->style()->drawPrimitive((myType == CheckBox || myType == CheckBoxController) + ? QStyle::PE_Q3CheckListIndicator + : QStyle::PE_Q3CheckListExclusiveIndicator, &opt, p, lv); + r += boxsize + 4; + } + + // Draw text ---------------------------------------------------- + p->translate(r, 0); + p->setPen(QPen(cg.text())); + Q3ListViewItem::paintCell(p, cg, column, width - r, align); +} + +/*! + Draws the focus rectangle \a r using the color group \a cg on the + painter \a p. +*/ +void Q3CheckListItem::paintFocus(QPainter *p, const QColorGroup & cg, + const QRect & r) +{ + bool intersect = true; + Q3ListView *lv = listView(); + if (lv && lv->header()->mapToActual(0) != 0) { + int xdepth = lv->treeStepSize() * (depth() + (lv->rootIsDecorated() ? 1 : 0)) + lv->itemMargin(); + int p = lv->header()->cellPos(lv->header()->mapToActual(0)); + xdepth += p; + intersect = r.intersects(QRect(p, r.y(), xdepth - p + 1, r.height())); + } + bool parentControl = false; + if (parent() && parent()->rtti() == 1 && + ((Q3CheckListItem*) parent())->type() == RadioButtonController) + parentControl = true; + if (myType != RadioButtonController && intersect && + (lv->rootIsDecorated() || myType == RadioButton || + (myType == CheckBox && parentControl))) { + QRect rect; + int boxsize = lv->style()->pixelMetric(QStyle::PM_CheckListButtonSize, 0, lv); + if (lv->columnAlignment(0) == Qt::AlignCenter) { + QFontMetrics fm(lv->font()); + int bx = (lv->columnWidth(0) - (boxsize + fm.width(text())))/2 + boxsize; + if (bx < 0) bx = 0; + rect.setRect(r.x() + bx + 5, r.y(), r.width() - bx - 5, + r.height()); + } else + rect.setRect(r.x() + boxsize + 5, r.y(), r.width() - boxsize - 5, + r.height()); + Q3ListViewItem::paintFocus(p, cg, rect); + } else { + Q3ListViewItem::paintFocus(p, cg, r); + } +} + +/*! + \reimp +*/ +QSize Q3ListView::sizeHint() const +{ + if (cachedSizeHint().isValid()) + return cachedSizeHint(); + + ensurePolished(); + + if (!isVisible() && d->drawables.isEmpty()) + // force the column widths to sanity, if possible + buildDrawableList(); + + QSize s(d->h->sizeHint()); + if (verticalScrollBar()->isVisible()) + s.setWidth(s.width() + style()->pixelMetric(QStyle::PM_ScrollBarExtent)); + s += QSize(frameWidth()*2,frameWidth()*2); + Q3ListViewItem * l = d->r; + while(l && !l->height()) + l = l->childItem ? l->childItem : l->siblingItem; + + if (l && l->height()) + s.setHeight(s.height() + 10 * l->height()); + else + s.setHeight(s.height() + 140); + + if (s.width() > s.height() * 3) + s.setHeight(s.width() / 3); + else if (s.width() *3 < s.height()) + s.setHeight(s.width() * 3); + + setCachedSizeHint(s); + + return s; +} + + +/*! + \reimp +*/ + +QSize Q3ListView::minimumSizeHint() const +{ + return Q3ScrollView::minimumSizeHint(); +} + + +/*! + Sets \a item to be open if \a open is true and \a item is + expandable, and to be closed if \a open is false. Repaints + accordingly. + + \sa Q3ListViewItem::setOpen() Q3ListViewItem::setExpandable() +*/ + +void Q3ListView::setOpen(Q3ListViewItem * item, bool open) +{ + if (!item || + item->isOpen() == open || + (open && !item->childCount() && !item->isExpandable())) + return; + + Q3ListViewItem* nextParent = 0; + if (open) + nextParent = item->itemBelow(); + + item->setOpen(open); + + if (open) { + Q3ListViewItem* lastChild = item; + Q3ListViewItem* tmp; + while (true) { + tmp = lastChild->itemBelow(); + if (!tmp || tmp == nextParent) + break; + lastChild = tmp; + } + ensureItemVisible(lastChild); + ensureItemVisible(item); + } + buildDrawableList(); + + int i = 0; + for (; i < d->drawables.size(); ++i) { + const Q3ListViewPrivate::DrawableItem &c = d->drawables.at(i); + if(c.i == item) + break; + } + + if (i < d->drawables.size()) { + d->dirtyItemTimer->start(0, true); + for (; i < d->drawables.size(); ++i) { + const Q3ListViewPrivate::DrawableItem &c = d->drawables.at(i); + d->dirtyItems.append(c.i); + } + } +} + + +/*! + Returns true if this list view item has children \e and they are + not explicitly hidden; otherwise returns false. + + Identical to \a{item}->isOpen(). Provided for completeness. + + \sa setOpen() +*/ + +bool Q3ListView::isOpen(const Q3ListViewItem * item) const +{ + return item->isOpen(); +} + + +/*! + \property Q3ListView::rootIsDecorated + \brief whether the list view shows open/close signs on root items + + Open/close signs are small \bold{+} or \bold{-} symbols in windows + style, or arrows in Motif style. The default is false. +*/ + +void Q3ListView::setRootIsDecorated(bool enable) +{ + if (enable != (bool)d->rootIsExpandable) { + d->rootIsExpandable = enable; + if (isVisible()) + triggerUpdate(); + } +} + +bool Q3ListView::rootIsDecorated() const +{ + return d->rootIsExpandable; +} + + +/*! + Ensures that item \a i is visible, scrolling the list view + vertically if necessary and opening (expanding) any parent items + if this is required to show the item. + + \sa itemRect() Q3ScrollView::ensureVisible() +*/ + +void Q3ListView::ensureItemVisible(const Q3ListViewItem * i) +{ + if (!i || !i->isVisible()) + return; + + Q3ListViewItem *parent = i->parent(); + while (parent) { + if (!parent->isOpen()) + parent->setOpen(true); + parent = parent->parent(); + } + + if (d->r->maybeTotalHeight < 0) + updateGeometries(); + int y = itemPos(i); + int h = i->height(); + if (isVisible() && y + h > contentsY() + visibleHeight()) + setContentsPos(contentsX(), y - visibleHeight() + h); + else if (!isVisible() || y < contentsY()) + setContentsPos(contentsX(), y); +} + + +/*! + \fn QString Q3CheckListItem::text(int n) const + + \reimp +*/ + +/*! + Returns the Q3Header object that manages this list view's columns. + Please don't modify the header behind the list view's back. + + You may safely call Q3Header::setClickEnabled(), + Q3Header::setResizeEnabled(), Q3Header::setMovingEnabled(), + Q3Header::hide() and all the const Q3Header functions. +*/ + +Q3Header * Q3ListView::header() const +{ + return d->h; +} + + +/*! + \property Q3ListView::childCount + \brief the number of parentless (top-level) Q3ListViewItem objects in this Q3ListView + + Holds the current number of parentless (top-level) Q3ListViewItem + objects in this Q3ListView. + + \sa Q3ListViewItem::childCount() +*/ + +int Q3ListView::childCount() const +{ + if (d->r) + return d->r->childCount(); + return 0; +} + + +/* + Moves this item to just after \a olderSibling. \a olderSibling and + this object must have the same parent. + + If you need to move an item in the hierarchy use takeItem() and + insertItem(). +*/ + +void Q3ListViewItem::moveToJustAfter(Q3ListViewItem * olderSibling) +{ + if (parentItem && olderSibling && + olderSibling->parentItem == parentItem && olderSibling != this) { + if (parentItem->childItem == this) { + parentItem->childItem = siblingItem; + } else { + Q3ListViewItem * i = parentItem->childItem; + while(i && i->siblingItem != this) + i = i->siblingItem; + if (i) + i->siblingItem = siblingItem; + } + siblingItem = olderSibling->siblingItem; + olderSibling->siblingItem = this; + parentItem->lsc = Unsorted; + } +} + +/*! + Move the item to be after item \a after, which must be one of the + item's siblings. To move an item in the hierarchy, use takeItem() + and insertItem(). + + Note that this function will have no effect if sorting is enabled + in the list view. +*/ + +void Q3ListViewItem::moveItem(Q3ListViewItem *after) +{ + if (!after || after == this) + return; + if (parent() != after->parent()) { + if (parentItem) + parentItem->takeItem(this); + if (after->parentItem) { + int tmpLsc = after->parentItem->lsc; + after->parentItem->insertItem(this); + after->parentItem->lsc = tmpLsc; + } + } + moveToJustAfter(after); + Q3ListView *lv = listView(); + if (lv) + lv->triggerUpdate(); +} + +/* + Recursively sorts items, from the root to this item. + (enforceSortOrder() won't work the other way around, as + documented.) +*/ +void Q3ListViewItem::enforceSortOrderBackToRoot() +{ + if (parentItem) { + parentItem->enforceSortOrderBackToRoot(); + parentItem->enforceSortOrder(); + } +} + +/*! + \reimp +*/ +void Q3ListView::showEvent(QShowEvent *) +{ + d->drawables.clear(); + d->dirtyItems.clear(); + d->dirtyItemTimer->stop(); + d->fullRepaintOnComlumnChange = true; + + updateGeometries(); +} + + +/*! + Returns the y coordinate of this item in the list view's + coordinate system. This function is normally much slower than + Q3ListView::itemAt(), but it works for all items whereas + Q3ListView::itemAt() normally only works for items on the screen. + + \sa Q3ListView::itemAt() Q3ListView::itemRect() Q3ListView::itemPos() +*/ + +int Q3ListViewItem::itemPos() const +{ + QStack<Q3ListViewItem *> s; + Q3ListViewItem * i = (Q3ListViewItem *)this; + while(i) { + s.push(i); + i = i->parentItem; + } + + int a = 0; + Q3ListViewItem * p = 0; + while(s.count()) { + i = s.pop(); + if (p) { + if (!p->configured) { + p->configured = true; + p->setup(); // ### virtual non-const function called in const + } + a += p->height(); + Q3ListViewItem * s = p->firstChild(); + while(s && s != i) { + a += s->totalHeight(); + s = s->nextSibling(); + } + } + p = i; + } + return a; +} + + +/*! + \fn void Q3ListView::removeItem(Q3ListViewItem *item) + + Removes the given \a item. Use takeItem() instead. +*/ + +/*! + Removes item \a i from the list view; \a i must be a top-level + item. The warnings regarding Q3ListViewItem::takeItem() apply to + this function, too. + + \sa insertItem() +*/ +void Q3ListView::takeItem(Q3ListViewItem * i) +{ + if (d->r) + d->r->takeItem(i); +} + + +void Q3ListView::openFocusItem() +{ + d->autoopenTimer->stop(); + if (d->focusItem && !d->focusItem->isOpen()) { + d->focusItem->setOpen(true); + d->focusItem->repaint(); + } +} + +static const int autoopenTime = 750; + +#ifndef QT_NO_DRAGANDDROP + +/*! \reimp */ + +void Q3ListView::contentsDragEnterEvent(QDragEnterEvent *e) +{ + d->oldFocusItem = d->focusItem; + Q3ListViewItem *i = d->focusItem; + d->focusItem = itemAt(contentsToViewport(e->pos())); + if (i) + i->repaint(); + if (d->focusItem) { + d->autoopenTimer->start(autoopenTime); + d->focusItem->dragEntered(); + d->focusItem->repaint(); + } + e->accept(); +} + +/*! \reimp */ + +void Q3ListView::contentsDragMoveEvent(QDragMoveEvent *e) +{ + Q3ListViewItem *i = d->focusItem; + d->focusItem = itemAt(contentsToViewport(e->pos())); + if (i) { + if (i != d->focusItem) + i->dragLeft(); + i->repaint(); + } + if (d->focusItem) { + if (i != d->focusItem) { + d->focusItem->dragEntered(); + d->autoopenTimer->stop(); + d->autoopenTimer->start(autoopenTime); + } + d->focusItem->repaint(); + } else { + d->autoopenTimer->stop(); + } + if ((i && i->dropEnabled() && i->acceptDrop(e)) || acceptDrops()) + e->accept(); + else + e->ignore(); +} + +/*! \reimp */ + +void Q3ListView::contentsDragLeaveEvent(QDragLeaveEvent *) +{ + d->autoopenTimer->stop(); + + if (d->focusItem) + d->focusItem->dragLeft(); + + setCurrentItem(d->oldFocusItem); + d->oldFocusItem = 0; +} + +/*! \reimp */ + +void Q3ListView::contentsDropEvent(QDropEvent *e) +{ + d->autoopenTimer->stop(); + + setCurrentItem(d->oldFocusItem); + Q3ListViewItem *i = itemAt(contentsToViewport(e->pos())); + if (i && i->dropEnabled() && i->acceptDrop(e)) { + i->dropped(e); + e->accept(); + } else if (acceptDrops()) { + emit dropped(e); + e->accept(); + } +} + +/*! + If the user presses the mouse on an item and starts moving the + mouse, and the item allow dragging (see + Q3ListViewItem::setDragEnabled()), this function is called to get a + drag object and a drag is started unless dragObject() returns 0. + + By default this function returns 0. You should reimplement it and + create a Q3DragObject depending on the selected items. +*/ + +Q3DragObject *Q3ListView::dragObject() +{ + return 0; +} + +/*! + Starts a drag. +*/ + +void Q3ListView::startDrag() +{ + if (!d->startDragItem) + return; + + d->startDragItem = 0; + d->buttonDown = false; + + Q3DragObject *drag = dragObject(); + if (!drag) + return; + + drag->drag(); +} + +#endif // QT_NO_DRAGANDDROP + +/*! + \property Q3ListView::defaultRenameAction + \brief What action to perform when the editor loses focus during renaming + + If this property is \c Accept, and the user renames an item and + the editor loses focus (without the user pressing Enter), the + item will still be renamed. If the property's value is \c Reject, + the item will not be renamed unless the user presses Enter. The + default is \c Reject. +*/ + +void Q3ListView::setDefaultRenameAction(RenameAction a) +{ + d->defRenameAction = a; +} + +Q3ListView::RenameAction Q3ListView::defaultRenameAction() const +{ + return d->defRenameAction; +} + +/*! + Returns true if an item is being renamed; otherwise returns false. +*/ + +bool Q3ListView::isRenaming() const +{ + return currentItem() && currentItem()->renameBox; +} + +/********************************************************************** + * + * Class Q3ListViewItemIterator + * + **********************************************************************/ + + +/*! + \class Q3ListViewItemIterator + \brief The Q3ListViewItemIterator class provides an iterator for collections of Q3ListViewItems. + + \compat + + Construct an instance of a Q3ListViewItemIterator, with either a + Q3ListView* or a Q3ListViewItem* as argument, to operate on the tree + of Q3ListViewItems, starting from the argument. + + A Q3ListViewItemIterator iterates over all the items from its + starting point. This means that it always makes the first child of + the current item the new current item. If there is no child, the + next sibling becomes the new current item; and if there is no next + sibling, the next sibling of the parent becomes current. + + The following example creates a list of all the items that have + been selected by the user, storing pointers to the items in a + QList: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 6 + + An alternative approach is to use an \c IteratorFlag: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3listview.cpp 7 + + A Q3ListViewItemIterator provides a convenient and easy way to + traverse a hierarchical Q3ListView. + + Multiple Q3ListViewItemIterators can operate on the tree of + Q3ListViewItems. A Q3ListView knows about all iterators operating on + its Q3ListViewItems. So when a Q3ListViewItem gets removed all + iterators that point to this item are updated and point to the + following item if possible, otherwise to a valid item before the + current one or to 0. Note however that deleting the parent item of + an item that an iterator points to is not safe. + + \sa Q3ListView, Q3ListViewItem +*/ + +/*! + \enum Q3ListViewItemIterator::IteratorFlag + + These flags can be passed to a Q3ListViewItemIterator constructor + (OR-ed together if more than one is used), so that the iterator + will only iterate over items that match the given flags. + + \value Visible + \value Invisible + \value Selected + \value Unselected + \value Selectable + \value NotSelectable + \value DragEnabled + \value DragDisabled + \value DropEnabled + \value DropDisabled + \value Expandable + \value NotExpandable + \value Checked + \value NotChecked +*/ + +/*! + Constructs an empty iterator. +*/ + +Q3ListViewItemIterator::Q3ListViewItemIterator() + : curr(0), listView(0), flags(0) +{ +} + +/*! + Constructs an iterator for the Q3ListView that contains the \a + item. The current iterator item is set to point to the \a item. +*/ + +Q3ListViewItemIterator::Q3ListViewItemIterator(Q3ListViewItem *item) + : curr(item), listView(0), flags(0) +{ + if (item) { + item->enforceSortOrderBackToRoot(); + listView = item->listView(); + } + if (listView) + listView->d->iterators.append(this); +} + +/*! + Constructs an iterator for the Q3ListView that contains the \a item + using the flags \a iteratorFlags. The current iterator item is set + to point to \a item or the next matching item if \a item doesn't + match the flags. + + \sa Q3ListViewItemIterator::IteratorFlag +*/ + +Q3ListViewItemIterator::Q3ListViewItemIterator(Q3ListViewItem *item, int iteratorFlags) + : curr(item), listView(0), flags(iteratorFlags) +{ + // go to next matching item if the current don't match + if (curr && !matchesFlags(curr)) + ++(*this); + + if (curr) { + curr->enforceSortOrderBackToRoot(); + listView = curr->listView(); + } + if (listView) + listView->d->iterators.append(this); +} + + +/*! + Constructs an iterator for the same Q3ListView as \a it. The + current iterator item is set to point on the current item of \a + it. +*/ + +Q3ListViewItemIterator::Q3ListViewItemIterator(const Q3ListViewItemIterator& it) + : curr(it.curr), listView(it.listView), flags(it.flags) +{ + if (listView) + listView->d->iterators.append(this); +} + +/*! + Constructs an iterator for the Q3ListView \a lv. The current + iterator item is set to point on the first child (Q3ListViewItem) + of \a lv. +*/ + +Q3ListViewItemIterator::Q3ListViewItemIterator(Q3ListView *lv) + : curr(lv->firstChild()), listView(lv), flags(0) +{ + if (listView) + listView->d->iterators.append(this); +} + +/*! + Constructs an iterator for the Q3ListView \a lv with the flags \a + iteratorFlags. The current iterator item is set to point on the + first child (Q3ListViewItem) of \a lv that matches the flags. + + \sa Q3ListViewItemIterator::IteratorFlag +*/ + +Q3ListViewItemIterator::Q3ListViewItemIterator(Q3ListView *lv, int iteratorFlags) + : curr (lv->firstChild()), listView(lv), flags(iteratorFlags) +{ + if (listView) + listView->d->iterators.append(this); + if (!matchesFlags(curr)) + ++(*this); +} + + + +/*! + Assignment. Makes a copy of \a it and returns a reference to its + iterator. +*/ + +Q3ListViewItemIterator &Q3ListViewItemIterator::operator=(const Q3ListViewItemIterator &it) +{ + if (listView) + listView->d->iterators.removeAll(this); + + listView = it.listView; + curr = it.curr; + flags = it.flags; + if (listView) + listView->d->iterators.append(this); + + // go to next matching item if the current don't match + if (curr && !matchesFlags(curr)) + ++(*this); + + return *this; +} + +/*! + Destroys the iterator. +*/ + +Q3ListViewItemIterator::~Q3ListViewItemIterator() +{ + if (listView) + listView->d->iterators.removeAll(this); +} + +/*! + Prefix ++. Makes the next item the new current item and returns + it. Returns 0 if the current item is the last item or the + Q3ListView is 0. +*/ + +Q3ListViewItemIterator &Q3ListViewItemIterator::operator++() +{ + if (!curr) + return *this; + + Q3ListViewItem *item = curr->firstChild(); + if (!item) { + while ((item = curr->nextSibling()) == 0 ) { + curr = curr->parent(); + if (curr == 0) + break; + } + } + curr = item; + // if the next one doesn't match the flags we try one more ahead + if (curr && !matchesFlags(curr)) + ++(*this); + return *this; +} + +/*! + \overload + + Postfix ++. Makes the next item the new current item and returns + the item that \e was the current item. +*/ + +const Q3ListViewItemIterator Q3ListViewItemIterator::operator++(int) +{ + Q3ListViewItemIterator oldValue = *this; + ++(*this); + return oldValue; +} + +/*! + Sets the current item to the item \a j positions after the current + item. If that item is beyond the last item, the current item is + set to 0. Returns the current item. +*/ + +Q3ListViewItemIterator &Q3ListViewItemIterator::operator+=(int j) +{ + while (curr && j--) + ++(*this); + + return *this; +} + +/*! + Prefix --. Makes the previous item the new current item and + returns it. Returns 0 if the current item is the first item or the + Q3ListView is 0. +*/ + +Q3ListViewItemIterator &Q3ListViewItemIterator::operator--() +{ + if (!curr) + return *this; + + if (!curr->parent()) { + // we are in the first depth + if (curr->listView()) { + if (curr->listView()->firstChild() != curr) { + // go the previous sibling + Q3ListViewItem *i = curr->listView()->firstChild(); + while (i && i->siblingItem != curr) + i = i->siblingItem; + + curr = i; + + if (i && i->firstChild()) { + // go to the last child of this item + Q3ListViewItemIterator it(curr->firstChild()); + for (; it.current() && it.current()->parent(); ++it) + curr = it.current(); + } + + if (curr && !matchesFlags(curr)) + --(*this); + + return *this; + } else { + //we are already the first child of the list view, so it's over + curr = 0; + return *this; + } + } else + return *this; + } else { + Q3ListViewItem *parent = curr->parent(); + + if (curr != parent->firstChild()) { + // go to the previous sibling + Q3ListViewItem *i = parent->firstChild(); + while (i && i->siblingItem != curr) + i = i->siblingItem; + + curr = i; + + if (i && i->firstChild()) { + // go to the last child of this item + Q3ListViewItemIterator it(curr->firstChild()); + for (; it.current() && it.current()->parent() != parent; ++it) + curr = it.current(); + } + + if (curr && !matchesFlags(curr)) + --(*this); + + return *this; + } else { + // make our parent the current item + curr = parent; + + if (curr && !matchesFlags(curr)) + --(*this); + + return *this; + } + } +} + +/*! + \overload + + Postfix --. Makes the previous item the new current item and + returns the item that \e was the current item. +*/ + +const Q3ListViewItemIterator Q3ListViewItemIterator::operator--(int) +{ + Q3ListViewItemIterator oldValue = *this; + --(*this); + return oldValue; +} + +/*! + Sets the current item to the item \a j positions before the + current item. If that item is before the first item, the current + item is set to 0. Returns the current item. +*/ + +Q3ListViewItemIterator &Q3ListViewItemIterator::operator-=(int j) +{ + while (curr && j--) + --(*this); + + return *this; +} + +/*! + Dereference operator. Returns a reference to the current item. The + same as current(). +*/ + +Q3ListViewItem* Q3ListViewItemIterator::operator*() +{ + if (curr != 0 && !matchesFlags(curr)) + qWarning("Q3ListViewItemIterator::operator*() curr out of sync"); + return curr; +} + +/*! + Returns iterator's current item. +*/ + +Q3ListViewItem *Q3ListViewItemIterator::current() const +{ + if (curr != 0 && !matchesFlags(curr)) + qWarning("Q3ListViewItemIterator::current() curr out of sync"); + return curr; +} + +/* + This function is called to notify the iterator that the current + item has been deleted, and sets the current item point to another + (valid) item or 0. +*/ + +void Q3ListViewItemIterator::currentRemoved() +{ + if (!curr) return; + + if (curr->parent()) + curr = curr->parent(); + else if (curr->nextSibling()) + curr = curr->nextSibling(); + else if (listView && listView->firstChild() && + listView->firstChild() != curr) + curr = listView->firstChild(); + else + curr = 0; +} + +/* + returns true if the item \a item matches all of the flags set for the iterator +*/ +bool Q3ListViewItemIterator::matchesFlags(const Q3ListViewItem *item) const +{ + if (!item) + return false; + + if (flags == 0) + return true; + + if (flags & Visible && !item->isVisible()) + return false; + if (flags & Invisible && item->isVisible()) + return false; + if (flags & Selected && !item->isSelected()) + return false; + if (flags & Unselected && item->isSelected()) + return false; + if (flags & Selectable && !item->isSelectable()) + return false; + if (flags & NotSelectable && item->isSelectable()) + return false; + if (flags & DragEnabled && !item->dragEnabled()) + return false; + if (flags & DragDisabled && item->dragEnabled()) + return false; + if (flags & DropEnabled && !item->dropEnabled()) + return false; + if (flags & DropDisabled && item->dropEnabled()) + return false; + if (flags & Expandable && !item->isExpandable()) + return false; + if (flags & NotExpandable && item->isExpandable()) + return false; + if (flags & Checked && !isChecked(item)) + return false; + if (flags & NotChecked && isChecked(item)) + return false; + + return true; +} + +/* + we want the iterator to check Q3CheckListItems as well, so we provide this convenience function + that checks if the rtti() is 1 which means Q3CheckListItem and if isOn is true, returns false otherwise. +*/ +bool Q3ListViewItemIterator::isChecked(const Q3ListViewItem *item) const +{ + if (item->rtti() == 1) + return ((const Q3CheckListItem*)item)->isOn(); + else return false; +} + +void Q3ListView::handleItemChange(Q3ListViewItem *old, bool shift, bool control) +{ + if (d->selectionMode == Single) { + // nothing + } else if (d->selectionMode == Extended) { + if (shift) { + selectRange(d->selectAnchor ? d->selectAnchor : old, + d->focusItem, false, true, (d->selectAnchor && !control) ? true : false); + } else if (!control) { + bool block = signalsBlocked(); + blockSignals(true); + selectAll(false); + blockSignals(block); + setSelected(d->focusItem, true); + } + } else if (d->selectionMode == Multi) { + if (shift) + selectRange(old, d->focusItem, true, false); + } +} + +void Q3ListView::startRename() +{ + if (!currentItem()) + return; + currentItem()->startRename(d->pressedColumn); + d->buttonDown = false; +} + +/* unselects items from to, including children, returns true if any items were unselected */ +bool Q3ListView::clearRange(Q3ListViewItem *from, Q3ListViewItem *to, bool includeFirst) +{ + if (!from || !to) + return false; + + // Swap + if (from->itemPos() > to->itemPos()) { + Q3ListViewItem *temp = from; + from = to; + to = temp; + } + + // Start on second? + if (!includeFirst) { + Q3ListViewItem *below = (from == to) ? from : from->itemBelow(); + if (below) + from = below; + } + + // Clear items <from, to> + bool changed = false; + + Q3ListViewItemIterator it(from); + while (it.current()) { + if (it.current()->isSelected()) { + it.current()->setSelected(false); + changed = true; + } + if (it.current() == to) + break; + ++it; + } + + // NOTE! This function does _not_ emit + // any signals about selection changed + return changed; +} + +void Q3ListView::selectRange(Q3ListViewItem *from, Q3ListViewItem *to, bool invert, bool includeFirst, bool clearSel) +{ + if (!from || !to) + return; + if (from == to && !includeFirst) + return; + bool swap = false; + if (to == from->itemAbove()) + swap = true; + if (!swap && from != to && from != to->itemAbove()) { + Q3ListViewItemIterator it(from); + bool found = false; + for (; it.current(); ++it) { + if (it.current() == to) { + found = true; + break; + } + } + if (!found) + swap = true; + } + if (swap) { + Q3ListViewItem *i = from; + from = to; + to = i; + if (!includeFirst) + to = to->itemAbove(); + } else { + if (!includeFirst) + from = from->itemBelow(); + } + + bool changed = false; + if (clearSel) { + Q3ListViewItemIterator it(firstChild()); + for (; it.current(); ++it) { + if (it.current()->selected) { + it.current()->setSelected(false); + changed = true; + } + } + it = Q3ListViewItemIterator(to); + for (; it.current(); ++it) { + if (it.current()->selected) { + it.current()->setSelected(false); + changed = true; + } + } + } + + for (Q3ListViewItem *i = from; i; i = i->itemBelow()) { + if (!invert) { + if (!i->selected && i->isSelectable()) { + i->setSelected(true); + changed = true; + } + } else { + bool sel = !i->selected; + if (((bool)i->selected != sel && sel && i->isSelectable()) || !sel) { + i->setSelected(sel); + changed = true; + } + } + if (i == to) + break; + } + if (changed) { + triggerUpdate(); + emit selectionChanged(); + } +} + +/* clears selection from anchor to old, selects from anchor to new, does not emit selectionChanged on change */ +bool Q3ListView::selectRange(Q3ListViewItem *newItem, Q3ListViewItem *oldItem, Q3ListViewItem *anchorItem) +{ + if (!newItem || !oldItem || !anchorItem) + return false; + + int anchorPos = anchorItem ? anchorItem->itemPos() : 0, + oldPos = oldItem ? oldItem->itemPos() : 0, + newPos = newItem->itemPos(); + Q3ListViewItem *top=0, *bottom=0; + if (anchorPos > newPos) { + top = newItem; + bottom = anchorItem; + } else { + top = anchorItem; + bottom = newItem; + } + + // removes the subControls of the old selection that will no longer be selected + bool changed = false; + int topPos = top ? top->itemPos() : 0, + bottomPos = bottom ? bottom->itemPos() : 0; + if (!(oldPos > topPos && oldPos < bottomPos)) { + if (oldPos < topPos) + changed = clearRange(oldItem, top); + else + changed = clearRange(bottom, oldItem); + } + + // selects the new (not already selected) items + Q3ListViewItemIterator lit(top); + for (; lit.current(); ++lit) { + if ((bool)lit.current()->selected != d->select) { + lit.current()->setSelected(d->select); + changed = true; + } + // Include bottom, then break + if (lit.current() == bottom) + break; + } + + return changed; +} + + +/*! + Finds the first list view item in column \a column, that matches + \a text and returns the item, or returns 0 of no such item could + be found. Pass OR-ed together \l ComparisonFlags values + in the \a compare flag, to control how the matching is performed. + The default comparison mode is case-sensitive, exact match. +*/ + +Q3ListViewItem *Q3ListView::findItem(const QString& text, int column, + ComparisonFlags compare) const +{ + if (text.isEmpty() && !(compare & ExactMatch)) + return 0; + + if (compare == Qt::CaseSensitive || compare == 0) + compare |= ExactMatch; + + QString itmtxt; + QString comtxt = text; + if (!(compare & Qt::CaseSensitive)) + comtxt = comtxt.toLower(); + + Q3ListViewItemIterator it(d->focusItem ? d->focusItem : firstChild()); + Q3ListViewItem *sentinel = 0; + Q3ListViewItem *item; + Q3ListViewItem *beginsWithItem = 0; + Q3ListViewItem *endsWithItem = 0; + Q3ListViewItem *containsItem = 0; + + for (int pass = 0; pass < 2; pass++) { + while ((item = it.current()) != sentinel) { + itmtxt = item->text(column); + if (!(compare & CaseSensitive)) + itmtxt = itmtxt.toLower(); + + if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt) + return item; + if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt)) + beginsWithItem = containsItem = item; + if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt)) + endsWithItem = containsItem = item; + if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt)) + containsItem = item; + ++it; + } + + it = Q3ListViewItemIterator(firstChild()); + sentinel = d->focusItem ? d->focusItem : firstChild(); + } + + // Obey the priorities + if (beginsWithItem) + return beginsWithItem; + else if (endsWithItem) + return endsWithItem; + else if (containsItem) + return containsItem; + return 0; +} + +/*! + Hides the column specified at \a column. This is a convenience + function that calls setColumnWidth(column, 0). + + Note: The user may still be able to resize the hidden column using + the header handles. To prevent this, call setResizeEnabled(false, + \a column) on the list views header. + + \sa setColumnWidth() +*/ + +void Q3ListView::hideColumn(int column) +{ + setColumnWidth(column, 0); +} + +/*! Adjusts the column \a col to its preferred width */ + +void Q3ListView::adjustColumn(int col) +{ + if (col < 0 || col > (int)d->column.count() - 1 || d->h->isStretchEnabled(col)) + return; + + int oldw = d->h->sectionSize(col); + + int w = d->h->sectionSizeHint(col, fontMetrics()).width(); + if (d->h->iconSet(col)) + w += d->h->iconSet(col)->pixmap().width(); + w = qMax(w, 20); + QFontMetrics fm(fontMetrics()); + Q3ListViewItem* item = firstChild(); + int rootDepth = rootIsDecorated() ? treeStepSize() : 0; + while (item) { + int iw = item->width(fm, this, col); + if (0 == col) + iw += itemMargin() + rootDepth + item->depth()*treeStepSize() - 1; + w = qMax(w, iw); + item = item->itemBelow(); + } + w = qMax(w, QApplication::globalStrut().width()); + + d->h->adjustHeaderSize(oldw - w); + if (oldw != w) { + d->fullRepaintOnComlumnChange = true; + d->h->resizeSection(col, w); + emit d->h->sizeChange(col, oldw, w); + } +} + +/*! + \enum Q3ListView::StringComparisonMode + + This enum type is used to set the string comparison mode when + searching for an item. We'll refer to the string being searched + as the 'target' string. + + \value CaseSensitive The strings must match case sensitively. + \value ExactMatch The target and search strings must match exactly. + \value BeginsWith The target string begins with the search string. + \value EndsWith The target string ends with the search string. + \value Contains The target string contains the search string. + + If you OR these flags together (excluding \c CaseSensitive), the + search criteria be applied in the following order: \c ExactMatch, + \c BeginsWith, \c EndsWith, \c Contains. + + Matching is case-insensitive unless \c CaseSensitive is set. \c + CaseSensitive can be OR-ed with any combination of the other + flags. + + \sa ComparisonFlags +*/ + +/*! + \typedef Q3ListView::ComparisonFlags + + This typedef is used in Q3ListView's API for values that are OR'd + combinations of \l StringComparisonMode values. + + \sa StringComparisonMode +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_LISTVIEW diff --git a/src/qt3support/itemviews/q3listview.h b/src/qt3support/itemviews/q3listview.h new file mode 100644 index 0000000..8a1ceda --- /dev/null +++ b/src/qt3support/itemviews/q3listview.h @@ -0,0 +1,609 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3LISTVIEW_H +#define Q3LISTVIEW_H + +#include <Qt3Support/q3scrollview.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_LISTVIEW + +class QPixmap; +class QFont; +class Q3Header; +class QIcon; + +class Q3ListView; +struct Q3ListViewPrivate; +struct Q3CheckListItemPrivate; +class Q3ListViewItemIterator; +struct Q3ListViewItemIteratorPrivate; +class Q3DragObject; +class QMimeSource; +class QLineEdit; +class Q3ListViewToolTip; + +class Q_COMPAT_EXPORT Q3ListViewItem +{ + friend class Q3ListViewItemIterator; + friend class Q3ListViewToolTip; + +public: + Q3ListViewItem(Q3ListView * parent); + Q3ListViewItem(Q3ListViewItem * parent); + Q3ListViewItem(Q3ListView * parent, Q3ListViewItem * after); + Q3ListViewItem(Q3ListViewItem * parent, Q3ListViewItem * after); + + Q3ListViewItem(Q3ListView * parent, + const QString&, const QString& = QString(), + const QString& = QString(), const QString& = QString(), + const QString& = QString(), const QString& = QString(), + const QString& = QString(), const QString& = QString()); + Q3ListViewItem(Q3ListViewItem * parent, + const QString&, const QString& = QString(), + const QString& = QString(), const QString& = QString(), + const QString& = QString(), const QString& = QString(), + const QString& = QString(), const QString& = QString()); + + Q3ListViewItem(Q3ListView * parent, Q3ListViewItem * after, + const QString&, const QString& = QString(), + const QString& = QString(), const QString& = QString(), + const QString& = QString(), const QString& = QString(), + const QString& = QString(), const QString& = QString()); + Q3ListViewItem(Q3ListViewItem * parent, Q3ListViewItem * after, + const QString&, const QString& = QString(), + const QString& = QString(), const QString& = QString(), + const QString& = QString(), const QString& = QString(), + const QString& = QString(), const QString& = QString()); + virtual ~Q3ListViewItem(); + + virtual void insertItem(Q3ListViewItem *); + virtual void takeItem(Q3ListViewItem *); + virtual void removeItem(Q3ListViewItem *item) { takeItem(item); } + + int height() const; + virtual void invalidateHeight(); + int totalHeight() const; + virtual int width(const QFontMetrics&, + const Q3ListView*, int column) const; + void widthChanged(int column=-1) const; + int depth() const; + + virtual void setText(int, const QString &); + virtual QString text(int) const; + + virtual void setPixmap(int, const QPixmap &); + virtual const QPixmap * pixmap(int) const; + + virtual QString key(int, bool) const; + virtual int compare(Q3ListViewItem *i, int col, bool) const; + virtual void sortChildItems(int, bool); + + int childCount() const { return nChildren; } + + bool isOpen() const { return open; } + virtual void setOpen(bool); + virtual void setup(); + + virtual void setSelected(bool); + bool isSelected() const { return selected; } + + virtual void paintCell(QPainter *, const QColorGroup & cg, + int column, int width, int alignment); + virtual void paintBranches(QPainter * p, const QColorGroup & cg, int w, int y, int h); + virtual void paintFocus(QPainter *, const QColorGroup & cg, const QRect & r); + + Q3ListViewItem * firstChild() const; + Q3ListViewItem * nextSibling() const { return siblingItem; } + Q3ListViewItem * parent() const; + + Q3ListViewItem * itemAbove() const; + Q3ListViewItem * itemBelow() const; + + int itemPos() const; + + Q3ListView *listView() const; + + virtual void setSelectable(bool enable); + bool isSelectable() const { return selectable && enabled; } + + virtual void setExpandable(bool); + bool isExpandable() const { return expandable; } + + void repaint() const; + + virtual void sort(); + void moveItem(Q3ListViewItem *after); + + virtual void setDragEnabled(bool allow); + virtual void setDropEnabled(bool allow); + bool dragEnabled() const; + bool dropEnabled() const; + virtual bool acceptDrop(const QMimeSource *mime) const; + + void setVisible(bool b); + bool isVisible() const; + + virtual void setRenameEnabled(int col, bool b); + bool renameEnabled(int col) const; + virtual void startRename(int col); + + virtual void setEnabled(bool b); + bool isEnabled() const; + + virtual int rtti() const; + enum { RTTI = 0 }; + + virtual void setMultiLinesEnabled(bool b); + bool multiLinesEnabled() const; + +protected: + virtual void enforceSortOrder() const; + virtual void setHeight(int); + virtual void activate(); + + bool activatedPos(QPoint &); +#ifndef QT_NO_DRAGANDDROP + virtual void dropped(QDropEvent *e); +#endif + virtual void dragEntered(); + virtual void dragLeft(); + virtual void okRename(int col); + virtual void cancelRename(int col); + + void ignoreDoubleClick(); + +private: + void init(); + void moveToJustAfter(Q3ListViewItem *); + void enforceSortOrderBackToRoot(); + void removeRenameBox(); + + int ownHeight; + int maybeTotalHeight; + int nChildren; + + uint lsc: 14; + uint lso: 1; + uint open : 1; + uint selected : 1; + uint selectable: 1; + uint configured: 1; + uint expandable: 1; + uint is_root: 1; + uint allow_drag : 1; + uint allow_drop : 1; + uint visible : 1; + uint enabled : 1; + uint mlenabled : 1; + + Q3ListViewItem * parentItem; + Q3ListViewItem * siblingItem; + Q3ListViewItem * childItem; + QLineEdit *renameBox; + int renameCol; + + void * columns; + + friend class Q3ListView; +}; + +class Q3CheckListItem; + +class Q_COMPAT_EXPORT Q3ListView : public Q3ScrollView +{ + friend class Q3ListViewItemIterator; + friend class Q3ListViewItem; + friend class Q3CheckListItem; + friend class Q3ListViewToolTip; + + Q_OBJECT + Q_ENUMS(SelectionMode ResizeMode RenameAction) + Q_PROPERTY(int columns READ columns) + Q_PROPERTY(bool multiSelection READ isMultiSelection WRITE setMultiSelection DESIGNABLE false) + Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) + Q_PROPERTY(int childCount READ childCount) + Q_PROPERTY(bool allColumnsShowFocus READ allColumnsShowFocus WRITE setAllColumnsShowFocus) + Q_PROPERTY(bool showSortIndicator READ showSortIndicator WRITE setShowSortIndicator) + Q_PROPERTY(int itemMargin READ itemMargin WRITE setItemMargin) + Q_PROPERTY(bool rootIsDecorated READ rootIsDecorated WRITE setRootIsDecorated) + Q_PROPERTY(bool showToolTips READ showToolTips WRITE setShowToolTips) + Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) + Q_PROPERTY(int treeStepSize READ treeStepSize WRITE setTreeStepSize) + Q_PROPERTY(RenameAction defaultRenameAction READ defaultRenameAction WRITE setDefaultRenameAction) + +public: + Q3ListView(QWidget* parent=0, const char* name=0, Qt::WindowFlags f = 0); + ~Q3ListView(); + + int treeStepSize() const; + virtual void setTreeStepSize(int); + + virtual void insertItem(Q3ListViewItem *); + virtual void takeItem(Q3ListViewItem *); + virtual void removeItem(Q3ListViewItem *item) { takeItem(item); } + + Q3Header * header() const; + + virtual int addColumn(const QString &label, int size = -1); + virtual int addColumn(const QIcon& icon, const QString &label, int size = -1); + virtual void removeColumn(int index); + virtual void setColumnText(int column, const QString &label); + virtual void setColumnText(int column, const QIcon& icon, const QString &label); + QString columnText(int column) const; + virtual void setColumnWidth(int column, int width); + int columnWidth(int column) const; + enum WidthMode { Manual, Maximum }; + virtual void setColumnWidthMode(int column, WidthMode); + WidthMode columnWidthMode(int column) const; + int columns() const; + + virtual void setColumnAlignment(int, int); + int columnAlignment(int) const; + + void show(); + + QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + Q3ListViewItem * itemAt(const QPoint & screenPos) const; + QRect itemRect(const Q3ListViewItem *) const; + int itemPos(const Q3ListViewItem *); + + void ensureItemVisible(const Q3ListViewItem *); + + void repaintItem(const Q3ListViewItem *) const; + + virtual void setMultiSelection(bool enable); + bool isMultiSelection() const; + + enum SelectionMode { Single, Multi, Extended, NoSelection }; + void setSelectionMode(SelectionMode mode); + SelectionMode selectionMode() const; + + virtual void clearSelection(); + virtual void setSelected(Q3ListViewItem *, bool); + void setSelectionAnchor(Q3ListViewItem *); + bool isSelected(const Q3ListViewItem *) const; + Q3ListViewItem * selectedItem() const; + virtual void setOpen(Q3ListViewItem *, bool); + bool isOpen(const Q3ListViewItem *) const; + + virtual void setCurrentItem(Q3ListViewItem *); + Q3ListViewItem * currentItem() const; + + Q3ListViewItem * firstChild() const; + Q3ListViewItem * lastItem() const; + + int childCount() const; + + virtual void setAllColumnsShowFocus(bool); + bool allColumnsShowFocus() const; + + virtual void setItemMargin(int); + int itemMargin() const; + + virtual void setRootIsDecorated(bool); + bool rootIsDecorated() const; + + virtual void setSorting(int column, bool ascending = true); + int sortColumn() const; + void setSortColumn(int column); + Qt::SortOrder sortOrder() const; + void setSortOrder(Qt::SortOrder order); + virtual void sort(); + + bool eventFilter(QObject * o, QEvent *); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + virtual void setShowSortIndicator(bool show); + bool showSortIndicator() const; + virtual void setShowToolTips(bool b); + bool showToolTips() const; + + enum ResizeMode { NoColumn, AllColumns, LastColumn }; + virtual void setResizeMode(ResizeMode m); + ResizeMode resizeMode() const; + + enum StringComparisonMode { + CaseSensitive = 0x00001, // 0 0001 + BeginsWith = 0x00002, // 0 0010 + EndsWith = 0x00004, // 0 0100 + Contains = 0x00008, // 0 1000 + ExactMatch = 0x00010 // 1 0000 + }; + typedef uint ComparisonFlags; + Q3ListViewItem * findItem(const QString& text, int column, + ComparisonFlags = ExactMatch | Qt::CaseSensitive ) const; + + enum RenameAction { Accept, Reject }; + virtual void setDefaultRenameAction(RenameAction a); + RenameAction defaultRenameAction() const; + bool isRenaming() const; + + void hideColumn(int column); + +public Q_SLOTS: + virtual void clear(); + virtual void invertSelection(); + virtual void selectAll(bool select); + void triggerUpdate(); + void setContentsPos(int x, int y); + void adjustColumn(int col); + +Q_SIGNALS: + void selectionChanged(); + void selectionChanged(Q3ListViewItem *); + void currentChanged(Q3ListViewItem *); + void clicked(Q3ListViewItem *); + void clicked(Q3ListViewItem *, const QPoint &, int); + void pressed(Q3ListViewItem *); + void pressed(Q3ListViewItem *, const QPoint &, int); + + void doubleClicked(Q3ListViewItem *); + void doubleClicked(Q3ListViewItem *, const QPoint&, int); + void returnPressed(Q3ListViewItem *); + void spacePressed(Q3ListViewItem *); + void rightButtonClicked(Q3ListViewItem *, const QPoint&, int); + void rightButtonPressed(Q3ListViewItem *, const QPoint&, int); + void mouseButtonPressed(int, Q3ListViewItem *, const QPoint& , int); + void mouseButtonClicked(int, Q3ListViewItem *, const QPoint&, int); + + void contextMenuRequested(Q3ListViewItem *, const QPoint &, int); + + void onItem(Q3ListViewItem *item); + void onViewport(); + + void expanded(Q3ListViewItem *item); + void collapsed(Q3ListViewItem *item); +#ifndef QT_NO_DRAGANDDROP + void dropped(QDropEvent *e); +#endif + void itemRenamed(Q3ListViewItem *item, int col, const QString &); + void itemRenamed(Q3ListViewItem *item, int col ); + +protected: + void contentsMousePressEvent(QMouseEvent * e); + void contentsMouseReleaseEvent(QMouseEvent * e); + void contentsMouseMoveEvent(QMouseEvent * e); + void contentsMouseDoubleClickEvent(QMouseEvent * e); + void contentsContextMenuEvent(QContextMenuEvent * e); +#ifndef QT_NO_DRAGANDDROP + void contentsDragEnterEvent(QDragEnterEvent *e); + void contentsDragMoveEvent(QDragMoveEvent *e); + void contentsDragLeaveEvent(QDragLeaveEvent *e); + void contentsDropEvent(QDropEvent *e); + virtual Q3DragObject *dragObject(); + virtual void startDrag(); +#endif + + void focusInEvent(QFocusEvent * e); + void focusOutEvent(QFocusEvent * e); + + void keyPressEvent(QKeyEvent *e); + + void resizeEvent(QResizeEvent *e); + void viewportResizeEvent(QResizeEvent *e); + + void showEvent(QShowEvent *); + + void drawContentsOffset(QPainter *, int ox, int oy, + int cx, int cy, int cw, int ch); + + virtual void paintEmptyArea(QPainter *, const QRect &); + void changeEvent(QEvent *); + +protected Q_SLOTS: + void updateContents(); + void doAutoScroll(); + +private Q_SLOTS: + void changeSortColumn(int); + void handleIndexChange(); + void updateDirtyItems(); + void makeVisible(); + void handleSizeChange(int, int, int); + void startRename(); + void openFocusItem(); + +private: + Q_DISABLE_COPY(Q3ListView) + + void contentsMousePressEventEx(QMouseEvent * e); + void contentsMouseReleaseEventEx(QMouseEvent * e); + void init(); + void updateGeometries(); + void buildDrawableList() const; + void reconfigureItems(); + void widthChanged(const Q3ListViewItem*, int c); + void handleItemChange(Q3ListViewItem *old, bool shift, bool control); + void selectRange(Q3ListViewItem *from, Q3ListViewItem *to, bool invert, bool includeFirst, bool clearSel = false); + bool selectRange(Q3ListViewItem *newItem, Q3ListViewItem *oldItem, Q3ListViewItem *anchorItem); + bool clearRange(Q3ListViewItem *from, Q3ListViewItem *to, bool includeFirst = true); + void doAutoScroll(const QPoint &cursorPos); + + Q3ListViewPrivate *d; +}; + + +class Q_COMPAT_EXPORT Q3CheckListItem : public Q3ListViewItem +{ +public: + enum Type { RadioButton, + CheckBox, + Controller, + RadioButtonController=Controller, + CheckBoxController }; + + enum ToggleState { Off, NoChange, On }; + + Q3CheckListItem(Q3CheckListItem *parent, const QString &text, + Type = RadioButtonController); + Q3CheckListItem(Q3CheckListItem *parent, Q3ListViewItem *after, + const QString &text, Type = RadioButtonController); + Q3CheckListItem(Q3ListViewItem *parent, const QString &text, + Type = RadioButtonController); + Q3CheckListItem(Q3ListViewItem *parent, Q3ListViewItem *after, + const QString &text, Type = RadioButtonController); + Q3CheckListItem(Q3ListView *parent, const QString &text, + Type = RadioButtonController); + Q3CheckListItem(Q3ListView *parent, Q3ListViewItem *after, + const QString &text, Type = RadioButtonController); + Q3CheckListItem(Q3ListViewItem *parent, const QString &text, + const QPixmap &); + Q3CheckListItem(Q3ListView *parent, const QString &text, + const QPixmap &); + ~Q3CheckListItem(); + + void paintCell(QPainter *, const QColorGroup & cg, + int column, int width, int alignment); + virtual void paintFocus(QPainter *, const QColorGroup &cg, + const QRect & r); + int width(const QFontMetrics&, const Q3ListView*, int column) const; + void setup(); + + virtual void setOn(bool); + bool isOn() const { return on; } + Type type() const { return myType; } + QString text() const { return Q3ListViewItem::text(0); } + QString text(int n) const { return Q3ListViewItem::text(n); } + + void setTristate(bool); + bool isTristate() const; + ToggleState state() const; + void setState(ToggleState s); + + int rtti() const; + enum { RTTI = 1 }; + +protected: + void activate(); + void turnOffChild(); + virtual void stateChange(bool); + +private: + void init(); + ToggleState internalState() const; + void setStoredState(ToggleState newState, Q3CheckListItem *key); + ToggleState storedState(Q3CheckListItem *key) const; + void stateChange(ToggleState s); + void restoreState(Q3CheckListItem *key, int depth = 0); + void updateController(bool update = true , bool store = false); + void updateStoredState(Q3CheckListItem *key); + void setState(ToggleState s, bool update, bool store); + void setCurrentState(ToggleState s); + + Type myType; + bool on; + Q3CheckListItemPrivate *d; +}; + +class Q_COMPAT_EXPORT Q3ListViewItemIterator +{ + friend struct Q3ListViewPrivate; + friend class Q3ListView; + friend class Q3ListViewItem; + +public: + enum IteratorFlag { + Visible = 0x00000001, + Invisible = 0x00000002, + Selected = 0x00000004, + Unselected = 0x00000008, + Selectable = 0x00000010, + NotSelectable = 0x00000020, + DragEnabled = 0x00000040, + DragDisabled = 0x00000080, + DropEnabled = 0x00000100, + DropDisabled = 0x00000200, + Expandable = 0x00000400, + NotExpandable = 0x00000800, + Checked = 0x00001000, + NotChecked = 0x00002000 + }; + + Q3ListViewItemIterator(); + Q3ListViewItemIterator(Q3ListViewItem *item); + Q3ListViewItemIterator(Q3ListViewItem *item, int iteratorFlags); + + Q3ListViewItemIterator(const Q3ListViewItemIterator &it); + Q3ListViewItemIterator(Q3ListView *lv); + Q3ListViewItemIterator(Q3ListView *lv, int iteratorFlags); + + Q3ListViewItemIterator &operator=(const Q3ListViewItemIterator &it); + + ~Q3ListViewItemIterator(); + + Q3ListViewItemIterator &operator++(); + const Q3ListViewItemIterator operator++(int); + Q3ListViewItemIterator &operator+=(int j); + + Q3ListViewItemIterator &operator--(); + const Q3ListViewItemIterator operator--(int); + Q3ListViewItemIterator &operator-=(int j); + + Q3ListViewItem* operator*(); + Q3ListViewItem *current() const; + +private: + Q3ListViewItem *curr; + Q3ListView *listView; + int flags; + + void currentRemoved(); + bool matchesFlags(const Q3ListViewItem*) const; + bool testPair(Q3ListViewItemIterator::IteratorFlag, Q3ListViewItemIterator::IteratorFlag, bool) const; + bool isChecked(const Q3ListViewItem*) const; +}; + +#endif // QT_NO_LISTVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3LISTVIEW_H diff --git a/src/qt3support/itemviews/q3table.cpp b/src/qt3support/itemviews/q3table.cpp new file mode 100644 index 0000000..658b0ad --- /dev/null +++ b/src/qt3support/itemviews/q3table.cpp @@ -0,0 +1,7333 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglobal.h" +#if defined(Q_CC_BOR) +// needed for qsort() because of a std namespace problem on Borland +#include "qplatformdefs.h" +#endif + +#include "q3table.h" + + +#include <qpainter.h> +#include <qlineedit.h> +#include <qcursor.h> +#include <qapplication.h> +#include <qtimer.h> +#include <qicon.h> +#include <q3combobox.h> +#include <qstyleoption.h> +#include <qcheckbox.h> +#include <q3dragobject.h> +#include <qevent.h> +#include <q3listbox.h> +#include <qstyle.h> +#include <q3datatable.h> +#include <qvalidator.h> +#include <q3button.h> + +#include <stdlib.h> +#include <limits.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt; + +class Q3HeaderData; +extern bool qt_get_null_label_bit(Q3HeaderData *data, int section); +extern void qt_set_null_label_bit(Q3HeaderData *data, int section, bool b); + +static bool qt_update_cell_widget = true; +static bool qt_table_clipper_enabled = true; +#ifndef QT_INTERNAL_TABLE +Q_COMPAT_EXPORT +#endif +void qt_set_table_clipper_enabled(bool enabled) +{ + qt_table_clipper_enabled = enabled; +} + +class Q_COMPAT_EXPORT Q3TableHeader : public Q3Header +{ + friend class Q3Table; + Q_OBJECT + +public: + enum SectionState { + Normal, + Bold, + Selected + }; + + Q3TableHeader(int, Q3Table *t, QWidget* parent=0, const char* name=0); + ~Q3TableHeader() {}; + void addLabel(const QString &s, int size); + void setLabel(int section, const QString & s, int size = -1); + void setLabel(int section, const QIconSet & iconset, const QString & s, + int size = -1); + + void setLabels(const QStringList & labels); + + void removeLabel(int section); + + void setSectionState(int s, SectionState state); + void setSectionStateToAll(SectionState state); + SectionState sectionState(int s) const; + + int sectionSize(int section) const; + int sectionPos(int section) const; + int sectionAt(int section) const; + + void setSectionStretchable(int s, bool b); + bool isSectionStretchable(int s) const; + + void updateCache(); + +signals: + void sectionSizeChanged(int s); + +protected: + void paintEvent(QPaintEvent *e); + void paintSection(QPainter *p, int index, const QRect& fr); + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + void resizeEvent(QResizeEvent *e); + +private slots: + void doAutoScroll(); + void sectionWidthChanged(int col, int os, int ns); + void indexChanged(int sec, int oldIdx, int newIdx); + void updateStretches(); + void updateWidgetStretches(); + +private: + void updateSelections(); + void saveStates(); + void setCaching(bool b); + void swapSections(int oldIdx, int newIdx, bool swapTable = true); + bool doSelection(QMouseEvent *e); + void sectionLabelChanged(int section); + void resizeArrays(int n); + +private: + Q3MemArray<int> states, oldStates; + Q3MemArray<bool> stretchable; + Q3MemArray<int> sectionSizes, sectionPoses; + bool mousePressed; + int pressPos, startPos, endPos; + Q3Table *table; + QTimer *autoScrollTimer; + QWidget *line1, *line2; + bool caching; + int resizedSection; + bool isResizing; + int numStretches; + QTimer *stretchTimer, *widgetStretchTimer; + Q3TableHeaderPrivate *d; + +}; + +#ifdef _WS_QWS_ +# define NO_LINE_WIDGET +#endif + + + +struct Q3TablePrivate +{ + Q3TablePrivate() : hasRowSpan(false), hasColSpan(false), + inMenuMode(false), redirectMouseEvent(false) + { + hiddenRows.setAutoDelete(true); + hiddenCols.setAutoDelete(true); + } + uint hasRowSpan : 1; + uint hasColSpan : 1; + uint inMenuMode : 1; + uint redirectMouseEvent : 1; + Q3IntDict<int> hiddenRows, hiddenCols; + QTimer *geomTimer; + int lastVisRow; + int lastVisCol; +}; + +struct Q3TableHeaderPrivate +{ +#ifdef NO_LINE_WIDGET + int oldLinePos; +#endif +}; + +static bool isRowSelection(Q3Table::SelectionMode selMode) +{ + return selMode == Q3Table::SingleRow || selMode == Q3Table::MultiRow; +} + +/*! + \class Q3TableSelection + \brief The Q3TableSelection class provides access to a selected area in a + Q3Table. + + \compat + + The selection is a rectangular set of cells in a Q3Table. One of + the rectangle's cells is called the anchor cell; this is the cell + that was selected first. The init() function sets the anchor and + the selection rectangle to exactly this cell; the expandTo() + function expands the selection rectangle to include additional + cells. + + There are various access functions to find out about the area: + anchorRow() and anchorCol() return the anchor's position; + leftCol(), rightCol(), topRow() and bottomRow() return the + rectangle's four edges. All four are part of the selection. + + A newly created Q3TableSelection is inactive -- isActive() returns + false. You must use init() and expandTo() to activate it. + + \sa Q3Table Q3Table::addSelection() Q3Table::selection() + Q3Table::selectCells() Q3Table::selectRow() Q3Table::selectColumn() +*/ + +/*! + Creates an inactive selection. Use init() and expandTo() to + activate it. +*/ + +Q3TableSelection::Q3TableSelection() + : active(false), inited(false), tRow(-1), lCol(-1), + bRow(-1), rCol(-1), aRow(-1), aCol(-1) +{ +} + +/*! + Creates an active selection, starting at \a start_row and \a + start_col, ending at \a end_row and \a end_col. +*/ + +Q3TableSelection::Q3TableSelection(int start_row, int start_col, int end_row, int end_col) + : active(false), inited(false), tRow(-1), lCol(-1), + bRow(-1), rCol(-1), aRow(-1), aCol(-1) +{ + init(start_row, start_col); + expandTo(end_row, end_col); +} + +/*! + Sets the selection anchor to cell \a row, \a col and the selection + to only contain this cell. The selection is not active until + expandTo() is called. + + To extend the selection to include additional cells, call + expandTo(). + + \sa isActive() +*/ + +void Q3TableSelection::init(int row, int col) +{ + aCol = lCol = rCol = col; + aRow = tRow = bRow = row; + active = false; + inited = true; +} + +/*! + Expands the selection to include cell \a row, \a col. The new + selection rectangle is the bounding rectangle of \a row, \a col + and the previous selection rectangle. After calling this function + the selection is active. + + If you haven't called init(), this function does nothing. + + \sa init() isActive() +*/ + +void Q3TableSelection::expandTo(int row, int col) +{ + if (!inited) + return; + active = true; + + if (row < aRow) { + tRow = row; + bRow = aRow; + } else { + tRow = aRow; + bRow = row; + } + + if (col < aCol) { + lCol = col; + rCol = aCol; + } else { + lCol = aCol; + rCol = col; + } +} + +/*! + Returns true if \a s includes the same cells as the selection; + otherwise returns false. +*/ + +bool Q3TableSelection::operator==(const Q3TableSelection &s) const +{ + return (s.active == active && + s.tRow == tRow && s.bRow == bRow && + s.lCol == lCol && s.rCol == rCol); +} + +/*! + \fn bool Q3TableSelection::operator!=(const Q3TableSelection &s) const + + Returns true if \a s does not include the same cells as the + selection; otherwise returns false. +*/ + + +/*! + \fn int Q3TableSelection::topRow() const + + Returns the top row of the selection. + + \sa bottomRow() leftCol() rightCol() +*/ + +/*! + \fn int Q3TableSelection::bottomRow() const + + Returns the bottom row of the selection. + + \sa topRow() leftCol() rightCol() +*/ + +/*! + \fn int Q3TableSelection::leftCol() const + + Returns the left column of the selection. + + \sa topRow() bottomRow() rightCol() +*/ + +/*! + \fn int Q3TableSelection::rightCol() const + + Returns the right column of the selection. + + \sa topRow() bottomRow() leftCol() +*/ + +/*! + \fn int Q3TableSelection::anchorRow() const + + Returns the anchor row of the selection. + + \sa anchorCol() expandTo() +*/ + +/*! + \fn int Q3TableSelection::anchorCol() const + + Returns the anchor column of the selection. + + \sa anchorRow() expandTo() +*/ + +/*! + \fn int Q3TableSelection::numRows() const + + Returns the number of rows in the selection. + + \sa numCols() +*/ +int Q3TableSelection::numRows() const +{ + return (tRow < 0) ? 0 : bRow - tRow + 1; +} + +/*! + Returns the number of columns in the selection. + + \sa numRows() +*/ +int Q3TableSelection::numCols() const +{ + return (lCol < 0) ? 0 : rCol - lCol + 1; +} + +/*! + \fn bool Q3TableSelection::isActive() const + + Returns whether the selection is active or not. A selection is + active after init() \e and expandTo() have been called. +*/ + +/*! + \fn bool Q3TableSelection::isEmpty() const + + Returns whether the selection is empty or not. + + \sa numRows(), numCols() +*/ + +/*! + \class Q3TableItem + \brief The Q3TableItem class provides the cell content for Q3Table cells. + + \compat + + For many applications Q3TableItems are ideal for presenting and + editing the contents of Q3Table cells. In situations where you need + to create very large tables you may prefer an alternative approach + to using Q3TableItems: see the notes on large tables. + + A Q3TableItem contains a cell's data, by default, a string and a + pixmap. The table item also holds the cell's display size and how + the data should be aligned. The table item specifies the cell's + \l EditType and the editor used for in-place editing (by default a + QLineEdit). If you want checkboxes use \l{Q3CheckTableItem}, and if + you want comboboxes use \l{Q3ComboTableItem}. The \l EditType (set + in the constructor) determines whether the cell's contents may be + edited. + + If a pixmap is specified it is displayed to the left of any text. + You can change the text or pixmap with setText() and setPixmap() + respectively. For text you can use setWordWrap(). + + When sorting table items the key() function is used; by default + this returns the table item's text(). Reimplement key() to + customize how your table items will sort. + + Table items are inserted into a table using Q3Table::setItem(). If + you insert an item into a cell that already contains a table item + the original item will be deleted. + + Example: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 0 + + You can move a table item from one cell to another, in the same or + a different table, using Q3Table::takeItem() and Q3Table::setItem() + but see also Q3Table::swapCells(). + + Table items can be deleted with delete in the standard way; the + table and cell will be updated accordingly. + + Note, that if you have a table item that is not currently in a table + then anything you do to that item other than insert it into a table + will result in undefined behaviour. + + Reimplement createEditor() and setContentFromEditor() if you want + to use your own widget instead of a QLineEdit for editing cell + contents. Reimplement paint() if you want to display custom + content. + + It is important to ensure that your custom widget can accept the + keyboard focus, so that the user can use the tab key to navigate the + table as normal. Therefore, if the widget returned by createEditor() + does not itself accept the keyboard focus, it is necessary to + nominate a child widget to do so on its behalf. For example, a + QHBox with two child QLineEdit widgets may use one of them to + accept the keyboard focus: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 1 + + By default, table items may be replaced by new Q3TableItems + during the lifetime of a Q3Table. Therefore, if you create your + own subclass of Q3TableItem, and you want to ensure that + this does not happen, you must call setReplaceable(false) + in the constructor of your subclass. + + \img qtableitems.png Table Items + + \sa Q3CheckTableItem Q3ComboTableItem + +*/ + +/*! + \fn Q3Table *Q3TableItem::table() const + + Returns the Q3Table the table item belongs to. + + \sa Q3Table::setItem() Q3TableItem() +*/ + +/*! + \enum Q3TableItem::EditType + + \target wheneditable + This enum is used to define whether a cell is editable or + read-only (in conjunction with other settings), and how the cell + should be displayed. + + \value Always + The cell always \e looks editable. + + Using this EditType ensures that the editor created with + createEditor() (by default a QLineEdit) is always visible. This + has implications for the alignment of the content: the default + editor aligns everything (even numbers) to the left whilst + numerical values in the cell are by default aligned to the right. + + If a cell with the edit type \c Always looks misaligned you could + reimplement createEditor() for these items. + + \value WhenCurrent + The cell \e looks editable only when it has keyboard focus (see + Q3Table::setCurrentCell()). + + \value OnTyping + The cell \e looks editable only when the user types in it or + double-clicks it. It resembles the \c WhenCurrent functionality + but is, perhaps, nicer. + + The \c OnTyping edit type is the default when Q3TableItem objects + are created by the convenience functions Q3Table::setText() and + Q3Table::setPixmap(). + + \value Never The cell is not editable. + + The cell is actually editable only if Q3Table::isRowReadOnly() is + false for its row, Q3Table::isColumnReadOnly() is false for its + column, and Q3Table::isReadOnly() is false. + + Q3ComboTableItems have an isEditable() property. This property is + used to indicate whether the user may enter their own text or are + restricted to choosing one of the choices in the list. + Q3ComboTableItems may be interacted with only if they are editable + in accordance with their EditType as described above. + +*/ + +/*! + Creates a table item that is a child of table \a table with no + text. The item has the \l EditType \a et. + + The table item will use a QLineEdit for its editor, will not + word-wrap and will occupy a single cell. Insert the table item + into a table with Q3Table::setItem(). + + The table takes ownership of the table item, so a table item + should not be inserted into more than one table at a time. +*/ + +Q3TableItem::Q3TableItem(Q3Table *table, EditType et) + : txt(), pix(), t(table), edType(et), wordwrap(false), + tcha(true), rw(-1), cl(-1), rowspan(1), colspan(1) +{ + enabled = true; +} + +/*! + Creates a table item that is a child of table \a table with text + \a text. The item has the \l EditType \a et. + + The table item will use a QLineEdit for its editor, will not + word-wrap and will occupy a single cell. Insert the table item + into a table with Q3Table::setItem(). + + The table takes ownership of the table item, so a table item + should not be inserted into more than one table at a time. +*/ + +Q3TableItem::Q3TableItem(Q3Table *table, EditType et, const QString &text) + : txt(text), pix(), t(table), edType(et), wordwrap(false), + tcha(true), rw(-1), cl(-1), rowspan(1), colspan(1) +{ + enabled = true; +} + +/*! + Creates a table item that is a child of table \a table with text + \a text and pixmap \a p. The item has the \l EditType \a et. + + The table item will display the pixmap to the left of the text. It + will use a QLineEdit for editing the text, will not word-wrap and + will occupy a single cell. Insert the table item into a table with + Q3Table::setItem(). + + The table takes ownership of the table item, so a table item + should not be inserted in more than one table at a time. +*/ + +Q3TableItem::Q3TableItem(Q3Table *table, EditType et, + const QString &text, const QPixmap &p) + : txt(text), pix(p), t(table), edType(et), wordwrap(false), + tcha(true), rw(-1), cl(-1), rowspan(1), colspan(1) +{ + enabled = true; +} + +/*! + The destructor deletes this item and frees all allocated + resources. + + If the table item is in a table (i.e. was inserted with + setItem()), it will be removed from the table and the cell it + occupied. +*/ + +Q3TableItem::~Q3TableItem() +{ + if (table()) + table()->takeItem(this); +} + +int Q3TableItem::RTTI = 0; + +/*! + Returns the Run Time Type Identification value for this table item + which for Q3TableItems is 0. + + When you create subclasses based on Q3TableItem make sure that each + subclass returns a unique rtti() value. It is advisable to use + values greater than 1000, preferably large random numbers, to + allow for extensions to this class. + + \sa Q3CheckTableItem::rtti() Q3ComboTableItem::rtti() +*/ + +int Q3TableItem::rtti() const +{ + return RTTI; +} + +/*! + Returns the table item's pixmap or a null pixmap if no pixmap has + been set. + + \sa setPixmap() text() +*/ + +QPixmap Q3TableItem::pixmap() const +{ + return pix; +} + + +/*! + Returns the text of the table item or an empty string if there is + no text. + + To ensure that the current value of the editor is returned, + setContentFromEditor() is called: + \list 1 + \i if the editMode() is \c Always, or + \i if editMode() is \e not \c Always but the editor of the cell is + active and the editor is not a QLineEdit. + \endlist + + This means that text() returns the original text value of the item + if the editor is a line edit, until the user commits an edit (e.g. + by pressing Enter or Tab) in which case the new text is returned. + For other editors (e.g. a combobox) setContentFromEditor() is + always called so the currently display value is the one returned. + + \sa setText() pixmap() +*/ + +QString Q3TableItem::text() const +{ + QWidget *w = table()->cellWidget(rw, cl); + if (w && (edType == Always || + rtti() == Q3ComboTableItem::RTTI || + rtti() == Q3CheckTableItem::RTTI)) + ((Q3TableItem*)this)->setContentFromEditor(w); + return txt; +} + +/*! + Sets pixmap \a p to be this item's pixmap. + + Note that setPixmap() does not update the cell the table item + belongs to. Use Q3Table::updateCell() to repaint the cell's + contents. + + For \l{Q3ComboTableItem}s and \l{Q3CheckTableItem}s this function + has no visible effect. + + \sa Q3Table::setPixmap() pixmap() setText() +*/ + +void Q3TableItem::setPixmap(const QPixmap &p) +{ + pix = p; +} + +/*! + Changes the table item's text to \a str. + + Note that setText() does not update the cell the table item + belongs to. Use Q3Table::updateCell() to repaint the cell's + contents. + + \sa Q3Table::setText() text() setPixmap() Q3Table::updateCell() +*/ + +void Q3TableItem::setText(const QString &str) +{ + txt = str; +} + +/*! + This virtual function is used to paint the contents of an item + using the painter \a p in the rectangular area \a cr using the + color group \a cg. + + If \a selected is true the cell is displayed in a way that + indicates that it is highlighted. + + You don't usually need to use this function but if you want to + draw custom content in a cell you will need to reimplement it. + + The painter passed to this function is translated so that 0, 0 + is the top-left corner of the item that is being painted. + + Note that the painter is not clipped by default in order to get + maximum efficiency. If you want clipping, use + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 2 + +*/ + +void Q3TableItem::paint(QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected) +{ + p->fillRect(0, 0, cr.width(), cr.height(), + selected ? cg.brush(QColorGroup::Highlight) + : cg.brush(QColorGroup::Base)); + + int w = cr.width(); + int h = cr.height(); + + int x = 0; + if (!pix.isNull()) { + p->drawPixmap(0, (cr.height() - pix.height()) / 2, pix); + x = pix.width() + 2; + } + + if (selected) + p->setPen(cg.highlightedText()); + else + p->setPen(cg.text()); + p->drawText(x + 2, 0, w - x - 4, h, + wordwrap ? (alignment() | WordBreak) : alignment(), text()); +} + +/*! +This virtual function creates an editor which the user can +interact with to edit the cell's contents. The default +implementation creates a QLineEdit. + +If the function returns 0, the cell is read-only. + +The returned widget should preferably be invisible, ideally with +Q3Table::viewport() as parent. + +If you reimplement this function you'll almost certainly need to +reimplement setContentFromEditor(), and may need to reimplement +sizeHint(). + +\sa Q3Table::createEditor() setContentFromEditor() Q3Table::viewport() setReplaceable() +*/ + +QWidget *Q3TableItem::createEditor() const +{ + QLineEdit *e = new QLineEdit(table()->viewport(), "qt_tableeditor"); + e->setFrame(false); + e->setText(text()); + return e; +} + +/*! +Whenever the content of a cell has been edited by the editor \a w, +Q3Table calls this virtual function to copy the new values into the +Q3TableItem. + +If you reimplement createEditor() and return something that is not +a QLineEdit you will need to reimplement this function. + +\sa Q3Table::setCellContentFromEditor() +*/ + +void Q3TableItem::setContentFromEditor(QWidget *w) +{ + QLineEdit *le = qobject_cast<QLineEdit*>(w); + if (le) { + QString input = le->text(); + if (le->validator()) + le->validator()->fixup(input); + setText(input); + } +} + +/*! + The alignment function returns how the text contents of the cell + are aligned when drawn. The default implementation aligns numbers + to the right and any other text to the left. + + \sa Qt::Alignment +*/ + +// ed: For consistency reasons a setAlignment() should be provided +// as well. + +int Q3TableItem::alignment() const +{ + bool num; + bool ok1 = false, ok2 = false; + (void)text().toInt(&ok1); + if (!ok1) + (void)text().toDouble(&ok2); // ### should be .-aligned + num = ok1 || ok2; + + return (num ? AlignRight : AlignLeft) | AlignVCenter; +} + +/*! + If \a b is true, the cell's text will be wrapped over multiple + lines, when necessary, to fit the width of the cell; otherwise the + text will be written as a single line. + + \sa wordWrap() Q3Table::adjustColumn() Q3Table::setColumnStretchable() +*/ + +void Q3TableItem::setWordWrap(bool b) +{ + wordwrap = b; +} + +/*! + Returns true if word wrap is enabled for the cell; otherwise + returns false. + + \sa setWordWrap() +*/ + +bool Q3TableItem::wordWrap() const +{ + return wordwrap; +} + +/*! \internal */ + +void Q3TableItem::updateEditor(int oldRow, int oldCol) +{ + if (edType != Always) + return; + if (oldRow != -1 && oldCol != -1) + table()->clearCellWidget(oldRow, oldCol); + if (rw != -1 && cl != -1) + table()->setCellWidget(rw, cl, createEditor()); +} + +/*! + Returns the table item's edit type. + + This is set when the table item is constructed. + + \sa EditType Q3TableItem() +*/ + +Q3TableItem::EditType Q3TableItem::editType() const +{ + return edType; +} + +/*! + If \a b is true it is acceptable to replace the contents of the + cell with the contents of another Q3TableItem. If \a b is false the + contents of the cell may not be replaced by the contents of + another table item. Table items that span more than one cell may + not have their contents replaced by another table item. + + (This differs from \l EditType because EditType is concerned with + whether the \e user is able to change the contents of a cell.) + + \sa isReplaceable() +*/ + +void Q3TableItem::setReplaceable(bool b) +{ + tcha = b; +} + +/*! + This function returns whether the contents of the cell may be + replaced with the contents of another table item. Regardless of + this setting, table items that span more than one cell may not + have their contents replaced by another table item. + + (This differs from \l EditType because EditType is concerned with + whether the \e user is able to change the contents of a cell.) + + \sa setReplaceable() EditType +*/ + +bool Q3TableItem::isReplaceable() const +{ + if (rowspan > 1 || colspan > 1) + return false; + return tcha; +} + +/*! + This virtual function returns the key that should be used for + sorting. The default implementation returns the text() of the + relevant item. + + \sa Q3Table::setSorting() +*/ + +QString Q3TableItem::key() const +{ + return text(); +} + +/*! + This virtual function returns the size a cell needs to show its + entire content. + + If you subclass Q3TableItem you will often need to reimplement this + function. +*/ + +QSize Q3TableItem::sizeHint() const +{ + QSize strutSize = QApplication::globalStrut(); + if (edType == Always && table()->cellWidget(rw, cl)) + return table()->cellWidget(rw, cl)->sizeHint().expandedTo(strutSize); + + QSize s; + int x = 0; + if (!pix.isNull()) { + s = pix.size(); + s.setWidth(s.width() + 2); + x = pix.width() + 2; + } + + QString t = text(); + if (!wordwrap && t.find(QLatin1Char('\n')) == -1) + return QSize(s.width() + table()->fontMetrics().width(text()) + 10, + QMAX(s.height(), table()->fontMetrics().height())).expandedTo(strutSize); + + QRect r = table()->fontMetrics().boundingRect(x + 2, 0, table()->columnWidth(col()) - x - 4, 0, + wordwrap ? (alignment() | WordBreak) : alignment(), + text()); + r.setWidth(QMAX(r.width() + 10, table()->columnWidth(col()))); + return QSize(r.width(), QMAX(s.height(), r.height())).expandedTo(strutSize); +} + +/*! + Changes the extent of the Q3TableItem so that it spans multiple + cells covering \a rs rows and \a cs columns. The top left cell is + the original cell. + + \warning This function only works if the item has already been + inserted into the table using e.g. Q3Table::setItem(). This + function also checks to make sure if \a rs and \a cs are within + the bounds of the table and returns without changing the span if + they are not. In addition swapping, inserting or removing rows and + columns that cross Q3TableItems spanning more than one cell is not + supported. + + \sa rowSpan() colSpan() +*/ + +void Q3TableItem::setSpan(int rs, int cs) +{ + if (rs == rowspan && cs == colspan) + return; + + if (!table()->d->hasRowSpan) + table()->d->hasRowSpan = rs > 1; + if (!table()->d->hasColSpan) + table()->d->hasColSpan = cs > 1; + // return if we are thinking too big... + if (rw + rs > table()->numRows()) + return; + + if (cl + cs > table()->numCols()) + return; + + if (rw == -1 || cl == -1) + return; + + int rrow = rw; + int rcol = cl; + if (rowspan > 1 || colspan > 1) { + Q3Table* t = table(); + t->takeItem(this); + t->setItem(rrow, rcol, this); + } + + rowspan = rs; + colspan = cs; + + for (int r = 0; r < rowspan; ++r) { + for (int c = 0; c < colspan; ++c) { + if (r == 0 && c == 0) + continue; + qt_update_cell_widget = false; + table()->setItem(r + rw, c + cl, this); + qt_update_cell_widget = true; + rw = rrow; + cl = rcol; + } + } + + table()->updateCell(rw, cl); + QWidget *w = table()->cellWidget(rw, cl); + if (w) + w->resize(table()->cellGeometry(rw, cl).size()); +} + +/*! + Returns the row span of the table item, usually 1. + + \sa setSpan() colSpan() +*/ + +int Q3TableItem::rowSpan() const +{ + return rowspan; +} + +/*! + Returns the column span of the table item, usually 1. + + \sa setSpan() rowSpan() +*/ + +int Q3TableItem::colSpan() const +{ + return colspan; +} + +/*! + Sets row \a r as the table item's row. Usually you do not need to + call this function. + + If the cell spans multiple rows, this function sets the top row + and retains the height of the multi-cell table item. + + \sa row() setCol() rowSpan() +*/ + +void Q3TableItem::setRow(int r) +{ + rw = r; +} + +/*! + Sets column \a c as the table item's column. Usually you will not + need to call this function. + + If the cell spans multiple columns, this function sets the + left-most column and retains the width of the multi-cell table + item. + + \sa col() setRow() colSpan() +*/ + +void Q3TableItem::setCol(int c) +{ + cl = c; +} + +/*! + Returns the row where the table item is located. If the cell spans + multiple rows, this function returns the top-most row. + + \sa col() setRow() +*/ + +int Q3TableItem::row() const +{ + return rw; +} + +/*! + Returns the column where the table item is located. If the cell + spans multiple columns, this function returns the left-most + column. + + \sa row() setCol() +*/ + +int Q3TableItem::col() const +{ + return cl; +} + +/*! + If \a b is true, the table item is enabled; if \a b is false the + table item is disabled. + + A disabled item doesn't respond to user interaction. + + \sa isEnabled() +*/ + +void Q3TableItem::setEnabled(bool b) +{ + if (b == (bool)enabled) + return; + enabled = b; + table()->updateCell(row(), col()); +} + +/*! + Returns true if the table item is enabled; otherwise returns false. + + \sa setEnabled() +*/ + +bool Q3TableItem::isEnabled() const +{ + return (bool)enabled; +} + +/*! + \class Q3ComboTableItem + \brief The Q3ComboTableItem class provides a means of using + comboboxes in Q3Tables. + + \compat + + A Q3ComboTableItem is a table item which looks and behaves like a + combobox. The advantage of using Q3ComboTableItems rather than real + comboboxes is that a Q3ComboTableItem uses far less resources than + real comboboxes in \l{Q3Table}s. When the cell has the focus it + displays a real combobox which the user can interact with. When + the cell does not have the focus the cell \e looks like a + combobox. Only text items (i.e. no pixmaps) may be used in + Q3ComboTableItems. + + Q3ComboTableItem items have the edit type \c WhenCurrent (see + \l{EditType}). The Q3ComboTableItem's list of items is provided by + a QStringList passed to the constructor. + + The list of items may be changed using setStringList(). The + current item can be set with setCurrentItem() and retrieved with + currentItem(). The text of the current item can be obtained with + currentText(), and the text of a particular item can be retrieved + with text(). + + If isEditable() is true the Q3ComboTableItem will permit the user + to either choose an existing list item, or create a new list item + by entering their own text; otherwise the user may only choose one + of the existing list items. + + To populate a table cell with a Q3ComboTableItem use + Q3Table::setItem(). + + Q3ComboTableItems may be deleted with Q3Table::clearCell(). + + Q3ComboTableItems can be distinguished from \l{Q3TableItem}s and + \l{Q3CheckTableItem}s using their Run Time Type Identification + number (see rtti()). + + \img qtableitems.png Table Items + + \sa Q3CheckTableItem Q3TableItem Q3ComboBox +*/ + +Q3ComboBox *Q3ComboTableItem::fakeCombo = 0; +QWidget *Q3ComboTableItem::fakeComboWidget = 0; +int Q3ComboTableItem::fakeRef = 0; + +/*! + Creates a combo table item for the table \a table. The combobox's + list of items is passed in the \a list argument. If \a editable is + true the user may type in new list items; if \a editable is false + the user may only select from the list of items provided. + + By default Q3ComboTableItems cannot be replaced by other table + items since isReplaceable() returns false by default. + + \sa Q3Table::clearCell() EditType +*/ + +Q3ComboTableItem::Q3ComboTableItem(Q3Table *table, const QStringList &list, bool editable) + : Q3TableItem(table, WhenCurrent, QLatin1String("")), entries(list), current(0), edit(editable) +{ + setReplaceable(false); + if (!Q3ComboTableItem::fakeCombo) { + Q3ComboTableItem::fakeComboWidget = new QWidget(0, 0); + Q3ComboTableItem::fakeCombo = new Q3ComboBox(false, Q3ComboTableItem::fakeComboWidget, 0); + Q3ComboTableItem::fakeCombo->hide(); + } + ++Q3ComboTableItem::fakeRef; + if (entries.count()) + setText(entries.at(current)); +} + +/*! + Q3ComboTableItem destructor. +*/ +Q3ComboTableItem::~Q3ComboTableItem() +{ + if (--Q3ComboTableItem::fakeRef <= 0) { + delete Q3ComboTableItem::fakeComboWidget; + Q3ComboTableItem::fakeComboWidget = 0; + Q3ComboTableItem::fakeCombo = 0; + } +} + +/*! + Sets the list items of this Q3ComboTableItem to the strings in the + string list \a l. +*/ + +void Q3ComboTableItem::setStringList(const QStringList &l) +{ + entries = l; + current = 0; + if (entries.count()) + setText(entries.at(current)); + if (table()->cellWidget(row(), col())) { + cb->clear(); + cb->insertStringList(entries); + } + table()->updateCell(row(), col()); +} + +/*! \reimp */ + +QWidget *Q3ComboTableItem::createEditor() const +{ + // create an editor - a combobox in our case + ((Q3ComboTableItem*)this)->cb = new Q3ComboBox(edit, table()->viewport(), "qt_editor_cb"); + cb->insertStringList(entries); + cb->setCurrentItem(current); + QObject::connect(cb, SIGNAL(activated(int)), table(), SLOT(doValueChanged())); + return cb; +} + +/*! \reimp */ + +void Q3ComboTableItem::setContentFromEditor(QWidget *w) +{ + Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w); + if (cb) { + entries.clear(); + for (int i = 0; i < cb->count(); ++i) + entries << cb->text(i); + current = cb->currentItem(); + setText(cb->currentText()); + } +} + +/*! \reimp */ + +void Q3ComboTableItem::paint(QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected) +{ + fakeCombo->resize(cr.width(), cr.height()); + + QPalette pal2(cg); + if (selected) { + pal2.setBrush(QPalette::Base, cg.QPalette::brush(QPalette::Highlight)); + pal2.setColor(QPalette::Text, cg.highlightedText()); + } + + QStyle::State flags = QStyle::State_None; + if(isEnabled() && table()->isEnabled()) + flags |= QStyle::State_Enabled; + // Since we still have the "fakeCombo" may as well use it in this case. + QStyleOptionComboBox opt; + opt.initFrom(table()); + opt.rect = fakeCombo->rect(); + opt.palette = pal2; + opt.state &= ~QStyle::State_HasFocus; + opt.state &= ~QStyle::State_MouseOver; + opt.state |= flags; + opt.subControls = QStyle::SC_All; + opt.activeSubControls = QStyle::SC_None; + opt.editable = fakeCombo->editable(); + table()->style()->drawComplexControl(QStyle::CC_ComboBox, &opt, p, fakeCombo); + + p->save(); + QRect textR = table()->style()->subControlRect(QStyle::CC_ComboBox, &opt, + QStyle::SC_ComboBoxEditField, fakeCombo); + int align = alignment(); // alignment() changes entries + p->drawText(textR, wordWrap() ? (align | Qt::WordBreak) : align, entries.value(current)); + p->restore(); +} + +/*! + Sets the list item \a i to be the combo table item's current list + item. + + \sa currentItem() +*/ + +void Q3ComboTableItem::setCurrentItem(int i) +{ + QWidget *w = table()->cellWidget(row(), col()); + Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w); + if (cb) { + cb->setCurrentItem(i); + current = cb->currentItem(); + setText(cb->currentText()); + } else { + if (i < 0 || i >= entries.count()) + return; + current = i; + setText(entries.at(i)); + table()->updateCell(row(), col()); + } +} + +/*! + \overload + + Sets the list item whose text is \a s to be the combo table item's + current list item. Does nothing if no list item has the text \a s. + + \sa currentItem() +*/ + +void Q3ComboTableItem::setCurrentItem(const QString &s) +{ + int i = entries.findIndex(s); + if (i != -1) + setCurrentItem(i); +} + +/*! + Returns the index of the combo table item's current list item. + + \sa setCurrentItem() +*/ + +int Q3ComboTableItem::currentItem() const +{ + QWidget *w = table()->cellWidget(row(), col()); + Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w); + if (cb) + return cb->currentItem(); + return current; +} + +/*! + Returns the text of the combo table item's current list item. + + \sa currentItem() text() +*/ + +QString Q3ComboTableItem::currentText() const +{ + QWidget *w = table()->cellWidget(row(), col()); + Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w); + if (cb) + return cb->currentText(); + return entries.value(current); +} + +/*! + Returns the total number of list items in the combo table item. +*/ + +int Q3ComboTableItem::count() const +{ + QWidget *w = table()->cellWidget(row(), col()); + Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w); + if (cb) + return cb->count(); + return (int)entries.count(); +} + +/*! + Returns the text of the combo's list item at index \a i. + + \sa currentText() +*/ + +QString Q3ComboTableItem::text(int i) const +{ + QWidget *w = table()->cellWidget(row(), col()); + Q3ComboBox *cb = qobject_cast<Q3ComboBox*>(w); + if (cb) + return cb->text(i); + return entries.value(i); +} + +/*! + If \a b is true the combo table item can be edited, i.e. the user + may enter a new text item themselves. If \a b is false the user may + may only choose one of the existing items. + + \sa isEditable() +*/ + +void Q3ComboTableItem::setEditable(bool b) +{ + edit = b; +} + +/*! + Returns true if the user can add their own list items to the + combobox's list of items; otherwise returns false. + + \sa setEditable() +*/ + +bool Q3ComboTableItem::isEditable() const +{ + return edit; +} + +int Q3ComboTableItem::RTTI = 1; + +/*! + \fn int Q3ComboTableItem::rtti() const + + Returns 1. + + Make your derived classes return their own values for rtti()to + distinguish between different table item subclasses. You should + use values greater than 1000, preferably a large random number, to + allow for extensions to this class. + + + \sa Q3TableItem::rtti() +*/ + +int Q3ComboTableItem::rtti() const +{ + return RTTI; +} + +/*! \reimp */ + +QSize Q3ComboTableItem::sizeHint() const +{ + fakeCombo->insertItem(currentText()); + fakeCombo->setCurrentItem(fakeCombo->count() - 1); + QSize sh = fakeCombo->sizeHint(); + fakeCombo->removeItem(fakeCombo->count() - 1); + return sh.expandedTo(QApplication::globalStrut()); +} + +/*! + \fn QString Q3ComboTableItem::text() const + + Returns the text of the table item or an empty string if there is + no text. + + \sa Q3TableItem::text() +*/ + +/*! + \class Q3CheckTableItem + \brief The Q3CheckTableItem class provides checkboxes in Q3Tables. + + \compat + + A Q3CheckTableItem is a table item which looks and behaves like a + checkbox. The advantage of using Q3CheckTableItems rather than real + checkboxes is that a Q3CheckTableItem uses far less resources than + a real checkbox would in a \l{Q3Table}. When the cell has the focus + it displays a real checkbox which the user can interact with. When + the cell does not have the focus the cell \e looks like a + checkbox. Pixmaps may not be used in Q3CheckTableItems. + + Q3CheckTableItem items have the edit type \c WhenCurrent (see + \l{EditType}). + + To change the checkbox's label use setText(). The checkbox can be + checked and unchecked with setChecked() and its state retrieved + using isChecked(). + + To populate a table cell with a Q3CheckTableItem use + Q3Table::setItem(). + + Q3CheckTableItems can be distinguished from \l{Q3TableItem}s and + \l{Q3ComboTableItem}s using their Run Time Type Identification + (rtti) value. + + \img qtableitems.png Table Items + + \sa rtti() EditType Q3ComboTableItem Q3TableItem QCheckBox +*/ + +/*! + Creates a Q3CheckTableItem with an \l{EditType} of \c WhenCurrent + as a child of \a table. The checkbox is initially unchecked and + its label is set to the string \a txt. +*/ + +Q3CheckTableItem::Q3CheckTableItem(Q3Table *table, const QString &txt) + : Q3TableItem(table, WhenCurrent, txt), checked(false) +{ +} + +/*! \reimp */ + +void Q3CheckTableItem::setText(const QString &t) +{ + Q3TableItem::setText(t); + QWidget *w = table()->cellWidget(row(), col()); + QCheckBox *cb = qobject_cast<QCheckBox*>(w); + if (cb) + cb->setText(t); +} + + +/*! \reimp */ + +QWidget *Q3CheckTableItem::createEditor() const +{ + // create an editor - a combobox in our case + ((Q3CheckTableItem*)this)->cb = new QCheckBox(table()->viewport(), "qt_editor_checkbox"); + cb->setChecked(checked); + cb->setText(text()); + cb->setBackgroundColor(table()->viewport()->backgroundColor()); + cb->setAutoFillBackground(true); + QObject::connect(cb, SIGNAL(toggled(bool)), table(), SLOT(doValueChanged())); + return cb; +} + +/*! \reimp */ + +void Q3CheckTableItem::setContentFromEditor(QWidget *w) +{ + QCheckBox *cb = qobject_cast<QCheckBox*>(w); + if (cb) + checked = cb->isChecked(); +} + +/*! \reimp */ + +void Q3CheckTableItem::paint(QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected) +{ + QPalette pal = cg; + + p->fillRect(0, 0, cr.width(), cr.height(), + selected ? pal.brush(QPalette::Highlight) + : pal.brush(QPalette::Base)); + + QSize sz = QSize(table()->style()->pixelMetric(QStyle::PM_IndicatorWidth), + table()->style()->pixelMetric(QStyle::PM_IndicatorHeight)); + QPalette pal2(pal); + pal2.setBrush(QPalette::Window, pal.brush(QPalette::Base)); + QStyleOptionButton opt; + opt.initFrom(table()); + opt.rect.setRect(0, (cr.height() - sz.height()) / 2, sz.width(), sz.height()); + opt.palette = pal2; + opt.state &= ~QStyle::State_HasFocus; + opt.state &= ~QStyle::State_MouseOver; + if(isEnabled()) + opt.state |= QStyle::State_Enabled; + if (checked) + opt.state |= QStyle::State_On; + else + opt.state |= QStyle::State_Off; + if (isEnabled() && table()->isEnabled()) + opt.state |= QStyle::State_Enabled; + table()->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, p, table()); + if (selected) + p->setPen(pal.highlightedText().color()); + else + p->setPen(pal.text().color()); + opt.rect.setRect(0, 0, cr.width(), cr.height()); + QRect textRect = table()->style()->subElementRect(QStyle::SE_CheckBoxContents, &opt, table()); + p->drawText(textRect, wordWrap() ? (alignment() | Qt::WordBreak) : alignment(), text()); +} + +/*! + If \a b is true the checkbox is checked; if \a b is false the + checkbox is unchecked. + + \sa isChecked() +*/ + +void Q3CheckTableItem::setChecked(bool b) +{ + checked = b; + table()->updateCell(row(), col()); + QWidget *w = table()->cellWidget(row(), col()); + QCheckBox *cb = qobject_cast<QCheckBox*>(w); + if (cb) + cb->setChecked(b); +} + +/*! + Returns true if the checkbox table item is checked; otherwise + returns false. + + \sa setChecked() +*/ + +bool Q3CheckTableItem::isChecked() const +{ + // #### why was this next line here. It must not be here, as + // #### people want to call isChecked() from within paintCell() + // #### and end up in an infinite loop that way + // table()->updateCell(row(), col()); + QWidget *w = table()->cellWidget(row(), col()); + QCheckBox *cb = qobject_cast<QCheckBox*>(w); + if (cb) + return cb->isChecked(); + return checked; +} + +int Q3CheckTableItem::RTTI = 2; + +/*! + \fn int Q3CheckTableItem::rtti() const + + Returns 2. + + Make your derived classes return their own values for rtti()to + distinguish between different table item subclasses. You should + use values greater than 1000, preferably a large random number, to + allow for extensions to this class. + + \sa Q3TableItem::rtti() +*/ + +int Q3CheckTableItem::rtti() const +{ + return RTTI; +} + +/*! \reimp */ + +QSize Q3CheckTableItem::sizeHint() const +{ + QSize sz = QSize(table()->style()->pixelMetric(QStyle::PM_IndicatorWidth), + table()->style()->pixelMetric(QStyle::PM_IndicatorHeight)); + sz.setWidth(sz.width() + 6); + QSize sh(Q3TableItem::sizeHint()); + return QSize(sh.width() + sz.width(), QMAX(sh.height(), sz.height())). + expandedTo(QApplication::globalStrut()); +} + +/*! + \class Q3Table + \brief The Q3Table class provides a flexible editable table widget. + + \compat + + Q3Table is easy to use, although it does have a large API because + of the comprehensive functionality that it provides. Q3Table + includes functions for manipulating \link #headers + headers\endlink, \link #columnsrows rows and columns\endlink, + \link #cells cells\endlink and \link #selections + selections\endlink. Q3Table also provides in-place editing and + drag and drop, as well as a useful set of + \link #signals signals\endlink. Q3Table efficiently supports very + large tables, for example, tables one million by one million cells + are perfectly possible. Q3Table is economical with memory, using + none for unused cells. + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 3 + + The first line constructs the table specifying its size in rows + and columns. We then insert a pixmap and some text into the \e + same \link #cells cell\endlink, with the pixmap appearing to the + left of the text. Q3Table cells can be populated with + \l{Q3TableItem}s, \l{Q3ComboTableItem}s or by \l{Q3CheckTableItem}s. + By default a vertical header appears at the left of the table + showing row numbers and a horizontal header appears at the top of + the table showing column numbers. (The numbers displayed start at + 1, although row and column numbers within Q3Table begin at 0.) + + If you want to use mouse tracking call setMouseTracking(true) on + the \e viewport. + + \img qtableitems.png Table Items + + \target headers + \section1 Headers + + Q3Table supports a header column, e.g. to display row numbers, and + a header row, e.g to display column titles. To set row or column + labels use Q3Header::setLabel() on the pointers returned by + verticalHeader() and horizontalHeader() respectively. The vertical + header is displayed within the table's left margin whose width is + set with setLeftMargin(). The horizontal header is displayed + within the table's top margin whose height is set with + setTopMargin(). The table's grid can be switched off with + setShowGrid(). If you want to hide a horizontal header call + hide(), and call setTopMargin(0) so that the area the header + would have occupied is reduced to zero size. + + Header labels are indexed via their section numbers. Note that the + default behavior of Q3Header regarding section numbers is overridden + for Q3Table. See the explanation below in the Rows and Columns + section in the discussion of moving columns and rows. + + \target columnsrows + \section1 Rows and Columns + + Row and column sizes are set with setRowHeight() and + setColumnWidth(). If you want a row high enough to show the + tallest item in its entirety, use adjustRow(). Similarly, to make + a column wide enough to show the widest item use adjustColumn(). + If you want the row height and column width to adjust + automatically as the height and width of the table changes use + setRowStretchable() and setColumnStretchable(). + + Rows and columns can be hidden and shown with hideRow(), + hideColumn(), showRow() and showColumn(). New rows and columns are + inserted using insertRows() and insertColumns(). Additional rows + and columns are added at the bottom (rows) or right (columns) if + you set setNumRows() or setNumCols() to be larger than numRows() + or numCols(). Existing rows and columns are removed with + removeRow() and removeColumn(). Multiple rows and columns can be + removed with removeRows() and removeColumns(). + + Rows and columns can be set to be movable using + rowMovingEnabled() and columnMovingEnabled(). The user can drag + them to reorder them holding down the Ctrl key and dragging the + mouse. For performance reasons, the default behavior of Q3Header + section numbers is overridden by Q3Table. Currently in Q3Table, when + a row or column is dragged and reordered, the section number is + also changed to its new position. Therefore, there is no + difference between the section and the index fields in Q3Header. + The Q3Table Q3Header classes do not provide a mechanism for indexing + independently of the user interface ordering. + + The table can be sorted using sortColumn(). Users can sort a + column by clicking its header if setSorting() is set to true. Rows + can be swapped with swapRows(), columns with swapColumns() and + cells with swapCells(). + + For editable tables (see setReadOnly()) you can set the read-only + property of individual rows and columns with setRowReadOnly() and + setColumnReadOnly(). (Whether a cell is editable or read-only + depends on these settings and the cell's Q3TableItem. + + The row and column which have the focus are returned by + currentRow() and currentColumn() respectively. + + Although many Q3Table functions operate in terms of rows and + columns the indexOf() function returns a single integer + identifying a particular cell. + + \target cells + \section1 Cells + + All of a Q3Table's cells are empty when the table is constructed. + + There are two approaches to populating the table's cells. The + first and simplest approach is to use Q3TableItems or Q3TableItem + subclasses. The second approach doesn't use Q3TableItems at all + which is useful for very large sparse tables but requires you to + reimplement a number of functions. We'll look at each approach in + turn. + + To put a string in a cell use setText(). This function will create + a new Q3TableItem for the cell if one doesn't already exist, and + displays the text in it. By default the table item's widget will + be a QLineEdit. A pixmap may be put in a cell with setPixmap(), + which also creates a table item if required. A cell may contain \e + both a pixmap and text; the pixmap is displayed to the left of the + text. Another approach is to construct a Q3TableItem or Q3TableItem + subclass, set its properties, then insert it into a cell with + setItem(). + + If you want cells which contain comboboxes use the Q3ComboTableItem + class. Similarly if you require cells containing checkboxes use + the Q3CheckTableItem class. These table items look and behave just + like the combobox or checkbox widgets but consume far less memory. + + Q3Table takes ownership of its Q3TableItems and will delete them + when the table itself is destroyed. You can take ownership of a + table item using takeItem() which you use to move a cell's + contents from one cell to another, either within the same table, + or from one table to another. (See also, swapCells()). + + In-place editing of the text in Q3TableItems, and the values in + Q3ComboTableItems and Q3CheckTableItems works automatically. Cells + may be editable or read-only, see Q3TableItem::EditType. If you + want fine control over editing see beginEdit() and endEdit(). + + The contents of a cell can be retrieved as a Q3TableItem using + item(), or as a string with text() or as a pixmap (if there is + one) with pixmap(). A cell's bounding rectangle is given by + cellGeometry(). Use updateCell() to repaint a cell, for example to + clear away a cell's visual representation after it has been + deleted with clearCell(). The table can be forced to scroll to + show a particular cell with ensureCellVisible(). The isSelected() + function indicates if a cell is selected. + + It is possible to use your own widget as a cell's widget using + setCellWidget(), but subclassing Q3TableItem might be a simpler + approach. The cell's widget (if there is one) can be removed with + clearCellWidget(). + + \keyword notes on large tables + \target bigtables + \section2 Large tables + + For large, sparse, tables using Q3TableItems or other widgets is + inefficient. The solution is to \e draw the cell as it should + appear and to create and destroy cell editors on demand. + + This approach requires that you reimplement various functions. + Reimplement paintCell() to display your data, and createEditor() + and setCellContentFromEditor() to support in-place editing. It + is very important to reimplement resizeData() to have no + functionality, to prevent Q3Table from attempting to create a huge + array. You will also need to reimplement item(), setItem(), + takeItem(), clearCell(), and insertWidget(), cellWidget() and + clearCellWidget(). In almost every circumstance (for sorting, + removing and inserting columns and rows, etc.), you also need + to reimplement swapRows(), swapCells() and swapColumns(), including + header handling. + + If you represent active cells with a dictionary of Q3TableItems and + QWidgets, i.e. only store references to cells that are actually + used, many of the functions can be implemented with a single line + of code. + + For more information on cells see the Q3TableItem documenation. + + \target selections + \section1 Selections + + Q3Table's support single selection, multi-selection (multiple + cells) or no selection. The selection mode is set with + setSelectionMode(). Use isSelected() to determine if a particular + cell is selected, and isRowSelected() and isColumnSelected() to + see if a row or column is selected. + + Q3Table's support many simultaneous selections. You can + programmatically select cells with addSelection(). The number of + selections is given by numSelections(). The current selection is + returned by currentSelection(). You can remove a selection with + removeSelection() and remove all selections with + clearSelection(). Selections are Q3TableSelection objects. + + To easily add a new selection use selectCells(), selectRow() or + selectColumn(). + + Alternatively, use addSelection() to add new selections using + Q3TableSelection objects. The advantage of using Q3TableSelection + objects is that you can call Q3TableSelection::expandTo() to resize + the selection and can query and compare them. + + The number of selections is given by numSelections(). The current + selection is returned by currentSelection(). You can remove a + selection with removeSelection() and remove all selections with + clearSelection(). + + \target signals + \section1 Signals + + When the user clicks a cell the currentChanged() signal is + emitted. You can also connect to the lower level clicked(), + doubleClicked() and pressed() signals. If the user changes the + selection the selectionChanged() signal is emitted; similarly if + the user changes a cell's value the valueChanged() signal is + emitted. If the user right-clicks (or presses the appropriate + platform-specific key sequence) the contextMenuRequested() signal + is emitted. If the user drops a drag and drop object the dropped() + signal is emitted with the drop event. +*/ + +/*! + \fn void Q3Table::currentChanged(int row, int col) + + This signal is emitted when the current cell has changed to \a + row, \a col. +*/ + +/*! + \fn void Q3Table::valueChanged(int row, int col) + + This signal is emitted when the user changed the value in the cell + at \a row, \a col. +*/ + +/*! + \fn int Q3Table::currentRow() const + + Returns the current row. + + \sa currentColumn() +*/ + +/*! + \fn int Q3Table::currentColumn() const + + Returns the current column. + + \sa currentRow() +*/ + +/*! + \enum Q3Table::EditMode + + \value NotEditing No cell is currently being edited. + + \value Editing A cell is currently being edited. The editor was + initialised with the cell's contents. + + \value Replacing A cell is currently being edited. The editor was + not initialised with the cell's contents. +*/ + +/*! + \enum Q3Table::SelectionMode + + \value NoSelection No cell can be selected by the user. + + \value Single The user may only select a single range of cells. + + \value Multi The user may select multiple ranges of cells. + + \value SingleRow The user may select one row at once. + + \value MultiRow The user may select multiple rows. +*/ + +/*! + \enum Q3Table::FocusStyle + + Specifies how the current cell (focus cell) is drawn. + + \value FollowStyle The current cell is drawn according to the + current style and the cell's background is also drawn selected, if + the current cell is within a selection + + \value SpreadSheet The current cell is drawn as in a spreadsheet. + This means, it is signified by a black rectangle around the cell, + and the background of the current cell is always drawn with the + widget's base color - even when selected. + +*/ + +/*! + \fn void Q3Table::clicked(int row, int col, int button, const QPoint &mousePos) + + This signal is emitted when mouse button \a button is clicked. The + cell where the event took place is at \a row, \a col, and the + mouse's position is in \a mousePos. + + \sa Qt::MouseButton +*/ + +/*! + \fn void Q3Table::doubleClicked(int row, int col, int button, const QPoint &mousePos) + + This signal is emitted when mouse button \a button is + double-clicked. The cell where the event took place is at \a row, + \a col, and the mouse's position is in \a mousePos. + + \sa Qt::MouseButton +*/ + +/*! + \fn void Q3Table::pressed(int row, int col, int button, const QPoint &mousePos) + + This signal is emitted when mouse button \a button is pressed. The + cell where the event took place is at \a row, \a col, and the + mouse's position is in \a mousePos. + + \sa Qt::MouseButton +*/ + +/*! + \fn void Q3Table::selectionChanged() + + This signal is emitted whenever a selection changes. + + \sa Q3TableSelection +*/ + +/*! + \fn void Q3Table::contextMenuRequested(int row, int col, const QPoint & pos) + + This signal is emitted when the user invokes a context menu with + the right mouse button (or with a system-specific keypress). The + cell where the event took place is at \a row, \a col. \a pos is + the position where the context menu will appear in the global + coordinate system. This signal is always emitted, even if the + contents of the cell are disabled. +*/ + +/*! + Creates an empty table object called \a name as a child of \a + parent. + + Call setNumRows() and setNumCols() to set the table size before + populating the table if you're using Q3TableItems. +*/ + +Q3Table::Q3Table(QWidget *parent, const char *name) + : Q3ScrollView(parent, name, WNoAutoErase | WStaticContents), + leftHeader(0), topHeader(0), + currentSel(0), lastSortCol(-1), sGrid(true), mRows(false), mCols(false), + asc(true), doSort(true), readOnly(false) +{ + init(0, 0); +} + +/*! + Constructs an empty table called \a name with \a numRows rows and + \a numCols columns. The table is a child of \a parent. + + If you're using \l{Q3TableItem}s to populate the table's cells, you + can create Q3TableItem, Q3ComboTableItem and Q3CheckTableItem items + and insert them into the table using setItem(). (See the notes on + large tables for an alternative to using Q3TableItems.) +*/ + +Q3Table::Q3Table(int numRows, int numCols, QWidget *parent, const char *name) + : Q3ScrollView(parent, name, WNoAutoErase | WStaticContents), + leftHeader(0), topHeader(0), + currentSel(0), lastSortCol(-1), sGrid(true), mRows(false), mCols(false), + asc(true), doSort(true), readOnly(false) +{ + init(numRows, numCols); +} + +/*! \internal +*/ + +void Q3Table::init(int rows, int cols) +{ +#ifndef QT_NO_DRAGANDDROP + setDragAutoScroll(false); +#endif + d = new Q3TablePrivate; + d->geomTimer = new QTimer(this); + d->lastVisCol = 0; + d->lastVisRow = 0; + connect(d->geomTimer, SIGNAL(timeout()), this, SLOT(updateGeometriesSlot())); + shouldClearSelection = false; + dEnabled = false; + roRows.setAutoDelete(true); + roCols.setAutoDelete(true); + setSorting(false); + + unused = true; // It's unused, ain't it? :) + + selMode = Multi; + + contents.setAutoDelete(true); + widgets.setAutoDelete(true); + + // Enable clipper and set background mode + enableClipper(qt_table_clipper_enabled); + + viewport()->setFocusProxy(this); + viewport()->setFocusPolicy(Qt::WheelFocus); + setFocusPolicy(Qt::WheelFocus); + + viewport()->setBackgroundMode(PaletteBase); + setBackgroundMode(PaletteBackground, PaletteBase); + setResizePolicy(Manual); + selections.setAutoDelete(true); + + // Create headers + leftHeader = new Q3TableHeader(rows, this, this, "left table header"); + leftHeader->setOrientation(Vertical); + leftHeader->setTracking(true); + leftHeader->setMovingEnabled(true); + topHeader = new Q3TableHeader(cols, this, this, "right table header"); + topHeader->setOrientation(Horizontal); + topHeader->setTracking(true); + topHeader->setMovingEnabled(true); + if (QApplication::reverseLayout()) + setMargins(0, fontMetrics().height() + 4, 30, 0); + else + setMargins(30, fontMetrics().height() + 4, 0, 0); + + topHeader->setUpdatesEnabled(false); + leftHeader->setUpdatesEnabled(false); + // Initialize headers + int i = 0; + for (i = 0; i < numCols(); ++i) + topHeader->resizeSection(i, QMAX(100, QApplication::globalStrut().height())); + for (i = 0; i < numRows(); ++i) + leftHeader->resizeSection(i, QMAX(20, QApplication::globalStrut().width())); + topHeader->setUpdatesEnabled(true); + leftHeader->setUpdatesEnabled(true); + + // Prepare for contents + contents.setAutoDelete(false); + + // Connect header, table and scroll bars + connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), + topHeader, SLOT(setOffset(int))); + connect(verticalScrollBar(), SIGNAL(valueChanged(int)), + leftHeader, SLOT(setOffset(int))); + connect(topHeader, SIGNAL(sectionSizeChanged(int)), + this, SLOT(columnWidthChanged(int))); + connect(topHeader, SIGNAL(indexChange(int,int,int)), + this, SLOT(columnIndexChanged(int,int,int))); + connect(topHeader, SIGNAL(sectionClicked(int)), + this, SLOT(columnClicked(int))); + connect(leftHeader, SIGNAL(sectionSizeChanged(int)), + this, SLOT(rowHeightChanged(int))); + connect(leftHeader, SIGNAL(indexChange(int,int,int)), + this, SLOT(rowIndexChanged(int,int,int))); + + // Initialize variables + autoScrollTimer = new QTimer(this); + connect(autoScrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll())); + curRow = curCol = 0; + topHeader->setSectionState(curCol, Q3TableHeader::Bold); + leftHeader->setSectionState(curRow, Q3TableHeader::Bold); + edMode = NotEditing; + editRow = editCol = -1; + + drawActiveSelection = true; + + installEventFilter(this); + + focusStl = SpreadSheet; + + was_visible = false; + + // initial size + resize(640, 480); +} + +/*! + Releases all the resources used by the Q3Table object, + including all \l{Q3TableItem}s and their widgets. +*/ + +Q3Table::~Q3Table() +{ + setUpdatesEnabled(false); + contents.setAutoDelete(true); + contents.clear(); + widgets.clear(); + + delete d; +} + +void Q3Table::setReadOnly(bool b) +{ + readOnly = b; + + Q3TableItem *i = item(curRow, curCol); + if (readOnly && isEditing()) { + endEdit(editRow, editCol, true, false); + } else if (!readOnly && i && (i->editType() == Q3TableItem::WhenCurrent + || i->editType() == Q3TableItem::Always)) { + editCell(curRow, curCol); + } +} + +/*! + If \a ro is true, row \a row is set to be read-only; otherwise the + row is set to be editable. + + Whether a cell in this row is editable or read-only depends on the + cell's EditType, and this setting. + + \sa isRowReadOnly() setColumnReadOnly() setReadOnly() +*/ + +void Q3Table::setRowReadOnly(int row, bool ro) +{ + if (ro) + roRows.replace(row, new int(0)); + else + roRows.remove(row); + + if (curRow == row) { + Q3TableItem *i = item(curRow, curCol); + if (ro && isEditing()) { + endEdit(editRow, editCol, true, false); + } else if (!ro && i && (i->editType() == Q3TableItem::WhenCurrent + || i->editType() == Q3TableItem::Always)) { + editCell(curRow, curCol); + } + } +} + +/*! + If \a ro is true, column \a col is set to be read-only; otherwise + the column is set to be editable. + + Whether a cell in this column is editable or read-only depends on + the cell's EditType, and this setting. + + \sa isColumnReadOnly() setRowReadOnly() setReadOnly() + +*/ + +void Q3Table::setColumnReadOnly(int col, bool ro) +{ + if (ro) + roCols.replace(col, new int(0)); + else + roCols.remove(col); + + if (curCol == col) { + Q3TableItem *i = item(curRow, curCol); + if (ro && isEditing()) { + endEdit(editRow, editCol, true, false); + } else if (!ro && i && (i->editType() == Q3TableItem::WhenCurrent + || i->editType() == Q3TableItem::Always)) { + editCell(curRow, curCol); + } + } +} + +/*! + \property Q3Table::readOnly + \brief whether the table is read-only + + Whether a cell in the table is editable or read-only depends on + the cell's \link Q3TableItem::EditType EditType\endlink, and this setting. + + \sa QWidget::enabled setColumnReadOnly() setRowReadOnly() +*/ + +bool Q3Table::isReadOnly() const +{ + return readOnly; +} + +/*! + Returns true if row \a row is read-only; otherwise returns false. + + Whether a cell in this row is editable or read-only depends on the + cell's \link Q3TableItem::EditType EditType\endlink, and this + setting. + + \sa setRowReadOnly() isColumnReadOnly() +*/ + +bool Q3Table::isRowReadOnly(int row) const +{ + return (roRows.find(row) != 0); +} + +/*! + Returns true if column \a col is read-only; otherwise returns + false. + + Whether a cell in this column is editable or read-only depends on + the cell's EditType, and this setting. + + \sa setColumnReadOnly() isRowReadOnly() +*/ + +bool Q3Table::isColumnReadOnly(int col) const +{ + return (roCols.find(col) != 0); +} + +void Q3Table::setSelectionMode(SelectionMode mode) +{ + if (mode == selMode) + return; + selMode = mode; + clearSelection(); + if (isRowSelection(selMode) && numRows() > 0 && numCols() > 0) { + currentSel = new Q3TableSelection(); + selections.append(currentSel); + currentSel->init(curRow, 0); + currentSel->expandTo(curRow, numCols() - 1); + repaintSelections(0, currentSel); + } +} + +/*! + \property Q3Table::selectionMode + \brief the current selection mode + + The default mode is \c Multi which allows the user to select + multiple ranges of cells. +*/ + +Q3Table::SelectionMode Q3Table::selectionMode() const +{ + return selMode; +} + +/*! + \property Q3Table::focusStyle + \brief how the current (focus) cell is drawn + + The default style is \c SpreadSheet. + + \sa Q3Table::FocusStyle +*/ + +void Q3Table::setFocusStyle(FocusStyle fs) +{ + focusStl = fs; + updateCell(curRow, curCol); +} + +Q3Table::FocusStyle Q3Table::focusStyle() const +{ + return focusStl; +} + +/*! + This functions updates all the header states to be in sync with + the current selections. This should be called after + programmatically changing, adding or removing selections, so that + the headers are updated. +*/ + +void Q3Table::updateHeaderStates() +{ + horizontalHeader()->setUpdatesEnabled(false); + verticalHeader()->setUpdatesEnabled(false); + + ((Q3TableHeader*)verticalHeader())->setSectionStateToAll(Q3TableHeader::Normal); + ((Q3TableHeader*)horizontalHeader())->setSectionStateToAll(Q3TableHeader::Normal); + + Q3PtrListIterator<Q3TableSelection> it(selections); + Q3TableSelection *s; + while ((s = it.current()) != 0) { + ++it; + if (s->isActive()) { + if (s->leftCol() == 0 && + s->rightCol() == numCols() - 1) { + for (int i = 0; i < s->bottomRow() - s->topRow() + 1; ++i) + leftHeader->setSectionState(s->topRow() + i, Q3TableHeader::Selected); + } + if (s->topRow() == 0 && + s->bottomRow() == numRows() - 1) { + for (int i = 0; i < s->rightCol() - s->leftCol() + 1; ++i) + topHeader->setSectionState(s->leftCol() + i, Q3TableHeader::Selected); + } + } + } + + horizontalHeader()->setUpdatesEnabled(true); + verticalHeader()->setUpdatesEnabled(true); + horizontalHeader()->repaint(false); + verticalHeader()->repaint(false); +} + +/*! + Returns the table's top Q3Header. + + This header contains the column labels. + + To modify a column label use Q3Header::setLabel(). + + \sa verticalHeader() setTopMargin() Q3Header +*/ + +Q3Header *Q3Table::horizontalHeader() const +{ + return (Q3Header*)topHeader; +} + +/*! + Returns the table's vertical Q3Header. + + This header contains the row labels. + + \sa horizontalHeader() setLeftMargin() Q3Header +*/ + +Q3Header *Q3Table::verticalHeader() const +{ + return (Q3Header*)leftHeader; +} + +void Q3Table::setShowGrid(bool b) +{ + if (sGrid == b) + return; + sGrid = b; + updateContents(); +} + +/*! + \property Q3Table::showGrid + \brief whether the table's grid is displayed + + The grid is shown by default. +*/ + +bool Q3Table::showGrid() const +{ + return sGrid; +} + +/*! + \property Q3Table::columnMovingEnabled + \brief whether columns can be moved by the user + + The default is false. Columns are moved by dragging whilst holding + down the Ctrl key. + + \sa rowMovingEnabled +*/ + +void Q3Table::setColumnMovingEnabled(bool b) +{ + mCols = b; +} + +bool Q3Table::columnMovingEnabled() const +{ + return mCols; +} + +/*! + \property Q3Table::rowMovingEnabled + \brief whether rows can be moved by the user + + The default is false. Rows are moved by dragging whilst holding + down the Ctrl key. + + + \sa columnMovingEnabled +*/ + +void Q3Table::setRowMovingEnabled(bool b) +{ + mRows = b; +} + +bool Q3Table::rowMovingEnabled() const +{ + return mRows; +} + +/*! + This is called when Q3Table's internal array needs to be resized to + \a len elements. + + If you don't use Q3TableItems you should reimplement this as an + empty method to avoid wasting memory. See the notes on large + tables for further details. +*/ + +void Q3Table::resizeData(int len) +{ + contents.resize(len); + widgets.resize(len); +} + +/*! + Swaps the data in \a row1 and \a row2. + + This function is used to swap the positions of two rows. It is + called when the user changes the order of rows (see + setRowMovingEnabled()), and when rows are sorted. + + If you don't use \l{Q3TableItem}s and want your users to be able to + swap rows, e.g. for sorting, you will need to reimplement this + function. (See the notes on large tables.) + + If \a swapHeader is true, the rows' header contents is also + swapped. + + This function will not update the Q3Table, you will have to do + this manually, e.g. by calling updateContents(). + + \sa swapColumns() swapCells() +*/ + +void Q3Table::swapRows(int row1, int row2, bool swapHeader) +{ + if (swapHeader) + leftHeader->swapSections(row1, row2, false); + + Q3PtrVector<Q3TableItem> tmpContents; + tmpContents.resize(numCols()); + Q3PtrVector<QWidget> tmpWidgets; + tmpWidgets.resize(numCols()); + int i; + + contents.setAutoDelete(false); + widgets.setAutoDelete(false); + for (i = 0; i < numCols(); ++i) { + Q3TableItem *i1, *i2; + i1 = item(row1, i); + i2 = item(row2, i); + if (i1 || i2) { + tmpContents.insert(i, i1); + contents.remove(indexOf(row1, i)); + contents.insert(indexOf(row1, i), i2); + contents.remove(indexOf(row2, i)); + contents.insert(indexOf(row2, i), tmpContents[ i ]); + if (contents[ indexOf(row1, i) ]) + contents[ indexOf(row1, i) ]->setRow(row1); + if (contents[ indexOf(row2, i) ]) + contents[ indexOf(row2, i) ]->setRow(row2); + } + + QWidget *w1, *w2; + w1 = cellWidget(row1, i); + w2 = cellWidget(row2, i); + if (w1 || w2) { + tmpWidgets.insert(i, w1); + widgets.remove(indexOf(row1, i)); + widgets.insert(indexOf(row1, i), w2); + widgets.remove(indexOf(row2, i)); + widgets.insert(indexOf(row2, i), tmpWidgets[ i ]); + } + } + contents.setAutoDelete(false); + widgets.setAutoDelete(true); + + updateRowWidgets(row1); + updateRowWidgets(row2); + if (curRow == row1) + curRow = row2; + else if (curRow == row2) + curRow = row1; + if (editRow == row1) + editRow = row2; + else if (editRow == row2) + editRow = row1; +} + +/*! + Sets the left margin to be \a m pixels wide. + + The verticalHeader(), which displays row labels, occupies this + margin. + + In an Arabic or Hebrew localization, the verticalHeader() will + appear on the right side of the table, and this call will set the + right margin. + + \sa leftMargin() setTopMargin() verticalHeader() +*/ + +void Q3Table::setLeftMargin(int m) +{ + if (QApplication::reverseLayout()) + setMargins(leftMargin(), topMargin(), m, bottomMargin()); + else + setMargins(m, topMargin(), rightMargin(), bottomMargin()); + updateGeometries(); +} + +/*! + Sets the top margin to be \a m pixels high. + + The horizontalHeader(), which displays column labels, occupies + this margin. + + \sa topMargin() setLeftMargin() +*/ + +void Q3Table::setTopMargin(int m) +{ + setMargins(leftMargin(), m, rightMargin(), bottomMargin()); + updateGeometries(); +} + +/*! + Swaps the data in \a col1 with \a col2. + + This function is used to swap the positions of two columns. It is + called when the user changes the order of columns (see + setColumnMovingEnabled(), and when columns are sorted. + + If you don't use \l{Q3TableItem}s and want your users to be able to + swap columns you will need to reimplement this function. (See the + notes on large tables.) + + If \a swapHeader is true, the columns' header contents is also + swapped. + + \sa swapCells() +*/ + +void Q3Table::swapColumns(int col1, int col2, bool swapHeader) +{ + if (swapHeader) + topHeader->swapSections(col1, col2, false); + + Q3PtrVector<Q3TableItem> tmpContents; + tmpContents.resize(numRows()); + Q3PtrVector<QWidget> tmpWidgets; + tmpWidgets.resize(numRows()); + int i; + + contents.setAutoDelete(false); + widgets.setAutoDelete(false); + for (i = 0; i < numRows(); ++i) { + Q3TableItem *i1, *i2; + i1 = item(i, col1); + i2 = item(i, col2); + if (i1 || i2) { + tmpContents.insert(i, i1); + contents.remove(indexOf(i, col1)); + contents.insert(indexOf(i, col1), i2); + contents.remove(indexOf(i, col2)); + contents.insert(indexOf(i, col2), tmpContents[ i ]); + if (contents[ indexOf(i, col1) ]) + contents[ indexOf(i, col1) ]->setCol(col1); + if (contents[ indexOf(i, col2) ]) + contents[ indexOf(i, col2) ]->setCol(col2); + } + + QWidget *w1, *w2; + w1 = cellWidget(i, col1); + w2 = cellWidget(i, col2); + if (w1 || w2) { + tmpWidgets.insert(i, w1); + widgets.remove(indexOf(i, col1)); + widgets.insert(indexOf(i, col1), w2); + widgets.remove(indexOf(i, col2)); + widgets.insert(indexOf(i, col2), tmpWidgets[ i ]); + } + } + contents.setAutoDelete(false); + widgets.setAutoDelete(true); + + columnWidthChanged(col1); + columnWidthChanged(col2); + if (curCol == col1) + curCol = col2; + else if (curCol == col2) + curCol = col1; + if (editCol == col1) + editCol = col2; + else if (editCol == col2) + editCol = col1; +} + +/*! + Swaps the contents of the cell at \a row1, \a col1 with the + contents of the cell at \a row2, \a col2. + + This function is also called when the table is sorted. + + If you don't use \l{Q3TableItem}s and want your users to be able to + swap cells, you will need to reimplement this function. (See the + notes on large tables.) + + \sa swapColumns() swapRows() +*/ + +void Q3Table::swapCells(int row1, int col1, int row2, int col2) +{ + contents.setAutoDelete(false); + widgets.setAutoDelete(false); + Q3TableItem *i1, *i2; + i1 = item(row1, col1); + i2 = item(row2, col2); + if (i1 || i2) { + Q3TableItem *tmp = i1; + contents.remove(indexOf(row1, col1)); + contents.insert(indexOf(row1, col1), i2); + contents.remove(indexOf(row2, col2)); + contents.insert(indexOf(row2, col2), tmp); + if (contents[ indexOf(row1, col1) ]) { + contents[ indexOf(row1, col1) ]->setRow(row1); + contents[ indexOf(row1, col1) ]->setCol(col1); + } + if (contents[ indexOf(row2, col2) ]) { + contents[ indexOf(row2, col2) ]->setRow(row2); + contents[ indexOf(row2, col2) ]->setCol(col2); + } + } + + QWidget *w1, *w2; + w1 = cellWidget(row1, col1); + w2 = cellWidget(row2, col2); + if (w1 || w2) { + QWidget *tmp = w1; + widgets.remove(indexOf(row1, col1)); + widgets.insert(indexOf(row1, col1), w2); + widgets.remove(indexOf(row2, col2)); + widgets.insert(indexOf(row2, col2), tmp); + } + + updateRowWidgets(row1); + updateRowWidgets(row2); + updateColWidgets(col1); + updateColWidgets(col2); + contents.setAutoDelete(false); + widgets.setAutoDelete(true); +} + +static bool is_child_of(QWidget *child, QWidget *parent) +{ + while (child) { + if (child == parent) + return true; + child = child->parentWidget(); + } + return false; +} + +/*! + Draws the table contents on the painter \a p. This function is + optimized so that it only draws the cells inside the \a cw pixels + wide and \a ch pixels high clipping rectangle at position \a cx, + \a cy. + + Additionally, drawContents() highlights the current cell. +*/ + +void Q3Table::drawContents(QPainter *p, int cx, int cy, int cw, int ch) +{ + int colfirst = columnAt(cx); + int collast = columnAt(cx + cw); + int rowfirst = rowAt(cy); + int rowlast = rowAt(cy + ch); + + if (rowfirst == -1 || colfirst == -1) { + paintEmptyArea(p, cx, cy, cw, ch); + return; + } + + drawActiveSelection = hasFocus() || viewport()->hasFocus() || d->inMenuMode + || is_child_of(qApp->focusWidget(), viewport()) + || !style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this); + if (rowlast == -1) + rowlast = numRows() - 1; + if (collast == -1) + collast = numCols() - 1; + + bool currentInSelection = false; + + Q3PtrListIterator<Q3TableSelection> it( selections ); + Q3TableSelection *s; + while ( ( s = it.current() ) != 0 ) { + ++it; + if (s->isActive() && + curRow >= s->topRow() && + curRow <= s->bottomRow() && + curCol >= s->leftCol() && + curCol <= s->rightCol()) { + currentInSelection = s->topRow() != curRow || s->bottomRow() != curRow || s->leftCol() != curCol || s->rightCol() != curCol; + break; + } + } + + // Go through the rows + for (int r = rowfirst; r <= rowlast; ++r) { + // get row position and height + int rowp = rowPos(r); + int rowh = rowHeight(r); + + // Go through the columns in row r + // if we know from where to where, go through [colfirst, collast], + // else go through all of them + for (int c = colfirst; c <= collast; ++c) { + // get position and width of column c + int colp, colw; + colp = columnPos(c); + colw = columnWidth(c); + int oldrp = rowp; + int oldrh = rowh; + + Q3TableItem *itm = item(r, c); + if (itm && + (itm->colSpan() > 1 || itm->rowSpan() > 1)) { + bool goon = (r == itm->row() && c == itm->col()) + || (r == rowfirst && c == itm->col()) + || (r == itm->row() && c == colfirst); + if (!goon) + continue; + rowp = rowPos(itm->row()); + rowh = 0; + int i; + for (i = 0; i < itm->rowSpan(); ++i) + rowh += rowHeight(i + itm->row()); + colp = columnPos(itm->col()); + colw = 0; + for (i = 0; i < itm->colSpan(); ++i) + colw += columnWidth(i + itm->col()); + } + + // Translate painter and draw the cell + p->translate(colp, rowp); + bool selected = isSelected(r, c); + if (focusStl != FollowStyle && selected && !currentInSelection && + r == curRow && c == curCol ) + selected = false; + paintCell(p, r, c, QRect(colp, rowp, colw, rowh), selected); + p->translate(-colp, -rowp); + + rowp = oldrp; + rowh = oldrh; + + QWidget *w = cellWidget(r, c); + QRect cg(cellGeometry(r, c)); + if (w && w->geometry() != QRect(contentsToViewport(cg.topLeft()), cg.size() - QSize(1, 1))) { + moveChild(w, colp, rowp); + w->resize(cg.size() - QSize(1, 1)); + } + } + } + d->lastVisCol = collast; + d->lastVisRow = rowlast; + + // draw indication of current cell + QRect focusRect = cellGeometry(curRow, curCol); + p->translate(focusRect.x(), focusRect.y()); + paintFocus(p, focusRect); + p->translate(-focusRect.x(), -focusRect.y()); + + // Paint empty rects + paintEmptyArea(p, cx, cy, cw, ch); + + drawActiveSelection = true; +} + +/*! + \reimp + + (Implemented to get rid of a compiler warning.) +*/ + +void Q3Table::drawContents(QPainter *) +{ +} + +/*! + Returns the geometry of cell \a row, \a col in the cell's + coordinate system. This is a convenience function useful in + paintCell(). It is equivalent to QRect(QPoint(0,0), cellGeometry( + row, col).size()); + + \sa cellGeometry() +*/ + +QRect Q3Table::cellRect(int row, int col) const +{ + return QRect(QPoint(0,0), cellGeometry(row, col).size()); +} + +/*! + \overload + + Use the other paintCell() function. This function is only included + for backwards compatibility. +*/ + +void Q3Table::paintCell(QPainter* p, int row, int col, + const QRect &cr, bool selected) +{ + if (cr.width() == 0 || cr.height() == 0) + return; +#if defined(Q_WS_WIN) + const QColorGroup &cg = (!drawActiveSelection && style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus) ? palette().inactive() : colorGroup()); +#else + const QColorGroup &cg = colorGroup(); +#endif + + Q3TableItem *itm = item(row, col); + QColorGroup cg2(cg); + if (itm && !itm->isEnabled()) + cg2 = palette().disabled(); + + paintCell(p, row, col, cr, selected, cg2); +} + +/*! + Paints the cell at \a row, \a col on the painter \a p. The painter + has already been translated to the cell's origin. \a cr describes + the cell coordinates in the content coordinate system. + + If \a selected is true the cell is highlighted. + + \a cg is the colorgroup which should be used to draw the cell + content. + + If you want to draw custom cell content, for example right-aligned + text, you must either reimplement paintCell(), or subclass + Q3TableItem and reimplement Q3TableItem::paint() to do the custom + drawing. + + If you're using a Q3TableItem subclass, for example, to store a + data structure, then reimplementing Q3TableItem::paint() may be the + best approach. For data you want to draw immediately, e.g. data + retrieved from a database, it is probably best to reimplement + paintCell(). Note that if you reimplement paintCell(), i.e. don't + use \l{Q3TableItem}s, you must reimplement other functions: see the + notes on large tables. + + Note that the painter is not clipped by default in order to get + maximum efficiency. If you want clipping, use code like this: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 4 +*/ + +void Q3Table::paintCell(QPainter *p, int row, int col, + const QRect &cr, bool selected, const QColorGroup &cg) +{ + if (focusStl == SpreadSheet && selected && + row == curRow && + col == curCol && (hasFocus() || viewport()->hasFocus())) + selected = false; + + QPalette pal = cg; + int w = cr.width(); + int h = cr.height(); + int x2 = w - 1; + int y2 = h - 1; + + + Q3TableItem *itm = item(row, col); + if (itm) { + p->save(); + itm->paint(p, pal, cr, selected); + p->restore(); + } else { + p->fillRect(0, 0, w, h, selected ? pal.brush(QPalette::Highlight) : pal.brush(QPalette::Base)); + } + + if (sGrid) { + // Draw our lines + QPen pen(p->pen()); + int gridColor = style()->styleHint(QStyle::SH_Table_GridLineColor, 0, this); + if (gridColor != -1) { + if (palette() != pal) + p->setPen(pal.mid().color()); + else + p->setPen((QRgb)gridColor); + } else { + p->setPen(pal.mid().color()); + } + p->drawLine(x2, 0, x2, y2); + p->drawLine(0, y2, x2, y2); + p->setPen(pen); + } +} + +/*! + Draws the focus rectangle of the current cell (see currentRow(), + currentColumn()). + + The painter \a p is already translated to the cell's origin, while + \a cr specifies the cell's geometry in content coordinates. +*/ + +void Q3Table::paintFocus(QPainter *p, const QRect &cr) +{ + if (!hasFocus() && !viewport()->hasFocus()) + return; + QRect focusRect(0, 0, cr.width(), cr.height()); + if (focusStyle() == SpreadSheet) { + p->setPen(QPen(Qt::black, 1)); + p->setBrush(Qt::NoBrush); + p->drawRect(focusRect.x(), focusRect.y(), focusRect.width() - 1, focusRect.height() - 1); + p->drawRect(focusRect.x() - 1, focusRect.y() - 1, focusRect.width() + 1, focusRect.height() + 1); + } else { + QStyleOptionFocusRect opt; + opt.init(this); + opt.rect = focusRect; + opt.palette = palette(); + opt.state |= QStyle::State_KeyboardFocusChange; + if (isSelected(curRow, curCol, false)) { + opt.state |= QStyle::State_FocusAtBorder; + opt.backgroundColor = palette().highlight().color(); + } else { + opt.state |= QStyle::State_None; + opt.backgroundColor = palette().base().color(); + } + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, this); + } +} + +/*! + This function fills the \a cw pixels wide and \a ch pixels high + rectangle starting at position \a cx, \a cy with the background + color using the painter \a p. + + paintEmptyArea() is invoked by drawContents() to erase or fill + unused areas. +*/ + +void Q3Table::paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch) +{ + // Regions work with shorts, so avoid an overflow and adjust the + // table size to the visible size + QSize ts(tableSize()); + ts.setWidth(QMIN(ts.width(), visibleWidth())); + ts.setHeight(QMIN(ts.height(), visibleHeight())); + + // Region of the rect we should draw, calculated in viewport + // coordinates, as a region can't handle bigger coordinates + contentsToViewport2(cx, cy, cx, cy); + QRegion reg(QRect(cx, cy, cw, ch)); + + // Subtract the table from it + reg = reg.subtracted(QRect(QPoint(0, 0), ts)); + + // And draw the rectangles (transformed inc contents coordinates as needed) + Q3MemArray<QRect> r = reg.rects(); + for (int i = 0; i < (int)r.count(); ++i) + p->fillRect(QRect(viewportToContents2(r[i].topLeft()),r[i].size()), viewport()->backgroundBrush()); +} + +/*! + Returns the Q3TableItem representing the contents of the cell at \a + row, \a col. + + If \a row or \a col are out of range or no content has been set + for this cell, item() returns 0. + + If you don't use \l{Q3TableItem}s you may need to reimplement this + function: see the notes on large tables. + + \sa setItem() +*/ + +Q3TableItem *Q3Table::item(int row, int col) const +{ + if (row < 0 || col < 0 || row > numRows() - 1 || + col > numCols() - 1 || row * col >= (int)contents.size()) + return 0; + + return contents[ indexOf(row, col) ]; // contents array lookup +} + +/*! + Inserts the table item \a item into the table at row \a row, + column \a col, and repaints the cell. If a table item already + exists in this cell it is deleted and replaced with \a item. The + table takes ownership of the table item. + + If you don't use \l{Q3TableItem}s you may need to reimplement this + function: see the notes on large tables. + + \sa item() takeItem() +*/ + +void Q3Table::setItem(int row, int col, Q3TableItem *item) +{ + if (!item) + return; + + if ((int)contents.size() != numRows() * numCols()) + resizeData(numRows() * numCols()); + + int orow = item->row(); + int ocol = item->col(); + clearCell(row, col); + + contents.insert(indexOf(row, col), item); + item->setRow(row); + item->setCol(col); + item->t = this; + updateCell(row, col); + if (qt_update_cell_widget) + item->updateEditor(orow, ocol); + + if (row == curRow && col == curCol && item->editType() == Q3TableItem::WhenCurrent) { + if (beginEdit(row, col, false)) + setEditMode(Editing, row, col); + } +} + +/*! + Removes the Q3TableItem at \a row, \a col. + + If you don't use \l{Q3TableItem}s you may need to reimplement this + function: see the notes on large tables. +*/ + +void Q3Table::clearCell(int row, int col) +{ + if ((int)contents.size() != numRows() * numCols()) + resizeData(numRows() * numCols()); + clearCellWidget(row, col); + contents.setAutoDelete(true); + contents.remove(indexOf(row, col)); + contents.setAutoDelete(false); +} + +/*! + Sets the text in the cell at \a row, \a col to \a text. + + If the cell does not contain a table item a Q3TableItem is created + with an \link Q3TableItem::EditType EditType\endlink of \c OnTyping, + otherwise the existing table item's text (if any) is replaced with + \a text. + + \sa text() setPixmap() setItem() Q3TableItem::setText() +*/ + +void Q3Table::setText(int row, int col, const QString &text) +{ + Q3TableItem *itm = item(row, col); + if (itm) { + itm->setText(text); + itm->updateEditor(row, col); + updateCell(row, col); + } else { + Q3TableItem *i = new Q3TableItem(this, Q3TableItem::OnTyping, + text, QPixmap()); + setItem(row, col, i); + } +} + +/*! + Sets the pixmap in the cell at \a row, \a col to \a pix. + + If the cell does not contain a table item a Q3TableItem is created + with an \link Q3TableItem::EditType EditType\endlink of \c OnTyping, + otherwise the existing table item's pixmap (if any) is replaced + with \a pix. + + Note that \l{Q3ComboTableItem}s and \l{Q3CheckTableItem}s don't show + pixmaps. + + \sa pixmap() setText() setItem() Q3TableItem::setPixmap() +*/ + +void Q3Table::setPixmap(int row, int col, const QPixmap &pix) +{ + Q3TableItem *itm = item(row, col); + if (itm) { + itm->setPixmap(pix); + updateCell(row, col); + } else { + Q3TableItem *i = new Q3TableItem(this, Q3TableItem::OnTyping, + QString(), pix); + setItem(row, col, i); + } +} + +/*! + Returns the text in the cell at \a row, \a col, or an empty string + if the relevant item does not exist or has no text. + + \sa setText() setPixmap() +*/ + +QString Q3Table::text(int row, int col) const +{ + Q3TableItem *itm = item(row, col); + if (itm) + return itm->text(); + return QString(); +} + +/*! + Returns the pixmap set for the cell at \a row, \a col, or a + null-pixmap if the cell contains no pixmap. + + \sa setPixmap() +*/ + +QPixmap Q3Table::pixmap(int row, int col) const +{ + Q3TableItem *itm = item(row, col); + if (itm) + return itm->pixmap(); + return QPixmap(); +} + +/*! + Moves the focus to the cell at \a row, \a col. + + \sa currentRow() currentColumn() +*/ + +void Q3Table::setCurrentCell(int row, int col) +{ + setCurrentCell(row, col, true, true); +} + +// need to use a define, as leftMargin() is protected +#define VERTICALMARGIN \ +(QApplication::reverseLayout() ? \ + rightMargin() \ + : \ + leftMargin() \ +) + +/*! + \reimp +*/ +QVariant Q3Table::inputMethodQuery(Qt::InputMethodQuery query) const +{ + if (query == Qt::ImMicroFocus) + return QRect(columnPos(curCol) + leftMargin() - contentsX(), rowPos(curRow) + topMargin() - contentsY(), + columnWidth(curCol), rowHeight(curRow)); + return QWidget::inputMethodQuery(query); + +} + +/*! \internal */ + +void Q3Table::setCurrentCell(int row, int col, bool updateSelections, bool ensureVisible) +{ + Q3TableItem *oldItem = item(curRow, curCol); + + if (row > numRows() - 1) + row = numRows() - 1; + if (col > numCols() - 1) + col = numCols() - 1; + + if (curRow == row && curCol == col) + return; + + + Q3TableItem *itm = oldItem; + if (itm && itm->editType() != Q3TableItem::Always && itm->editType() != Q3TableItem::Never) + endEdit(curRow, curCol, true, false); + int oldRow = curRow; + int oldCol = curCol; + curRow = row; + curCol = col; + repaintCell(oldRow, oldCol); + repaintCell(curRow, curCol); + if (ensureVisible) + ensureCellVisible(curRow, curCol); + emit currentChanged(row, col); + + if (oldCol != curCol) { + if (!isColumnSelected(oldCol)) + topHeader->setSectionState(oldCol, Q3TableHeader::Normal); + else if (isRowSelection(selectionMode())) + topHeader->setSectionState(oldCol, Q3TableHeader::Selected); + topHeader->setSectionState(curCol, isColumnSelected(curCol, true) ? + Q3TableHeader::Selected : Q3TableHeader::Bold); + } + + if (oldRow != curRow) { + if (!isRowSelected(oldRow)) + leftHeader->setSectionState(oldRow, Q3TableHeader::Normal); + leftHeader->setSectionState(curRow, isRowSelected(curRow, true) ? + Q3TableHeader::Selected : Q3TableHeader::Bold); + } + + itm = item(curRow, curCol); + + + if (cellWidget(oldRow, oldCol) && + cellWidget(oldRow, oldCol)->hasFocus()) + viewport()->setFocus(); + + if (itm && itm->editType() == Q3TableItem::WhenCurrent) { + if (beginEdit(curRow, curCol, false)) + setEditMode(Editing, row, col); + } else if (itm && itm->editType() == Q3TableItem::Always) { + if (cellWidget(itm->row(), itm->col())) + cellWidget(itm->row(), itm->col())->setFocus(); + } + + if (updateSelections && isRowSelection(selectionMode()) && + !isSelected(curRow, curCol, false)) { + if (selectionMode() == Q3Table::SingleRow) + clearSelection(); + currentSel = new Q3TableSelection(); + selections.append(currentSel); + currentSel->init(curRow, 0); + currentSel->expandTo(curRow, numCols() - 1); + repaintSelections(0, currentSel); + } +} + +/*! + Scrolls the table until the cell at \a row, \a col becomes + visible. +*/ + +void Q3Table::ensureCellVisible(int row, int col) +{ + if (!updatesEnabled() || !viewport()->updatesEnabled()) + return; + int cw = columnWidth(col); + int rh = rowHeight(row); + if (cw < visibleWidth()) + ensureVisible(columnPos(col) + cw / 2, rowPos(row) + rh / 2, cw / 2, rh / 2); + else + ensureVisible(columnPos(col), rowPos(row) + rh / 2, 0, rh / 2); +} + +/*! + Returns true if the cell at \a row, \a col is selected; otherwise + returns false. + + \sa isRowSelected() isColumnSelected() +*/ + +bool Q3Table::isSelected(int row, int col) const +{ + return isSelected(row, col, true); +} + +/*! \internal */ + +bool Q3Table::isSelected(int row, int col, bool includeCurrent) const +{ + Q3PtrListIterator<Q3TableSelection> it(selections); + Q3TableSelection *s; + while ((s = it.current()) != 0) { + ++it; + if (s->isActive() && + row >= s->topRow() && + row <= s->bottomRow() && + col >= s->leftCol() && + col <= s->rightCol()) + return true; + if (includeCurrent && row == currentRow() && col == currentColumn()) + return true; + } + return false; +} + +/*! + Returns true if row \a row is selected; otherwise returns false. + + If \a full is false (the default), 'row is selected' means that at + least one cell in the row is selected. If \a full is true, then 'row + is selected' means every cell in the row is selected. + + \sa isColumnSelected() isSelected() +*/ + +bool Q3Table::isRowSelected(int row, bool full) const +{ + if (!full) { + Q3PtrListIterator<Q3TableSelection> it(selections); + Q3TableSelection *s; + while ((s = it.current()) != 0) { + ++it; + if (s->isActive() && + row >= s->topRow() && + row <= s->bottomRow()) + return true; + if (row == currentRow()) + return true; + } + } else { + Q3PtrListIterator<Q3TableSelection> it(selections); + Q3TableSelection *s; + while ((s = it.current()) != 0) { + ++it; + if (s->isActive() && + row >= s->topRow() && + row <= s->bottomRow() && + s->leftCol() == 0 && + s->rightCol() == numCols() - 1) + return true; + } + } + return false; +} + +/*! + Returns true if column \a col is selected; otherwise returns false. + + If \a full is false (the default), 'column is selected' means that + at least one cell in the column is selected. If \a full is true, + then 'column is selected' means every cell in the column is + selected. + + \sa isRowSelected() isSelected() +*/ + +bool Q3Table::isColumnSelected(int col, bool full) const +{ + if (!full) { + Q3PtrListIterator<Q3TableSelection> it(selections); + Q3TableSelection *s; + while ((s = it.current()) != 0) { + ++it; + if (s->isActive() && + col >= s->leftCol() && + col <= s->rightCol()) + return true; + if (col == currentColumn()) + return true; + } + } else { + Q3PtrListIterator<Q3TableSelection> it(selections); + Q3TableSelection *s; + while ((s = it.current()) != 0) { + ++it; + if (s->isActive() && + col >= s->leftCol() && + col <= s->rightCol() && + s->topRow() == 0 && + s->bottomRow() == numRows() - 1) + return true; + } + } + return false; +} + +/*! + \property Q3Table::numSelections + \brief The number of selections. + + \sa currentSelection() +*/ + +int Q3Table::numSelections() const +{ + return selections.count(); +} + +/*! + Returns selection number \a num, or an inactive Q3TableSelection if \a + num is out of range (see Q3TableSelection::isActive()). +*/ + +Q3TableSelection Q3Table::selection(int num) const +{ + if (num < 0 || num >= (int)selections.count()) + return Q3TableSelection(); + + Q3TableSelection *s = ((Q3Table*)this)->selections.at(num); + return *s; +} + +/*! + Adds a selection described by \a s to the table and returns its + number or -1 if the selection is invalid. + + Remember to call Q3TableSelection::init() and + Q3TableSelection::expandTo() to make the selection valid (see also + Q3TableSelection::isActive(), or use the + Q3TableSelection(int,int,int,int) constructor). + + \sa numSelections() removeSelection() clearSelection() +*/ + +int Q3Table::addSelection(const Q3TableSelection &s) +{ + if (!s.isActive()) + return -1; + + const int maxr = numRows()-1; + const int maxc = numCols()-1; + currentSel = new Q3TableSelection(QMIN(s.anchorRow(), maxr), QMIN(s.anchorCol(), maxc), + QMIN(s.bottomRow(), maxr), QMIN(s.rightCol(), maxc)); + + selections.append(currentSel); + + repaintSelections(0, currentSel, true, true); + + emit selectionChanged(); + + return selections.count() - 1; +} + +/*! + If the table has a selection, \a s, this selection is removed from + the table. + + \sa addSelection() numSelections() +*/ + +void Q3Table::removeSelection(const Q3TableSelection &s) +{ + selections.setAutoDelete(false); + for (Q3TableSelection *sel = selections.first(); sel; sel = selections.next()) { + if (s == *sel) { + selections.removeRef(sel); + repaintSelections(sel, 0, true, true); + if (sel == currentSel) + currentSel = 0; + delete sel; + } + } + selections.setAutoDelete(true); + emit selectionChanged(); +} + +/*! + \overload + + Removes selection number \a num from the table. + + \sa numSelections() addSelection() clearSelection() +*/ + +void Q3Table::removeSelection(int num) +{ + if (num < 0 || num >= (int)selections.count()) + return; + + Q3TableSelection *s = selections.at(num); + if (s == currentSel) + currentSel = 0; + selections.removeRef(s); + repaintContents(false); +} + +/*! + Returns the number of the current selection or -1 if there is no + current selection. + + \sa numSelections() +*/ + +int Q3Table::currentSelection() const +{ + if (!currentSel) + return -1; + return ((Q3Table*)this)->selections.findRef(currentSel); +} + +/*! Selects the range starting at \a start_row and \a start_col and + ending at \a end_row and \a end_col. + + \sa Q3TableSelection +*/ + +void Q3Table::selectCells(int start_row, int start_col, int end_row, int end_col) +{ + const int maxr = numRows()-1; + const int maxc = numCols()-1; + + start_row = QMIN(maxr, QMAX(0, start_row)); + start_col = QMIN(maxc, QMAX(0, start_col)); + end_row = QMIN(maxr, end_row); + end_col = QMIN(maxc, end_col); + Q3TableSelection sel(start_row, start_col, end_row, end_col); + addSelection(sel); +} + +/*! Selects the row \a row. + + \sa Q3TableSelection +*/ + +void Q3Table::selectRow(int row) +{ + row = QMIN(numRows()-1, row); + if (row < 0) + return; + if (selectionMode() == SingleRow) { + setCurrentCell(row, currentColumn()); + } else { + Q3TableSelection sel(row, 0, row, numCols() - 1); + addSelection(sel); + } +} + +/*! Selects the column \a col. + + \sa Q3TableSelection +*/ + +void Q3Table::selectColumn(int col) +{ + col = QMIN(numCols()-1, col); + if (col < 0) + return; + Q3TableSelection sel(0, col, numRows() - 1, col); + addSelection(sel); +} + +/*! \reimp +*/ +void Q3Table::contentsMousePressEvent(QMouseEvent* e) +{ + contentsMousePressEventEx(e); +} + +void Q3Table::contentsMousePressEventEx(QMouseEvent* e) +{ + shouldClearSelection = false; + if (isEditing()) { + if (!cellGeometry(editRow, editCol).contains(e->pos())) { + endEdit(editRow, editCol, true, edMode != Editing); + } else { + e->ignore(); + return; + } + } + + d->redirectMouseEvent = false; + + int tmpRow = rowAt(e->pos().y()); + int tmpCol = columnAt(e->pos().x()); + pressedRow = tmpRow; + pressedCol = tmpCol; + fixRow(tmpRow, e->pos().y()); + fixCol(tmpCol, e->pos().x()); + startDragCol = -1; + startDragRow = -1; + + if (isSelected(tmpRow, tmpCol)) { + startDragCol = tmpCol; + startDragRow = tmpRow; + dragStartPos = e->pos(); + } + + Q3TableItem *itm = item(pressedRow, pressedCol); + if (itm && !itm->isEnabled()) { + emit pressed(tmpRow, tmpCol, e->button(), e->pos()); + return; + } + + if ((e->state() & ShiftButton) == ShiftButton) { + int oldRow = curRow; + int oldCol = curCol; + setCurrentCell(tmpRow, tmpCol, selMode == SingleRow, true); + if (selMode != NoSelection && selMode != SingleRow) { + if (!currentSel) { + currentSel = new Q3TableSelection(); + selections.append(currentSel); + if (!isRowSelection(selectionMode())) + currentSel->init(oldRow, oldCol); + else + currentSel->init(oldRow, 0); + } + Q3TableSelection oldSelection = *currentSel; + if (!isRowSelection(selectionMode())) + currentSel->expandTo(tmpRow, tmpCol); + else + currentSel->expandTo(tmpRow, numCols() - 1); + repaintSelections(&oldSelection, currentSel); + emit selectionChanged(); + } + } else if ((e->state() & ControlButton) == ControlButton) { + setCurrentCell(tmpRow, tmpCol, false, true); + if (selMode != NoSelection) { + if (selMode == Single || (selMode == SingleRow && !isSelected(tmpRow, tmpCol, false))) + clearSelection(); + if (!(selMode == SingleRow && isSelected(tmpRow, tmpCol, false))) { + currentSel = new Q3TableSelection(); + selections.append(currentSel); + if (!isRowSelection(selectionMode())) { + currentSel->init(tmpRow, tmpCol); + currentSel->expandTo(tmpRow, tmpCol); + } else { + currentSel->init(tmpRow, 0); + currentSel->expandTo(tmpRow, numCols() - 1); + repaintSelections(0, currentSel); + } + emit selectionChanged(); + } + } + } else { + setCurrentCell(tmpRow, tmpCol, false, true); + Q3TableItem *itm = item(tmpRow, tmpCol); + if (itm && itm->editType() == Q3TableItem::WhenCurrent) { + QWidget *w = cellWidget(tmpRow, tmpCol); + if (qobject_cast<Q3ComboBox*>(w) || qobject_cast<QAbstractButton*>(w)) { + QMouseEvent ev(e->type(), w->mapFromGlobal(e->globalPos()), + e->globalPos(), e->button(), e->state()); + QApplication::sendPostedEvents(w, 0); + QApplication::sendEvent(w, &ev); + d->redirectMouseEvent = true; + } + } + if (isSelected(tmpRow, tmpCol, false)) { + shouldClearSelection = true; + } else { + bool b = signalsBlocked(); + if (selMode != NoSelection) + blockSignals(true); + clearSelection(); + blockSignals(b); + if (selMode != NoSelection) { + currentSel = new Q3TableSelection(); + selections.append(currentSel); + if (!isRowSelection(selectionMode())) { + currentSel->init(tmpRow, tmpCol); + currentSel->expandTo(tmpRow, tmpCol); + } else { + currentSel->init(tmpRow, 0); + currentSel->expandTo(tmpRow, numCols() - 1); + repaintSelections(0, currentSel); + } + emit selectionChanged(); + } + } + } + + emit pressed(tmpRow, tmpCol, e->button(), e->pos()); +} + +/*! \reimp +*/ + +void Q3Table::contentsMouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() != LeftButton) + return; + if (!isRowSelection(selectionMode())) + clearSelection(); + int tmpRow = rowAt(e->pos().y()); + int tmpCol = columnAt(e->pos().x()); + Q3TableItem *itm = item(tmpRow, tmpCol); + if (itm && !itm->isEnabled()) + return; + if (tmpRow != -1 && tmpCol != -1) { + if (beginEdit(tmpRow, tmpCol, false)) + setEditMode(Editing, tmpRow, tmpCol); + } + + emit doubleClicked(tmpRow, tmpCol, e->button(), e->pos()); +} + +/*! + Sets the current edit mode to \a mode, the current edit row to \a + row and the current edit column to \a col. + + \sa EditMode +*/ + +void Q3Table::setEditMode(EditMode mode, int row, int col) +{ + edMode = mode; + editRow = row; + editCol = col; +} + + +/*! \reimp +*/ + +void Q3Table::contentsMouseMoveEvent(QMouseEvent *e) +{ + if ((e->state() & MouseButtonMask) == NoButton) + return; + int tmpRow = rowAt(e->pos().y()); + int tmpCol = columnAt(e->pos().x()); + fixRow(tmpRow, e->pos().y()); + fixCol(tmpCol, e->pos().x()); + +#ifndef QT_NO_DRAGANDDROP + if (dragEnabled() && startDragRow != -1 && startDragCol != -1) { + if (QPoint(dragStartPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) + startDrag(); + return; + } +#endif + if (selectionMode() == MultiRow && (e->state() & ControlButton) == ControlButton) + shouldClearSelection = false; + + if (shouldClearSelection) { + clearSelection(); + if (selMode != NoSelection) { + currentSel = new Q3TableSelection(); + selections.append(currentSel); + if (!isRowSelection(selectionMode())) + currentSel->init(tmpRow, tmpCol); + else + currentSel->init(tmpRow, 0); + emit selectionChanged(); + } + shouldClearSelection = false; + } + + QPoint pos = mapFromGlobal(e->globalPos()); + pos -= QPoint(leftHeader->width(), topHeader->height()); + autoScrollTimer->stop(); + doAutoScroll(); + if (pos.x() < 0 || pos.x() > visibleWidth() || pos.y() < 0 || pos.y() > visibleHeight()) + autoScrollTimer->start(100, true); +} + +/*! \internal + */ + +void Q3Table::doValueChanged() +{ + emit valueChanged(editRow, editCol); +} + +/*! \internal +*/ + +void Q3Table::doAutoScroll() +{ + QPoint pos = QCursor::pos(); + pos = mapFromGlobal(pos); + pos -= QPoint(leftHeader->width(), topHeader->height()); + + int tmpRow = curRow; + int tmpCol = curCol; + if (pos.y() < 0) + tmpRow--; + else if (pos.y() > visibleHeight()) + tmpRow++; + if (pos.x() < 0) + tmpCol--; + else if (pos.x() > visibleWidth()) + tmpCol++; + + pos += QPoint(contentsX(), contentsY()); + if (tmpRow == curRow) + tmpRow = rowAt(pos.y()); + if (tmpCol == curCol) + tmpCol = columnAt(pos.x()); + pos -= QPoint(contentsX(), contentsY()); + + fixRow(tmpRow, pos.y()); + fixCol(tmpCol, pos.x()); + + if (tmpRow < 0 || tmpRow > numRows() - 1) + tmpRow = currentRow(); + if (tmpCol < 0 || tmpCol > numCols() - 1) + tmpCol = currentColumn(); + + ensureCellVisible(tmpRow, tmpCol); + + if (currentSel && selMode != NoSelection) { + Q3TableSelection oldSelection = *currentSel; + bool useOld = true; + if (selMode != SingleRow) { + if (!isRowSelection(selectionMode())) { + currentSel->expandTo(tmpRow, tmpCol); + } else { + currentSel->expandTo(tmpRow, numCols() - 1); + } + } else { + bool currentInSelection = tmpRow == curRow && isSelected(tmpRow, tmpCol); + if (!currentInSelection) { + useOld = false; + clearSelection(); + currentSel = new Q3TableSelection(); + selections.append(currentSel); + currentSel->init(tmpRow, 0); + currentSel->expandTo(tmpRow, numCols() - 1); + repaintSelections(0, currentSel); + } else { + currentSel->expandTo(tmpRow, numCols() - 1); + } + } + setCurrentCell(tmpRow, tmpCol, false, true); + repaintSelections(useOld ? &oldSelection : 0, currentSel); + if (currentSel && oldSelection != *currentSel) + emit selectionChanged(); + } else { + setCurrentCell(tmpRow, tmpCol, false, true); + } + + if (pos.x() < 0 || pos.x() > visibleWidth() || pos.y() < 0 || pos.y() > visibleHeight()) + autoScrollTimer->start(100, true); +} + +/*! \reimp +*/ + +void Q3Table::contentsMouseReleaseEvent(QMouseEvent *e) +{ + if (pressedRow == curRow && pressedCol == curCol) + emit clicked(curRow, curCol, e->button(), e->pos()); + + if (e->button() != LeftButton) + return; + if (shouldClearSelection) { + int tmpRow = rowAt(e->pos().y()); + int tmpCol = columnAt(e->pos().x()); + fixRow(tmpRow, e->pos().y()); + fixCol(tmpCol, e->pos().x()); + clearSelection(); + if (selMode != NoSelection) { + currentSel = new Q3TableSelection(); + selections.append(currentSel); + if (!isRowSelection(selectionMode())) { + currentSel->init(tmpRow, tmpCol); + } else { + currentSel->init(tmpRow, 0); + currentSel->expandTo(tmpRow, numCols() - 1); + repaintSelections(0, currentSel); + } + emit selectionChanged(); + } + shouldClearSelection = false; + } + autoScrollTimer->stop(); + + if (d->redirectMouseEvent && pressedRow == curRow && pressedCol == curCol && + item(pressedRow, pressedCol) && item(pressedRow, pressedCol)->editType() == + Q3TableItem::WhenCurrent) { + QWidget *w = cellWidget(pressedRow, pressedCol); + if (w) { + QMouseEvent ev(e->type(), w->mapFromGlobal(e->globalPos()), + e->globalPos(), e->button(), e->state()); + QApplication::sendPostedEvents(w, 0); + bool old = w->testAttribute(Qt::WA_NoMousePropagation); + w->setAttribute(Qt::WA_NoMousePropagation, true); + QApplication::sendEvent(w, &ev); + w->setAttribute(Qt::WA_NoMousePropagation, old); + } + } +} + +/*! + \reimp +*/ + +void Q3Table::contentsContextMenuEvent(QContextMenuEvent *e) +{ + if (!receivers(SIGNAL(contextMenuRequested(int,int,QPoint)))) { + e->ignore(); + return; + } + if (e->reason() == QContextMenuEvent::Keyboard) { + QRect r = cellGeometry(curRow, curCol); + emit contextMenuRequested(curRow, curCol, viewport()->mapToGlobal(contentsToViewport(r.center()))); + } else { + int tmpRow = rowAt(e->pos().y()); + int tmpCol = columnAt(e->pos().x()); + emit contextMenuRequested(tmpRow, tmpCol, e->globalPos()); + } +} + + +/*! \reimp +*/ + +bool Q3Table::eventFilter(QObject *o, QEvent *e) +{ + switch (e->type()) { + case QEvent::KeyPress: { + Q3TableItem *itm = item(curRow, curCol); + QWidget *editorWidget = cellWidget(editRow, editCol); + + if (isEditing() && editorWidget && o == editorWidget) { + itm = item(editRow, editCol); + QKeyEvent *ke = (QKeyEvent*)e; + if (ke->key() == Key_Escape) { + if (!itm || itm->editType() == Q3TableItem::OnTyping) + endEdit(editRow, editCol, false, edMode != Editing); + return true; + } + + if ((ke->state() == NoButton || ke->state() == Keypad) + && (ke->key() == Key_Return || ke->key() == Key_Enter)) { + if (!itm || itm->editType() == Q3TableItem::OnTyping) + endEdit(editRow, editCol, true, edMode != Editing); + activateNextCell(); + return true; + } + + if (ke->key() == Key_Tab || ke->key() == Key_BackTab) { + if (ke->state() & Qt::ControlButton) + return false; + if (!itm || itm->editType() == Q3TableItem::OnTyping) + endEdit(editRow, editCol, true, edMode != Editing); + if ((ke->key() == Key_Tab) && !(ke->state() & ShiftButton)) { + if (currentColumn() >= numCols() - 1) + return true; + int cc = QMIN(numCols() - 1, currentColumn() + 1); + while (cc < numCols()) { + Q3TableItem *i = item(currentRow(), cc); + if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled())) + break; + ++cc; + } + setCurrentCell(currentRow(), cc); + } else { // Key_BackTab + if (currentColumn() == 0) + return true; + int cc = QMAX(0, currentColumn() - 1); + while (cc >= 0) { + Q3TableItem *i = item(currentRow(), cc); + if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled())) + break; + --cc; + } + setCurrentCell(currentRow(), cc); + } + itm = item(curRow, curCol); + if (beginEdit(curRow, curCol, false)) + setEditMode(Editing, curRow, curCol); + return true; + } + + if ((edMode == Replacing || + (itm && itm->editType() == Q3TableItem::WhenCurrent)) && + (ke->key() == Key_Up || ke->key() == Key_Prior || + ke->key() == Key_Home || ke->key() == Key_Down || + ke->key() == Key_Next || ke->key() == Key_End || + ke->key() == Key_Left || ke->key() == Key_Right)) { + if (!itm || itm->editType() == Q3TableItem::OnTyping) { + endEdit(editRow, editCol, true, edMode != Editing); + } + keyPressEvent(ke); + return true; + } + } else { + QObjectList l = viewport()->queryList("QWidget"); + if (l.contains(o)) { + QKeyEvent *ke = (QKeyEvent*)e; + if ((ke->state() & ControlButton) == ControlButton || + (ke->key() != Key_Left && ke->key() != Key_Right && + ke->key() != Key_Up && ke->key() != Key_Down && + ke->key() != Key_Prior && ke->key() != Key_Next && + ke->key() != Key_Home && ke->key() != Key_End)) + return false; + keyPressEvent((QKeyEvent*)e); + return true; + } + } + + } break; + case QEvent::FocusOut: { + QWidget *editorWidget = cellWidget(editRow, editCol); + if (isEditing() && editorWidget && o == editorWidget && ((QFocusEvent*)e)->reason() != Qt::PopupFocusReason) { + // if the editor is the parent of the new focus widget, do nothing + QWidget *w = QApplication::focusWidget(); + while (w) { + w = w->parentWidget(); + if (w == editorWidget) + break; + } + if (w) + break; + // otherwise, end editing + Q3TableItem *itm = item(editRow, editCol); + if (!itm || itm->editType() == Q3TableItem::OnTyping) { + endEdit(editRow, editCol, true, edMode != Editing); + return true; + } + } + break; + } +#ifndef QT_NO_WHEELEVENT + case QEvent::Wheel: + if (o == this || o == viewport()) { + QWheelEvent* we = (QWheelEvent*)e; + scrollBy(0, -we->delta()); + we->accept(); + return true; + } +#endif + default: + break; + } + + return Q3ScrollView::eventFilter(o, e); +} + +void Q3Table::fixCell(int &row, int &col, int key) +{ + if (rowHeight(row) > 0 && columnWidth(col) > 0) + return; + if (rowHeight(row) <= 0) { + if (key == Key_Down || + key == Key_Next || + key == Key_End) { + while (row < numRows() && rowHeight(row) <= 0) + row++; + if (rowHeight(row) <= 0) + row = curRow; + } else if (key == Key_Up || + key == Key_Prior || + key == Key_Home) + while (row >= 0 && rowHeight(row) <= 0) + row--; + if (rowHeight(row) <= 0) + row = curRow; + } else if (columnWidth(col) <= 0) { + if (key == Key_Left) { + while (col >= 0 && columnWidth(col) <= 0) + col--; + if (columnWidth(col) <= 0) + col = curCol; + } else if (key == Key_Right) { + while (col < numCols() && columnWidth(col) <= 0) + col++; + if (columnWidth(col) <= 0) + col = curCol; + } + } +} + +/*! \reimp +*/ + +void Q3Table::keyPressEvent(QKeyEvent* e) +{ + if (isEditing() && item(editRow, editCol) && + item(editRow, editCol)->editType() == Q3TableItem::OnTyping) + return; + + int tmpRow = curRow; + int tmpCol = curCol; + int oldRow = tmpRow; + int oldCol = tmpCol; + + bool navigationKey = false; + int r; + switch (e->key()) { + case Key_Left: + tmpCol = QMAX(0, tmpCol - 1); + navigationKey = true; + break; + case Key_Right: + tmpCol = QMIN(numCols() - 1, tmpCol + 1); + navigationKey = true; + break; + case Key_Up: + tmpRow = QMAX(0, tmpRow - 1); + navigationKey = true; + break; + case Key_Down: + tmpRow = QMIN(numRows() - 1, tmpRow + 1); + navigationKey = true; + break; + case Key_Prior: + r = QMAX(0, rowAt(rowPos(tmpRow) - visibleHeight())); + if (r < tmpRow || tmpRow < 0) + tmpRow = r; + navigationKey = true; + break; + case Key_Next: + r = QMIN(numRows() - 1, rowAt(rowPos(tmpRow) + visibleHeight())); + if (r > tmpRow) + tmpRow = r; + else + tmpRow = numRows() - 1; + navigationKey = true; + break; + case Key_Home: + tmpRow = 0; + navigationKey = true; + break; + case Key_End: + tmpRow = numRows() - 1; + navigationKey = true; + break; + case Key_F2: + if (beginEdit(tmpRow, tmpCol, false)) + setEditMode(Editing, tmpRow, tmpCol); + break; + case Key_Enter: case Key_Return: + activateNextCell(); + return; + case Key_Tab: case Key_BackTab: + if ((e->key() == Key_Tab) && !(e->state() & ShiftButton)) { + if (currentColumn() >= numCols() - 1) + return; + int cc = QMIN(numCols() - 1, currentColumn() + 1); + while (cc < numCols()) { + Q3TableItem *i = item(currentRow(), cc); + if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled())) + break; + ++cc; + } + setCurrentCell(currentRow(), cc); + } else { // Key_BackTab + if (currentColumn() == 0) + return; + int cc = QMAX(0, currentColumn() - 1); + while (cc >= 0) { + Q3TableItem *i = item(currentRow(), cc); + if (!d->hiddenCols.find(cc) && !isColumnReadOnly(cc) && (!i || i->isEnabled())) + break; + --cc; + } + setCurrentCell(currentRow(), cc); + } + return; + case Key_Escape: + e->ignore(); + return; + default: // ... or start in-place editing + if (e->text()[ 0 ].isPrint()) { + Q3TableItem *itm = item(tmpRow, tmpCol); + if (!itm || itm->editType() == Q3TableItem::OnTyping) { + QWidget *w = beginEdit(tmpRow, tmpCol, + itm ? itm->isReplaceable() : true); + if (w) { + setEditMode((!itm || (itm && itm->isReplaceable()) + ? Replacing : Editing), tmpRow, tmpCol); + QApplication::sendEvent(w, e); + return; + } + } + } + e->ignore(); + return; + } + + if (navigationKey) { + fixCell(tmpRow, tmpCol, e->key()); + if ((e->state() & ShiftButton) == ShiftButton && + selMode != NoSelection && selMode != SingleRow) { + bool justCreated = false; + setCurrentCell(tmpRow, tmpCol, false, true); + if (!currentSel) { + justCreated = true; + currentSel = new Q3TableSelection(); + selections.append(currentSel); + if (!isRowSelection(selectionMode())) + currentSel->init(oldRow, oldCol); + else + currentSel->init(oldRow < 0 ? 0 : oldRow, 0); + } + Q3TableSelection oldSelection = *currentSel; + if (!isRowSelection(selectionMode())) + currentSel->expandTo(tmpRow, tmpCol); + else + currentSel->expandTo(tmpRow, numCols() - 1); + repaintSelections(justCreated ? 0 : &oldSelection, currentSel); + emit selectionChanged(); + } else { + setCurrentCell(tmpRow, tmpCol, false, true); + if (!isRowSelection(selectionMode())) { + clearSelection(); + } else { + bool currentInSelection = tmpRow == oldRow && isSelected(tmpRow, tmpCol, false); + if (!currentInSelection) { + bool hasOldSel = false; + Q3TableSelection oldSelection; + if (selectionMode() == MultiRow) { + bool b = signalsBlocked(); + blockSignals(true); + clearSelection(); + blockSignals(b); + } else { + if (currentSel) { + oldSelection = *currentSel; + hasOldSel = true; + selections.removeRef(currentSel); + leftHeader->setSectionState(oldSelection.topRow(), Q3TableHeader::Normal); + } + } + currentSel = new Q3TableSelection(); + selections.append(currentSel); + currentSel->init(tmpRow, 0); + currentSel->expandTo(tmpRow, numCols() - 1); + repaintSelections(hasOldSel ? &oldSelection : 0, currentSel, !hasOldSel); + emit selectionChanged(); + } + } + } + } else { + setCurrentCell(tmpRow, tmpCol, false, true); + } +} + +/*! \reimp +*/ + +void Q3Table::focusInEvent(QFocusEvent*) +{ + d->inMenuMode = false; + QWidget *editorWidget = cellWidget(editRow, editCol); + updateCell(curRow, curCol); + if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) + repaintSelections(); + if (isEditing() && editorWidget) + editorWidget->setFocus(); + +} + + +/*! \reimp +*/ + +void Q3Table::focusOutEvent(QFocusEvent *e) +{ + updateCell(curRow, curCol); + if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) { + d->inMenuMode = + e->reason() == Qt::PopupFocusReason || + (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar")); + if (!d->inMenuMode) + repaintSelections(); + } +} + +/*! \reimp +*/ + +QSize Q3Table::sizeHint() const +{ + if (cachedSizeHint().isValid()) + return cachedSizeHint(); + + constPolish(); + + QSize s = tableSize(); + QSize sh; + if (s.width() < 500 && s.height() < 500) { + sh = QSize(tableSize().width() + VERTICALMARGIN + 5, + tableSize().height() + topMargin() + 5); + } else { + sh = Q3ScrollView::sizeHint(); + if (!topHeader->isHidden()) + sh.setHeight(sh.height() + topHeader->height()); + if (!leftHeader->isHidden()) + sh.setWidth(sh.width() + leftHeader->width()); + } + setCachedSizeHint(sh); + return sh; +} + +/*! \reimp +*/ + +void Q3Table::viewportResizeEvent(QResizeEvent *e) +{ + Q3ScrollView::viewportResizeEvent(e); + updateGeometries(); +} + +/*! \reimp +*/ + +void Q3Table::showEvent(QShowEvent *e) +{ + Q3ScrollView::showEvent(e); + QRect r(cellGeometry(numRows() - 1, numCols() - 1)); + resizeContents(r.right() + 1, r.bottom() + 1); + updateGeometries(); +} + +/*! \reimp +*/ + +void Q3Table::paintEvent(QPaintEvent *e) +{ + QRect topLeftCorner = QStyle::visualRect(layoutDirection(), rect(), QRect(frameWidth(), frameWidth(), VERTICALMARGIN, topMargin())); + erase(topLeftCorner); // erase instead of widget on top + Q3ScrollView::paintEvent(e); + +#ifdef Q_OS_WINCE + QPainter p(this); + p.drawLine(topLeftCorner.bottomLeft(), topLeftCorner.bottomRight()); + p.drawLine(topLeftCorner.bottomRight(), topLeftCorner.topRight()); +#endif +} + +static bool inUpdateCell = false; + +/*! + Repaints the cell at \a row, \a col. +*/ + +void Q3Table::updateCell(int row, int col) +{ + if (inUpdateCell || row < 0 || col < 0) + return; + inUpdateCell = true; + QRect cg = cellGeometry(row, col); + QRect r(contentsToViewport(QPoint(cg.x() - 2, cg.y() - 2)), + QSize(cg.width() + 4, cg.height() + 4)); + viewport()->update(r); + inUpdateCell = false; +} + +void Q3Table::repaintCell(int row, int col) +{ + if (row == -1 || col == -1) + return; + QRect cg = cellGeometry(row, col); + QRect r(QPoint(cg.x() - 2, cg.y() - 2), + QSize(cg.width() + 4, cg.height() + 4)); + repaintContents(r, false); +} + +void Q3Table::contentsToViewport2(int x, int y, int& vx, int& vy) +{ + const QPoint v = contentsToViewport2(QPoint(x, y)); + vx = v.x(); + vy = v.y(); +} + +QPoint Q3Table::contentsToViewport2(const QPoint &p) +{ + return QPoint(p.x() - contentsX(), + p.y() - contentsY()); +} + +QPoint Q3Table::viewportToContents2(const QPoint& vp) +{ + return QPoint(vp.x() + contentsX(), + vp.y() + contentsY()); +} + +void Q3Table::viewportToContents2(int vx, int vy, int& x, int& y) +{ + const QPoint c = viewportToContents2(QPoint(vx, vy)); + x = c.x(); + y = c.y(); +} + +/*! + This function should be called whenever the column width of \a col + has been changed. It updates the geometry of any affected columns + and repaints the table to reflect the changes it has made. +*/ + +void Q3Table::columnWidthChanged(int col) +{ + int p = columnPos(col); + if (d->hasColSpan) + p = contentsX(); + updateContents(p, contentsY(), contentsWidth(), visibleHeight()); + QSize s(tableSize()); + int w = contentsWidth(); + resizeContents(s.width(), s.height()); + if (contentsWidth() < w) + repaintContents(s.width(), contentsY(), + w - s.width() + 1, visibleHeight(), true); + else + repaintContents(w, contentsY(), + s.width() - w + 1, visibleHeight(), false); + + // update widgets that are affected by this change + if (widgets.size()) { + int last = isHidden() ? numCols() - 1 : d->lastVisCol; + for (int c = col; c <= last; ++c) + updateColWidgets(c); + } + delayedUpdateGeometries(); +} + +/*! + This function should be called whenever the row height of \a row + has been changed. It updates the geometry of any affected rows and + repaints the table to reflect the changes it has made. +*/ + +void Q3Table::rowHeightChanged(int row) +{ + int p = rowPos(row); + if (d->hasRowSpan) + p = contentsY(); + updateContents(contentsX(), p, visibleWidth(), contentsHeight()); + QSize s(tableSize()); + int h = contentsHeight(); + resizeContents(s.width(), s.height()); + if (contentsHeight() < h) { + repaintContents(contentsX(), contentsHeight(), + visibleWidth(), h - s.height() + 1, true); + } else { + repaintContents(contentsX(), h, + visibleWidth(), s.height() - h + 1, false); + } + + // update widgets that are affected by this change + if (widgets.size()) { + d->lastVisRow = rowAt(contentsY() + visibleHeight() + (s.height() - h + 1)); + int last = isHidden() ? numRows() - 1 : d->lastVisRow; + for (int r = row; r <= last; ++r) + updateRowWidgets(r); + } + delayedUpdateGeometries(); +} + +/*! \internal */ + +void Q3Table::updateRowWidgets(int row) +{ + for (int i = 0; i < numCols(); ++i) { + QWidget *w = cellWidget(row, i); + if (!w) + continue; + moveChild(w, columnPos(i), rowPos(row)); + w->resize(columnWidth(i) - 1, rowHeight(row) - 1); + } +} + +/*! \internal */ + +void Q3Table::updateColWidgets(int col) +{ + for (int i = 0; i < numRows(); ++i) { + QWidget *w = cellWidget(i, col); + if (!w) + continue; + moveChild(w, columnPos(col), rowPos(i)); + w->resize(columnWidth(col) - 1, rowHeight(i) - 1); + } +} + +/*! + This function is called when column order is to be changed, i.e. + when the user moved the column header \a section from \a fromIndex + to \a toIndex. + + If you want to change the column order programmatically, call + swapRows() or swapColumns(); + + \sa Q3Header::indexChange() rowIndexChanged() +*/ + +void Q3Table::columnIndexChanged(int, int fromIndex, int toIndex) +{ + if (doSort && lastSortCol == fromIndex && topHeader) + topHeader->setSortIndicator(toIndex, topHeader->sortIndicatorOrder()); + repaintContents(contentsX(), contentsY(), + visibleWidth(), visibleHeight(), false); +} + +/*! + This function is called when the order of the rows is to be + changed, i.e. the user moved the row header section \a section + from \a fromIndex to \a toIndex. + + If you want to change the order programmatically, call swapRows() + or swapColumns(); + + \sa Q3Header::indexChange() columnIndexChanged() +*/ + +void Q3Table::rowIndexChanged(int, int, int) +{ + repaintContents(contentsX(), contentsY(), + visibleWidth(), visibleHeight(), false); +} + +/*! + This function is called when the column \a col has been clicked. + The default implementation sorts this column if sorting() is true. +*/ + +void Q3Table::columnClicked(int col) +{ + if (!sorting()) + return; + + if (col == lastSortCol) { + asc = !asc; + } else { + lastSortCol = col; + asc = true; + } + sortColumn(lastSortCol, asc); +} + +/*! + \property Q3Table::sorting + \brief whether a click on the header of a column sorts that column + + \sa sortColumn() +*/ + +void Q3Table::setSorting(bool b) +{ + doSort = b; + if (topHeader) + topHeader->setSortIndicator(b ? lastSortCol : -1); +} + +bool Q3Table::sorting() const +{ + return doSort; +} + +static bool inUpdateGeometries = false; + +void Q3Table::delayedUpdateGeometries() +{ + d->geomTimer->start(0, true); +} + +void Q3Table::updateGeometriesSlot() +{ + updateGeometries(); +} + +/*! + This function updates the geometries of the left and top header. + You do not normally need to call this function. +*/ + +void Q3Table::updateGeometries() +{ + if (inUpdateGeometries) + return; + inUpdateGeometries = true; + QSize ts = tableSize(); + if (topHeader->offset() && + ts.width() < topHeader->offset() + topHeader->width()) + horizontalScrollBar()->setValue(ts.width() - topHeader->width()); + if (leftHeader->offset() && + ts.height() < leftHeader->offset() + leftHeader->height()) + verticalScrollBar()->setValue(ts.height() - leftHeader->height()); + + leftHeader->setGeometry(QStyle::visualRect(layoutDirection(), rect(), QRect(frameWidth(), topMargin() + frameWidth(), + VERTICALMARGIN, visibleHeight()))); + topHeader->setGeometry(QStyle::visualRect(layoutDirection(), rect(), QRect(VERTICALMARGIN + frameWidth(), frameWidth(), + visibleWidth(), topMargin()))); + horizontalScrollBar()->raise(); + verticalScrollBar()->raise(); + topHeader->updateStretches(); + leftHeader->updateStretches(); + inUpdateGeometries = false; +} + +/*! + Returns the width of column \a col. + + \sa setColumnWidth() rowHeight() +*/ + +int Q3Table::columnWidth(int col) const +{ + return topHeader->sectionSize(col); +} + +/*! + Returns the height of row \a row. + + \sa setRowHeight() columnWidth() +*/ + +int Q3Table::rowHeight(int row) const +{ + return leftHeader->sectionSize(row); +} + +/*! + Returns the x-coordinate of the column \a col in content + coordinates. + + \sa columnAt() rowPos() +*/ + +int Q3Table::columnPos(int col) const +{ + return topHeader->sectionPos(col); +} + +/*! + Returns the y-coordinate of the row \a row in content coordinates. + + \sa rowAt() columnPos() +*/ + +int Q3Table::rowPos(int row) const +{ + return leftHeader->sectionPos(row); +} + +/*! + Returns the number of the column at position \a x. \a x must be + given in content coordinates. + + \sa columnPos() rowAt() +*/ + +int Q3Table::columnAt(int x) const +{ + return topHeader->sectionAt(x); +} + +/*! + Returns the number of the row at position \a y. \a y must be given + in content coordinates. + + \sa rowPos() columnAt() +*/ + +int Q3Table::rowAt(int y) const +{ + return leftHeader->sectionAt(y); +} + +/*! + Returns the bounding rectangle of the cell at \a row, \a col in + content coordinates. +*/ + +QRect Q3Table::cellGeometry(int row, int col) const +{ + Q3TableItem *itm = item(row, col); + + if (!itm || (itm->rowSpan() == 1 && itm->colSpan() == 1)) + return QRect(columnPos(col), rowPos(row), + columnWidth(col), rowHeight(row)); + + while (row != itm->row()) + row--; + while (col != itm->col()) + col--; + + QRect rect(columnPos(col), rowPos(row), + columnWidth(col), rowHeight(row)); + + for (int r = 1; r < itm->rowSpan(); ++r) + rect.setHeight(rect.height() + rowHeight(r + row)); + + for (int c = 1; c < itm->colSpan(); ++c) + rect.setWidth(rect.width() + columnWidth(c + col)); + + return rect; +} + +/*! + Returns the size of the table. + + This is the same as the coordinates of the bottom-right edge of + the last table cell. +*/ + +QSize Q3Table::tableSize() const +{ + return QSize(columnPos(numCols() - 1) + columnWidth(numCols() - 1), + rowPos(numRows() - 1) + rowHeight(numRows() - 1)); +} + +/*! + \property Q3Table::numRows + \brief The number of rows in the table + + \sa numCols +*/ + +int Q3Table::numRows() const +{ + return leftHeader->count(); +} + +/*! + \property Q3Table::numCols + \brief The number of columns in the table + + \sa numRows +*/ + +int Q3Table::numCols() const +{ + return topHeader->count(); +} + +void Q3Table::saveContents(Q3PtrVector<Q3TableItem> &tmp, + Q3PtrVector<Q3Table::TableWidget> &tmp2) +{ + int nCols = numCols(); + if (editRow != -1 && editCol != -1) + endEdit(editRow, editCol, false, edMode != Editing); + tmp.resize(contents.size()); + tmp2.resize(widgets.size()); + int i; + for (i = 0; i < (int)tmp.size(); ++i) { + Q3TableItem *item = contents[ i ]; + if (item && (item->row() * nCols) + item->col() == i) + tmp.insert(i, item); + else + tmp.insert(i, 0); + } + for (i = 0; i < (int)tmp2.size(); ++i) { + QWidget *w = widgets[ i ]; + if (w) + tmp2.insert(i, new TableWidget(w, i / nCols, i % nCols)); + else + tmp2.insert(i, 0); + } +} + +void Q3Table::updateHeaderAndResizeContents(Q3TableHeader *header, + int num, int rowCol, + int width, bool &updateBefore) +{ + updateBefore = rowCol < num; + if (rowCol > num) { + header->Q3Header::resizeArrays(rowCol); + header->Q3TableHeader::resizeArrays(rowCol); + int old = num; + clearSelection(false); + int i = 0; + for (i = old; i < rowCol; ++i) + header->addLabel(QString(), width); + } else { + clearSelection(false); + if (header == leftHeader) { + while (numRows() > rowCol) + header->removeLabel(numRows() - 1); + } else { + while (numCols() > rowCol) + header->removeLabel(numCols() - 1); + } + } + + contents.setAutoDelete(false); + contents.clear(); + contents.setAutoDelete(true); + widgets.setAutoDelete(false); + widgets.clear(); + widgets.setAutoDelete(true); + resizeData(numRows() * numCols()); + + // keep numStretches in sync + int n = 0; + for (uint i = 0; i < header->stretchable.size(); i++) + n += (header->stretchable.at(i) & 1); // avoid cmp + header->numStretches = n; +} + +void Q3Table::restoreContents(Q3PtrVector<Q3TableItem> &tmp, + Q3PtrVector<Q3Table::TableWidget> &tmp2) +{ + int i; + int nCols = numCols(); + for (i = 0; i < (int)tmp.size(); ++i) { + Q3TableItem *it = tmp[ i ]; + if (it) { + int idx = (it->row() * nCols) + it->col(); + if ((uint)idx < contents.size() && + it->row() == idx / nCols && it->col() == idx % nCols) { + contents.insert(idx, it); + if (it->rowSpan() > 1 || it->colSpan() > 1) { + int ridx, iidx; + for (int irow = 0; irow < it->rowSpan(); irow++) { + ridx = idx + irow * nCols; + for (int icol = 0; icol < it->colSpan(); icol++) { + iidx = ridx + icol; + if (idx != iidx && (uint)iidx < contents.size()) + contents.insert(iidx, it); + } + } + + } + } else { + delete it; + } + } + } + for (i = 0; i < (int)tmp2.size(); ++i) { + TableWidget *w = tmp2[ i ]; + if (w) { + int idx = (w->row * nCols) + w->col; + if ((uint)idx < widgets.size() && + w->row == idx / nCols && w->col == idx % nCols) + widgets.insert(idx, w->wid); + else + delete w->wid; + delete w; + } + } +} + +void Q3Table::finishContentsResze(bool updateBefore) +{ + QRect r(cellGeometry(numRows() - 1, numCols() - 1)); + resizeContents(r.right() + 1, r.bottom() + 1); + updateGeometries(); + if (updateBefore) + repaintContents(contentsX(), contentsY(), + visibleWidth(), visibleHeight(), true); + else + repaintContents(contentsX(), contentsY(), + visibleWidth(), visibleHeight(), false); + + if (isRowSelection(selectionMode())) { + int r = curRow; + curRow = -1; + setCurrentCell(r, curCol); + } +} + +void Q3Table::setNumRows(int r) +{ + if (r < 0) + return; + + if (r < numRows()) { + // Removed rows are no longer hidden, and should thus be removed from "hiddenRows" + for (int rr = numRows()-1; rr >= r; --rr) { + if (d->hiddenRows.find(rr)) + d->hiddenRows.remove(rr); + } + } + + fontChange(font()); // invalidate the sizeHintCache + + Q3PtrVector<Q3TableItem> tmp; + Q3PtrVector<TableWidget> tmp2; + saveContents(tmp, tmp2); + + bool updatesEnabled = leftHeader->updatesEnabled(); + if (updatesEnabled) + leftHeader->setUpdatesEnabled(false); + + bool updateBefore; + updateHeaderAndResizeContents(leftHeader, numRows(), r, 20, updateBefore); + + int w = fontMetrics().width(QString::number(r) + QLatin1Char('W')); + if (VERTICALMARGIN > 0 && w > VERTICALMARGIN) + setLeftMargin(w); + + restoreContents(tmp, tmp2); + + leftHeader->calculatePositions(); + finishContentsResze(updateBefore); + if (updatesEnabled) { + leftHeader->setUpdatesEnabled(true); + leftHeader->update(); + } + leftHeader->updateCache(); + if (curRow >= numRows()) { + curRow = numRows() - 1; + if (curRow < 0) + curCol = -1; + else + repaintCell(curRow, curCol); + } + + if (curRow > numRows()) + curRow = numRows(); +} + +void Q3Table::setNumCols(int c) +{ + if (c < 0) + return; + + if (c < numCols()) { + // Removed columns are no longer hidden, and should thus be removed from "hiddenCols" + for (int cc = numCols()-1; cc >= c; --cc) { + if (d->hiddenCols.find(cc)) + d->hiddenCols.remove(cc); + } + } + + fontChange(font()); // invalidate the sizeHintCache + + Q3PtrVector<Q3TableItem> tmp; + Q3PtrVector<TableWidget> tmp2; + saveContents(tmp, tmp2); + + bool updatesEnabled = topHeader->updatesEnabled(); + if (updatesEnabled) + topHeader->setUpdatesEnabled(false); + + bool updateBefore; + updateHeaderAndResizeContents(topHeader, numCols(), c, 100, updateBefore); + + restoreContents(tmp, tmp2); + + topHeader->calculatePositions(); + finishContentsResze(updateBefore); + if (updatesEnabled) { + topHeader->setUpdatesEnabled(true); + topHeader->update(); + } + topHeader->updateCache(); + if (curCol >= numCols()) { + curCol = numCols() - 1; + if (curCol < 0) + curRow = -1; + else + repaintCell(curRow, curCol); + } +} + +/*! Sets the section labels of the verticalHeader() to \a labels */ + +void Q3Table::setRowLabels(const QStringList &labels) +{ + leftHeader->setLabels(labels); +} + +/*! Sets the section labels of the horizontalHeader() to \a labels */ + +void Q3Table::setColumnLabels(const QStringList &labels) +{ + topHeader->setLabels(labels); +} + +/*! + This function returns the widget which should be used as an editor + for the contents of the cell at \a row, \a col. + + If \a initFromCell is true, the editor is used to edit the current + contents of the cell (so the editor widget should be initialized + with this content). If \a initFromCell is false, the content of + the cell is replaced with the new content which the user entered + into the widget created by this function. + + The default functionality is as follows: if \a initFromCell is + true or the cell has a Q3TableItem and the table item's + Q3TableItem::isReplaceable() is false then the cell is asked to + create an appropriate editor (using Q3TableItem::createEditor()). + Otherwise a QLineEdit is used as the editor. + + If you want to create your own editor for certain cells, implement + a custom Q3TableItem subclass and reimplement + Q3TableItem::createEditor(). + + If you are not using \l{Q3TableItem}s and you don't want to use a + QLineEdit as the default editor, subclass Q3Table and reimplement + this function with code like this: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3table.cpp 5 + Ownership of the editor widget is transferred to the caller. + + If you reimplement this function return 0 for read-only cells. You + will need to reimplement setCellContentFromEditor() to retrieve + the data the user entered. + + \sa Q3TableItem::createEditor() +*/ + +QWidget *Q3Table::createEditor(int row, int col, bool initFromCell) const +{ + if (isReadOnly() || isRowReadOnly(row) || isColumnReadOnly(col)) + return 0; + + QWidget *e = 0; + + // the current item in the cell should be edited if possible + Q3TableItem *i = item(row, col); + if (initFromCell || (i && !i->isReplaceable())) { + if (i) { + if (i->editType() == Q3TableItem::Never) + return 0; + + e = i->createEditor(); + if (!e) + return 0; + } + } + + // no contents in the cell yet, so open the default editor + if (!e) { + e = new QLineEdit(viewport(), "qt_lineeditor"); + ((QLineEdit*)e)->setFrame(false); + } + + return e; +} + +/*! + This function is called to start in-place editing of the cell at + \a row, \a col. Editing is achieved by creating an editor + (createEditor() is called) and setting the cell's editor with + setCellWidget() to the newly created editor. (After editing is + complete endEdit() will be called to replace the cell's content + with the editor's content.) If \a replace is true the editor will + start empty; otherwise it will be initialized with the cell's + content (if any), i.e. the user will be modifying the original + cell content. + + \sa endEdit() +*/ + +QWidget *Q3Table::beginEdit(int row, int col, bool replace) +{ + if (isReadOnly() || isRowReadOnly(row) || isColumnReadOnly(col)) + return 0; + if ( row < 0 || row >= numRows() || col < 0 || col >= numCols() ) + return 0; + Q3TableItem *itm = item(row, col); + if (itm && !itm->isEnabled()) + return 0; + if (cellWidget(row, col)) + return 0; + ensureCellVisible(row, col); + QWidget *e = createEditor(row, col, !replace); + if (!e) + return 0; + setCellWidget(row, col, e); + e->setActiveWindow(); + e->setFocus(); + updateCell(row, col); + return e; +} + +/*! + This function is called when in-place editing of the cell at \a + row, \a col is requested to stop. + + If the cell is not being edited or \a accept is false the function + returns and the cell's contents are left unchanged. + + If \a accept is true the content of the editor must be transferred + to the relevant cell. If \a replace is true the current content of + this cell should be replaced by the content of the editor (this + means removing the current Q3TableItem of the cell and creating a + new one for the cell). Otherwise (if possible) the content of the + editor should just be set to the existing Q3TableItem of this cell. + + setCellContentFromEditor() is called to replace the contents of + the cell with the contents of the cell's editor. + + Finally clearCellWidget() is called to remove the editor widget. + + \sa setCellContentFromEditor(), beginEdit() +*/ + +void Q3Table::endEdit(int row, int col, bool accept, bool replace) +{ + QWidget *editor = cellWidget(row, col); + if (!editor) + return; + + if (!accept) { + if (row == editRow && col == editCol) + setEditMode(NotEditing, -1, -1); + clearCellWidget(row, col); + updateCell(row, col); + viewport()->setFocus(); + updateCell(row, col); + return; + } + + Q3TableItem *i = item(row, col); + QString oldContent; + if (i) + oldContent = i->text(); + + if (!i || replace) { + setCellContentFromEditor(row, col); + i = item(row, col); + } else { + i->setContentFromEditor(editor); + } + + if (row == editRow && col == editCol) + setEditMode(NotEditing, -1, -1); + + viewport()->setFocus(); + updateCell(row, col); + + if (!i || (oldContent != i->text())) + emit valueChanged(row, col); + + clearCellWidget(row, col); +} + +/*! + This function is called to replace the contents of the cell at \a + row, \a col with the contents of the cell's editor. + + If there already exists a Q3TableItem for the cell, + it calls Q3TableItem::setContentFromEditor() on this Q3TableItem. + + If, for example, you want to create different \l{Q3TableItem}s + depending on the contents of the editor, you might reimplement + this function. + + If you want to work without \l{Q3TableItem}s, you will need to + reimplement this function to save the data the user entered into + your data structure. (See the notes on large tables.) + + \sa Q3TableItem::setContentFromEditor() createEditor() +*/ + +void Q3Table::setCellContentFromEditor(int row, int col) +{ + QWidget *editor = cellWidget(row, col); + if (!editor) + return; + + Q3TableItem *i = item(row, col); + if (i) { + i->setContentFromEditor(editor); + } else { + QLineEdit *le = qobject_cast<QLineEdit*>(editor); + if (le) + setText(row, col, le->text()); + } +} + +/*! + Returns true if the \l EditMode is \c Editing or \c Replacing; + otherwise (i.e. the \l EditMode is \c NotEditing) returns false. + + \sa Q3Table::EditMode +*/ + +bool Q3Table::isEditing() const +{ + return edMode != NotEditing; +} + +/*! + Returns the current edit mode + + \sa Q3Table::EditMode +*/ + +Q3Table::EditMode Q3Table::editMode() const +{ + return edMode; +} + +/*! + Returns the current edited row +*/ + +int Q3Table::currEditRow() const +{ + return editRow; +} + +/*! + Returns the current edited column +*/ + +int Q3Table::currEditCol() const +{ + return editCol; +} + +/*! + Returns a single integer which identifies a particular \a row and \a + col by mapping the 2D table to a 1D array. + + This is useful, for example, if you have a sparse table and want to + use a Q3IntDict to map integers to the cells that are used. +*/ + +int Q3Table::indexOf(int row, int col) const +{ + return (row * numCols()) + col; +} + +/*! \internal +*/ + +void Q3Table::repaintSelections(Q3TableSelection *oldSelection, + Q3TableSelection *newSelection, + bool updateVertical, bool updateHorizontal) +{ + if (!oldSelection && !newSelection) + return; + if (oldSelection && newSelection && *oldSelection == *newSelection) + return; + if (oldSelection && !oldSelection->isActive()) + oldSelection = 0; + + bool optimizeOld = false; + bool optimizeNew = false; + + QRect old; + if (oldSelection) + old = rangeGeometry(oldSelection->topRow(), + oldSelection->leftCol(), + oldSelection->bottomRow(), + oldSelection->rightCol(), + optimizeOld); + else + old = QRect(0, 0, 0, 0); + + QRect cur; + if (newSelection) + cur = rangeGeometry(newSelection->topRow(), + newSelection->leftCol(), + newSelection->bottomRow(), + newSelection->rightCol(), + optimizeNew); + else + cur = QRect(0, 0, 0, 0); + int i; + + if (!optimizeOld || !optimizeNew || + old.width() > SHRT_MAX || old.height() > SHRT_MAX || + cur.width() > SHRT_MAX || cur.height() > SHRT_MAX) { + QRect rr = cur.united(old); + repaintContents(rr, false); + } else { + old = QRect(contentsToViewport2(old.topLeft()), old.size()); + cur = QRect(contentsToViewport2(cur.topLeft()), cur.size()); + QRegion r1(old); + QRegion r2(cur); + QRegion r3 = r1.subtracted(r2); + QRegion r4 = r2.subtracted(r1); + + for (i = 0; i < (int)r3.rects().count(); ++i) { + QRect r(r3.rects()[ i ]); + r = QRect(viewportToContents2(r.topLeft()), r.size()); + repaintContents(r, false); + } + for (i = 0; i < (int)r4.rects().count(); ++i) { + QRect r(r4.rects()[ i ]); + r = QRect(viewportToContents2(r.topLeft()), r.size()); + repaintContents(r, false); + } + } + + int top, left, bottom, right; + { + int oldTopRow = oldSelection ? oldSelection->topRow() : numRows() - 1; + int newTopRow = newSelection ? newSelection->topRow() : numRows() - 1; + top = QMIN(oldTopRow, newTopRow); + } + + { + int oldLeftCol = oldSelection ? oldSelection->leftCol() : numCols() - 1; + int newLeftCol = newSelection ? newSelection->leftCol() : numCols() - 1; + left = QMIN(oldLeftCol, newLeftCol); + } + + { + int oldBottomRow = oldSelection ? oldSelection->bottomRow() : 0; + int newBottomRow = newSelection ? newSelection->bottomRow() : 0; + bottom = QMAX(oldBottomRow, newBottomRow); + } + + { + int oldRightCol = oldSelection ? oldSelection->rightCol() : 0; + int newRightCol = newSelection ? newSelection->rightCol() : 0; + right = QMAX(oldRightCol, newRightCol); + } + + if (updateHorizontal && numCols() > 0 && left >= 0 && !isRowSelection(selectionMode())) { + register int *s = &topHeader->states.data()[left]; + for (i = left; i <= right; ++i) { + if (!isColumnSelected(i)) + *s = Q3TableHeader::Normal; + else if (isColumnSelected(i, true)) + *s = Q3TableHeader::Selected; + else + *s = Q3TableHeader::Bold; + ++s; + } + topHeader->repaint(false); + } + + if (updateVertical && numRows() > 0 && top >= 0) { + register int *s = &leftHeader->states.data()[top]; + for (i = top; i <= bottom; ++i) { + if (!isRowSelected(i)) + *s = Q3TableHeader::Normal; + else if (isRowSelected(i, true)) + *s = Q3TableHeader::Selected; + else + *s = Q3TableHeader::Bold; + ++s; + } + leftHeader->repaint(false); + } +} + +/*! + Repaints all selections +*/ + +void Q3Table::repaintSelections() +{ + if (selections.isEmpty()) + return; + + QRect r; + for (Q3TableSelection *s = selections.first(); s; s = selections.next()) { + bool b; + r = r.united(rangeGeometry(s->topRow(), + s->leftCol(), + s->bottomRow(), + s->rightCol(), b)); + } + + repaintContents(r, false); +} + +/*! + Clears all selections and repaints the appropriate regions if \a + repaint is true. + + \sa removeSelection() +*/ + +void Q3Table::clearSelection(bool repaint) +{ + if (selections.isEmpty()) + return; + bool needRepaint = !selections.isEmpty(); + + QRect r; + for (Q3TableSelection *s = selections.first(); s; s = selections.next()) { + bool b; + r = r.united(rangeGeometry(s->topRow(), + s->leftCol(), + s->bottomRow(), + s->rightCol(), b)); + } + + currentSel = 0; + selections.clear(); + + if (needRepaint && repaint) + repaintContents(r, false); + + leftHeader->setSectionStateToAll(Q3TableHeader::Normal); + leftHeader->repaint(false); + if (!isRowSelection(selectionMode())) { + topHeader->setSectionStateToAll(Q3TableHeader::Normal); + topHeader->repaint(false); + } + topHeader->setSectionState(curCol, Q3TableHeader::Bold); + leftHeader->setSectionState(curRow, Q3TableHeader::Bold); + emit selectionChanged(); +} + +/*! \internal +*/ + +QRect Q3Table::rangeGeometry(int topRow, int leftCol, + int bottomRow, int rightCol, bool &optimize) +{ + topRow = QMAX(topRow, rowAt(contentsY())); + leftCol = QMAX(leftCol, columnAt(contentsX())); + int ra = rowAt(contentsY() + visibleHeight()); + if (ra != -1) + bottomRow = QMIN(bottomRow, ra); + int ca = columnAt(contentsX() + visibleWidth()); + if (ca != -1) + rightCol = QMIN(rightCol, ca); + optimize = true; + QRect rect; + for (int r = topRow; r <= bottomRow; ++r) { + for (int c = leftCol; c <= rightCol; ++c) { + rect = rect.united(cellGeometry(r, c)); + Q3TableItem *i = item(r, c); + if (i && (i->rowSpan() > 1 || i->colSpan() > 1)) + optimize = false; + } + } + return rect; +} + +/*! + This function is called to activate the next cell if in-place + editing was finished by pressing the Enter key. + + The default behaviour is to move from top to bottom, i.e. move to + the cell beneath the cell being edited. Reimplement this function + if you want different behaviour, e.g. moving from left to right. +*/ + +void Q3Table::activateNextCell() +{ + int firstRow = 0; + while (d->hiddenRows.find(firstRow)) + firstRow++; + int firstCol = 0; + while (d->hiddenCols.find(firstCol)) + firstCol++; + int nextRow = curRow; + int nextCol = curCol; + while (d->hiddenRows.find(++nextRow)) {} + if (nextRow >= numRows()) { + nextRow = firstRow; + while (d->hiddenCols.find(++nextCol)) {} + if (nextCol >= numCols()) + nextCol = firstCol; + } + + if (!currentSel || !currentSel->isActive() || + (currentSel->leftCol() == currentSel->rightCol() && + currentSel->topRow() == currentSel->bottomRow())) { + clearSelection(); + setCurrentCell(nextRow, nextCol); + } else { + if (curRow < currentSel->bottomRow()) + setCurrentCell(nextRow, curCol); + else if (curCol < currentSel->rightCol()) + setCurrentCell(currentSel->topRow(), nextCol); + else + setCurrentCell(currentSel->topRow(), currentSel->leftCol()); + } + +} + +/*! \internal +*/ + +void Q3Table::fixRow(int &row, int y) +{ + if (row == -1) { + if (y < 0) + row = 0; + else + row = numRows() - 1; + } +} + +/*! \internal +*/ + +void Q3Table::fixCol(int &col, int x) +{ + if (col == -1) { + if (x < 0) + col = 0; + else + col = numCols() - 1; + } +} + +struct SortableTableItem +{ + Q3TableItem *item; +}; + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +#ifdef Q_OS_WINCE +static int _cdecl cmpTableItems(const void *n1, const void *n2) +#else +static int cmpTableItems(const void *n1, const void *n2) +#endif +{ + if (!n1 || !n2) + return 0; + + SortableTableItem *i1 = (SortableTableItem *)n1; + SortableTableItem *i2 = (SortableTableItem *)n2; + + return i1->item->key().localeAwareCompare(i2->item->key()); +} + +#if defined(Q_C_CALLBACKS) +} +#endif + +/*! + Sorts column \a col. If \a ascending is true the sort is in + ascending order, otherwise the sort is in descending order. + + If \a wholeRows is true, entire rows are sorted using swapRows(); + otherwise only cells in the column are sorted using swapCells(). + + Note that if you are not using Q3TableItems you will need to + reimplement swapRows() and swapCells(). (See the notes on large + tables.) + + \sa swapRows() +*/ + +void Q3Table::sortColumn(int col, bool ascending, bool wholeRows) +{ + int filledRows = 0, i; + for (i = 0; i < numRows(); ++i) { + Q3TableItem *itm = item(i, col); + if (itm) + filledRows++; + } + + if (!filledRows) + return; + + SortableTableItem *items = new SortableTableItem[ filledRows ]; + int j = 0; + for (i = 0; i < numRows(); ++i) { + Q3TableItem *itm = item(i, col); + if (!itm) + continue; + items[ j++ ].item = itm; + } + + qsort(items, filledRows, sizeof(SortableTableItem), cmpTableItems); + + bool updatesWereEnabled = updatesEnabled(); + if (updatesWereEnabled) + setUpdatesEnabled(false); + for (i = 0; i < numRows(); ++i) { + if (i < filledRows) { + if (ascending) { + if (items[ i ].item->row() == i) + continue; + if (wholeRows) + swapRows(items[ i ].item->row(), i); + else + swapCells(items[ i ].item->row(), col, i, col); + } else { + if (items[ i ].item->row() == filledRows - i - 1) + continue; + if (wholeRows) + swapRows(items[ i ].item->row(), filledRows - i - 1); + else + swapCells(items[ i ].item->row(), col, + filledRows - i - 1, col); + } + } + } + if (updatesWereEnabled) + setUpdatesEnabled(true); + if (topHeader) + topHeader->setSortIndicator(col, ascending ? Qt::Ascending : Qt::Descending); + + if (!wholeRows) + repaintContents(columnPos(col), contentsY(), + columnWidth(col), visibleHeight(), false); + else + repaintContents(contentsX(), contentsY(), + visibleWidth(), visibleHeight(), false); + + delete [] items; +} + +/*! + Hides row \a row. + + \sa showRow() hideColumn() +*/ + +void Q3Table::hideRow(int row) +{ + if (d->hiddenRows.find(row)) + return; + d->hiddenRows.replace(row, new int(leftHeader->sectionSize(row))); + leftHeader->resizeSection(row, 0); + leftHeader->setResizeEnabled(false, row); + if (isRowStretchable(row)) + leftHeader->numStretches--; + rowHeightChanged(row); + if (curRow == row) { + int r = curRow; + int c = curCol; + int k = (r >= numRows() - 1 ? Key_Up : Key_Down); + fixCell(r, c, k); + if (numRows() > 0) + setCurrentCell(r, c); + } +} + +/*! + Hides column \a col. + + \sa showColumn() hideRow() +*/ + +void Q3Table::hideColumn(int col) +{ + if (!numCols() || d->hiddenCols.find(col)) + return; + d->hiddenCols.replace(col, new int(topHeader->sectionSize(col))); + topHeader->resizeSection(col, 0); + topHeader->setResizeEnabled(false, col); + if (isColumnStretchable(col)) + topHeader->numStretches--; + columnWidthChanged(col); + if (curCol == col) { + int r = curRow; + int c = curCol; + int k = (c >= numCols() - 1 ? Key_Left : Key_Right); + fixCell(r, c, k); + if (numCols() > 0) + setCurrentCell(r, c); + } +} + +/*! + Shows row \a row. + + \sa hideRow() showColumn() +*/ + +void Q3Table::showRow(int row) +{ + int *h = d->hiddenRows.find(row); + if (h) { + int rh = *h; + d->hiddenRows.remove(row); + setRowHeight(row, rh); + if (isRowStretchable(row)) + leftHeader->numStretches++; + } else if (rowHeight(row) == 0) { + setRowHeight(row, 20); + } + leftHeader->setResizeEnabled(true, row); +} + +/*! + Shows column \a col. + + \sa hideColumn() showRow() +*/ + +void Q3Table::showColumn(int col) +{ + int *w = d->hiddenCols.find(col); + if (w) { + int cw = *w; + d->hiddenCols.remove(col); + setColumnWidth(col, cw); + if (isColumnStretchable(col)) + topHeader->numStretches++; + } else if (columnWidth(col) == 0) { + setColumnWidth(col, 20); + } + topHeader->setResizeEnabled(true, col); +} + +/*! + Returns true if row \a row is hidden; otherwise returns + false. + + \sa hideRow(), isColumnHidden() +*/ +bool Q3Table::isRowHidden(int row) const +{ + return d->hiddenRows.find(row); +} + +/*! + Returns true if column \a col is hidden; otherwise returns + false. + + \sa hideColumn(), isRowHidden() +*/ +bool Q3Table::isColumnHidden(int col) const +{ + return d->hiddenCols.find(col); +} + +/*! + Resizes column \a col to be \a w pixels wide. + + \sa columnWidth() setRowHeight() +*/ + +void Q3Table::setColumnWidth(int col, int w) +{ + int *ow = d->hiddenCols.find(col); + if (ow) { + d->hiddenCols.replace(col, new int(w)); + } else { + topHeader->resizeSection(col, w); + columnWidthChanged(col); + } +} + +/*! + Resizes row \a row to be \a h pixels high. + + \sa rowHeight() setColumnWidth() +*/ + +void Q3Table::setRowHeight(int row, int h) +{ + int *oh = d->hiddenRows.find(row); + if (oh) { + d->hiddenRows.replace(row, new int(h)); + } else { + leftHeader->resizeSection(row, h); + rowHeightChanged(row); + } +} + +/*! + Resizes column \a col so that the column width is wide enough to + display the widest item the column contains. + + \sa adjustRow() +*/ + +void Q3Table::adjustColumn(int col) +{ + int w; + if ( currentColumn() == col ) { + QFont f = font(); + f.setBold(true); + w = topHeader->sectionSizeHint( col, QFontMetrics(f) ).width(); + } else { + w = topHeader->sectionSizeHint( col, fontMetrics() ).width(); + } + if (topHeader->iconSet(col)) + w += topHeader->iconSet(col)->pixmap().width(); + w = QMAX(w, 20); + for (int i = 0; i < numRows(); ++i) { + Q3TableItem *itm = item(i, col); + if (!itm) { + QWidget *widget = cellWidget(i, col); + if (widget) + w = QMAX(w, widget->sizeHint().width()); + } else { + if (itm->colSpan() > 1) + w = QMAX(w, itm->sizeHint().width() / itm->colSpan()); + else + w = QMAX(w, itm->sizeHint().width()); + } + } + w = QMAX(w, QApplication::globalStrut().width()); + setColumnWidth(col, w); +} + +/*! + Resizes row \a row so that the row height is tall enough to + display the tallest item the row contains. + + \sa adjustColumn() +*/ + +void Q3Table::adjustRow(int row) +{ + int h = 20; + h = QMAX(h, leftHeader->sectionSizeHint(row, leftHeader->fontMetrics()).height()); + if (leftHeader->iconSet(row)) + h = QMAX(h, leftHeader->iconSet(row)->pixmap().height()); + for (int i = 0; i < numCols(); ++i) { + Q3TableItem *itm = item(row, i); + if (!itm) { + QWidget *widget = cellWidget(row, i); + if (widget) + h = QMAX(h, widget->sizeHint().height()); + } else { + if (itm->rowSpan() > 1) + h = QMAX(h, itm->sizeHint().height() / itm->rowSpan()); + else + h = QMAX(h, itm->sizeHint().height()); + } + } + h = QMAX(h, QApplication::globalStrut().height()); + setRowHeight(row, h); +} + +/*! + If \a stretch is true, column \a col is set to be stretchable; + otherwise column \a col is set to be unstretchable. + + If the table widget's width decreases or increases stretchable + columns will grow narrower or wider to fit the space available as + completely as possible. The user cannot manually resize stretchable + columns. + + \sa isColumnStretchable() setRowStretchable() adjustColumn() +*/ + +void Q3Table::setColumnStretchable(int col, bool stretch) +{ + topHeader->setSectionStretchable(col, stretch); + + if (stretch && d->hiddenCols.find(col)) + topHeader->numStretches--; +} + +/*! + If \a stretch is true, row \a row is set to be stretchable; + otherwise row \a row is set to be unstretchable. + + If the table widget's height decreases or increases stretchable + rows will grow shorter or taller to fit the space available as + completely as possible. The user cannot manually resize + stretchable rows. + + \sa isRowStretchable() setColumnStretchable() +*/ + +void Q3Table::setRowStretchable(int row, bool stretch) +{ + leftHeader->setSectionStretchable(row, stretch); + + if (stretch && d->hiddenRows.find(row)) + leftHeader->numStretches--; +} + +/*! + Returns true if column \a col is stretchable; otherwise returns + false. + + \sa setColumnStretchable() isRowStretchable() +*/ + +bool Q3Table::isColumnStretchable(int col) const +{ + return topHeader->isSectionStretchable(col); +} + +/*! + Returns true if row \a row is stretchable; otherwise returns + false. + + \sa setRowStretchable() isColumnStretchable() +*/ + +bool Q3Table::isRowStretchable(int row) const +{ + return leftHeader->isSectionStretchable(row); +} + +/*! + Takes the table item \a i out of the table. This function does \e + not delete the table item. You must either delete the table item + yourself or put it into a table (using setItem()) which will then + take ownership of it. + + Use this function if you want to move an item from one cell in a + table to another, or to move an item from one table to another, + reinserting the item with setItem(). + + If you want to exchange two cells use swapCells(). +*/ + +void Q3Table::takeItem(Q3TableItem *i) +{ + if (!i) + return; + if (i->row() != -1 && i->col() != -1) { + QRect rect = cellGeometry(i->row(), i->col()); + contents.setAutoDelete(false); + int bottom = i->row() + i->rowSpan(); + if (bottom > numRows()) + bottom = numRows(); + int right = i->col() + i->colSpan(); + if (right > numCols()) + right = numCols(); + for (int r = i->row(); r < bottom; ++r) { + for (int c = i->col(); c < right; ++c) + contents.remove(indexOf(r, c)); + } + contents.setAutoDelete(true); + repaintContents(rect, false); + int orow = i->row(); + int ocol = i->col(); + i->setRow(-1); + i->setCol(-1); + i->updateEditor(orow, ocol); + } + i->t = 0; +} + +/*! + Sets the widget \a e to the cell at \a row, \a col and takes care of + placing and resizing the widget when the cell geometry changes. + + By default widgets are inserted into a vector with numRows() * + numCols() elements. In very large tables you will probably want to + store the widgets in a data structure that consumes less memory (see + the notes on large tables). To support the use of your own data + structure this function calls insertWidget() to add the widget to + the internal data structure. To use your own data structure + reimplement insertWidget(), cellWidget() and clearCellWidget(). + + Cell widgets are created dynamically with the \c new operator. The + cell widgets are destroyed automatically once the table is + destroyed; the table takes ownership of the widget when using + setCellWidget. + +*/ + +void Q3Table::setCellWidget(int row, int col, QWidget *e) +{ + if (!e || row >= numRows() || col >= numCols()) + return; + + QWidget *w = cellWidget(row, col); + if (w && row == editRow && col == editCol) + endEdit(editRow, editCol, false, edMode != Editing); + + e->installEventFilter(this); + clearCellWidget(row, col); + if (e->parent() != viewport()) + e->reparent(viewport(), QPoint(0,0)); + Q3TableItem *itm = item(row, col); + if (itm && itm->row() >= 0 && itm->col() >= 0) { // get the correct row and col if the item is spanning + row = itm->row(); + col = itm->col(); + } + insertWidget(row, col, e); + QRect cr = cellGeometry(row, col); + e->resize(cr.size()); + moveChild(e, cr.x(), cr.y()); + e->show(); +} + +/*! + Inserts widget \a w at \a row, \a col into the internal + data structure. See the documentation of setCellWidget() for + further details. + + If you don't use \l{Q3TableItem}s you may need to reimplement this + function: see the notes on large tables. +*/ + +void Q3Table::insertWidget(int row, int col, QWidget *w) +{ + if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1) + return; + + if ((int)widgets.size() != numRows() * numCols()) + widgets.resize(numRows() * numCols()); + + widgets.insert(indexOf(row, col), w); +} + +/*! + Returns the widget that has been set for the cell at \a row, \a + col, or 0 if no widget has been set. + + If you don't use \l{Q3TableItem}s you may need to reimplement this + function: see the notes on large tables. + + \sa clearCellWidget() setCellWidget() +*/ + +QWidget *Q3Table::cellWidget(int row, int col) const +{ + if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1) + return 0; + + if ((int)widgets.size() != numRows() * numCols()) + ((Q3Table*)this)->widgets.resize(numRows() * numCols()); + + return widgets[ indexOf(row, col) ]; +} + +/*! + Removes the widget (if there is one) set for the cell at \a row, + \a col. + + If you don't use \l{Q3TableItem}s you may need to reimplement this + function: see the notes on large tables. + + This function deletes the widget at \a row, \a col. Note that the + widget is not deleted immediately; instead QObject::deleteLater() + is called on the widget to avoid problems with timing issues. + + \sa cellWidget() setCellWidget() +*/ + +void Q3Table::clearCellWidget(int row, int col) +{ + if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1) + return; + + if ((int)widgets.size() != numRows() * numCols()) + widgets.resize(numRows() * numCols()); + + QWidget *w = cellWidget(row, col); + if (w) { + w->removeEventFilter(this); + w->hide(); + w->deleteLater(); + } + widgets.setAutoDelete(false); + widgets.remove(indexOf(row, col)); + widgets.setAutoDelete(true); +} + +/*! + \fn void Q3Table::dropped (QDropEvent * e) + + This signal is emitted when a drop event occurred on the table. + + \a e contains information about the drop. +*/ + +/*! + If \a b is true, the table starts a drag (see dragObject()) when + the user presses and moves the mouse on a selected cell. +*/ + +void Q3Table::setDragEnabled(bool b) +{ + dEnabled = b; +} + +/*! + If this function returns true, the table supports dragging. + + \sa setDragEnabled() +*/ + +bool Q3Table::dragEnabled() const +{ + return dEnabled; +} + +/*! + Inserts \a count empty rows at row \a row. Also clears the selection(s). + + \sa insertColumns() removeRow() +*/ + +void Q3Table::insertRows(int row, int count) +{ + // special case, so a call like insertRow(currentRow(), 1) also + // works, when we have 0 rows and currentRow() is -1 + if (row == -1 && curRow == -1) + row = 0; + if (row < 0 || count <= 0) + return; + + if (curRow >= row && curRow < row + count) + curRow = row + count; + + --row; + if (row >= numRows()) + return; + + bool updatesWereEnabled = updatesEnabled(); + if (updatesWereEnabled) + setUpdatesEnabled(false); + bool leftHeaderUpdatesEnabled = leftHeader->updatesEnabled(); + if (leftHeaderUpdatesEnabled) + leftHeader->setUpdatesEnabled(false); + int oldLeftMargin = leftMargin(); + + setNumRows(numRows() + count); + + for (int i = numRows() - count - 1; i > row; --i) + leftHeader->swapSections(i, i + count); + + if (leftHeaderUpdatesEnabled) + leftHeader->setUpdatesEnabled(leftHeaderUpdatesEnabled); + + if (updatesWereEnabled) + setUpdatesEnabled(true); + + int cr = QMAX(0, currentRow()); + int cc = QMAX(0, currentColumn()); + if (curRow > row) + curRow -= count; // this is where curRow was + setCurrentCell(cr, cc, true, false); // without ensureCellVisible + + // Repaint the header + if (leftHeaderUpdatesEnabled) { + int y = rowPos(row) - contentsY(); + if (leftMargin() != oldLeftMargin || d->hasRowSpan) + y = 0; // full repaint + QRect rect(0, y, leftHeader->width(), contentsHeight()); + leftHeader->update(rect); + } + + if (updatesWereEnabled) { + int p = rowPos(row); + if (d->hasRowSpan) + p = contentsY(); + updateContents(contentsX(), p, visibleWidth(), contentsHeight() + 1); + } +} + +/*! + Inserts \a count empty columns at column \a col. Also clears the selection(s). + + \sa insertRows() removeColumn() +*/ + +void Q3Table::insertColumns(int col, int count) +{ + // see comment in insertRows() + if (col == -1 && curCol == -1) + col = 0; + if (col < 0 || count <= 0) + return; + + if (curCol >= col && curCol < col + count) + curCol = col + count; + + --col; + if (col >= numCols()) + return; + + bool updatesWereEnabled = updatesEnabled(); + if (updatesWereEnabled) + setUpdatesEnabled(false); + bool topHeaderUpdatesEnabled = topHeader->updatesEnabled(); + if (topHeaderUpdatesEnabled) + topHeader->setUpdatesEnabled(false); + int oldTopMargin = topMargin(); + + setNumCols(numCols() + count); + + for (int i = numCols() - count - 1; i > col; --i) + topHeader->swapSections(i, i + count); + + if (topHeaderUpdatesEnabled) + topHeader->setUpdatesEnabled(true); + if (updatesWereEnabled) + setUpdatesEnabled(true); + + int cr = QMAX(0, currentRow()); + int cc = QMAX(0, currentColumn()); + if (curCol > col) + curCol -= count; // this is where curCol was + setCurrentCell(cr, cc, true, false); // without ensureCellVisible + + // Repaint the header + if (topHeaderUpdatesEnabled) { + int x = columnPos(col) - contentsX(); + if (topMargin() != oldTopMargin || d->hasColSpan) + x = 0; // full repaint + QRect rect(x, 0, contentsWidth(), topHeader->height()); + topHeader->update(rect); + } + + if (updatesWereEnabled) { + int p = columnPos(col); + if (d->hasColSpan) + p = contentsX(); + updateContents(p, contentsY(), contentsWidth() + 1, visibleHeight()); + } +} + +/*! + Removes row \a row, and deletes all its cells including any table + items and widgets the cells may contain. Also clears the selection(s). + + \sa hideRow() insertRows() removeColumn() removeRows() +*/ + +void Q3Table::removeRow(int row) +{ + if (row < 0 || row >= numRows()) + return; + if (row < numRows() - 1) { + if (d->hiddenRows.find(row)) + d->hiddenRows.remove(row); + + for (int i = row; i < numRows() - 1; ++i) + ((Q3TableHeader*)verticalHeader())->swapSections(i, i + 1); + } + setNumRows(numRows() - 1); +} + +/*! + Removes the rows listed in the array \a rows, and deletes all their + cells including any table items and widgets the cells may contain. + + The array passed in must only contain valid rows (in the range + from 0 to numRows() - 1) with no duplicates, and must be sorted in + ascending order. Also clears the selection(s). + + \sa removeRow() insertRows() removeColumns() +*/ + +void Q3Table::removeRows(const Q3MemArray<int> &rows) +{ + if (rows.count() == 0) + return; + int i; + for (i = 0; i < (int)rows.count() - 1; ++i) { + for (int j = rows[i] - i; j < rows[i + 1] - i - 1; j++) { + ((Q3TableHeader*)verticalHeader())->swapSections(j, j + i + 1); + } + } + + for (int j = rows[i] - i; j < numRows() - (int)rows.size(); j++) + ((Q3TableHeader*)verticalHeader())->swapSections(j, j + rows.count()); + + setNumRows(numRows() - rows.count()); +} + +/*! + Removes column \a col, and deletes all its cells including any + table items and widgets the cells may contain. Also clears the + selection(s). + + \sa removeColumns() hideColumn() insertColumns() removeRow() +*/ + +void Q3Table::removeColumn(int col) +{ + if (col < 0 || col >= numCols()) + return; + if (col < numCols() - 1) { + if (d->hiddenCols.find(col)) + d->hiddenCols.remove(col); + + for (int i = col; i < numCols() - 1; ++i) + ((Q3TableHeader*)horizontalHeader())->swapSections(i, i + 1); + } + setNumCols(numCols() - 1); +} + +/*! + Removes the columns listed in the array \a cols, and deletes all + their cells including any table items and widgets the cells may + contain. + + The array passed in must only contain valid columns (in the range + from 0 to numCols() - 1) with no duplicates, and must be sorted in + ascending order. Also clears the selection(s). + + \sa removeColumn() insertColumns() removeRows() +*/ + +void Q3Table::removeColumns(const Q3MemArray<int> &cols) +{ + if (cols.count() == 0) + return; + int i; + for (i = 0; i < (int)cols.count() - 1; ++i) { + for (int j = cols[i] - i; j < cols[i + 1] - i - 1; j++) { + ((Q3TableHeader*)horizontalHeader())->swapSections(j, j + i + 1); + } + } + + for (int j = cols[i] - i; j < numCols() - (int)cols.size(); j++) + ((Q3TableHeader*)horizontalHeader())->swapSections(j, j + cols.count()); + + setNumCols(numCols() - cols.count()); +} + +/*! + Starts editing the cell at \a row, \a col. + + If \a replace is true the content of this cell will be replaced by + the content of the editor when editing is finished, i.e. the user + will be entering new data; otherwise the current content of the + cell (if any) will be modified in the editor. + + \sa beginEdit() +*/ + +void Q3Table::editCell(int row, int col, bool replace) +{ + if (row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1) + return; + + if (beginEdit(row, col, replace)) { + edMode = Editing; + editRow = row; + editCol = col; + } +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + This event handler is called whenever a Q3Table object receives a + \l QDragEnterEvent \a e, i.e. when the user pressed the mouse + button to drag something. + + The focus is moved to the cell where the QDragEnterEvent occurred. +*/ + +void Q3Table::contentsDragEnterEvent(QDragEnterEvent *e) +{ + oldCurrentRow = curRow; + oldCurrentCol = curCol; + int tmpRow = rowAt(e->pos().y()); + int tmpCol = columnAt(e->pos().x()); + fixRow(tmpRow, e->pos().y()); + fixCol(tmpCol, e->pos().x()); + if (e->source() != (QObject*)cellWidget(currentRow(), currentColumn())) + setCurrentCell(tmpRow, tmpCol, false, true); + e->accept(); +} + +/*! + This event handler is called whenever a Q3Table object receives a + \l QDragMoveEvent \a e, i.e. when the user actually drags the + mouse. + + The focus is moved to the cell where the QDragMoveEvent occurred. +*/ + +void Q3Table::contentsDragMoveEvent(QDragMoveEvent *e) +{ + int tmpRow = rowAt(e->pos().y()); + int tmpCol = columnAt(e->pos().x()); + fixRow(tmpRow, e->pos().y()); + fixCol(tmpCol, e->pos().x()); + if (e->source() != (QObject*)cellWidget(currentRow(), currentColumn())) + setCurrentCell(tmpRow, tmpCol, false, true); + e->accept(); +} + +/*! + This event handler is called when a drag activity leaves \e this + Q3Table object with event \a e. +*/ + +void Q3Table::contentsDragLeaveEvent(QDragLeaveEvent *) +{ + setCurrentCell(oldCurrentRow, oldCurrentCol, false, true); +} + +/*! + This event handler is called when the user ends a drag and drop by + dropping something onto \e this Q3Table and thus triggers the drop + event, \a e. +*/ + +void Q3Table::contentsDropEvent(QDropEvent *e) +{ + setCurrentCell(oldCurrentRow, oldCurrentCol, false, true); + emit dropped(e); +} + +/*! + If the user presses the mouse on a selected cell, starts moving + (i.e. dragging), and dragEnabled() is true, this function is + called to obtain a drag object. A drag using this object begins + immediately unless dragObject() returns 0. + + By default this function returns 0. You might reimplement it and + create a Q3DragObject depending on the selected items. + + \sa dropped() +*/ + +Q3DragObject *Q3Table::dragObject() +{ + return 0; +} + +/*! + Starts a drag. + + Usually you don't need to call or reimplement this function yourself. + + \sa dragObject() +*/ + +void Q3Table::startDrag() +{ + if (startDragRow == -1 || startDragCol == -1) + return; + + startDragRow = startDragCol = -1; + + Q3DragObject *drag = dragObject(); + if (!drag) + return; + + drag->drag(); +} + +#endif + +/*! \reimp */ +void Q3Table::windowActivationChange(bool oldActive) +{ + if (oldActive && autoScrollTimer) + autoScrollTimer->stop(); + + if (!isVisible()) + return; + + if (palette().active() != palette().inactive()) + updateContents(); +} + +/*! + \internal +*/ +void Q3Table::setEnabled(bool b) +{ + if (!b) { + // editor will lose focus, causing a crash deep in setEnabled(), + // so we'll end the edit early. + endEdit(editRow, editCol, true, edMode != Editing); + } + Q3ScrollView::setEnabled(b); +} + + +/* + \class Q3TableHeader + \brief The Q3TableHeader class allows for creation and manipulation + of table headers. + + \compat + + Q3Table uses this subclass of Q3Header for its headers. Q3Table has a + horizontalHeader() for displaying column labels, and a + verticalHeader() for displaying row labels. + +*/ + +/* + \enum Q3TableHeader::SectionState + + This enum type denotes the state of the header's text + + \value Normal the default + \value Bold + \value Selected typically represented by showing the section "sunken" + or "pressed in" +*/ + +/*! + Creates a new table header called \a name with \a i sections. It + is a child of widget \a parent and attached to table \a t. +*/ + +Q3TableHeader::Q3TableHeader(int i, Q3Table *t, + QWidget *parent, const char *name) + : Q3Header(i, parent, name), mousePressed(false), startPos(-1), + table(t), caching(false), resizedSection(-1), + numStretches(0) +{ + setIsATableHeader(true); + d = 0; + states.resize(i); + stretchable.resize(i); + states.fill(Normal, -1); + stretchable.fill(false, -1); + autoScrollTimer = new QTimer(this); + connect(autoScrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll())); +#ifndef NO_LINE_WIDGET + line1 = new QWidget(table->viewport(), "qt_line1"); + line1->hide(); + line1->setBackgroundMode(PaletteText); + table->addChild(line1); + line2 = new QWidget(table->viewport(), "qt_line2"); + line2->hide(); + line2->setBackgroundMode(PaletteText); + table->addChild(line2); +#else + d = new Q3TableHeaderPrivate; + d->oldLinePos = -1; //outside, in contents coords +#endif + connect(this, SIGNAL(sizeChange(int,int,int)), + this, SLOT(sectionWidthChanged(int,int,int))); + connect(this, SIGNAL(indexChange(int,int,int)), + this, SLOT(indexChanged(int,int,int))); + + stretchTimer = new QTimer(this); + widgetStretchTimer = new QTimer(this); + connect(stretchTimer, SIGNAL(timeout()), + this, SLOT(updateStretches())); + connect(widgetStretchTimer, SIGNAL(timeout()), + this, SLOT(updateWidgetStretches())); + startPos = -1; +} + +/*! + Adds a new section, \a size pixels wide (or high for vertical + headers) with the label \a s. If \a size is negative the section's + size is calculated based on the width (or height) of the label's + text. +*/ + +void Q3TableHeader::addLabel(const QString &s , int size) +{ + Q3Header::addLabel(s, size); + if (count() > (int)states.size()) { + int s = states.size(); + states.resize(count()); + stretchable.resize(count()); + for (; s < count(); ++s) { + states[ s ] = Normal; + stretchable[ s ] = false; + } + } +} + +void Q3TableHeader::removeLabel(int section) +{ + Q3Header::removeLabel(section); + if (section == (int)states.size() - 1) { + states.resize(states.size() - 1); + stretchable.resize(stretchable.size() - 1); + } +} + +void Q3TableHeader::resizeArrays(int n) +{ + int old = states.size(); + states.resize(n); + stretchable.resize(n); + if (n > old) { + for (int i = old; i < n; ++i) { + stretchable[ i ] = false; + states[ i ] = Normal; + } + } +} + +void Q3TableHeader::setLabel(int section, const QString & s, int size) +{ + Q3Header::setLabel(section, s, size); + sectionLabelChanged(section); +} + +void Q3TableHeader::setLabel(int section, const QIconSet & iconset, + const QString & s, int size) +{ + Q3Header::setLabel(section, iconset, s, size); + sectionLabelChanged(section); +} + +/*! + Sets the SectionState of section \a s to \a astate. + + \sa sectionState() +*/ + +void Q3TableHeader::setSectionState(int s, SectionState astate) +{ + if (s < 0 || s >= (int)states.count()) + return; + if (states.data()[ s ] == astate) + return; + if (isRowSelection(table->selectionMode()) && orientation() == Horizontal) + return; + + states.data()[ s ] = astate; + if (updatesEnabled()) { + if (orientation() == Horizontal) + repaint(sectionPos(s) - offset(), 0, sectionSize(s), height(), false); + else + repaint(0, sectionPos(s) - offset(), width(), sectionSize(s), false); + } +} + +void Q3TableHeader::setSectionStateToAll(SectionState state) +{ + if (isRowSelection(table->selectionMode()) && orientation() == Horizontal) + return; + + register int *d = (int *) states.data(); + int n = count(); + + while (n >= 4) { + d[0] = state; + d[1] = state; + d[2] = state; + d[3] = state; + d += 4; + n -= 4; + } + + if (n > 0) { + d[0] = state; + if (n > 1) { + d[1] = state; + if (n > 2) { + d[2] = state; + } + } + } +} + +/*! + Returns the SectionState of section \a s. + + \sa setSectionState() +*/ + +Q3TableHeader::SectionState Q3TableHeader::sectionState(int s) const +{ + return (s < 0 || s >= (int)states.count() ? Normal : (Q3TableHeader::SectionState)states[s]); +} + +/*! \reimp +*/ + +void Q3TableHeader::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + p.setPen(colorGroup().buttonText()); + int pos = orientation() == Horizontal + ? e->rect().left() + : e->rect().top(); + int id = mapToIndex(sectionAt(pos + offset())); + if (id < 0) { + if (pos > 0) + return; + else + id = 0; + } + + QRegion reg = e->region(); + for (int i = id; i < count(); i++) { + QRect r = sRect(i); + reg -= r; + p.save(); + if (!(orientation() == Horizontal && isRowSelection(table->selectionMode())) && + (sectionState(i) == Bold || sectionState(i) == Selected)) { + QFont f(font()); + f.setBold(true); + p.setFont(f); + } + paintSection(&p, i, r); + p.restore(); + if ((orientation() == Horizontal && r. right() >= e->rect().right()) + || (orientation() == Vertical && r. bottom() >= e->rect().bottom())) + return; + } + p.end(); + if (!reg.isEmpty()) + erase(reg); +} + +/*! + \reimp + + Paints the header section with index \a index into the rectangular + region \a fr on the painter \a p. +*/ + +void Q3TableHeader::paintSection(QPainter *p, int index, const QRect& fr) +{ + int section = mapToSection(index); + if (section < 0 || cellSize(section) <= 0) + return; + + if (sectionState(index) != Selected || + (orientation() == Horizontal && isRowSelection(table->selectionMode()))) { + Q3Header::paintSection(p, index, fr); + } else { + QStyleOptionHeader opt; + opt.palette = palette(); + opt.rect = fr; + opt.state = QStyle::State_Off | (orient == Qt::Horizontal ? QStyle::State_Horizontal + : QStyle::State_None); + if (isEnabled()) + opt.state |= QStyle::State_Enabled; + if (isClickEnabled()) { + if (sectionState(index) == Selected) { + opt.state |= QStyle::State_Sunken; + if (!mousePressed) + opt.state |= QStyle::State_On; + } + } + if (!(opt.state & QStyle::State_Sunken)) + opt.state |= QStyle::State_Raised; + style()->drawControl(QStyle::CE_HeaderSection, &opt, p, this); + paintSectionLabel(p, index, fr); + } +} + +static int real_pos(const QPoint &p, Qt::Orientation o) +{ + if (o == Qt::Horizontal) + return p.x(); + return p.y(); +} + +/*! \reimp +*/ + +void Q3TableHeader::mousePressEvent(QMouseEvent *e) +{ + if (e->button() != LeftButton) + return; + Q3Header::mousePressEvent(e); + mousePressed = true; + pressPos = real_pos(e->pos(), orientation()); + if (!table->currentSel || (e->state() & ShiftButton) != ShiftButton) + startPos = -1; + setCaching(true); + resizedSection = -1; +#ifdef QT_NO_CURSOR + isResizing = false; +#else + isResizing = cursor().shape() != ArrowCursor; + if (!isResizing && sectionAt(pressPos) != -1) + doSelection(e); +#endif +} + +/*! \reimp +*/ + +void Q3TableHeader::mouseMoveEvent(QMouseEvent *e) +{ + if ((e->state() & MouseButtonMask) != LeftButton // Using LeftButton simulates old behavior. +#ifndef QT_NO_CURSOR + || cursor().shape() != ArrowCursor +#endif + || ((e->state() & ControlButton) == ControlButton && + (orientation() == Horizontal + ? table->columnMovingEnabled() : table->rowMovingEnabled()))) { + Q3Header::mouseMoveEvent(e); + return; + } + + if (!doSelection(e)) + Q3Header::mouseMoveEvent(e); +} + +bool Q3TableHeader::doSelection(QMouseEvent *e) +{ + int p = real_pos(e->pos(), orientation()) + offset(); + + if (isRowSelection(table->selectionMode())) { + if (orientation() == Horizontal) + return true; + if (table->selectionMode() == Q3Table::SingleRow) { + int secAt = sectionAt(p); + if (secAt == -1) + return true; + table->setCurrentCell(secAt, table->currentColumn()); + return true; + } + } + + if (startPos == -1) { + int secAt = sectionAt(p); + if (((e->state() & ControlButton) != ControlButton && (e->state() & ShiftButton) != ShiftButton) + || table->selectionMode() == Q3Table::Single + || table->selectionMode() == Q3Table::SingleRow) { + startPos = p; + bool b = table->signalsBlocked(); + table->blockSignals(true); + table->clearSelection(); + table->blockSignals(b); + } + saveStates(); + + if (table->selectionMode() != Q3Table::NoSelection) { + startPos = p; + Q3TableSelection *oldSelection = table->currentSel; + + if (orientation() == Vertical) { + if (!table->isRowSelected(secAt, true)) { + table->currentSel = new Q3TableSelection(); + table->selections.append(table->currentSel); + table->currentSel->init(secAt, 0); + table->currentSel->expandTo(secAt, table->numCols() - 1); + emit table->selectionChanged(); + } + table->setCurrentCell(secAt, 0); + } else { // orientation == Horizontal + if (!table->isColumnSelected(secAt, true)) { + table->currentSel = new Q3TableSelection(); + table->selections.append(table->currentSel); + table->currentSel->init(0, secAt); + table->currentSel->expandTo(table->numRows() - 1, secAt); + emit table->selectionChanged(); + } + table->setCurrentCell(0, secAt); + } + + if ((orientation() == Horizontal && table->isColumnSelected(secAt)) + || (orientation() == Vertical && table->isRowSelected(secAt))) { + setSectionState(secAt, Selected); + } + + table->repaintSelections(oldSelection, table->currentSel, + orientation() == Horizontal, + orientation() == Vertical); + if (sectionAt(p) != -1) + endPos = p; + + return true; + } + } + + if (sectionAt(p) != -1) + endPos = p; + if (startPos != -1) { + updateSelections(); + p -= offset(); + if (orientation() == Horizontal && (p < 0 || p > width())) { + doAutoScroll(); + autoScrollTimer->start(100, true); + } else if (orientation() == Vertical && (p < 0 || p > height())) { + doAutoScroll(); + autoScrollTimer->start(100, true); + } + return true; + } + return table->selectionMode() == Q3Table::NoSelection; +} + +static inline bool mayOverwriteMargin(int before, int after) +{ + /* + 0 is the only user value that we always respect. We also never + shrink a margin, in case the user wanted it that way. + */ + return before != 0 && before < after; +} + +void Q3TableHeader::sectionLabelChanged(int section) +{ + emit sectionSizeChanged(section); + + // this does not really belong here + if (orientation() == Horizontal) { + int h = sizeHint().height(); + if (h != height() && mayOverwriteMargin(table->topMargin(), h)) + table->setTopMargin(h); + } else { + int w = sizeHint().width(); + if (w != width() && mayOverwriteMargin((QApplication::reverseLayout() ? table->rightMargin() : table->leftMargin()), w)) + table->setLeftMargin(w); + } +} + +/*! \reimp */ +void Q3TableHeader::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() != LeftButton) + return; + autoScrollTimer->stop(); + mousePressed = false; + setCaching(false); + Q3Header::mouseReleaseEvent(e); +#ifndef NO_LINE_WIDGET + line1->hide(); + line2->hide(); +#else + if (d->oldLinePos >= 0) + if (orientation() == Horizontal) + table->updateContents(d->oldLinePos, table->contentsY(), + 1, table->visibleHeight()); + else + table->updateContents( table->contentsX(), d->oldLinePos, + table->visibleWidth(), 1); + d->oldLinePos = -1; +#endif + if (resizedSection != -1) { + emit sectionSizeChanged(resizedSection); + updateStretches(); + } + + //Make sure all newly selected sections are painted one last time + QRect selectedRects; + for (int i = 0; i < count(); i++) { + if(sectionState(i) == Selected) + selectedRects |= sRect(i); + } + if(!selectedRects.isNull()) + repaint(selectedRects); +} + +/*! \reimp +*/ + +void Q3TableHeader::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() != LeftButton) + return; + if (isResizing) { + int p = real_pos(e->pos(), orientation()) + offset(); + int section = sectionAt(p); + if (section == -1) + return; + section--; + if (p >= sectionPos(count() - 1) + sectionSize(count() - 1)) + ++section; + while (sectionSize(section) == 0) + section--; + if (section < 0) + return; + int oldSize = sectionSize(section); + if (orientation() == Horizontal) { + table->adjustColumn(section); + int newSize = sectionSize(section); + if (oldSize != newSize) + emit sizeChange(section, oldSize, newSize); + for (int i = 0; i < table->numCols(); ++i) { + if (table->isColumnSelected(i) && sectionSize(i) != 0) + table->adjustColumn(i); + } + } else { + table->adjustRow(section); + int newSize = sectionSize(section); + if (oldSize != newSize) + emit sizeChange(section, oldSize, newSize); + for (int i = 0; i < table->numRows(); ++i) { + if (table->isRowSelected(i) && sectionSize(i) != 0) + table->adjustRow(i); + } + } + } +} + +/*! \reimp +*/ + +void Q3TableHeader::resizeEvent(QResizeEvent *e) +{ + stretchTimer->stop(); + widgetStretchTimer->stop(); + Q3Header::resizeEvent(e); + if (numStretches == 0) + return; + stretchTimer->start(0, true); +} + +void Q3TableHeader::updateStretches() +{ + if (numStretches == 0) + return; + + int dim = orientation() == Horizontal ? width() : height(); + if (sectionPos(count() - 1) + sectionSize(count() - 1) == dim) + return; + int i; + int pd = dim - (sectionPos(count() - 1) + + sectionSize(count() - 1)); + bool block = signalsBlocked(); + blockSignals(true); + for (i = 0; i < (int)stretchable.count(); ++i) { + if (!stretchable[i] || + (stretchable[i] && table->d->hiddenCols[i])) + continue; + pd += sectionSize(i); + } + pd /= numStretches; + for (i = 0; i < (int)stretchable.count(); ++i) { + if (!stretchable[i] || + (stretchable[i] && table->d->hiddenCols[i])) + continue; + if (i == (int)stretchable.count() - 1 && + sectionPos(i) + pd < dim) + pd = dim - sectionPos(i); + resizeSection(i, QMAX(20, pd)); + } + blockSignals(block); + table->repaintContents(false); + widgetStretchTimer->start(100, true); +} + +void Q3TableHeader::updateWidgetStretches() +{ + QSize s = table->tableSize(); + table->resizeContents(s.width(), s.height()); + for (int i = 0; i < table->numCols(); ++i) + table->updateColWidgets(i); +} + +void Q3TableHeader::updateSelections() +{ + if (table->selectionMode() == Q3Table::NoSelection || + (isRowSelection(table->selectionMode()) && orientation() != Vertical )) + return; + int a = sectionAt(startPos); + int b = sectionAt(endPos); + int start = QMIN(a, b); + int end = QMAX(a, b); + register int *s = states.data(); + for (int i = 0; i < count(); ++i) { + if (i < start || i > end) + *s = oldStates.data()[ i ]; + else + *s = Selected; + ++s; + } + repaint(false); + + if (table->currentSel) { + Q3TableSelection oldSelection = *table->currentSel; + if (orientation() == Vertical) + table->currentSel->expandTo(b, table->horizontalHeader()->count() - 1); + else + table->currentSel->expandTo(table->verticalHeader()->count() - 1, b); + table->repaintSelections(&oldSelection, table->currentSel, + orientation() == Horizontal, + orientation() == Vertical); + } + emit table->selectionChanged(); +} + +void Q3TableHeader::saveStates() +{ + oldStates.resize(count()); + register int *s = states.data(); + register int *s2 = oldStates.data(); + for (int i = 0; i < count(); ++i) { + *s2 = *s; + ++s2; + ++s; + } +} + +void Q3TableHeader::doAutoScroll() +{ + QPoint pos = mapFromGlobal(QCursor::pos()); + int p = real_pos(pos, orientation()) + offset(); + if (sectionAt(p) != -1) + endPos = p; + if (orientation() == Horizontal) + table->ensureVisible(endPos, table->contentsY()); + else + table->ensureVisible(table->contentsX(), endPos); + updateSelections(); + autoScrollTimer->start(100, true); +} + +void Q3TableHeader::sectionWidthChanged(int col, int, int) +{ + resizedSection = col; + if (orientation() == Horizontal) { +#ifndef NO_LINE_WIDGET + table->moveChild(line1, Q3Header::sectionPos(col) - 1, + table->contentsY()); + line1->resize(1, table->visibleHeight()); + line1->show(); + line1->raise(); + table->moveChild(line2, + Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1, + table->contentsY()); + line2->resize(1, table->visibleHeight()); + line2->show(); + line2->raise(); +#else + QPainter p(table->viewport()); + int lx = Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1; + int ly = table->contentsY(); + + if (lx != d->oldLinePos) { + QPoint pt = table->contentsToViewport(QPoint(lx, ly)); + p.drawLine(pt.x(), pt.y()+1, + pt.x(), pt.y()+ table->visibleHeight()); + if (d->oldLinePos >= 0) + table->repaintContents(d->oldLinePos, table->contentsY(), + 1, table->visibleHeight()); + + d->oldLinePos = lx; + } +#endif + } else { +#ifndef NO_LINE_WIDGET + table->moveChild(line1, table->contentsX(), + Q3Header::sectionPos(col) - 1); + line1->resize(table->visibleWidth(), 1); + line1->show(); + line1->raise(); + table->moveChild(line2, table->contentsX(), + Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1); + line2->resize(table->visibleWidth(), 1); + line2->show(); + line2->raise(); + +#else + QPainter p(table->viewport()); + int lx = table->contentsX(); + int ly = Q3Header::sectionPos(col) + Q3Header::sectionSize(col) - 1; + + if (ly != d->oldLinePos) { + QPoint pt = table->contentsToViewport(QPoint(lx, ly)); + p.drawLine(pt.x()+1, pt.y(), + pt.x() + table->visibleWidth(), pt.y()); + if (d->oldLinePos >= 0) + table->repaintContents( table->contentsX(), d->oldLinePos, + table->visibleWidth(), 1); + d->oldLinePos = ly; + } + +#endif + } +} + +/*! + \reimp + + Returns the size of section \a section in pixels or -1 if \a + section is out of range. +*/ + +int Q3TableHeader::sectionSize(int section) const +{ + if (count() <= 0 || section < 0 || section >= count()) + return -1; + if (caching && section < (int)sectionSizes.count()) + return sectionSizes[ section ]; + return Q3Header::sectionSize(section); +} + +/*! + \reimp + + Returns the start position of section \a section in pixels or -1 + if \a section is out of range. + + \sa sectionAt() +*/ + +int Q3TableHeader::sectionPos(int section) const +{ + if (count() <= 0 || section < 0 || section >= count()) + return -1; + if (caching && section < (int)sectionPoses.count()) + return sectionPoses[ section ]; + return Q3Header::sectionPos(section); +} + +/*! + \reimp + + Returns the number of the section at index position \a pos or -1 + if there is no section at the position given. + + \sa sectionPos() +*/ + +int Q3TableHeader::sectionAt(int pos) const +{ + if (!caching || sectionSizes.count() <= 0 || sectionPoses.count() <= 0) + return Q3Header::sectionAt(pos); + if (count() <= 0 || pos > sectionPoses[ count() - 1 ] + sectionSizes[ count() - 1 ]) + return -1; + int l = 0; + int r = count() - 1; + int i = ((l+r+1) / 2); + while (r - l) { + if (sectionPoses[i] > pos) + r = i -1; + else + l = i; + i = ((l+r+1) / 2); + } + if (sectionPoses[i] <= pos && + pos <= sectionPoses[i] + sectionSizes[ mapToSection(i) ]) + return mapToSection(i); + return -1; +} + +void Q3TableHeader::updateCache() +{ + sectionPoses.resize(count()); + sectionSizes.resize(count()); + if (!caching) + return; + for (int i = 0; i < count(); ++i) { + sectionSizes[ i ] = Q3Header::sectionSize(i); + sectionPoses[ i ] = Q3Header::sectionPos(i); + } +} + +void Q3TableHeader::setCaching(bool b) +{ + if (caching == b) + return; + caching = b; + sectionPoses.resize(count()); + sectionSizes.resize(count()); + if (b) { + for (int i = 0; i < count(); ++i) { + sectionSizes[ i ] = Q3Header::sectionSize(i); + sectionPoses[ i ] = Q3Header::sectionPos(i); + } + } +} + +/*! + If \a b is true, section \a s is stretchable; otherwise the + section is not stretchable. + + \sa isSectionStretchable() +*/ + +void Q3TableHeader::setSectionStretchable(int s, bool b) +{ + if (stretchable[ s ] == b) + return; + stretchable[ s ] = b; + if (b) + numStretches++; + else + numStretches--; +} + +/*! + Returns true if section \a s is stretcheable; otherwise returns + false. + + \sa setSectionStretchable() +*/ + +bool Q3TableHeader::isSectionStretchable(int s) const +{ + return stretchable[ s ]; +} + +void Q3TableHeader::swapSections(int oldIdx, int newIdx, bool swapTable) +{ + extern bool qt_qheader_label_return_null_strings; // qheader.cpp + qt_qheader_label_return_null_strings = true; + + QIconSet oldIconSet, newIconSet; + if (iconSet(oldIdx)) + oldIconSet = *iconSet(oldIdx); + if (iconSet(newIdx)) + newIconSet = *iconSet(newIdx); + QString oldLabel = label(oldIdx); + QString newLabel = label(newIdx); + bool sectionsHasContent = !(oldIconSet.isNull() && newIconSet.isNull() + && oldLabel.isNull() && newLabel.isNull()); + if (sectionsHasContent) { + Q3HeaderData *data = static_cast<Q3Header*>(this)->d; + bool oldNullLabel = qt_get_null_label_bit(data, oldIdx); + bool newNullLabel = qt_get_null_label_bit(data, newIdx); + setLabel(oldIdx, newIconSet, newLabel); + setLabel(newIdx, oldIconSet, oldLabel); + qt_set_null_label_bit(data, oldIdx, newNullLabel); + qt_set_null_label_bit(data, newIdx, oldNullLabel); + } + + qt_qheader_label_return_null_strings = false; + + int w1 = sectionSize(oldIdx); + int w2 = sectionSize(newIdx); + if (w1 != w2) { + resizeSection(oldIdx, w2); + resizeSection(newIdx, w1); + } + + if (!swapTable) + return; + if (orientation() == Horizontal) + table->swapColumns(oldIdx, newIdx); + else + table->swapRows(oldIdx, newIdx); +} + +void Q3TableHeader::indexChanged(int sec, int oldIdx, int newIdx) +{ + newIdx = mapToIndex(sec); + if (oldIdx > newIdx) + moveSection(sec, oldIdx + 1); + else + moveSection(sec, oldIdx); + + if (oldIdx < newIdx) { + while (oldIdx < newIdx) { + swapSections(oldIdx, oldIdx + 1); + oldIdx++; + } + } else { + while (oldIdx > newIdx) { + swapSections(oldIdx - 1, oldIdx); + oldIdx--; + } + } + + table->repaintContents(table->contentsX(), table->contentsY(), + table->visibleWidth(), table->visibleHeight()); +} + +void Q3TableHeader::setLabels(const QStringList & labels) +{ + int i = 0; + const int c = QMIN(count(), (int)labels.count()); + bool updates = updatesEnabled(); + if (updates) + setUpdatesEnabled(false); + for (QStringList::ConstIterator it = labels.begin(); i < c; ++i, ++it) { + if (i == c - 1) { + if (updates) + setUpdatesEnabled(true); + setLabel(i, *it); + } else { + Q3Header::setLabel(i, *it); + emit sectionSizeChanged(i); + } + } +} + +QT_END_NAMESPACE + +#include "q3table.moc" diff --git a/src/qt3support/itemviews/q3table.h b/src/qt3support/itemviews/q3table.h new file mode 100644 index 0000000..6331ca4 --- /dev/null +++ b/src/qt3support/itemviews/q3table.h @@ -0,0 +1,548 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3TABLE_H +#define Q3TABLE_H + +#include <Qt3Support/q3scrollview.h> +#include <QtGui/qpixmap.h> +#include <Qt3Support/q3ptrvector.h> +#include <Qt3Support/q3header.h> +#include <Qt3Support/q3memarray.h> +#include <Qt3Support/q3ptrlist.h> +#include <Qt3Support/q3shared.h> +#include <Qt3Support/q3intdict.h> +#include <QtCore/qstringlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +class Q3TableHeader; +class QValidator; +class Q3Table; +class QPaintEvent; +class QTimer; +class QResizeEvent; +class Q3ComboBox; +class QCheckBox; +class Q3DragObject; +class QColorGroup; + +struct Q3TablePrivate; +struct Q3TableHeaderPrivate; + + +class Q_COMPAT_EXPORT Q3TableSelection +{ +public: + Q3TableSelection(); + Q3TableSelection(int start_row, int start_col, int end_row, int end_col); + void init(int row, int col); + void expandTo(int row, int col); + bool operator==(const Q3TableSelection &s) const; + bool operator!=(const Q3TableSelection &s) const { return !(operator==(s)); } + + int topRow() const { return tRow; } + int bottomRow() const { return bRow; } + int leftCol() const { return lCol; } + int rightCol() const { return rCol; } + int anchorRow() const { return aRow; } + int anchorCol() const { return aCol; } + int numRows() const; + int numCols() const; + + bool isActive() const { return active; } + bool isEmpty() const { return numRows() == 0; } + +private: + uint active : 1; + uint inited : 1; + int tRow, lCol, bRow, rCol; + int aRow, aCol; +}; + +class Q_COMPAT_EXPORT Q3TableItem +{ + friend class Q3Table; + +public: + enum EditType { Never, OnTyping, WhenCurrent, Always }; + + Q3TableItem(Q3Table *table, EditType et); + Q3TableItem(Q3Table *table, EditType et, const QString &text); + Q3TableItem(Q3Table *table, EditType et, const QString &text, + const QPixmap &p); + virtual ~Q3TableItem(); + + virtual QPixmap pixmap() const; + virtual QString text() const; + virtual void setPixmap(const QPixmap &p); + virtual void setText(const QString &t); + Q3Table *table() const { return t; } + + virtual int alignment() const; + virtual void setWordWrap(bool b); + bool wordWrap() const; + + EditType editType() const; + virtual QWidget *createEditor() const; + virtual void setContentFromEditor(QWidget *w); + virtual void setReplaceable(bool); + bool isReplaceable() const; + + virtual QString key() const; + virtual QSize sizeHint() const; + + virtual void setSpan(int rs, int cs); + int rowSpan() const; + int colSpan() const; + + virtual void setRow(int r); + virtual void setCol(int c); + int row() const; + int col() const; + + virtual void paint(QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected); + + void updateEditor(int oldRow, int oldCol); + + virtual void setEnabled(bool b); + bool isEnabled() const; + + virtual int rtti() const; + static int RTTI; + +private: + QString txt; + QPixmap pix; + Q3Table *t; + EditType edType; + uint wordwrap : 1; + uint tcha : 1; + uint enabled : 1; + int rw, cl; + int rowspan, colspan; +}; + +class Q_COMPAT_EXPORT Q3ComboTableItem : public Q3TableItem +{ +public: + Q3ComboTableItem(Q3Table *table, const QStringList &list, bool editable = false); + ~Q3ComboTableItem(); + virtual QWidget *createEditor() const; + virtual void setContentFromEditor(QWidget *w); + virtual void paint(QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected); + virtual void setCurrentItem(int i); + virtual void setCurrentItem(const QString &i); + int currentItem() const; + QString currentText() const; + int count() const; +#if !defined(Q_NO_USING_KEYWORD) + using Q3TableItem::text; +#else + inline QString text() const { return Q3TableItem::text(); } +#endif + QString text(int i) const; + virtual void setEditable(bool b); + bool isEditable() const; + virtual void setStringList(const QStringList &l); + + int rtti() const; + static int RTTI; + + QSize sizeHint() const; + +private: + Q3ComboBox *cb; + QStringList entries; + int current; + bool edit; + static Q3ComboBox *fakeCombo; + static QWidget *fakeComboWidget; + static int fakeRef; + +}; + +class Q_COMPAT_EXPORT Q3CheckTableItem : public Q3TableItem +{ +public: + Q3CheckTableItem(Q3Table *table, const QString &txt); + void setText(const QString &t); + virtual QWidget *createEditor() const; + virtual void setContentFromEditor(QWidget *w); + virtual void paint(QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected); + virtual void setChecked(bool b); + bool isChecked() const; + + int rtti() const; + static int RTTI; + + QSize sizeHint() const; + +private: + QCheckBox *cb; + bool checked; + +}; + +class Q_COMPAT_EXPORT Q3Table : public Q3ScrollView +{ + Q_OBJECT + Q_ENUMS(SelectionMode FocusStyle) + Q_PROPERTY(int numRows READ numRows WRITE setNumRows) + Q_PROPERTY(int numCols READ numCols WRITE setNumCols) + Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid) + Q_PROPERTY(bool rowMovingEnabled READ rowMovingEnabled WRITE setRowMovingEnabled) + Q_PROPERTY(bool columnMovingEnabled READ columnMovingEnabled WRITE setColumnMovingEnabled) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) + Q_PROPERTY(bool sorting READ sorting WRITE setSorting) + Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) + Q_PROPERTY(FocusStyle focusStyle READ focusStyle WRITE setFocusStyle) + Q_PROPERTY(int numSelections READ numSelections) + + friend class Q3TableHeader; + friend class Q3ComboTableItem; + friend class Q3CheckTableItem; + friend class Q3TableItem; + +public: + Q3Table(QWidget* parent=0, const char* name=0); + Q3Table(int numRows, int numCols, + QWidget* parent=0, const char* name=0); + ~Q3Table(); + + Q3Header *horizontalHeader() const; + Q3Header *verticalHeader() const; + + enum SelectionMode { Single, Multi, SingleRow, MultiRow, NoSelection }; + virtual void setSelectionMode(SelectionMode mode); + SelectionMode selectionMode() const; + + virtual void setItem(int row, int col, Q3TableItem *item); + virtual void setText(int row, int col, const QString &text); + virtual void setPixmap(int row, int col, const QPixmap &pix); + virtual Q3TableItem *item(int row, int col) const; + virtual QString text(int row, int col) const; + virtual QPixmap pixmap(int row, int col) const; + virtual void clearCell(int row, int col); + + virtual QRect cellGeometry(int row, int col) const; + virtual int columnWidth(int col) const; + virtual int rowHeight(int row) const; + virtual int columnPos(int col) const; + virtual int rowPos(int row) const; + virtual int columnAt(int x) const; + virtual int rowAt(int y) const; + + virtual int numRows() const; + virtual int numCols() const; + + void updateCell(int row, int col); + + bool eventFilter(QObject * o, QEvent *); + + int currentRow() const { return curRow; } + int currentColumn() const { return curCol; } + void ensureCellVisible(int row, int col); + + bool isSelected(int row, int col) const; + bool isRowSelected(int row, bool full = false) const; + bool isColumnSelected(int col, bool full = false) const; + int numSelections() const; + Q3TableSelection selection(int num) const; + virtual int addSelection(const Q3TableSelection &s); + virtual void removeSelection(const Q3TableSelection &s); + virtual void removeSelection(int num); + virtual int currentSelection() const; + + void selectCells(int start_row, int start_col, int end_row, int end_col); + virtual void selectRow(int row); + virtual void selectColumn(int col); + + bool showGrid() const; + + QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + bool columnMovingEnabled() const; + bool rowMovingEnabled() const; + + virtual void sortColumn(int col, bool ascending = true, + bool wholeRows = false); + bool sorting() const; + + virtual void takeItem(Q3TableItem *i); + + virtual void setCellWidget(int row, int col, QWidget *e); + virtual QWidget *cellWidget(int row, int col) const; + virtual void clearCellWidget(int row, int col); + + virtual QRect cellRect(int row, int col) const; + + virtual void paintCell(QPainter *p, int row, int col, + const QRect &cr, bool selected); + virtual void paintCell(QPainter *p, int row, int col, + const QRect &cr, bool selected, const QColorGroup &cg); + virtual void paintFocus(QPainter *p, const QRect &r); + QSize sizeHint() const; + + bool isReadOnly() const; + bool isRowReadOnly(int row) const; + bool isColumnReadOnly(int col) const; + + void setEnabled(bool b); + + void repaintSelections(); + + enum FocusStyle { FollowStyle, SpreadSheet }; + virtual void setFocusStyle(FocusStyle fs); + FocusStyle focusStyle() const; + + void updateHeaderStates(); + + bool isRowHidden(int row) const; + bool isColumnHidden(int col) const; + bool isColumnStretchable(int col) const; + bool isRowStretchable(int row) const; + bool dragEnabled() const; + +public Q_SLOTS: + virtual void setNumRows(int r); + virtual void setNumCols(int r); + virtual void setShowGrid(bool b); + virtual void hideRow(int row); + virtual void hideColumn(int col); + virtual void showRow(int row); + virtual void showColumn(int col); + + virtual void setColumnWidth(int col, int w); + virtual void setRowHeight(int row, int h); + + virtual void adjustColumn(int col); + virtual void adjustRow(int row); + + virtual void setColumnStretchable(int col, bool stretch); + virtual void setRowStretchable(int row, bool stretch); + virtual void setSorting(bool b); + virtual void swapRows(int row1, int row2, bool swapHeader = false); + virtual void swapColumns(int col1, int col2, bool swapHeader = false); + virtual void swapCells(int row1, int col1, int row2, int col2); + + virtual void setLeftMargin(int m); + virtual void setTopMargin(int m); + virtual void setCurrentCell(int row, int col); + void clearSelection(bool repaint = true); + virtual void setColumnMovingEnabled(bool b); + virtual void setRowMovingEnabled(bool b); + + virtual void setReadOnly(bool b); + virtual void setRowReadOnly(int row, bool ro); + virtual void setColumnReadOnly(int col, bool ro); + + virtual void setDragEnabled(bool b); + + virtual void insertRows(int row, int count = 1); + virtual void insertColumns(int col, int count = 1); + virtual void removeRow(int row); + virtual void removeRows(const Q3MemArray<int> &rows); + virtual void removeColumn(int col); + virtual void removeColumns(const Q3MemArray<int> &cols); + + virtual void editCell(int row, int col, bool replace = false); + + void setRowLabels(const QStringList &labels); + void setColumnLabels(const QStringList &labels); + +protected: + enum EditMode { NotEditing, Editing, Replacing }; + void drawContents(QPainter *p, int cx, int cy, int cw, int ch); + void contentsMousePressEvent(QMouseEvent*); + void contentsMouseMoveEvent(QMouseEvent*); + void contentsMouseDoubleClickEvent(QMouseEvent*); + void contentsMouseReleaseEvent(QMouseEvent*); + void contentsContextMenuEvent(QContextMenuEvent * e); + void keyPressEvent(QKeyEvent*); + void focusInEvent(QFocusEvent*); + void focusOutEvent(QFocusEvent*); + void viewportResizeEvent(QResizeEvent *); + void showEvent(QShowEvent *e); + void paintEvent(QPaintEvent *e); + void setEditMode(EditMode mode, int row, int col); +#ifndef QT_NO_DRAGANDDROP + virtual void contentsDragEnterEvent(QDragEnterEvent *e); + virtual void contentsDragMoveEvent(QDragMoveEvent *e); + virtual void contentsDragLeaveEvent(QDragLeaveEvent *e); + virtual void contentsDropEvent(QDropEvent *e); + virtual Q3DragObject *dragObject(); + virtual void startDrag(); +#endif + + virtual void paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch); + virtual void activateNextCell(); + virtual QWidget *createEditor(int row, int col, bool initFromCell) const; + virtual void setCellContentFromEditor(int row, int col); + virtual QWidget *beginEdit(int row, int col, bool replace); + virtual void endEdit(int row, int col, bool accept, bool replace); + + virtual void resizeData(int len); + virtual void insertWidget(int row, int col, QWidget *w); + int indexOf(int row, int col) const; + + void windowActivationChange(bool); + bool isEditing() const; + EditMode editMode() const; + int currEditRow() const; + int currEditCol() const; + +protected Q_SLOTS: + virtual void columnWidthChanged(int col); + virtual void rowHeightChanged(int row); + virtual void columnIndexChanged(int section, int fromIndex, int toIndex); + virtual void rowIndexChanged(int section, int fromIndex, int toIndex); + virtual void columnClicked(int col); + +Q_SIGNALS: + void currentChanged(int row, int col); + void clicked(int row, int col, int button, const QPoint &mousePos); + void doubleClicked(int row, int col, int button, const QPoint &mousePos); + void pressed(int row, int col, int button, const QPoint &mousePos); + void selectionChanged(); + void valueChanged(int row, int col); + void contextMenuRequested(int row, int col, const QPoint &pos); +#ifndef QT_NO_DRAGANDDROP + void dropped(QDropEvent *e); +#endif + +private Q_SLOTS: + void doAutoScroll(); + void doValueChanged(); + void updateGeometriesSlot(); + +private: + void contentsMousePressEventEx(QMouseEvent*); + void drawContents(QPainter*); + void updateGeometries(); + void repaintSelections(Q3TableSelection *oldSelection, + Q3TableSelection *newSelection, + bool updateVertical = true, + bool updateHorizontal = true); + QRect rangeGeometry(int topRow, int leftCol, + int bottomRow, int rightCol, bool &optimize); + void fixRow(int &row, int y); + void fixCol(int &col, int x); + + void init(int numRows, int numCols); + QSize tableSize() const; + void repaintCell(int row, int col); + void contentsToViewport2(int x, int y, int& vx, int& vy); + QPoint contentsToViewport2(const QPoint &p); + void viewportToContents2(int vx, int vy, int& x, int& y); + QPoint viewportToContents2(const QPoint &p); + + void updateRowWidgets(int row); + void updateColWidgets(int col); + bool isSelected(int row, int col, bool includeCurrent) const; + void setCurrentCell(int row, int col, bool updateSelections, bool ensureVisible = false); + void fixCell(int &row, int &col, int key); + void delayedUpdateGeometries(); + struct TableWidget + { + TableWidget(QWidget *w, int r, int c) : wid(w), row(r), col (c) {} + QWidget *wid; + int row, col; + }; + void saveContents(Q3PtrVector<Q3TableItem> &tmp, + Q3PtrVector<TableWidget> &tmp2); + void updateHeaderAndResizeContents(Q3TableHeader *header, + int num, int colRow, + int width, bool &updateBefore); + void restoreContents(Q3PtrVector<Q3TableItem> &tmp, + Q3PtrVector<TableWidget> &tmp2); + void finishContentsResze(bool updateBefore); + +private: + Q3PtrVector<Q3TableItem> contents; + Q3PtrVector<QWidget> widgets; + int curRow; + int curCol; + Q3TableHeader *leftHeader, *topHeader; + EditMode edMode; + int editCol, editRow; + Q3PtrList<Q3TableSelection> selections; + Q3TableSelection *currentSel; + QTimer *autoScrollTimer; + int lastSortCol; + bool sGrid : 1; + bool mRows : 1; + bool mCols : 1; + bool asc : 1; + bool doSort : 1; + bool unused : 1; + bool readOnly : 1; + bool shouldClearSelection : 1; + bool dEnabled : 1; + bool context_menu : 1; + bool drawActiveSelection : 1; + bool was_visible : 1; + SelectionMode selMode; + int pressedRow, pressedCol; + Q3TablePrivate *d; + Q3IntDict<int> roRows; + Q3IntDict<int> roCols; + int startDragRow; + int startDragCol; + QPoint dragStartPos; + int oldCurrentRow, oldCurrentCol; + FocusStyle focusStl; + + Q_DISABLE_COPY(Q3Table) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3TABLE_H |