mirror of
https://invent.kde.org/system/dolphin
synced 2024-09-12 12:54:29 +00:00
Improvements for selections, smooth scrolling, tooltips and info-panel
This commit is contained in:
parent
4692eb7c96
commit
3957884bf2
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
File=dolphin_generalsettings.kcfg
|
||||
ClassName=GeneralSettings
|
||||
Singleton=false
|
||||
ClassName=GeneralSettings
|
||||
Singleton=yes
|
||||
Mutators=true
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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");
|
||||
|
|
172
src/tests/kitemlistselectionmanagertest.cpp
Normal file
172
src/tests/kitemlistselectionmanagertest.cpp
Normal 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"
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue