Add shortcut that jumps to the markerline
authorManuel Nickschas <sputnick@quassel-irc.org>
Tue, 29 Jun 2010 20:51:13 +0000 (22:51 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Tue, 29 Jun 2010 20:51:13 +0000 (22:51 +0200)
By default (and subject to change), Ctrl+K now jumps to the markerline
(which is either set manually by Ctrl+R, or by default automatically when
switching channels).

If necessary, additional backlog will be fetched first. Note that this might require
multiple presses of Ctrl+K if you tinkered with message filters after setting the markerline,
and also for markerlines that have been set using older clients.

The markerline will now never be set on a day change message.

src/qtui/bufferwidget.cpp
src/qtui/bufferwidget.h
src/qtui/chatline.h
src/qtui/chatscene.cpp
src/qtui/chatscene.h
src/qtui/chatview.cpp
src/qtui/chatview.h
src/qtui/markerlineitem.cpp
src/qtui/markerlineitem.h

index 047759e..7b4e954 100644 (file)
@@ -94,6 +94,10 @@ BufferWidget::BufferWidget(QWidget *parent)
   setMarkerLine->setText(tr("Set Marker Line"));
   setMarkerLine->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
 
   setMarkerLine->setText(tr("Set Marker Line"));
   setMarkerLine->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
 
+  Action *jumpToMarkerLine = QtUi::actionCollection("Navigation")->add<Action>("JumpToMarkerLine", this, SLOT(jumpToMarkerLine()));
+  jumpToMarkerLine->setText(tr("Go to Marker Line"));
+  jumpToMarkerLine->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K));
+
   ChatViewSettings s;
   s.initAndNotify("AutoMarkerLine", this, SLOT(setAutoMarkerLine(QVariant)), true);
 }
   ChatViewSettings s;
   s.initAndNotify("AutoMarkerLine", this, SLOT(setAutoMarkerLine(QVariant)), true);
 }
@@ -250,3 +254,12 @@ void BufferWidget::setMarkerLine(ChatView *view, bool allowGoingBack) {
     view->setMarkerLine(msgId);
   }
 }
     view->setMarkerLine(msgId);
   }
 }
+
+void BufferWidget::jumpToMarkerLine(ChatView *view, bool requestBacklog) {
+  if(!view)
+    view = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
+  if(!view)
+    return;
+
+  view->jumpToMarkerLine(requestBacklog);
+}
index b818435..4eb8b6d 100644 (file)
@@ -44,6 +44,7 @@ public:
 
 public slots:
   virtual void setMarkerLine(ChatView *view = 0, bool allowGoingBack = true);
 
 public slots:
   virtual void setMarkerLine(ChatView *view = 0, bool allowGoingBack = true);
+  virtual void jumpToMarkerLine(ChatView *view = 0, bool requestBacklog = true);
 
 protected:
   virtual AbstractChatView *createChatView(BufferId);
 
 protected:
   virtual AbstractChatView *createChatView(BufferId);
index 4536520..9498156 100644 (file)
@@ -41,6 +41,8 @@ public:
 
   inline QModelIndex index() const { return model()->index(row(), 0); }
   inline MsgId msgId() const { return index().data(MessageModel::MsgIdRole).value<MsgId>(); }
 
   inline QModelIndex index() const { return model()->index(row(), 0); }
   inline MsgId msgId() const { return index().data(MessageModel::MsgIdRole).value<MsgId>(); }
+  inline Message::Type msgType() const { return (Message::Type)index().data(MessageModel::TypeRole).toInt(); }
+
   inline int row() const { return _row; }
   inline void setRow(int row) { _row = row; }
 
   inline int row() const { return _row; }
   inline void setRow(int row) { _row = row; }
 
index f7d4bd1..3ab059a 100644 (file)
@@ -66,8 +66,9 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, qreal w
     _firstLineRow(-1),
     _viewportHeight(0),
     _markerLine(new MarkerLineItem(width)),
     _firstLineRow(-1),
     _viewportHeight(0),
     _markerLine(new MarkerLineItem(width)),
-    _markerLineValid(false),
     _markerLineVisible(false),
     _markerLineVisible(false),
+    _markerLineValid(false),
+    _markerLineJumpPending(false),
     _cutoffMode(CutoffRight),
     _selectingItem(0),
     _selectionStart(-1),
     _cutoffMode(CutoffRight),
     _selectingItem(0),
     _selectionStart(-1),
@@ -147,7 +148,7 @@ ColumnHandleItem *ChatScene::secondColumnHandle() const {
   return _secondColHandle;
 }
 
   return _secondColHandle;
 }
 
-ChatLine *ChatScene::chatLine(MsgId msgId, bool matchExact) const {
+ChatLine *ChatScene::chatLine(MsgId msgId, bool matchExact, bool ignoreDayChange) const {
   if(!_lines.count())
     return 0;
 
   if(!_lines.count())
     return 0;
 
@@ -169,21 +170,37 @@ ChatLine *ChatScene::chatLine(MsgId msgId, bool matchExact) const {
     }
   }
 
     }
   }
 
-  if(start != end && (*start)->msgId() == msgId)
+  if(start != end && (*start)->msgId() == msgId && (ignoreDayChange? (*start)->msgType() != Message::DayChange : true))
     return *start;
 
   if(matchExact)
     return 0;
 
     return *start;
 
   if(matchExact)
     return 0;
 
+  if(start == _lines.begin()) // not (yet?) in our scene
+    return 0;
+
   // if we didn't find the exact msgId, take the next-lower one (this makes sense for lastSeen)
   // if we didn't find the exact msgId, take the next-lower one (this makes sense for lastSeen)
-  if(start == end) // higher than last element
-    return _lines.last();
 
 
-  if(start == _lines.begin()) // not (yet?) in our scene
+  if(start == end) { // higher than last element
+    if(!ignoreDayChange)
+      return _lines.last();
+
+    for(int i = _lines.count() -1; i >= 0; i--) {
+      if(_lines.at(i)->msgType() != Message::DayChange)
+        return _lines.at(i);
+    }
     return 0;
     return 0;
+  }
 
   // return the next-lower line
 
   // return the next-lower line
-  return *(--start);
+  if(!ignoreDayChange)
+    return *(--start);
+
+  do {
+    if((*(--start))->msgType() != Message::DayChange)
+      return *start;
+  } while(start != _lines.begin());
+  return 0;
 }
 
 ChatItem *ChatScene::chatItemAt(const QPointF &scenePos) const {
 }
 
 ChatItem *ChatScene::chatItemAt(const QPointF &scenePos) const {
@@ -206,35 +223,64 @@ bool ChatScene::containsBuffer(const BufferId &id) const {
 void ChatScene::setMarkerLineVisible(bool visible) {
   _markerLineVisible = visible;
   if(visible && _markerLineValid)
 void ChatScene::setMarkerLineVisible(bool visible) {
   _markerLineVisible = visible;
   if(visible && _markerLineValid)
-    _markerLine->setVisible(true);
+    markerLine()->setVisible(true);
   else
   else
-    _markerLine->setVisible(false);
+    markerLine()->setVisible(false);
 }
 
 void ChatScene::setMarkerLine(MsgId msgId) {
   if(!isSingleBufferScene())
     return;
 }
 
 void ChatScene::setMarkerLine(MsgId msgId) {
   if(!isSingleBufferScene())
     return;
-  if(!msgId.isValid()) {
+
+  if(!msgId.isValid())
     msgId = Client::markerLine(singleBufferId());
     msgId = Client::markerLine(singleBufferId());
-    if(!msgId.isValid()) {
-      _markerLineValid = false;
-      _markerLine->setVisible(false);
-      return;
+
+  if(msgId.isValid()) {
+    ChatLine *line = chatLine(msgId, false, true);
+    if(line) {
+      markerLine()->setChatLine(line);
+      // if this was the last line, we won't see it because it's outside the sceneRect
+      // .. which is exactly what we want :)
+      markerLine()->setPos(line->pos() + QPointF(0, line->height()));
+
+      // DayChange messages might have been hidden outside the scene rect, don't make the markerline visible then!
+      if(markerLine()->pos().y() >= sceneRect().y()) {
+        _markerLineValid = true;
+        if(_markerLineVisible)
+          markerLine()->setVisible(true);
+        if(_markerLineJumpPending) {
+          _markerLineJumpPending = false;
+          if(markerLine()->isVisible()) {
+            markerLine()->ensureVisible(QRectF(), 50, 50);
+          }
+        }
+        return;
+      }
     }
   }
     }
   }
+  _markerLineValid = false;
+  markerLine()->setVisible(false);
+}
 
 
-  ChatLine *line = chatLine(msgId, false);
-  if(line) {
-    // if this was the last line, we won't see it because it's outside the sceneRect
-    // .. which is exactly what we want :)
-    _markerLine->setPos(line->pos() + QPointF(0, line->height()));
-
-    // DayChange messages might have been hidden outside the scene rect, don't make the markerline visible then!
-    if(_markerLine->pos().y() >= sceneRect().y()) {
-      _markerLineValid = true;
-      if(_markerLineVisible)
-        _markerLine->setVisible(true);
-      return;
+void ChatScene::jumpToMarkerLine(bool requestBacklog) {
+  if(!isSingleBufferScene())
+    return;
+
+  if(markerLine()->isVisible()) {
+    markerLine()->ensureVisible(QRectF(), 50, 50);
+    return;
+  }
+  if(!_markerLineValid && requestBacklog) {
+    MsgId msgId = Client::markerLine(singleBufferId());
+    if(msgId.isValid()) {
+      _markerLineJumpPending = true;
+      Client::backlogManager()->requestBacklog(singleBufferId(), msgId, -1, -1, 0);
+
+      // If we filtered out the lastSeenMsg (by changing filters after setting it), we'd never jump because the above request
+      // won't fetch any prior lines. Thus, trigger a dynamic backlog request just in case, so repeated
+      // jump tries will eventually cause enough backlog to be fetched.
+      // This is a bit hackish, but not wasteful, as jumping to the top of the ChatView would trigger a dynamic fetch anyway.
+      this->requestBacklog();
     }
   }
 }
     }
   }
 }
@@ -242,7 +288,6 @@ void ChatScene::setMarkerLine(MsgId msgId) {
 void ChatScene::rowsInserted(const QModelIndex &index, int start, int end) {
   Q_UNUSED(index);
 
 void ChatScene::rowsInserted(const QModelIndex &index, int start, int end) {
   Q_UNUSED(index);
 
-
 //   QModelIndex sidx = model()->index(start, 2);
 //   QModelIndex eidx = model()->index(end, 2);
 //   qDebug() << "rowsInserted:";
 //   QModelIndex sidx = model()->index(start, 2);
 //   QModelIndex eidx = model()->index(end, 2);
 //   qDebug() << "rowsInserted:";
@@ -330,6 +375,8 @@ void ChatScene::rowsInserted(const QModelIndex &index, int start, int end) {
     for(int i = 0; i <= end; i++) {
       line = _lines.at(i);
       line->setPos(0, line->pos().y() - h);
     for(int i = 0; i <= end; i++) {
       line = _lines.at(i);
       line->setPos(0, line->pos().y() - h);
+      if(line == markerLine()->chatLine())
+        markerLine()->setPos(line->pos() + QPointF(0, line->height()));
     }
   }
 
     }
   }
 
@@ -371,7 +418,7 @@ void ChatScene::rowsInserted(const QModelIndex &index, int start, int end) {
   }
 
   // now move the marker line if necessary. we don't need to do anything if we appended lines though...
   }
 
   // now move the marker line if necessary. we don't need to do anything if we appended lines though...
-  if(!_markerLineValid || !atBottom)
+  if(!_markerLineValid)
     setMarkerLine();
 }
 
     setMarkerLine();
 }
 
@@ -395,6 +442,8 @@ void ChatScene::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int e
   QList<ChatLine *>::iterator lineIter = _lines.begin() + start;
   int lineCount = start;
   while(lineIter != _lines.end() && lineCount <= end) {
   QList<ChatLine *>::iterator lineIter = _lines.begin() + start;
   int lineCount = start;
   while(lineIter != _lines.end() && lineCount <= end) {
+    if((*lineIter) == markerLine()->chatLine())
+      markerLine()->setChatLine(0);
     h += (*lineIter)->height();
     delete *lineIter;
     lineIter = _lines.erase(lineIter);
     h += (*lineIter)->height();
     delete *lineIter;
     lineIter = _lines.erase(lineIter);
index 56212ad..a5cbe7b 100644 (file)
@@ -88,18 +88,22 @@ public:
   inline ChatLine *chatLine(const QModelIndex &index) const { return _lines.value(index.row()); }
 
   //! Find the ChatLine belonging to a MsgId
   inline ChatLine *chatLine(const QModelIndex &index) const { return _lines.value(index.row()); }
 
   //! Find the ChatLine belonging to a MsgId
-  /** Searches for the ChatLine belonging to a MsgId.
+  /** Searches for the ChatLine belonging to a MsgId. If there are more than one ChatLine with the same msgId,
+   *  the first one is returned.
    *  Note that this method performs a binary search, hence it has as complexity of O(log n).
    *  If matchExact is false, and we don't have an exact match for the given msgId, we return the visible line right
    *  above the requested one.
    *  \param msgId      The message ID to look for
    *  \param matchExact Whether we find only exact matches
    *  Note that this method performs a binary search, hence it has as complexity of O(log n).
    *  If matchExact is false, and we don't have an exact match for the given msgId, we return the visible line right
    *  above the requested one.
    *  \param msgId      The message ID to look for
    *  \param matchExact Whether we find only exact matches
+   *  \param ignoreDayChange Whether we ignore day change messages
    *  \return The ChatLine corresponding to the given MsgId
    */
    *  \return The ChatLine corresponding to the given MsgId
    */
-  ChatLine *chatLine(MsgId msgId, bool matchExact = true) const;
+  ChatLine *chatLine(MsgId msgId, bool matchExact = true, bool ignoreDayChange = true) const;
 
   inline ChatLine *lastLine() const { return _lines.count() ? _lines.last() : 0; }
 
 
   inline ChatLine *lastLine() const { return _lines.count() ? _lines.last() : 0; }
 
+  inline MarkerLineItem *markerLine() const { return _markerLine; }
+
   inline bool isSingleBufferScene() const { return _singleBufferId.isValid(); }
   inline BufferId singleBufferId() const { return _singleBufferId; }
   bool containsBuffer(const BufferId &id) const;
   inline bool isSingleBufferScene() const { return _singleBufferId.isValid(); }
   inline BufferId singleBufferId() const { return _singleBufferId; }
   bool containsBuffer(const BufferId &id) const;
@@ -128,6 +132,7 @@ public:
 
   void setMarkerLineVisible(bool visible = true);
   void setMarkerLine(MsgId msgId = MsgId());
 
   void setMarkerLineVisible(bool visible = true);
   void setMarkerLine(MsgId msgId = MsgId());
+  void jumpToMarkerLine(bool requestBacklog);
 
   // these are used by the chatitems to notify the scene and manage selections
   void setSelectingItem(ChatItem *item);
 
   // these are used by the chatitems to notify the scene and manage selections
   void setSelectingItem(ChatItem *item);
@@ -195,7 +200,7 @@ private:
   qreal _viewportHeight;
 
   MarkerLineItem *_markerLine;
   qreal _viewportHeight;
 
   MarkerLineItem *_markerLine;
-  bool _markerLineValid, _markerLineVisible;
+  bool _markerLineVisible, _markerLineValid, _markerLineJumpPending;
 
   ColumnHandleItem *_firstColHandle, *_secondColHandle;
   qreal _firstColHandlePos, _secondColHandlePos;
 
   ColumnHandleItem *_firstColHandle, *_secondColHandle;
   qreal _firstColHandlePos, _secondColHandlePos;
index 96d7ee2..cc5009b 100644 (file)
@@ -241,7 +241,7 @@ QList<ChatLine *> ChatView::visibleChatLinesSorted(Qt::ItemSelectionMode mode) c
   return result;
 }
 
   return result;
 }
 
-ChatLine *ChatView::lastVisibleChatLine() const {
+ChatLine *ChatView::lastVisibleChatLine(bool ignoreDayChange) const {
   if(!scene())
     return 0;
 
   if(!scene())
     return 0;
 
@@ -253,7 +253,7 @@ ChatLine *ChatView::lastVisibleChatLine() const {
 
   QSet<ChatLine *> visibleLines = visibleChatLines(Qt::ContainsItemBoundingRect);
   foreach(ChatLine *line, visibleLines) {
 
   QSet<ChatLine *> visibleLines = visibleChatLines(Qt::ContainsItemBoundingRect);
   foreach(ChatLine *line, visibleLines) {
-    if(line->row() > row)
+    if(line->row() > row && (ignoreDayChange? line->msgType() != Message::DayChange : true))
       row = line->row();
   }
 
       row = line->row();
   }
 
@@ -283,6 +283,10 @@ void ChatView::markerLineSet(BufferId buffer, MsgId msgId) {
   scene()->setMarkerLineVisible(true);
 }
 
   scene()->setMarkerLineVisible(true);
 }
 
+void ChatView::jumpToMarkerLine(bool requestBacklog) {
+  scene()->jumpToMarkerLine(requestBacklog);
+}
+
 void ChatView::addActionsToMenu(QMenu *menu, const QPointF &pos) {
   // zoom actions
   BufferWidget *bw = qobject_cast<BufferWidget *>(bufferContainer());
 void ChatView::addActionsToMenu(QMenu *menu, const QPointF &pos) {
   // zoom actions
   BufferWidget *bw = qobject_cast<BufferWidget *>(bufferContainer());
index c2aa604..51522d2 100644 (file)
@@ -65,7 +65,7 @@ public:
   /** Using this method more efficient than calling visibleChatLinesSorted() and taking its last element.
    *  \return The last fully visible ChatLine in the view
    */
   /** Using this method more efficient than calling visibleChatLinesSorted() and taking its last element.
    *  \return The last fully visible ChatLine in the view
    */
-  ChatLine *lastVisibleChatLine() const;
+  ChatLine *lastVisibleChatLine(bool ignoreDayChange = false) const;
 
   virtual void addActionsToMenu(QMenu *, const QPointF &pos);
 
 
   virtual void addActionsToMenu(QMenu *, const QPointF &pos);
 
@@ -86,6 +86,7 @@ public slots:
 
   void setMarkerLineVisible(bool visible = true);
   void setMarkerLine(MsgId msgId);
 
   void setMarkerLineVisible(bool visible = true);
   void setMarkerLine(MsgId msgId);
+  void jumpToMarkerLine(bool requestBacklog);
 
 protected:
   virtual bool event(QEvent *event);
 
 protected:
   virtual bool event(QEvent *event);
index 9782a79..563ef4e 100644 (file)
 
 MarkerLineItem::MarkerLineItem(qreal sceneWidth, QGraphicsItem *parent)
   : QGraphicsObject(parent),
 
 MarkerLineItem::MarkerLineItem(qreal sceneWidth, QGraphicsItem *parent)
   : QGraphicsObject(parent),
-    _boundingRect(0, 0, sceneWidth, 1)
+    _boundingRect(0, 0, sceneWidth, 1),
+    _chatLine(0)
 {
 {
+  setVisible(false);
   setZValue(8);
   styleChanged(); // init brush and height
   connect(QtUi::style(), SIGNAL(changed()), SLOT(styleChanged()));
 }
 
   setZValue(8);
   styleChanged(); // init brush and height
   connect(QtUi::style(), SIGNAL(changed()), SLOT(styleChanged()));
 }
 
+void MarkerLineItem::setChatLine(ChatLine *line) {
+  _chatLine = line;
+  if(!line)
+    setVisible(false);
+}
+
 void MarkerLineItem::styleChanged() {
   _brush = QtUi::style()->brush(UiStyle::MarkerLine);
 
 void MarkerLineItem::styleChanged() {
   _brush = QtUi::style()->brush(UiStyle::MarkerLine);
 
index 3911da8..827fa25 100644 (file)
@@ -25,6 +25,8 @@
 
 #include "chatscene.h"
 
 
 #include "chatscene.h"
 
+class ChatLine;
+
 class MarkerLineItem : public QGraphicsObject {
   Q_OBJECT
 
 class MarkerLineItem : public QGraphicsObject {
   Q_OBJECT
 
@@ -33,10 +35,13 @@ public:
   virtual inline int type() const { return ChatScene::MarkerLineType; }
 
   inline QRectF boundingRect() const { return _boundingRect; }
   virtual inline int type() const { return ChatScene::MarkerLineType; }
 
   inline QRectF boundingRect() const { return _boundingRect; }
-
   void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
 
   void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
 
+  inline ChatLine *chatLine() const { return _chatLine; }
+
 public slots:
 public slots:
+  //! Set the ChatLine this MarkerLineItem is associated to
+  void setChatLine(ChatLine *line);
   void sceneRectChanged(const QRectF &);
 
 private slots:
   void sceneRectChanged(const QRectF &);
 
 private slots:
@@ -45,6 +50,7 @@ private slots:
 private:
   QRectF _boundingRect;
   QBrush _brush;
 private:
   QRectF _boundingRect;
   QBrush _brush;
+  ChatLine *_chatLine;
 };
 
 #endif
 };
 
 #endif