diff -r 000000000000 -r 1918ee327afb src/qt3support/itemviews/q3listview.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/qt3support/itemviews/q3listview.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,7949 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#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 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 drawables; + int topPixel; + int bottomPixel; + + QList dirtyItems; + + Q3ListView::SelectionMode selectionMode; + + // Per-column structure for information not in the Q3Header + struct Column { + Q3ListView::WidthMode wmode; + }; + QVector 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 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 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(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 stack; + Q3ListViewPrivate::DrawableItem di(((int)d->rootIsExpandable)-1, 0, d->r); + stack.push(di); + + Q3ListView *that = const_cast(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); + if (wasUpdatesEnabled) + viewport()->setUpdatesEnabled(true); + + 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(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(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(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 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::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 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 + 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