#include "kitemlistview.h"
+#include <KDebug>
+#include "kitemlistcontainer.h"
#include "kitemlistcontroller.h"
-#include "kitemlistheader_p.h"
-#include "kitemlistrubberband_p.h"
+#include "kitemlistheader.h"
#include "kitemlistselectionmanager.h"
-#include "kitemlistsizehintresolver_p.h"
-#include "kitemlistviewlayouter_p.h"
-#include "kitemlistviewanimation_p.h"
#include "kitemlistwidget.h"
-#include <KDebug>
+#include "private/kitemlistheaderwidget.h"
+#include "private/kitemlistrubberband.h"
+#include "private/kitemlistsizehintresolver.h"
+#include "private/kitemlistviewlayouter.h"
+#include "private/kitemlistviewanimation.h"
#include <QCursor>
#include <QGraphicsSceneMouseEvent>
+#include <QGraphicsView>
#include <QPainter>
#include <QPropertyAnimation>
#include <QStyle>
#include <QStyleOptionRubberBand>
#include <QTimer>
+#include "kitemlistviewaccessible.h"
+
namespace {
// Time in ms until reaching the autoscroll margin triggers
// an initial autoscrolling
const int RepeatingAutoScrollDelay = 1000 / 60;
}
+#ifndef QT_NO_ACCESSIBILITY
+QAccessibleInterface* accessibleInterfaceFactory(const QString &key, QObject *object)
+{
+ Q_UNUSED(key)
+
+ if (KItemListContainer* container = qobject_cast<KItemListContainer*>(object)) {
+ return new KItemListContainerAccessible(container);
+ } else if (KItemListView* view = qobject_cast<KItemListView*>(object)) {
+ return new KItemListViewAccessible(view);
+ }
+
+ return 0;
+}
+#endif
+
KItemListView::KItemListView(QGraphicsWidget* parent) :
QGraphicsWidget(parent),
m_enabledSelectionToggles(false),
m_grouped(false),
m_supportsItemExpanding(false),
+ m_editingRole(false),
m_activeTransactions(0),
m_endTransactionAnimationHint(Animation),
m_itemSize(),
m_controller(0),
m_model(0),
m_visibleRoles(),
- m_visibleRolesSizes(),
- m_stretchedVisibleRolesSizes(),
m_widgetCreator(0),
m_groupHeaderCreator(0),
m_styleOption(),
m_autoScrollIncrement(0),
m_autoScrollTimer(0),
m_header(0),
- m_useHeaderWidths(false)
+ m_headerWidget(0),
+ m_dropIndicator()
{
setAcceptHoverEvents(true);
m_rubberBand = new KItemListRubberBand(this);
connect(m_rubberBand, SIGNAL(activationChanged(bool)), this, SLOT(slotRubberBandActivationChanged(bool)));
-}
-
-KItemListView::~KItemListView()
-{
- delete m_sizeHintResolver;
- m_sizeHintResolver = 0;
-}
-
-void KItemListView::setScrollOrientation(Qt::Orientation orientation)
-{
- const Qt::Orientation previousOrientation = m_layouter->scrollOrientation();
- if (orientation == previousOrientation) {
- return;
- }
-
- m_layouter->setScrollOrientation(orientation);
- m_animation->setScrollOrientation(orientation);
- m_sizeHintResolver->clearCache();
- if (m_grouped) {
- QMutableHashIterator<KItemListWidget*, KItemListGroupHeader*> it (m_visibleGroups);
- while (it.hasNext()) {
- it.next();
- it.value()->setScrollOrientation(orientation);
- }
- updateGroupHeaderHeight();
-
- }
+ m_headerWidget = new KItemListHeaderWidget(this);
+ m_headerWidget->setVisible(false);
- doLayout(NoAnimation);
+ m_header = new KItemListHeader(this);
- onScrollOrientationChanged(orientation, previousOrientation);
- emit scrollOrientationChanged(orientation, previousOrientation);
-}
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::installFactory(accessibleInterfaceFactory);
+#endif
-Qt::Orientation KItemListView::scrollOrientation() const
-{
- return m_layouter->scrollOrientation();
}
-void KItemListView::setItemSize(const QSizeF& itemSize)
+KItemListView::~KItemListView()
{
- const QSizeF previousSize = m_itemSize;
- if (itemSize == previousSize) {
- return;
- }
-
- // Skip animations when the number of rows or columns
- // are changed in the grid layout. Although the animation
- // engine can handle this usecase, it looks obtrusive.
- const bool animate = !changesItemGridLayout(m_layouter->size(),
- itemSize,
- m_layouter->itemMargin());
-
- const bool updateAlternateBackgrounds = (m_visibleRoles.count() > 1) &&
- (( m_itemSize.isEmpty() && !itemSize.isEmpty()) ||
- (!m_itemSize.isEmpty() && itemSize.isEmpty()));
-
- m_itemSize = itemSize;
-
- if (updateAlternateBackgrounds) {
- // For an empty item size alternate backgrounds are drawn if more than
- // one role is shown. Assure that the backgrounds for visible items are
- // updated when changing the size in this context.
- QHashIterator<int, KItemListWidget*> it(m_visibleItems);
- while (it.hasNext()) {
- it.next();
- updateAlternateBackgroundForWidget(it.value());
- }
- }
-
- if (itemSize.isEmpty()) {
- updateVisibleRolesSizes();
- } else {
- m_layouter->setItemSize(itemSize);
- }
+ // The group headers are children of the widgets created by
+ // widgetCreator(). So it is mandatory to delete the group headers
+ // first.
+ delete m_groupHeaderCreator;
+ m_groupHeaderCreator = 0;
- m_sizeHintResolver->clearCache();
- doLayout(animate ? Animation : NoAnimation);
- onItemSizeChanged(itemSize, previousSize);
-}
+ delete m_widgetCreator;
+ m_widgetCreator = 0;
-QSizeF KItemListView::itemSize() const
-{
- return m_itemSize;
+ delete m_sizeHintResolver;
+ m_sizeHintResolver = 0;
}
void KItemListView::setScrollOffset(qreal offset)
}
m_layouter->setItemOffset(offset);
- if (m_header) {
- m_header->setPos(-offset, 0);
+ if (m_headerWidget->isVisible()) {
+ m_headerWidget->setOffset(offset);
}
// Don't check whether the m_layoutTimer is active: Changing the
{
const QList<QByteArray> previousRoles = m_visibleRoles;
m_visibleRoles = roles;
+ onVisibleRolesChanged(roles, previousRoles);
- const bool updateAlternateBackgrounds = m_itemSize.isEmpty() &&
- ((roles.count() > 1 && previousRoles.count() <= 1) ||
- (roles.count() <= 1 && previousRoles.count() > 1));
+ m_sizeHintResolver->clearCache();
+ m_layouter->markAsDirty();
+
+ if (m_itemSize.isEmpty()) {
+ m_headerWidget->setColumns(roles);
+ updatePreferredColumnWidths();
+ if (!m_headerWidget->automaticColumnResizing()) {
+ // The column-width of new roles are still 0. Apply the preferred
+ // column-width as default with.
+ foreach (const QByteArray& role, m_visibleRoles) {
+ if (m_headerWidget->columnWidth(role) == 0) {
+ const qreal width = m_headerWidget->preferredColumnWidth(role);
+ m_headerWidget->setColumnWidth(role, width);
+ }
+ }
+
+ applyColumnWidthsFromHeader();
+ }
+ }
+
+ const bool alternateBackgroundsChanged = m_itemSize.isEmpty() &&
+ ((roles.count() > 1 && previousRoles.count() <= 1) ||
+ (roles.count() <= 1 && previousRoles.count() > 1));
QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
KItemListWidget* widget = it.value();
widget->setVisibleRoles(roles);
- widget->setVisibleRolesSizes(m_stretchedVisibleRolesSizes);
- if (updateAlternateBackgrounds) {
+ if (alternateBackgroundsChanged) {
updateAlternateBackgroundForWidget(widget);
}
}
- m_sizeHintResolver->clearCache();
- m_layouter->markAsDirty();
-
- if (m_header) {
- m_header->setVisibleRoles(roles);
- m_header->setVisibleRolesWidths(headerRolesWidths());
- m_useHeaderWidths = false;
- }
-
- updateVisibleRolesSizes();
doLayout(NoAnimation);
-
- onVisibleRolesChanged(roles, previousRoles);
}
QList<QByteArray> KItemListView::visibleRoles() const
void KItemListView::setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator)
{
+ if (m_widgetCreator) {
+ delete m_widgetCreator;
+ }
m_widgetCreator = widgetCreator;
}
KItemListWidgetCreatorBase* KItemListView::widgetCreator() const
{
+ if (!m_widgetCreator) {
+ m_widgetCreator = defaultWidgetCreator();
+ }
return m_widgetCreator;
}
void KItemListView::setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator)
{
+ if (m_groupHeaderCreator) {
+ delete m_groupHeaderCreator;
+ }
m_groupHeaderCreator = groupHeaderCreator;
}
KItemListGroupHeaderCreatorBase* KItemListView::groupHeaderCreator() const
{
+ if (!m_groupHeaderCreator) {
+ m_groupHeaderCreator = defaultGroupHeaderCreator();
+ }
return m_groupHeaderCreator;
}
-void KItemListView::setStyleOption(const KItemListStyleOption& option)
+QSizeF KItemListView::itemSize() const
{
- const KItemListStyleOption previousOption = m_styleOption;
- m_styleOption = option;
-
- bool animate = true;
- const QSizeF margin(option.horizontalMargin, option.verticalMargin);
- if (margin != m_layouter->itemMargin()) {
- // Skip animations when the number of rows or columns
- // are changed in the grid layout. Although the animation
- // engine can handle this usecase, it looks obtrusive.
- animate = !changesItemGridLayout(m_layouter->size(),
- m_layouter->itemSize(),
- margin);
- m_layouter->setItemMargin(margin);
- }
-
- if (m_grouped) {
- updateGroupHeaderHeight();
- }
-
- QHashIterator<int, KItemListWidget*> it(m_visibleItems);
- while (it.hasNext()) {
- it.next();
- it.value()->setStyleOption(option);
- }
-
- m_sizeHintResolver->clearCache();
- doLayout(animate ? Animation : NoAnimation);
-
- onStyleOptionChanged(option, previousOption);
+ return m_itemSize;
}
const KItemListStyleOption& KItemListView::styleOption() const
const QSizeF newSize = rect.size();
if (m_itemSize.isEmpty()) {
- // The item size is dynamic:
- // Changing the geometry does not require to do an expensive
- // update of the visible-roles sizes, only the stretched sizes
- // need to be adjusted to the new size.
- updateStretchedVisibleRolesSizes();
-
- if (m_useHeaderWidths) {
- QSizeF dynamicItemSize = m_layouter->itemSize();
-
- if (m_itemSize.width() < 0) {
- const qreal requiredWidth = visibleRolesSizesWidthSum();
- if (newSize.width() > requiredWidth) {
- dynamicItemSize.setWidth(newSize.width());
- }
- const qreal headerWidth = qMax(newSize.width(), requiredWidth);
- m_header->resize(headerWidth, m_header->size().height());
- }
-
- if (m_itemSize.height() < 0) {
- const qreal requiredHeight = visibleRolesSizesHeightSum();
- if (newSize.height() > requiredHeight) {
- dynamicItemSize.setHeight(newSize.height());
- }
- // TODO: KItemListHeader is not prepared for vertical alignment
- }
-
+ m_headerWidget->resize(rect.width(), m_headerWidget->size().height());
+ if (m_headerWidget->automaticColumnResizing()) {
+ applyAutomaticColumnWidths();
+ } else {
+ const qreal requiredWidth = columnWidthsSum();
+ const QSizeF dynamicItemSize(qMax(newSize.width(), requiredWidth),
+ m_itemSize.height());
m_layouter->setItemSize(dynamicItemSize);
}
// Triggering a synchronous layout is fine from a performance point of view,
// as with dynamic item sizes no moving animation must be done.
m_layouter->setSize(newSize);
- doLayout(Animation);
+ doLayout(NoAnimation);
} else {
const bool animate = !changesItemGridLayout(newSize,
m_layouter->itemSize(),
QSizeF KItemListView::itemSizeHint(int index) const
{
- Q_UNUSED(index);
- return itemSize();
-}
-
-QHash<QByteArray, QSizeF> KItemListView::visibleRolesSizes(const KItemRangeList& itemRanges) const
-{
- Q_UNUSED(itemRanges);
- return QHash<QByteArray, QSizeF>();
+ return widgetCreator()->itemSizeHint(index, this);
}
void KItemListView::setSupportsItemExpanding(bool supportsExpanding)
void KItemListView::scrollToItem(int index)
{
QRectF viewGeometry = geometry();
- if (m_header) {
- const qreal headerHeight = m_header->size().height();
+ if (m_headerWidget->isVisible()) {
+ const qreal headerHeight = m_headerWidget->size().height();
viewGeometry.adjust(0, headerHeight, 0, 0);
}
const QRectF currentRect = itemRect(index);
return m_activeTransactions > 0;
}
-void KItemListView::setHeaderShown(bool show)
+void KItemListView::setHeaderVisible(bool visible)
{
+ if (visible && !m_headerWidget->isVisible()) {
+ QStyleOptionHeader option;
+ const QSize headerSize = style()->sizeFromContents(QStyle::CT_HeaderSection,
+ &option, QSize());
- if (show && !m_header) {
- m_header = new KItemListHeader(this);
- m_header->setPos(0, 0);
- m_header->setModel(m_model);
- m_header->setVisibleRoles(m_visibleRoles);
- m_header->setVisibleRolesWidths(headerRolesWidths());
- m_header->setZValue(1);
+ m_headerWidget->setPos(0, 0);
+ m_headerWidget->resize(size().width(), headerSize.height());
+ m_headerWidget->setModel(m_model);
+ m_headerWidget->setColumns(m_visibleRoles);
+ m_headerWidget->setZValue(1);
- connect(m_header, SIGNAL(visibleRoleWidthChanged(QByteArray,qreal,qreal)),
- this, SLOT(slotVisibleRoleWidthChanged(QByteArray,qreal,qreal)));
- connect(m_header, SIGNAL(visibleRoleMoved(QByteArray,int,int)),
- this, SLOT(slotVisibleRoleMoved(QByteArray,int,int)));
- connect(m_header, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
+ connect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)),
+ this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal)));
+ connect(m_headerWidget, SIGNAL(columnMoved(QByteArray,int,int)),
+ this, SLOT(slotHeaderColumnMoved(QByteArray,int,int)));
+ connect(m_headerWidget, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
this, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
- connect(m_header, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
+ connect(m_headerWidget, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
this, SIGNAL(sortRoleChanged(QByteArray,QByteArray)));
- m_useHeaderWidths = false;
+ m_layouter->setHeaderHeight(headerSize.height());
+ m_headerWidget->setVisible(true);
+ } else if (!visible && m_headerWidget->isVisible()) {
+ disconnect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)),
+ this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal)));
+ disconnect(m_headerWidget, SIGNAL(columnMoved(QByteArray,int,int)),
+ this, SLOT(slotHeaderColumnMoved(QByteArray,int,int)));
+ disconnect(m_headerWidget, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
+ this, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
+ disconnect(m_headerWidget, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
+ this, SIGNAL(sortRoleChanged(QByteArray,QByteArray)));
- m_layouter->setHeaderHeight(m_header->size().height());
- } else if (!show && m_header) {
- delete m_header;
- m_header = 0;
- m_useHeaderWidths = false;
m_layouter->setHeaderHeight(0);
+ m_headerWidget->setVisible(false);
}
}
-bool KItemListView::isHeaderShown() const
+bool KItemListView::isHeaderVisible() const
+{
+ return m_headerWidget->isVisible();
+}
+
+KItemListHeader* KItemListView::header() const
{
- return m_header != 0;
+ return m_header;
}
QPixmap KItemListView::createDragPixmap(const QSet<int>& indexes) const
{
- Q_UNUSED(indexes);
- return QPixmap();
+ QPixmap pixmap;
+
+ if (indexes.count() == 1) {
+ KItemListWidget* item = m_visibleItems.value(indexes.toList().first());
+ QGraphicsView* graphicsView = scene()->views()[0];
+ if (item && graphicsView) {
+ pixmap = item->createDragPixmap(0, graphicsView);
+ }
+ } else {
+ // TODO: Not implemented yet. Probably extend the interface
+ // from KItemListWidget::createDragPixmap() to return a pixmap
+ // that can be used for multiple indexes.
+ }
+
+ return pixmap;
+}
+
+void KItemListView::editRole(int index, const QByteArray& role)
+{
+ KItemListWidget* widget = m_visibleItems.value(index);
+ if (!widget || m_editingRole) {
+ return;
+ }
+
+ m_editingRole = true;
+ widget->setEditedRole(role);
+
+ connect(widget, SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)),
+ this, SLOT(slotRoleEditingCanceled(int,QByteArray,QVariant)));
+ connect(widget, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)),
+ this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant)));
}
void KItemListView::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
opt.rect = rubberBandRect.toRect();
style()->drawControl(QStyle::CE_RubberBand, &opt, painter);
}
+
+ if (!m_dropIndicator.isEmpty()) {
+ const QRectF r = m_dropIndicator.toRect();
+
+ QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color();
+ painter->setPen(color);
+
+ // TODO: The following implementation works only for a vertical scroll-orientation
+ // and assumes a height of the m_draggingInsertIndicator of 1.
+ Q_ASSERT(r.height() == 1);
+ painter->drawLine(r.left() + 1, r.top(), r.right() - 1, r.top());
+
+ color.setAlpha(128);
+ painter->setPen(color);
+ painter->drawRect(r.left(), r.top() - 1, r.width() - 1, 2);
+ }
+}
+
+void KItemListView::setItemSize(const QSizeF& size)
+{
+ const QSizeF previousSize = m_itemSize;
+ if (size == previousSize) {
+ return;
+ }
+
+ // Skip animations when the number of rows or columns
+ // are changed in the grid layout. Although the animation
+ // engine can handle this usecase, it looks obtrusive.
+ const bool animate = !changesItemGridLayout(m_layouter->size(),
+ size,
+ m_layouter->itemMargin());
+
+ const bool alternateBackgroundsChanged = (m_visibleRoles.count() > 1) &&
+ (( m_itemSize.isEmpty() && !size.isEmpty()) ||
+ (!m_itemSize.isEmpty() && size.isEmpty()));
+
+ m_itemSize = size;
+
+ if (alternateBackgroundsChanged) {
+ // For an empty item size alternate backgrounds are drawn if more than
+ // one role is shown. Assure that the backgrounds for visible items are
+ // updated when changing the size in this context.
+ updateAlternateBackgrounds();
+ }
+
+ if (size.isEmpty()) {
+ if (m_headerWidget->automaticColumnResizing()) {
+ updatePreferredColumnWidths();
+ } else {
+ // Only apply the changed height and respect the header widths
+ // set by the user
+ const qreal currentWidth = m_layouter->itemSize().width();
+ const QSizeF newSize(currentWidth, size.height());
+ m_layouter->setItemSize(newSize);
+ }
+ } else {
+ m_layouter->setItemSize(size);
+ }
+
+ m_sizeHintResolver->clearCache();
+ doLayout(animate ? Animation : NoAnimation);
+ onItemSizeChanged(size, previousSize);
+}
+
+void KItemListView::setStyleOption(const KItemListStyleOption& option)
+{
+ const KItemListStyleOption previousOption = m_styleOption;
+ m_styleOption = option;
+
+ bool animate = true;
+ const QSizeF margin(option.horizontalMargin, option.verticalMargin);
+ if (margin != m_layouter->itemMargin()) {
+ // Skip animations when the number of rows or columns
+ // are changed in the grid layout. Although the animation
+ // engine can handle this usecase, it looks obtrusive.
+ animate = !changesItemGridLayout(m_layouter->size(),
+ m_layouter->itemSize(),
+ margin);
+ m_layouter->setItemMargin(margin);
+ }
+
+ if (m_grouped) {
+ updateGroupHeaderHeight();
+ }
+
+ if (animate && previousOption.maxTextSize != option.maxTextSize) {
+ // Animating a change of the maximum text size just results in expensive
+ // temporary eliding and clipping operations and does not look good visually.
+ animate = false;
+ }
+
+ QHashIterator<int, KItemListWidget*> it(m_visibleItems);
+ while (it.hasNext()) {
+ it.next();
+ it.value()->setStyleOption(option);
+ }
+
+ m_sizeHintResolver->clearCache();
+ m_layouter->markAsDirty();
+ doLayout(animate ? Animation : NoAnimation);
+
+ if (m_itemSize.isEmpty()) {
+ updatePreferredColumnWidths();
+ }
+
+ onStyleOptionChanged(option, previousOption);
+}
+
+void KItemListView::setScrollOrientation(Qt::Orientation orientation)
+{
+ const Qt::Orientation previousOrientation = m_layouter->scrollOrientation();
+ if (orientation == previousOrientation) {
+ return;
+ }
+
+ m_layouter->setScrollOrientation(orientation);
+ m_animation->setScrollOrientation(orientation);
+ m_sizeHintResolver->clearCache();
+
+ if (m_grouped) {
+ QMutableHashIterator<KItemListWidget*, KItemListGroupHeader*> it (m_visibleGroups);
+ while (it.hasNext()) {
+ it.next();
+ it.value()->setScrollOrientation(orientation);
+ }
+ updateGroupHeaderHeight();
+
+ }
+
+ doLayout(NoAnimation);
+
+ onScrollOrientationChanged(orientation, previousOrientation);
+ emit scrollOrientationChanged(orientation, previousOrientation);
+}
+
+Qt::Orientation KItemListView::scrollOrientation() const
+{
+ return m_layouter->scrollOrientation();
+}
+
+KItemListWidgetCreatorBase* KItemListView::defaultWidgetCreator() const
+{
+ return 0;
+}
+
+KItemListGroupHeaderCreatorBase* KItemListView::defaultGroupHeaderCreator() const
+{
+ return 0;
}
void KItemListView::initializeItemListWidget(KItemListWidget* item)
bool KItemListView::event(QEvent* event)
{
// Forward all events to the controller and handle them there
- if (m_controller && m_controller->processEvent(event, transform())) {
+ if (!m_editingRole && m_controller && m_controller->processEvent(event, transform())) {
event->accept();
return true;
}
void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges)
{
- updateVisibleRolesSizes(itemRanges);
+ if (m_itemSize.isEmpty()) {
+ updatePreferredColumnWidths(itemRanges);
+ }
const bool hasMultipleRanges = (itemRanges.count() > 1);
if (hasMultipleRanges) {
#endif
m_endTransactionAnimationHint = NoAnimation;
endTransaction();
+
updateSiblingsInformation();
}
+
+ if (m_grouped && (hasMultipleRanges || itemRanges.first().count < m_model->count())) {
+ // In case if items of the same group have been inserted before an item that
+ // currently represents the first item of the group, the group header of
+ // this item must be removed.
+ updateVisibleGroupHeaders();
+ }
+
+ if (useAlternateBackgrounds()) {
+ updateAlternateBackgrounds();
+ }
}
void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges)
{
- updateVisibleRolesSizes();
+ if (m_itemSize.isEmpty()) {
+ // Don't pass the item-range: The preferred column-widths of
+ // all items must be adjusted when removing items.
+ updatePreferredColumnWidths();
+ }
const bool hasMultipleRanges = (itemRanges.count() > 1);
if (hasMultipleRanges) {
m_layouter->markAsDirty();
+ int removedItemsCount = 0;
+ for (int i = 0; i < itemRanges.count(); ++i) {
+ removedItemsCount += itemRanges[i].count;
+ }
+
for (int i = itemRanges.count() - 1; i >= 0; --i) {
- const KItemRange& range = itemRanges.at(i);
+ const KItemRange& range = itemRanges[i];
const int index = range.index;
const int count = range.count;
if (index < 0 || count <= 0) {
const int firstRemovedIndex = index;
const int lastRemovedIndex = index + count - 1;
- const int lastIndex = m_model->count() + count - 1;
+ const int lastIndex = m_model->count() - 1 + removedItemsCount;
+ removedItemsCount -= count;
// Remove all KItemListWidget instances that got deleted
for (int i = firstRemovedIndex; i <= lastRemovedIndex; ++i) {
endTransaction();
updateSiblingsInformation();
}
+
+ if (m_grouped && (hasMultipleRanges || m_model->count() > 0)) {
+ // In case if the first item of a group has been removed, the group header
+ // must be applied to the next visible item.
+ updateVisibleGroupHeaders();
+ }
+
+ if (useAlternateBackgrounds()) {
+ updateAlternateBackgrounds();
+ }
}
void KItemListView::slotItemsMoved(const KItemRange& itemRange, const QList<int>& movedToIndexes)
const QSet<QByteArray>& roles)
{
const bool updateSizeHints = itemSizeHintUpdateRequired(roles);
- if (updateSizeHints) {
- updateVisibleRolesSizes(itemRanges);
+ if (updateSizeHints && m_itemSize.isEmpty()) {
+ updatePreferredColumnWidths(itemRanges);
}
foreach (const KItemRange& itemRange, itemRanges) {
doLayout(NoAnimation);
}
}
+ QAccessible::updateAccessibility(this, 0, QAccessible::TableModelChanged);
}
void KItemListView::slotGroupedSortingChanged(bool current)
// Changing the group mode requires to update the alternate backgrounds
// as with the enabled group mode the altering is done on base of the first
// group item.
- QHashIterator<int, KItemListWidget*> it(m_visibleItems);
- while (it.hasNext()) {
- it.next();
- updateAlternateBackgroundForWidget(it.value());
- }
+ updateAlternateBackgrounds();
}
-
updateSiblingsInformation();
doLayout(NoAnimation);
}
if (currentWidget) {
currentWidget->setCurrent(true);
}
+ QAccessible::updateAccessibility(this, current+1, QAccessible::Focus);
}
void KItemListView::slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous)
// by m_visibleWidgets and must be deleted manually after the animation has
// been finished.
recycleGroupHeaderForWidget(itemListWidget);
- m_widgetCreator->recycle(itemListWidget);
+ widgetCreator()->recycle(itemListWidget);
break;
}
update();
}
-void KItemListView::slotVisibleRoleWidthChanged(const QByteArray& role,
- qreal currentWidth,
- qreal previousWidth)
+void KItemListView::slotHeaderColumnWidthChanged(const QByteArray& role,
+ qreal currentWidth,
+ qreal previousWidth)
{
+ Q_UNUSED(role);
+ Q_UNUSED(currentWidth);
Q_UNUSED(previousWidth);
- m_useHeaderWidths = true;
-
- if (m_visibleRolesSizes.contains(role)) {
- QSizeF roleSize = m_visibleRolesSizes.value(role);
- roleSize.setWidth(currentWidth);
- m_visibleRolesSizes.insert(role, roleSize);
- m_stretchedVisibleRolesSizes.insert(role, roleSize);
-
- // Apply the new size to the layouter
- QSizeF dynamicItemSize = m_itemSize;
- if (dynamicItemSize.width() < 0) {
- const qreal requiredWidth = visibleRolesSizesWidthSum();
- dynamicItemSize.setWidth(qMax(size().width(), requiredWidth));
- }
- if (dynamicItemSize.height() < 0) {
- const qreal requiredHeight = visibleRolesSizesHeightSum();
- dynamicItemSize.setHeight(qMax(size().height(), requiredHeight));
- }
-
- m_layouter->setItemSize(dynamicItemSize);
-
- // Update the role sizes for all visible widgets
- QHashIterator<int, KItemListWidget*> it(m_visibleItems);
- while (it.hasNext()) {
- it.next();
- it.value()->setVisibleRolesSizes(m_stretchedVisibleRolesSizes);
- }
- doLayout(NoAnimation);
- }
+ m_headerWidget->setAutomaticColumnResizing(false);
+ applyColumnWidthsFromHeader();
+ doLayout(NoAnimation);
}
-void KItemListView::slotVisibleRoleMoved(const QByteArray& role,
- int currentIndex,
- int previousIndex)
+void KItemListView::slotHeaderColumnMoved(const QByteArray& role,
+ int currentIndex,
+ int previousIndex)
{
Q_ASSERT(m_visibleRoles[previousIndex] == role);
const qreal newScrollOffset = qMin(scrollOffset() + m_autoScrollIncrement, maxVisibleOffset);
setScrollOffset(newScrollOffset);
- // Trigger the autoscroll timer which will periodically call
- // triggerAutoScrolling()
- m_autoScrollTimer->start(RepeatingAutoScrollDelay);
+ // Trigger the autoscroll timer which will periodically call
+ // triggerAutoScrolling()
+ m_autoScrollTimer->start(RepeatingAutoScrollDelay);
}
void KItemListView::slotGeometryOfGroupHeaderParentChanged()
updateGroupHeaderLayout(widget);
}
+void KItemListView::slotRoleEditingCanceled(int index, const QByteArray& role, const QVariant& value)
+{
+ disconnectRoleEditingSignals(index);
+
+ emit roleEditingCanceled(index, role, value);
+ m_editingRole = false;
+}
+
+void KItemListView::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value)
+{
+ disconnectRoleEditingSignals(index);
+
+ emit roleEditingFinished(index, role, value);
+ m_editingRole = false;
+}
+
void KItemListView::setController(KItemListController* controller)
{
if (m_controller != controller) {
this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
}
+ m_sizeHintResolver->clearCache();
+
m_model = model;
m_layouter->setModel(model);
m_grouped = model->groupedSorting();
this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
+
+ const int itemCount = m_model->count();
+ if (itemCount > 0) {
+ m_sizeHintResolver->itemsInserted(0, itemCount);
+ slotItemsInserted(KItemRangeList() << KItemRange(0, itemCount));
+ }
}
onModelChanged(model, previous);
}
if (animate) {
+ if (m_animation->isStarted(widget, KItemListViewAnimation::MovingAnimation)) {
+ m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos);
+ applyNewPos = false;
+ }
+
const bool itemsRemoved = (changedCount < 0);
const bool itemsInserted = (changedCount > 0);
if (itemsRemoved && (i >= changedIndex + changedCount + 1)) {
bool startMovingAnim = false;
- // When having a grid the moving-animation should only be started, if it is done within
- // one row in the vertical scroll-orientation or one column in the horizontal scroll-orientation.
- // Otherwise instead of a moving-animation a create-animation on the new position will be used
- // instead. This is done to prevent overlapping (and confusing) moving-animations.
- const int index = widget->index();
- const Cell cell = m_visibleCells.value(index);
- if (cell.column >= 0 && cell.row >= 0) {
- if (scrollOrientation() == Qt::Vertical) {
- startMovingAnim = (cell.row == m_layouter->itemRow(index));
- } else {
- startMovingAnim = (cell.column == m_layouter->itemColumn(index));
+ if (m_itemSize.isEmpty()) {
+ // The items are not aligned in a grid but either as columns or rows.
+ startMovingAnim = true;
+ } else {
+ // When having a grid the moving-animation should only be started, if it is done within
+ // one row in the vertical scroll-orientation or one column in the horizontal scroll-orientation.
+ // Otherwise instead of a moving-animation a create-animation on the new position will be used
+ // instead. This is done to prevent overlapping (and confusing) moving-animations.
+ const int index = widget->index();
+ const Cell cell = m_visibleCells.value(index);
+ if (cell.column >= 0 && cell.row >= 0) {
+ if (scrollOrientation() == Qt::Vertical) {
+ startMovingAnim = (cell.row == m_layouter->itemRow(index));
+ } else {
+ startMovingAnim = (cell.column == m_layouter->itemColumn(index));
+ }
}
}
KItemListWidget* KItemListView::createWidget(int index)
{
- KItemListWidget* widget = m_widgetCreator->create(this);
+ KItemListWidget* widget = widgetCreator()->create(this);
widget->setFlag(QGraphicsItem::ItemStacksBehindParent);
m_visibleItems.insert(index, widget);
m_visibleItems.remove(index);
m_visibleCells.remove(index);
- m_widgetCreator->recycle(widget);
+ widgetCreator()->recycle(widget);
}
void KItemListView::setWidgetIndex(KItemListWidget* widget, int index)
void KItemListView::updateWidgetProperties(KItemListWidget* widget, int index)
{
widget->setVisibleRoles(m_visibleRoles);
- widget->setVisibleRolesSizes(m_stretchedVisibleRolesSizes);
+ updateWidgetColumnWidths(widget);
widget->setStyleOption(m_styleOption);
const KItemListSelectionManager* selectionManager = m_controller->selectionManager();
}
const QList<QPair<int, QVariant> > groups = model()->groups();
- if (groups.isEmpty()) {
+ if (groups.isEmpty() || !groupHeaderCreator()) {
return;
}
KItemListGroupHeader* groupHeader = m_visibleGroups.value(widget);
if (!groupHeader) {
- groupHeader = m_groupHeaderCreator->create(this);
+ groupHeader = groupHeaderCreator()->create(this);
groupHeader->setParentItem(widget);
m_visibleGroups.insert(widget, groupHeader);
connect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged()));
// In the vertical scroll orientation the group header should always span
// the whole width no matter which temporary position the parent widget
// has. In this case the x-position and width will be adjusted manually.
- groupHeader->setPos(-widget->x(), -groupHeaderRect.height());
- groupHeader->resize(size().width(), groupHeaderRect.size().height());
+ const qreal x = -widget->x() - itemOffset();
+ const qreal width = maximumItemOffset();
+ groupHeader->setPos(x, -groupHeaderRect.height());
+ groupHeader->resize(width, groupHeaderRect.size().height());
} else {
groupHeader->setPos(groupHeaderRect.x() - itemRect.x(), -widget->y());
groupHeader->resize(groupHeaderRect.size());
KItemListGroupHeader* header = m_visibleGroups.value(widget);
if (header) {
header->setParentItem(0);
- m_groupHeaderCreator->recycle(header);
+ groupHeaderCreator()->recycle(header);
m_visibleGroups.remove(widget);
disconnect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged()));
}
return mid;
}
+void KItemListView::updateAlternateBackgrounds()
+{
+ QHashIterator<int, KItemListWidget*> it(m_visibleItems);
+ while (it.hasNext()) {
+ it.next();
+ updateAlternateBackgroundForWidget(it.value());
+ }
+}
+
void KItemListView::updateAlternateBackgroundForWidget(KItemListWidget* widget)
{
bool enabled = useAlternateBackgrounds();
return m_itemSize.isEmpty() && m_visibleRoles.count() > 1;
}
-QHash<QByteArray, qreal> KItemListView::headerRolesWidths() const
+QHash<QByteArray, qreal> KItemListView::preferredColumnWidths(const KItemRangeList& itemRanges) const
+{
+ QElapsedTimer timer;
+ timer.start();
+
+ QHash<QByteArray, qreal> widths;
+
+ // Calculate the minimum width for each column that is required
+ // to show the headline unclipped.
+ const QFontMetricsF fontMetrics(m_headerWidget->font());
+ const int gripMargin = m_headerWidget->style()->pixelMetric(QStyle::PM_HeaderGripMargin);
+ const int headerMargin = m_headerWidget->style()->pixelMetric(QStyle::PM_HeaderMargin);
+ foreach (const QByteArray& visibleRole, visibleRoles()) {
+ const QString headerText = m_model->roleDescription(visibleRole);
+ const qreal headerWidth = fontMetrics.width(headerText) + gripMargin + headerMargin * 2;
+ widths.insert(visibleRole, headerWidth);
+ }
+
+ // Calculate the preferred column withs for each item and ignore values
+ // smaller than the width for showing the headline unclipped.
+ const KItemListWidgetCreatorBase* creator = widgetCreator();
+ int calculatedItemCount = 0;
+ bool maxTimeExceeded = false;
+ foreach (const KItemRange& itemRange, itemRanges) {
+ const int startIndex = itemRange.index;
+ const int endIndex = startIndex + itemRange.count - 1;
+
+ for (int i = startIndex; i <= endIndex; ++i) {
+ foreach (const QByteArray& visibleRole, visibleRoles()) {
+ qreal maxWidth = widths.value(visibleRole, 0);
+ const qreal width = creator->preferredRoleColumnWidth(visibleRole, i, this);
+ maxWidth = qMax(width, maxWidth);
+ widths.insert(visibleRole, maxWidth);
+ }
+
+ if (calculatedItemCount > 100 && timer.elapsed() > 200) {
+ // When having several thousands of items calculating the sizes can get
+ // very expensive. We accept a possibly too small role-size in favour
+ // of having no blocking user interface.
+ maxTimeExceeded = true;
+ break;
+ }
+ ++calculatedItemCount;
+ }
+ if (maxTimeExceeded) {
+ break;
+ }
+ }
+
+ return widths;
+}
+
+void KItemListView::applyColumnWidthsFromHeader()
{
- QHash<QByteArray, qreal> rolesWidths;
+ // Apply the new size to the layouter
+ const qreal requiredWidth = columnWidthsSum();
+ const QSizeF dynamicItemSize(qMax(size().width(), requiredWidth),
+ m_itemSize.height());
+ m_layouter->setItemSize(dynamicItemSize);
- QHashIterator<QByteArray, QSizeF> it(m_stretchedVisibleRolesSizes);
+ // Update the role sizes for all visible widgets
+ QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
- rolesWidths.insert(it.key(), it.value().width());
+ updateWidgetColumnWidths(it.value());
}
-
- return rolesWidths;
}
-void KItemListView::updateVisibleRolesSizes(const KItemRangeList& itemRanges)
+void KItemListView::updateWidgetColumnWidths(KItemListWidget* widget)
{
- if (!m_itemSize.isEmpty() || m_useHeaderWidths) {
- return;
+ foreach (const QByteArray& role, m_visibleRoles) {
+ widget->setColumnWidth(role, m_headerWidget->columnWidth(role));
}
+}
+void KItemListView::updatePreferredColumnWidths(const KItemRangeList& itemRanges)
+{
+ Q_ASSERT(m_itemSize.isEmpty());
const int itemCount = m_model->count();
int rangesItemCount = 0;
foreach (const KItemRange& range, itemRanges) {
}
if (itemCount == rangesItemCount) {
- m_visibleRolesSizes = visibleRolesSizes(itemRanges);
- if (m_header) {
- // Assure the the sizes are not smaller than the minimum defined by the header
- // TODO: Currently only implemented for a top-aligned header
- const qreal minHeaderRoleWidth = m_header->minimumRoleWidth();
- QMutableHashIterator<QByteArray, QSizeF> it (m_visibleRolesSizes);
- while (it.hasNext()) {
- it.next();
- const QSizeF& size = it.value();
- if (size.width() < minHeaderRoleWidth) {
- const QSizeF newSize(minHeaderRoleWidth, size.height());
- m_visibleRolesSizes.insert(it.key(), newSize);
- }
- }
+ const QHash<QByteArray, qreal> preferredWidths = preferredColumnWidths(itemRanges);
+ foreach (const QByteArray& role, m_visibleRoles) {
+ m_headerWidget->setPreferredColumnWidth(role, preferredWidths.value(role));
}
} else {
// Only a sub range of the roles need to be determined.
- // The chances are good that the sizes of the sub ranges
- // already fit into the available sizes and hence no
+ // The chances are good that the widths of the sub ranges
+ // already fit into the available widths and hence no
// expensive update might be required.
- bool updateRequired = false;
+ bool changed = false;
- const QHash<QByteArray, QSizeF> updatedSizes = visibleRolesSizes(itemRanges);
- QHashIterator<QByteArray, QSizeF> it(updatedSizes);
+ const QHash<QByteArray, qreal> updatedWidths = preferredColumnWidths(itemRanges);
+ QHashIterator<QByteArray, qreal> it(updatedWidths);
while (it.hasNext()) {
it.next();
const QByteArray& role = it.key();
- const QSizeF& updatedSize = it.value();
- const QSizeF currentSize = m_visibleRolesSizes.value(role);
- if (updatedSize.width() > currentSize.width() || updatedSize.height() > currentSize.height()) {
- m_visibleRolesSizes.insert(role, updatedSize);
- updateRequired = true;
+ const qreal updatedWidth = it.value();
+ const qreal currentWidth = m_headerWidget->preferredColumnWidth(role);
+ if (updatedWidth > currentWidth) {
+ m_headerWidget->setPreferredColumnWidth(role, updatedWidth);
+ changed = true;
}
}
- if (!updateRequired) {
+ if (!changed) {
// All the updated sizes are smaller than the current sizes and no change
// of the stretched roles-widths is required
return;
}
}
- updateStretchedVisibleRolesSizes();
+ if (m_headerWidget->automaticColumnResizing()) {
+ applyAutomaticColumnWidths();
+ }
}
-void KItemListView::updateVisibleRolesSizes()
+void KItemListView::updatePreferredColumnWidths()
{
- if (!m_model) {
- return;
- }
-
- const int itemCount = m_model->count();
- if (itemCount > 0) {
- updateVisibleRolesSizes(KItemRangeList() << KItemRange(0, itemCount));
+ if (m_model) {
+ updatePreferredColumnWidths(KItemRangeList() << KItemRange(0, m_model->count()));
}
}
-void KItemListView::updateStretchedVisibleRolesSizes()
+void KItemListView::applyAutomaticColumnWidths()
{
- if (!m_itemSize.isEmpty() || m_useHeaderWidths || m_visibleRoles.isEmpty()) {
+ Q_ASSERT(m_itemSize.isEmpty());
+ Q_ASSERT(m_headerWidget->automaticColumnResizing());
+ if (m_visibleRoles.isEmpty()) {
return;
}
// Calculate the maximum size of an item by considering the
// visible role sizes and apply them to the layouter. If the
- // size does not use the available view-size it the size of the
+ // size does not use the available view-size the size of the
// first role will get stretched.
- m_stretchedVisibleRolesSizes = m_visibleRolesSizes;
- const QByteArray role = m_visibleRoles.first();
- QSizeF firstRoleSize = m_stretchedVisibleRolesSizes.value(role);
+ foreach (const QByteArray& role, m_visibleRoles) {
+ const qreal preferredWidth = m_headerWidget->preferredColumnWidth(role);
+ m_headerWidget->setColumnWidth(role, preferredWidth);
+ }
+
+ const QByteArray firstRole = m_visibleRoles.first();
+ qreal firstColumnWidth = m_headerWidget->columnWidth(firstRole);
QSizeF dynamicItemSize = m_itemSize;
- if (dynamicItemSize.width() <= 0) {
- const qreal requiredWidth = visibleRolesSizesWidthSum();
- const qreal availableWidth = size().width();
- if (requiredWidth < availableWidth) {
- // Stretch the first role to use the whole width for the item
- firstRoleSize.rwidth() += availableWidth - requiredWidth;
- m_stretchedVisibleRolesSizes.insert(role, firstRoleSize);
+ qreal requiredWidth = columnWidthsSum();
+ const qreal availableWidth = size().width();
+ if (requiredWidth < availableWidth) {
+ // Stretch the first column to use the whole remaining width
+ firstColumnWidth += availableWidth - requiredWidth;
+ m_headerWidget->setColumnWidth(firstRole, firstColumnWidth);
+ } else if (requiredWidth > availableWidth && m_visibleRoles.count() > 1) {
+ // Shrink the first column to be able to show as much other
+ // columns as possible
+ qreal shrinkedFirstColumnWidth = firstColumnWidth - requiredWidth + availableWidth;
+
+ // TODO: A proper calculation of the minimum width depends on the implementation
+ // of KItemListWidget. Probably a kind of minimum size-hint should be introduced
+ // later.
+ const qreal minWidth = qMin(firstColumnWidth, qreal(m_styleOption.iconSize * 2 + 200));
+ if (shrinkedFirstColumnWidth < minWidth) {
+ shrinkedFirstColumnWidth = minWidth;
}
- dynamicItemSize.setWidth(qMax(requiredWidth, availableWidth));
- }
- if (dynamicItemSize.height() <= 0) {
- const qreal requiredHeight = visibleRolesSizesHeightSum();
- const qreal availableHeight = size().height();
- if (requiredHeight < availableHeight) {
- // Stretch the first role to use the whole height for the item
- firstRoleSize.rheight() += availableHeight - requiredHeight;
- m_stretchedVisibleRolesSizes.insert(role, firstRoleSize);
- }
- dynamicItemSize.setHeight(qMax(requiredHeight, availableHeight));
+ m_headerWidget->setColumnWidth(firstRole, shrinkedFirstColumnWidth);
+ requiredWidth -= firstColumnWidth - shrinkedFirstColumnWidth;
}
- m_layouter->setItemSize(dynamicItemSize);
+ dynamicItemSize.rwidth() = qMax(requiredWidth, availableWidth);
- if (m_header) {
- m_header->setVisibleRolesWidths(headerRolesWidths());
- m_header->resize(dynamicItemSize.width(), m_header->size().height());
- }
+ m_layouter->setItemSize(dynamicItemSize);
// Update the role sizes for all visible widgets
QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
- it.value()->setVisibleRolesSizes(m_stretchedVisibleRolesSizes);
- }
-}
-
-qreal KItemListView::visibleRolesSizesWidthSum() const
-{
- qreal widthSum = 0;
- QHashIterator<QByteArray, QSizeF> it(m_visibleRolesSizes);
- while (it.hasNext()) {
- it.next();
- widthSum += it.value().width();
+ updateWidgetColumnWidths(it.value());
}
- return widthSum;
}
-qreal KItemListView::visibleRolesSizesHeightSum() const
+qreal KItemListView::columnWidthsSum() const
{
- qreal heightSum = 0;
- QHashIterator<QByteArray, QSizeF> it(m_visibleRolesSizes);
- while (it.hasNext()) {
- it.next();
- heightSum += it.value().height();
+ qreal widthsSum = 0;
+ foreach (const QByteArray& role, m_visibleRoles) {
+ widthsSum += m_headerWidget->columnWidth(role);
}
- return heightSum;
+ return widthsSum;
}
QRectF KItemListView::headerBoundaries() const
{
- return m_header ? m_header->geometry() : QRectF();
+ return m_headerWidget->isVisible() ? m_headerWidget->geometry() : QRectF();
}
bool KItemListView::changesItemGridLayout(const QSizeF& newGridSize,
bool KItemListView::animateChangedItemCount(int changedItemCount) const
{
+ if (m_itemSize.isEmpty()) {
+ // We have only columns or only rows, but no grid: An animation is usually
+ // welcome when inserting or removing items.
+ return !supportsItemExpanding();
+ }
+
if (m_layouter->size().isEmpty() || m_layouter->itemSize().isEmpty()) {
return false;
}
: maxOffset > size.width();
}
+int KItemListView::showDropIndicator(const QPointF& pos)
+{
+ QHashIterator<int, KItemListWidget*> it(m_visibleItems);
+ while (it.hasNext()) {
+ it.next();
+ const KItemListWidget* widget = it.value();
+
+ const QPointF mappedPos = widget->mapFromItem(this, pos);
+ const QRectF rect = itemRect(widget->index());
+ if (mappedPos.y() >= 0 && mappedPos.y() <= rect.height()) {
+ if (m_model->supportsDropping(widget->index())) {
+ const int gap = qMax(4, m_styleOption.padding);
+ if (mappedPos.y() >= gap && mappedPos.y() <= rect.height() - gap) {
+ return -1;
+ }
+ }
+
+ const bool isAboveItem = (mappedPos.y () < rect.height() / 2);
+ const qreal y = isAboveItem ? rect.top() : rect.bottom();
+
+ const QRectF draggingInsertIndicator(rect.left(), y, rect.width(), 1);
+ if (m_dropIndicator != draggingInsertIndicator) {
+ m_dropIndicator = draggingInsertIndicator;
+ update();
+ }
+
+ int index = widget->index();
+ if (!isAboveItem) {
+ ++index;
+ }
+ return index;
+ }
+ }
+
+ const QRectF firstItemRect = itemRect(firstVisibleIndex());
+ return (pos.y() <= firstItemRect.top()) ? 0 : -1;
+}
+
+void KItemListView::hideDropIndicator()
+{
+ if (!m_dropIndicator.isNull()) {
+ m_dropIndicator = QRectF();
+ update();
+ }
+}
+
void KItemListView::updateGroupHeaderHeight()
{
qreal groupHeaderHeight = m_styleOption.fontMetrics.height();
return hasSuccessor;
}
+void KItemListView::disconnectRoleEditingSignals(int index)
+{
+ KItemListWidget* widget = m_visibleItems.value(index);
+ if (!widget) {
+ return;
+ }
+
+ widget->disconnect(SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), this);
+ widget->disconnect(SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), this);
+}
+
int KItemListView::calculateAutoScrollingIncrement(int pos, int range, int oldInc)
{
int inc = 0;