]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kitemlistcontroller.cpp
Two clicks on file/folder to rename
[dolphin.git] / src / kitemviews / kitemlistcontroller.cpp
1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * Copyright (C) 2012 by Frank Reininghaus <frank78ac@googlemail.com> *
4 * *
5 * Based on the Itemviews NG project from Trolltech Labs: *
6 * http://qt.gitorious.org/qt-labs/itemviews-ng *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
22 ***************************************************************************/
23
24 #include "kitemlistcontroller.h"
25
26
27 #include "kitemlistview.h"
28 #include "kitemlistselectionmanager.h"
29
30 #include "private/kitemlistrubberband.h"
31 #include "private/kitemlistkeyboardsearchmanager.h"
32
33 #include <QApplication>
34 #include <QDrag>
35 #include <QEvent>
36 #include <QGraphicsScene>
37 #include <QGraphicsSceneEvent>
38 #include <QGraphicsView>
39 #include <QMimeData>
40 #include <QTimer>
41 #include <QAccessible>
42
43 KItemListController::KItemListController(KItemModelBase* model, KItemListView* view, QObject* parent) :
44 QObject(parent),
45 m_singleClickActivationEnforced(false),
46 m_selectionTogglePressed(false),
47 m_clearSelectionIfItemsAreNotDragged(false),
48 m_selectionBehavior(NoSelection),
49 m_autoActivationBehavior(ActivationAndExpansion),
50 m_mouseDoubleClickAction(ActivateItemOnly),
51 m_model(0),
52 m_view(0),
53 m_selectionManager(new KItemListSelectionManager(this)),
54 m_keyboardManager(new KItemListKeyboardSearchManager(this)),
55 m_pressedIndex(-1),
56 m_pressedMousePos(),
57 m_autoActivationTimer(0),
58 m_oldSelection(),
59 m_keyboardAnchorIndex(-1),
60 m_keyboardAnchorPos(0)
61 {
62 connect(m_keyboardManager, &KItemListKeyboardSearchManager::changeCurrentItem,
63 this, &KItemListController::slotChangeCurrentItem);
64 connect(m_selectionManager, &KItemListSelectionManager::currentChanged,
65 m_keyboardManager, &KItemListKeyboardSearchManager::slotCurrentChanged);
66
67 m_autoActivationTimer = new QTimer(this);
68 m_autoActivationTimer->setSingleShot(true);
69 m_autoActivationTimer->setInterval(-1);
70 connect(m_autoActivationTimer, &QTimer::timeout, this, &KItemListController::slotAutoActivationTimeout);
71
72 setModel(model);
73 setView(view);
74 }
75
76 KItemListController::~KItemListController()
77 {
78 setView(0);
79 Q_ASSERT(!m_view);
80
81 setModel(0);
82 Q_ASSERT(!m_model);
83 }
84
85 void KItemListController::setModel(KItemModelBase* model)
86 {
87 if (m_model == model) {
88 return;
89 }
90
91 KItemModelBase* oldModel = m_model;
92 if (oldModel) {
93 oldModel->deleteLater();
94 }
95
96 m_model = model;
97 if (m_model) {
98 m_model->setParent(this);
99 }
100
101 if (m_view) {
102 m_view->setModel(m_model);
103 }
104
105 m_selectionManager->setModel(m_model);
106
107 emit modelChanged(m_model, oldModel);
108 }
109
110 KItemModelBase* KItemListController::model() const
111 {
112 return m_model;
113 }
114
115 KItemListSelectionManager* KItemListController::selectionManager() const
116 {
117 return m_selectionManager;
118 }
119
120 void KItemListController::setView(KItemListView* view)
121 {
122 if (m_view == view) {
123 return;
124 }
125
126 KItemListView* oldView = m_view;
127 if (oldView) {
128 disconnect(oldView, &KItemListView::scrollOffsetChanged, this, &KItemListController::slotViewScrollOffsetChanged);
129 oldView->deleteLater();
130 }
131
132 m_view = view;
133
134 if (m_view) {
135 m_view->setParent(this);
136 m_view->setController(this);
137 m_view->setModel(m_model);
138 connect(m_view, &KItemListView::scrollOffsetChanged, this, &KItemListController::slotViewScrollOffsetChanged);
139 updateExtendedSelectionRegion();
140 }
141
142 emit viewChanged(m_view, oldView);
143 }
144
145 KItemListView* KItemListController::view() const
146 {
147 return m_view;
148 }
149
150 void KItemListController::setSelectionBehavior(SelectionBehavior behavior)
151 {
152 m_selectionBehavior = behavior;
153 updateExtendedSelectionRegion();
154 }
155
156 KItemListController::SelectionBehavior KItemListController::selectionBehavior() const
157 {
158 return m_selectionBehavior;
159 }
160
161 void KItemListController::setAutoActivationBehavior(AutoActivationBehavior behavior)
162 {
163 m_autoActivationBehavior = behavior;
164 }
165
166 KItemListController::AutoActivationBehavior KItemListController::autoActivationBehavior() const
167 {
168 return m_autoActivationBehavior;
169 }
170
171 void KItemListController::setMouseDoubleClickAction(MouseDoubleClickAction action)
172 {
173 m_mouseDoubleClickAction = action;
174 }
175
176 KItemListController::MouseDoubleClickAction KItemListController::mouseDoubleClickAction() const
177 {
178 return m_mouseDoubleClickAction;
179 }
180
181 void KItemListController::setAutoActivationDelay(int delay)
182 {
183 m_autoActivationTimer->setInterval(delay);
184 }
185
186 int KItemListController::autoActivationDelay() const
187 {
188 return m_autoActivationTimer->interval();
189 }
190
191 void KItemListController::setSingleClickActivationEnforced(bool singleClick)
192 {
193 m_singleClickActivationEnforced = singleClick;
194 }
195
196 bool KItemListController::singleClickActivationEnforced() const
197 {
198 return m_singleClickActivationEnforced;
199 }
200
201 bool KItemListController::showEvent(QShowEvent* event)
202 {
203 Q_UNUSED(event);
204 return false;
205 }
206
207 bool KItemListController::hideEvent(QHideEvent* event)
208 {
209 Q_UNUSED(event);
210 return false;
211 }
212
213 bool KItemListController::keyPressEvent(QKeyEvent* event)
214 {
215 int index = m_selectionManager->currentItem();
216 int key = event->key();
217
218 // Handle the expanding/collapsing of items
219 if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
220 if (key == Qt::Key_Right) {
221 if (m_model->setExpanded(index, true)) {
222 return true;
223 }
224 } else if (key == Qt::Key_Left) {
225 if (m_model->setExpanded(index, false)) {
226 return true;
227 }
228 }
229 }
230
231 const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
232 const bool controlPressed = event->modifiers() & Qt::ControlModifier;
233 const bool shiftOrControlPressed = shiftPressed || controlPressed;
234
235 const int itemCount = m_model->count();
236
237 // For horizontal scroll orientation, transform
238 // the arrow keys to simplify the event handling.
239 if (m_view->scrollOrientation() == Qt::Horizontal) {
240 switch (key) {
241 case Qt::Key_Up: key = Qt::Key_Left; break;
242 case Qt::Key_Down: key = Qt::Key_Right; break;
243 case Qt::Key_Left: key = Qt::Key_Up; break;
244 case Qt::Key_Right: key = Qt::Key_Down; break;
245 default: break;
246 }
247 }
248
249 const bool selectSingleItem = m_selectionBehavior != NoSelection &&
250 itemCount == 1 &&
251 (key == Qt::Key_Home || key == Qt::Key_End ||
252 key == Qt::Key_Up || key == Qt::Key_Down ||
253 key == Qt::Key_Left || key == Qt::Key_Right);
254 if (selectSingleItem) {
255 const int current = m_selectionManager->currentItem();
256 m_selectionManager->setSelected(current);
257 return true;
258 }
259
260 switch (key) {
261 case Qt::Key_Home:
262 index = 0;
263 m_keyboardAnchorIndex = index;
264 m_keyboardAnchorPos = keyboardAnchorPos(index);
265 break;
266
267 case Qt::Key_End:
268 index = itemCount - 1;
269 m_keyboardAnchorIndex = index;
270 m_keyboardAnchorPos = keyboardAnchorPos(index);
271 break;
272
273 case Qt::Key_Left:
274 if (index > 0) {
275 const int expandedParentsCount = m_model->expandedParentsCount(index);
276 if (expandedParentsCount == 0) {
277 --index;
278 } else {
279 // Go to the parent of the current item.
280 do {
281 --index;
282 } while (index > 0 && m_model->expandedParentsCount(index) == expandedParentsCount);
283 }
284 m_keyboardAnchorIndex = index;
285 m_keyboardAnchorPos = keyboardAnchorPos(index);
286 }
287 break;
288
289 case Qt::Key_Right:
290 if (index < itemCount - 1) {
291 ++index;
292 m_keyboardAnchorIndex = index;
293 m_keyboardAnchorPos = keyboardAnchorPos(index);
294 }
295 break;
296
297 case Qt::Key_Up:
298 updateKeyboardAnchor();
299 index = previousRowIndex(index);
300 break;
301
302 case Qt::Key_Down:
303 updateKeyboardAnchor();
304 index = nextRowIndex(index);
305 break;
306
307 case Qt::Key_PageUp:
308 if (m_view->scrollOrientation() == Qt::Horizontal) {
309 // The new current index should correspond to the first item in the current column.
310 int newIndex = qMax(index - 1, 0);
311 while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() < m_view->itemRect(index).topLeft().y()) {
312 index = newIndex;
313 newIndex = qMax(index - 1, 0);
314 }
315 m_keyboardAnchorIndex = index;
316 m_keyboardAnchorPos = keyboardAnchorPos(index);
317 } else {
318 const qreal currentItemBottom = m_view->itemRect(index).bottomLeft().y();
319 const qreal height = m_view->geometry().height();
320
321 // The new current item should be the first item in the current
322 // column whose itemRect's top coordinate is larger than targetY.
323 const qreal targetY = currentItemBottom - height;
324
325 updateKeyboardAnchor();
326 int newIndex = previousRowIndex(index);
327 do {
328 index = newIndex;
329 updateKeyboardAnchor();
330 newIndex = previousRowIndex(index);
331 } while (m_view->itemRect(newIndex).topLeft().y() > targetY && newIndex != index);
332 }
333 break;
334
335 case Qt::Key_PageDown:
336 if (m_view->scrollOrientation() == Qt::Horizontal) {
337 // The new current index should correspond to the last item in the current column.
338 int newIndex = qMin(index + 1, m_model->count() - 1);
339 while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() > m_view->itemRect(index).topLeft().y()) {
340 index = newIndex;
341 newIndex = qMin(index + 1, m_model->count() - 1);
342 }
343 m_keyboardAnchorIndex = index;
344 m_keyboardAnchorPos = keyboardAnchorPos(index);
345 } else {
346 const qreal currentItemTop = m_view->itemRect(index).topLeft().y();
347 const qreal height = m_view->geometry().height();
348
349 // The new current item should be the last item in the current
350 // column whose itemRect's bottom coordinate is smaller than targetY.
351 const qreal targetY = currentItemTop + height;
352
353 updateKeyboardAnchor();
354 int newIndex = nextRowIndex(index);
355 do {
356 index = newIndex;
357 updateKeyboardAnchor();
358 newIndex = nextRowIndex(index);
359 } while (m_view->itemRect(newIndex).bottomLeft().y() < targetY && newIndex != index);
360 }
361 break;
362
363 case Qt::Key_Enter:
364 case Qt::Key_Return: {
365 const KItemSet selectedItems = m_selectionManager->selectedItems();
366 if (selectedItems.count() >= 2) {
367 emit itemsActivated(selectedItems);
368 } else if (selectedItems.count() == 1) {
369 emit itemActivated(selectedItems.first());
370 } else {
371 emit itemActivated(index);
372 }
373 break;
374 }
375
376 case Qt::Key_Menu: {
377 // Emit the signal itemContextMenuRequested() in case if at least one
378 // item is selected. Otherwise the signal viewContextMenuRequested() will be emitted.
379 const KItemSet selectedItems = m_selectionManager->selectedItems();
380 int index = -1;
381 if (selectedItems.count() >= 2) {
382 const int currentItemIndex = m_selectionManager->currentItem();
383 index = selectedItems.contains(currentItemIndex)
384 ? currentItemIndex : selectedItems.first();
385 } else if (selectedItems.count() == 1) {
386 index = selectedItems.first();
387 }
388
389 if (index >= 0) {
390 const QRectF contextRect = m_view->itemContextRect(index);
391 const QPointF pos(m_view->scene()->views().first()->mapToGlobal(contextRect.bottomRight().toPoint()));
392 emit itemContextMenuRequested(index, pos);
393 } else {
394 emit viewContextMenuRequested(QCursor::pos());
395 }
396 break;
397 }
398
399 case Qt::Key_Escape:
400 if (m_selectionBehavior != SingleSelection) {
401 m_selectionManager->clearSelection();
402 }
403 m_keyboardManager->cancelSearch();
404 emit escapePressed();
405 break;
406
407 case Qt::Key_Space:
408 if (m_selectionBehavior == MultiSelection) {
409 if (controlPressed) {
410 // Toggle the selection state of the current item.
411 m_selectionManager->endAnchoredSelection();
412 m_selectionManager->setSelected(index, 1, KItemListSelectionManager::Toggle);
413 m_selectionManager->beginAnchoredSelection(index);
414 break;
415 } else {
416 // Select the current item if it is not selected yet.
417 const int current = m_selectionManager->currentItem();
418 if (!m_selectionManager->isSelected(current)) {
419 m_selectionManager->setSelected(current);
420 break;
421 }
422 }
423 }
424 // Fall through to the default case and add the Space to the current search string.
425
426 default:
427 m_keyboardManager->addKeys(event->text());
428 // Make sure unconsumed events get propagated up the chain. #302329
429 event->ignore();
430 return false;
431 }
432
433 if (m_selectionManager->currentItem() != index) {
434 switch (m_selectionBehavior) {
435 case NoSelection:
436 m_selectionManager->setCurrentItem(index);
437 break;
438
439 case SingleSelection:
440 m_selectionManager->setCurrentItem(index);
441 m_selectionManager->clearSelection();
442 m_selectionManager->setSelected(index, 1);
443 break;
444
445 case MultiSelection:
446 if (controlPressed) {
447 m_selectionManager->endAnchoredSelection();
448 }
449
450 m_selectionManager->setCurrentItem(index);
451
452 if (!shiftOrControlPressed) {
453 m_selectionManager->clearSelection();
454 m_selectionManager->setSelected(index, 1);
455 }
456
457 if (!shiftPressed) {
458 m_selectionManager->beginAnchoredSelection(index);
459 }
460 break;
461 }
462
463 m_view->scrollToItem(index);
464 }
465 return true;
466 }
467
468 void KItemListController::slotChangeCurrentItem(const QString& text, bool searchFromNextItem)
469 {
470 if (!m_model || m_model->count() == 0) {
471 return;
472 }
473 const int currentIndex = m_selectionManager->currentItem();
474 int index;
475 if (searchFromNextItem) {
476 index = m_model->indexForKeyboardSearch(text, (currentIndex + 1) % m_model->count());
477 } else {
478 index = m_model->indexForKeyboardSearch(text, currentIndex);
479 }
480 if (index >= 0) {
481 m_selectionManager->setCurrentItem(index);
482
483 if (m_selectionBehavior != NoSelection) {
484 m_selectionManager->clearSelection();
485 m_selectionManager->setSelected(index, 1);
486 m_selectionManager->beginAnchoredSelection(index);
487 }
488
489 m_view->scrollToItem(index);
490 }
491 }
492
493 void KItemListController::slotAutoActivationTimeout()
494 {
495 if (!m_model || !m_view) {
496 return;
497 }
498
499 const int index = m_autoActivationTimer->property("index").toInt();
500 if (index < 0 || index >= m_model->count()) {
501 return;
502 }
503
504 /* m_view->isUnderMouse() fixes a bug in the Folder-View-Panel and in the
505 * Places-Panel.
506 *
507 * Bug: When you drag a file onto a Folder-View-Item or a Places-Item and
508 * then move away before the auto-activation timeout triggers, than the
509 * item still becomes activated/expanded.
510 *
511 * See Bug 293200 and 305783
512 */
513 if (m_model->supportsDropping(index) && m_view->isUnderMouse()) {
514 if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
515 const bool expanded = m_model->isExpanded(index);
516 m_model->setExpanded(index, !expanded);
517 } else if (m_autoActivationBehavior != ExpansionOnly) {
518 emit itemActivated(index);
519 }
520 }
521 }
522
523 bool KItemListController::inputMethodEvent(QInputMethodEvent* event)
524 {
525 Q_UNUSED(event);
526 return false;
527 }
528
529 bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
530 {
531 if (!m_view) {
532 return false;
533 }
534
535 m_pressedMousePos = transform.map(event->pos());
536 m_pressedIndex = m_view->itemAt(m_pressedMousePos);
537 emit mouseButtonPressed(m_pressedIndex, event->buttons());
538
539 if (event->buttons() & (Qt::BackButton | Qt::ForwardButton)) {
540 // Do not select items when clicking the back/forward buttons, see
541 // https://bugs.kde.org/show_bug.cgi?id=327412.
542 return true;
543 }
544
545 if (m_view->isAboveExpansionToggle(m_pressedIndex, m_pressedMousePos)) {
546 m_selectionManager->endAnchoredSelection();
547 m_selectionManager->setCurrentItem(m_pressedIndex);
548 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
549 return true;
550 }
551
552 m_selectionTogglePressed = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos);
553 if (m_selectionTogglePressed) {
554 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
555 // The previous anchored selection has been finished already in
556 // KItemListSelectionManager::setSelected(). We can safely change
557 // the current item and start a new anchored selection now.
558 m_selectionManager->setCurrentItem(m_pressedIndex);
559 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
560 return true;
561 }
562
563 const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
564 const bool controlPressed = event->modifiers() & Qt::ControlModifier;
565
566 // The previous selection is cleared if either
567 // 1. The selection mode is SingleSelection, or
568 // 2. the selection mode is MultiSelection, and *none* of the following conditions are met:
569 // a) Shift or Control are pressed.
570 // b) The clicked item is selected already. In that case, the user might want to:
571 // - start dragging multiple items, or
572 // - open the context menu and perform an action for all selected items.
573 const bool shiftOrControlPressed = shiftPressed || controlPressed;
574 const bool pressedItemAlreadySelected = m_pressedIndex >= 0 && m_selectionManager->isSelected(m_pressedIndex);
575 const bool clearSelection = m_selectionBehavior == SingleSelection ||
576 (!shiftOrControlPressed && !pressedItemAlreadySelected);
577 if (clearSelection) {
578 m_selectionManager->clearSelection();
579 } else if (pressedItemAlreadySelected && !shiftOrControlPressed && (event->buttons() & Qt::LeftButton)) {
580 // The user might want to start dragging multiple items, but if he clicks the item
581 // in order to trigger it instead, the other selected items must be deselected.
582 // However, we do not know yet what the user is going to do.
583 // -> remember that the user pressed an item which had been selected already and
584 // clear the selection in mouseReleaseEvent(), unless the items are dragged.
585 m_clearSelectionIfItemsAreNotDragged = true;
586
587 if (m_selectionManager->selectedItems().count() == 1 && m_view->isAboveText(m_pressedIndex, m_pressedMousePos)) {
588 emit selectedItemTextPressed(m_pressedIndex);
589 }
590 }
591
592 if (!shiftPressed) {
593 // Finish the anchored selection before the current index is changed
594 m_selectionManager->endAnchoredSelection();
595 }
596
597 if (m_pressedIndex >= 0) {
598 m_selectionManager->setCurrentItem(m_pressedIndex);
599
600 switch (m_selectionBehavior) {
601 case NoSelection:
602 break;
603
604 case SingleSelection:
605 m_selectionManager->setSelected(m_pressedIndex);
606 break;
607
608 case MultiSelection:
609 if (controlPressed && !shiftPressed) {
610 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
611 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
612 } else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) {
613 // Select the pressed item and start a new anchored selection
614 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select);
615 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
616 }
617 break;
618
619 default:
620 Q_ASSERT(false);
621 break;
622 }
623
624 if (event->buttons() & Qt::RightButton) {
625 emit itemContextMenuRequested(m_pressedIndex, event->screenPos());
626 }
627
628 return true;
629 }
630
631 if (event->buttons() & Qt::RightButton) {
632 const QRectF headerBounds = m_view->headerBoundaries();
633 if (headerBounds.contains(event->pos())) {
634 emit headerContextMenuRequested(event->screenPos());
635 } else {
636 emit viewContextMenuRequested(event->screenPos());
637 }
638 return true;
639 }
640
641 if (m_selectionBehavior == MultiSelection) {
642 QPointF startPos = m_pressedMousePos;
643 if (m_view->scrollOrientation() == Qt::Vertical) {
644 startPos.ry() += m_view->scrollOffset();
645 if (m_view->itemSize().width() < 0) {
646 // Use a special rubberband for views that have only one column and
647 // expand the rubberband to use the whole width of the view.
648 startPos.setX(0);
649 }
650 } else {
651 startPos.rx() += m_view->scrollOffset();
652 }
653
654 m_oldSelection = m_selectionManager->selectedItems();
655 KItemListRubberBand* rubberBand = m_view->rubberBand();
656 rubberBand->setStartPosition(startPos);
657 rubberBand->setEndPosition(startPos);
658 rubberBand->setActive(true);
659 connect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged);
660 m_view->setAutoScroll(true);
661 }
662
663 return false;
664 }
665
666 bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
667 {
668 if (!m_view) {
669 return false;
670 }
671
672 if (m_pressedIndex >= 0) {
673 // Check whether a dragging should be started
674 if (event->buttons() & Qt::LeftButton) {
675 const QPointF pos = transform.map(event->pos());
676 if ((pos - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
677 if (!m_selectionManager->isSelected(m_pressedIndex)) {
678 // Always assure that the dragged item gets selected. Usually this is already
679 // done on the mouse-press event, but when using the selection-toggle on a
680 // selected item the dragged item is not selected yet.
681 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
682 } else {
683 // A selected item has been clicked to drag all selected items
684 // -> the selection should not be cleared when the mouse button is released.
685 m_clearSelectionIfItemsAreNotDragged = false;
686 }
687
688 startDragging();
689 }
690 }
691 } else {
692 KItemListRubberBand* rubberBand = m_view->rubberBand();
693 if (rubberBand->isActive()) {
694 QPointF endPos = transform.map(event->pos());
695
696 // Update the current item.
697 const int newCurrent = m_view->itemAt(endPos);
698 if (newCurrent >= 0) {
699 // It's expected that the new current index is also the new anchor (bug 163451).
700 m_selectionManager->endAnchoredSelection();
701 m_selectionManager->setCurrentItem(newCurrent);
702 m_selectionManager->beginAnchoredSelection(newCurrent);
703 }
704
705 if (m_view->scrollOrientation() == Qt::Vertical) {
706 endPos.ry() += m_view->scrollOffset();
707 if (m_view->itemSize().width() < 0) {
708 // Use a special rubberband for views that have only one column and
709 // expand the rubberband to use the whole width of the view.
710 endPos.setX(m_view->size().width());
711 }
712 } else {
713 endPos.rx() += m_view->scrollOffset();
714 }
715 rubberBand->setEndPosition(endPos);
716 }
717 }
718
719 return false;
720 }
721
722 bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
723 {
724 if (!m_view) {
725 return false;
726 }
727
728 emit mouseButtonReleased(m_pressedIndex, event->buttons());
729
730 const bool isAboveSelectionToggle = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos);
731 if (isAboveSelectionToggle) {
732 m_selectionTogglePressed = false;
733 return true;
734 }
735
736 if (!isAboveSelectionToggle && m_selectionTogglePressed) {
737 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
738 m_selectionTogglePressed = false;
739 return true;
740 }
741
742 const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier ||
743 event->modifiers() & Qt::ControlModifier;
744
745 KItemListRubberBand* rubberBand = m_view->rubberBand();
746 if (rubberBand->isActive()) {
747 disconnect(rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListController::slotRubberBandChanged);
748 rubberBand->setActive(false);
749 m_oldSelection.clear();
750 m_view->setAutoScroll(false);
751 }
752
753 const QPointF pos = transform.map(event->pos());
754 const int index = m_view->itemAt(pos);
755
756 if (index >= 0 && index == m_pressedIndex) {
757 // The release event is done above the same item as the press event
758
759 if (m_clearSelectionIfItemsAreNotDragged) {
760 // A selected item has been clicked, but no drag operation has been started
761 // -> clear the rest of the selection.
762 m_selectionManager->clearSelection();
763 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select);
764 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
765 }
766
767 if (event->button() & Qt::LeftButton) {
768 bool emitItemActivated = true;
769 if (m_view->isAboveExpansionToggle(index, pos)) {
770 const bool expanded = m_model->isExpanded(index);
771 m_model->setExpanded(index, !expanded);
772
773 emit itemExpansionToggleClicked(index);
774 emitItemActivated = false;
775 } else if (shiftOrControlPressed) {
776 // The mouse click should only update the selection, not trigger the item
777 emitItemActivated = false;
778 } else if (!(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced)) {
779 emitItemActivated = false;
780 }
781 if (emitItemActivated) {
782 emit itemActivated(index);
783 }
784 } else if (event->button() & Qt::MidButton) {
785 emit itemMiddleClicked(index);
786 }
787 }
788
789 m_pressedMousePos = QPointF();
790 m_pressedIndex = -1;
791 m_clearSelectionIfItemsAreNotDragged = false;
792 return false;
793 }
794
795 bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
796 {
797 const QPointF pos = transform.map(event->pos());
798 const int index = m_view->itemAt(pos);
799
800 // Expand item if desired - See Bug 295573
801 if (m_mouseDoubleClickAction != ActivateItemOnly) {
802 if (m_view && m_model && m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
803 const bool expanded = m_model->isExpanded(index);
804 m_model->setExpanded(index, !expanded);
805 }
806 }
807
808 if (event->button() & Qt::RightButton) {
809 m_selectionManager->clearSelection();
810 if (index >= 0) {
811 m_selectionManager->setSelected(index);
812 emit itemContextMenuRequested(index, event->screenPos());
813 } else {
814 const QRectF headerBounds = m_view->headerBoundaries();
815 if (headerBounds.contains(event->pos())) {
816 emit headerContextMenuRequested(event->screenPos());
817 } else {
818 emit viewContextMenuRequested(event->screenPos());
819 }
820 }
821 return true;
822 }
823
824 bool emitItemActivated = !(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced) &&
825 (event->button() & Qt::LeftButton) &&
826 index >= 0 && index < m_model->count();
827 if (emitItemActivated) {
828 emit itemActivated(index);
829 }
830 return false;
831 }
832
833 bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
834 {
835 Q_UNUSED(event);
836 Q_UNUSED(transform);
837 return false;
838 }
839
840 bool KItemListController::dragLeaveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
841 {
842 Q_UNUSED(event);
843 Q_UNUSED(transform);
844
845 m_view->setAutoScroll(false);
846 m_view->hideDropIndicator();
847
848 KItemListWidget* widget = hoveredWidget();
849 if (widget) {
850 widget->setHovered(false);
851 emit itemUnhovered(widget->index());
852 }
853 return false;
854 }
855
856 bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
857 {
858 if (!m_model || !m_view) {
859 return false;
860 }
861
862 event->acceptProposedAction();
863
864 KItemListWidget* oldHoveredWidget = hoveredWidget();
865
866 const QPointF pos = transform.map(event->pos());
867 KItemListWidget* newHoveredWidget = widgetForPos(pos);
868
869 if (oldHoveredWidget != newHoveredWidget) {
870 m_autoActivationTimer->stop();
871
872 if (oldHoveredWidget) {
873 oldHoveredWidget->setHovered(false);
874 emit itemUnhovered(oldHoveredWidget->index());
875 }
876 }
877
878 if (newHoveredWidget) {
879 bool droppingBetweenItems = false;
880 if (m_model->sortRole().isEmpty()) {
881 // The model supports inserting items between other items.
882 droppingBetweenItems = (m_view->showDropIndicator(pos) >= 0);
883 }
884
885 const int index = newHoveredWidget->index();
886 if (!droppingBetweenItems) {
887 if (m_model->supportsDropping(index)) {
888 // Something has been dragged on an item.
889 m_view->hideDropIndicator();
890 if (!newHoveredWidget->isHovered()) {
891 newHoveredWidget->setHovered(true);
892 emit itemHovered(index);
893 }
894
895 if (!m_autoActivationTimer->isActive() && m_autoActivationTimer->interval() >= 0) {
896 m_autoActivationTimer->setProperty("index", index);
897 m_autoActivationTimer->start();
898 }
899 }
900 } else {
901 m_autoActivationTimer->stop();
902 if (newHoveredWidget && newHoveredWidget->isHovered()) {
903 newHoveredWidget->setHovered(false);
904 emit itemUnhovered(index);
905 }
906 }
907 } else {
908 m_view->hideDropIndicator();
909 }
910
911 return false;
912 }
913
914 bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
915 {
916 if (!m_view) {
917 return false;
918 }
919
920 m_autoActivationTimer->stop();
921 m_view->setAutoScroll(false);
922
923 const QPointF pos = transform.map(event->pos());
924
925 int dropAboveIndex = -1;
926 if (m_model->sortRole().isEmpty()) {
927 // The model supports inserting of items between other items.
928 dropAboveIndex = m_view->showDropIndicator(pos);
929 }
930
931 if (dropAboveIndex >= 0) {
932 // Something has been dropped between two items.
933 m_view->hideDropIndicator();
934 emit aboveItemDropEvent(dropAboveIndex, event);
935 } else if (!event->mimeData()->hasFormat(m_model->blacklistItemDropEventMimeType())) {
936 // Something has been dropped on an item or on an empty part of the view.
937 emit itemDropEvent(m_view->itemAt(pos), event);
938 }
939
940 QAccessibleEvent accessibilityEvent(view(), QAccessible::DragDropEnd);
941 QAccessible::updateAccessibility(&accessibilityEvent);
942
943 return true;
944 }
945
946 bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
947 {
948 Q_UNUSED(event);
949 Q_UNUSED(transform);
950 return false;
951 }
952
953 bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
954 {
955 Q_UNUSED(transform);
956 if (!m_model || !m_view) {
957 return false;
958 }
959
960 KItemListWidget* oldHoveredWidget = hoveredWidget();
961 const QPointF pos = transform.map(event->pos());
962 KItemListWidget* newHoveredWidget = widgetForPos(pos);
963
964 if (oldHoveredWidget != newHoveredWidget) {
965 if (oldHoveredWidget) {
966 oldHoveredWidget->setHovered(false);
967 emit itemUnhovered(oldHoveredWidget->index());
968 }
969
970 if (newHoveredWidget) {
971 newHoveredWidget->setHovered(true);
972 const QPointF mappedPos = newHoveredWidget->mapFromItem(m_view, pos);
973 newHoveredWidget->setHoverPosition(mappedPos);
974 emit itemHovered(newHoveredWidget->index());
975 }
976 } else if (oldHoveredWidget) {
977 const QPointF mappedPos = oldHoveredWidget->mapFromItem(m_view, pos);
978 oldHoveredWidget->setHoverPosition(mappedPos);
979 }
980
981 return false;
982 }
983
984 bool KItemListController::hoverLeaveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
985 {
986 Q_UNUSED(event);
987 Q_UNUSED(transform);
988
989 if (!m_model || !m_view) {
990 return false;
991 }
992
993 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
994 if (widget->isHovered()) {
995 widget->setHovered(false);
996 emit itemUnhovered(widget->index());
997 }
998 }
999 return false;
1000 }
1001
1002 bool KItemListController::wheelEvent(QGraphicsSceneWheelEvent* event, const QTransform& transform)
1003 {
1004 Q_UNUSED(event);
1005 Q_UNUSED(transform);
1006 return false;
1007 }
1008
1009 bool KItemListController::resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform)
1010 {
1011 Q_UNUSED(event);
1012 Q_UNUSED(transform);
1013 return false;
1014 }
1015
1016 bool KItemListController::processEvent(QEvent* event, const QTransform& transform)
1017 {
1018 if (!event) {
1019 return false;
1020 }
1021
1022 switch (event->type()) {
1023 case QEvent::KeyPress:
1024 return keyPressEvent(static_cast<QKeyEvent*>(event));
1025 case QEvent::InputMethod:
1026 return inputMethodEvent(static_cast<QInputMethodEvent*>(event));
1027 case QEvent::GraphicsSceneMousePress:
1028 return mousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
1029 case QEvent::GraphicsSceneMouseMove:
1030 return mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
1031 case QEvent::GraphicsSceneMouseRelease:
1032 return mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
1033 case QEvent::GraphicsSceneMouseDoubleClick:
1034 return mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
1035 case QEvent::GraphicsSceneWheel:
1036 return wheelEvent(static_cast<QGraphicsSceneWheelEvent*>(event), QTransform());
1037 case QEvent::GraphicsSceneDragEnter:
1038 return dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
1039 case QEvent::GraphicsSceneDragLeave:
1040 return dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
1041 case QEvent::GraphicsSceneDragMove:
1042 return dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
1043 case QEvent::GraphicsSceneDrop:
1044 return dropEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
1045 case QEvent::GraphicsSceneHoverEnter:
1046 return hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
1047 case QEvent::GraphicsSceneHoverMove:
1048 return hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
1049 case QEvent::GraphicsSceneHoverLeave:
1050 return hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
1051 case QEvent::GraphicsSceneResize:
1052 return resizeEvent(static_cast<QGraphicsSceneResizeEvent*>(event), transform);
1053 default:
1054 break;
1055 }
1056
1057 return false;
1058 }
1059
1060 void KItemListController::slotViewScrollOffsetChanged(qreal current, qreal previous)
1061 {
1062 if (!m_view) {
1063 return;
1064 }
1065
1066 KItemListRubberBand* rubberBand = m_view->rubberBand();
1067 if (rubberBand->isActive()) {
1068 const qreal diff = current - previous;
1069 // TODO: Ideally just QCursor::pos() should be used as
1070 // new end-position but it seems there is no easy way
1071 // to have something like QWidget::mapFromGlobal() for QGraphicsWidget
1072 // (... or I just missed an easy way to do the mapping)
1073 QPointF endPos = rubberBand->endPosition();
1074 if (m_view->scrollOrientation() == Qt::Vertical) {
1075 endPos.ry() += diff;
1076 } else {
1077 endPos.rx() += diff;
1078 }
1079
1080 rubberBand->setEndPosition(endPos);
1081 }
1082 }
1083
1084 void KItemListController::slotRubberBandChanged()
1085 {
1086 if (!m_view || !m_model || m_model->count() <= 0) {
1087 return;
1088 }
1089
1090 const KItemListRubberBand* rubberBand = m_view->rubberBand();
1091 const QPointF startPos = rubberBand->startPosition();
1092 const QPointF endPos = rubberBand->endPosition();
1093 QRectF rubberBandRect = QRectF(startPos, endPos).normalized();
1094
1095 const bool scrollVertical = (m_view->scrollOrientation() == Qt::Vertical);
1096 if (scrollVertical) {
1097 rubberBandRect.translate(0, -m_view->scrollOffset());
1098 } else {
1099 rubberBandRect.translate(-m_view->scrollOffset(), 0);
1100 }
1101
1102 if (!m_oldSelection.isEmpty()) {
1103 // Clear the old selection that was available before the rubberband has
1104 // been activated in case if no Shift- or Control-key are pressed
1105 const bool shiftOrControlPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier ||
1106 QApplication::keyboardModifiers() & Qt::ControlModifier;
1107 if (!shiftOrControlPressed) {
1108 m_oldSelection.clear();
1109 }
1110 }
1111
1112 KItemSet selectedItems;
1113
1114 // Select all visible items that intersect with the rubberband
1115 foreach (const KItemListWidget* widget, m_view->visibleItemListWidgets()) {
1116 const int index = widget->index();
1117
1118 const QRectF widgetRect = m_view->itemRect(index);
1119 if (widgetRect.intersects(rubberBandRect)) {
1120 const QRectF iconRect = widget->iconRect().translated(widgetRect.topLeft());
1121 const QRectF textRect = widget->textRect().translated(widgetRect.topLeft());
1122 if (iconRect.intersects(rubberBandRect) || textRect.intersects(rubberBandRect)) {
1123 selectedItems.insert(index);
1124 }
1125 }
1126 }
1127
1128 // Select all invisible items that intersect with the rubberband. Instead of
1129 // iterating all items only the area which might be touched by the rubberband
1130 // will be checked.
1131 const bool increaseIndex = scrollVertical ?
1132 startPos.y() > endPos.y(): startPos.x() > endPos.x();
1133
1134 int index = increaseIndex ? m_view->lastVisibleIndex() + 1 : m_view->firstVisibleIndex() - 1;
1135 bool selectionFinished = false;
1136 do {
1137 const QRectF widgetRect = m_view->itemRect(index);
1138 if (widgetRect.intersects(rubberBandRect)) {
1139 selectedItems.insert(index);
1140 }
1141
1142 if (increaseIndex) {
1143 ++index;
1144 selectionFinished = (index >= m_model->count()) ||
1145 ( scrollVertical && widgetRect.top() > rubberBandRect.bottom()) ||
1146 (!scrollVertical && widgetRect.left() > rubberBandRect.right());
1147 } else {
1148 --index;
1149 selectionFinished = (index < 0) ||
1150 ( scrollVertical && widgetRect.bottom() < rubberBandRect.top()) ||
1151 (!scrollVertical && widgetRect.right() < rubberBandRect.left());
1152 }
1153 } while (!selectionFinished);
1154
1155 if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
1156 // If Control is pressed, the selection state of all items in the rubberband is toggled.
1157 // Therefore, the new selection contains:
1158 // 1. All previously selected items which are not inside the rubberband, and
1159 // 2. all items inside the rubberband which have not been selected previously.
1160 m_selectionManager->setSelectedItems(m_oldSelection ^ selectedItems);
1161 }
1162 else {
1163 m_selectionManager->setSelectedItems(selectedItems + m_oldSelection);
1164 }
1165 }
1166
1167 void KItemListController::startDragging()
1168 {
1169 if (!m_view || !m_model) {
1170 return;
1171 }
1172
1173 const KItemSet selectedItems = m_selectionManager->selectedItems();
1174 if (selectedItems.isEmpty()) {
1175 return;
1176 }
1177
1178 QMimeData* data = m_model->createMimeData(selectedItems);
1179 if (!data) {
1180 return;
1181 }
1182
1183 // The created drag object will be owned and deleted
1184 // by QApplication::activeWindow().
1185 QDrag* drag = new QDrag(QApplication::activeWindow());
1186 drag->setMimeData(data);
1187
1188 const QPixmap pixmap = m_view->createDragPixmap(selectedItems);
1189 drag->setPixmap(pixmap);
1190
1191 const QPoint hotSpot((pixmap.width() / pixmap.devicePixelRatio()) / 2, 0);
1192 drag->setHotSpot(hotSpot);
1193
1194 drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::CopyAction);
1195
1196 QAccessibleEvent accessibilityEvent(view(), QAccessible::DragDropStart);
1197 QAccessible::updateAccessibility(&accessibilityEvent);
1198 }
1199
1200 KItemListWidget* KItemListController::hoveredWidget() const
1201 {
1202 Q_ASSERT(m_view);
1203
1204 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
1205 if (widget->isHovered()) {
1206 return widget;
1207 }
1208 }
1209
1210 return 0;
1211 }
1212
1213 KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const
1214 {
1215 Q_ASSERT(m_view);
1216
1217 foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) {
1218 const QPointF mappedPos = widget->mapFromItem(m_view, pos);
1219
1220 const bool hovered = widget->contains(mappedPos) &&
1221 !widget->expansionToggleRect().contains(mappedPos);
1222 if (hovered) {
1223 return widget;
1224 }
1225 }
1226
1227 return 0;
1228 }
1229
1230 void KItemListController::updateKeyboardAnchor()
1231 {
1232 const bool validAnchor = m_keyboardAnchorIndex >= 0 &&
1233 m_keyboardAnchorIndex < m_model->count() &&
1234 keyboardAnchorPos(m_keyboardAnchorIndex) == m_keyboardAnchorPos;
1235 if (!validAnchor) {
1236 const int index = m_selectionManager->currentItem();
1237 m_keyboardAnchorIndex = index;
1238 m_keyboardAnchorPos = keyboardAnchorPos(index);
1239 }
1240 }
1241
1242 int KItemListController::nextRowIndex(int index) const
1243 {
1244 if (m_keyboardAnchorIndex < 0) {
1245 return index;
1246 }
1247
1248 const int maxIndex = m_model->count() - 1;
1249 if (index == maxIndex) {
1250 return index;
1251 }
1252
1253 // Calculate the index of the last column inside the row of the current index
1254 int lastColumnIndex = index;
1255 while (keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) {
1256 ++lastColumnIndex;
1257 if (lastColumnIndex >= maxIndex) {
1258 return index;
1259 }
1260 }
1261
1262 // Based on the last column index go to the next row and calculate the nearest index
1263 // that is below the current index
1264 int nextRowIndex = lastColumnIndex + 1;
1265 int searchIndex = nextRowIndex;
1266 qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(nextRowIndex));
1267 while (searchIndex < maxIndex && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex)) {
1268 ++searchIndex;
1269 const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
1270 if (searchDiff < minDiff) {
1271 minDiff = searchDiff;
1272 nextRowIndex = searchIndex;
1273 }
1274 }
1275
1276 return nextRowIndex;
1277 }
1278
1279 int KItemListController::previousRowIndex(int index) const
1280 {
1281 if (m_keyboardAnchorIndex < 0 || index == 0) {
1282 return index;
1283 }
1284
1285 // Calculate the index of the first column inside the row of the current index
1286 int firstColumnIndex = index;
1287 while (keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) {
1288 --firstColumnIndex;
1289 if (firstColumnIndex <= 0) {
1290 return index;
1291 }
1292 }
1293
1294 // Based on the first column index go to the previous row and calculate the nearest index
1295 // that is above the current index
1296 int previousRowIndex = firstColumnIndex - 1;
1297 int searchIndex = previousRowIndex;
1298 qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(previousRowIndex));
1299 while (searchIndex > 0 && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex)) {
1300 --searchIndex;
1301 const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex));
1302 if (searchDiff < minDiff) {
1303 minDiff = searchDiff;
1304 previousRowIndex = searchIndex;
1305 }
1306 }
1307
1308 return previousRowIndex;
1309 }
1310
1311 qreal KItemListController::keyboardAnchorPos(int index) const
1312 {
1313 const QRectF itemRect = m_view->itemRect(index);
1314 if (!itemRect.isEmpty()) {
1315 return (m_view->scrollOrientation() == Qt::Vertical) ? itemRect.x() : itemRect.y();
1316 }
1317
1318 return 0;
1319 }
1320
1321 void KItemListController::updateExtendedSelectionRegion()
1322 {
1323 if (m_view) {
1324 const bool extend = (m_selectionBehavior != MultiSelection);
1325 KItemListStyleOption option = m_view->styleOption();
1326 if (option.extendedSelectionRegion != extend) {
1327 option.extendedSelectionRegion = extend;
1328 m_view->setStyleOption(option);
1329 }
1330 }
1331 }
1332