+
+void ChatScene::updateSelection(const QPointF &pos)
+{
+ int curRow = rowByScenePos(pos);
+ if (curRow < 0) return;
+ int curColumn = (int)columnByScenePos(pos);
+ ChatLineModel::ColumnType minColumn = (ChatLineModel::ColumnType)qMin(curColumn, _selectionStartCol);
+ if (minColumn != _selectionMinCol) {
+ _selectionMinCol = minColumn;
+ for (int l = qMin(_selectionStart, _selectionEnd); l <= qMax(_selectionStart, _selectionEnd); l++) {
+ _lines[l]->setSelected(true, minColumn);
+ }
+ }
+ int newstart = qMin(curRow, _firstSelectionRow);
+ int newend = qMax(curRow, _firstSelectionRow);
+ if (newstart < _selectionStart) {
+ for (int l = newstart; l < _selectionStart; l++)
+ _lines[l]->setSelected(true, minColumn);
+ }
+ if (newstart > _selectionStart) {
+ for (int l = _selectionStart; l < newstart; l++)
+ _lines[l]->setSelected(false);
+ }
+ if (newend > _selectionEnd) {
+ for (int l = _selectionEnd+1; l <= newend; l++)
+ _lines[l]->setSelected(true, minColumn);
+ }
+ if (newend < _selectionEnd) {
+ for (int l = newend+1; l <= _selectionEnd; l++)
+ _lines[l]->setSelected(false);
+ }
+
+ _selectionStart = newstart;
+ _selectionEnd = newend;
+
+ if (newstart == newend && minColumn == ChatLineModel::ContentsColumn) {
+ if (!_selectingItem) {
+ // _selectingItem has been removed already
+ return;
+ }
+ _lines[curRow]->setSelected(false);
+ _isSelecting = false;
+ _selectionStart = -1;
+ _selectingItem->continueSelecting(_selectingItem->mapFromScene(pos));
+ }
+}
+
+
+bool ChatScene::isPosOverSelection(const QPointF &pos) const
+{
+ ChatItem *chatItem = chatItemAt(pos);
+ if (!chatItem)
+ return false;
+ if (hasGlobalSelection()) {
+ int row = chatItem->row();
+ if (row >= qMin(_selectionStart, _selectionEnd) && row <= qMax(_selectionStart, _selectionEnd))
+ return columnByScenePos(pos) >= _selectionMinCol;
+ }
+ else {
+ return chatItem->isPosOverSelection(chatItem->mapFromScene(pos));
+ }
+ return false;
+}
+
+
+bool ChatScene::isScrollingAllowed() const
+{
+ if (_isSelecting)
+ return false;
+
+ // TODO: Handle clicks and single-item selections too
+
+ return true;
+}
+
+
+/******** MOUSE HANDLING **************************************************************************/
+
+void ChatScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
+{
+ QPointF pos = event->scenePos();
+ QMenu menu;
+
+ // zoom actions and similar
+ chatView()->addActionsToMenu(&menu, pos);
+ menu.addSeparator();
+
+ // item-specific options (select link etc)
+ ChatItem *item = chatItemAt(pos);
+ if (item)
+ item->addActionsToMenu(&menu, item->mapFromScene(pos));
+ else
+ // no item -> default scene actions
+ GraphicalUi::contextMenuActionProvider()->addActions(&menu, filter(), BufferId());
+
+ // If we have text selected, insert the Copy Selection as first item
+ if (isPosOverSelection(pos)) {
+ QAction *sep = menu.insertSeparator(menu.actions().first());
+ QAction *act = new Action(QIcon::fromTheme("edit-copy"), tr("Copy Selection"), &menu, this,
+ SLOT(selectionToClipboard()), QKeySequence::Copy);
+ menu.insertAction(sep, act);
+
+ QString searchSelectionText = selection();
+ if (searchSelectionText.length() > _webSearchSelectionTextMaxVisible)
+ searchSelectionText = searchSelectionText.left(_webSearchSelectionTextMaxVisible).append(QString::fromUtf8("…"));
+ searchSelectionText = tr("Search '%1'").arg(searchSelectionText);
+
+ QAction *webSearchAction = new Action(QIcon::fromTheme("edit-find"), searchSelectionText, &menu, this, SLOT(webSearchOnSelection()));
+ menu.insertAction(sep, webSearchAction);
+ }
+
+ if (QtUi::mainWindow()->menuBar()->isHidden())
+ menu.addAction(QtUi::actionCollection("General")->action("ToggleMenuBar"));
+
+ // show column reset action if columns have been resized in this session or there is at least one very narrow column
+ if ((_firstColHandlePos != _defaultFirstColHandlePos) || (_secondColHandlePos != _defaultSecondColHandlePos) ||
+ (_firstColHandlePos <= 10) || (_secondColHandlePos - _firstColHandlePos <= 10))
+ menu.addAction(new Action(tr("Reset Column Widths"), &menu, this, SLOT(resetColumnWidths()), 0));
+
+ menu.exec(event->screenPos());
+}
+
+
+void ChatScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (event->buttons() == Qt::LeftButton) {
+ if (!_clickHandled && (event->scenePos() - _clickPos).toPoint().manhattanLength() >= QApplication::startDragDistance()) {
+ if (_clickTimer.isActive())
+ _clickTimer.stop();
+ if (_clickMode == SingleClick && isPosOverSelection(_clickPos))
+ initiateDrag(event->widget());
+ else {
+ _clickMode = DragStartClick;
+ handleClick(Qt::LeftButton, _clickPos);
+ }
+ _clickMode = NoClick;
+ }
+ if (_isSelecting) {
+ updateSelection(event->scenePos());
+ emit mouseMoveWhileSelecting(event->scenePos());
+ event->accept();
+ }
+ else if (_clickHandled && _clickMode < DoubleClick)
+ QGraphicsScene::mouseMoveEvent(event);
+ }
+ else
+ QGraphicsScene::mouseMoveEvent(event);
+}
+
+
+void ChatScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (event->buttons() == Qt::LeftButton) {
+ _leftButtonPressed = true;
+ _clickHandled = false;
+ if (!isPosOverSelection(event->scenePos())) {
+ // immediately clear selection if clicked outside; otherwise, wait for potential drag
+ clearSelection();
+ }
+ if (_clickMode != NoClick && _clickTimer.isActive()) {
+ switch (_clickMode) {
+ case NoClick:
+ _clickMode = SingleClick; break;
+ case SingleClick:
+ _clickMode = DoubleClick; break;
+ case DoubleClick:
+ _clickMode = TripleClick; break;
+ case TripleClick:
+ _clickMode = DoubleClick; break;
+ case DragStartClick:
+ break;
+ }
+ handleClick(Qt::LeftButton, _clickPos);
+ }
+ else {
+ _clickMode = SingleClick;
+ _clickPos = event->scenePos();
+ }
+ _clickTimer.start();
+ }
+ if (event->type() == QEvent::GraphicsSceneMouseDoubleClick)
+ QGraphicsScene::mouseDoubleClickEvent(event);
+ else
+ QGraphicsScene::mousePressEvent(event);
+}
+
+
+void ChatScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ // we check for doubleclick ourselves, so just call press handler
+ mousePressEvent(event);