Improvements for selections, smooth scrolling, tooltips and info-panel

This commit is contained in:
Peter Penz 2011-08-08 23:41:18 +02:00
parent 4692eb7c96
commit 3957884bf2
28 changed files with 1164 additions and 508 deletions

View file

@ -154,6 +154,7 @@ kde4_add_kcfg_files(dolphin_SRCS
panels/information/dolphin_informationpanelsettings.kcfgc
settings/dolphin_compactmodesettings.kcfgc
settings/dolphin_detailsmodesettings.kcfgc
settings/dolphin_generalsettings.kcfgc
settings/dolphin_iconsmodesettings.kcfgc
search/dolphin_searchsettings.kcfgc
settings/dolphin_versioncontrolsettings.kcfgc

View file

@ -33,6 +33,7 @@
#include <QFontMetricsF>
#include <QGraphicsSceneResizeEvent>
#include <QPainter>
#include <QStyleOption>
#include <QTextLayout>
#include <QTextLine>
@ -91,6 +92,7 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte
const_cast<KFileItemListWidget*>(this)->updateCache();
}
// Draw expansion toggle '>' or 'V'
if (m_isDir && !m_expansionArea.isEmpty()) {
QStyleOption arrowOption;
arrowOption.rect = m_expansionArea.toRect();
@ -99,10 +101,8 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte
style()->drawPrimitive(arrow, &arrowOption, painter);
}
const bool isHovered = (hoverOpacity() > 0.0);
const KItemListStyleOption& itemListStyleOption = styleOption();
if (isHovered) {
if (isHovered()) {
// Blend the unhovered and hovered pixmap if the hovering
// animation is ongoing
if (hoverOpacity() < 1.0) {
@ -127,11 +127,11 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte
drawPixmap(painter, m_pixmap);
}
QFont font(itemListStyleOption.font);
if (itemListStyleOption.state & QStyle::State_HasFocus) {
font.setUnderline(true);
if (isCurrent()) {
drawFocusIndicator(painter);
}
painter->setFont(font);
painter->setFont(itemListStyleOption.font);
painter->setPen(itemListStyleOption.palette.text().color());
painter->drawStaticText(m_textPos[Name], m_text[Name]);
@ -244,6 +244,12 @@ void KFileItemListWidget::styleOptionChanged(const KItemListStyleOption& current
m_dirtyLayout = true;
}
void KFileItemListWidget::hoveredChanged(bool hovered)
{
Q_UNUSED(hovered);
m_dirtyLayout = true;
}
void KFileItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event)
{
KItemListWidget::resizeEvent(event);
@ -374,7 +380,7 @@ void KFileItemListWidget::updatePixmapCache()
m_hoverPixmapRect.moveTopLeft(QPointF(x, y));
// Prepare the pixmap that is used when the item gets hovered
if (option.state & QStyle::State_MouseOver) {
if (isHovered()) {
m_hoverPixmap = m_pixmap;
KIconEffect* effect = KIconLoader::global()->iconEffect();
// In the KIconLoader terminology, active = hover.
@ -681,6 +687,31 @@ void KFileItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap)
}
}
void KFileItemListWidget::drawFocusIndicator(QPainter* painter)
{
// Ideally style()->drawPrimitive(QStyle::PE_FrameFocusRect...)
// should be used, but Oxygen only draws indicators within classes
// derived from QAbstractItemView or Q3ListView. As a workaround
// the indicator is drawn manually. Code copied from oxygenstyle.cpp
// Copyright ( C ) 2009-2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
// TODO: Clarify with Oxygen maintainers how to proceed with this.
const KItemListStyleOption& option = styleOption();
const QPalette palette = option.palette;
const QRect rect = m_textsBoundingRect.toRect().adjusted(0, 0, 0, -1);
QLinearGradient gradient(rect.bottomLeft(), rect.bottomRight());
gradient.setColorAt(0.0, Qt::transparent);
gradient.setColorAt(1.0, Qt::transparent);
gradient.setColorAt(0.2, palette.color(QPalette::Text));
gradient.setColorAt(0.8, palette.color(QPalette::Text));
painter->setRenderHint(QPainter::Antialiasing, false);
painter->setPen(QPen(gradient, 1));
painter->drawLine(rect.bottomLeft(), rect.bottomRight());
painter->setRenderHint(QPainter::Antialiasing, true);
}
QPixmap KFileItemListWidget::pixmapForIcon(const QString& name, int size)
{
const KIcon icon(name);

View file

@ -57,6 +57,7 @@ protected:
virtual void visibleRolesChanged(const QHash<QByteArray, int>& current, const QHash<QByteArray, int>& previous);
virtual void visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current, const QHash<QByteArray, QSizeF>& previous);
virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous);
virtual void hoveredChanged(bool hovered);
virtual void resizeEvent(QGraphicsSceneResizeEvent* event);
private:
@ -85,6 +86,7 @@ private:
QString roleText(TextId textId, const QVariant& roleValue) const;
void drawPixmap(QPainter* painter, const QPixmap& pixmap);
void drawFocusIndicator(QPainter* painter);
static QPixmap pixmapForIcon(const QString& name, int size);
static TextId roleTextId(const QByteArray& role);

View file

@ -403,8 +403,9 @@ void KFileItemModel::insertItems(const KFileItemList& items)
KItemRangeList itemRanges;
int targetIndex = 0;
int sourceIndex = 0;
int insertedAtIndex = -1;
int insertedCount = 0;
int insertedAtIndex = -1; // Index for the current item-range
int insertedCount = 0; // Count for the current item-range
int previouslyInsertedCount = 0; // Sum of previously inserted items for all ranges
while (sourceIndex < sortedItems.count()) {
// Find target index from m_items to insert the current item
// in a sorted order
@ -418,7 +419,8 @@ void KFileItemModel::insertItems(const KFileItemList& items)
if (targetIndex - previousTargetIndex > 0 && insertedAtIndex >= 0) {
itemRanges << KItemRange(insertedAtIndex, insertedCount);
insertedAtIndex = targetIndex;
previouslyInsertedCount += insertedCount;
insertedAtIndex = targetIndex - previouslyInsertedCount;
insertedCount = 0;
}
@ -431,6 +433,7 @@ void KFileItemModel::insertItems(const KFileItemList& items)
if (insertedAtIndex < 0) {
insertedAtIndex = targetIndex;
Q_ASSERT(previouslyInsertedCount == 0);
}
++targetIndex;
++sourceIndex;

View file

@ -28,6 +28,7 @@
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QPropertyAnimation>
#include <QScrollBar>
#include <QStyle>
@ -56,7 +57,9 @@ public:
KItemListContainer::KItemListContainer(KItemListController* controller, QWidget* parent) :
QAbstractScrollArea(parent),
m_controller(controller)
m_controller(controller),
m_sliderMovedByUser(false),
m_viewOffsetAnimation(0)
{
Q_ASSERT(controller);
controller->setParent(this);
@ -99,8 +102,86 @@ void KItemListContainer::scrollContentsBy(int dx, int dy)
}
const qreal currentOffset = view->offset();
const qreal offsetDiff = (view->scrollOrientation() == Qt::Vertical) ? dy : dx;
view->setOffset(currentOffset - offsetDiff);
qreal offsetDiff = (view->scrollOrientation() == Qt::Vertical) ? dy : dx;
const bool animRunning = (m_viewOffsetAnimation->state() == QAbstractAnimation::Running);
if (animRunning) {
// Stopping a running animation means skipping the range from the current offset
// until the target offset. To prevent skipping of the range the difference
// is added to the new target offset.
m_viewOffsetAnimation->stop();
const qreal targetOffset = m_viewOffsetAnimation->endValue().toReal();
offsetDiff += (currentOffset - targetOffset);
}
const qreal newOffset = currentOffset - offsetDiff;
if (m_sliderMovedByUser || animRunning) {
m_viewOffsetAnimation->stop();
m_viewOffsetAnimation->setStartValue(currentOffset);
m_viewOffsetAnimation->setEndValue(newOffset);
m_viewOffsetAnimation->setEasingCurve(animRunning ? QEasingCurve::OutQuad : QEasingCurve::InOutQuad);
m_viewOffsetAnimation->start();
} else {
view->setOffset(newOffset);
}
}
bool KItemListContainer::eventFilter(QObject* obj, QEvent* event)
{
Q_ASSERT(obj == horizontalScrollBar() || obj == verticalScrollBar());
// Check whether the scrollbar has been adjusted by a mouse-event
// triggered by the user and remember this in m_sliderMovedByUser.
// The smooth scrolling will only get active if m_sliderMovedByUser
// is true (see scrollContentsBy()).
const bool scrollVertical = (m_controller->view()->scrollOrientation() == Qt::Vertical);
const bool checkEvent = ( scrollVertical && obj == verticalScrollBar()) ||
(!scrollVertical && obj == horizontalScrollBar());
if (checkEvent) {
switch (event->type()) {
case QEvent::MouseButtonPress:
m_sliderMovedByUser = true;
break;
case QEvent::MouseButtonRelease:
m_sliderMovedByUser = false;
break;
case QEvent::Wheel:
wheelEvent(static_cast<QWheelEvent*>(event));
break;
default:
break;
}
}
return QAbstractScrollArea::eventFilter(obj, event);
}
void KItemListContainer::wheelEvent(QWheelEvent* event)
{
KItemListView* view = m_controller->view();
if (!view || event->orientation() != view->scrollOrientation()) {
return;
}
const int numDegrees = event->delta() / 8;
const int numSteps = numDegrees / 15;
const bool previous = m_sliderMovedByUser;
m_sliderMovedByUser = true;
if (view->scrollOrientation() == Qt::Vertical) {
const int value = verticalScrollBar()->value();
verticalScrollBar()->setValue(value - numSteps * view->size().height());
} else {
const int value = horizontalScrollBar()->value();
horizontalScrollBar()->setValue(value - numSteps * view->size().width());
}
m_sliderMovedByUser = previous;
event->accept();
}
void KItemListContainer::slotModelChanged(KItemModelBase* current, KItemModelBase* previous)
@ -116,37 +197,57 @@ void KItemListContainer::slotViewChanged(KItemListView* current, KItemListView*
scene->removeItem(previous);
disconnect(previous, SIGNAL(offsetChanged(int,int)), this, SLOT(updateScrollBars()));
disconnect(previous, SIGNAL(maximumOffsetChanged(int,int)), this, SLOT(updateScrollBars()));
m_viewOffsetAnimation->setTargetObject(0);
}
if (current) {
scene->addItem(current);
connect(previous, SIGNAL(offsetChanged(int,int)), this, SLOT(updateScrollBars()));
connect(current, SIGNAL(offsetChanged(int,int)), this, SLOT(updateScrollBars()));
connect(current, SIGNAL(maximumOffsetChanged(int,int)), this, SLOT(updateScrollBars()));
m_viewOffsetAnimation->setTargetObject(current);
}
}
void KItemListContainer::updateScrollBars()
{
const QSizeF size = m_controller->view()->size();
if (m_controller->view()->scrollOrientation() == Qt::Vertical) {
QScrollBar* scrollBar = verticalScrollBar();
const int value = m_controller->view()->offset();
const int maximum = qMax(0, int(m_controller->view()->maximumOffset() - size.height()));
scrollBar->setPageStep(size.height());
scrollBar->setMinimum(0);
scrollBar->setMaximum(maximum);
scrollBar->setValue(value);
horizontalScrollBar()->setMaximum(0);
} else {
QScrollBar* scrollBar = horizontalScrollBar();
const int value = m_controller->view()->offset();
const int maximum = qMax(0, int(m_controller->view()->maximumOffset() - size.width()));
scrollBar->setPageStep(size.width());
scrollBar->setMinimum(0);
scrollBar->setMaximum(maximum);
scrollBar->setValue(value);
verticalScrollBar()->setMaximum(0);
const KItemListView* view = m_controller->view();
if (!view) {
return;
}
QScrollBar* scrollBar = 0;
int singleStep = 0;
int pageStep = 0;
if (view->scrollOrientation() == Qt::Vertical) {
scrollBar = verticalScrollBar();
singleStep = view->itemSize().height();
pageStep = view->size().height();
} else {
scrollBar = horizontalScrollBar();
singleStep = view->itemSize().width();
pageStep = view->size().width();
}
const int value = view->offset();
const int maximum = qMax(0, int(view->maximumOffset() - pageStep));
if (m_viewOffsetAnimation->state() == QAbstractAnimation::Running) {
if (maximum == scrollBar->maximum()) {
// The value has been changed by the animation, no update
// of the scrollbars is required as their target state will be
// reached with the end of the animation.
return;
}
// The maximum has been changed which indicates that the content
// of the view has been changed. Stop the animation in any case and
// update the scrollbars immediately.
m_viewOffsetAnimation->stop();
}
scrollBar->setSingleStep(singleStep);
scrollBar->setPageStep(pageStep);
scrollBar->setMinimum(0);
scrollBar->setMaximum(maximum);
scrollBar->setValue(value);
}
void KItemListContainer::updateGeometries()
@ -186,6 +287,12 @@ void KItemListContainer::initialize()
QGraphicsView* graphicsView = new KItemListContainerViewport(new QGraphicsScene(this), this);
setViewport(graphicsView);
m_viewOffsetAnimation = new QPropertyAnimation(this, "offset");
m_viewOffsetAnimation->setDuration(500);
horizontalScrollBar()->installEventFilter(this);
verticalScrollBar()->installEventFilter(this);
}
#include "kitemlistcontainer.moc"

View file

@ -30,6 +30,7 @@
class KItemListController;
class KItemListView;
class KItemModelBase;
class QPropertyAnimation;
/**
* @brief Provides a QWidget based scrolling view for a KItemListController.
@ -51,6 +52,8 @@ protected:
virtual void showEvent(QShowEvent* event);
virtual void resizeEvent(QResizeEvent* event);
virtual void scrollContentsBy(int dx, int dy);
virtual bool eventFilter(QObject* obj, QEvent* event);
virtual void wheelEvent(QWheelEvent* event);
private slots:
void slotModelChanged(KItemModelBase* current, KItemModelBase* previous);
@ -63,6 +66,9 @@ private:
private:
KItemListController* m_controller;
bool m_sliderMovedByUser;
QPropertyAnimation* m_viewOffsetAnimation;
};
#endif

View file

@ -82,14 +82,9 @@ void KItemListController::setView(KItemListView* view)
KItemListView* oldView = m_view;
m_view = view;
if (oldView) {
disconnect(m_selectionManager, SIGNAL(currentChanged(int,int)), oldView, SLOT(currentChanged(int,int)));
}
if (m_view) {
m_view->setController(this);
m_view->setModel(m_model);
connect(m_selectionManager, SIGNAL(currentChanged(int,int)), m_view, SLOT(currentChanged(int,int)));
}
emit viewChanged(m_view, oldView);
@ -139,14 +134,20 @@ bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const
const QPointF pos = transform.map(event->pos());
m_pressedIndex = m_view->itemAt(pos);
m_selectionManager->setCurrentItem(m_pressedIndex);
// The anchor for the current selection is updated except for Shift+LeftButton events
// (the current selection is continued with the previous anchor in that case).
if (!(event->buttons() & Qt::LeftButton && event->modifiers() & Qt::ShiftModifier)) {
const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier ||
event->modifiers() & Qt::ControlModifier;
if (!shiftOrControlPressed) {
m_selectionManager->clearSelection();
m_selectionManager->setAnchorItem(m_pressedIndex);
}
if (m_pressedIndex >= 0) {
m_selectionManager->setCurrentItem(m_pressedIndex);
if (!m_view->isAboveExpansionToggle(m_pressedIndex, pos)) {
m_selectionManager->setSelected(m_pressedIndex);
}
}
return false;
}
@ -162,15 +163,18 @@ bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, con
if (m_view) {
const QPointF pos = transform.map(event->pos());
const int index = m_view->itemAt(pos);
const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier || event->modifiers() & Qt::ControlModifier;
if (index >= 0 && index == m_pressedIndex) {
// The release event is done above the same item as the press event
bool emitItemClicked = true;
if (event->button() & Qt::LeftButton) {
if (m_view->isAboveExpansionToggle(index, pos)) {
emit itemExpansionToggleClicked(index);
emitItemClicked = false;
}
else if (event->modifiers() & Qt::ShiftModifier || event->modifiers() & Qt::ControlModifier) {
// The mouse click should only update the selection, not trigger the item.
else if (shiftOrControlPressed) {
// The mouse click should only update the selection, not trigger the item
emitItemClicked = false;
}
}
@ -178,6 +182,8 @@ bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, con
if (emitItemClicked) {
emit itemClicked(index, event->button());
}
} else if (!shiftOrControlPressed) {
m_selectionManager->clearSelection();
}
}
@ -229,8 +235,49 @@ bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const
bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
// The implementation assumes that only one item can get hovered no matter
// whether they overlap or not.
Q_UNUSED(transform);
if (!m_model || !m_view) {
return false;
}
// Search the previously hovered item that might get unhovered
KItemListWidget* unhoveredWidget = 0;
foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
if (widget->isHovered()) {
unhoveredWidget = widget;
break;
}
}
// Search the currently hovered item
KItemListWidget* hoveredWidget = 0;
foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
const QPointF mappedPos = widget->mapFromItem(m_view, event->pos());
const bool hovered = widget->contains(mappedPos) &&
!widget->expansionToggleRect().contains(mappedPos) &&
!widget->selectionToggleRect().contains(mappedPos);
if (hovered) {
hoveredWidget = widget;
break;
}
}
if (unhoveredWidget != hoveredWidget) {
if (unhoveredWidget) {
unhoveredWidget->setHovered(false);
emit itemUnhovered(unhoveredWidget->index());
}
if (hoveredWidget) {
hoveredWidget->setHovered(true);
emit itemHovered(hoveredWidget->index());
}
}
return false;
}
@ -238,6 +285,17 @@ bool KItemListController::hoverLeaveEvent(QGraphicsSceneHoverEvent* event, const
{
Q_UNUSED(event);
Q_UNUSED(transform);
if (!m_model || !m_view) {
return false;
}
foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
if (widget->isHovered()) {
widget->setHovered(false);
emit itemUnhovered(widget->index());
}
}
return false;
}
@ -262,9 +320,6 @@ bool KItemListController::processEvent(QEvent* event, const QTransform& transfor
}
switch (event->type()) {
// case QEvent::FocusIn:
// case QEvent::FocusOut:
// return focusEvent(static_cast<QFocusEvent*>(event));
case QEvent::KeyPress:
return keyPressEvent(static_cast<QKeyEvent*>(event));
case QEvent::InputMethod:

View file

@ -101,6 +101,19 @@ public:
signals:
void itemClicked(int index, Qt::MouseButton button);
/**
* Is emitted if the item with the index \p index gets hovered.
*/
void itemHovered(int index);
/**
* Is emitted if the item with the index \p index gets unhovered.
* It is assured that the signal itemHovered() for this index
* has been emitted before.
*/
void itemUnhovered(int index);
void itemExpansionToggleClicked(int index);
void modelChanged(KItemModelBase* current, KItemModelBase* previous);

View file

@ -23,11 +23,13 @@
#include "kitemlistselectionmanager.h"
#include "kitemmodelbase.h"
#include <KDebug>
KItemListSelectionManager::KItemListSelectionManager(QObject* parent) :
QObject(parent),
m_currentItem(-1),
m_anchorItem(-1),
m_selectedItems(),
m_model(0)
{
}
@ -39,7 +41,7 @@ KItemListSelectionManager::~KItemListSelectionManager()
void KItemListSelectionManager::setCurrentItem(int current)
{
const int previous = m_currentItem;
if (m_model && current < m_model->count()) {
if (m_model && current >= 0 && current < m_model->count()) {
m_currentItem = current;
} else {
m_currentItem = -1;
@ -55,6 +57,88 @@ int KItemListSelectionManager::currentItem() const
return m_currentItem;
}
void KItemListSelectionManager::setSelectedItems(const QSet<int>& items)
{
if (m_selectedItems != items) {
const QSet<int> previous = m_selectedItems;
m_selectedItems = items;
emit selectionChanged(m_selectedItems, previous);
}
}
QSet<int> KItemListSelectionManager::selectedItems() const
{
return m_selectedItems;
}
bool KItemListSelectionManager::hasSelection() const
{
return !m_selectedItems.isEmpty();
}
void KItemListSelectionManager::setSelected(int index, int count, SelectionMode mode)
{
if (index < 0 || count < 1 || !m_model || index >= m_model->count()) {
return;
}
const QSet<int> previous = m_selectedItems;
count = qMin(count, m_model->count() - index);
const int endIndex = index + count -1;
switch (mode) {
case Select:
for (int i = index; i <= endIndex; ++i) {
m_selectedItems.insert(i);
}
break;
case Deselect:
for (int i = index; i <= endIndex; ++i) {
m_selectedItems.remove(i);
}
break;
case Toggle:
for (int i = index; i <= endIndex; ++i) {
if (m_selectedItems.contains(i)) {
m_selectedItems.remove(i);
} else {
m_selectedItems.insert(i);
}
}
break;
default:
Q_ASSERT(false);
break;
}
if (m_selectedItems != previous) {
emit selectionChanged(m_selectedItems, previous);
}
}
void KItemListSelectionManager::clearSelection()
{
if (!m_selectedItems.isEmpty()) {
const QSet<int> previous = m_selectedItems;
m_selectedItems.clear();
emit selectionChanged(m_selectedItems, previous);
}
}
void KItemListSelectionManager::beginAnchoredSelection(int anchor, SelectionMode mode)
{
Q_UNUSED(anchor);
Q_UNUSED(mode);
}
void KItemListSelectionManager::endAnchoredSelection()
{
}
void KItemListSelectionManager::setAnchorItem(int anchor)
{
const int previous = m_anchorItem;
@ -82,6 +166,106 @@ KItemModelBase* KItemListSelectionManager::model() const
void KItemListSelectionManager::setModel(KItemModelBase* model)
{
m_model = model;
if (model && model->count() > 0) {
m_currentItem = 0;
}
}
void KItemListSelectionManager::itemsInserted(const KItemRangeList& itemRanges)
{
// Update the current item
if (m_currentItem < 0) {
setCurrentItem(0);
} else {
int inc = 0;
foreach (const KItemRange& itemRange, itemRanges) {
if (m_currentItem < itemRange.index) {
break;
}
inc += itemRange.count;
}
setCurrentItem(m_currentItem + inc);
}
// Update the selections
if (!m_selectedItems.isEmpty()) {
const QSet<int> previous = m_selectedItems;
QSet<int> current;
current.reserve(m_selectedItems.count());
QSetIterator<int> it(m_selectedItems);
while (it.hasNext()) {
const int index = it.next();
int inc = 0;
foreach (const KItemRange& itemRange, itemRanges) {
if (index < itemRange.index) {
break;
}
inc += itemRange.count;
}
current.insert(index + inc);
}
if (current != previous) {
m_selectedItems = current;
emit selectionChanged(current, previous);
}
}
}
void KItemListSelectionManager::itemsRemoved(const KItemRangeList& itemRanges)
{
// Update the current item
if (m_currentItem >= 0) {
int currentItem = m_currentItem;
foreach (const KItemRange& itemRange, itemRanges) {
if (currentItem < itemRange.index) {
break;
}
if (currentItem >= itemRange.index + itemRange.count) {
currentItem -= itemRange.count;
} else if (currentItem >= m_model->count()) {
currentItem = m_model->count() - 1;
}
}
setCurrentItem(currentItem);
}
// Update the selections
if (!m_selectedItems.isEmpty()) {
const QSet<int> previous = m_selectedItems;
QSet<int> current;
current.reserve(m_selectedItems.count());
QSetIterator<int> it(m_selectedItems);
while (it.hasNext()) {
int index = it.next();
int dec = 0;
foreach (const KItemRange& itemRange, itemRanges) {
if (index < itemRange.index) {
break;
}
if (index < itemRange.index + itemRange.count) {
// The selection is part of the removed range
// and will get deleted
index = -1;
break;
}
dec += itemRange.count;
}
index -= dec;
if (index >= 0) {
current.insert(index);
}
}
if (current != previous) {
m_selectedItems = current;
emit selectionChanged(current, previous);
}
}
}
#include "kitemlistselectionmanager.moc"

View file

@ -25,10 +25,16 @@
#include <libdolphin_export.h>
#include <kitemviews/kitemmodelbase.h>
#include <QObject>
#include <QSet>
class KItemModelBase;
/**
* @brief Allows to select and deselect items of a KItemListView.
*/
class LIBDOLPHINPRIVATE_EXPORT KItemListSelectionManager : public QObject
{
Q_OBJECT
@ -39,13 +45,22 @@ public:
Deselect,
Toggle
};
KItemListSelectionManager(QObject* parent = 0);
virtual ~KItemListSelectionManager();
void setCurrentItem(int current);
int currentItem() const;
void setSelectedItems(const QSet<int>& items);
QSet<int> selectedItems() const;
bool hasSelection() const;
void setSelected(int index, int count = 1, SelectionMode mode = Select);
void clearSelection();
void beginAnchoredSelection(int anchor, SelectionMode mode = Select);
void endAnchoredSelection();
void setAnchorItem(int anchor);
int anchorItem() const;
@ -53,17 +68,24 @@ public:
signals:
void currentChanged(int current, int previous);
void selectionChanged(const QSet<int>& current, const QSet<int>& previous);
void anchorChanged(int anchor, int previous);
protected:
private:
void setModel(KItemModelBase* model);
void itemsInserted(const KItemRangeList& itemRanges);
void itemsRemoved(const KItemRangeList& itemRanges);
private:
int m_currentItem;
int m_anchorItem;
QSet<int> m_selectedItems;
KItemModelBase* m_model;
friend class KItemListController;
friend class KItemListController; // Calls setModel()
friend class KItemListView; // Calls itemsInserted() and itemsRemoved()
friend class KItemListSelectionManagerTest;
};
#endif

View file

@ -20,16 +20,23 @@
#include "kitemliststyleoption.h"
KItemListStyleOption::KItemListStyleOption() :
QStyleOption(QStyleOption::Version, QStyleOption::SO_CustomBase + 1)
rect(),
font(),
fontMetrics(QFont()),
palette(),
margin(0),
iconSize(0)
{
}
KItemListStyleOption::KItemListStyleOption(const KItemListStyleOption& other) :
QStyleOption(other)
rect(other.rect),
font(other.font),
fontMetrics(other.fontMetrics),
palette(other.palette),
margin(other.margin),
iconSize(other.iconSize)
{
margin = other.margin;
iconSize = other.iconSize;
font = other.font;
}
KItemListStyleOption::~KItemListStyleOption()

View file

@ -23,18 +23,23 @@
#include <libdolphin_export.h>
#include <QFont>
#include <QStyleOption>
#include <QFontMetrics>
#include <QPalette>
#include <QRect>
class LIBDOLPHINPRIVATE_EXPORT KItemListStyleOption : public QStyleOption
class LIBDOLPHINPRIVATE_EXPORT KItemListStyleOption
{
public:
KItemListStyleOption();
KItemListStyleOption(const KItemListStyleOption& other);
virtual ~KItemListStyleOption();
QRect rect;
QFont font;
QFontMetrics fontMetrics;
QPalette palette;
int margin;
int iconSize;
QFont font;
};
#endif

View file

@ -256,10 +256,6 @@ void KItemListView::setGeometry(const QRectF& rect)
int KItemListView::itemAt(const QPointF& pos) const
{
if (!m_model) {
return -1;
}
QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
@ -315,6 +311,11 @@ QHash<QByteArray, QSizeF> KItemListView::visibleRoleSizes() const
return QHash<QByteArray, QSizeF>();
}
QRectF KItemListView::itemBoundingRect(int index) const
{
return m_layouter->itemBoundingRect(index);
}
void KItemListView::beginTransaction()
{
++m_activeTransactions;
@ -412,56 +413,6 @@ void KItemListView::mousePressEvent(QGraphicsSceneMouseEvent* event)
event->accept();
}
void KItemListView::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
if (!m_model) {
return;
}
QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
KItemListWidget* widget = it.value();
KItemListStyleOption styleOption = widget->styleOption();
const QPointF mappedPos = widget->mapFromItem(this, event->pos());
const bool hovered = widget->contains(mappedPos) &&
!widget->expansionToggleRect().contains(mappedPos) &&
!widget->selectionToggleRect().contains(mappedPos);
if (hovered) {
if (!(styleOption.state & QStyle::State_MouseOver)) {
styleOption.state |= QStyle::State_MouseOver;
widget->setStyleOption(styleOption);
}
} else if (styleOption.state & QStyle::State_MouseOver) {
styleOption.state &= ~QStyle::State_MouseOver;
widget->setStyleOption(styleOption);
}
}
}
void KItemListView::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
{
Q_UNUSED(event);
if (!m_model) {
return;
}
QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
KItemListWidget* widget = it.value();
KItemListStyleOption styleOption = widget->styleOption();
if (styleOption.state & QStyle::State_MouseOver) {
styleOption.state &= ~QStyle::State_MouseOver;
widget->setStyleOption(styleOption);
}
}
}
QList<KItemListWidget*> KItemListView::visibleItemListWidgets() const
{
return m_visibleItems.values();
@ -476,13 +427,18 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges)
beginTransaction();
}
int previouslyInsertedCount = 0;
foreach (const KItemRange& range, itemRanges) {
const int index = range.index;
// range.index is related to the model before anything has been inserted.
// As in each loop the current item-range gets inserted the index must
// be increased by the already previoulsy inserted items.
const int index = range.index + previouslyInsertedCount;
const int count = range.count;
if (index < 0 || count <= 0) {
kWarning() << "Invalid item range (index:" << index << ", count:" << count << ")";
continue;
}
previouslyInsertedCount += count;
m_sizeHintResolver->itemsInserted(index, count);
@ -524,16 +480,10 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges)
doLayout(Animation, index, count);
update();
}
}
if (m_controller) {
KItemListSelectionManager* selectionManager = m_controller->selectionManager();
const int current = selectionManager->currentItem();
if (current < 0) {
selectionManager->setCurrentItem(0);
} else if (current >= index) {
selectionManager->setCurrentItem(current + count);
}
}
if (m_controller) {
m_controller->selectionManager()->itemsInserted(itemRanges);
}
if (hasMultipleRanges) {
@ -611,14 +561,10 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges)
doLayout(Animation, index, -count);
update();
}
}
/*KItemListSelectionManager* selectionManager = m_controller->selectionManager();
const int current = selectionManager->currentItem();
if (count() <= 0) {
selectionManager->setCurrentItem(-1);
} else if (current >= index) {
selectionManager->setCurrentItem(current + count);
}*/
if (m_controller) {
m_controller->selectionManager()->itemsRemoved(itemRanges);
}
if (hasMultipleRanges) {
@ -645,24 +591,33 @@ void KItemListView::slotItemsChanged(const KItemRangeList& itemRanges,
}
}
void KItemListView::currentChanged(int current, int previous)
void KItemListView::slotCurrentChanged(int current, int previous)
{
Q_UNUSED(previous);
KItemListWidget* previousWidget = m_visibleItems.value(previous, 0);
if (previousWidget) {
Q_ASSERT(previousWidget->isCurrent());
previousWidget->setCurrent(false);
}
KItemListWidget* currentWidget = m_visibleItems.value(current, 0);
if (currentWidget) {
Q_ASSERT(!currentWidget->isCurrent());
currentWidget->setCurrent(true);
}
}
void KItemListView::slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous)
{
Q_UNUSED(previous);
QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
const int index = it.key();
KItemListWidget* widget = it.value();
KItemListStyleOption styleOption = widget->styleOption();
if (it.key() == current) {
styleOption.state |= QStyle::State_HasFocus;
widget->setStyleOption(styleOption);
}
else if (styleOption.state & QStyle::State_HasFocus) {
styleOption.state &= ~QStyle::State_HasFocus;
widget->setStyleOption(styleOption);
}
widget->setSelected(current.contains(index));
}
}
@ -717,7 +672,20 @@ void KItemListView::setController(KItemListController* controller)
{
if (m_controller != controller) {
KItemListController* previous = m_controller;
if (previous) {
KItemListSelectionManager* selectionManager = previous->selectionManager();
disconnect(selectionManager, SIGNAL(currentChanged(int,int)), this, SLOT(slotCurrentChanged(int,int)));
disconnect(selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)), this, SLOT(slotSelectionChanged(QSet<int>,QSet<int>)));
}
m_controller = controller;
if (controller) {
KItemListSelectionManager* selectionManager = controller->selectionManager();
connect(selectionManager, SIGNAL(currentChanged(int,int)), this, SLOT(slotCurrentChanged(int,int)));
connect(selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)), this, SLOT(slotSelectionChanged(QSet<int>,QSet<int>)));
}
onControllerChanged(controller, previous);
}
}
@ -918,17 +886,7 @@ void KItemListView::emitOffsetChanges()
KItemListWidget* KItemListView::createWidget(int index)
{
KItemListWidget* widget = m_widgetCreator->create(this);
widget->setVisibleRoles(m_visibleRoles);
widget->setVisibleRolesSizes(m_visibleRolesSizes);
KItemListStyleOption option = m_styleOption;
if (index == m_controller->selectionManager()->currentItem()) {
option.state |= QStyle::State_HasFocus;
}
widget->setStyleOption(option);
widget->setIndex(index);
widget->setData(m_model->data(index));
updateWidgetProperties(widget, index);
m_visibleItems.insert(index, widget);
if (m_grouped) {
@ -982,17 +940,7 @@ void KItemListView::setWidgetIndex(KItemListWidget* widget, int index)
const int oldIndex = widget->index();
m_visibleItems.remove(oldIndex);
widget->setVisibleRoles(m_visibleRoles);
widget->setVisibleRolesSizes(m_visibleRolesSizes);
KItemListStyleOption option = m_styleOption;
if (index == m_controller->selectionManager()->currentItem()) {
option.state |= QStyle::State_HasFocus;
}
widget->setStyleOption(option);
widget->setIndex(index);
widget->setData(m_model->data(index));
updateWidgetProperties(widget, index);
m_visibleItems.insert(index, widget);
initializeItemListWidget(widget);
@ -1099,6 +1047,28 @@ void KItemListView::applyDynamicItemSize()
}
}
void KItemListView::updateWidgetProperties(KItemListWidget* widget, int index)
{
widget->setVisibleRoles(m_visibleRoles);
widget->setVisibleRolesSizes(m_visibleRolesSizes);
widget->setStyleOption(m_styleOption);
const KItemListSelectionManager* selectionManager = m_controller->selectionManager();
widget->setCurrent(index == selectionManager->currentItem());
if (selectionManager->hasSelection()) {
const QSet<int> selectedItems = selectionManager->selectedItems();
widget->setSelected(selectedItems.contains(index));
} else {
widget->setSelected(false);
}
widget->setHovered(false);
widget->setIndex(index);
widget->setData(m_model->data(index));
}
KItemListCreatorBase::~KItemListCreatorBase()
{
qDeleteAll(m_recycleableWidgets);

View file

@ -30,6 +30,7 @@
#include <kitemviews/kitemlistwidget.h>
#include <kitemviews/kitemmodelbase.h>
#include <QGraphicsWidget>
#include <QSet>
class KItemListController;
class KItemListWidgetCreatorBase;
@ -62,6 +63,8 @@ class LIBDOLPHINPRIVATE_EXPORT KItemListView : public QGraphicsWidget
{
Q_OBJECT
Q_PROPERTY(qreal offset READ offset WRITE setOffset)
public:
KItemListView(QGraphicsWidget* parent = 0);
virtual ~KItemListView();
@ -132,6 +135,8 @@ public:
virtual QSizeF itemSizeHint(int index) const;
virtual QHash<QByteArray, QSizeF> visibleRoleSizes() const;
QRectF itemBoundingRect(int index) const;
void beginTransaction();
void endTransaction();
bool isTransactionActive() const;
@ -157,8 +162,6 @@ protected:
virtual bool event(QEvent* event);
virtual void mousePressEvent(QGraphicsSceneMouseEvent* event);
virtual void hoverMoveEvent(QGraphicsSceneHoverEvent* event);
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
QList<KItemListWidget*> visibleItemListWidgets() const;
@ -169,7 +172,8 @@ protected slots:
const QSet<QByteArray>& roles);
private slots:
void currentChanged(int current, int previous);
void slotCurrentChanged(int current, int previous);
void slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous);
void slotAnimationFinished(QGraphicsWidget* widget,
KItemListViewAnimation::AnimationType type);
void slotLayoutTimerFinished();
@ -229,6 +233,12 @@ private:
*/
void applyDynamicItemSize();
/**
* Helper method for createWidget() and setWidgetIndex() to update the properties
* of the itemlist widget.
*/
void updateWidgetProperties(KItemListWidget* widget, int index);
private:
bool m_grouped;
int m_activeTransactions; // Counter for beginTransaction()/endTransaction()

View file

@ -30,10 +30,14 @@
#include <QPainter>
#include <QPropertyAnimation>
#include <QStyle>
#include <QStyleOption>
KItemListWidget::KItemListWidget(QGraphicsItem* parent) :
QGraphicsWidget(parent, 0),
m_index(-1),
m_selected(false),
m_current(false),
m_hovered(false),
m_data(),
m_visibleRoles(),
m_visibleRolesSizes(),
@ -90,11 +94,21 @@ QHash<QByteArray, QVariant> KItemListWidget::data() const
void KItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
Q_UNUSED(option);
const QRect hoverBounds = hoverBoundingRect().toRect();
if (m_selected) {
QStyleOptionViewItemV4 viewItemOption;
viewItemOption.initFrom(widget);
viewItemOption.rect = hoverBounds;
viewItemOption.state = QStyle::State_Enabled | QStyle::State_Selected | QStyle::State_Item;
viewItemOption.viewItemPosition = QStyleOptionViewItemV4::OnlyOne;
widget->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &viewItemOption, painter, widget);
}
if (m_hoverOpacity <= 0.0) {
return;
}
const QRect hoverBounds = hoverBoundingRect().toRect();
if (!m_hoverCache) {
m_hoverCache = new QPixmap(hoverBounds.size());
m_hoverCache->fill(Qt::transparent);
@ -104,7 +118,7 @@ void KItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* o
QStyleOptionViewItemV4 viewItemOption;
viewItemOption.initFrom(widget);
viewItemOption.rect = QRect(0, 0, hoverBounds.width(), hoverBounds.height());
viewItemOption.state = QStyle::State_Enabled | QStyle::State_MouseOver;
viewItemOption.state = QStyle::State_Enabled | QStyle::State_MouseOver | QStyle::State_Item;
viewItemOption.viewItemPosition = QStyleOptionViewItemV4::OnlyOne;
widget->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &viewItemOption, &pixmapPainter, widget);
@ -143,34 +157,8 @@ QHash<QByteArray, QSizeF> KItemListWidget::visibleRolesSizes() const
void KItemListWidget::setStyleOption(const KItemListStyleOption& option)
{
const KItemListStyleOption previous = m_styleOption;
if (m_index >= 0) {
clearCache();
const bool wasHovered = (previous.state & QStyle::State_MouseOver);
m_styleOption = option;
const bool isHovered = (m_styleOption.state & QStyle::State_MouseOver);
if (wasHovered != isHovered) {
// The hovering state has been changed. Assure that a fade-animation
// is done to the new state.
if (!m_hoverAnimation) {
m_hoverAnimation = new QPropertyAnimation(this, "hoverOpacity", this);
m_hoverAnimation->setDuration(200);
}
m_hoverAnimation->stop();
if (!wasHovered && isHovered) {
m_hoverAnimation->setEndValue(1.0);
} else {
Q_ASSERT(wasHovered && !isHovered);
m_hoverAnimation->setEndValue(0.0);
}
m_hoverAnimation->start();
}
} else {
m_styleOption = option;
}
clearCache();
m_styleOption = option;
styleOptionChanged(option, previous);
}
@ -180,6 +168,66 @@ const KItemListStyleOption& KItemListWidget::styleOption() const
return m_styleOption;
}
void KItemListWidget::setSelected(bool selected)
{
if (m_selected != selected) {
m_selected = selected;
selectedChanged(selected);
update();
}
}
bool KItemListWidget::isSelected() const
{
return m_selected;
}
void KItemListWidget::setCurrent(bool current)
{
if (m_current != current) {
m_current = current;
currentChanged(current);
update();
}
}
bool KItemListWidget::isCurrent() const
{
return m_current;
}
void KItemListWidget::setHovered(bool hovered)
{
if (hovered == m_hovered) {
return;
}
m_hovered = hovered;
if (!m_hoverAnimation) {
m_hoverAnimation = new QPropertyAnimation(this, "hoverOpacity", this);
m_hoverAnimation->setDuration(200);
}
m_hoverAnimation->stop();
if (hovered) {
m_hoverAnimation->setEndValue(1.0);
} else {
m_hoverAnimation->setEndValue(0.0);
}
m_hoverAnimation->start();
hoveredChanged(hovered);
update();
}
bool KItemListWidget::isHovered() const
{
return m_hovered;
}
bool KItemListWidget::contains(const QPointF& point) const
{
return hoverBoundingRect().contains(point) ||
@ -234,6 +282,21 @@ void KItemListWidget::styleOptionChanged(const KItemListStyleOption& current,
update();
}
void KItemListWidget::currentChanged(bool current)
{
Q_UNUSED(current);
}
void KItemListWidget::selectedChanged(bool selected)
{
Q_UNUSED(selected);
}
void KItemListWidget::hoveredChanged(bool hovered)
{
Q_UNUSED(hovered);
}
void KItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event)
{
QGraphicsWidget::resizeEvent(event);

View file

@ -72,6 +72,15 @@ public:
void setStyleOption(const KItemListStyleOption& option);
const KItemListStyleOption& styleOption() const;
void setSelected(bool selected);
bool isSelected() const;
void setCurrent(bool current);
bool isCurrent() const;
void setHovered(bool hovered);
bool isHovered() const;
/**
* @return True if \a point is inside KItemListWidget::hoverBoundingRect(),
* KItemListWidget::selectionToggleRect() or KItemListWidget::expansionToggleRect().
@ -104,6 +113,9 @@ protected:
virtual void visibleRolesChanged(const QHash<QByteArray, int>& current, const QHash<QByteArray, int>& previous);
virtual void visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current, const QHash<QByteArray, QSizeF>& previous);
virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous);
virtual void currentChanged(bool current);
virtual void selectedChanged(bool selected);
virtual void hoveredChanged(bool hovered);
virtual void resizeEvent(QGraphicsSceneResizeEvent* event);
/**
@ -120,6 +132,9 @@ private:
Q_PROPERTY(qreal hoverOpacity READ hoverOpacity WRITE setHoverOpacity)
int m_index;
bool m_selected;
bool m_current;
bool m_hovered;
QHash<QByteArray, QVariant> m_data;
QHash<QByteArray, int> m_visibleRoles;
QHash<QByteArray, QSizeF> m_visibleRolesSizes;

View file

@ -106,9 +106,46 @@ public:
virtual QString roleDescription(const QByteArray& role) const;
signals:
/**
* Is emitted if one or more items have been inserted. Each item-range consists
* of:
* - an index where items have been inserted
* - the number of inserted items.
* The index of each item-range represents the index of the model
* before the items have been inserted.
*
* For the item-ranges it is assured that:
* - They don't overlap
* - The index of item-range n is smaller than the index of item-range n + 1.
*/
void itemsInserted(const KItemRangeList& itemRanges);
/**
* Is emitted if one or more items have been removed. Each item-range consists
* of:
* - an index where items have been inserted
* - the number of inserted items.
* The index of each item-range represents the index of the model
* before the items have been removed.
*
* For the item-ranges it is assured that:
* - They don't overlap
* - The index of item-range n is smaller than the index of item-range n + 1.
*/
void itemsRemoved(const KItemRangeList& itemRanges);
void itemsMoved(const KItemRangeList& itemRanges);
/**
* Is emitted if one ore more items get moved.
* @param itemRanges Item-ranges that get moved to a new position.
* @param movedToIndexes New position of the ranges.
* It is assured that the itemRanges list has the same size as the movedToIndexes list.
*
* For the item-ranges it is assured that:
* - They don't overlap
* - The index of item-range n is smaller than the index of item-range n + 1.
*/
void itemsMoved(const KItemRangeList& itemRanges, const QList<int> movedToIndexes);
void itemsChanged(const KItemRangeList& itemRanges, const QSet<QByteArray>& roles);
void groupRoleChanged(const QByteArray& current, const QByteArray& previous);

View file

@ -1,4 +1,4 @@
File=dolphin_generalsettings.kcfg
ClassName=GeneralSettings
Singleton=false
ClassName=GeneralSettings
Singleton=yes
Mutators=true

View file

@ -48,15 +48,12 @@ void DolphinSettings::save()
DolphinSettings::DolphinSettings()
{
m_generalSettings = new GeneralSettings();
m_generalSettings = GeneralSettings::self();
m_placesModel = new KFilePlacesModel();
}
DolphinSettings::~DolphinSettings()
{
delete m_generalSettings;
m_generalSettings = 0;
delete m_placesModel;
m_placesModel = 0;
}

View file

@ -122,7 +122,7 @@ void BehaviorSettingsPage::applySettings()
const bool useGlobalProps = m_globalProps->isChecked();
GeneralSettings* settings = DolphinSettings::instance().generalSettings();
GeneralSettings* settings = GeneralSettings::self();
settings->setGlobalViewProps(useGlobalProps);
if (useGlobalProps) {

View file

@ -1,6 +1,15 @@
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BUILD_DIR}/.. ${KDE4_INCLUDES} )
# KItemListSelectionManagerTest
set(kitemlistselectionmanagertest_SRCS
kitemlistselectionmanagertest.cpp
../kitemviews/kitemlistselectionmanager.cpp
../kitemviews/kitemmodelbase.cpp
)
kde4_add_unit_test(kitemlistselectionmanagertest TEST ${kitemlistselectionmanagertest_SRCS})
target_link_libraries(kitemlistselectionmanagertest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
# KFileItemListViewTest
set(kfileitemlistviewtest_SRCS
kfileitemlistviewtest.cpp

View file

@ -38,8 +38,6 @@ private slots:
void init();
void cleanup();
void testFeffi();
private:
KFileItemListView* m_listView;
KFileItemModel* m_model;
@ -83,18 +81,6 @@ void KFileItemListViewTest::cleanup()
m_testDir = 0;
}
void KFileItemListViewTest::testFeffi()
{
QStringList files;
files << "a.txt" << "b.txt" << "c.txt";
m_testDir->createFiles(files);
m_dirLister->openUrl(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 3);
}
QTEST_KDEMAIN(KFileItemListViewTest, GUI)
#include "kfileitemlistviewtest.moc"

View file

@ -24,9 +24,11 @@
#include "testdir.h"
namespace {
const int DefaultTimeout = 2000;
const int DefaultTimeout = 5000;
};
Q_DECLARE_METATYPE(KItemRangeList)
class KFileItemModelTest : public QObject
{
Q_OBJECT
@ -39,7 +41,8 @@ private slots:
void testDefaultSortRole();
void testDefaultGroupRole();
void testNewItems();
void testInsertingItems();
void testModelConsistencyWhenInsertingItems();
void testItemRangeConsistencyWhenInsertingItems();
void testExpansionLevelsCompare_data();
void testExpansionLevelsCompare();
@ -120,9 +123,9 @@ void KFileItemModelTest::testNewItems()
QVERIFY(isModelConsistent());
}
void KFileItemModelTest::testInsertingItems()
void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
{
// QSKIP("Temporary disabled", SkipSingle);
QSKIP("Temporary disabled", SkipSingle);
// KFileItemModel prevents that inserting a punch of items sequentially
// results in an itemsInserted()-signal for each item. Instead internally
@ -164,6 +167,58 @@ void KFileItemModelTest::testInsertingItems()
QCOMPARE(m_model->count(), 201);
}
void KFileItemModelTest::testItemRangeConsistencyWhenInsertingItems()
{
QStringList files;
files << "B" << "E" << "G";
m_testDir->createFiles(files);
// Due to inserting the 3 items one item-range with index == 0 and
// count == 3 must be given
QSignalSpy spy1(m_model, SIGNAL(itemsInserted(KItemRangeList)));
m_dirLister->openUrl(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(spy1.count(), 1);
QList<QVariant> arguments = spy1.takeFirst();
KItemRangeList itemRangeList = arguments.at(0).value<KItemRangeList>();
QCOMPARE(itemRangeList.count(), 1);
KItemRange itemRange = itemRangeList.first();
QCOMPARE(itemRange.index, 0);
QCOMPARE(itemRange.count, 3);
// The indexes of the item-ranges must always be related to the model before
// the items have been inserted. Having:
// 0 1 2
// B E G
// and inserting A, C, D, F the resulting model will be:
// 0 1 2 3 4 5 6
// A B C D E F G
// and the item-ranges must be:
// index: 0, count: 1 for A
// index: 1, count: 2 for B, C
// index: 2, count: 1 for G
files.clear();
files << "A" << "C" << "D" << "F";
m_testDir->createFiles(files);
QSignalSpy spy2(m_model, SIGNAL(itemsInserted(KItemRangeList)));
m_dirLister->updateDirectory(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(spy2.count(), 1);
arguments = spy2.takeFirst();
itemRangeList = arguments.at(0).value<KItemRangeList>();
QCOMPARE(itemRangeList.count(), 3);
QCOMPARE(itemRangeList.at(0).index, 0);
QCOMPARE(itemRangeList.at(0).count, 1);
QCOMPARE(itemRangeList.at(1).index, 1);
QCOMPARE(itemRangeList.at(1).count, 2);
QCOMPARE(itemRangeList.at(2).index, 2);
QCOMPARE(itemRangeList.at(2).count, 1);
}
void KFileItemModelTest::testExpansionLevelsCompare_data()
{
QTest::addColumn<QString>("urlA");

View file

@ -0,0 +1,172 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include <qtest_kde.h>
#include "kitemviews/kitemmodelbase.h"
#include "kitemviews/kitemlistselectionmanager.h"
class DummyModel : public KItemModelBase
{
public:
DummyModel();
virtual int count() const;
virtual QHash<QByteArray, QVariant> data(int index) const;
};
DummyModel::DummyModel() :
KItemModelBase()
{
}
int DummyModel::count() const
{
return 100;
}
QHash<QByteArray, QVariant> DummyModel::data(int index) const
{
Q_UNUSED(index);
return QHash<QByteArray, QVariant>();
}
class KItemListSelectionManagerTest : public QObject
{
Q_OBJECT
private slots:
void init();
void cleanup();
void testConstructor();
void testSetSelected_data();
void testSetSelected();
void testItemsInserted();
void testItemsRemoved();
private:
KItemListSelectionManager* m_selectionManager;
};
void KItemListSelectionManagerTest::init()
{
m_selectionManager = new KItemListSelectionManager();
m_selectionManager->setModel(new DummyModel());
}
void KItemListSelectionManagerTest::cleanup()
{
delete m_selectionManager->model();
delete m_selectionManager;
m_selectionManager = 0;
}
void KItemListSelectionManagerTest::testConstructor()
{
QVERIFY(!m_selectionManager->hasSelection());
QCOMPARE(m_selectionManager->selectedItems().count(), 0);
QCOMPARE(m_selectionManager->currentItem(), 0);
QCOMPARE(m_selectionManager->anchorItem(), -1);
}
void KItemListSelectionManagerTest::testSetSelected_data()
{
QTest::addColumn<int>("index");
QTest::addColumn<int>("count");
QTest::addColumn<int>("expectedSelectionCount");
QTest::newRow("Select all") << 0 << 100 << 100;
QTest::newRow("Sub selection 15 items") << 20 << 15 << 15;
QTest::newRow("Sub selection 1 item") << 20 << 1 << 1;
QTest::newRow("Too small index") << -1 << 100 << 0;
QTest::newRow("Too large index") << 100 << 100 << 0;
QTest::newRow("Too large count") << 0 << 100000 << 100;
QTest::newRow("Too small count") << 0 << 0 << 0;
}
void KItemListSelectionManagerTest::testSetSelected()
{
QFETCH(int, index);
QFETCH(int, count);
QFETCH(int, expectedSelectionCount);
m_selectionManager->setSelected(index, count);
QCOMPARE(m_selectionManager->selectedItems().count(), expectedSelectionCount);
}
void KItemListSelectionManagerTest::testItemsInserted()
{
// Select items 10 to 12
m_selectionManager->setSelected(10, 3);
QSet<int> selectedItems = m_selectionManager->selectedItems();
QVERIFY(selectedItems.contains(10));
QVERIFY(selectedItems.contains(11));
QVERIFY(selectedItems.contains(12));
// Insert items 0 to 4 -> selection must be 15 to 17
m_selectionManager->itemsInserted(KItemRangeList() << KItemRange(0, 5));
selectedItems = m_selectionManager->selectedItems();
QVERIFY(selectedItems.contains(15));
QVERIFY(selectedItems.contains(16));
QVERIFY(selectedItems.contains(17));
// Insert 3 items between the selections
m_selectionManager->itemsInserted(KItemRangeList() <<
KItemRange(15, 1) <<
KItemRange(16, 1) <<
KItemRange(17, 1));
selectedItems = m_selectionManager->selectedItems();
QVERIFY(selectedItems.contains(16));
QVERIFY(selectedItems.contains(18));
QVERIFY(selectedItems.contains(20));
}
void KItemListSelectionManagerTest::testItemsRemoved()
{
// Select items 10 to 15
m_selectionManager->setSelected(10, 6);
QSet<int> selectedItems = m_selectionManager->selectedItems();
for (int i = 10; i <= 15; ++i) {
QVERIFY(selectedItems.contains(i));
}
// Remove items 0 to 4 -> selection must be 5 to 10
m_selectionManager->itemsRemoved(KItemRangeList() << KItemRange(0, 5));
selectedItems = m_selectionManager->selectedItems();
for (int i = 5; i <= 10; ++i) {
QVERIFY(selectedItems.contains(i));
}
// Remove the items 6 , 8 and 10
m_selectionManager->itemsRemoved(KItemRangeList() <<
KItemRange(6, 1) <<
KItemRange(8, 1) <<
KItemRange(10, 1));
selectedItems = m_selectionManager->selectedItems();
QCOMPARE(selectedItems.count(), 3);
QVERIFY(selectedItems.contains(5));
QVERIFY(selectedItems.contains(6));
QVERIFY(selectedItems.contains(7));
}
QTEST_KDEMAIN(KItemListSelectionManagerTest, NoGUI)
#include "kitemlistselectionmanagertest.moc"

View file

@ -38,6 +38,7 @@
#include <KLocale>
#include <kitemviews/kfileitemmodel.h>
#include <kitemviews/kfileitemlistview.h>
#include <kitemviews/kitemlistselectionmanager.h>
#include <kitemviews/kitemlistview.h>
#include <kitemviews/kitemlistcontroller.h>
#include <KIO/DeleteJob>
@ -57,15 +58,14 @@
#include "dolphinnewfilemenuobserver.h"
#include "dolphin_detailsmodesettings.h"
#include "dolphin_generalsettings.h"
#include "dolphinitemlistcontainer.h"
#include "renamedialog.h"
#include "settings/dolphinsettings.h"
#include "viewmodecontroller.h"
#include "viewproperties.h"
#include "views/tooltips/tooltipmanager.h"
#include "zoomlevelinfo.h"
// TODO:
#include "dolphinitemlistcontainer.h"
namespace {
const int MaxModeEnum = DolphinView::CompactView;
const int MaxSortingEnum = DolphinView::SortByPath;
@ -76,7 +76,6 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
m_active(true),
m_tabsForFiles(false),
m_assureVisibleCurrentIndex(false),
m_expanderActive(false),
m_isFolderWritable(true),
m_url(url),
m_mode(DolphinView::IconsView),
@ -84,12 +83,12 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
m_topLayout(0),
m_dirLister(0),
m_container(0),
m_toolTipManager(0),
m_selectionChangedTimer(0),
m_activeItemUrl(),
m_currentItemIndex(-1),
m_restoredContentsPosition(),
m_createdItemUrl(),
m_selectedItems(),
m_newFileNames()
m_selectedItems()
{
m_topLayout = new QVBoxLayout(this);
m_topLayout->setSpacing(0);
@ -167,6 +166,14 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
connect(controller, SIGNAL(itemClicked(int,Qt::MouseButton)),
this, SLOT(slotItemClicked(int,Qt::MouseButton)));
connect(controller, SIGNAL(itemExpansionToggleClicked(int)), this, SLOT(slotItemExpansionToggleClicked(int)));
connect(controller, SIGNAL(itemHovered(int)), this, SLOT(slotItemHovered(int)));
connect(controller, SIGNAL(itemUnhovered(int)), this, SLOT(slotItemUnhovered(int)));
KItemListSelectionManager* selectionManager = controller->selectionManager();
connect(selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)),
this, SLOT(slotSelectionChanged(QSet<int>,QSet<int>)));
m_toolTipManager = new ToolTipManager(this);
applyViewProperties();
m_topLayout->addWidget(m_container);
@ -211,7 +218,6 @@ void DolphinView::setActive(bool active)
// view->setFocus();
//}
emit activated();
emitSelectionChangedSignal();
emit writeStateChanged(m_isFolderWritable);
}
@ -261,35 +267,23 @@ KFileItemList DolphinView::items() const
KFileItemList DolphinView::selectedItems() const
{
return KFileItemList();
/* KFileItemList itemList;
const QAbstractItemView* view = m_viewAccessor.itemView();
if (!view) {
return itemList;
const KFileItemModel* model = fileItemModel();
const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
const QSet<int> selectedIndexes = selectionManager->selectedItems();
KFileItemList selectedItems;
QSetIterator<int> it(selectedIndexes);
while (it.hasNext()) {
const int index = it.next();
selectedItems.append(model->fileItem(index));
}
const QItemSelection selection = m_viewAccessor.proxyModel()->mapSelectionToSource(view->selectionModel()->selection());
const QModelIndexList indexList = selection.indexes();
foreach (const QModelIndex &index, indexList) {
KFileItem item = m_viewAccessor.dirModel()->itemForIndex(index);
if (!item.isNull()) {
itemList.append(item);
}
}
return itemList;*/
return selectedItems;
}
int DolphinView::selectedItemsCount() const
{
return 0;
/*const QAbstractItemView* view = m_viewAccessor.itemView();
if (!view) {
return 0;
}
return view->selectionModel()->selectedIndexes().count();*/
const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
return selectionManager->selectedItems().count();
}
void DolphinView::markUrlsAsSelected(const QList<KUrl>& urls)
@ -509,11 +503,12 @@ void DolphinView::setUrl(const KUrl& url)
}
emit urlAboutToBeChanged(url);
const bool hadSelection = hasSelection();
m_newFileNames.clear();
m_url = url;
if (GeneralSettings::showToolTips()) {
m_toolTipManager->hideToolTip();
}
// It is important to clear the items from the model before
// applying the view properties, otherwise expensive operations
// might be done on the existing items although they get cleared
@ -523,36 +518,28 @@ void DolphinView::setUrl(const KUrl& url)
loadDirectory(url);
emit urlChanged(url);
if (hadSelection || hasSelection()) {
emitSelectionChangedSignal();
}
}
void DolphinView::selectAll()
{
//m_viewAccessor.itemView()->selectAll();
KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
selectionManager->setSelected(0, fileItemModel()->count());
}
void DolphinView::invertSelection()
{
/* // Implementation note: Using selectionModel->select(selection, QItemSelectionModel::Toggle) does not
// work, as QItemSelectionModel::hasSelection() provides invalid values in this case. This might be a Qt-issue -
// when changing the implementation with an updated Qt-version don't forget to run the Dolphin-unit-tests that
// verify this usecase.
const KFileItemList selItems = selectedItems();
clearSelection();
KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
const QSet<int> selectedItems = selectionManager->selectedItems();
QSet<int> invertedSelectedItems;
QItemSelection invertedSelection;
foreach (const KFileItem& item, items()) {
if (!selItems.contains(item)) {
const QModelIndex index = m_viewAccessor.proxyModel()->mapFromSource(m_viewAccessor.dirModel()->indexForItem(item));
invertedSelection.select(index, index);
}
}
const int maxIndex = fileItemModel()->count() - 1;
for (int i = 0; i <= maxIndex; ++i) {
if (!selectedItems.contains(i)) {
invertedSelectedItems.insert(i);
}
}
QItemSelectionModel* selectionModel = m_viewAccessor.itemView()->selectionModel();
selectionModel->select(invertedSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
*/
selectionManager->setSelectedItems(invertedSelectedItems);
}
void DolphinView::clearSelection()
@ -711,23 +698,54 @@ void DolphinView::slotItemClicked(int index, Qt::MouseButton button)
emit tabRequested(item.url());
}
} else if (button & Qt::RightButton) {
// TODO: attach customActions for the details-view
if (GeneralSettings::showToolTips()) {
m_toolTipManager->hideToolTip();
}
emit requestContextMenu(item, url(), QList<QAction*>());
}
}
void DolphinView::slotItemExpansionToggleClicked(int index)
{
// TODO: When doing a model->setExpanded(false) it should
// be checked here whether the current index is part of the
// closed sub-tree. If this is the case, the current index
// should be adjusted to the parent index.
KFileItemModel* model = fileItemModel();
const bool expanded = model->isExpanded(index);
model->setExpanded(index, !expanded);
}
void DolphinView::slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
void DolphinView::slotItemHovered(int index)
{
const int count = selectedItemsCount();
const bool selectionStateChanged = ((count > 0) && (selected.count() == count)) ||
((count == 0) && !deselected.isEmpty());
const KFileItem item = fileItemModel()->fileItem(index);
if (GeneralSettings::showToolTips()) {
QRectF itemRect = m_container->controller()->view()->itemBoundingRect(index);
const QPoint pos = m_container->mapToGlobal(itemRect.topLeft().toPoint());
itemRect.moveTo(pos);
m_toolTipManager->showToolTip(item, itemRect);
}
emit requestItemInfo(item);
}
void DolphinView::slotItemUnhovered(int index)
{
Q_UNUSED(index);
if (GeneralSettings::showToolTips()) {
m_toolTipManager->hideToolTip();
}
emit requestItemInfo(KFileItem());
}
void DolphinView::slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous)
{
const int currentCount = current.count();
const int previousCount = previous.count();
const bool selectionStateChanged = (currentCount == 0 && previousCount > 0) ||
(currentCount > 0 && previousCount == 0);
// If nothing has been selected before and something got selected (or if something
// was selected before and now nothing is selected) the selectionChangedSignal must
@ -745,19 +763,12 @@ void DolphinView::emitSelectionChangedSignal()
void DolphinView::openContextMenu(const QPoint& pos,
const QList<QAction*>& customActions)
{
Q_UNUSED(pos);
KFileItem item;
/*QAbstractItemView* view = m_viewAccessor.itemView();
QModelIndex index;
if (view) {
index = view->indexAt(pos);
const int index = m_container->controller()->view()->itemAt(pos);
if (index >= 0) {
item = fileItemModel()->fileItem(index);
}
if (index.isValid() && (index.column() == DolphinModel::Name)) {
const QModelIndex dolphinModelIndex = m_viewAccessor.proxyModel()->mapToSource(index);
item = m_viewAccessor.dirModel()->itemForIndex(dolphinModelIndex);
}*/
emit requestContextMenu(item, url(), customActions);
}
@ -767,7 +778,7 @@ void DolphinView::dropUrls(const KFileItem& destItem,
{
Q_UNUSED(destItem);
Q_UNUSED(destPath);
addNewFileNames(event->mimeData());
markPastedUrlsAsSelected(event->mimeData());
//DragAndDropHelper::instance().dropUrls(destItem, destPath, event, this);
}
@ -824,8 +835,8 @@ bool DolphinView::itemsExpandable() const
void DolphinView::restoreState(QDataStream& stream)
{
// Restore the URL of the current item that had the keyboard focus
stream >> m_activeItemUrl;
// Restore the current item that had the keyboard focus
stream >> m_currentItemIndex;
// Restore the view position
stream >> m_restoredContentsPosition;
@ -845,20 +856,13 @@ void DolphinView::restoreState(QDataStream& stream)
void DolphinView::saveState(QDataStream& stream)
{
// Save the URL of the current item that has the keyboard focus
KUrl currentItemUrl;
//if (!currentItem.isNull()) {
// currentItemUrl = currentItem.url();
//}
stream << currentItemUrl;
// Save the current item that has the keyboard focus
stream << m_container->controller()->selectionManager()->currentItem();
// Save view position
const qreal x = m_container->horizontalScrollBar()->value();
const qreal y = m_container->verticalScrollBar()->value();
stream << QPoint(x, y);
kDebug() << "saving view state" << QPoint(x, y);
// Save expanded folders (only relevant for the details view - the set will be empty in other view modes)
//stream << m_viewAccessor.expandedUrls();
@ -866,9 +870,7 @@ void DolphinView::saveState(QDataStream& stream)
bool DolphinView::hasSelection() const
{
//const QAbstractItemView* view = m_viewAccessor.itemView();
//return view && view->selectionModel()->hasSelection();
return false;
return m_container->controller()->selectionManager()->hasSelection();
}
KFileItem DolphinView::rootItem() const
@ -907,8 +909,14 @@ void DolphinView::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl)
}
}
void DolphinView::restoreContentsPosition()
void DolphinView::updateViewState()
{
if (m_currentItemIndex >= 0) {
KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
selectionManager->setCurrentItem(m_currentItemIndex);
m_currentItemIndex =-1;
}
if (!m_restoredContentsPosition.isNull()) {
const int x = m_restoredContentsPosition.x();
const int y = m_restoredContentsPosition.y();
@ -917,13 +925,23 @@ void DolphinView::restoreContentsPosition()
m_container->horizontalScrollBar()->setValue(x);
m_container->verticalScrollBar()->setValue(y);
}
}
/*void DolphinView::slotUrlChangeRequested(const KUrl& url)
{
m_viewModeController->setUrl(url);
updateWritableState();
}*/
if (!m_selectedItems.isEmpty()) {
KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
QSet<int> selectedItems = selectionManager->selectedItems();
const KFileItemModel* model = fileItemModel();
foreach (const KFileItem& selectedItem, m_selectedItems) {
const int index = model->index(selectedItem);
if (index >= 0) {
selectedItems.insert(index);
}
}
selectionManager->setSelectedItems(selectedItems);
m_selectedItems.clear();
}
}
void DolphinView::showHoverInformation(const KFileItem& item)
{
@ -958,81 +976,15 @@ void DolphinView::slotDirListerStarted(const KUrl& url)
void DolphinView::slotDirListerCompleted()
{
if (!m_expanderActive) {
slotLoadingCompleted();
}
if (!m_newFileNames.isEmpty()) {
// select all newly added items created by a paste operation or
// a drag & drop operation, and clear the previous selection
/*QAbstractItemView* view = m_viewAccessor.itemView();
if (view) {
view->clearSelection();
const int rowCount = m_viewAccessor.proxyModel()->rowCount();
QItemSelection selection;
for (int row = 0; row < rowCount; ++row) {
const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->index(row, 0);
const QModelIndex dirIndex = m_viewAccessor.proxyModel()->mapToSource(proxyIndex);
const KUrl url = m_viewAccessor.dirModel()->itemForIndex(dirIndex).url();
if (m_newFileNames.contains(url.fileName())) {
selection.merge(QItemSelection(proxyIndex, proxyIndex), QItemSelectionModel::Select);
}
}
view->selectionModel()->select(selection, QItemSelectionModel::Select);
}*/
m_newFileNames.clear();
}
updateWritableState();
}
void DolphinView::slotLoadingCompleted()
{
m_expanderActive = false;
if (!m_activeItemUrl.isEmpty()) {
// assure that the current item remains visible
/*const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_activeItemUrl);
if (dirIndex.isValid()) {
const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex);
QAbstractItemView* view = m_viewAccessor.itemView();
if (view) {
const bool clearSelection = !hasSelection();
view->setCurrentIndex(proxyIndex);
if (clearSelection) {
view->clearSelection();
}
}
m_activeItemUrl.clear();
}*/
}
if (!m_selectedItems.isEmpty()) {
/*const KUrl& baseUrl = url();
KUrl url;
QItemSelection newSelection;
foreach(const KFileItem& item, m_selectedItems) {
url = item.url().upUrl();
if (baseUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) {
const QModelIndex index = m_viewAccessor.proxyModel()->mapFromSource(m_viewAccessor.dirModel()->indexForItem(item));
newSelection.select(index, index);
}
}
QAbstractItemView* view = m_viewAccessor.itemView();
if (view) {
view->selectionModel()->select(newSelection,
QItemSelectionModel::ClearAndSelect
| QItemSelectionModel::Current);
}*/
m_selectedItems.clear();
}
// Restore the contents position. This has to be done using a Qt::QueuedConnection
// because the view might not be in its final state yet.
QTimer::singleShot(0, this, SLOT(restoreContentsPosition()));
// Update the view-state. This has to be done using a Qt::QueuedConnection
// because the view might not be in its final state yet (the view also
// listens to the completed()-signal from KDirLister and the order of
// of slots is undefined).
QTimer::singleShot(0, this, SLOT(updateViewState()));
emit finishedPathLoading(url());
updateWritableState();
}
void DolphinView::slotRefreshItems()
@ -1172,7 +1124,7 @@ void DolphinView::applyAdditionalInfoListToView()
void DolphinView::pasteToUrl(const KUrl& url)
{
addNewFileNames(QApplication::clipboard()->mimeData());
markPastedUrlsAsSelected(QApplication::clipboard()->mimeData());
KonqOperations::doPaste(this, url);
}
@ -1213,12 +1165,10 @@ QMimeData* DolphinView::selectionMimeData() const
return 0;
}
void DolphinView::addNewFileNames(const QMimeData* mimeData)
void DolphinView::markPastedUrlsAsSelected(const QMimeData* mimeData)
{
const KUrl::List urls = KUrl::List::fromMimeData(mimeData);
foreach (const KUrl& url, urls) {
m_newFileNames.insert(url.fileName());
}
markUrlsAsSelected(urls);
}
QItemSelection DolphinView::childrenMatchingPattern(const QModelIndex& parent, const QRegExp& pattern) const
@ -1252,7 +1202,7 @@ void DolphinView::updateWritableState()
const bool wasFolderWritable = m_isFolderWritable;
m_isFolderWritable = true;
const KFileItem item; // = m_viewAccessor.dirLister()->rootItem();
const KFileItem item = m_dirLister->rootItem();
if (!item.isNull()) {
KFileItemListProperties capabilities(KFileItemList() << item);
m_isFolderWritable = capabilities.supportsWriting();

View file

@ -47,6 +47,7 @@ class KAction;
class KActionCollection;
class KFileItemModel;
class KUrl;
class ToolTipManager;
class ViewProperties;
class QRegExp;
@ -553,17 +554,18 @@ private slots:
void activate();
void slotItemClicked(int index, Qt::MouseButton button);
void slotItemExpansionToggleClicked(int index);
void slotItemHovered(int index);
void slotItemUnhovered(int index);
/**
* Emits the signal \a selectionChanged() with a small delay. This is
* because getting all file items for the signal can be an expensive
* because getting all file items for the selection can be an expensive
* operation. Fast selection changes are collected in this case and
* the signal is emitted only after no selection change has been done
* within a small delay.
*/
void slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous);
/**
* Is called by emitDelayedSelectionChangedSignal() and emits the
@ -637,12 +639,6 @@ private slots:
*/
void slotDirListerCompleted();
/**
* Invoked when the loading of the directory is finished.
* Restores the active item and the scroll position if possible.
*/
void slotLoadingCompleted();
/**
* Is invoked when the KDirLister indicates refreshed items.
*/
@ -670,9 +666,10 @@ private slots:
void slotRedirection(const KUrl& oldUrl, const KUrl& newUrl);
/**
* Restores the contents position, if history information about the old position is available.
* Applies the state that has been restored by restoreViewState()
* to the view.
*/
void restoreContentsPosition();
void updateViewState();
//void slotUrlChangeRequested(const KUrl& url);
@ -717,11 +714,11 @@ private:
/**
* Is invoked after a paste operation or a drag & drop
* operation and adds the filenames of all URLs from \a mimeData to
* m_newFileNames. This allows to select all newly added
* items in slotDirListerCompleted().
* operation and URLs from \a mimeData as selected.
* This allows to select all newly pasted
* items in restoreViewState().
*/
void addNewFileNames(const QMimeData* mimeData);
void markPastedUrlsAsSelected(const QMimeData* mimeData);
/**
* Helper method for DolphinView::setItemSelectionEnabled(): Returns the selection for
@ -742,7 +739,6 @@ private:
bool m_active : 1;
bool m_tabsForFiles : 1;
bool m_assureVisibleCurrentIndex : 1;
bool m_expanderActive : 1;
bool m_isFolderWritable : 1;
KUrl m_url;
@ -754,20 +750,15 @@ private:
DolphinDirLister* m_dirLister;
DolphinItemListContainer* m_container;
ToolTipManager* m_toolTipManager;
QTimer* m_selectionChangedTimer;
KUrl m_activeItemUrl;
int m_currentItemIndex;
QPoint m_restoredContentsPosition;
KUrl m_createdItemUrl; // URL for a new item that got created by the "Create New..." menu
KFileItemList m_selectedItems; // this is used for making the View to remember selections after F5
/**
* Remembers the filenames that have been added by a paste operation
* or a drag & drop operation. Allows to select the items in
* slotDirListerCompleted().
*/
QSet<QString> m_newFileNames;
// For unit tests
friend class TestBase;
friend class DolphinDetailsViewTest;

View file

@ -22,18 +22,18 @@
#include "filemetadatatooltip.h"
#include <KIcon>
#include <KIO/PreviewJob>
#include <KSharedConfig>
#include <QApplication>
#include <QDesktopWidget>
#include <QLayout>
#include <QScrollArea>
#include <QScrollBar>
#include <QStyle>
#include <QTimer>
ToolTipManager::ToolTipManager(QWidget* parent) :
QObject(parent),
m_view(parent),
m_parentWidget(parent),
m_showToolTipTimer(0),
m_contentRetrievalTimer(0),
m_fileMetaDataToolTip(0),
@ -43,13 +43,6 @@ ToolTipManager::ToolTipManager(QWidget* parent) :
m_item(),
m_itemRect()
{
//m_dolphinModel = static_cast<DolphinModel*>(m_proxyModel->sourceModel());
//connect(parent, SIGNAL(entered(QModelIndex)),
// this, SLOT(requestToolTip(QModelIndex)));
//connect(parent, SIGNAL(viewportEntered()),
// this, SLOT(hideToolTip()));
// Initialize timers
m_showToolTipTimer = new QTimer(this);
m_showToolTipTimer->setSingleShot(true);
m_showToolTipTimer->setInterval(500);
@ -61,22 +54,36 @@ ToolTipManager::ToolTipManager(QWidget* parent) :
connect(m_contentRetrievalTimer, SIGNAL(timeout()), this, SLOT(startContentRetrieval()));
Q_ASSERT(m_contentRetrievalTimer->interval() < m_showToolTipTimer->interval());
// When the mousewheel is used, the items don't get a hovered indication
// (Qt-issue #200665). To assure that the tooltip still gets hidden,
// the scrollbars are observed.
/*connect(parent->horizontalScrollBar(), SIGNAL(valueChanged(int)),
this, SLOT(hideToolTip()));
connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)),
this, SLOT(hideToolTip()));*/
Q_ASSERT(m_view);
//m_view->viewport()->installEventFilter(this);
//m_view->installEventFilter(this);
}
ToolTipManager::~ToolTipManager()
{
delete m_fileMetaDataToolTip;
m_fileMetaDataToolTip = 0;
}
void ToolTipManager::showToolTip(const KFileItem& item, const QRectF& itemRect)
{
hideToolTip();
m_itemRect = itemRect.toRect();
const int margin = toolTipMargin();
m_itemRect.adjust(-margin, -margin, margin, margin);
m_item = item;
// Only start the retrieving of the content, when the mouse has been over this
// item for 200 milliseconds. This prevents a lot of useless preview jobs and
// meta data retrieval, when passing rapidly over a lot of items.
Q_ASSERT(!m_fileMetaDataToolTip);
m_fileMetaDataToolTip = new FileMetaDataToolTip(m_parentWidget);
connect(m_fileMetaDataToolTip, SIGNAL(metaDataRequestFinished(KFileItemList)),
this, SLOT(slotMetaDataRequestFinished()));
m_contentRetrievalTimer->start();
m_showToolTipTimer->start();
m_toolTipRequested = true;
Q_ASSERT(!m_metaDataRequested);
}
void ToolTipManager::hideToolTip()
@ -91,56 +98,10 @@ void ToolTipManager::hideToolTip()
m_showToolTipTimer->stop();
m_contentRetrievalTimer->stop();
delete m_fileMetaDataToolTip;
m_fileMetaDataToolTip = 0;
}
bool ToolTipManager::eventFilter(QObject* watched, QEvent* event)
{
/*if (watched == m_view->viewport()) {
switch (event->type()) {
case QEvent::Leave:
case QEvent::MouseButtonPress:
hideToolTip();
break;
default:
break;
}
} else if ((watched == m_view) && (event->type() == QEvent::KeyPress)) {
hideToolTip();
}*/
return QObject::eventFilter(watched, event);
}
void ToolTipManager::requestToolTip(const QModelIndex& index)
{
Q_UNUSED(index);
hideToolTip();
// Only request a tooltip for the name column and when no selection or
// drag & drop operation is done (indicated by the left mouse button)
if (!(QApplication::mouseButtons() & Qt::LeftButton)) {
m_itemRect = QRect(); //m_view->visualRect(index);
const QPoint pos; // = m_view->viewport()->mapToGlobal(m_itemRect.topLeft());
m_itemRect.moveTo(pos);
//const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
//m_item = m_dolphinModel->itemForIndex(dirIndex);
// Only start the retrieving of the content, when the mouse has been over this
// item for 200 milliseconds. This prevents a lot of useless preview jobs and
// meta data retrieval, when passing rapidly over a lot of items.
Q_ASSERT(!m_fileMetaDataToolTip);
m_fileMetaDataToolTip = new FileMetaDataToolTip(m_view);
connect(m_fileMetaDataToolTip, SIGNAL(metaDataRequestFinished(KFileItemList)),
this, SLOT(slotMetaDataRequestFinished()));
m_contentRetrievalTimer->start();
m_showToolTipTimer->start();
m_toolTipRequested = true;
Q_ASSERT(!m_metaDataRequested);
if (m_fileMetaDataToolTip) {
m_fileMetaDataToolTip->hide();
delete m_fileMetaDataToolTip;
m_fileMetaDataToolTip = 0;
}
}
@ -223,10 +184,6 @@ void ToolTipManager::showToolTip()
m_appliedWaitCursor = false;
}
if (QApplication::mouseButtons() & Qt::LeftButton) {
return;
}
if (m_fileMetaDataToolTip->preview().isNull() || m_metaDataRequested) {
Q_ASSERT(!m_appliedWaitCursor);
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
@ -259,7 +216,7 @@ void ToolTipManager::showToolTip()
// It must be assured that:
// - the content is fully visible
// - the content is not drawn inside m_itemRect
const int margin = 3;
const int margin = toolTipMargin();
const bool hasRoomToLeft = (m_itemRect.left() - size.width() - margin >= screen.left());
const bool hasRoomToRight = (m_itemRect.right() + size.width() + margin <= screen.right());
const bool hasRoomAbove = (m_itemRect.top() - size.height() - margin >= screen.top());
@ -300,4 +257,10 @@ void ToolTipManager::showToolTip()
m_toolTipRequested = false;
}
int ToolTipManager::toolTipMargin() const
{
const int margin = m_parentWidget->style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth);
return qMax(4, margin);
}
#include "tooltipmanager.moc"

View file

@ -47,19 +47,20 @@ public:
explicit ToolTipManager(QWidget* parent);
virtual ~ToolTipManager();
public slots:
/**
* Hides the currently shown tooltip. Invoking this method is
* only needed when the tooltip should be hidden although
* an item is hovered.
* Triggers the showing of the tooltip for the item \p item
* where the item has the maximum boundaries of \p itemRect.
* The tooltip manager takes care that the tooltip is shown
* slightly delayed.
*/
void showToolTip(const KFileItem& item, const QRectF& itemRect);
/**
* Hides the currently shown tooltip.
*/
void hideToolTip();
protected:
virtual bool eventFilter(QObject* watched, QEvent* event);
private slots:
void requestToolTip(const QModelIndex& index);
void startContentRetrieval();
void setPreviewPix(const KFileItem& item, const QPixmap& pix);
void previewFailed();
@ -67,9 +68,10 @@ private slots:
void showToolTip();
private:
QWidget* m_view;
DolphinModel* m_dolphinModel;
DolphinSortFilterProxyModel* m_proxyModel;
int toolTipMargin() const;
private:
QWidget* m_parentWidget;
/// Timeout from requesting a tooltip until the tooltip
/// should be shown