-void ChatScene::setHandleXLimits() {
- _firstColHandle->setXLimits(0, _secondColHandle->sceneLeft());
- _secondColHandle->setXLimits(_firstColHandle->sceneRight(), width() - minContentsWidth);
-}
-
-void ChatScene::setSelectingItem(ChatItem *item) {
- if(_selectingItem) _selectingItem->clearSelection();
- _selectingItem = item;
-}
-
-void ChatScene::startGlobalSelection(ChatItem *item, const QPointF &itemPos) {
- _selectionStart = _selectionEnd = _firstSelectionRow = item->row();
- _selectionStartCol = _selectionMinCol = item->column();
- _isSelecting = true;
- _lines[_selectionStart]->setSelected(true, (ChatLineModel::ColumnType)_selectionMinCol);
- updateSelection(item->mapToScene(itemPos));
-}
-
-void ChatScene::updateSelection(const QPointF &pos) {
- // This is somewhat hacky... we look at the contents item that is at the cursor's y position (ignoring x), since
- // it has the full height. From this item, we can then determine the row index and hence the ChatLine.
- ChatItem *contentItem = static_cast<ChatItem *>(itemAt(QPointF(_secondColHandle->sceneRight() + 1, pos.y())));
- if(!contentItem) return;
-
- int curRow = contentItem->row();
- int curColumn;
- if(pos.x() > _secondColHandle->sceneRight()) curColumn = ChatLineModel::ContentsColumn;
- else if(pos.x() > _firstColHandlePos) curColumn = ChatLineModel::SenderColumn;
- else curColumn = ChatLineModel::TimestampColumn;
-
- 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;
- _selectingItem->continueSelecting(_selectingItem->mapFromScene(pos));
- }
-}
-
-void ChatScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
- if(_isSelecting && event->buttons() == Qt::LeftButton) {
- updateSelection(event->scenePos());
- emit mouseMoveWhileSelecting(event->scenePos());
- event->accept();
- } else {
- QGraphicsScene::mouseMoveEvent(event);
- }
-}
-
-void ChatScene::mousePressEvent(QGraphicsSceneMouseEvent *event) {
- if(_selectionStart >= 0 && event->buttons() == Qt::LeftButton) {
- for(int l = qMin(_selectionStart, _selectionEnd); l <= qMax(_selectionStart, _selectionEnd); l++) {
- _lines[l]->setSelected(false);
- }
- _isSelecting = false;
- _selectionStart = -1;
- QGraphicsScene::mousePressEvent(event); // so we can start a new local selection
- } else {
- QGraphicsScene::mousePressEvent(event);
- }
-}
-
-void ChatScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
- if(_isSelecting && !event->buttons() & Qt::LeftButton) {
- putToClipboard(selectionToString());
- _isSelecting = false;
- event->accept();
- } else {
+
+void ChatScene::secondHandlePositionChanged(qreal xpos)
+{
+ if (_secondColHandlePos == xpos)
+ return;
+
+ _secondColHandlePos = xpos;
+ ChatViewSettings viewSettings(this);
+ viewSettings.setValue("SecondColumnHandlePos", _secondColHandlePos);
+ ChatViewSettings defaultSettings;
+ defaultSettings.setValue("SecondColumnHandlePos", _secondColHandlePos);
+
+ // clock_t startT = clock();
+
+ // disabling the index while doing this complex updates is about
+ // 2 to 10 times faster!
+ //setItemIndexMethod(QGraphicsScene::NoIndex);
+
+ QList<ChatLine *>::iterator lineIter = _lines.end();
+ QList<ChatLine *>::iterator lineIterBegin = _lines.begin();
+ qreal linePos = _sceneRect.y() + _sceneRect.height();
+ qreal senderWidth = secondColumnHandle()->sceneLeft() - firstColumnHandle()->sceneRight();
+ qreal contentsWidth = _sceneRect.width() - secondColumnHandle()->sceneRight();
+ QPointF contentsPos(secondColumnHandle()->sceneRight(), 0);
+ while (lineIter != lineIterBegin) {
+ lineIter--;
+ (*lineIter)->setSecondColumn(senderWidth, contentsWidth, contentsPos, linePos);
+ }
+ //setItemIndexMethod(QGraphicsScene::BspTreeIndex);
+
+ updateSceneRect();
+ setHandleXLimits();
+ emit layoutChanged();
+
+// clock_t endT = clock();
+// qDebug() << "resized" << _lines.count() << "in" << (float)(endT - startT) / CLOCKS_PER_SEC << "sec";
+}
+
+
+void ChatScene::setHandleXLimits()
+{
+ _firstColHandle->setXLimits(0, _secondColHandle->sceneLeft());
+ _secondColHandle->setXLimits(_firstColHandle->sceneRight(), width() - minContentsWidth);
+ update();
+}
+
+
+void ChatScene::setSelectingItem(ChatItem *item)
+{
+ if (_selectingItem) _selectingItem->clearSelection();
+ _selectingItem = item;
+}
+
+
+void ChatScene::startGlobalSelection(ChatItem *item, const QPointF &itemPos)
+{
+ _selectionStart = _selectionEnd = _firstSelectionRow = item->row();
+ _selectionStartCol = _selectionMinCol = item->column();
+ _isSelecting = true;
+ _lines[_selectionStart]->setSelected(true, (ChatLineModel::ColumnType)_selectionMinCol);
+ updateSelection(item->mapToScene(itemPos));
+}
+
+
+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();
+
+ if (isPosOverSelection(pos))
+ menu.addAction(SmallIcon("edit-copy"), tr("Copy Selection"),
+ this, SLOT(selectionToClipboard()),
+ QKeySequence::Copy);
+
+ // 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 (QtUi::mainWindow()->menuBar()->isHidden())
+ menu.addAction(QtUi::actionCollection("General")->action("ToggleMenuBar"));
+
+ 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);
+}
+
+
+void ChatScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton && _leftButtonPressed) {
+ _leftButtonPressed = false;
+ if (_clickMode != NoClick) {
+ if (_clickMode == SingleClick)
+ clearSelection();
+ event->accept();
+ if (!_clickTimer.isActive())
+ handleClick(Qt::LeftButton, _clickPos);
+ }
+ else {
+ // no click -> drag or selection move
+ if (isGloballySelecting()) {
+ selectionToClipboard(QClipboard::Selection);
+ _isSelecting = false;
+ event->accept();
+ return;
+ }
+ }
+ }