#include "kitemlistview.h"
#include "kitemlistrubberband_p.h"
#include "kitemlistselectionmanager.h"
-
-// TODO: Remove after moving mimeData() and createDropPixmap() into
-// KFileItemModel/KFileItemListView
-#include "kfileitemmodel.h"
-#include <KIcon>
+#include "kitemlistkeyboardsearchmanager_p.h"
#include <QApplication>
#include <QDrag>
#include <QGraphicsSceneEvent>
#include <QMimeData>
+#include <KGlobalSettings>
#include <KDebug>
KItemListController::KItemListController(QObject* parent) :
QObject(parent),
- m_dragging(false),
m_selectionBehavior(NoSelection),
m_model(0),
m_view(0),
m_selectionManager(new KItemListSelectionManager(this)),
+ m_keyboardManager(new KItemListKeyboardSearchManager(this)),
m_pressedIndex(-1),
m_pressedMousePos(),
m_oldSelection()
{
+ connect(m_keyboardManager, SIGNAL(changeCurrentItem(QString,bool)),
+ this, SLOT(slotChangeCurrentItem(QString,bool)));
}
KItemListController::~KItemListController()
KItemListView* oldView = m_view;
if (oldView) {
- disconnect(oldView, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(slotViewOffsetChanged(qreal,qreal)));
+ disconnect(oldView, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(slotViewScrollOffsetChanged(qreal,qreal)));
}
m_view = view;
if (m_view) {
m_view->setController(this);
m_view->setModel(m_model);
- connect(m_view, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(slotViewOffsetChanged(qreal,qreal)));
+ connect(m_view, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(slotViewScrollOffsetChanged(qreal,qreal)));
}
emit viewChanged(m_view, oldView);
}
break;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ emit itemActivated(index);
+ break;
+
case Qt::Key_Space:
if (controlPressed) {
m_selectionManager->endAnchoredSelection();
m_selectionManager->setSelected(index, 1, KItemListSelectionManager::Toggle);
m_selectionManager->beginAnchoredSelection(index);
+ break;
}
default:
- break;
+ m_keyboardManager->addKeys(event->text());
+ return false;
}
if (m_selectionManager->currentItem() != index) {
return true;
}
+void KItemListController::slotChangeCurrentItem(const QString& text, bool searchFromNextItem)
+{
+ if (!m_model) {
+ return;
+ }
+ const int currentIndex = m_selectionManager->currentItem();
+ int index;
+ if (searchFromNextItem) {
+ index = m_model->indexForKeyboardSearch(text, (currentIndex + 1) % m_model->count());
+ }
+ else {
+ index = m_model->indexForKeyboardSearch(text, currentIndex);
+ }
+ if (index >= 0) {
+ m_selectionManager->setCurrentItem(index);
+ m_selectionManager->clearSelection();
+ m_selectionManager->setSelected(index, 1);
+ m_selectionManager->beginAnchoredSelection(index);
+ }
+}
+
bool KItemListController::inputMethodEvent(QInputMethodEvent* event)
{
Q_UNUSED(event);
m_pressedIndex = m_view->itemAt(m_pressedMousePos);
if (m_view->isAboveExpansionToggle(m_pressedIndex, m_pressedMousePos)) {
+ m_selectionManager->setCurrentItem(m_pressedIndex);
return true;
}
const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
const bool controlPressed = event->modifiers() & Qt::ControlModifier;
- const bool shiftOrControlPressed = shiftPressed || controlPressed;
- if (!shiftOrControlPressed || m_selectionBehavior == SingleSelection) {
+ if (m_selectionBehavior == SingleSelection) {
m_selectionManager->clearSelection();
}
if (controlPressed) {
m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
m_selectionManager->beginAnchoredSelection(m_pressedIndex);
+ } else if (event->buttons() & Qt::RightButton) {
+ // Only clear the selection if a context menu is requested above a non-selected item
+ if (!m_selectionManager->selectedItems().contains(m_pressedIndex)) {
+ m_selectionManager->setSelectedItems(QSet<int>() << m_pressedIndex);
+ }
} else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) {
// Select the pressed item and start a new anchored selection
m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select);
break;
}
+ if (event->buttons() & Qt::RightButton) {
+ emit itemContextMenuRequested(m_pressedIndex, event->screenPos());
+ }
+
return true;
- } else {
- KItemListRubberBand* rubberBand = m_view->rubberBand();
- QPointF startPos = m_pressedMousePos;
- if (m_view->scrollOrientation() == Qt::Vertical) {
- startPos.ry() += m_view->offset();
- if (m_view->itemSize().width() < 0) {
- // Use a special rubberband for views that have only one column and
- // expand the rubberband to use the whole width of the view.
- startPos.setX(0);
- }
+ }
+
+ if (event->buttons() & Qt::RightButton) {
+ m_selectionManager->clearSelection();
+
+ const QRectF headerBounds = m_view->headerBoundaries();
+ if (headerBounds.contains(event->pos())) {
+ emit headerContextMenuRequested(event->screenPos());
} else {
- startPos.rx() += m_view->offset();
+ emit viewContextMenuRequested(event->screenPos());
}
+ return true;
+ }
- m_oldSelection = m_selectionManager->selectedItems();
- rubberBand->setStartPosition(startPos);
- rubberBand->setEndPosition(startPos);
- rubberBand->setActive(true);
- connect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged()));
+ KItemListRubberBand* rubberBand = m_view->rubberBand();
+ QPointF startPos = m_pressedMousePos;
+ if (m_view->scrollOrientation() == Qt::Vertical) {
+ startPos.ry() += m_view->scrollOffset();
+ if (m_view->itemSize().width() < 0) {
+ // Use a special rubberband for views that have only one column and
+ // expand the rubberband to use the whole width of the view.
+ startPos.setX(0);
+ }
+ } else {
+ startPos.rx() += m_view->scrollOffset();
}
+ m_oldSelection = m_selectionManager->selectedItems();
+ rubberBand->setStartPosition(startPos);
+ rubberBand->setEndPosition(startPos);
+ rubberBand->setActive(true);
+ connect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged()));
+ m_view->setAutoScroll(true);
+
return false;
}
if (m_pressedIndex >= 0) {
// Check whether a dragging should be started
- if (!m_dragging) {
+ if (event->buttons() & Qt::LeftButton) {
const QPointF pos = transform.map(event->pos());
- const qreal minDragDiff = 4;
- m_dragging = qAbs(pos.x() - m_pressedMousePos.x()) >= minDragDiff ||
- qAbs(pos.y() - m_pressedMousePos.y()) >= minDragDiff;
- if (m_dragging) {
+ if ((pos - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
startDragging();
}
}
if (rubberBand->isActive()) {
QPointF endPos = transform.map(event->pos());
if (m_view->scrollOrientation() == Qt::Vertical) {
- endPos.ry() += m_view->offset();
+ endPos.ry() += m_view->scrollOffset();
if (m_view->itemSize().width() < 0) {
// Use a special rubberband for views that have only one column and
// expand the rubberband to use the whole width of the view.
endPos.setX(m_view->size().width());
}
} else {
- endPos.rx() += m_view->offset();
+ endPos.rx() += m_view->scrollOffset();
}
rubberBand->setEndPosition(endPos);
}
return false;
}
+ const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier ||
+ event->modifiers() & Qt::ControlModifier;
+
+ bool clearSelection = !shiftOrControlPressed && event->button() != Qt::RightButton;
+
KItemListRubberBand* rubberBand = m_view->rubberBand();
if (rubberBand->isActive()) {
disconnect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged()));
rubberBand->setActive(false);
m_oldSelection.clear();
- } else {
- 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 (shiftOrControlPressed) {
- // The mouse click should only update the selection, not trigger the item
- emitItemClicked = false;
- }
- }
+ m_view->setAutoScroll(false);
- if (emitItemClicked) {
- emit itemClicked(index, event->button());
+ if (rubberBand->startPosition() != rubberBand->endPosition()) {
+ clearSelection = false;
+ }
+ }
+
+ const QPointF pos = transform.map(event->pos());
+ const int index = m_view->itemAt(pos);
+
+ if (index >= 0 && index == m_pressedIndex) {
+ // The release event is done above the same item as the press event
+
+ if (clearSelection) {
+ // Clear the previous selection but reselect the current index
+ m_selectionManager->setSelectedItems(QSet<int>() << index);
+ }
+
+ if (event->button() & Qt::LeftButton) {
+ bool emitItemActivated = true;
+ if (m_view->isAboveExpansionToggle(index, pos)) {
+ emit itemExpansionToggleClicked(index);
+ emitItemActivated = false;
+ } else if (shiftOrControlPressed) {
+ // The mouse click should only update the selection, not trigger the item
+ emitItemActivated = false;
+ } else if (!KGlobalSettings::singleClick()) {
+ emitItemActivated = false;
}
- } else if (!shiftOrControlPressed) {
- m_selectionManager->clearSelection();
+ if (emitItemActivated) {
+ emit itemActivated(index);
+ }
+ } else if (event->button() & Qt::MidButton) {
+ emit itemMiddleClicked(index);
}
+ } else if (clearSelection) {
+ m_selectionManager->clearSelection();
}
- m_dragging = false;
m_pressedMousePos = QPointF();
m_pressedIndex = -1;
return false;
bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
{
- Q_UNUSED(event);
- Q_UNUSED(transform);
+ const QPointF pos = transform.map(event->pos());
+ const int index = m_view->itemAt(pos);
+
+ bool emitItemActivated = !KGlobalSettings::singleClick() &&
+ (event->button() & Qt::LeftButton) &&
+ index >= 0 && index < m_model->count();
+ if (emitItemActivated) {
+ emit itemActivated(index);
+ }
return false;
}
bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
{
- Q_UNUSED(event);
Q_UNUSED(transform);
+ if (!m_model || !m_view) {
+ return false;
+ }
+
+ KItemListWidget* oldHoveredWidget = hoveredWidget();
+ KItemListWidget* newHoveredWidget = widgetForPos(event->pos());
+ if (oldHoveredWidget != newHoveredWidget) {
+ if (oldHoveredWidget) {
+ oldHoveredWidget->setHovered(false);
+ emit itemUnhovered(oldHoveredWidget->index());
+ }
+
+ if (newHoveredWidget) {
+ const int index = newHoveredWidget->index();
+ if (m_model->supportsDropping(index)) {
+ newHoveredWidget->setHovered(true);
+ }
+ emit itemHovered(index);
+ }
+ }
+
return false;
}
bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
{
- Q_UNUSED(event);
- Q_UNUSED(transform);
+ Q_UNUSED(transform)
+ if (!m_view) {
+ return false;
+ }
- m_dragging = false;
- return false;
+ const QPointF pos = transform.map(event->pos());
+ const int index = m_view->itemAt(pos);
+ emit itemDropEvent(index, event);
+
+ return true;
}
bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
{
- // 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;
+ KItemListWidget* oldHoveredWidget = hoveredWidget();
+ KItemListWidget* newHoveredWidget = widgetForPos(event->pos());
+ if (oldHoveredWidget != newHoveredWidget) {
+ if (oldHoveredWidget) {
+ oldHoveredWidget->setHovered(false);
+ emit itemUnhovered(oldHoveredWidget->index());
}
- }
- // 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());
+ if (newHoveredWidget) {
+ newHoveredWidget->setHovered(true);
+ emit itemHovered(newHoveredWidget->index());
}
}
return mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
case QEvent::GraphicsSceneMouseRelease:
return mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ return mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
case QEvent::GraphicsSceneWheel:
return wheelEvent(static_cast<QGraphicsSceneWheelEvent*>(event), QTransform());
case QEvent::GraphicsSceneDragEnter:
return false;
}
-void KItemListController::slotViewOffsetChanged(qreal current, qreal previous)
+void KItemListController::slotViewScrollOffsetChanged(qreal current, qreal previous)
{
if (!m_view) {
return;
const bool scrollVertical = (m_view->scrollOrientation() == Qt::Vertical);
if (scrollVertical) {
- rubberBandRect.translate(0, -m_view->offset());
+ rubberBandRect.translate(0, -m_view->scrollOffset());
} else {
- rubberBandRect.translate(-m_view->offset(), 0);
+ rubberBandRect.translate(-m_view->scrollOffset(), 0);
}
if (!m_oldSelection.isEmpty()) {
foreach (const KItemListWidget* widget, m_view->visibleItemListWidgets()) {
const int index = widget->index();
- const QRectF widgetRect = m_view->itemBoundingRect(index);
+ const QRectF widgetRect = m_view->itemRect(index);
if (widgetRect.intersects(rubberBandRect)) {
- const QRectF iconRect = widget->iconBoundingRect().translated(widgetRect.topLeft());
- const QRectF textRect = widget->textBoundingRect().translated(widgetRect.topLeft());
+ const QRectF iconRect = widget->iconRect().translated(widgetRect.topLeft());
+ const QRectF textRect = widget->textRect().translated(widgetRect.topLeft());
if (iconRect.intersects(rubberBandRect) || textRect.intersects(rubberBandRect)) {
selectedItems.insert(index);
}
int index = increaseIndex ? m_view->lastVisibleIndex() + 1 : m_view->firstVisibleIndex() - 1;
bool selectionFinished = false;
do {
- const QRectF widgetRect = m_view->itemBoundingRect(index);
+ const QRectF widgetRect = m_view->itemRect(index);
if (widgetRect.intersects(rubberBandRect)) {
selectedItems.insert(index);
}
}
} while (!selectionFinished);
- m_selectionManager->setSelectedItems(selectedItems + m_oldSelection);
+ if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
+ // If Control is pressed, the selection state of all items in the rubberband is toggled.
+ // Therefore, the new selection contains:
+ // 1. All previously selected items which are not inside the rubberband, and
+ // 2. all items inside the rubberband which have not been selected previously.
+ m_selectionManager->setSelectedItems((m_oldSelection - selectedItems) + (selectedItems - m_oldSelection));
+ }
+ else {
+ m_selectionManager->setSelectedItems(selectedItems + m_oldSelection);
+ }
}
-QPixmap KItemListController::createDragPixmap(const QSet<int>& indexes) const
+void KItemListController::startDragging()
{
- if (!m_model || !m_view) {
- return QPixmap();
- }
-
- // TODO: The current hack assumes a property "iconPixmap" in the model. The method
- // will get an interface of KFileItemList later.
- QSetIterator<int> it(indexes);
- while (it.hasNext()) {
- const int index = it.next();
- // TODO: Only one item is considered currently
- QPixmap pixmap = m_model->data(index).value("iconPixmap").value<QPixmap>();
- if (pixmap.isNull()) {
- KIcon icon(m_model->data(index).value("iconName").toString());
- const QSizeF size = m_view->itemSize();
- pixmap = icon.pixmap(size.toSize());
- }
- return pixmap;
+ if (!m_view || !m_model) {
+ return;
+ }
+
+ const QSet<int> selectedItems = m_selectionManager->selectedItems();
+ QMimeData* data = m_model->createMimeData(selectedItems);
+ if (!data) {
+ return;
}
- return QPixmap();
+ // The created drag object will be owned and deleted
+ // by QApplication::activeWindow().
+ QDrag* drag = new QDrag(QApplication::activeWindow());
+ drag->setMimeData(data);
+
+ const QPixmap pixmap = m_view->createDragPixmap(selectedItems);
+ drag->setPixmap(pixmap);
+
+ drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::IgnoreAction);
}
-QMimeData* KItemListController::createMimeData(const QSet<int>& indexes) const
+KItemListWidget* KItemListController::hoveredWidget() const
{
- if (!m_model) {
- return 0;
- }
-
- QMimeData* data = new QMimeData();
+ Q_ASSERT(m_view);
- // TODO: Check KDirModel::mimeData() for a good reference implementation
- KUrl::List urls;
- QSetIterator<int> it(indexes);
- while (it.hasNext()) {
- const int index = it.next();
- // TODO: Big hack to use KFileItemModel here. Remove after moving mimeData()
- // into KFileItemModel.
- KFileItemModel* model = qobject_cast<KFileItemModel*>(m_model);
- Q_ASSERT(model);
- const KUrl url = model->fileItem(index).url();
- urls.append(url);
+ foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
+ if (widget->isHovered()) {
+ return widget;
+ }
}
- urls.populateMimeData(data);
-
- return data;
+ return 0;
}
-void KItemListController::startDragging()
+KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const
{
- // The created drag object will be owned and deleted
- // by QApplication::activeWindow().
- QDrag* drag = new QDrag(QApplication::activeWindow());
+ Q_ASSERT(m_view);
- const QSet<int> selectedItems = m_selectionManager->selectedItems();
-
- const QPixmap pixmap = createDragPixmap(selectedItems);
- drag->setPixmap(pixmap);
+ foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
+ const QPointF mappedPos = widget->mapFromItem(m_view, pos);
- QMimeData* data = createMimeData(selectedItems);
- drag->setMimeData(data);
+ const bool hovered = widget->contains(mappedPos) &&
+ !widget->expansionToggleRect().contains(mappedPos) &&
+ !widget->selectionToggleRect().contains(mappedPos);
+ if (hovered) {
+ return widget;
+ }
+ }
- drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::IgnoreAction);
+ return 0;
}
#include "kitemlistcontroller.moc"