Qt: Automatically scroll the packet list.
authorMichal Labedzki <michal.labedzki@tieto.com>
Fri, 20 Feb 2015 14:30:23 +0000 (15:30 +0100)
committerGerald Combs <gerald@wireshark.org>
Fri, 27 Feb 2015 20:27:14 +0000 (20:27 +0000)
Add an "Auto Scroll in Live Capture" action to the Go menu. It's in the
View menu in the GTK+ UI but it seems to make more sense as a navigation
item.

Use a timeout interval for automatic scrolling. I haven't run any tests
to see if this makes a difference but it would seem that the less
drawing we do during a high speed capture the better, particularly for
remote displays.

Update the x-stay-last icons.

Note that we might want to make prefs.capture_auto_scroll a "recent"
setting.

Mark auto_scroll_live and packet_list_check_end GTK+ only.

Bug: 10601
Co-authored-by: Gerald Combs <gerald@wireshark.org>
Change-Id: I645d27c0814f0e4a0d5b01ae68be366847e2522d
Reviewed-on: https://code.wireshark.org/review/7292
Reviewed-by: Gerald Combs <gerald@wireshark.org>
17 files changed:
epan/prefs.c
file.c
image/toolbar/16x16/x-stay-last.png
image/toolbar/16x16/x-stay-last.svg
image/toolbar/16x16/x-stay-last@2x.png
image/toolbar/24x24/x-stay-last.png
image/toolbar/24x24/x-stay-last.svg
image/toolbar/24x24/x-stay-last@2x.png
ui/preference_utils.h
ui/qt/capture_file.cpp
ui/qt/main_window.cpp
ui/qt/main_window.h
ui/qt/main_window.ui
ui/qt/main_window_slots.cpp
ui/qt/packet_list.cpp
ui/qt/packet_list.h
ui/ui_util.h

index 7c90d8570d9b8bafbb0938947e414a18e43c29a6..00890d9c66089aa6320590090404d393577dbf6b 100644 (file)
@@ -2416,6 +2416,7 @@ prefs_register_modules(void)
     prefs_register_bool_preference(capture_module, "real_time_update", "Update packet list in real time during capture",
         "Update packet list in real time during capture?", &prefs.capture_real_time);
 
+    /* We might want to make this a "recent" setting. */
     prefs_register_bool_preference(capture_module, "auto_scroll", "Scroll packet list during capture",
         "Scroll packet list during capture?", &prefs.capture_auto_scroll);
 
diff --git a/file.c b/file.c
index 01ed0d4fda124fcae23cb715d8966c1be7f172ff..8e531d8222cce58d857bb2b77ed7e9dfea35bf49 100644 (file)
--- a/file.c
+++ b/file.c
 #endif
 
 #ifdef HAVE_LIBPCAP
-gboolean auto_scroll_live;
+gboolean auto_scroll_live; /* GTK+ only? */
 #endif
 
 static int read_packet(capture_file *cf, dfilter_t *dfcode, epan_dissect_t *edt,
index e5bb6ea5b509ff7a0032290d3d9d1e372734569e..68629cc289f6eff62e203391889c6e89c1494cfa 100644 (file)
Binary files a/image/toolbar/16x16/x-stay-last.png and b/image/toolbar/16x16/x-stay-last.png differ
index acec8379f7dd37e777b9f53ce81f2eb836d7461e..29625568691fe6f5de0f68b463d3f69f57da58e1 100644 (file)
@@ -14,7 +14,7 @@
    id="svg2"
    version="1.1"
    inkscape:version="0.48.2 r9819"
-   sodipodi:docname="x-colorize-packets.svg"
+   sodipodi:docname="x-stay-last.svg"
    inkscape:export-filename="x-colorize-packets.png"
    inkscape:export-xdpi="90"
    inkscape:export-ydpi="90">
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="34.9375"
-     inkscape:cx="7.7733362"
+     inkscape:zoom="39.25"
+     inkscape:cx="8"
      inkscape:cy="8"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="true"
-     inkscape:window-width="1028"
-     inkscape:window-height="728"
-     inkscape:window-x="283"
-     inkscape:window-y="792"
+     inkscape:window-width="1263"
+     inkscape:window-height="799"
+     inkscape:window-x="65"
+     inkscape:window-y="1"
      inkscape:window-maximized="0">
     <inkscape:grid
        type="xygrid"
@@ -54,7 +54,7 @@
         <dc:format>image/svg+xml</dc:format>
         <dc:type
            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
+        <dc:title />
       </cc:Work>
     </rdf:RDF>
   </metadata>
@@ -95,7 +95,7 @@
        id="path4099"
        inkscape:connector-curvature="0" />
     <path
-       style="fill:#204a87;stroke:#ef2929;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+       style="fill:#204a87;stroke:#babdb6;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
        d="m 1.4821429,1040.8622 c 13.0357141,0 13.0357141,0 13.0357141,0"
        id="path4103"
        inkscape:connector-curvature="0" />
        inkscape:connector-curvature="0"
        id="path4105"
        d="m 1.5185185,1043.8622 c 12.9629625,0 12.9629625,0 12.9629625,0"
-       style="fill:none;stroke:#3465a4;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
+       style="fill:none;stroke:#babdb6;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
     <path
        inkscape:connector-curvature="0"
        id="path4109"
        d="m 1.5185185,1046.8622 c 12.9629625,0 12.9629625,0 12.9629625,0"
-       style="fill:none;stroke:#75507b;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
+       style="fill:none;stroke:#babdb6;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
     <path
        style="fill:none;stroke:#2e3436;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
        d="m 1.5,1050.8622 c 13,0 13,0 13,0"
        inkscape:connector-curvature="0" />
     <path
        sodipodi:type="star"
-       style="fill:#c4a000;fill-opacity:1;stroke:#725000;stroke-width:3.77809595999999992;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       style="fill:#c4a000;fill-opacity:1;stroke:#725000;stroke-width:3.77809596;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
        id="path4205"
        sodipodi:sides="3"
        sodipodi:cx="28"
        inkscape:rounded="1"
        inkscape:randomized="0"
        d="M 28,14 C 17.607695,14 17.607695,14 22.803847,5.0000001 28,-4 28,-4 33.196152,4.9999999 38.392305,14 38.392305,14 28,14 z"
-       transform="matrix(-0.36780134,0,0,-0.19047619,21.631938,1050.5289)"
+       transform="matrix(-0.36780134,0,0,-0.19047619,33.631938,1050.5289)"
        inkscape:transform-center-y="0.35714706" />
+    <path
+       inkscape:transform-center-x="-3.8849402e-05"
+       transform="matrix(-0.40653866,0,0,-0.25396825,22.383082,1050.4178)"
+       d="M 28,14 C 17.607695,14 17.607695,14 22.803847,5.0000001 28,-4 28,-4 33.196152,4.9999999 38.392305,14 38.392305,14 28,14 z"
+       inkscape:randomized="0"
+       inkscape:rounded="1"
+       inkscape:flatsided="true"
+       sodipodi:arg2="2.6179939"
+       sodipodi:arg1="1.5707963"
+       sodipodi:r2="3"
+       sodipodi:r1="6"
+       sodipodi:cy="8"
+       sodipodi:cx="28"
+       sodipodi:sides="3"
+       id="path4590"
+       style="fill:#3465a4;fill-opacity:1;stroke:#3465a4;stroke-width:3.11214161;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       sodipodi:type="star"
+       inkscape:transform-center-y="0.47633785" />
   </g>
 </svg>
index cb8ee9eb885a2dca2dade6b99f5991d50ffa9a03..23c73aab9ae1458a1ca6f5f0cb5946c66c6abaf8 100644 (file)
Binary files a/image/toolbar/16x16/x-stay-last@2x.png and b/image/toolbar/16x16/x-stay-last@2x.png differ
index 06b9d30bcdd9019d9ea434a9c3a5a2d9fb5ca9ce..9c04995d80e258ed89636993dffece3ffac1c480 100644 (file)
Binary files a/image/toolbar/24x24/x-stay-last.png and b/image/toolbar/24x24/x-stay-last.png differ
index 06ce2fdc8b1dddab21df9533780659deeb5e00cb..2bbd401b6feba615d9019d295f1c194e1282b2ce 100644 (file)
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="23.291667"
-     inkscape:cx="10.503891"
+     inkscape:zoom="23.369223"
+     inkscape:cx="15.441568"
      inkscape:cy="12"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="true"
-     inkscape:window-width="1028"
-     inkscape:window-height="728"
-     inkscape:window-x="132"
-     inkscape:window-y="821"
+     inkscape:window-width="1300"
+     inkscape:window-height="732"
+     inkscape:window-x="70"
+     inkscape:window-y="702"
      inkscape:window-maximized="0">
     <inkscape:grid
        type="xygrid"
@@ -54,7 +54,7 @@
         <dc:format>image/svg+xml</dc:format>
         <dc:type
            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
+        <dc:title />
       </cc:Work>
     </rdf:RDF>
   </metadata>
@@ -97,9 +97,9 @@
        inkscape:connector-curvature="0"
        id="path4115"
        d="m 1.5,1032.8622 c 21,0 21,0 21,0"
-       style="fill:#ef2929;stroke:#ef2929;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
+       style="fill:#babdb6;stroke:#babdb6;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1" />
     <path
-       style="fill:none;stroke:#73d216;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+       style="fill:#babdb6;stroke:#babdb6;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
        d="m 1.4999999,1038.8622 c 20.9999991,0 20.9999991,0 20.9999991,0"
        id="path4117"
        inkscape:connector-curvature="0" />
        inkscape:connector-curvature="0"
        id="path4119"
        d="m 1.4999999,1035.8622 c 20.9999991,0 20.9999991,0 20.9999991,0"
-       style="fill:none;stroke:#3465a4;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
+       style="fill:#babdb6;stroke:#babdb6;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1" />
     <path
-       style="fill:none;stroke:#3465a4;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+       style="fill:#babdb6;stroke:#babdb6;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
        d="m 1.4999999,1041.8622 c 20.9999991,0 20.9999991,0 20.9999991,0"
        id="path4121"
        inkscape:connector-curvature="0" />
        inkscape:connector-curvature="0"
        id="path4123"
        d="m 1.5,1047.8622 c 21,0 21,0 21,0"
-       style="fill:#f57900;stroke:#f57900;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
+       style="fill:#babdb6;stroke:#babdb6;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1" />
     <path
-       style="fill:none;stroke:#75507b;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
+       style="fill:#babdb6;stroke:#babdb6;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
        d="m 1.4999999,1044.8622 c 20.9999991,0 20.9999991,0 20.9999991,0"
        id="path4125"
        inkscape:connector-curvature="0" />
        inkscape:rounded="1"
        inkscape:randomized="0"
        d="M 28,14 C 17.607695,14 17.607695,14 22.803847,5.0000001 28,-4 28,-4 33.196152,4.9999999 38.392305,14 38.392305,14 28,14 z"
-       transform="matrix(0.58076952,0,0,-0.31746032,1.2384534,1050.3066)"
+       transform="matrix(0.58076952,0,0,-0.31746032,15.238453,1050.3066)"
        inkscape:transform-center-y="0.59532957" />
+    <path
+       inkscape:transform-center-x="-6.9566908e-05"
+       transform="matrix(-0.58076952,0,0,-0.31746032,32.761547,1050.3066)"
+       d="M 28,14 C 17.607695,14 17.607695,14 22.803847,5.0000001 28,-4 28,-4 33.196152,4.9999999 38.392305,14 38.392305,14 28,14 z"
+       inkscape:randomized="0"
+       inkscape:rounded="1"
+       inkscape:flatsided="true"
+       sodipodi:arg2="2.6179939"
+       sodipodi:arg1="1.5707963"
+       sodipodi:r2="3"
+       sodipodi:r1="6"
+       sodipodi:cy="8"
+       sodipodi:cx="28"
+       sodipodi:sides="3"
+       id="path4550"
+       style="fill:#3465a4;fill-opacity:1;stroke:#3465a4;stroke-width:2.32891345;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       sodipodi:type="star"
+       inkscape:transform-center-y="0.5953435" />
   </g>
 </svg>
index fff5b22a0fc187f480310e459a74716392f4e65f..a66c2a8007f1665f921d363b84ee97d6d060e489 100644 (file)
Binary files a/image/toolbar/24x24/x-stay-last@2x.png and b/image/toolbar/24x24/x-stay-last@2x.png differ
index 074b9748442530355dfbc62f6e6322a4af54e8ff..e4117d682ee8fd27dc9757e2e3a4ac10bf5eb634 100644 (file)
@@ -70,7 +70,7 @@ extern void reset_stashed_pref(pref_t *pref);
 
 /** If autoscroll in live captures is active or not
  */
-extern gboolean auto_scroll_live;
+extern gboolean auto_scroll_live; /* GTK+ only. */
 
 /** Fill in capture options with values from the preferences
  */
index a32f225f15a88e9110687b89c41defc1626833e9..a15e8ebdbae48c023ecbb3904c26721a7b445f57 100644 (file)
@@ -40,6 +40,7 @@ capture_file cfile;
 // To do:
 // - Add getters and (if needed) setters:
 //   - Full filename
+//   - Capture state (stopped, prepared, running).
 
 QString CaptureFile::no_capture_file_ = QObject::tr("[no capture file]");
 
index c69d50a68dfbf01ffb4ca7743e621f235681478f..7eb7b702b7739c5524fef12a15fdee2c6aafd94a 100644 (file)
@@ -187,7 +187,6 @@ MainWindow::MainWindow(QWidget *parent) :
     setForCapturedPackets(false);
     setMenusForSelectedPacket();
     setMenusForSelectedTreeRow();
-    setForCaptureInProgress(false);
     setMenusForFileSet(false);
     interfaceSelectionChanged();
     loadWindowGeometry();
@@ -202,7 +201,7 @@ MainWindow::MainWindow(QWidget *parent) :
     connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
-    connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updateNameResolutionActions()));
+    connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
 
     connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
@@ -286,7 +285,8 @@ MainWindow::MainWindow(QWidget *parent) :
     initShowHideMainWidgets();
     initTimeDisplayFormatMenu();
     initTimePrecisionFormatMenu();
-    updateNameResolutionActions();
+    updatePreferenceActions();
+    setForCaptureInProgress(false);
 
     connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
             this, SLOT(captureCapturePrepared(capture_session *)));
@@ -388,6 +388,8 @@ MainWindow::MainWindow(QWidget *parent) :
             packet_list_, SLOT(redrawVisiblePackets()));
     connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
             this, SLOT(openPacketDialog()));
+    connect(packet_list_, SIGNAL(packetListScrolled(bool)),
+            main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
 
     connect(proto_tree_, SIGNAL(protoItemSelected(QString&)),
             main_ui_->statusBar, SLOT(pushFieldStatus(QString&)));
@@ -1420,6 +1422,7 @@ void MainWindow::initMainToolbarIcons()
     main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
     main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
     main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
+    main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
 
     main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
     main_ui_->actionViewColorizePacketList->setChecked(recent.packet_list_colorize);
@@ -1818,11 +1821,12 @@ void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
 {
     setMenusForCaptureInProgress(capture_in_progress);
 
-//#ifdef HAVE_LIBPCAP
+#ifdef HAVE_LIBPCAP
+    packet_list_->setCaptureInProgress(capture_in_progress);
 //    set_toolbar_for_capture_in_progress(capture_in_progress);
 
 //    set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
-//#endif
+#endif
 }
 
 /*
index 5310c000339fa747d93f92be88a251b6daf3561d..05f73e3f610b4e43ad06b3cef48a9f5bc98feb29 100644 (file)
@@ -190,7 +190,7 @@ public slots:
     void layoutPanes();
     void applyRecentPaneGeometry();
     void layoutToolbars();
-    void updateNameResolutionActions();
+    void updatePreferenceActions();
 
     void captureCapturePrepared(capture_session *);
     void captureCaptureUpdateStarted(capture_session *);
@@ -324,6 +324,7 @@ private slots:
     void on_actionViewReload_triggered();
 
     void on_actionGoGoToPacket_triggered();
+    void on_actionGoAutoScroll_toggled(bool checked);
     void resetPreviousFocus();
 
 #ifdef HAVE_LIBPCAP
index 29a9efba840fa65670001fc70400e64ed4a7bdaa..6a9901a3a45882b279535ec24919366537b20b96 100644 (file)
     <addaction name="actionGoPreviousPacket"/>
     <addaction name="actionGoFirstPacket"/>
     <addaction name="actionGoLastPacket"/>
+    <addaction name="separator"/>
+    <addaction name="actionGoAutoScroll"/>
    </widget>
    <widget class="QMenu" name="menuView">
     <property name="title">
    <addaction name="actionGoGoToPacket"/>
    <addaction name="actionGoFirstPacket"/>
    <addaction name="actionGoLastPacket"/>
+   <addaction name="actionGoAutoScroll"/>
    <addaction name="separator"/>
    <addaction name="actionViewColorizePacketList"/>
    <addaction name="separator"/>
     <string>Show the linked packet in a separate window.</string>
    </property>
   </action>
+  <action name="actionGoAutoScroll">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Auto Scroll in Li&amp;ve Capture</string>
+   </property>
+   <property name="toolTip">
+    <string>Automatically scroll to the last packet during a live capture.</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <customwidgets>
index 8291e0d3c8d844679e050b7a1ed6868adb19053d..bd366ae0e89df9ce847125fa9fa8b22449f6300c 100644 (file)
@@ -415,11 +415,14 @@ void MainWindow::layoutToolbars()
     main_ui_->mainToolBar->setToolButtonStyle(tbstyle);
 }
 
-void MainWindow::updateNameResolutionActions()
+void MainWindow::updatePreferenceActions()
 {
     main_ui_->actionViewNameResolutionPhysical->setChecked(gbl_resolv_flags.mac_name);
     main_ui_->actionViewNameResolutionNetwork->setChecked(gbl_resolv_flags.network_name);
     main_ui_->actionViewNameResolutionTransport->setChecked(gbl_resolv_flags.transport_name);
+
+    // Should this be a "recent" setting?
+    main_ui_->actionGoAutoScroll->setChecked(prefs.capture_auto_scroll);
 }
 
 void MainWindow::filterAction(QString &action_filter, FilterAction::Action action, FilterAction::ActionType type)
@@ -2693,6 +2696,11 @@ void MainWindow::on_actionGoGoToPacket_triggered() {
     main_ui_->goToLineEdit->setFocus();
 }
 
+void MainWindow::on_actionGoAutoScroll_toggled(bool checked)
+{
+    packet_list_->setAutoScroll(checked);
+}
+
 void MainWindow::resetPreviousFocus() {
     previous_focus_ = NULL;
 }
index 428a51f1f7204682750796e76f5513f37f64b2ab..4d6e3905acbe7c759b301a71451bdfb6ba117ca4 100644 (file)
 #include <QScrollBar>
 #include <QTabWidget>
 #include <QTextEdit>
+#include <QTimerEvent>
 #include <QTreeWidget>
 
 // To do:
-// - Heading context menus
 // - Catch column reordering and rebuild the column list accoringly.
+// - Use a timer to trigger automatic scrolling.
 
 // If we ever add the ability to open multiple capture files we might be
 // able to use something like QMap<capture_file *, PacketList *> to match
@@ -72,6 +73,7 @@
 static PacketList *gbl_cur_packet_list = NULL;
 
 const int max_comments_to_fetch_ = 20000000; // Arbitrary
+const int tail_update_interval_ = 100; // Milliseconds.
 
 guint
 packet_list_append(column_info *cinfo, frame_data *fdata)
@@ -135,13 +137,7 @@ packet_list_select_row_from_data(frame_data *fdata_needle)
 gboolean
 packet_list_check_end(void)
 {
-    if (gbl_cur_packet_list) {
-        QScrollBar *sb = gbl_cur_packet_list->verticalScrollBar();
-        if (sb && sb->isVisible() && sb->value() == sb->maximum()) {
-            return TRUE;
-        }
-    }
-    return FALSE;
+    return FALSE; // GTK+ only.
 }
 
 void
@@ -230,7 +226,9 @@ PacketList::PacketList(QWidget *parent) :
     byte_view_tab_(NULL),
     cap_file_(NULL),
     decode_as_(NULL),
-    ctx_column_(-1)
+    ctx_column_(-1),
+    capture_in_progress_(false),
+    tail_timer_id_(0)
 {
     QMenu *submenu, *subsubmenu;
     QAction *action;
@@ -419,6 +417,8 @@ PacketList::PacketList(QWidget *parent) :
     connect(header(), SIGNAL(customContextMenuRequested(QPoint)),
                 SLOT(showHeaderMenu(QPoint)));
     connect(header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(sectionResized(int,int,int)));
+
+    connect(verticalScrollBar(), SIGNAL(actionTriggered(int)), this, SLOT(vScrollBarActionTriggered(int)));
 }
 
 void PacketList::setProtoTree (ProtoTree *proto_tree) {
@@ -519,6 +519,27 @@ void PacketList::contextMenuEvent(QContextMenuEvent *event)
     decode_as_->setData(QVariant());
 }
 
+// Auto scroll if:
+// - We're not at the end
+// - We are capturing
+// - actionGoAutoScroll in the main UI is checked.
+// - It's been more than tail_update_interval_ ms since we last scrolled
+// - The last user-set vertical scrollbar position was at the end.
+
+// Using a timer assumes that we can save CPU overhead by updating
+// periodically. If that's not the case we can dispense with it and call
+// scrollToBottom() from rowsInserted().
+void PacketList::timerEvent(QTimerEvent *event)
+{
+    if (rows_inserted_
+            && event->timerId() == tail_timer_id_
+            && capture_in_progress_
+            && tail_at_end_) {
+        scrollToBottom();
+        rows_inserted_ = false;
+    }
+}
+
 void PacketList::markFramesReady()
 {
     packets_bar_update();
@@ -661,6 +682,18 @@ void PacketList::recolorPackets()
     redrawVisiblePackets();
 }
 
+void PacketList::setAutoScroll(bool enabled)
+{
+    tail_at_end_ = enabled;
+    if (enabled) {
+        scrollToBottom();
+        if (tail_timer_id_ < 1) tail_timer_id_ = startTimer(tail_update_interval_);
+    } else if (tail_timer_id_ > 0) {
+        killTimer(tail_timer_id_);
+        tail_timer_id_ = 0;
+    }
+}
+
 void PacketList::freeze()
 {
     setUpdatesEnabled(false);
@@ -1129,6 +1162,25 @@ void PacketList::sectionResized(int, int, int)
     }
 }
 
+// We need to tell when the user has scrolled the packet list, either to
+// the end or anywhere other than the end.
+void PacketList::vScrollBarActionTriggered(int)
+{
+    // If we're scrolling with a mouse wheel or trackpad sliderPosition can end up
+    // past the end.
+    tail_at_end_ = (verticalScrollBar()->sliderPosition() >= verticalScrollBar()->maximum());
+
+    if (capture_in_progress_ && prefs.capture_auto_scroll) {
+        emit packetListScrolled(tail_at_end_);
+    }
+}
+
+void PacketList::rowsInserted(const QModelIndex &parent, int start, int end)
+{
+    QTreeView::rowsInserted(parent, start, end);
+    rows_inserted_ = true;
+}
+
 /*
  * Editor modelines
  *
index 04fcf74e2e42aa8101598e786f298e2625fcb660..357533c67bf995e4dbf2a1dbeb80c9b4ca398f3f 100644 (file)
 #include "proto_tree.h"
 #include "related_packet_delegate.h"
 
-#include <QTreeView>
 #include <QMenu>
+#include <QTime>
+#include <QTreeView>
 
 class QAction;
+class QTimerEvent;
 
 class PacketList : public QTreeView
 {
@@ -63,12 +65,17 @@ public:
     void setPacketComment(QString new_comment);
     QString allPacketComments();
     void recolorPackets();
+    void setAutoScroll(bool enabled = true);
+    void setCaptureInProgress(bool in_progress = false) { capture_in_progress_ = in_progress; tail_at_end_ = in_progress; }
 
 protected:
     void showEvent (QShowEvent *);
     void selectionChanged (const QItemSelection & selected, const QItemSelection & deselected);
     void contextMenuEvent(QContextMenuEvent *event);
+    void timerEvent(QTimerEvent *event);
 
+protected slots:
+    void rowsInserted(const QModelIndex &parent, int start, int end);
 
 private:
     PacketListModel *packet_list_model_;
@@ -87,6 +94,10 @@ private:
     int header_ctx_column_;
     QAction *show_hide_separator_;
     QList<QAction *>show_hide_actions_;
+    bool capture_in_progress_;
+    int tail_timer_id_;
+    bool tail_at_end_;
+    bool rows_inserted_;
 
     void markFramesReady();
     void setFrameMark(gboolean set, frame_data *fdata);
@@ -94,12 +105,12 @@ private:
     void setFrameReftime(gboolean set, frame_data *fdata);
     void setColumnVisibility();
     void initHeaderContextMenu();
-
 signals:
     void packetDissectionChanged();
     void packetSelectionChanged();
     void showPreferences(PreferencesDialog::PreferencesPane start_pane);
     void editColumn(int column);
+    void packetListScrolled(bool at_end);
 
 public slots:
     void setCaptureFile(capture_file *cf);
@@ -124,6 +135,7 @@ private slots:
     void headerMenuTriggered();
     void columnVisibilityTriggered();
     void sectionResized(int, int, int);
+    void vScrollBarActionTriggered(int);
 };
 
 #endif // PACKET_LIST_H
index 7ebd59bd3d059bcc56771d06c3e46ebcddc89325..b76e218951e0db4bc760de50589c4feaa1612631 100644 (file)
@@ -74,7 +74,7 @@ void packet_list_queue_draw(void);
 void packet_list_select_first_row(void);
 void packet_list_select_last_row(void);
 void packet_list_moveto_end(void);
-gboolean packet_list_check_end(void);
+gboolean packet_list_check_end(void); /* GTK+ only */
 gboolean packet_list_select_row_from_data(frame_data *fdata_needle);
 void packet_list_resize_column(gint col);