#include "kitemlistview.h"
+#include <KDebug>
#include "kitemlistcontroller.h"
#include "kitemlistheader.h"
#include "kitemlistselectionmanager.h"
#include "private/kitemlistviewlayouter.h"
#include "private/kitemlistviewanimation.h"
-#include <KDebug>
-
#include <QCursor>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
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();
-
- }
-
- doLayout(NoAnimation);
-
- onScrollOrientationChanged(orientation, previousOrientation);
- emit scrollOrientationChanged(orientation, previousOrientation);
-}
-
-Qt::Orientation KItemListView::scrollOrientation() const
-{
- return m_layouter->scrollOrientation();
-}
-
-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);
- }
+ // 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(size, 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)
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();
- }
-
- 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);
-
- onStyleOptionChanged(option, previousOption);
+ return m_itemSize;
}
const KItemListStyleOption& KItemListView::styleOption() const
QSizeF KItemListView::itemSizeHint(int index) const
{
- return m_widgetCreator->itemSizeHint(index, this);
+ return widgetCreator()->itemSizeHint(index, this);
}
void KItemListView::setSupportsItemExpanding(bool supportsExpanding)
void KItemListView::editRole(int index, const QByteArray& role)
{
KItemListWidget* widget = m_visibleItems.value(index);
- if (!widget) {
+ if (!widget || m_editingRole) {
return;
}
- Q_ASSERT(!m_editingRole);
m_editingRole = true;
widget->setEditedRole(role);
}
}
+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)
{
Q_UNUSED(item);
// by m_visibleWidgets and must be deleted manually after the animation has
// been finished.
recycleGroupHeaderForWidget(itemListWidget);
- m_widgetCreator->recycle(itemListWidget);
+ widgetCreator()->recycle(itemListWidget);
break;
}
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);
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)
}
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()));
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()));
}
// 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) {
for (int i = startIndex; i <= endIndex; ++i) {
foreach (const QByteArray& visibleRole, visibleRoles()) {
qreal maxWidth = widths.value(visibleRole, 0);
- const qreal width = m_widgetCreator->preferredRoleColumnWidth(visibleRole, i, this);
+ const qreal width = creator->preferredRoleColumnWidth(visibleRole, i, this);
maxWidth = qMax(width, maxWidth);
widths.insert(visibleRole, maxWidth);
}
{
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
// Stretch the first column to use the whole remaining width
firstColumnWidth += availableWidth - requiredWidth;
m_headerWidget->setColumnWidth(firstRole, firstColumnWidth);
- } else if (requiredWidth > availableWidth) {
+ } 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;