diff options
Diffstat (limited to 'src/qt3support/itemviews/q3iconview.cpp')
-rw-r--r-- | src/qt3support/itemviews/q3iconview.cpp | 6203 |
1 files changed, 6203 insertions, 0 deletions
diff --git a/src/qt3support/itemviews/q3iconview.cpp b/src/qt3support/itemviews/q3iconview.cpp new file mode 100644 index 0000000..d1a9c1e --- /dev/null +++ b/src/qt3support/itemviews/q3iconview.cpp @@ -0,0 +1,6203 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglobal.h" +#if defined(Q_CC_BOR) +// needed for qsort() because of a std namespace problem on Borland +#include "qplatformdefs.h" +#endif + +#include "q3iconview.h" + +#ifndef QT_NO_ICONVIEW + +#include "private/q3richtext_p.h" +#include "q3textedit.h" +#include "qapplication.h" +#include "qbitmap.h" +#include "qbrush.h" +#include "q3cleanuphandler.h" +#include "qcursor.h" +#include "qevent.h" +#include "qfontmetrics.h" +#include "qhash.h" +#include "qimage.h" +#include "qmime.h" +#include "qpainter.h" +#include "qpalette.h" +#include "qpen.h" +#include "qpixmapcache.h" +#include "qstringlist.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtimer.h" +#include "qtooltip.h" +#include "q3strlist.h" + +#include <limits.h> +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +#define RECT_EXTENSION 300 + +static const char * const unknown_xpm[] = { + "32 32 11 1", + "c c #ffffff", + "g c #c0c0c0", + "a c #c0ffc0", + "h c #a0a0a4", + "d c #585858", + "f c #303030", + "i c #400000", + "b c #00c000", + "e c #000000", + "# c #000000", + ". c None", + "...###..........................", + "...#aa##........................", + ".###baaa##......................", + ".#cde#baaa##....................", + ".#cccdeebaaa##..##f.............", + ".#cccccdeebaaa##aaa##...........", + ".#cccccccdeebaaaaaaaa##.........", + ".#cccccccccdeebaaaaaaa#.........", + ".#cccccgcgghhebbbbaaaaa#........", + ".#ccccccgcgggdebbbbbbaa#........", + ".#cccgcgcgcgghdeebiebbba#.......", + ".#ccccgcggggggghdeddeeba#.......", + ".#cgcgcgcggggggggghghdebb#......", + ".#ccgcggggggggghghghghd#b#......", + ".#cgcgcggggggggghghghhd#b#......", + ".#gcggggggggghghghhhhhd#b#......", + ".#cgcggggggggghghghhhhd#b#......", + ".#ggggggggghghghhhhhhhdib#......", + ".#gggggggggghghghhhhhhd#b#......", + ".#hhggggghghghhhhhhhhhd#b#......", + ".#ddhhgggghghghhhhhhhhd#b#......", + "..##ddhhghghhhhhhhhhhhdeb#......", + "....##ddhhhghhhhhhhhhhd#b#......", + "......##ddhhhhhhhhhhhhd#b#......", + "........##ddhhhhhhhhhhd#b#......", + "..........##ddhhhhhhhhd#b#......", + "............##ddhhhhhhd#b###....", + "..............##ddhhhhd#b#####..", + "................##ddhhd#b######.", + "..................##dddeb#####..", + "....................##d#b###....", + "......................####......"}; + +static QPixmap *unknown_icon = 0; +static QPixmap *qiv_buffer_pixmap = 0; +#if !defined(Q_WS_X11) +static QPixmap *qiv_selection = 0; +#endif +static bool optimize_layout = false; + +static Q3CleanupHandler<QPixmap> qiv_cleanup_pixmap; + + +static QPixmap *get_qiv_buffer_pixmap(const QSize &s) +{ + if (!qiv_buffer_pixmap) { + qiv_buffer_pixmap = new QPixmap(s); + qiv_cleanup_pixmap.add(&qiv_buffer_pixmap); + return qiv_buffer_pixmap; + } + + qiv_buffer_pixmap->resize(s); + return qiv_buffer_pixmap; +} + +#ifndef QT_NO_DRAGANDDROP + +class Q_COMPAT_EXPORT Q3IconDragData +{ +public: + Q3IconDragData(); + Q3IconDragData(const QRect &ir, const QRect &tr); + + QRect pixmapRect() const; + QRect textRect() const; + + void setPixmapRect(const QRect &r); + void setTextRect(const QRect &r); + + QRect iconRect_, textRect_; + QString key_; + + bool operator==(const Q3IconDragData &i) const; +}; + +class Q_COMPAT_EXPORT Q3IconDragDataItem +{ +public: + Q3IconDragDataItem() {} + Q3IconDragDataItem(const Q3IconDragItem &i1, const Q3IconDragData &i2) : data(i1), item(i2) {} + Q3IconDragItem data; + Q3IconDragData item; + bool operator== (const Q3IconDragDataItem&) const; +}; + +class Q3IconDragPrivate +{ +public: + QLinkedList<Q3IconDragDataItem> items; + static bool decode(QMimeSource* e, QLinkedList<Q3IconDragDataItem> &lst); +}; + +#endif + +class Q3IconViewPrivate +{ +public: + Q3IconViewItem *firstItem, *lastItem; + uint count; + Q3IconView::SelectionMode selectionMode; + Q3IconViewItem *currentItem, *tmpCurrentItem, *highlightedItem, + *startDragItem, *pressedItem, *selectAnchor, *renamingItem; + QRect *rubber; + QTimer *scrollTimer, *adjustTimer, *updateTimer, *inputTimer, + *fullRedrawTimer; + int rastX, rastY, spacing; + int dragItems; + QPoint oldDragPos; + Q3IconView::Arrangement arrangement; + Q3IconView::ResizeMode resizeMode; + QSize oldSize; +#ifndef QT_NO_DRAGANDDROP + QLinkedList<Q3IconDragDataItem> iconDragData; +#endif + int numDragItems, cachedW, cachedH; + int maxItemWidth, maxItemTextLength; + QPoint dragStart; + QString currInputString; + Q3IconView::ItemTextPos itemTextPos; +#ifndef QT_NO_CURSOR + QCursor oldCursor; +#endif + int cachedContentsX, cachedContentsY; + QBrush itemTextBrush; + QRegion clipRegion; + QPoint dragStartPos; + QFontMetrics *fm; + int minLeftBearing, minRightBearing; + + uint mousePressed : 1; + uint cleared : 1; + uint dropped : 1; + uint clearing : 1; + uint oldDragAcceptAction : 1; + uint isIconDrag : 1; + uint drawDragShapes : 1; + uint dirty : 1; + uint rearrangeEnabled : 1; + uint reorderItemsWhenInsert : 1; + uint drawAllBack : 1; + uint resortItemsWhenInsert : 1; + uint sortDirection : 1; + uint wordWrapIconText : 1; + uint containerUpdateLocked : 1; + uint firstSizeHint : 1; + uint showTips : 1; + uint pressedSelected : 1; + uint dragging : 1; + uint drawActiveSelection : 1; + uint inMenuMode : 1; + + QPoint dragPos; + QPixmapCache maskCache; + QHash<Q3IconViewItem *, Q3IconViewItem *> selectedItems; + + struct ItemContainer { + ItemContainer(ItemContainer *pr, ItemContainer *nx, const QRect &r) + : p(pr), n(nx), rect(r) + { + if (p) + p->n = this; + if (n) + n->p = this; + } + ItemContainer *p, *n; + QRect rect; + QList<Q3IconViewItem*> items; + } *firstContainer, *lastContainer; + + struct SortableItem { + Q3IconViewItem *item; + }; + +public: + + /* finds the containers that intersect with \a searchRect in the direction \a dir relative to \a relativeTo */ + QList<ItemContainer* >* findContainers( + Q3IconView:: Direction dir, + const QPoint &relativeTo, + const QRect &searchRect) const; + // friend int cmpIconViewItems(const void *n1, const void *n2); +}; + + +QList<Q3IconViewPrivate::ItemContainer *>* Q3IconViewPrivate::findContainers( + Q3IconView:: Direction dir, + const QPoint &relativeTo, + const QRect &searchRect) const +{ + + QList<Q3IconViewPrivate::ItemContainer *>* list = + new QList<Q3IconViewPrivate::ItemContainer*>(); + + if (arrangement == Q3IconView::LeftToRight) { + if (dir == Q3IconView::DirLeft || dir == Q3IconView::DirRight) { + ItemContainer *c = firstContainer; + for (; c; c = c->n) + if (c->rect.intersects(searchRect)) + list->append(c); + } else { + if (dir == Q3IconView::DirDown) { + ItemContainer *c = firstContainer; + for (; c; c = c->n) + if (c->rect.intersects(searchRect) && + c->rect.bottom() >= relativeTo.y()) + list->append(c); + } else { + ItemContainer *c = lastContainer; + for (; c; c = c->p) + if (c->rect.intersects(searchRect) && + c->rect.top() <= relativeTo.y()) + list->append(c); + } + } + } else { + if (dir == Q3IconView::DirUp || dir == Q3IconView::DirDown) { + ItemContainer *c = firstContainer; + for (; c; c = c->n) + if (c->rect.intersects(searchRect)) + list->append(c); + } else { + if (dir == Q3IconView::DirRight) { + ItemContainer *c = firstContainer; + for (; c; c = c->n) + if (c->rect.intersects(searchRect) && + c->rect.right() >= relativeTo.x()) + list->append(c); + } else { + ItemContainer *c = lastContainer; + for (; c; c = c->p) + if (c->rect.intersects(searchRect) && + c->rect.left() <= relativeTo.x()) + list->append(c); + } + } + } + return list; +} + + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +#ifdef Q_OS_WINCE +static int _cdecl cmpIconViewItems(const void *n1, const void *n2) +#else +static int cmpIconViewItems(const void *n1, const void *n2) +#endif +{ + if (!n1 || !n2) + return 0; + + Q3IconViewPrivate::SortableItem *i1 = (Q3IconViewPrivate::SortableItem *)n1; + Q3IconViewPrivate::SortableItem *i2 = (Q3IconViewPrivate::SortableItem *)n2; + + return i1->item->compare(i2->item); +} + +#if defined(Q_C_CALLBACKS) +} +#endif + +class Q3IconViewItemPrivate +{ +public: + Q3IconViewPrivate::ItemContainer *container1, *container2; +}; + +#ifndef QT_NO_TEXTEDIT + +class Q3IconViewItemLineEdit : public Q3TextEdit +{ + friend class Q3IconViewItem; + +public: + Q3IconViewItemLineEdit(const QString &text, QWidget *parent, Q3IconViewItem *theItem, const char* name=0); + +protected: + void keyPressEvent(QKeyEvent *e); + void focusOutEvent(QFocusEvent *e); + +protected: + Q3IconViewItem *item; + QString startText; + +private: + Q_DISABLE_COPY(Q3IconViewItemLineEdit) +}; + +Q3IconViewItemLineEdit::Q3IconViewItemLineEdit(const QString &text, QWidget *parent, + Q3IconViewItem *theItem, const char *name) + : Q3TextEdit(parent, name), item(theItem), startText(text) +{ + setFrameStyle(QFrame::Plain | QFrame::Box); + setLineWidth(1); + + setHScrollBarMode(AlwaysOff); + setVScrollBarMode(AlwaysOff); + + setWordWrap(WidgetWidth); + setWrapColumnOrWidth(item->iconView()->maxItemWidth() - + (item->iconView()->itemTextPos() == Q3IconView::Bottom ? + 0 : item->pixmapRect().width())); + document()->formatter()->setAllowBreakInWords(true); + resize(200, 200); // ### some size, there should be a forceReformat() + setTextFormat(Qt::PlainText); + setText(text); + setAlignment(Qt::AlignCenter); + + resize(wrapColumnOrWidth() + 2, heightForWidth(wrapColumnOrWidth()) + 2); +} + +void Q3IconViewItemLineEdit::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Escape) { + item->Q3IconViewItem::setText(startText); + item->cancelRenameItem(); + } else if (e->key() == Qt::Key_Enter || + e->key() == Qt::Key_Return) { + item->renameItem(); + } else { + Q3TextEdit::keyPressEvent(e); + sync(); + resize(width(), document()->height() + 2); + + } +} + +void Q3IconViewItemLineEdit::focusOutEvent(QFocusEvent *e) +{ + Q_UNUSED(e) // I need this to get rid of a Borland warning + if (e->reason() != Qt::PopupFocusReason) + item->cancelRenameItem(); +} +#endif + +#ifndef QT_NO_DRAGANDDROP + + +/*! + \class Q3IconDragItem + \brief The Q3IconDragItem class encapsulates a drag item. + \compat + + The Q3IconDrag class uses a list of Q3IconDragItems to support drag + and drop operations. + + In practice a Q3IconDragItem object (or an object of a class derived + from Q3IconDragItem) is created for each icon view item which is + dragged. Each of these Q3IconDragItems is stored in a Q3IconDrag + object. + + See Q3IconView::dragObject() for more information. +*/ + +/*! + Constructs a Q3IconDragItem with no data. +*/ + +Q3IconDragItem::Q3IconDragItem() +{ + ba = "no data"; +} + +/*! + Destructor. +*/ + +Q3IconDragItem::~Q3IconDragItem() +{ +} + +/*! + Returns the data contained in the Q3IconDragItem. +*/ + +QByteArray Q3IconDragItem::data() const +{ + return ba; +} + +/*! + Sets the data for the Q3IconDragItem to the data stored in the + QByteArray \a d. +*/ + +void Q3IconDragItem::setData(const QByteArray &d) +{ + ba = d; +} + +/*! + \internal +*/ + +bool Q3IconDragItem::operator==(const Q3IconDragItem &i) const +{ + return ba == i.ba; +} + +/*! + \reimp +*/ + +bool Q3IconDragDataItem::operator==(const Q3IconDragDataItem &i) const +{ + return (i.item == item && + i.data == data); +} + +/*! + \reimp +*/ + +bool Q3IconDragData::operator==(const Q3IconDragData &i) const +{ + return key_ == i.key_; +} + + +/*! + \class Q3IconDrag + \brief The Q3IconDrag class supports drag and drop operations + within a Q3IconView. + + \compat + + A Q3IconDrag object is used to maintain information about the + positions of dragged items and the data associated with them. + Q3IconViews are able to use this information to paint the dragged + items in the correct positions. Internally, Q3IconDrag stores the + data associated with drag items in Q3IconDragItem objects. + + If you want to use the extended drag and drop functionality of + Q3IconView, create a Q3IconDrag object in a reimplementation of + Q3IconView::dragObject(). Then create a Q3IconDragItem for each item + which should be dragged, set the data it represents with + Q3IconDragItem::setData(), and add each Q3IconDragItem to the drag + object using append(). + + The data in Q3IconDragItems is stored in a QByteArray and is + mime-typed (see QMimeSource and the + \link http://doc.trolltech.com/dnd.html Drag and Drop\endlink + overview). If you want to use your own mime-types derive a class + from Q3IconDrag and reimplement format(), encodedData() and + canDecode(). + + The fileiconview example program demonstrates the use of the + Q3IconDrag class including subclassing and reimplementing + dragObject(), format(), encodedData() and canDecode(). + + \sa QMimeSource::format() +*/ + +/*! + Constructs a drag object called \a name, which is a child of \a + dragSource. + + Note that the drag object will be deleted when \a dragSource is deleted. +*/ + +Q3IconDrag::Q3IconDrag(QWidget * dragSource, const char* name) + : Q3DragObject(dragSource, name) +{ + d = new Q3IconDragPrivate; +} + +/*! + Destructor. +*/ + +Q3IconDrag::~Q3IconDrag() +{ + delete d; +} + +/*! + Append the Q3IconDragItem, \a i, to the Q3IconDrag object's list of + items. You must also supply the geometry of the pixmap, \a pr, and + the textual caption, \a tr. + + \sa Q3IconDragItem +*/ + +void Q3IconDrag::append(const Q3IconDragItem &i, const QRect &pr, const QRect &tr) +{ + d->items.append(Q3IconDragDataItem(i, Q3IconDragData(pr, tr))); +} + +/*! + \reimp +*/ + +const char* Q3IconDrag::format(int i) const +{ + if (i == 0) + return "application/x-qiconlist"; + return 0; +} + +/*! + Returns the encoded data of the drag object if \a mime is + application/x-qiconlist. +*/ + +QByteArray Q3IconDrag::encodedData(const char* mime) const +{ + if (d->items.size() <= 0 || QString::fromLatin1(mime) != + QString::fromLatin1("application/x-qiconlist")) + return QByteArray(); + + QLinkedList<Q3IconDragDataItem>::ConstIterator it = d->items.begin(); + QString s; + for (; it != d->items.end(); ++it) { + QString k(QLatin1String("%1$@@$%2$@@$%3$@@$%4$@@$%5$@@$%6$@@$%7$@@$%8$@@$")); + k = k.arg((*it).item.pixmapRect().x()).arg( + (*it).item.pixmapRect().y()).arg((*it).item.pixmapRect().width()). + arg((*it).item.pixmapRect().height()).arg( + (*it).item.textRect().x()).arg((*it).item.textRect().y()). + arg((*it).item.textRect().width()).arg( + (*it).item.textRect().height()); + k += QString(QLatin1String((*it).data.data())) + QLatin1String("$@@$"); + s += k; + } + + QByteArray a; + a.resize(s.length() + 1); + memcpy(a.data(), s.latin1(), a.size()); + return a; +} + +/*! + Returns true if \a e can be decoded by the Q3IconDrag, otherwise + return false. +*/ + +bool Q3IconDrag::canDecode(QMimeSource* e) +{ + if (e->provides("application/x-qiconlist")) + return true; + return false; +} + +/*! + Decodes the data which is stored (encoded) in \a e and, if + successful, fills the \a list of icon drag items with the decoded + data. Returns true if there was some data, false otherwise. +*/ + +bool Q3IconDragPrivate::decode(QMimeSource* e, QLinkedList<Q3IconDragDataItem> &lst) +{ + QByteArray ba = e->encodedData("application/x-qiconlist"); + if (ba.size()) { + lst.clear(); + // #### unicode clean???? + QString s = QString::fromLatin1(ba); + Q3IconDragDataItem item; + QRect ir, tr; + QStringList l = QStringList::split(QLatin1String("$@@$"), s); + + int i = 0; + QStringList::Iterator it = l.begin(); + for (; it != l.end(); ++it) { + if (i == 0) { + ir.setX((*it).toInt()); + } else if (i == 1) { + ir.setY((*it).toInt()); + } else if (i == 2) { + ir.setWidth((*it).toInt()); + } else if (i == 3) { + ir.setHeight((*it).toInt()); + } else if (i == 4) { + tr.setX((*it).toInt()); + } else if (i == 5) { + tr.setY((*it).toInt()); + } else if (i == 6) { + tr.setWidth((*it).toInt()); + } else if (i == 7) { + tr.setHeight((*it).toInt()); + } else if (i == 8) { + QByteArray d; + d.resize((*it).length()); + memcpy(d.data(), (*it).latin1(), (*it).length()); + item.item.setPixmapRect(ir); + item.item.setTextRect(tr); + item.data.setData(d); + lst.append(item); + } + ++i; + if (i > 8) + i = 0; + } + return true; + } + + return false; +} + +Q3IconDragData::Q3IconDragData() + : iconRect_(), textRect_() +{ +} + +Q3IconDragData::Q3IconDragData(const QRect &ir, const QRect &tr) + : iconRect_(ir), textRect_(tr) +{ +} + +QRect Q3IconDragData::textRect() const +{ + return textRect_; +} + +QRect Q3IconDragData::pixmapRect() const +{ + return iconRect_; +} + +void Q3IconDragData::setPixmapRect(const QRect &r) +{ + iconRect_ = r; +} + +void Q3IconDragData::setTextRect(const QRect &r) +{ + textRect_ = r; +} + +#endif + + +/*! + \class Q3IconViewItem + \brief The Q3IconViewItem class provides a single item in a Q3IconView. + + \compat + + A Q3IconViewItem contains an icon, a string and optionally a sort + key, and can display itself in a Q3IconView. + + The simplest way to create a Q3IconViewItem and insert it into a + Q3IconView is to construct the item passing the constructor a + pointer to the icon view, a string and an icon: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 0 + + By default the text of an icon view item may not be edited by the + user but calling setRenameEnabled(true) will allow the user to + perform in-place editing of the item's text. + + When the icon view is deleted all items in it are deleted + automatically. + + The Q3IconView::firstItem() and Q3IconViewItem::nextItem() functions + provide a means of iterating over all the items in a Q3IconView: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 1 + + The item's icon view is available from iconView(), and its + position in the icon view from index(). + + The item's selection status is available from isSelected() and is + set and controlled by setSelected() and isSelectable(). + + The text and icon can be set with setText() and setPixmap() and + retrieved with text() and pixmap(). The item's sort key defaults + to text() but may be set with setKey() and retrieved with key(). + The comparison function, compare() uses key(). + + Items may be repositioned with move() and moveBy(). An item's + geometry is available from rect(), x(), y(), width(), height(), + size(), pos(), textRect() and pixmapRect(). You can also test + against the position of a point with contains() and intersects(). + + To remove an item from an icon view, just delete the item. The + Q3IconViewItem destructor removes it cleanly from its icon view. + + Because the icon view is designed to use drag-and-drop, the icon + view item also has functions for drag-and-drop which may be + reimplemented. + + The class is designed to be very similar to Q3ListView and Q3ListBox + in use, both via instantiation and subclassing. +*/ + +/*! + Constructs a Q3IconViewItem and inserts it into icon view \a parent + with no text and a default icon. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent) + : view(parent), itemText(), itemIcon(unknown_icon) +{ + init(); +} + +/*! + Constructs a Q3IconViewItem and inserts it into the icon view \a + parent with no text and a default icon, after the icon view item + \a after. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after) + : view(parent), itemText(), itemIcon(unknown_icon), + prev(0), next(0) +{ + init(after); +} + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and a default icon. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, const QString &text) + : view(parent), itemText(text), itemIcon(unknown_icon) +{ + init(0); +} + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and a default icon, after the + icon view item \a after. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, + const QString &text) + : view(parent), itemText(text), itemIcon(unknown_icon) +{ + init(after); +} + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and \a icon as the icon. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, const QString &text, + const QPixmap &icon) + : view(parent), + itemText(text), itemIcon(new QPixmap(icon)) +{ + init(0); +} + + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and \a icon as the icon, after + the icon view item \a after. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, + const QString &text, const QPixmap &icon) + : view(parent), itemText(text), itemIcon(new QPixmap(icon)) +{ + init(after); +} + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and \a picture as the icon. +*/ + +#ifndef QT_NO_PICTURE +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, const QString &text, + const QPicture &picture) + : view(parent), itemText(text), itemIcon(0) +{ + init(0, new QPicture(picture)); +} + +/*! + Constructs an icon view item and inserts it into the icon view \a + parent using \a text as the text and \a picture as the icon, after + the icon view item \a after. +*/ + +Q3IconViewItem::Q3IconViewItem(Q3IconView *parent, Q3IconViewItem *after, + const QString &text, const QPicture &picture) + : view(parent), itemText(text), itemIcon(0) +{ + init(after, new QPicture(picture)); +} +#endif + +/*! + This private function initializes the icon view item and inserts it + into the icon view. +*/ + +void Q3IconViewItem::init(Q3IconViewItem *after +#ifndef QT_NO_PICTURE + , QPicture *pic +#endif + ) +{ + d = new Q3IconViewItemPrivate; + d->container1 = 0; + d->container2 = 0; + prev = next = 0; + allow_rename = false; + allow_drag = true; + allow_drop = true; + selected = false; + selectable = true; +#ifndef QT_NO_TEXTEDIT + renameBox = 0; +#endif +#ifndef QT_NO_PICTURE + itemPic = pic; +#endif + if (view) { + itemKey = itemText; + dirty = true; + wordWrapDirty = true; + itemRect = QRect(-1, -1, 0, 0); + calcRect(); + view->insertItem(this, after); + } +} + +/*! + Destroys the icon view item and tells the parent icon view that + the item has been destroyed. +*/ + +Q3IconViewItem::~Q3IconViewItem() +{ +#ifndef QT_NO_TEXTEDIT + removeRenameBox(); +#endif + if (view && !view->d->clearing) + view->takeItem(this); + view = 0; + if (itemIcon && itemIcon->serialNumber() != unknown_icon->serialNumber()) + delete itemIcon; +#ifndef QT_NO_PICTURE + delete itemPic; +#endif + delete d; +} + +int Q3IconViewItem::RTTI = 0; + +/*! + Returns 0. + + Make your derived classes return their own values for rtti(), so + that you can distinguish between icon view item types. You should + use values greater than 1000, preferably a large random number, to + allow for extensions to this class. +*/ + +int Q3IconViewItem::rtti() const +{ + return RTTI; +} + + +/*! + Sets \a text as the text of the icon view item. This function + might be a no-op if you reimplement text(). + + \sa text() +*/ + +void Q3IconViewItem::setText(const QString &text) +{ + if (text == itemText) + return; + + wordWrapDirty = true; + itemText = text; + if (itemKey.isEmpty()) + itemKey = itemText; + + QRect oR = rect(); + calcRect(); + oR = oR.united(rect()); + + if (view) { + if (QRect(view->contentsX(), view->contentsY(), + view->visibleWidth(), view->visibleHeight()). + intersects(oR)) + view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2); + } +} + +/*! + Sets \a k as the sort key of the icon view item. By default + text() is used for sorting. + + \sa compare() +*/ + +void Q3IconViewItem::setKey(const QString &k) +{ + if (k == itemKey) + return; + + itemKey = k; +} + +/*! + Sets \a icon as the item's icon in the icon view. This function + might be a no-op if you reimplement pixmap(). + + \sa pixmap() +*/ + +void Q3IconViewItem::setPixmap(const QPixmap &icon) +{ + if (itemIcon && itemIcon == unknown_icon) + itemIcon = 0; + + if (itemIcon) + *itemIcon = icon; + else + itemIcon = new QPixmap(icon); + QRect oR = rect(); + calcRect(); + oR = oR.united(rect()); + + if (view) { + if (QRect(view->contentsX(), view->contentsY(), + view->visibleWidth(), view->visibleHeight()). + intersects(oR)) + view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2); + } +} + +/*! + Sets \a icon as the item's icon in the icon view. This function + might be a no-op if you reimplement picture(). + + \sa picture() +*/ + +#ifndef QT_NO_PICTURE +void Q3IconViewItem::setPicture(const QPicture &icon) +{ + // clear assigned pixmap if any + if (itemIcon) { + if (itemIcon == unknown_icon) { + itemIcon = 0; + } else { + delete itemIcon; + itemIcon = 0; + } + } + if (itemPic) + delete itemPic; + itemPic = new QPicture(icon); + + QRect oR = rect(); + calcRect(); + oR = oR.united(rect()); + + if (view) { + if (QRect(view->contentsX(), view->contentsY(), + view->visibleWidth(), view->visibleHeight()). + intersects(oR)) + view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2); + } +} +#endif + +/*! + \overload + + Sets \a text as the text of the icon view item. If \a recalc is + true, the icon view's layout is recalculated. If \a redraw is true + (the default), the icon view is repainted. + + \sa text() +*/ + +void Q3IconViewItem::setText(const QString &text, bool recalc, bool redraw) +{ + if (text == itemText) + return; + + wordWrapDirty = true; + itemText = text; + + if (recalc) + calcRect(); + if (redraw) + repaint(); +} + +/*! + \overload + + Sets \a icon as the item's icon in the icon view. If \a recalc is + true, the icon view's layout is recalculated. If \a redraw is true + (the default), the icon view is repainted. + + \sa pixmap() +*/ + +void Q3IconViewItem::setPixmap(const QPixmap &icon, bool recalc, bool redraw) +{ + if (itemIcon && itemIcon == unknown_icon) + itemIcon = 0; + + if (itemIcon) + *itemIcon = icon; + else + itemIcon = new QPixmap(icon); + + if (redraw) { + if (recalc) { + QRect oR = rect(); + calcRect(); + oR = oR.united(rect()); + + if (view) { + if (QRect(view->contentsX(), view->contentsY(), + view->visibleWidth(), view->visibleHeight()). + intersects(oR)) + view->repaintContents(oR.x() - 1, oR.y() - 1, oR.width() + 2, oR.height() + 2); + } + } else { + repaint(); + } + } else if (recalc) { + calcRect(); + } +} + +/*! + If \a allow is true, the user can rename the icon view item by + clicking on the text (or pressing F2) while the item is selected + (in-place renaming). If \a allow is false, in-place renaming is + not possible. +*/ + +void Q3IconViewItem::setRenameEnabled(bool allow) +{ + allow_rename = (uint)allow; +} + +/*! + If \a allow is true, the icon view permits the user to drag the + icon view item either to another position within the icon view or + to somewhere outside of it. If \a allow is false, the item cannot + be dragged. +*/ + +void Q3IconViewItem::setDragEnabled(bool allow) +{ + allow_drag = (uint)allow; +} + +/*! + If \a allow is true, the icon view lets the user drop something on + this icon view item. +*/ + +void Q3IconViewItem::setDropEnabled(bool allow) +{ + allow_drop = (uint)allow; +} + +/*! + Returns the text of the icon view item. Normally you set the text + of the item with setText(), but sometimes it's inconvenient to + call setText() for every item; so you can subclass Q3IconViewItem, + reimplement this function, and return the text of the item. If you + do this, you must call calcRect() manually each time the text + (and therefore its size) changes. + + \sa setText() +*/ + +QString Q3IconViewItem::text() const +{ + return itemText; +} + +/*! + Returns the key of the icon view item or text() if no key has been + explicitly set. + + \sa setKey(), compare() +*/ + +QString Q3IconViewItem::key() const +{ + return itemKey; +} + +/*! + Returns the icon of the icon view item if it is a pixmap, or 0 if + it is a picture. In the latter case use picture() instead. + Normally you set the pixmap of the item with setPixmap(), but + sometimes it's inconvenient to call setPixmap() for every item. So + you can subclass Q3IconViewItem, reimplement this function and + return a pointer to the item's pixmap. If you do this, you \e must + call calcRect() manually each time the size of this pixmap + changes. + + \sa setPixmap() +*/ + +QPixmap *Q3IconViewItem::pixmap() const +{ + return itemIcon; +} + +/*! + Returns the icon of the icon view item if it is a picture, or 0 if + it is a pixmap. In the latter case use pixmap() instead. Normally + you set the picture of the item with setPicture(), but sometimes + it's inconvenient to call setPicture() for every item. So you can + subclass Q3IconViewItem, reimplement this function and return a + pointer to the item's picture. If you do this, you \e must call + calcRect() manually each time the size of this picture changes. + + \sa setPicture() +*/ + +#ifndef QT_NO_PICTURE +QPicture *Q3IconViewItem::picture() const +{ + return itemPic; +} +#endif + +/*! + Returns true if the item can be renamed by the user with in-place + renaming; otherwise returns false. + + \sa setRenameEnabled() +*/ + +bool Q3IconViewItem::renameEnabled() const +{ + return (bool)allow_rename; +} + +/*! + Returns true if the user is allowed to drag the icon view item; + otherwise returns false. + + \sa setDragEnabled() +*/ + +bool Q3IconViewItem::dragEnabled() const +{ + return (bool)allow_drag; +} + +/*! + Returns true if the user is allowed to drop something onto the + item; otherwise returns false. + + \sa setDropEnabled() +*/ + +bool Q3IconViewItem::dropEnabled() const +{ + return (bool)allow_drop; +} + +/*! + Returns a pointer to this item's icon view parent. +*/ + +Q3IconView *Q3IconViewItem::iconView() const +{ + return view; +} + +/*! + Returns a pointer to the previous item, or 0 if this is the first + item in the icon view. + + \sa nextItem() Q3IconView::firstItem() +*/ + +Q3IconViewItem *Q3IconViewItem::prevItem() const +{ + return prev; +} + +/*! + Returns a pointer to the next item, or 0 if this is the last item + in the icon view. + + To find the first item use Q3IconView::firstItem(). + + Example: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 2 + + \sa prevItem() +*/ + +Q3IconViewItem *Q3IconViewItem::nextItem() const +{ + return next; +} + +/*! + Returns the index of this item in the icon view, or -1 if an error + occurred. +*/ + +int Q3IconViewItem::index() const +{ + if (view) + return view->index(this); + + return -1; +} + + + +/*! + \overload + + This variant is equivalent to calling the other variant with \e cb + set to false. +*/ + +void Q3IconViewItem::setSelected(bool s) +{ + setSelected(s, false); +} + +/*! + Selects or unselects the item, depending on \a s; it may also + unselect other items, depending on Q3IconView::selectionMode() and + \a cb. + + If \a s is false, the item is unselected. + + If \a s is true and Q3IconView::selectionMode() is + Q3IconView::Single, the item is selected and the item previously + selected is unselected. + + If \a s is true and Q3IconView::selectionMode() is + Q3IconView::Extended, the item is selected. If \a cb is true, the + selection state of the other items is left unchanged. If \a cb is + false (the default) all other items are unselected. + + If \a s is true and Q3IconView::selectionMode() is + Q3IconView::Multi, the item is selected. + + Note that \a cb is used only if Q3IconView::selectionMode() is + Q3IconView::Extended; cb defaults to false. + + All items whose selection status changes repaint themselves. +*/ + +void Q3IconViewItem::setSelected(bool s, bool cb) +{ + if (!view) + return; + if (view->selectionMode() != Q3IconView::NoSelection && + selectable && s != (bool)selected) { + + if (view->d->selectionMode == Q3IconView::Single && this != view->d->currentItem) { + Q3IconViewItem *o = view->d->currentItem; + if (o && o->selected) + o->selected = false; + view->d->currentItem = this; + if (o) + o->repaint(); + emit view->currentChanged(this); + } + + if (!s) { + selected = false; + } else { + if (view->d->selectionMode == Q3IconView::Single && view->d->currentItem) { + view->d->currentItem->selected = false; + } + if ((view->d->selectionMode == Q3IconView::Extended && !cb) || + view->d->selectionMode == Q3IconView::Single) { + bool b = view->signalsBlocked(); + view->blockSignals(true); + view->selectAll(false); + view->blockSignals(b); + } + selected = s; + } + + repaint(); + if (!view->signalsBlocked()) { + bool emitIt = view->d->selectionMode == Q3IconView::Single && s; + Q3IconView *v = view; + emit v->selectionChanged(); + if (emitIt) + emit v->selectionChanged(this); + } + } +} + +/*! + Sets this item to be selectable if \a enable is true (the default) + or unselectable if \a enable is false. + + The user is unable to select a non-selectable item using either + the keyboard or the mouse. (The application programmer can select + an item in code regardless of this setting.) + + \sa isSelectable() +*/ + +void Q3IconViewItem::setSelectable(bool enable) +{ + selectable = (uint)enable; +} + +/*! + Returns true if the item is selected; otherwise returns false. + + \sa setSelected() +*/ + +bool Q3IconViewItem::isSelected() const +{ + return (bool)selected; +} + +/*! + Returns true if the item is selectable; otherwise returns false. + + \sa setSelectable() +*/ + +bool Q3IconViewItem::isSelectable() const +{ + return (bool)selectable; +} + +/*! + Repaints the item. +*/ + +void Q3IconViewItem::repaint() +{ + if (view) + view->repaintItem(this); +} + +/*! + Moves the item to position (\a x, \a y) in the icon view (these + are contents coordinates). Returns true if the item is moved. + Returns false if the item is already at the specified position. +*/ + +bool Q3IconViewItem::move(int x, int y) +{ + if (x == this->x() && y == this->y()) + return false; + itemRect.setRect(x, y, itemRect.width(), itemRect.height()); + checkRect(); + if (view) + view->updateItemContainer(this); + return true; +} + +/*! + Moves the item \a dx pixels in the x-direction and \a dy pixels in + the y-direction. +*/ + +void Q3IconViewItem::moveBy(int dx, int dy) +{ + itemRect.moveBy(dx, dy); + checkRect(); + if (view) + view->updateItemContainer(this); +} + +/*! + \overload + + Moves the item to the point \a pnt. +*/ + +bool Q3IconViewItem::move(const QPoint &pnt) +{ + return move(pnt.x(), pnt.y()); +} + +/*! + \overload + + Moves the item by the x, y values in point \a pnt. +*/ + +void Q3IconViewItem::moveBy(const QPoint &pnt) +{ + moveBy(pnt.x(), pnt.y()); +} + +/*! + Returns the bounding rectangle of the item (in contents + coordinates). +*/ + +QRect Q3IconViewItem::rect() const +{ + return itemRect; +} + +/*! + Returns the x-coordinate of the item (in contents coordinates). +*/ + +int Q3IconViewItem::x() const +{ + return itemRect.x(); +} + +/*! + Returns the y-coordinate of the item (in contents coordinates). +*/ + +int Q3IconViewItem::y() const +{ + return itemRect.y(); +} + +/*! + Returns the width of the item. +*/ + +int Q3IconViewItem::width() const +{ + return qMax(itemRect.width(), QApplication::globalStrut().width()); +} + +/*! + Returns the height of the item. +*/ + +int Q3IconViewItem::height() const +{ + return qMax(itemRect.height(), QApplication::globalStrut().height()); +} + +/*! + Returns the size of the item. +*/ + +QSize Q3IconViewItem::size() const +{ + return QSize(itemRect.width(), itemRect.height()); +} + +/*! + Returns the position of the item (in contents coordinates). +*/ + +QPoint Q3IconViewItem::pos() const +{ + return QPoint(itemRect.x(), itemRect.y()); +} + +/*! + Returns the bounding rectangle of the item's text. + + If \a relative is true, (the default), the returned rectangle is + relative to the origin of the item's rectangle. If \a relative is + false, the returned rectangle is relative to the origin of the + icon view's contents coordinate system. +*/ + +QRect Q3IconViewItem::textRect(bool relative) const +{ + if (relative) + return itemTextRect; + else + return QRect(x() + itemTextRect.x(), y() + itemTextRect.y(), itemTextRect.width(), itemTextRect.height()); +} + +/*! + Returns the bounding rectangle of the item's icon. + + If \a relative is true, (the default), the rectangle is relative to + the origin of the item's rectangle. If \a relative is false, the + returned rectangle is relative to the origin of the icon view's + contents coordinate system. +*/ + +QRect Q3IconViewItem::pixmapRect(bool relative) const +{ + if (relative) + return itemIconRect; + else + return QRect(x() + itemIconRect.x(), y() + itemIconRect.y(), itemIconRect.width(), itemIconRect.height()); +} + +/*! + Returns true if the item contains the point \a pnt (in contents + coordinates); otherwise returns false. +*/ + +bool Q3IconViewItem::contains(const QPoint& pnt) const +{ + QRect textArea = textRect(false); + QRect pixmapArea = pixmapRect(false); + if (iconView()->itemTextPos() == Q3IconView::Bottom) + textArea.setTop(pixmapArea.bottom()); + else + textArea.setLeft(pixmapArea.right()); + return textArea.contains(pnt) || pixmapArea.contains(pnt); +} + +/*! + Returns true if the item intersects the rectangle \a r (in + contents coordinates); otherwise returns false. +*/ + +bool Q3IconViewItem::intersects(const QRect& r) const +{ + return (textRect(false).intersects(r) || + pixmapRect(false).intersects(r)); +} + +/*! + \fn bool Q3IconViewItem::acceptDrop(const QMimeSource *mime) const + + Returns true if you can drop things with a QMimeSource of \a mime + onto this item; otherwise returns false. + + The default implementation always returns false. You must subclass + Q3IconViewItem and reimplement acceptDrop() to accept drops. +*/ + +bool Q3IconViewItem::acceptDrop(const QMimeSource *) const +{ + return false; +} + +#ifndef QT_NO_TEXTEDIT +/*! + Starts in-place renaming of an icon, if allowed. + + This function sets up the icon view so that the user can edit the + item text, and then returns. When the user is done, setText() will + be called and Q3IconView::itemRenamed() will be emitted (unless the + user canceled, e.g. by pressing the Escape key). + + \sa setRenameEnabled() +*/ + +void Q3IconViewItem::rename() +{ + if (!view) + return; + if (renameBox) + removeRenameBox(); + oldRect = rect(); + renameBox = new Q3IconViewItemLineEdit(itemText, view->viewport(), this, "qt_renamebox"); + iconView()->ensureItemVisible(this); + QRect tr(textRect(false)); + view->addChild(renameBox, tr.x() + (tr.width() / 2 - renameBox->width() / 2), tr.y() - 3); + renameBox->selectAll(); + view->viewport()->setFocusProxy(renameBox); + renameBox->setFocus(); + renameBox->show(); + Q_ASSERT(view->d->renamingItem == 0L); + view->d->renamingItem = this; +} +#endif + +/*! + Compares this icon view item to \a i. Returns -1 if this item is + less than \a i, 0 if they are equal, and 1 if this icon view item + is greater than \a i. + + The default implementation compares the item keys (key()) using + QString::localeAwareCompare(). A reimplementation may use + different values and a different comparison function. Here is a + reimplementation that uses plain Unicode comparison: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 3 + + \sa key() QString::localeAwareCompare() QString::compare() +*/ + +int Q3IconViewItem::compare(Q3IconViewItem *i) const +{ + return key().localeAwareCompare(i->key()); +} + +#ifndef QT_NO_TEXTEDIT +/*! + This private function is called when the user pressed Return during + in-place renaming. +*/ + +void Q3IconViewItem::renameItem() +{ + if (!renameBox || !view) + return; + + if (!view->d->wordWrapIconText) { + wordWrapDirty = true; + calcRect(); + } + QRect r = itemRect; + setText(renameBox->text()); + view->repaintContents(oldRect.x() - 1, oldRect.y() - 1, oldRect.width() + 2, oldRect.height() + 2); + view->repaintContents(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2); + removeRenameBox(); + + view->emitRenamed(this); +} + +/*! + Cancels in-place renaming. +*/ + +void Q3IconViewItem::cancelRenameItem() +{ + if (!view) + return; + + QRect r = itemRect; + calcRect(); + view->repaintContents(oldRect.x() - 1, oldRect.y() - 1, oldRect.width() + 2, oldRect.height() + 2); + view->repaintContents(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2); + + if (!renameBox) + return; + + removeRenameBox(); +} + +/*! + Removes the editbox that is used for in-place renaming. +*/ + +void Q3IconViewItem::removeRenameBox() +{ + if (!renameBox || !view) + return; + + bool resetFocus = view->viewport()->focusProxy() == renameBox; + renameBox->hide(); + renameBox->deleteLater(); + renameBox = 0; + if (resetFocus) { + view->viewport()->setFocusProxy(view); + view->setFocus(); + } + Q_ASSERT(view->d->renamingItem == this); + view->d->renamingItem = 0L; +} +#endif + +/*! + This virtual function is responsible for calculating the + rectangles returned by rect(), textRect() and pixmapRect(). + setRect(), setTextRect() and setPixmapRect() are provided mainly + for reimplementations of this function. + + \a text_ is an internal parameter which defaults to an empty + string. +*/ + +void Q3IconViewItem::calcRect(const QString &text_) +{ + if (!view) // ##### + return; + + wordWrapDirty = true; + int pw = 0; + int ph = 0; + +#ifndef QT_NO_PICTURE + if (picture()) { + QRect br = picture()->boundingRect(); + pw = br.width() + 2; + ph = br.height() + 2; + } else +#endif + { + pw = (pixmap() ? pixmap() : unknown_icon)->width() + 2; + ph = (pixmap() ? pixmap() : unknown_icon)->height() + 2; + } + + itemIconRect.setWidth(pw); + itemIconRect.setHeight(ph); + + calcTmpText(); + + QString t = text_; + if (t.isEmpty()) { + if (view->d->wordWrapIconText) + t = itemText; + else + t = tmpText; + } + + int tw = 0; + int th = 0; + // ##### TODO: fix font bearings! + QRect r; + if (view->d->wordWrapIconText) { + r = QRect(view->d->fm->boundingRect(0, 0, iconView()->maxItemWidth() - + (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 : + pixmapRect().width()), + 0xFFFFFFFF, Qt::AlignHCenter | Qt::WordBreak | Qt::BreakAnywhere, t)); + r.setWidth(r.width() + 4); + } else { + r = QRect(0, 0, view->d->fm->width(t), view->d->fm->height()); + r.setWidth(r.width() + 4); + } + + if (r.width() > iconView()->maxItemWidth() - + (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 : + pixmapRect().width())) + r.setWidth(iconView()->maxItemWidth() - (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 : + pixmapRect().width())); + + tw = r.width(); + th = r.height(); + if (tw < view->d->fm->width(QLatin1String("X"))) + tw = view->d->fm->width(QLatin1String("X")); + + itemTextRect.setWidth(tw); + itemTextRect.setHeight(th); + + int w = 0; + int h = 0; + if (view->itemTextPos() == Q3IconView::Bottom) { + w = qMax(itemTextRect.width(), itemIconRect.width()); + h = itemTextRect.height() + itemIconRect.height() + 1; + + itemRect.setWidth(w); + itemRect.setHeight(h); + + itemTextRect = QRect((width() - itemTextRect.width()) / 2, height() - itemTextRect.height(), + itemTextRect.width(), itemTextRect.height()); + itemIconRect = QRect((width() - itemIconRect.width()) / 2, 0, + itemIconRect.width(), itemIconRect.height()); + } else { + h = qMax(itemTextRect.height(), itemIconRect.height()); + w = itemTextRect.width() + itemIconRect.width() + 1; + + itemRect.setWidth(w); + itemRect.setHeight(h); + + itemTextRect = QRect(width() - itemTextRect.width(), (height() - itemTextRect.height()) / 2, + itemTextRect.width(), itemTextRect.height()); + itemIconRect = QRect(0, (height() - itemIconRect.height()) / 2, + itemIconRect.width(), itemIconRect.height()); + } + if (view) + view->updateItemContainer(this); +} + +/*! + Paints the item using the painter \a p and the color group \a cg. + If you want the item to be drawn with a different font or color, + reimplement this function, change the values of the color group or + the painter's font, and then call the Q3IconViewItem::paintItem() + with the changed values. +*/ + +void Q3IconViewItem::paintItem(QPainter *p, const QColorGroup &cg) +{ + if (!view) + return; + + p->save(); + + if (isSelected()) { + p->setPen(cg.highlightedText()); + } else { + p->setPen(cg.text()); + } + + calcTmpText(); + +#ifndef QT_NO_PICTURE + if (picture()) { + QPicture *pic = picture(); + if (isSelected()) { + p->fillRect(pixmapRect(false), QBrush(cg.highlight(), Qt::Dense4Pattern)); + } + p->drawPicture(x()-pic->boundingRect().x(), y()-pic->boundingRect().y(), *pic); + if (isSelected()) { + p->fillRect(textRect(false), cg.highlight()); + p->setPen(QPen(cg.highlightedText())); + } else if (view->d->itemTextBrush != QBrush(Qt::NoBrush)) + p->fillRect(textRect(false), view->d->itemTextBrush); + + int align = view->itemTextPos() == Q3IconView::Bottom ? Qt::AlignHCenter : Qt::AlignAuto; + if (view->d->wordWrapIconText) + align |= Qt::WordBreak | Qt::BreakAnywhere; + p->drawText(textRect(false), align, view->d->wordWrapIconText ? itemText : tmpText); + p->restore(); + return; + } +#endif + bool textOnBottom = (view->itemTextPos() == Q3IconView::Bottom); + int dim; + if (textOnBottom) + dim = (pixmap() ? pixmap() : unknown_icon)->width(); + else + dim = (pixmap() ? pixmap() : unknown_icon)->height(); + if (isSelected()) { + QPixmap *pix = pixmap() ? pixmap() : unknown_icon; + if (pix && !pix->isNull()) { + QPixmap *buffer = get_qiv_buffer_pixmap(pix->size()); + QBitmap mask = view->mask(pix); + + QPainter p2(buffer); + p2.fillRect(pix->rect(), Qt::white); + p2.drawPixmap(0, 0, *pix); + p2.end(); + + p2.begin(buffer); + p2.fillRect(pix->rect(), QBrush(cg.highlight(), Qt::Dense4Pattern)); + p2.end(); + buffer->setMask(mask); + + QRect cr = pix->rect(); + if (textOnBottom) + p->drawPixmap(x() + (width() - dim) / 2, y(), *buffer, 0, 0, + cr.width(), cr.height()); + else + p->drawPixmap(x() , y() + (height() - dim) / 2, *buffer, 0, 0, + cr.width(), cr.height()); + } + } else { + if (textOnBottom) + p->drawPixmap(x() + (width() - dim) / 2, y(), + *(pixmap() ? pixmap() : unknown_icon)); + else + p->drawPixmap(x() , y() + (height() - dim) / 2, + *(pixmap() ? pixmap() : unknown_icon)); + } + + p->save(); + if (isSelected()) { + p->fillRect(textRect(false), cg.highlight()); + p->setPen(QPen(cg.highlightedText())); + } else if (view->d->itemTextBrush != QBrush(Qt::NoBrush)) + p->fillRect(textRect(false), view->d->itemTextBrush); + + int align = Qt::AlignHCenter; + if (view->d->wordWrapIconText) + align |= Qt::WordBreak | Qt::BreakAnywhere; + p->drawText(textRect(false), align, + view->d->wordWrapIconText ? itemText : tmpText); + + p->restore(); + + p->restore(); +} + +/*! + Paints the focus rectangle of the item using the painter \a p and + the color group \a cg. +*/ + +void Q3IconViewItem::paintFocus(QPainter *p, const QColorGroup &cg) +{ + if (!view) + return; + + QStyleOptionFocusRect opt; + opt.rect = textRect(false); + opt.palette = cg; + if (isSelected()) { + opt.state = QStyle::State_FocusAtBorder; + opt.backgroundColor = cg.highlight(); + } else { + opt.state = QStyle::State_None; + opt.backgroundColor = cg.base(); + } + view->style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p); + + if (this != view->d->currentItem) { + opt.rect = pixmapRect(false); + opt.backgroundColor = cg.base(); + opt.state = QStyle::State_None; + view->style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p); + } +} + +#ifndef QT_NO_DRAGANDDROP +/*! + \fn void Q3IconViewItem::dropped(QDropEvent *e, const Q3ValueList<Q3IconDragItem> &lst) + + This function is called when something is dropped on the item. \a + e provides all the information about the drop. If the drag object + of the drop was a Q3IconDrag, \a lst contains the list of the + dropped items. You can get the data by calling + Q3IconDragItem::data() on each item. If the \a lst is empty, i.e. + the drag was not a Q3IconDrag, you must decode the data in \a e and + work with that. + + The default implementation does nothing; subclasses may + reimplement this function. +*/ + +void Q3IconViewItem::dropped(QDropEvent *, const Q3ValueList<Q3IconDragItem> &) +{ +} +#endif + +/*! + This function is called when a drag enters the item's bounding + rectangle. + + The default implementation does nothing; subclasses may + reimplement this function. +*/ + +void Q3IconViewItem::dragEntered() +{ +} + +/*! + This function is called when a drag leaves the item's bounding + rectangle. + + The default implementation does nothing; subclasses may + reimplement this function. +*/ + +void Q3IconViewItem::dragLeft() +{ +} + +/*! + Sets the bounding rectangle of the whole item to \a r. This + function is provided for subclasses which reimplement calcRect(), + so that they can set the calculated rectangle. \e{Any other use is + discouraged.} + + \sa calcRect() textRect() setTextRect() pixmapRect() setPixmapRect() +*/ + +void Q3IconViewItem::setItemRect(const QRect &r) +{ + itemRect = r; + checkRect(); + if (view) + view->updateItemContainer(this); +} + +/*! + Sets the bounding rectangle of the item's text to \a r. This + function is provided for subclasses which reimplement calcRect(), + so that they can set the calculated rectangle. \e{Any other use is + discouraged.} + + \sa calcRect() textRect() setItemRect() setPixmapRect() +*/ + +void Q3IconViewItem::setTextRect(const QRect &r) +{ + itemTextRect = r; + if (view) + view->updateItemContainer(this); +} + +/*! + Sets the bounding rectangle of the item's icon to \a r. This + function is provided for subclasses which reimplement calcRect(), + so that they can set the calculated rectangle. \e{Any other use is + discouraged.} + + \sa calcRect() pixmapRect() setItemRect() setTextRect() +*/ + +void Q3IconViewItem::setPixmapRect(const QRect &r) +{ + itemIconRect = r; + if (view) + view->updateItemContainer(this); +} + +/*! + \internal +*/ + +void Q3IconViewItem::calcTmpText() +{ + if (!view || view->d->wordWrapIconText || !wordWrapDirty) + return; + wordWrapDirty = false; + + int w = iconView()->maxItemWidth() - (iconView()->itemTextPos() == Q3IconView::Bottom ? 0 : + pixmapRect().width()); + if (view->d->fm->width(itemText) < w) { + tmpText = itemText; + return; + } + + tmpText = QLatin1String("..."); + int i = 0; + while (view->d->fm->width(tmpText + itemText[i]) < w) + tmpText += itemText[i++]; + tmpText.remove((uint)0, 3); + tmpText += QLatin1String("..."); +} + +/*! \internal */ + +QString Q3IconViewItem::tempText() const +{ + return tmpText; +} + +void Q3IconViewItem::checkRect() +{ + int x = itemRect.x(); + int y = itemRect.y(); + int w = itemRect.width(); + int h = itemRect.height(); + + bool changed = false; + if (x < 0) { + x = 0; + changed = true; + } + if (y < 0) { + y = 0; + changed = true; + } + + if (changed) + itemRect.setRect(x, y, w, h); +} + + +/*! + \class Q3IconView + \brief The Q3IconView class provides an area with movable labelled icons. + + \compat + + A Q3IconView can display and manage a grid or other 2D layout of + labelled icons. Each labelled icon is a Q3IconViewItem. Items + (Q3IconViewItems) can be added or deleted at any time; items can be + moved within the Q3IconView. Single or multiple items can be + selected. Items can be renamed in-place. Q3IconView also supports + \link #draganddrop drag and drop\endlink. + + Each item contains a label string, a pixmap or picture (the icon + itself) and optionally a sort key. The sort key is used for + sorting the items and defaults to the label string. The label + string can be displayed below or to the right of the icon (see \l + ItemTextPos). + + The simplest way to create a Q3IconView is to create a Q3IconView + object and create some Q3IconViewItems with the Q3IconView as their + parent, set the icon view's geometry and show it. + For example: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 4 + + The Q3IconViewItem call passes a pointer to the Q3IconView we wish to + populate, along with the label text and a QPixmap. + + When an item is inserted the Q3IconView allocates a position for it. + Existing items are rearranged if autoArrange() is true. The + default arrangement is \l LeftToRight -- the Q3IconView fills up + the \e left-most column from top to bottom, then moves one column + \e right and fills that from top to bottom and so on. The + arrangement can be modified with any of the following approaches: + \list + \i Call setArrangement(), e.g. with \l TopToBottom which will fill + the \e top-most row from left to right, then moves one row \e down + and fills that row from left to right and so on. + \i Construct each Q3IconViewItem using a constructor which allows + you to specify which item the new one is to follow. + \i Call setSorting() or sort() to sort the items. + \endlist + + The spacing between items is set with setSpacing(). Items can be + laid out using a fixed grid using setGridX() and setGridY(); by + default the Q3IconView calculates a grid dynamically. The position + of items' label text is set with setItemTextPos(). The text's + background can be set with setItemTextBackground(). The maximum + width of an item and of its text are set with setMaxItemWidth() + and setMaxItemTextLength(). The label text will be word-wrapped if + it is too long; this is controlled by setWordWrapIconText(). If + the label text is truncated, the user can still see the entire + text in a tool tip if they hover the mouse over the item. This is + controlled with setShowToolTips(). + + Items which are \link Q3IconViewItem::isSelectable() + selectable\endlink may be selected depending on the SelectionMode; + the default is \l Single. Because Q3IconView offers multiple + selection it must display keyboard focus and selection state + separately. Therefore there are functions to set the selection + state of an item (setSelected()) and to select which item displays + keyboard focus (setCurrentItem()). When multiple items may be + selected the icon view provides a rubberband, too. + + When in-place renaming is enabled (it is disabled by default), the + user may change the item's label. They do this by selecting the item + (single clicking it or navigating to it with the arrow keys), then + single clicking it (or pressing F2), and entering their text. If no + key has been set with Q3IconViewItem::setKey() the new text will also + serve as the key. (See Q3IconViewItem::setRenameEnabled().) + + You can control whether users can move items themselves with + setItemsMovable(). + + Because the internal structure used to store the icon view items is + linear, no iterator class is needed to iterate over all the items. + Instead we iterate by getting the first item from the \e{icon view} + and then each subsequent (\l Q3IconViewItem::nextItem()) from each + \e item in turn: + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 5 + Q3IconView also provides currentItem(). You can search for an item + using findItem() (searching by position or for label text) and + with findFirstVisibleItem() and findLastVisibleItem(). The number + of items is returned by count(). An item can be removed from an + icon view using takeItem(); to delete an item use \c delete. All + the items can be deleted with clear(). + + The Q3IconView emits a wide range of useful signals, including + selectionChanged(), currentChanged(), clicked(), moved() and + itemRenamed(). + + \target draganddrop + \section1 Drag and Drop + + Q3IconView supports the drag and drop of items within the Q3IconView + itself. It also supports the drag and drop of items out of or into + the Q3IconView and drag and drop onto items themselves. The drag and + drop of items outside the Q3IconView can be achieved in a simple way + with basic functionality, or in a more sophisticated way which + provides more power and control. + + The simple approach to dragging items out of the icon view is to + subclass Q3IconView and reimplement Q3IconView::dragObject(). + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 6 + + In this example we create a Q3TextDrag object, (derived from + Q3DragObject), containing the item's label and return it as the drag + object. We could just as easily have created a Q3ImageDrag from the + item's pixmap and returned that instead. + + Q3IconViews and their Q3IconViewItems can also be the targets of drag + and drops. To make the Q3IconView itself able to accept drops connect + to the dropped() signal. When a drop occurs this signal will be + emitted with a QDragEvent and a QLinkedList of Q3IconDragItems. To + make a Q3IconViewItem into a drop target subclass Q3IconViewItem and + reimplement Q3IconViewItem::acceptDrop() and + Q3IconViewItem::dropped(). + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 7 + + If you want to use extended drag-and-drop or have drag shapes drawn + you must take a more sophisticated approach. + + The first part is starting drags -- you should use a Q3IconDrag (or a + class derived from it) for the drag object. In dragObject() create the + drag object, populate it with Q3IconDragItems and return it. Normally + such a drag should offer each selected item's data. So in dragObject() + you should iterate over all the items, and create a Q3IconDragItem for + each selected item, and append these items with Q3IconDrag::append() to + the Q3IconDrag object. You can use Q3IconDragItem::setData() to set the + data of each item that should be dragged. If you want to offer the + data in additional mime-types, it's best to use a class derived from + Q3IconDrag, which implements additional encoding and decoding + functions. + + When a drag enters the icon view, there is little to do. Simply + connect to the dropped() signal and reimplement + Q3IconViewItem::acceptDrop() and Q3IconViewItem::dropped(). If you've + used a Q3IconDrag (or a subclass of it) the second argument to the + dropped signal contains a QLinkedList of Q3IconDragItems -- you can + access their data by calling Q3IconDragItem::data() on each one. + + For an example implementation of complex drag-and-drop look at the + fileiconview example (qt/examples/fileiconview). + + \sa Q3IconViewItem::setDragEnabled(), Q3IconViewItem::setDropEnabled(), + Q3IconViewItem::acceptDrop(), Q3IconViewItem::dropped() +*/ + +/*! \enum Q3IconView::ResizeMode + + This enum type is used to tell Q3IconView how it should treat the + positions of its icons when the widget is resized. The modes are: + + \value Fixed The icons' positions are not changed. + \value Adjust The icons' positions are adjusted to be within the + new geometry, if possible. +*/ + +/*! + \enum Q3IconView::SelectionMode + + This enumerated type is used by Q3IconView to indicate how it + reacts to selection by the user. It has four values: + + \value Single When the user selects an item, any already-selected + item becomes unselected and the user cannot unselect the selected + item. This means that the user can never clear the selection. (The + application programmer can, using Q3IconView::clearSelection().) + + \value Multi When the user selects an item, e.g. by navigating to + it with the keyboard arrow keys or by clicking it, the selection + status of that item is toggled and the other items are left alone. + + \value Extended When the user selects an item the selection is + cleared and the new item selected. However, if the user presses + the Ctrl key when clicking on an item, the clicked item gets + toggled and all other items are left untouched. If the user + presses the Shift key while clicking on an item, all items between + the current item and the clicked item get selected or unselected, + depending on the state of the clicked item. Also, multiple items + can be selected by dragging the mouse while the left mouse button + stays pressed. + + \value NoSelection Items cannot be selected. + + To summarize: \c Single is a real single-selection icon view; \c + Multi a real multi-selection icon view; \c Extended is an icon + view in which users can select multiple items but usually want to + select either just one or a range of contiguous items; and \c + NoSelection mode is for an icon view where the user can look but + not touch. +*/ + +/*! + \enum Q3IconView::Arrangement + + This enum type determines in which direction the items flow when + the view runs out of space. + + \value LeftToRight Items which don't fit into the view go further + down (you get a vertical scroll bar) + + \value TopToBottom Items which don't fit into the view go further + right (you get a horizontal scroll bar) +*/ + +/*! + \enum Q3IconView::ItemTextPos + + This enum type specifies the position of the item text in relation + to the icon. + + \value Bottom The text is drawn below the icon. + \value Right The text is drawn to the right of the icon. +*/ + +/*! + \fn void Q3IconView::dropped(QDropEvent *e, const Q3ValueList<Q3IconDragItem> &lst) + + This signal is emitted when a drop event occurs in the viewport + (but not on any icon) which the icon view itself can't handle. + + \a e provides all the information about the drop. If the drag + object of the drop was a Q3IconDrag, \a lst contains the list of + the dropped items. You can get the data using + Q3IconDragItem::data() on each item. If the \a lst is empty, i.e. + the drag was not a Q3IconDrag, you have to decode the data in \a e + and work with that. + + Note Q3IconViewItems may be drop targets; if a drop event occurs on + an item the item handles the drop. +*/ + +/*! + \fn void Q3IconView::moved() + + This signal is emitted after successfully dropping one (or more) + items of the icon view. If the items should be removed, it's best + to do so in a slot connected to this signal. +*/ + +/*! + \fn void Q3IconView::doubleClicked(Q3IconViewItem * item) + + This signal is emitted when the user double-clicks on \a item. +*/ + +/*! + \fn void Q3IconView::returnPressed (Q3IconViewItem * item) + + This signal is emitted if the user presses the Return or Enter + key. \a item is the currentItem() at the time of the keypress. +*/ + +/*! + \fn void Q3IconView::selectionChanged() + + This signal is emitted when the selection has been changed. It's + emitted in each selection mode. +*/ + +/*! + \fn void Q3IconView::selectionChanged(Q3IconViewItem *item) + \overload + + This signal is emitted when the selection changes. \a item is the + newly selected item. This signal is emitted only in single + selection mode. +*/ + +/*! + \fn void Q3IconView::currentChanged(Q3IconViewItem *item) + + This signal is emitted when a new item becomes current. \a item is + the new current item (or 0 if no item is now current). + + \sa currentItem() +*/ + +/*! + \fn void Q3IconView::onItem(Q3IconViewItem *item) + + This signal is emitted when the user moves the mouse cursor onto + an \a item, similar to the QWidget::enterEvent() function. +*/ + +// ### bug here - enter/leave event aren't considered. move the mouse +// out of the window and back in, to the same item. + +/*! + \fn void Q3IconView::onViewport() + + This signal is emitted when the user moves the mouse cursor from + an item to an empty part of the icon view. + + \sa onItem() +*/ + +/*! + \fn void Q3IconView::itemRenamed (Q3IconViewItem * item) + \overload + + This signal is emitted when \a item has been renamed, usually by + in-place renaming. + + \sa Q3IconViewItem::setRenameEnabled() Q3IconViewItem::rename() +*/ + +/*! + \fn void Q3IconView::itemRenamed (Q3IconViewItem * item, const QString &name) + + This signal is emitted when \a item has been renamed to \a name, + usually by in-place renaming. + + \sa Q3IconViewItem::setRenameEnabled() Q3IconViewItem::rename() +*/ + +/*! + \fn void Q3IconView::rightButtonClicked (Q3IconViewItem * item, const QPoint & pos) + + This signal is emitted when the user clicks the right mouse + button. If \a item is non-null, the cursor is on \a item. If \a + item is null, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). (If the click's + press and release differ by a pixel or two, \a pos is the + position at release time.) + + \sa rightButtonPressed() mouseButtonClicked() clicked() +*/ + +/*! + \fn void Q3IconView::contextMenuRequested(Q3IconViewItem *item, const QPoint & pos) + + This signal is emitted when the user invokes a context menu with + the right mouse button or with special system keys, with \a item + being the item under the mouse cursor or the current item, + respectively. + + \a pos is the position for the context menu in the global + coordinate system. +*/ + +/*! + \fn void Q3IconView::mouseButtonPressed(int button, Q3IconViewItem *item, const QPoint &pos) + + This signal is emitted when the user presses mouse button \a + button. If \a item is non-null, the cursor is on \a item. If \a + item is null, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). + + \sa rightButtonClicked() mouseButtonClicked() pressed() +*/ + +/*! + \fn void Q3IconView::mouseButtonClicked (int button, Q3IconViewItem * item, const QPoint & pos) + + This signal is emitted when the user clicks mouse button \a + button. If \a item is non-null, the cursor is on \a item. If \a + item is null, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). (If the click's + press and release differ by a pixel or two, \a pos is the + position at release time.) + + \sa mouseButtonPressed() rightButtonClicked() clicked() +*/ + +/*! + \fn void Q3IconView::clicked (Q3IconViewItem * item, const QPoint & pos) + \overload + + This signal is emitted when the user clicks any mouse button on an + icon view item. \a item is a pointer to the item that has been + clicked. + + \a pos is the position of the mouse cursor in the global coordinate + system (QMouseEvent::globalPos()). (If the click's press and release + differ by a pixel or two, \a pos is the position at release time.) + + \sa mouseButtonClicked() rightButtonClicked() pressed() +*/ + +/*! + \fn void Q3IconView::pressed (Q3IconViewItem * item, const QPoint & pos) + \overload + + This signal is emitted when the user presses any mouse button. If + \a item is non-null, the cursor is on \a item. If \a item is null, + the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). (If the click's + press and release differ by a pixel or two, \a pos is the + position at release time.) + + \sa mouseButtonPressed() rightButtonPressed() clicked() +*/ + +/*! + \fn void Q3IconView::clicked (Q3IconViewItem * item) + + This signal is emitted when the user clicks any mouse button. If + \a item is non-null, the cursor is on \a item. If \a item is null, + the mouse cursor isn't on any item. + + \sa mouseButtonClicked() rightButtonClicked() pressed() +*/ + +/*! + \fn void Q3IconView::pressed (Q3IconViewItem * item) + + This signal is emitted when the user presses any mouse button. If + \a item is non-null, the cursor is on \a item. If \a item is null, + the mouse cursor isn't on any item. + + \sa mouseButtonPressed() rightButtonPressed() clicked() +*/ + +/*! + \fn void Q3IconView::rightButtonPressed(Q3IconViewItem * item, const QPoint & pos) + + This signal is emitted when the user presses the right mouse + button. If \a item is non-null, the cursor is on \a item. If \a + item is null, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). +*/ + +/*! + Constructs an empty icon view called \a name, with parent \a + parent and using the widget flags \a f. +*/ + +Q3IconView::Q3IconView(QWidget *parent, const char *name, Qt::WindowFlags f) + : Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase | f) +{ + if (!unknown_icon) { + unknown_icon = new QPixmap((const char **)unknown_xpm); + qiv_cleanup_pixmap.add(&unknown_icon); + } + + d = new Q3IconViewPrivate; + d->dragging = false; + d->firstItem = 0; + d->lastItem = 0; + d->count = 0; + d->mousePressed = false; + d->selectionMode = Single; + d->currentItem = 0; + d->highlightedItem = 0; + d->rubber = 0; + d->scrollTimer = 0; + d->startDragItem = 0; + d->tmpCurrentItem = 0; + d->rastX = d->rastY = -1; + d->spacing = 5; + d->cleared = false; + d->arrangement = LeftToRight; + d->resizeMode = Fixed; + d->dropped = false; + d->adjustTimer = new QTimer(this, "iconview adjust timer"); + d->isIconDrag = false; + d->inMenuMode = false; +#ifndef QT_NO_DRAGANDDROP + d->iconDragData.clear(); +#endif + d->numDragItems = 0; + d->updateTimer = new QTimer(this, "iconview update timer"); + d->cachedW = d->cachedH = 0; + d->maxItemWidth = 100; + d->maxItemTextLength = 255; + d->inputTimer = new QTimer(this, "iconview input timer"); + d->currInputString.clear(); + d->dirty = false; + d->rearrangeEnabled = true; + d->itemTextPos = Bottom; + d->reorderItemsWhenInsert = true; +#ifndef QT_NO_CURSOR + d->oldCursor = Qt::ArrowCursor; +#endif + d->resortItemsWhenInsert = false; + d->sortDirection = true; + d->wordWrapIconText = true; + d->cachedContentsX = d->cachedContentsY = -1; + d->clearing = false; + d->fullRedrawTimer = new QTimer(this, "iconview full redraw timer"); + d->itemTextBrush = Qt::NoBrush; + d->drawAllBack = true; + d->fm = new QFontMetrics(font()); + d->minLeftBearing = d->fm->minLeftBearing(); + d->minRightBearing = d->fm->minRightBearing(); + d->firstContainer = d->lastContainer = 0; + d->containerUpdateLocked = false; + d->firstSizeHint = false; + d->selectAnchor = 0; + d->renamingItem = 0; + d->drawActiveSelection = true; + d->drawDragShapes = false; + + connect(d->adjustTimer, SIGNAL(timeout()), + this, SLOT(adjustItems())); + connect(d->updateTimer, SIGNAL(timeout()), + this, SLOT(slotUpdate())); + connect(d->fullRedrawTimer, SIGNAL(timeout()), + this, SLOT(updateContents())); + connect(this, SIGNAL(contentsMoving(int,int)), + this, SLOT(movedContents(int,int))); + + setAcceptDrops(true); + viewport()->setAcceptDrops(true); + + setMouseTracking(true); + viewport()->setMouseTracking(true); + + viewport()->setBackgroundRole(QPalette::Base); + viewport()->setFocusProxy(this); + viewport()->setFocusPolicy(Qt::WheelFocus); + setFocusPolicy(Qt::WheelFocus); + + d->showTips = true; +} + +/*! + \reimp +*/ +void Q3IconView::changeEvent(QEvent *ev) +{ + if(ev->type() == QEvent::StyleChange) { + *d->fm = QFontMetrics(font()); + d->minLeftBearing = d->fm->minLeftBearing(); + d->minRightBearing = d->fm->minRightBearing(); + + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) { + item->wordWrapDirty = true; + item->calcRect(); + } + +#if !defined(Q_WS_X11) + delete qiv_selection; + qiv_selection = 0; +#endif + } else if(ev->type() == QEvent::ActivationChange) { + if (!isActiveWindow() && d->scrollTimer) + d->scrollTimer->stop(); + if(isVisible() && !palette().isEqual(QPalette::Active, QPalette::Inactive)) + repaintSelectedItems(); + } + + Q3ScrollView::changeEvent(ev); + + if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange) { + *d->fm = QFontMetrics(font()); + d->minLeftBearing = d->fm->minLeftBearing(); + d->minRightBearing = d->fm->minRightBearing(); + + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) { + item->wordWrapDirty = true; + item->calcRect(); + } + } +} + +/*! + Destroys the icon view and deletes all items. +*/ + +Q3IconView::~Q3IconView() +{ + Q3IconViewItem *tmp, *item = d->firstItem; + d->clearing = true; + Q3IconViewPrivate::ItemContainer *c = d->firstContainer, *tmpc; + while (c) { + tmpc = c->n; + delete c; + c = tmpc; + } + while (item) { + tmp = item->next; + delete item; + item = tmp; + } + delete d->fm; + d->fm = 0; + delete d; +} + +/*! + Inserts the icon view item \a item after \a after. If \a after is + 0, \a item is appended after the last item. + + \e{You should never need to call this function.} Instead create + Q3IconViewItem's and associate them with your icon view like this: + + \snippet doc/src/snippets/code/src_qt3support_itemviews_q3iconview.cpp 8 +*/ + +void Q3IconView::insertItem(Q3IconViewItem *item, Q3IconViewItem *after) +{ + if (!item) + return; + + if (d->firstItem == item || item->prev || item->next) + return; + + if (!item->view) + item->view = this; + + if (!d->firstItem) { + d->firstItem = d->lastItem = item; + item->prev = 0; + item->next = 0; + } else { + if (!after || after == d->lastItem) { + d->lastItem->next = item; + item->prev = d->lastItem; + item->next = 0; + d->lastItem = item; + } else { + Q3IconViewItem *i = d->firstItem; + while (i != after) + i = i->next; + + if (i) { + Q3IconViewItem *next = i->next; + item->next = next; + item->prev = i; + i->next = item; + next->prev = item; + } + } + } + + if (isVisible()) { + if (d->reorderItemsWhenInsert) { + if (d->updateTimer->isActive()) + d->updateTimer->stop(); + d->fullRedrawTimer->stop(); + // #### uncomment this ASA insertInGrid uses cached values and is efficient + //insertInGrid(item); + + d->cachedW = qMax(d->cachedW, item->x() + item->width()); + d->cachedH= qMax(d->cachedH, item->y() + item->height()); + + d->updateTimer->start(0, true); + } else { + insertInGrid(item); + + viewport()->update(item->x() - contentsX(), + item->y() - contentsY(), + item->width(), item->height()); + } + } else if (!autoArrange()) { + item->dirty = false; + } + + d->count++; + d->dirty = true; +} + +/*! + This slot is used for a slightly-delayed update. + + The icon view is not redrawn immediately after inserting a new item + but after a very small delay using a QTimer. This means that when + many items are inserted in a loop the icon view is probably redrawn + only once at the end of the loop. This makes the insertions both + flicker-free and faster. +*/ + +void Q3IconView::slotUpdate() +{ + d->updateTimer->stop(); + d->fullRedrawTimer->stop(); + + if (!d->firstItem || !d->lastItem) + return; + + // #### remove that ASA insertInGrid uses cached values and is efficient + if (d->resortItemsWhenInsert) + sort(d->sortDirection); + else { + int y = d->spacing; + Q3IconViewItem *item = d->firstItem; + int w = 0, h = 0; + while (item) { + bool changed; + Q3IconViewItem *next = makeRowLayout(item, y, changed); + if (!next || !next->next) + break; + + if(!QApplication::reverseLayout()) + item = next; + w = qMax(w, item->x() + item->width()); + h = qMax(h, item->y() + item->height()); + item = next; + if (d->arrangement == LeftToRight) + h = qMax(h, y); + + item = item->next; + } + + if (d->lastItem && d->arrangement == TopToBottom) { + item = d->lastItem; + int x = item->x(); + while (item && item->x() >= x) { + w = qMax(w, item->x() + item->width()); + h = qMax(h, item->y() + item->height()); + item = item->prev; + } + } + + w = qMax(qMax(d->cachedW, w), d->lastItem->x() + d->lastItem->width()); + h = qMax(qMax(d->cachedH, h), d->lastItem->y() + d->lastItem->height()); + + if (d->arrangement == TopToBottom) + w += d->spacing; + else + h += d->spacing; + viewport()->setUpdatesEnabled(false); + resizeContents(w, h); + viewport()->setUpdatesEnabled(true); + viewport()->repaint(); + } + + int cx = d->cachedContentsX == -1 ? contentsX() : d->cachedContentsX; + int cy = d->cachedContentsY == -1 ? contentsY() : d->cachedContentsY; + + if (cx != contentsX() || cy != contentsY()) + setContentsPos(cx, cy); + + d->cachedContentsX = d->cachedContentsY = -1; + d->cachedW = d->cachedH = 0; +} + +/*! + Takes the icon view item \a item out of the icon view and causes + an update of the screen display. The item is not deleted. You + should normally not need to call this function because + Q3IconViewItem::~Q3IconViewItem() calls it. The normal way to delete + an item is to delete it. +*/ + +void Q3IconView::takeItem(Q3IconViewItem *item) +{ + if (!item) + return; + + if (item->d->container1) + item->d->container1->items.removeAll(item); + if (item->d->container2) + item->d->container2->items.removeAll(item); + item->d->container2 = 0; + item->d->container1 = 0; + + bool block = signalsBlocked(); + blockSignals(d->clearing); + + QRect r = item->rect(); + + if (d->currentItem == item) { + if (item->prev) { + d->currentItem = item->prev; + emit currentChanged(d->currentItem); + repaintItem(d->currentItem); + } else if (item->next) { + d->currentItem = item->next; + emit currentChanged(d->currentItem); + repaintItem(d->currentItem); + } else { + d->currentItem = 0; + emit currentChanged(d->currentItem); + } + } + if (item->isSelected()) { + item->selected = false; + emit selectionChanged(); + } + + if (item == d->firstItem) { + d->firstItem = d->firstItem->next; + if (d->firstItem) + d->firstItem->prev = 0; + } else if (item == d->lastItem) { + d->lastItem = d->lastItem->prev; + if (d->lastItem) + d->lastItem->next = 0; + } else { + Q3IconViewItem *i = item; + if (i) { + if (i->prev) + i->prev->next = i->next; + if (i->next) + i->next->prev = i->prev; + } + } + + if (d->selectAnchor == item) + d->selectAnchor = d->currentItem; + + if (!d->clearing) + repaintContents(r.x(), r.y(), r.width(), r.height()); + + item->view = 0; + item->prev = 0; + item->next = 0; + d->count--; + + blockSignals(block); +} + +/*! + Returns the index of \a item, or -1 if \a item doesn't exist in + this icon view. +*/ + +int Q3IconView::index(const Q3IconViewItem *item) const +{ + if (!item) + return -1; + + if (item == d->firstItem) + return 0; + else if (item == d->lastItem) + return d->count - 1; + else { + Q3IconViewItem *i = d->firstItem; + int j = 0; + while (i && i != item) { + i = i->next; + ++j; + } + + return i ? j : -1; + } +} + +/*! + Returns a pointer to the first item of the icon view, or 0 if + there are no items in the icon view. + + \sa lastItem() currentItem() +*/ + +Q3IconViewItem *Q3IconView::firstItem() const +{ + return d->firstItem; +} + +/*! + Returns a pointer to the last item of the icon view, or 0 if there + are no items in the icon view. + + \sa firstItem() currentItem() +*/ + +Q3IconViewItem *Q3IconView::lastItem() const +{ + return d->lastItem; +} + +/*! + Returns a pointer to the current item of the icon view, or 0 if no + item is current. + + \sa setCurrentItem() firstItem() lastItem() +*/ + +Q3IconViewItem *Q3IconView::currentItem() const +{ + return d->currentItem; +} + +/*! + \reimp +*/ +QVariant Q3IconView::inputMethodQuery(Qt::InputMethodQuery query) const +{ + if (query == Qt::ImMicroFocus) { + return d->currentItem ? d->currentItem->rect() : QRect(); + } + return QWidget::inputMethodQuery(query); +} + +/*! + Makes \a item the new current item of the icon view. +*/ + +void Q3IconView::setCurrentItem(Q3IconViewItem *item) +{ + if (!item || item == d->currentItem) + return; + + Q3IconViewItem *old = d->currentItem; + d->currentItem = item; + emit currentChanged(d->currentItem); + if (d->selectionMode == Single) { + bool changed = false; + if (old && old->selected) { + old->selected = false; + changed = true; + } + if (item && !item->selected && item->isSelectable() && d->selectionMode != NoSelection) { + item->selected = true; + changed = true; + emit selectionChanged(item); + } + if (changed) + emit selectionChanged(); + } + + if (old) + repaintItem(old); + repaintItem(d->currentItem); +} + +/*! + Selects or unselects \a item depending on \a s, and may also + unselect other items, depending on Q3IconView::selectionMode() and + \a cb. + + If \a s is false, \a item is unselected. + + If \a s is true and Q3IconView::selectionMode() is \l Single, \a + item is selected, and the item which was selected is unselected. + + If \a s is true and Q3IconView::selectionMode() is \l Extended, \a + item is selected. If \a cb is true, the selection state of the + icon view's other items is left unchanged. If \a cb is false (the + default) all other items are unselected. + + If \a s is true and Q3IconView::selectionMode() is \l Multi \a item + is selected. + + Note that \a cb is used only if Q3IconView::selectionMode() is \l + Extended. \a cb defaults to false. + + All items whose selection status is changed repaint themselves. +*/ + +void Q3IconView::setSelected(Q3IconViewItem *item, bool s, bool cb) +{ + if (!item) + return; + item->setSelected(s, cb); +} + +/*! + \property Q3IconView::count + \brief the number of items in the icon view +*/ + +uint Q3IconView::count() const +{ + return d->count; +} + +/*! + Performs autoscrolling when selecting multiple icons with the + rubber band. +*/ + +void Q3IconView::doAutoScroll() +{ + QRect oldRubber = QRect(*d->rubber); + + QPoint vp = viewport()->mapFromGlobal(QCursor::pos()); + QPoint pos = viewportToContents(vp); + + if (pos == d->rubber->bottomRight()) + return; + + d->rubber->setRight(pos.x()); + d->rubber->setBottom(pos.y()); + + int minx = contentsWidth(), miny = contentsHeight(); + int maxx = 0, maxy = 0; + bool changed = false; + bool block = signalsBlocked(); + + QRect rr; + QRegion region(0, 0, visibleWidth(), visibleHeight()); + + blockSignals(true); + viewport()->setUpdatesEnabled(false); + bool alreadyIntersected = false; + QRect nr = d->rubber->normalized(); + QRect rubberUnion = nr.united(oldRubber.normalized()); + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + for (; c; c = c->n) { + if (c->rect.intersects(rubberUnion)) { + alreadyIntersected = true; + for (int i = 0; i < c->items.size(); ++i) { + Q3IconViewItem *item = c->items.at(i); + if (d->selectedItems.contains(item)) + continue; + if (!item->intersects(nr)) { + if (item->isSelected()) { + item->setSelected(false); + changed = true; + rr = rr.united(item->rect()); + } + } else if (item->intersects(nr)) { + if (!item->isSelected() && item->isSelectable()) { + item->setSelected(true, true); + changed = true; + rr = rr.united(item->rect()); + } else { + region = region.subtracted(QRect(contentsToViewport(item->pos()), + item->size())); + } + + minx = qMin(minx, item->x() - 1); + miny = qMin(miny, item->y() - 1); + maxx = qMax(maxx, item->x() + item->width() + 1); + maxy = qMax(maxy, item->y() + item->height() + 1); + } + } + } else { + if (alreadyIntersected) + break; + } + } + viewport()->setUpdatesEnabled(true); + blockSignals(block); + + QRect r = *d->rubber; + *d->rubber = oldRubber; + d->dragging = false; + *d->rubber = r; + if (changed) { + d->drawAllBack = false; + d->clipRegion = region; + repaintContents(rr); + d->drawAllBack = true; + } + ensureVisible(pos.x(), pos.y()); + d->dragging = true; + + if (changed) { + emit selectionChanged(); + if (d->selectionMode == Single) + emit selectionChanged(d->currentItem); + } + + if (!QRect(50, 50, viewport()->width()-100, viewport()->height()-100).contains(vp) && + !d->scrollTimer) { + d->scrollTimer = new QTimer(this); + + connect(d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll())); + d->scrollTimer->start(100, false); + } else if (QRect(50, 50, viewport()->width()-100, viewport()->height()-100).contains(vp) && + d->scrollTimer) { + disconnect(d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll())); + d->scrollTimer->stop(); + delete d->scrollTimer; + d->scrollTimer = 0; + } + +} + +/*! + \reimp +*/ + +void Q3IconView::drawContents(QPainter *p, int cx, int cy, int cw, int ch) +{ + if (d->dragging && d->rubber) + drawRubber(p); + + QRect r = QRect(cx, cy, cw, ch); + + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + QRegion remaining(QRect(cx, cy, cw, ch)); + bool alreadyIntersected = false; + while (c) { + if (c->rect.intersects(r)) { + p->save(); + p->resetXForm(); + QRect r2 = c->rect; + r2 = r2.intersected(r); + QRect r3(contentsToViewport(QPoint(r2.x(), r2.y())), QSize(r2.width(), r2.height())); + if (d->drawAllBack) { + p->setClipRect(r3); + } else { + QRegion reg = d->clipRegion.intersected(r3); + p->setClipRegion(reg); + } + drawBackground(p, r3); + remaining = remaining.subtracted(r3); + p->restore(); + + QPalette pal = palette(); + d->drawActiveSelection = hasFocus() || d->inMenuMode + || !style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this); + if (!d->drawActiveSelection) + pal.setCurrentColorGroup(QPalette::Inactive); + + // clip items to the container rect by default... this + // prevents icons with alpha channels from being painted + // twice when they are in 2 containers + // + // NOTE: the item could override this cliprect in its + // paintItem() implementation, which makes this useless + p->setClipRect(r2); + for (int i = 0; i < c->items.size(); ++i) { + Q3IconViewItem *item = c->items.at(i); + if (item->rect().intersects(r) && !item->dirty) { + p->save(); + p->setFont(font()); + item->paintItem(p, pal); + p->restore(); + } + } + alreadyIntersected = true; + } else { + if (alreadyIntersected) + break; + } + c = c->n; + } + + if (!remaining.isEmpty()) { + p->save(); + p->resetXForm(); + if (d->drawAllBack) { + p->setClipRegion(remaining); + } else { + remaining = d->clipRegion.intersected(remaining); + p->setClipRegion(remaining); + } + drawBackground(p, remaining.boundingRect()); + p->restore(); + } + + if ((hasFocus() || viewport()->hasFocus()) && d->currentItem && + d->currentItem->rect().intersects(r)) { + d->currentItem->paintFocus(p, palette()); + } + + if (d->dragging && d->rubber) + drawRubber(p); +} + +/*! + \overload + + Arranges all the items in the grid specified by gridX() and gridY(). + + Even if sorting() is enabled, the items are not sorted by this + function. If you want to sort or rearrange the items, use + iconview->sort(iconview->sortDirection()). + + If \a update is true (the default), the viewport is repainted as + well. + + \sa Q3IconView::setGridX(), Q3IconView::setGridY(), Q3IconView::sort() +*/ + +void Q3IconView::arrangeItemsInGrid(bool update) +{ + if (!d->firstItem || !d->lastItem) + return; + + d->containerUpdateLocked = true; + + int w = 0, h = 0, y = d->spacing; + + Q3IconViewItem *item = d->firstItem; + bool changedLayout = false; + while (item) { + bool changed; + Q3IconViewItem *next = makeRowLayout(item, y, changed); + changedLayout = changed || changedLayout; + if(!QApplication::reverseLayout()) + item = next; + w = qMax(w, item->x() + item->width()); + h = qMax(h, item->y() + item->height()); + item = next; + if (d->arrangement == LeftToRight) + h = qMax(h, y); + + if (!item || !item->next) + break; + + item = item->next; + } + + if (d->lastItem && d->arrangement == TopToBottom) { + item = d->lastItem; + int x = item->x(); + while (item && item->x() >= x) { + w = qMax(w, item->x() + item->width()); + h = qMax(h, item->y() + item->height()); + item = item->prev; + } + } + d->containerUpdateLocked = false; + + w = qMax(qMax(d->cachedW, w), d->lastItem->x() + d->lastItem->width()); + h = qMax(qMax(d->cachedH, h), d->lastItem->y() + d->lastItem->height()); + + if (d->arrangement == TopToBottom) + w += d->spacing; + else + h += d->spacing; + + bool ue = updatesEnabled(); + if (ue) + viewport()->setUpdatesEnabled(false); + int vw = visibleWidth(); + int vh = visibleHeight(); + resizeContents(w, h); + bool doAgain = false; + if (d->arrangement == LeftToRight) + doAgain = visibleWidth() != vw; + if (d->arrangement == TopToBottom) + doAgain = visibleHeight() != vh; + if (doAgain) // in the case that the visibleExtend changed because of the resizeContents (scroll bar show/hide), redo layout again + arrangeItemsInGrid(false); + if (ue) + viewport()->setUpdatesEnabled(true); + d->dirty = !isVisible(); + rebuildContainers(); + if (update && (!optimize_layout || changedLayout)) + repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height()); +} + +/*! + This variant uses \a grid instead of (gridX(), gridY()). If \a + grid is invalid (see QSize::isValid()), arrangeItemsInGrid() + calculates a valid grid itself and uses that. + + If \a update is true (the default) the viewport is repainted. +*/ + +void Q3IconView::arrangeItemsInGrid(const QSize &grid, bool update) +{ + d->containerUpdateLocked = true; + QSize grid_(grid); + if (!grid_.isValid()) { + int w = 0, h = 0; + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) { + w = qMax(w, item->width()); + h = qMax(h, item->height()); + } + + grid_ = QSize(qMax(d->rastX + d->spacing, w), + qMax(d->rastY + d->spacing, h)); + } + + int w = 0, h = 0; + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) { + int nx = item->x() / grid_.width(); + int ny = item->y() / grid_.height(); + item->move(nx * grid_.width(), + ny * grid_.height()); + w = qMax(w, item->x() + item->width()); + h = qMax(h, item->y() + item->height()); + item->dirty = false; + } + d->containerUpdateLocked = false; + + resizeContents(w, h); + rebuildContainers(); + if (update) + repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height()); +} + +/*! + \reimp +*/ + +void Q3IconView::setContentsPos(int x, int y) +{ + if (d->updateTimer->isActive()) { + d->cachedContentsX = x; + d->cachedContentsY = y; + } else { + d->cachedContentsY = d->cachedContentsX = -1; + Q3ScrollView::setContentsPos(x, y); + } +} + +/*! + \reimp +*/ + +void Q3IconView::showEvent(QShowEvent *) +{ + if (d->dirty) { + resizeContents(qMax(contentsWidth(), viewport()->width()), + qMax(contentsHeight(), viewport()->height())); + if (d->resortItemsWhenInsert) + sort(d->sortDirection); + if (autoArrange()) + arrangeItemsInGrid(false); + } + Q3ScrollView::show(); +} + +/*! + \property Q3IconView::selectionMode + \brief the selection mode of the icon view + + This can be \l Single (the default), \l Extended, \l Multi or \l + NoSelection. +*/ + +void Q3IconView::setSelectionMode(SelectionMode m) +{ + d->selectionMode = m; +} + +Q3IconView::SelectionMode Q3IconView::selectionMode() const +{ + return d->selectionMode; +} + +/*! + Returns a pointer to the item that contains point \a pos, which is + given in contents coordinates, or 0 if no item contains point \a + pos. +*/ + +Q3IconViewItem *Q3IconView::findItem(const QPoint &pos) const +{ + if (!d->firstItem) + return 0; + + Q3IconViewPrivate::ItemContainer *c = d->lastContainer; + for (; c; c = c->p) { + if (c->rect.contains(pos)) + for (int i = c->items.size()-1; i >= 0; --i) + if (c->items.at(i)->contains(pos)) + return c->items.at(i); + } + + return 0; +} + +/*! + \overload + + Returns a pointer to the first item whose text begins with \a + text, or 0 if no such item could be found. Use the \a compare flag + to control the comparison behavior. +*/ + +Q3IconViewItem *Q3IconView::findItem(const QString &text, ComparisonFlags compare) const +{ + if (!d->firstItem) + return 0; + + if (compare == CaseSensitive || compare == 0) + compare |= ExactMatch; + + QString itmtxt; + QString comtxt = text; + if (! (compare & CaseSensitive)) + comtxt = text.toLower(); + + Q3IconViewItem *item; + if (d->currentItem) + item = d->currentItem; + else + item = d->firstItem; + + Q3IconViewItem *beginsWithItem = 0; + Q3IconViewItem *endsWithItem = 0; + Q3IconViewItem *containsItem = 0; + + if (item) { + for (; item; item = item->next) { + if (!(compare & CaseSensitive)) + itmtxt = item->text().toLower(); + else + itmtxt = item->text(); + + if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt) + return item; + if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt)) + beginsWithItem = containsItem = item; + if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt)) + endsWithItem = containsItem = item; + if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt)) + containsItem = item; + } + + if (d->currentItem && d->firstItem) { + item = d->firstItem; + for (; item && item != d->currentItem; item = item->next) { + if (!(compare & CaseSensitive)) + itmtxt = item->text().toLower(); + else + itmtxt = item->text(); + + if ((compare & ExactMatch)==ExactMatch && itmtxt == comtxt) + return item; + if (compare & BeginsWith && !beginsWithItem && itmtxt.startsWith(comtxt)) + beginsWithItem = containsItem = item; + if (compare & EndsWith && !endsWithItem && itmtxt.endsWith(comtxt)) + endsWithItem = containsItem = item; + if ((compare & ExactMatch)==0 && !containsItem && itmtxt.contains(comtxt)) + containsItem = item; + } + } + } + + // Obey the priorities + if (beginsWithItem) + return beginsWithItem; + else if (endsWithItem) + return endsWithItem; + else if (containsItem) + return containsItem; + return 0; +} + +/*! + Unselects all the items. +*/ + +void Q3IconView::clearSelection() +{ + selectAll(false); +} + +/*! + In Multi and Extended modes, this function sets all items to be + selected if \a select is true, and to be unselected if \a select + is false. + + In Single and NoSelection modes, this function only changes the + selection status of currentItem(). +*/ + +void Q3IconView::selectAll(bool select) +{ + if (d->selectionMode == NoSelection) + return; + + if (d->selectionMode == Single) { + if (d->currentItem) + d->currentItem->setSelected(select); + return; + } + + bool b = signalsBlocked(); + blockSignals(true); + Q3IconViewItem *item = d->firstItem; + Q3IconViewItem *i = d->currentItem; + bool changed = false; + bool ue = viewport()->updatesEnabled(); + if (ue) + viewport()->setUpdatesEnabled(false); + QRect rr; + for (; item; item = item->next) { + if (select != item->isSelected()) { + item->setSelected(select, true); + rr = rr.united(item->rect()); + changed = true; + } + } + if (ue) + viewport()->setUpdatesEnabled(true); + // we call updateContents not repaintContents because of possible previous updateContents + Q3ScrollView::updateContents(rr); + QApplication::sendPostedEvents(viewport(), QEvent::Paint); + if (i) + setCurrentItem(i); + blockSignals(b); + if (changed) { + emit selectionChanged(); + } +} + +/*! + Inverts the selection. Works only in Multi and Extended selection + mode. +*/ + +void Q3IconView::invertSelection() +{ + if (d->selectionMode == Single || + d->selectionMode == NoSelection) + return; + + bool b = signalsBlocked(); + blockSignals(true); + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) + item->setSelected(!item->isSelected(), true); + blockSignals(b); + emit selectionChanged(); +} + +/*! + Repaints the \a item. +*/ + +void Q3IconView::repaintItem(Q3IconViewItem *item) +{ + if (!item || item->dirty) + return; + + if (QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()). + intersects(QRect(item->x() - 1, item->y() - 1, item->width() + 2, item->height() + 2))) + repaintContents(item->x() - 1, item->y() - 1, item->width() + 2, item->height() + 2); +} + +/*! + Repaints the selected items. +*/ +void Q3IconView::repaintSelectedItems() +{ + if (selectionMode() == NoSelection) + return; + + if (selectionMode() == Single) { + if (!currentItem() || !currentItem()->isSelected()) + return; + QRect itemRect = currentItem()->rect(); //rect in contents coordinates + itemRect.moveBy(-contentsX(), -contentsY()); + viewport()->update(itemRect); + } else { + // check if any selected items are visible + Q3IconViewItem *item = firstItem(); + const QRect vr = QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); + + while (item) { + if (item->isSelected() && item->rect().intersects(vr)) + repaintItem(item); + item = item->nextItem(); + } + } +} + +/*! + Makes sure that \a item is entirely visible. If necessary, + ensureItemVisible() scrolls the icon view. + + \sa ensureVisible() +*/ + +void Q3IconView::ensureItemVisible(Q3IconViewItem *item) +{ + if (!item) + return; + + if ((d->updateTimer && d->updateTimer->isActive()) + || (d->fullRedrawTimer && d->fullRedrawTimer->isActive())) + slotUpdate(); + + int w = item->width(); + int h = item->height(); + ensureVisible(item->x() + w / 2, item->y() + h / 2, + w / 2 + 1, h / 2 + 1); +} + +/*! + Finds the first item whose bounding rectangle overlaps \a r and + returns a pointer to that item. \a r is given in content + coordinates. Returns 0 if no item overlaps \a r. + + If you want to find all items that touch \a r, you will need to + use this function and nextItem() in a loop ending at + findLastVisibleItem() and test Q3IconViewItem::rect() for each of + these items. + + \sa findLastVisibleItem() Q3IconViewItem::rect() +*/ + +Q3IconViewItem* Q3IconView::findFirstVisibleItem(const QRect &r) const +{ + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + Q3IconViewItem *i = 0; + bool alreadyIntersected = false; + for (; c; c = c->n) { + if (c->rect.intersects(r)) { + alreadyIntersected = true; + for (int j = 0; j < c->items.size(); ++j) { + Q3IconViewItem *item = c->items.at(j); + if (r.intersects(item->rect())) { + if (!i) { + i = item; + } else { + QRect r2 = item->rect(); + QRect r3 = i->rect(); + if (r2.y() < r3.y()) + i = item; + else if (r2.y() == r3.y() && + r2.x() < r3.x()) + i = item; + } + } + } + } else { + if (alreadyIntersected) + break; + } + } + + return i; +} + +/*! + Finds the last item whose bounding rectangle overlaps \a r and + returns a pointer to that item. \a r is given in content + coordinates. Returns 0 if no item overlaps \a r. + + \sa findFirstVisibleItem() +*/ + +Q3IconViewItem* Q3IconView::findLastVisibleItem(const QRect &r) const +{ + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + Q3IconViewItem *i = 0; + bool alreadyIntersected = false; + for (; c; c = c->n) { + if (c->rect.intersects(r)) { + alreadyIntersected = true; + for (int j = 0; j < c->items.size(); ++j) { + Q3IconViewItem *item = c->items.at(j); + if (r.intersects(item->rect())) { + if (!i) { + i = item; + } else { + QRect r2 = item->rect(); + QRect r3 = i->rect(); + if (r2.y() > r3.y()) + i = item; + else if (r2.y() == r3.y() && + r2.x() > r3.x()) + i = item; + } + } + } + } else { + if (alreadyIntersected) + break; + } + } + + return i; +} + +/*! + Clears the icon view. All items are deleted. +*/ + +void Q3IconView::clear() +{ + setContentsPos(0, 0); + d->clearing = true; + bool block = signalsBlocked(); + blockSignals(true); + clearSelection(); + blockSignals(block); + setContentsPos(0, 0); + d->currentItem = 0; + + if (!d->firstItem) { + d->clearing = false; + return; + } + + Q3IconViewItem *item = d->firstItem, *tmp; + d->firstItem = 0; + while (item) { + tmp = item->next; + delete item; + item = tmp; + } + Q3IconViewPrivate::ItemContainer *c = d->firstContainer, *tmpc; + while (c) { + tmpc = c->n; + delete c; + c = tmpc; + } + d->firstContainer = d->lastContainer = 0; + + d->count = 0; + d->lastItem = 0; + setCurrentItem(0); + d->highlightedItem = 0; + d->tmpCurrentItem = 0; + d->drawDragShapes = false; + + resizeContents(0, 0); + // maybe we don't need this update, so delay it + d->fullRedrawTimer->start(0, true); + + d->cleared = true; + d->clearing = false; +} + +/*! + \property Q3IconView::gridX + \brief the horizontal grid of the icon view + + If the value is -1, (the default), Q3IconView computes suitable + column widths based on the icon view's contents. + + Note that setting a grid width overrides setMaxItemWidth(). +*/ + +void Q3IconView::setGridX(int rx) +{ + d->rastX = rx >= 0 ? rx : -1; +} + +/*! + \property Q3IconView::gridY + \brief the vertical grid of the icon view + + If the value is -1, (the default), Q3IconView computes suitable + column heights based on the icon view's contents. +*/ + +void Q3IconView::setGridY(int ry) +{ + d->rastY = ry >= 0 ? ry : -1; +} + +int Q3IconView::gridX() const +{ + return d->rastX; +} + +int Q3IconView::gridY() const +{ + return d->rastY; +} + +/*! + \property Q3IconView::spacing + \brief the space in pixels between icon view items + + The default is 5 pixels. + + Negative values for spacing are illegal. +*/ + +void Q3IconView::setSpacing(int sp) +{ + d->spacing = sp; +} + +int Q3IconView::spacing() const +{ + return d->spacing; +} + +/*! + \property Q3IconView::itemTextPos + \brief the position where the text of each item is drawn. + + Valid values are \l Bottom or \l Right. The default is \l Bottom. +*/ + +void Q3IconView::setItemTextPos(ItemTextPos pos) +{ + if (pos == d->itemTextPos || (pos != Bottom && pos != Right)) + return; + + d->itemTextPos = pos; + + Q3IconViewItem *item = d->firstItem; + for (; item; item = item->next) { + item->wordWrapDirty = true; + item->calcRect(); + } + + arrangeItemsInGrid(true); +} + +Q3IconView::ItemTextPos Q3IconView::itemTextPos() const +{ + return d->itemTextPos; +} + +/*! + \property Q3IconView::itemTextBackground + \brief the brush to use when drawing the background of an item's text. + + By default this brush is set to Qt::NoBrush, meaning that only the + normal icon view background is used. +*/ + +void Q3IconView::setItemTextBackground(const QBrush &brush) +{ + d->itemTextBrush = brush; +} + +QBrush Q3IconView::itemTextBackground() const +{ + return d->itemTextBrush; +} + +/*! + \property Q3IconView::arrangement + \brief the arrangement mode of the icon view + + This can be \l LeftToRight or \l TopToBottom. The default is \l + LeftToRight. +*/ + +void Q3IconView::setArrangement(Arrangement am) +{ + if (d->arrangement == am) + return; + + d->arrangement = am; + + viewport()->setUpdatesEnabled(false); + resizeContents(viewport()->width(), viewport()->height()); + viewport()->setUpdatesEnabled(true); + arrangeItemsInGrid(true); +} + +Q3IconView::Arrangement Q3IconView::arrangement() const +{ + return d->arrangement; +} + +/*! + \property Q3IconView::resizeMode + \brief the resize mode of the icon view + + This can be \l Fixed or \l Adjust. The default is \l Fixed. + See \l ResizeMode. +*/ + +void Q3IconView::setResizeMode(ResizeMode rm) +{ + if (d->resizeMode == rm) + return; + + d->resizeMode = rm; +} + +Q3IconView::ResizeMode Q3IconView::resizeMode() const +{ + return d->resizeMode; +} + +/*! + \property Q3IconView::maxItemWidth + \brief the maximum width that an item may have. + + The default is 100 pixels. + + Note that if the gridX() value is set Q3IconView will ignore + this property. +*/ + +void Q3IconView::setMaxItemWidth(int w) +{ + d->maxItemWidth = w; +} + +/*! + \property Q3IconView::maxItemTextLength + \brief the maximum length (in characters) that an item's text may have. + + The default is 255 characters. +*/ + +void Q3IconView::setMaxItemTextLength(int w) +{ + d->maxItemTextLength = w; +} + +int Q3IconView::maxItemWidth() const +{ + if (d->rastX != -1) + return d->rastX - 2; + else + return d->maxItemWidth; +} + +int Q3IconView::maxItemTextLength() const +{ + return d->maxItemTextLength; +} + +/*! + \property Q3IconView::itemsMovable + \brief whether the user is allowed to move items around in the icon view + + The default is true. +*/ + +void Q3IconView::setItemsMovable(bool b) +{ + d->rearrangeEnabled = b; +} + +bool Q3IconView::itemsMovable() const +{ + return d->rearrangeEnabled; +} + +/*! + \property Q3IconView::autoArrange + \brief whether the icon view rearranges its items when a new item is inserted. + + The default is true. + + Note that if the icon view is not visible at the time of + insertion, Q3IconView defers all position-related work until it is + shown and then calls arrangeItemsInGrid(). +*/ + +void Q3IconView::setAutoArrange(bool b) +{ + d->reorderItemsWhenInsert = b; +} + +bool Q3IconView::autoArrange() const +{ + return d->reorderItemsWhenInsert; +} + +/*! + If \a sort is true, this function sets the icon view to sort items + when a new item is inserted. If \a sort is false, the icon view + will not be sorted. + + Note that autoArrange() must be true for sorting to take place. + + If \a ascending is true (the default), items are sorted in + ascending order. If \a ascending is false, items are sorted in + descending order. + + Q3IconViewItem::compare() is used to compare pairs of items. The + sorting is based on the items' keys; these default to the items' + text unless specifically set to something else. + + \sa Q3IconView::setAutoArrange(), Q3IconView::autoArrange(), + sortDirection(), sort(), Q3IconViewItem::setKey() +*/ + +void Q3IconView::setSorting(bool sort, bool ascending) +{ + d->resortItemsWhenInsert = sort; + d->sortDirection = ascending; +} + +/*! + \property Q3IconView::sorting + \brief whether the icon view sorts on insertion + + The default is false, i.e. no sorting on insertion. + + To set the sorting, use setSorting(). +*/ + +bool Q3IconView::sorting() const +{ + return d->resortItemsWhenInsert; +} + +/*! + \property Q3IconView::sortDirection + \brief whether the sort direction for inserting new items is ascending; + + The default is true (i.e. ascending). This sort direction is only + meaningful if both sorting() and autoArrange() are true. + + To set the sort direction, use setSorting() +*/ + +bool Q3IconView::sortDirection() const +{ + return d->sortDirection; +} + +/*! + \property Q3IconView::wordWrapIconText + \brief whether the item text will be word-wrapped if it is too long + + The default is true. + + If this property is false, icon text that is too long is + truncated, and an ellipsis (...) appended to indicate that + truncation has occurred. The full text can still be seen by the + user if they hover the mouse because the full text is shown in a + tooltip; see setShowToolTips(). +*/ + +void Q3IconView::setWordWrapIconText(bool b) +{ + if (d->wordWrapIconText == (uint)b) + return; + + d->wordWrapIconText = b; + for (Q3IconViewItem *item = d->firstItem; item; item = item->next) { + item->wordWrapDirty = true; + item->calcRect(); + } + arrangeItemsInGrid(true); +} + +bool Q3IconView::wordWrapIconText() const +{ + return d->wordWrapIconText; +} + +/*! + \property Q3IconView::showToolTips + \brief whether the icon view will display a tool tip with the complete text for any truncated item text + + The default is true. Note that this has no effect if + setWordWrapIconText() is true, as it is by default. +*/ + +void Q3IconView::setShowToolTips(bool b) +{ + d->showTips = b; +} + +bool Q3IconView::showToolTips() const +{ + return d->showTips; +} + +/*! + \reimp +*/ +void Q3IconView::contentsMousePressEvent(QMouseEvent *e) +{ + contentsMousePressEventEx(e); +} + +void Q3IconView::contentsMousePressEventEx(QMouseEvent *e) +{ + if (d->rubber) { + d->dragging = false; + delete d->rubber; + d->rubber = 0; + viewport()->update(); + + if (d->scrollTimer) { + disconnect(d->scrollTimer, SIGNAL(timeout()), this, SLOT(doAutoScroll())); + d->scrollTimer->stop(); + delete d->scrollTimer; + d->scrollTimer = 0; + } + } + + d->dragStartPos = e->pos(); + Q3IconViewItem *item = findItem(e->pos()); + d->pressedItem = item; + + if (item) + d->selectAnchor = item; + +#ifndef QT_NO_TEXTEDIT + if (d->renamingItem) + d->renamingItem->renameItem(); +#endif + + if (!d->currentItem && !item && d->firstItem) { + d->currentItem = d->firstItem; + repaintItem(d->firstItem); + } + + if (item && item->dragEnabled()) + d->startDragItem = item; + else + d->startDragItem = 0; + + if (e->button() == Qt::LeftButton && !(e->state() & Qt::ShiftButton) && + !(e->state() & Qt::ControlButton) && item && item->isSelected() && + item->textRect(false).contains(e->pos())) { + + if (!item->renameEnabled()) { + d->mousePressed = true; +#ifndef QT_NO_TEXTEDIT + } else { + ensureItemVisible(item); + setCurrentItem(item); + item->rename(); + goto emit_signals; +#endif + } + } + + d->pressedSelected = item && item->isSelected(); + + if (item && item->isSelectable()) { + if (d->selectionMode == Single) + item->setSelected(true, e->state() & Qt::ControlButton); + else if (d->selectionMode == Multi && !item->isSelected()) + item->setSelected(true, e->state() & Qt::ControlButton); + else if (d->selectionMode == Extended) { + if (e->state() & Qt::ShiftButton) { + d->pressedSelected = false; + bool block = signalsBlocked(); + blockSignals(true); + viewport()->setUpdatesEnabled(false); + QRect r; + bool select = true; + if (d->currentItem) + r = QRect(qMin(d->currentItem->x(), item->x()), + qMin(d->currentItem->y(), item->y()), + 0, 0); + else + r = QRect(0, 0, 0, 0); + if (d->currentItem) { + if (d->currentItem->x() < item->x()) + r.setWidth(item->x() - d->currentItem->x() + item->width()); + else + r.setWidth(d->currentItem->x() - item->x() + d->currentItem->width()); + if (d->currentItem->y() < item->y()) + r.setHeight(item->y() - d->currentItem->y() + item->height()); + else + r.setHeight(d->currentItem->y() - item->y() + d->currentItem->height()); + r = r.normalized(); + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + bool alreadyIntersected = false; + QRect redraw; + for (; c; c = c->n) { + if (c->rect.intersects(r)) { + alreadyIntersected = true; + for (int i = 0; i < c->items.size(); ++i) { + Q3IconViewItem *item = c->items.at(i); + if (r.intersects(item->rect())) { + redraw = redraw.united(item->rect()); + item->setSelected(select, true); + } + } + } else { + if (alreadyIntersected) + break; + } + } + redraw = redraw.united(item->rect()); + viewport()->setUpdatesEnabled(true); + repaintContents(redraw); + } + blockSignals(block); + viewport()->setUpdatesEnabled(true); + item->setSelected(select, true); + emit selectionChanged(); + } else if (e->state() & Qt::ControlButton) { + d->pressedSelected = false; + item->setSelected(!item->isSelected(), e->state() & Qt::ControlButton); + } else { + item->setSelected(true, e->state() & Qt::ControlButton); + } + } + } else if ((d->selectionMode != Single || e->button() == Qt::RightButton) + && !(e->state() & Qt::ControlButton)) + selectAll(false); + + setCurrentItem(item); + + if (e->button() == Qt::LeftButton) { + if (!item && (d->selectionMode == Multi || + d->selectionMode == Extended)) { + d->tmpCurrentItem = d->currentItem; + d->currentItem = 0; + repaintItem(d->tmpCurrentItem); + if (d->rubber) + delete d->rubber; + d->rubber = 0; + d->rubber = new QRect(e->x(), e->y(), 0, 0); + d->selectedItems.clear(); + if ((e->state() & Qt::ControlButton) == Qt::ControlButton) { + for (Q3IconViewItem *i = firstItem(); i; i = i->nextItem()) + if (i->isSelected()) + d->selectedItems.insert(i, i); + } + } + + d->mousePressed = true; + } + + emit_signals: + if (!d->rubber) { + emit mouseButtonPressed(e->button(), item, e->globalPos()); + emit pressed(item); + emit pressed(item, e->globalPos()); + + if (e->button() == Qt::RightButton) + emit rightButtonPressed(item, e->globalPos()); + } +} + +/*! + \reimp +*/ + +void Q3IconView::contentsContextMenuEvent(QContextMenuEvent *e) +{ + if (!receivers(SIGNAL(contextMenuRequested(Q3IconViewItem*,QPoint)))) { + e->ignore(); + return; + } + if (e->reason() == QContextMenuEvent::Keyboard) { + Q3IconViewItem *item = currentItem(); + QRect r = item ? item->rect() : QRect(0, 0, visibleWidth(), visibleHeight()); + emit contextMenuRequested(item, viewport()->mapToGlobal(contentsToViewport(r.center()))); + } else { + d->mousePressed = false; + Q3IconViewItem *item = findItem(e->pos()); + emit contextMenuRequested(item, e->globalPos()); + } +} + +/*! + \reimp +*/ + +void Q3IconView::contentsMouseReleaseEvent(QMouseEvent *e) +{ + Q3IconViewItem *item = findItem(e->pos()); + d->selectedItems.clear(); + + bool emitClicked = true; + d->mousePressed = false; + d->startDragItem = 0; + + if (d->rubber) { + d->dragging = false; + viewport()->update(); + + if ((d->rubber->topLeft() - d->rubber->bottomRight()).manhattanLength() > + QApplication::startDragDistance()) + emitClicked = false; + delete d->rubber; + d->rubber = 0; + d->currentItem = d->tmpCurrentItem; + d->tmpCurrentItem = 0; + if (d->currentItem) + repaintItem(d->currentItem); + } + + if (d->scrollTimer) { + disconnect(d->scrollTimer, SIGNAL(timeout()), this, SLOT(doAutoScroll())); + d->scrollTimer->stop(); + delete d->scrollTimer; + d->scrollTimer = 0; + } + + if ((d->selectionMode == Extended || d->selectionMode == Multi) && + d->currentItem == d->pressedItem && + d->pressedSelected && d->currentItem) { + if (d->selectionMode == Extended) { + bool block = signalsBlocked(); + blockSignals(true); + clearSelection(); + blockSignals(block); + } + if (d->currentItem->isSelectable()) { + d->currentItem->selected = (d->selectionMode == Extended); + repaintItem(d->currentItem); + } + emit selectionChanged(); + } + d->pressedItem = 0; + + if (emitClicked) { + emit mouseButtonClicked(e->button(), item, e->globalPos()); + emit clicked(item); + emit clicked(item, e->globalPos()); + if (e->button() == Qt::RightButton) + emit rightButtonClicked(item, e->globalPos()); + } +} + +/*! + \reimp +*/ + +void Q3IconView::contentsMouseMoveEvent(QMouseEvent *e) +{ + Q3IconViewItem *item = findItem(e->pos()); + if (d->highlightedItem != item) { + if (item) + emit onItem(item); + else + emit onViewport(); + d->highlightedItem = item; + } + + if (d->mousePressed && e->state() == Qt::NoButton) + d->mousePressed = false; + + if (d->startDragItem) + item = d->startDragItem; + + if (d->mousePressed && d->startDragItem && item && item == d->currentItem && + (item->isSelected() || d->selectionMode == NoSelection) && item->dragEnabled()) { + if ((d->dragStartPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) { + d->mousePressed = false; + d->cleared = false; +#ifndef QT_NO_DRAGANDDROP + startDrag(); +#endif + if (d->tmpCurrentItem) + repaintItem(d->tmpCurrentItem); + } + } else if (d->mousePressed && !d->currentItem && d->rubber) { + doAutoScroll(); + } +} + +/*! + \reimp +*/ + +void Q3IconView::contentsMouseDoubleClickEvent(QMouseEvent *e) +{ + Q3IconViewItem *item = findItem(e->pos()); + if (item) { + selectAll(false); + item->setSelected(true, true); + emit doubleClicked(item); + } +} + +/*! + \reimp +*/ + +#ifndef QT_NO_DRAGANDDROP +void Q3IconView::contentsDragEnterEvent(QDragEnterEvent *e) +{ + d->dragging = true; + d->drawDragShapes = true; + d->tmpCurrentItem = 0; + initDragEnter(e); + d->oldDragPos = e->pos(); + d->oldDragAcceptAction = false; + drawDragShapes(e->pos()); + d->dropped = false; + e->accept(); +} + +/*! + \reimp +*/ + +void Q3IconView::contentsDragMoveEvent(QDragMoveEvent *e) +{ + if (e->pos() == d->oldDragPos) { + if (d->oldDragAcceptAction) + e->acceptAction(); + else + e->ignore(); + return; + } + + drawDragShapes(d->oldDragPos); + d->dragging = false; + + Q3IconViewItem *old = d->tmpCurrentItem; + d->tmpCurrentItem = 0; + + Q3IconViewItem *item = findItem(e->pos()); + + if (item) { + if (old && + old->rect().contains(d->oldDragPos) && + !old->rect().contains(e->pos())) { + old->dragLeft(); + repaintItem(old); + } + if (!item->rect().contains(d->oldDragPos)) + item->dragEntered(); + if (item->acceptDrop(e) || (item->isSelected() && e->source() == viewport())) { + d->oldDragAcceptAction = true; + e->acceptAction(); + } else { + d->oldDragAcceptAction = false; + e->ignore(); + } + + d->tmpCurrentItem = item; + viewport()->update(); + } else { + e->acceptAction(); + d->oldDragAcceptAction = true; + if (old) { + old->dragLeft(); + repaintItem(old); + } + } + + d->oldDragPos = e->pos(); + drawDragShapes(e->pos()); + d->dragging = true; +} + +/*! + \reimp +*/ + +void Q3IconView::contentsDragLeaveEvent(QDragLeaveEvent *) +{ + if (!d->dropped) + drawDragShapes(d->oldDragPos); + d->dragging = false; + + if (d->tmpCurrentItem) { + repaintItem(d->tmpCurrentItem); + d->tmpCurrentItem->dragLeft(); + } + + d->tmpCurrentItem = 0; + d->isIconDrag = false; + d->iconDragData.clear(); +} + +/*! + \reimp +*/ + +void Q3IconView::contentsDropEvent(QDropEvent *e) +{ + d->dropped = true; + d->dragging = false; + drawDragShapes(d->oldDragPos); + + if (d->tmpCurrentItem) + repaintItem(d->tmpCurrentItem); + + Q3IconViewItem *i = findItem(e->pos()); + + if ((!i || i->isSelected()) && e->source() == viewport() && d->currentItem && !d->cleared) { + if (!d->rearrangeEnabled) + return; + QRect r = d->currentItem->rect(); + + d->currentItem->move(e->pos() - d->dragStart); + + int w = d->currentItem->x() + d->currentItem->width() + 1; + int h = d->currentItem->y() + d->currentItem->height() + 1; + + repaintItem(d->currentItem); + repaintContents(r.x(), r.y(), r.width(), r.height()); + + int dx = d->currentItem->x() - r.x(); + int dy = d->currentItem->y() - r.y(); + + Q3IconViewItem *item = d->firstItem; + QRect rr; + for (; item; item = item->next) { + if (item->isSelected() && item != d->currentItem) { + rr = rr.united(item->rect()); + item->moveBy(dx, dy); + rr = rr.united(item->rect()); + } + w = qMax(w, item->x() + item->width() + 1); + h = qMax(h, item->y() + item->height() + 1); + } + repaintContents(rr); + bool fullRepaint = false; + if (w > contentsWidth() || + h > contentsHeight()) + fullRepaint = true; + + int oldw = contentsWidth(); + int oldh = contentsHeight(); + + resizeContents(w, h); + + + if (fullRepaint) { + repaintContents(oldw, 0, contentsWidth() - oldw, contentsHeight()); + repaintContents(0, oldh, contentsWidth(), contentsHeight() - oldh); + } + e->acceptAction(); + } else if (!i && (e->source() != viewport() || d->cleared)) { + QLinkedList<Q3IconDragItem> lst; + if (Q3IconDrag::canDecode(e)) { + QLinkedList<Q3IconDragDataItem> l; + Q3IconDragPrivate::decode(e, l); + QLinkedList<Q3IconDragDataItem>::Iterator it = l.begin(); + for (; it != l.end(); ++it) + lst << (*it).data; + } + emit dropped(e, lst); + } else if (i) { + QLinkedList<Q3IconDragItem> lst; + if (Q3IconDrag::canDecode(e)) { + QLinkedList<Q3IconDragDataItem> l; + Q3IconDragPrivate::decode(e, l); + QLinkedList<Q3IconDragDataItem>::Iterator it = l.begin(); + for (; it != l.end(); ++it) + lst << (*it).data; + } + i->dropped(e, lst); + } + d->isIconDrag = false; +} +#endif + +/*! + \reimp +*/ + +void Q3IconView::resizeEvent(QResizeEvent* e) +{ + Q3ScrollView::resizeEvent(e); + if (d->resizeMode == Adjust) { + optimize_layout = true; + adjustItems(); + optimize_layout = false; +#if 0 // no need for timer delay anymore + d->oldSize = e->oldSize(); + if (d->adjustTimer->isActive()) + d->adjustTimer->stop(); + d->adjustTimer->start(0, true); +#endif + } +} + +/*! + Adjusts the positions of the items to the geometry of the icon + view. +*/ + +void Q3IconView::adjustItems() +{ + d->adjustTimer->stop(); + if (d->resizeMode == Adjust) + arrangeItemsInGrid(true); +} + +/*! + \reimp +*/ + +void Q3IconView::keyPressEvent(QKeyEvent *e) +{ + if (!d->firstItem) + return; + + if (!d->currentItem) { + setCurrentItem(d->firstItem); + if (d->selectionMode == Single) + d->currentItem->setSelected(true, true); + return; + } + + bool selectCurrent = true; + + switch (e->key()) { + case Qt::Key_Escape: + e->ignore(); + break; +#ifndef QT_NO_TEXTEDIT + case Qt::Key_F2: { + if (d->currentItem->renameEnabled()) { + d->currentItem->renameItem(); + d->currentItem->rename(); + return; + } + } break; +#endif + case Qt::Key_Home: { + d->currInputString.clear(); + if (!d->firstItem) + break; + + selectCurrent = false; + + Q3IconViewItem *item = 0; + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + while (!item && c) { + QList<Q3IconViewItem*> &list = c->items; + for (int j = 0; j < list.size(); ++j) { + Q3IconViewItem *i = list.at(j); + if (!item) { + item = i; + } else { + if (d->arrangement == LeftToRight) { + // we use pixmap so the items textlength are ignored + // find topmost, leftmost item + if (i->pixmapRect(false).y() < item->pixmapRect(false).y() || + (i->pixmapRect(false).y() == item->pixmapRect(false).y() && + i->pixmapRect(false).x() < item->pixmapRect(false).x())) + item = i; + } else { + // find leftmost, topmost item + if (i->pixmapRect(false).x() < item->pixmapRect(false).x() || + (i->pixmapRect(false).x() == item->pixmapRect(false).x() && + i->pixmapRect(false).y() < item->pixmapRect(false).y())) + item = i; + } + } + } + c = c->n; + } + + if (item) { + Q3IconViewItem *old = d->currentItem; + setCurrentItem(item); + ensureItemVisible(item); + handleItemChange(old, e->state() & Qt::ShiftButton, + e->state() & Qt::ControlButton, true); + } + } break; + case Qt::Key_End: { + d->currInputString.clear(); + if (!d->lastItem) + break; + + selectCurrent = false; + + Q3IconViewItem *item = 0; + Q3IconViewPrivate::ItemContainer *c = d->lastContainer; + while (!item && c) { + QList<Q3IconViewItem*> &list = c->items; + for (int j = 0; j < list.size(); ++j) { + Q3IconViewItem *i = list.at(j); + if (!item) { + item = i; + } else { + if (d->arrangement == LeftToRight) { + // find bottommost, rightmost item + if (i->pixmapRect(false).bottom() > item->pixmapRect(false).bottom() || + (i->pixmapRect(false).bottom() == item->pixmapRect(false).bottom() && + i->pixmapRect(false).right() > item->pixmapRect(false).right())) + item = i; + } else { + // find rightmost, bottommost item + if (i->pixmapRect(false).right() > item->pixmapRect(false).right() || + (i->pixmapRect(false).right() == item->pixmapRect(false).right() && + i->pixmapRect(false).bottom() > item->pixmapRect(false).bottom())) + item = i; + } + } + } + c = c->p; + } + + if (item) { + Q3IconViewItem *old = d->currentItem; + setCurrentItem(item); + ensureItemVisible(item); + handleItemChange(old, e->state() & Qt::ShiftButton, + e->state() & Qt::ControlButton, true); + } + } break; + case Qt::Key_Right: { + d->currInputString.clear(); + Q3IconViewItem *item; + selectCurrent = false; + Direction dir = DirRight; + + QRect r(0, d->currentItem->y(), contentsWidth(), d->currentItem->height()); + item = findItem(dir, d->currentItem->rect().center(), r); + + // search the row below from the right + while (!item && r.y() < contentsHeight()) { + r.moveBy(0, d->currentItem->height()); + item = findItem(dir, QPoint(0, r.center().y()), r); + } + + if (item) { + Q3IconViewItem *old = d->currentItem; + setCurrentItem(item); + ensureItemVisible(item); + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + } break; + case Qt::Key_Left: { + d->currInputString.clear(); + Q3IconViewItem *item; + selectCurrent = false; + Direction dir = DirLeft; + + QRect r(0, d->currentItem->y(), contentsWidth(), d->currentItem->height()); + item = findItem(dir, d->currentItem->rect().center(), r); + + // search the row above from the left + while (!item && r.y() >= 0) { + r.moveBy(0, - d->currentItem->height()); + item = findItem(dir, QPoint(contentsWidth(), r.center().y()), r); + } + + if (item) { + Q3IconViewItem *old = d->currentItem; + setCurrentItem(item); + ensureItemVisible(item); + handleItemChange(old, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + } break; + case Qt::Key_Space: { + d->currInputString.clear(); + if (d->selectionMode == Single) + break; + + d->currentItem->setSelected(!d->currentItem->isSelected(), true); + } break; + case Qt::Key_Enter: case Qt::Key_Return: + d->currInputString.clear(); + emit returnPressed(d->currentItem); + break; + case Qt::Key_Down: { + d->currInputString.clear(); + Q3IconViewItem *item; + selectCurrent = false; + Direction dir = DirDown; + + QRect r(d->currentItem->x(), 0, d->currentItem->width(), contentsHeight()); + item = findItem(dir, d->currentItem->rect().center(), r); + + // finding the closest item below and to the right + while (!item && r.x() < contentsWidth()) { + r.moveBy(r.width() , 0); + item = findItem(dir, QPoint(r.center().x(), 0), r); + } + + + Q3IconViewItem *i = d->currentItem; + setCurrentItem(item); + item = i; + handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } break; + case Qt::Key_Up: { + d->currInputString.clear(); + Q3IconViewItem *item; + selectCurrent = false; + Direction dir = DirUp; + + QRect r(d->currentItem->x(), 0, d->currentItem->width(), contentsHeight()); + item = findItem(dir, d->currentItem->rect().center(), r); + + // finding the closest item above and to the left + while (!item && r.x() >= 0) { + r.moveBy(- r.width(), 0); + item = findItem(dir, QPoint(r.center().x(), contentsHeight()), r); + } + + Q3IconViewItem *i = d->currentItem; + setCurrentItem(item); + item = i; + handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } break; + case Qt::Key_Next: { + d->currInputString.clear(); + selectCurrent = false; + QRect r; + if (d->arrangement == LeftToRight) + r = QRect(0, d->currentItem->y() + visibleHeight(), contentsWidth(), visibleHeight()); + else + r = QRect(d->currentItem->x() + visibleWidth(), 0, visibleWidth(), contentsHeight()); + Q3IconViewItem *item = d->currentItem; + Q3IconViewItem *ni = findFirstVisibleItem(r ); + if (!ni) { + if (d->arrangement == LeftToRight) + r = QRect(0, d->currentItem->y() + d->currentItem->height(), contentsWidth(), contentsHeight()); + else + r = QRect(d->currentItem->x() + d->currentItem->width(), 0, contentsWidth(), contentsHeight()); + ni = findLastVisibleItem(r ); + } + if (ni) { + setCurrentItem(ni); + handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + } break; + case Qt::Key_Prior: { + d->currInputString.clear(); + selectCurrent = false; + QRect r; + if (d->arrangement == LeftToRight) + r = QRect(0, d->currentItem->y() - visibleHeight(), contentsWidth(), visibleHeight()); + else + r = QRect(d->currentItem->x() - visibleWidth(), 0, visibleWidth(), contentsHeight()); + Q3IconViewItem *item = d->currentItem; + Q3IconViewItem *ni = findFirstVisibleItem(r ); + if (!ni) { + if (d->arrangement == LeftToRight) + r = QRect(0, 0, contentsWidth(), d->currentItem->y()); + else + r = QRect(0, 0, d->currentItem->x(), contentsHeight()); + ni = findFirstVisibleItem(r ); + } + if (ni) { + setCurrentItem(ni); + handleItemChange(item, e->state() & Qt::ShiftButton, e->state() & Qt::ControlButton); + } + } break; + default: + if (!e->text().isEmpty() && e->text()[0].isPrint()) { + selectCurrent = false; + Q3IconViewItem *i = d->currentItem; + if (!i) + i = d->firstItem; + if (!d->inputTimer->isActive()) { + d->currInputString = e->text(); + i = i->next; + if (!i) + i = d->firstItem; + i = findItemByName(i); + } else { + d->inputTimer->stop(); + d->currInputString += e->text(); + i = findItemByName(i); + if (!i) { + d->currInputString = e->text(); + if (d->currentItem && d->currentItem->next) + i = d->currentItem->next; + else + i = d->firstItem; + i = findItemByName(i); + } + } + if (i) { + setCurrentItem(i); + if (d->selectionMode == Extended) { + bool changed = false; + bool block = signalsBlocked(); + blockSignals(true); + selectAll(false); + blockSignals(block); + if (!i->selected && i->isSelectable()) { + changed = true; + i->selected = true; + repaintItem(i); + } + if (changed) + emit selectionChanged(); + } + } + d->inputTimer->start(400, true); + } else { + selectCurrent = false; + d->currInputString.clear(); + if (e->state() & Qt::ControlButton) { + switch (e->key()) { + case Qt::Key_A: + selectAll(true); + break; + } + } + e->ignore(); + return; + } + } + + if (!(e->state() & Qt::ShiftButton) || !d->selectAnchor) + d->selectAnchor = d->currentItem; + + if (d->currentItem && !d->currentItem->isSelected() && + d->selectionMode == Single && selectCurrent) { + d->currentItem->setSelected(true); + } + + ensureItemVisible(d->currentItem); +} + +/* + Finds the closest item in the Direction \a dir relative from the point \a relativeTo + which intersects with the searchRect. + + The function chooses the closest item with its center in the \a searchRect. +*/ +Q3IconViewItem* Q3IconView::findItem(Direction dir, + const QPoint &relativeTo, + const QRect &searchRect) const +{ + Q3IconViewItem *centerMatch = 0; + int centerMatchML = 0; + + // gets list of containers with potential items + QList<Q3IconViewPrivate::ItemContainer * >* cList = + d->findContainers(dir, relativeTo, searchRect); + + for (int i = 0; i < cList->size() && !centerMatch; ++i) { + QList<Q3IconViewItem *> &list = (cList->at(i))->items; + for (int j = 0; j < list.size(); ++j) { + Q3IconViewItem *item = list.at(j); + if (neighbourItem(dir, relativeTo, item) && + searchRect.contains(item->rect().center()) && item != currentItem()) { + int ml = (relativeTo - item->rect().center()).manhattanLength(); + if (centerMatch) { + if (ml < centerMatchML) { + centerMatch = item; + centerMatchML = ml; + } + } else { + centerMatch = item; + centerMatchML = ml; + } + } + } + } + return centerMatch; +} + + +/* + Returns true if the items orientation compared to + the point \a relativeTo is correct. +*/ +bool Q3IconView::neighbourItem(Direction dir, + const QPoint &relativeTo, + const Q3IconViewItem *item) const +{ + switch (dir) { + case DirUp: + if (item->rect().center().y() < relativeTo.y()) + return true; + break; + case DirDown: + if (item->rect().center().y() > relativeTo.y()) + return true; + break; + case DirLeft: + if (item->rect().center().x() < relativeTo.x()) + return true; + break; + case DirRight: + if (item->rect().center().x() > relativeTo.x()) + return true; + break; + default: + // nothing + break; + } + return false; +} + +/*! + \reimp +*/ + +void Q3IconView::focusInEvent(QFocusEvent *e) +{ + d->mousePressed = false; + d->inMenuMode = false; + if (d->currentItem) { + repaintItem(d->currentItem); + } else if (d->firstItem && e->reason() != Qt::MouseFocusReason) { + d->currentItem = d->firstItem; + emit currentChanged(d->currentItem); + repaintItem(d->currentItem); + } + + if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) + repaintSelectedItems(); +} + +/*! + \reimp +*/ + +void Q3IconView::focusOutEvent(QFocusEvent *e) +{ + if (style()->styleHint(QStyle::SH_ItemView_ChangeHighlightOnFocus, 0, this)) { + d->inMenuMode = + e->reason() == Qt::PopupFocusReason || + (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar")); + if (!d->inMenuMode) + repaintSelectedItems(); + } + if (d->currentItem) + repaintItem(d->currentItem); +} + +/*! + Draws the rubber band using the painter \a p. +*/ + +void Q3IconView::drawRubber(QPainter *p) +{ + if (!p || !d->rubber) + return; + QStyleOptionRubberBand opt; + opt.rect = d->rubber->normalized(); + opt.shape = QRubberBand::Rectangle; + opt.palette = palette(); + opt.state = QStyle::State_None; + style()->drawControl(QStyle::CE_RubberBand, &opt, p, this); +} + +/*! + Returns the Q3DragObject that should be used for drag-and-drop. + This function is called by the icon view when starting a drag to + get the dragobject that should be used for the drag. Subclasses + may reimplement this. + + \sa Q3IconDrag +*/ + +#ifndef QT_NO_DRAGANDDROP +Q3DragObject *Q3IconView::dragObject() +{ + if (!d->currentItem) + return 0; + + QPoint orig = d->dragStartPos; + + Q3IconDrag *drag = new Q3IconDrag(viewport()); + drag->setPixmap((d->currentItem->pixmap() ? + *d->currentItem->pixmap() : QPixmap()), // ### QPicture + QPoint(d->currentItem->pixmapRect().width() / 2, + d->currentItem->pixmapRect().height() / 2)); + + if (d->selectionMode == NoSelection) { + Q3IconViewItem *item = d->currentItem; + drag->append(Q3IconDragItem(), + QRect(item->pixmapRect(false).x() - orig.x(), + item->pixmapRect(false).y() - orig.y(), + item->pixmapRect().width(), item->pixmapRect().height()), + QRect(item->textRect(false).x() - orig.x(), + item->textRect(false).y() - orig.y(), + item->textRect().width(), item->textRect().height())); + } else { + for (Q3IconViewItem *item = d->firstItem; item; item = item->next) { + if (item->isSelected()) { + drag->append(Q3IconDragItem(), + QRect(item->pixmapRect(false).x() - orig.x(), + item->pixmapRect(false).y() - orig.y(), + item->pixmapRect().width(), item->pixmapRect().height()), + QRect(item->textRect(false).x() - orig.x(), + item->textRect(false).y() - orig.y(), + item->textRect().width(), item->textRect().height())); + } + } + } + + return drag; +} + +/*! + Starts a drag. +*/ + +void Q3IconView::startDrag() +{ + if (!d->startDragItem) + return; + + QPoint orig = d->dragStartPos; + d->dragStart = QPoint(orig.x() - d->startDragItem->x(), + orig.y() - d->startDragItem->y()); + d->startDragItem = 0; + d->mousePressed = false; + d->pressedItem = 0; + d->pressedSelected = 0; + + Q3DragObject *drag = dragObject(); + if (!drag) + return; + + if (drag->drag()) + if (drag->target() != viewport()) + emit moved(); +} + +#endif + +/*! + Inserts the Q3IconViewItem \a item in the icon view's grid. \e{You + should never need to call this function.} Instead, insert + Q3IconViewItems by creating them with a pointer to the Q3IconView + that they are to be inserted into. +*/ + +void Q3IconView::insertInGrid(Q3IconViewItem *item) +{ + if (!item) + return; + + if (d->reorderItemsWhenInsert) { + // #### make this efficient - but it's not too dramatic + int y = d->spacing; + + item->dirty = false; + if (item == d->firstItem) { + bool dummy; + makeRowLayout(item, y, dummy); + return; + } + + Q3IconViewItem *begin = rowBegin(item); + y = begin->y(); + while (begin) { + bool dummy; + begin = makeRowLayout(begin, y, dummy); + + if (!begin || !begin->next) + break; + + begin = begin->next; + } + item->dirty = false; + } else { + QRegion r(QRect(0, 0, qMax(contentsWidth(), visibleWidth()), + qMax(contentsHeight(), visibleHeight()))); + + int y = -1; + for (Q3IconViewItem *i = d->firstItem; i; i = i->next) { + r = r.subtracted(i->rect()); + y = qMax(y, i->y() + i->height()); + } + + QVector<QRect> rects = r.rects(); + bool foundPlace = false; + for (int j = 0; j < rects.size(); ++j) { + const QRect rect = rects.at(j); + if (rect.width() >= item->width() && + rect.height() >= item->height()) { + int sx = 0, sy = 0; + if (rect.width() >= item->width() + d->spacing) + sx = d->spacing; + if (rect.height() >= item->height() + d->spacing) + sy = d->spacing; + item->move(rect.x() + sx, rect.y() + sy); + foundPlace = true; + break; + } + } + + if (!foundPlace) + item->move(d->spacing, y + d->spacing); + + resizeContents(qMax(contentsWidth(), item->x() + item->width()), + qMax(contentsHeight(), item->y() + item->height())); + item->dirty = false; + } +} + +/*! + Emits a signal to indicate selection changes. \a i is the + Q3IconViewItem that was selected or de-selected. + + \e{You should never need to call this function.} +*/ + +void Q3IconView::emitSelectionChanged(Q3IconViewItem *i) +{ + emit selectionChanged(); + if (d->selectionMode == Single) + emit selectionChanged(i ? i : d->currentItem); +} + +/*! + \internal +*/ + +void Q3IconView::emitRenamed(Q3IconViewItem *item) +{ + if (!item) + return; + + emit itemRenamed(item, item->text()); + emit itemRenamed(item); +} + +/*! + If a drag enters the icon view the shapes of the objects which the + drag contains are drawn, usnig \a pos as origin. +*/ + +void Q3IconView::drawDragShapes(const QPoint &pos) +{ +#ifndef QT_NO_DRAGANDDROP + if (pos == QPoint(-1, -1)) + return; + + if (!d->drawDragShapes) { + d->drawDragShapes = true; + return; + } + + d->dragPos = pos; + viewport()->update(); +#endif +} + +/*! + When a drag enters the icon view, this function is called to + initialize it. Initializing in this context means getting + information about the drag, for example so that the icon view + knows enough about the drag to be able to draw drag shapes for the + drag data (e.g. shapes of icons which are dragged), etc. +*/ + +#ifndef QT_NO_DRAGANDDROP +void Q3IconView::initDragEnter(QDropEvent *e) +{ + if (Q3IconDrag::canDecode(e)) { + Q3IconDragPrivate::decode(e, d->iconDragData); + d->isIconDrag = true; + } else if (Q3UriDrag::canDecode(e)) { + Q3StrList lst; + Q3UriDrag::decode(e, lst); + d->numDragItems = lst.count(); + } else { + d->numDragItems = 0; + } + +} +#endif + +/*! + This function is called to draw the rectangle \a r of the + background using the painter \a p. + + The default implementation fills \a r with the viewport's + backgroundBrush(). Subclasses can reimplement this to draw custom + backgrounds. + + \sa drawContents() +*/ + +void Q3IconView::drawBackground(QPainter *p, const QRect &r) +{ + p->fillRect(r, viewport()->backgroundBrush()); +} + +/*! + \reimp +*/ + +bool Q3IconView::eventFilter(QObject * o, QEvent * e) +{ + if (o == viewport()) { + switch(e->type()) { + case QEvent::FocusIn: + focusInEvent((QFocusEvent*)e); + return true; + case QEvent::FocusOut: + focusOutEvent((QFocusEvent*)e); + return true; + case QEvent::Enter: + enterEvent(e); + return true; + case QEvent::Paint: + if (o == viewport()) { + viewportPaintEvent((QPaintEvent*)e); + QPainter p(viewport()); + if (d->dragging) { + if (!d->rubber && d->drawDragShapes) { + p.setPen(QPen(Qt::color0)); + QStyleOptionFocusRect opt; + opt.palette = palette(); + opt.state = QStyle::State_KeyboardFocusChange; + opt.backgroundColor = palette().base().color(); + if (d->isIconDrag) { + d->dragPos = contentsToViewport(d->dragPos); + QLinkedList<Q3IconDragDataItem>::Iterator it = d->iconDragData.begin(); + for (; it != d->iconDragData.end(); ++it) { + QRect ir = (*it).item.pixmapRect(); + QRect tr = (*it).item.textRect(); + tr.moveBy(d->dragPos.x(), d->dragPos.y()); + ir.moveBy(d->dragPos.x(), d->dragPos.y()); + if (!ir.intersects(QRect(0, 0, visibleWidth(), visibleHeight()))) + continue; + opt.rect = ir; + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this); + opt.rect = tr; + p.drawRect(tr); + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this); + } + } else if (d->numDragItems > 0) { + for (int i = 0; i < d->numDragItems; ++i) { + opt.rect.setRect(d->dragPos.x() + i * 40, d->dragPos.y(), 35, 35); + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this); + } + + } + p.end(); + } + } else { + p.translate(-contentsX(), -contentsY()); + drawRubber(&p); + } + } + return true; +#ifndef QT_NO_TOOLTIP + case QHelpEvent::ToolTip: + { + if (wordWrapIconText() || !showToolTips()) + return false; + + QHelpEvent *he = static_cast<QHelpEvent *>(e); + Q3IconViewItem *item = findItem(viewportToContents(he->pos())); + if (!item || item->tmpText == item->itemText) { + QToolTip::showText(he->globalPos(), QString(), viewport()); + return true; + } + + QToolTip::showText(he->globalPos(), item->itemText, viewport()); + return true; + } +#endif + default: + // nothing + break; + } + } + + return Q3ScrollView::eventFilter(o, e); +} + + +/*! + \reimp +*/ + +QSize Q3IconView::minimumSizeHint() const +{ + return Q3ScrollView::minimumSizeHint(); +} + +/*! + \internal + Finds the next item after the start item beginning + with \a text. +*/ + +Q3IconViewItem* Q3IconView::findItemByName(Q3IconViewItem *start) +{ + if (!start) + return 0; + QString match = d->currInputString.toLower(); + if (match.length() < 1) + return start; + QString curText; + Q3IconViewItem *i = start; + do { + curText = i->text().toLower(); + if (curText.startsWith(match)) + return i; + i = i->next; + if (!i) + i = d->firstItem; + } while (i != start); + return 0; +} + +/*! + Lays out a row of icons (if Arrangement == \l TopToBottom this is + a column). Starts laying out with the item \a begin. \a y is the + starting coordinate. Returns the last item of the row (column) and + sets the new starting coordinate to \a y. The \a changed parameter + is used internally. + + \warning This function may be made private in a future version of + Qt. We do not recommend calling it. +*/ + +Q3IconViewItem *Q3IconView::makeRowLayout(Q3IconViewItem *begin, int &y, bool &changed) +{ + Q3IconViewItem *end = 0; + + bool reverse = QApplication::reverseLayout(); + changed = false; + + if (d->arrangement == LeftToRight) { + + if (d->rastX == -1) { + // first calculate the row height + int h = 0; + int x = 0; + int ih = 0; + Q3IconViewItem *item = begin; + for (;;) { + x += d->spacing + item->width(); + if (x > visibleWidth() && item != begin) { + item = item->prev; + break; + } + h = qMax(h, item->height()); + ih = qMax(ih, item->pixmapRect().height()); + Q3IconViewItem *old = item; + item = item->next; + if (!item) { + item = old; + break; + } + } + end = item; + + if (d->rastY != -1) + h = qMax(h, d->rastY); + + // now move the items + item = begin; + for (;;) { + item->dirty = false; + int x; + if (item == begin) { + if (reverse) + x = visibleWidth() - d->spacing - item->width(); + else + x = d->spacing; + } else { + if (reverse) + x = item->prev->x() - item->width() - d->spacing; + else + x = item->prev->x() + item->prev->width() + d->spacing; + } + changed = item->move(x, y + ih - item->pixmapRect().height()) || changed; + if (y + h < item->y() + item->height()) + h = qMax(h, ih + item->textRect().height()); + if (item == end) + break; + item = item->next; + } + y += h + d->spacing; + } else { + // first calculate the row height + int h = begin->height(); + int x = d->spacing; + int ih = begin->pixmapRect().height(); + Q3IconViewItem *item = begin; + int i = 0; + int sp = 0; + for (;;) { + int r = calcGridNum(item->width(), d->rastX); + if (item == begin) { + i += r; + sp += r; + x = d->spacing + d->rastX * r; + } else { + sp += r; + i += r; + x = i * d->rastX + sp * d->spacing; + } + if (x > visibleWidth() && item != begin) { + item = item->prev; + break; + } + h = qMax(h, item->height()); + ih = qMax(ih, item->pixmapRect().height()); + Q3IconViewItem *old = item; + item = item->next; + if (!item) { + item = old; + break; + } + } + end = item; + + if (d->rastY != -1) + h = qMax(h, d->rastY); + + // now move the items + item = begin; + i = 0; + sp = 0; + for (;;) { + item->dirty = false; + int r = calcGridNum(item->width(), d->rastX); + if (item == begin) { + if (d->itemTextPos == Bottom) + changed = item->move(d->spacing + (r * d->rastX - item->width()) / 2, + y + ih - item->pixmapRect().height()) || changed; + else + changed = item->move(d->spacing, y + ih - item->pixmapRect().height()) || changed; + i += r; + sp += r; + } else { + sp += r; + int x = i * d->rastX + sp * d->spacing; + if (d->itemTextPos == Bottom) + changed = item->move(x + (r * d->rastX - item->width()) / 2, + y + ih - item->pixmapRect().height()) || changed; + else + changed = item->move(x, y + ih - item->pixmapRect().height()) || changed; + i += r; + } + if (y + h < item->y() + item->height()) + h = qMax(h, ih + item->textRect().height()); + if (item == end) + break; + item = item->next; + } + y += h + d->spacing; + } + + + } else { // -------------------------------- SOUTH ------------------------------ + + int x = y; + + { + int w = 0; + int y = 0; + Q3IconViewItem *item = begin; + for (;;) { + y += d->spacing + item->height(); + if (y > visibleHeight() && item != begin) { + item = item->prev; + break; + } + w = qMax(w, item->width()); + Q3IconViewItem *old = item; + item = item->next; + if (!item) { + item = old; + break; + } + } + end = item; + + if (d->rastX != -1) + w = qMax(w, d->rastX); + + // now move the items + item = begin; + for (;;) { + item->dirty = false; + if (d->itemTextPos == Bottom) { + if (item == begin) + changed = item->move(x + (w - item->width()) / 2, d->spacing) || changed; + else + changed = item->move(x + (w - item->width()) / 2, + item->prev->y() + item->prev->height() + d->spacing) || changed; + } else { + if (item == begin) + changed = item->move(x, d->spacing) || changed; + else + changed = item->move(x, item->prev->y() + item->prev->height() + d->spacing) || changed; + } + if (item == end) + break; + item = item->next; + } + x += w + d->spacing; + } + + y = x; + } + + return end; +} + +/*! + \internal + Calculates how many cells an item of width \a w needs in a grid with of + \a x and returns the result. +*/ + +int Q3IconView::calcGridNum(int w, int x) const +{ + float r = (float)w / (float)x; + if ((w / x) * x != w) + r += 1.0; + return (int)r; +} + +/*! + \internal + Returns the first item of the row which contains \a item. +*/ + +Q3IconViewItem *Q3IconView::rowBegin(Q3IconViewItem *) const +{ + // #### todo + return d->firstItem; +} + +/*! + Sorts and rearranges all the items in the icon view. If \a + ascending is true, the items are sorted in increasing order, + otherwise they are sorted in decreasing order. + + Q3IconViewItem::compare() is used to compare pairs of items. The + sorting is based on the items' keys; these default to the items' + text unless specifically set to something else. + + Note that this function sets the sort order to \a ascending. + + \sa Q3IconViewItem::key(), Q3IconViewItem::setKey(), + Q3IconViewItem::compare(), Q3IconView::setSorting(), + Q3IconView::sortDirection() +*/ + +void Q3IconView::sort(bool ascending) +{ + if (count() == 0) + return; + + d->sortDirection = ascending; + Q3IconViewPrivate::SortableItem *items = new Q3IconViewPrivate::SortableItem[count()]; + + Q3IconViewItem *item = d->firstItem; + int i = 0; + for (; item; item = item->next) + items[i++].item = item; + + qsort(items, count(), sizeof(Q3IconViewPrivate::SortableItem), cmpIconViewItems); + + Q3IconViewItem *prev = 0; + item = 0; + if (ascending) { + for (i = 0; i < (int)count(); ++i) { + item = items[i].item; + if (item) { + item->prev = prev; + if (item->prev) + item->prev->next = item; + item->next = 0; + } + if (i == 0) + d->firstItem = item; + if (i == (int)count() - 1) + d->lastItem = item; + prev = item; + } + } else { + for (i = (int)count() - 1; i >= 0 ; --i) { + item = items[i].item; + if (item) { + item->prev = prev; + if (item->prev) + item->prev->next = item; + item->next = 0; + } + if (i == (int)count() - 1) + d->firstItem = item; + if (i == 0) + d->lastItem = item; + prev = item; + } + } + + delete [] items; + + arrangeItemsInGrid(true); +} + +/*! + \reimp +*/ + +QSize Q3IconView::sizeHint() const +{ + ensurePolished(); + + if (!d->firstItem) + return Q3ScrollView::sizeHint(); + + if (d->dirty && d->firstSizeHint) { + ((Q3IconView*)this)->resizeContents(qMax(400, contentsWidth()), + qMax(400, contentsHeight())); + if (autoArrange()) + ((Q3IconView*)this)->arrangeItemsInGrid(false); + d->firstSizeHint = false; + } + + d->dirty = true; + const QScrollBar *sb = verticalScrollBar(); + QStyleOptionSlider opt; + opt.init(sb); + opt.orientation = sb->orientation(); + int extra = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt, sb) + 2 * frameWidth(); + QSize s(qMin(400, contentsWidth() + extra), + qMin(400, contentsHeight() + extra)); + return s; +} + +/*! + \internal +*/ + +void Q3IconView::updateContents() +{ + viewport()->update(); +} + +/*! + \reimp +*/ + +void Q3IconView::enterEvent(QEvent *e) +{ + Q3ScrollView::enterEvent(e); + emit onViewport(); +} + +/*! + \internal + This function is always called when the geometry of an item changes. + This function moves the item into the correct area in the internal + data structure. +*/ + +void Q3IconView::updateItemContainer(Q3IconViewItem *item) +{ + if (!item || d->containerUpdateLocked || (!isVisible() && autoArrange())) + return; + + if (item->d->container1 && d->firstContainer) { + //Special-case to check if we can use removeLast otherwise use removeAll (slower) + if (item->d->container1->items.last() == item) + item->d->container1->items.removeLast(); + else + item->d->container1->items.removeAll(item); + } + item->d->container1 = 0; + if (item->d->container2 && d->firstContainer) { + //Special-case to check if we can use removeLast otherwise use removeAll (slower) + if (item->d->container2->items.last() == item) + item->d->container2->items.removeLast(); + else + item->d->container2->items.removeAll(item); + } + item->d->container2 = 0; + + Q3IconViewPrivate::ItemContainer *c = d->firstContainer; + if (!c) { + appendItemContainer(); + c = d->firstContainer; + } + + const QRect irect = item->rect(); + bool contains = false; + for (;;) { + if (c->rect.intersects(irect)) { + contains = c->rect.contains(irect); + break; + } + + c = c->n; + if (!c) { + appendItemContainer(); + c = d->lastContainer; + } + } + + if (!c) { + qWarning("Q3IconViewItem::updateItemContainer(): No fitting container found!"); + return; + } + + c->items.append(item); + item->d->container1 = c; + + if (!contains) { + c = c->n; + if (!c) { + appendItemContainer(); + c = d->lastContainer; + } + c->items.append(item); + item->d->container2 = c; + } + if (contentsWidth() < irect.right() || contentsHeight() < irect.bottom()) + resizeContents(qMax(contentsWidth(), irect.right()), qMax(contentsHeight(), irect.bottom())); +} + +/*! + \internal + Appends a new rect area to the internal data structure of the items. +*/ + +void Q3IconView::appendItemContainer() +{ + QSize s; + // #### We have to find out which value is best here + if (d->arrangement == LeftToRight) + s = QSize(INT_MAX - 1, RECT_EXTENSION); + else + s = QSize(RECT_EXTENSION, INT_MAX - 1); + + if (!d->firstContainer) { + d->firstContainer = new Q3IconViewPrivate::ItemContainer(0, 0, QRect(QPoint(0, 0), s)); + d->lastContainer = d->firstContainer; + } else { + if (d->arrangement == LeftToRight) + d->lastContainer = new Q3IconViewPrivate::ItemContainer( + d->lastContainer, 0, QRect(d->lastContainer->rect.bottomLeft(), s)); + else + d->lastContainer = new Q3IconViewPrivate::ItemContainer( + d->lastContainer, 0, QRect(d->lastContainer->rect.topRight(), s)); + } +} + +/*! \internal + + Rebuilds the whole internal data structure. This is done when it's + likely that most/all items change their geometry (e.g. in + arrangeItemsInGrid()), because calling this is then more efficient + than calling updateItemContainer() for each item. +*/ + +void Q3IconView::rebuildContainers() +{ + Q3IconViewPrivate::ItemContainer *c = d->firstContainer, *tmpc; + while (c) { + tmpc = c->n; + delete c; + c = tmpc; + } + d->firstContainer = d->lastContainer = 0; + + Q3IconViewItem *item = d->firstItem; + appendItemContainer(); + c = d->lastContainer; + while (item) { + if (c->rect.contains(item->rect())) { + item->d->container1 = c; + item->d->container2 = 0; + c->items.append(item); + item = item->next; + } else if (c->rect.intersects(item->rect())) { + item->d->container1 = c; + c->items.append(item); + c = c->n; + if (!c) { + appendItemContainer(); + c = d->lastContainer; + } + c->items.append(item); + item->d->container2 = c; + item = item->next; + c = c->p; + } else { + if (d->arrangement == LeftToRight) { + if (item->y() < c->rect.y() && c->p) { + c = c->p; + continue; + } + } else { + if (item->x() < c->rect.x() && c->p) { + c = c->p; + continue; + } + } + + c = c->n; + if (!c) { + appendItemContainer(); + c = d->lastContainer; + } + } + } +} + +/*! + \internal +*/ + +void Q3IconView::movedContents(int, int) +{ + if (d->drawDragShapes) { + drawDragShapes(d->oldDragPos); + d->oldDragPos = QPoint(-1, -1); + } +} + +void Q3IconView::handleItemChange(Q3IconViewItem *old, bool shift, + bool control, bool homeend) +{ + if (d->selectionMode == Single) { + bool block = signalsBlocked(); + blockSignals(true); + if (old) + old->setSelected(false); + blockSignals(block); + d->currentItem->setSelected(true, true); + } else if (d->selectionMode == Extended) { + if (shift) { + if (!d->selectAnchor) { + if (old && !old->selected && old->isSelectable()) { + old->selected = true; + repaintItem(old); + } + d->currentItem->setSelected(true, true); + } else { + Q3IconViewItem *from = d->selectAnchor, *to = d->currentItem; + if (!from || !to) + return; + + // checking if it's downwards and if we span rows + bool downwards = false; + bool spanning = false; + if (d->arrangement == LeftToRight) { + if (from->rect().center().y() < to->rect().center().y()) + downwards = true; + } else { + if (from->rect().center().x() < to->rect().center().x()) + downwards = true; + } + + QRect fr = from->rect(); + QRect tr = to->rect(); + if (d->arrangement == LeftToRight) { + fr.moveTopLeft(QPoint(tr.x(), fr.y())); + if (!tr.intersects(fr)) + spanning = true; + } else { + fr.moveTopLeft(QPoint(fr.x(), tr.y())); + if (!tr.intersects(fr)) + spanning = true; + } + + + // finding the rectangles + QRect topRect, bottomRect, midRect; + if (!spanning) { + midRect = from->rect().united(to->rect()); + } else { + if (downwards) { + topRect = from->rect(); + bottomRect = to->rect(); + } else { + topRect = to->rect(); + bottomRect = from->rect(); + } + if (d->arrangement == LeftToRight) { + topRect.setRight(contentsWidth()); + bottomRect.setLeft(0); + midRect.setRect(0, topRect.bottom(), + contentsWidth(), + bottomRect.top() - topRect.bottom()); + } else { + topRect.setBottom(contentsHeight()); + bottomRect.setTop(0); + midRect.setRect(topRect.right(), + 0, + bottomRect.left() - topRect.right(), + contentsHeight()); + } + } + + // finding contained items and selecting them + Q3IconViewItem *item = 0; + bool changed = false; + bool midValid = midRect.isValid(); + bool topValid = topRect.isValid(); + bool bottomValid = bottomRect.isValid(); + QRect selectedRect, unselectedRect; + for (item = d->firstItem; item; item = item->next) { + bool contained = false; + QPoint itemCenter = item->rect().center(); + if (midValid && midRect.contains(itemCenter)) + contained = true; + if (!contained && topValid && topRect.contains(itemCenter)) + contained = true; + if (!contained && bottomValid && bottomRect.contains(itemCenter)) + contained = true; + + if (contained) { + if (!item->selected && item->isSelectable()) { + changed = true; + item->selected = true; + selectedRect = selectedRect.united(item->rect()); + } + } else if (item->selected && !control) { + item->selected = false; + unselectedRect = unselectedRect.united(item->rect()); + changed = true; + } + } + + QRect viewRect(contentsX(), contentsY(), + visibleWidth(), visibleHeight()); + + if (viewRect.intersects(selectedRect)) { + if (homeend) + Q3ScrollView::updateContents(viewRect.intersected(selectedRect)); + else + repaintContents(viewRect.intersected(selectedRect)); + } + if (viewRect.intersects(unselectedRect)) { + if (homeend) + Q3ScrollView::updateContents(viewRect.intersected(unselectedRect)); + else + repaintContents(viewRect.intersected(unselectedRect)); + } + + if (changed) + emit selectionChanged(); + } + } else if (!control) { + blockSignals(true); + selectAll(false); + blockSignals(false); + d->currentItem->setSelected(true, true); + } + } else { + if (shift) + d->currentItem->setSelected(!d->currentItem->isSelected(), true); + } +} + +QBitmap Q3IconView::mask(QPixmap *pix) const +{ + QBitmap m; + if (d->maskCache.find(QString::number(pix->serialNumber()), m)) + return m; + if (pix->hasAlphaChannel()) + m = pix->mask(); + else + m = pix->createHeuristicMask(); + d->maskCache.insert(QString::number(pix->serialNumber()), m); + return m; +} + +/*! + Returns true if an iconview item is being renamed; otherwise + returns false. +*/ + +bool Q3IconView::isRenaming() const +{ +#ifndef QT_NO_TEXTEDIT + return d->renamingItem && d->renamingItem->renameBox; +#else + return false; +#endif +} + +/*! + \enum Q3IconView::StringComparisonMode + + This enum type is used to set the string comparison mode when + searching for an item. We'll refer to the string being searched + as the 'target' string. + + \value CaseSensitive The strings must match case sensitively. + \value ExactMatch The target and search strings must match exactly. + \value BeginsWith The target string begins with the search string. + \value EndsWith The target string ends with the search string. + \value Contains The target string contains the search string. + + If you OR these flags together (excluding \c CaseSensitive), the + search criteria be applied in the following order: \c ExactMatch, + \c BeginsWith, \c EndsWith, \c Contains. + + Matching is case-insensitive unless \c CaseSensitive is set. \c + CaseSensitive can be OR-ed with any combination of the other + flags. + + \sa ComparisonFlags +*/ + +/*! + \typedef Q3IconView::ComparisonFlags + + This typedef is used in Q3IconView's API for values that are OR'd + combinations of \l StringComparisonMode values. + + \sa StringComparisonMode +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_ICONVIEW |