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
#include <QFontMetricsF>
#include <QGraphicsSceneResizeEvent>
#include <QPainter>
+#include <QStyleOption>
#include <QTextLayout>
#include <QTextLine>
const_cast<KFileItemListWidget*>(this)->updateCache();
}
+ // Draw expansion toggle '>' or 'V'
if (m_isDir && !m_expansionArea.isEmpty()) {
QStyleOption arrowOption;
arrowOption.rect = m_expansionArea.toRect();
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) {
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]);
m_dirtyLayout = true;
}
+void KFileItemListWidget::hoveredChanged(bool hovered)
+{
+ Q_UNUSED(hovered);
+ m_dirtyLayout = true;
+}
+
void KFileItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event)
{
KItemListWidget::resizeEvent(event);
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.
}
}
+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);
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:
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);
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
if (targetIndex - previousTargetIndex > 0 && insertedAtIndex >= 0) {
itemRanges << KItemRange(insertedAtIndex, insertedCount);
- insertedAtIndex = targetIndex;
+ previouslyInsertedCount += insertedCount;
+ insertedAtIndex = targetIndex - previouslyInsertedCount;
insertedCount = 0;
}
if (insertedAtIndex < 0) {
insertedAtIndex = targetIndex;
+ Q_ASSERT(previouslyInsertedCount == 0);
}
++targetIndex;
++sourceIndex;
#include <QGraphicsScene>
#include <QGraphicsView>
+#include <QPropertyAnimation>
#include <QScrollBar>
#include <QStyle>
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);
}
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)
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);
+ 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 {
- 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);
+ 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()
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"
class KItemListController;
class KItemListView;
class KItemModelBase;
+class QPropertyAnimation;
/**
* @brief Provides a QWidget based scrolling view for a KItemListController.
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);
private:
KItemListController* m_controller;
+
+ bool m_sliderMovedByUser;
+ QPropertyAnimation* m_viewOffsetAnimation;
};
#endif
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);
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;
}
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;
}
}
if (emitItemClicked) {
emit itemClicked(index, event->button());
}
+ } else if (!shiftOrControlPressed) {
+ m_selectionManager->clearSelection();
}
}
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;
}
{
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;
}
}
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:
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);
#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)
{
}
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;
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;
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"
#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
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;
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
#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()
#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
int KItemListView::itemAt(const QPointF& pos) const
{
- if (!m_model) {
- return -1;
- }
-
QHashIterator<int, KItemListWidget*> it(m_visibleItems);
while (it.hasNext()) {
it.next();
return QHash<QByteArray, QSizeF>();
}
+QRectF KItemListView::itemBoundingRect(int index) const
+{
+ return m_layouter->itemBoundingRect(index);
+}
+
void KItemListView::beginTransaction()
{
++m_activeTransactions;
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();
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);
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) {
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) {
}
}
-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));
}
}
{
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);
}
}
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) {
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);
}
}
+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);
#include <kitemviews/kitemlistwidget.h>
#include <kitemviews/kitemmodelbase.h>
#include <QGraphicsWidget>
+#include <QSet>
class KItemListController;
class KItemListWidgetCreatorBase;
{
Q_OBJECT
+ Q_PROPERTY(qreal offset READ offset WRITE setOffset)
+
public:
KItemListView(QGraphicsWidget* parent = 0);
virtual ~KItemListView();
virtual QSizeF itemSizeHint(int index) const;
virtual QHash<QByteArray, QSizeF> visibleRoleSizes() const;
+ QRectF itemBoundingRect(int index) const;
+
void beginTransaction();
void endTransaction();
bool isTransactionActive() const;
virtual bool event(QEvent* event);
virtual void mousePressEvent(QGraphicsSceneMouseEvent* event);
- virtual void hoverMoveEvent(QGraphicsSceneHoverEvent* event);
- virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
QList<KItemListWidget*> visibleItemListWidgets() const;
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();
*/
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()
#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(),
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);
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);
void KItemListWidget::setStyleOption(const KItemListStyleOption& option)
{
const KItemListStyleOption previous = m_styleOption;
- if (m_index >= 0) {
- clearCache();
+ clearCache();
+ m_styleOption = option;
- 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();
+ styleOptionChanged(option, previous);
+}
+
+const KItemListStyleOption& KItemListWidget::styleOption() const
+{
+ return m_styleOption;
+}
+
+void KItemListWidget::setSelected(bool selected)
+{
+ if (m_selected != selected) {
+ m_selected = selected;
+ selectedChanged(selected);
+ update();
+ }
+}
- if (!wasHovered && isHovered) {
- m_hoverAnimation->setEndValue(1.0);
- } else {
- Q_ASSERT(wasHovered && !isHovered);
- m_hoverAnimation->setEndValue(0.0);
- }
+bool KItemListWidget::isSelected() const
+{
+ return m_selected;
+}
- m_hoverAnimation->start();
- }
+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_styleOption = option;
+ m_hoverAnimation->setEndValue(0.0);
}
- styleOptionChanged(option, previous);
+ m_hoverAnimation->start();
+
+ hoveredChanged(hovered);
+
+ update();
}
-const KItemListStyleOption& KItemListWidget::styleOption() const
+bool KItemListWidget::isHovered() const
{
- return m_styleOption;
+ return m_hovered;
}
bool KItemListWidget::contains(const QPointF& point) const
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);
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().
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);
/**
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;
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);
File=dolphin_generalsettings.kcfg
-ClassName=GeneralSettings
-Singleton=false
+ClassName=GeneralSettings
+Singleton=yes
Mutators=true
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;
}
const bool useGlobalProps = m_globalProps->isChecked();
- GeneralSettings* settings = DolphinSettings::instance().generalSettings();
+ GeneralSettings* settings = GeneralSettings::self();
settings->setGlobalViewProps(useGlobalProps);
if (useGlobalProps) {
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
void init();
void cleanup();
- void testFeffi();
-
private:
KFileItemListView* m_listView;
KFileItemModel* m_model;
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"
#include "testdir.h"
namespace {
- const int DefaultTimeout = 2000;
+ const int DefaultTimeout = 5000;
};
+Q_DECLARE_METATYPE(KItemRangeList)
+
class KFileItemModelTest : public QObject
{
Q_OBJECT
void testDefaultSortRole();
void testDefaultGroupRole();
void testNewItems();
- void testInsertingItems();
+ void testModelConsistencyWhenInsertingItems();
+ void testItemRangeConsistencyWhenInsertingItems();
void testExpansionLevelsCompare_data();
void testExpansionLevelsCompare();
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
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");
--- /dev/null
+/***************************************************************************
+ * 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"
#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>
#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;
m_active(true),
m_tabsForFiles(false),
m_assureVisibleCurrentIndex(false),
- m_expanderActive(false),
m_isFolderWritable(true),
m_url(url),
m_mode(DolphinView::IconsView),
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);
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);
// view->setFocus();
//}
emit activated();
- emitSelectionChangedSignal();
emit writeStateChanged(m_isFolderWritable);
}
KFileItemList DolphinView::selectedItems() const
{
- return KFileItemList();
-/* KFileItemList itemList;
- const QAbstractItemView* view = m_viewAccessor.itemView();
- if (!view) {
- return itemList;
- }
-
- const QItemSelection selection = m_viewAccessor.proxyModel()->mapSelectionToSource(view->selectionModel()->selection());
+ const KFileItemModel* model = fileItemModel();
+ const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
+ const QSet<int> selectedIndexes = selectionManager->selectedItems();
- const QModelIndexList indexList = selection.indexes();
- foreach (const QModelIndex &index, indexList) {
- KFileItem item = m_viewAccessor.dirModel()->itemForIndex(index);
- if (!item.isNull()) {
- itemList.append(item);
- }
+ KFileItemList selectedItems;
+ QSetIterator<int> it(selectedIndexes);
+ while (it.hasNext()) {
+ const int index = it.next();
+ selectedItems.append(model->fileItem(index));
}
-
- 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)
}
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
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();
-
- 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);
- }
- }
+ KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
+ const QSet<int> selectedItems = selectionManager->selectedItems();
+ QSet<int> invertedSelectedItems;
- QItemSelectionModel* selectionModel = m_viewAccessor.itemView()->selectionModel();
- selectionModel->select(invertedSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
- */
+ const int maxIndex = fileItemModel()->count() - 1;
+ for (int i = 0; i <= maxIndex; ++i) {
+ if (!selectedItems.contains(i)) {
+ invertedSelectedItems.insert(i);
+ }
+ }
+
+ selectionManager->setSelectedItems(invertedSelectedItems);
}
void DolphinView::clearSelection()
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 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 count = selectedItemsCount();
- const bool selectionStateChanged = ((count > 0) && (selected.count() == count)) ||
- ((count == 0) && !deselected.isEmpty());
+ 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
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);
}
{
Q_UNUSED(destItem);
Q_UNUSED(destPath);
- addNewFileNames(event->mimeData());
+ markPastedUrlsAsSelected(event->mimeData());
//DragAndDropHelper::instance().dropUrls(destItem, destPath, event, this);
}
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;
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();
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
}
}
-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();
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)
{
void DolphinView::slotDirListerCompleted()
{
- if (!m_expanderActive) {
- slotLoadingCompleted();
- }
+ // 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()));
- 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();
- }
+ emit finishedPathLoading(url());
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()));
-
- emit finishedPathLoading(url());
-}
-
void DolphinView::slotRefreshItems()
{
if (m_assureVisibleCurrentIndex) {
void DolphinView::pasteToUrl(const KUrl& url)
{
- addNewFileNames(QApplication::clipboard()->mimeData());
+ markPastedUrlsAsSelected(QApplication::clipboard()->mimeData());
KonqOperations::doPaste(this, url);
}
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
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();
class KActionCollection;
class KFileItemModel;
class KUrl;
+class ToolTipManager;
class ViewProperties;
class QRegExp;
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
*/
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.
*/
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);
/**
* 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
bool m_active : 1;
bool m_tabsForFiles : 1;
bool m_assureVisibleCurrentIndex : 1;
- bool m_expanderActive : 1;
bool m_isFolderWritable : 1;
KUrl m_url;
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;
#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),
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);
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()
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;
}
}
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));
// 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());
m_toolTipRequested = false;
}
+int ToolTipManager::toolTipMargin() const
+{
+ const int margin = m_parentWidget->style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth);
+ return qMax(4, margin);
+}
+
#include "tooltipmanager.moc"
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 hideToolTip();
+ void showToolTip(const KFileItem& item, const QRectF& itemRect);
-protected:
- virtual bool eventFilter(QObject* watched, QEvent* event);
+ /**
+ * Hides the currently shown tooltip.
+ */
+ void hideToolTip();
private slots:
- void requestToolTip(const QModelIndex& index);
void startContentRetrieval();
void setPreviewPix(const KFileItem& item, const QPixmap& pix);
void previewFailed();
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