diff --git a/epan/prefs.c b/epan/prefs.c index d096f28a38..a54cafac3a 100644 --- a/epan/prefs.c +++ b/epan/prefs.c @@ -3485,6 +3485,11 @@ prefs_register_modules(void) "To prevent sorting by mistake (which can take some time to calculate), it can be disabled", &prefs.gui_packet_list_sortable); + prefs_register_uint_preference(gui_module, "packet_list_cached_rows_max", + "Maximum cached rows", + "Maximum number of rows that can be sorted by columns that require dissection. Increasing this increases memory consumption by caching column text", + 10, + &prefs.gui_packet_list_cached_rows_max); prefs_register_bool_preference(gui_module, "interfaces_show_hidden", "Show hidden interfaces", @@ -4190,6 +4195,7 @@ pre_init_prefs(void) prefs.gui_packet_list_show_related = TRUE; prefs.gui_packet_list_show_minimap = TRUE; prefs.gui_packet_list_sortable = TRUE; + prefs.gui_packet_list_cached_rows_max = 10000; g_free (prefs.gui_interfaces_hide_types); prefs.gui_interfaces_hide_types = g_strdup(""); prefs.gui_interfaces_show_hidden = FALSE; diff --git a/epan/prefs.h b/epan/prefs.h index 9e6fcff56c..6bfd0ae1ea 100644 --- a/epan/prefs.h +++ b/epan/prefs.h @@ -220,6 +220,7 @@ typedef struct _e_prefs { gboolean gui_packet_list_show_related; gboolean gui_packet_list_show_minimap; gboolean gui_packet_list_sortable; + guint gui_packet_list_cached_rows_max; gint gui_decimal_places1; /* Used for type 1 calculations */ gint gui_decimal_places2; /* Used for type 2 calculations */ gint gui_decimal_places3; /* Used for type 3 calculations */ diff --git a/ui/qt/layout_preferences_frame.cpp b/ui/qt/layout_preferences_frame.cpp index 34d6d2d866..e1a5d99015 100644 --- a/ui/qt/layout_preferences_frame.cpp +++ b/ui/qt/layout_preferences_frame.cpp @@ -41,7 +41,7 @@ LayoutPreferencesFrame::LayoutPreferencesFrame(QWidget *parent) : QStyleOption style_opt; QString indent_ss = QString( - "QCheckBox {" + "QCheckBox, QLabel {" " margin-left: %1px;" "}" ).arg(ui->packetListSeparatorCheckBox->style()->subElementRect(QStyle::SE_CheckBoxContents, &style_opt).left()); @@ -49,6 +49,7 @@ LayoutPreferencesFrame::LayoutPreferencesFrame(QWidget *parent) : ui->packetListHeaderShowColumnDefinition->setStyleSheet(indent_ss); ui->packetListHoverStyleCheckbox->setStyleSheet(indent_ss); ui->packetListAllowSorting->setStyleSheet(indent_ss); + ui->packetListCachedRowsLabel->setStyleSheet(indent_ss); ui->statusBarShowSelectedPacketCheckBox->setStyleSheet(indent_ss); ui->statusBarShowFileLoadTimeCheckBox->setStyleSheet(indent_ss); @@ -64,6 +65,8 @@ LayoutPreferencesFrame::LayoutPreferencesFrame(QWidget *parent) : pref_packet_list_sorting_ = prefFromPrefPtr(&prefs.gui_packet_list_sortable); ui->packetListAllowSorting->setChecked(prefs_get_bool_value(pref_packet_list_sorting_, pref_stashed)); + pref_packet_list_cached_rows_max_ = prefFromPrefPtr(&prefs.gui_packet_list_cached_rows_max); + pref_show_selected_packet_ = prefFromPrefPtr(&prefs.gui_qt_show_selected_packet); ui->statusBarShowSelectedPacketCheckBox->setChecked(prefs_get_bool_value(pref_show_selected_packet_, pref_stashed)); @@ -157,6 +160,8 @@ void LayoutPreferencesFrame::updateWidgets() ui->pane3NoneRadioButton->setChecked(true); break; } + + ui->packetListCachedRowsLineEdit->setText(QString::number(prefs_get_uint_value_real(pref_packet_list_cached_rows_max_, pref_stashed))); } void LayoutPreferencesFrame::on_layout5ToolButton_toggled(bool checked) @@ -371,6 +376,15 @@ void LayoutPreferencesFrame::on_packetListAllowSorting_toggled(bool checked) prefs_set_bool_value(pref_packet_list_sorting_, (gboolean) checked, pref_stashed); } +void LayoutPreferencesFrame::on_packetListCachedRowsLineEdit_textEdited(const QString &new_str) +{ + bool ok; + uint new_uint = new_str.toUInt(&ok, 0); + if (ok) { + prefs_set_uint_value(pref_packet_list_cached_rows_max_, new_uint, pref_stashed); + } +} + void LayoutPreferencesFrame::on_statusBarShowSelectedPacketCheckBox_toggled(bool checked) { prefs_set_bool_value(pref_show_selected_packet_, (gboolean) checked, pref_stashed); diff --git a/ui/qt/layout_preferences_frame.h b/ui/qt/layout_preferences_frame.h index 372caeae17..6773c5eeb9 100644 --- a/ui/qt/layout_preferences_frame.h +++ b/ui/qt/layout_preferences_frame.h @@ -41,6 +41,7 @@ private: pref_t *pref_packet_header_column_definition_; pref_t *pref_packet_list_hover_style_; pref_t *pref_packet_list_sorting_; + pref_t *pref_packet_list_cached_rows_max_; pref_t *pref_show_selected_packet_; pref_t *pref_show_file_load_time_; @@ -73,6 +74,7 @@ private slots: void on_packetListHeaderShowColumnDefinition_toggled(bool checked); void on_packetListHoverStyleCheckbox_toggled(bool checked); void on_packetListAllowSorting_toggled(bool checked); + void on_packetListCachedRowsLineEdit_textEdited(const QString &new_str); void on_statusBarShowSelectedPacketCheckBox_toggled(bool checked); void on_statusBarShowFileLoadTimeCheckBox_toggled(bool checked); }; diff --git a/ui/qt/layout_preferences_frame.ui b/ui/qt/layout_preferences_frame.ui index 77a7edf62f..70600706a4 100644 --- a/ui/qt/layout_preferences_frame.ui +++ b/ui/qt/layout_preferences_frame.ui @@ -402,6 +402,34 @@ + + + + + + Maximum number of cached rows (affects sorting) + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + + + + <html><head/><body><p>If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.</p></body></html> + + + + + + + Qt:Horizontal + + + + + diff --git a/ui/qt/models/packet_list_model.cpp b/ui/qt/models/packet_list_model.cpp index a9726a8aa1..7c71e824c6 100644 --- a/ui/qt/models/packet_list_model.cpp +++ b/ui/qt/models/packet_list_model.cpp @@ -358,6 +358,27 @@ void PacketListModel::sort(int column, Qt::SortOrder order) QString col_title = get_column_title(column); + if (text_sort_column_ >= 0 && (guint)visible_rows_.count() > prefs.gui_packet_list_cached_rows_max) { + /* Column not based on frame data but by column text that requires + * dissection, so to sort in a reasonable amount of time the column + * text needs to be cached. + */ + /* If the sort is being triggered because the columns were already + * sorted and the filter is being cleared (or changed to something + * else with more rows than fit in the cache), then the temporary + * message will be immediately overwritten with the standard capture + * statistics by the packets_bar_update() call after thawing the rows. + * It will still blink yellow, and the user will get the message if + * they then click on the header file (wondering why it didn't sort.) + */ + if (col_title.isEmpty()) { + col_title = tr("Column"); + } + QString temp_msg = tr("%1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences").arg(col_title).arg(prefs.gui_packet_list_cached_rows_max); + mainApp->pushStatus(MainApplication::TemporaryStatus, temp_msg); + return; + } + // XXX Use updateProgress instead. We'd have to switch from std::sort to // something we can interrupt. if (!col_title.isEmpty()) { diff --git a/ui/qt/models/packet_list_record.h b/ui/qt/models/packet_list_record.h index 5afe37b7ba..2a6db75cba 100644 --- a/ui/qt/models/packet_list_record.h +++ b/ui/qt/models/packet_list_record.h @@ -45,6 +45,10 @@ public: int columnTextSize(const char *str); static void invalidateAllRecords() { col_text_cache_.clear(); } + /* In Qt 6, QCache maxCost is a qsizetype, but the QAbstractItemModel + * number of rows is still an int, so we're limited to INT_MAX anyway. + */ + static void setMaxCache(int cost) { col_text_cache_.setMaxCost(cost); } static void resetColumns(column_info *cinfo); static void resetColorization() { rows_color_ver_++; } diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp index 2f35683609..227fc139c3 100644 --- a/ui/qt/packet_list.cpp +++ b/ui/qt/packet_list.cpp @@ -252,6 +252,12 @@ PacketList::PacketList(QWidget *parent) : connect(mainApp, SIGNAL(addressResolutionChanged()), this, SLOT(redrawVisiblePacketsDontSelectCurrent())); connect(mainApp, SIGNAL(columnDataChanged()), this, SLOT(redrawVisiblePacketsDontSelectCurrent())); connect(mainApp, &MainApplication::preferencesChanged, this, [=]() { + /* The pref is a uint but QCache maxCost is a signed int (/ + * qsizetype in Qt 6). Note that QAbstractItemModel row numbers + * are ints, not unsigned ints, so we're limited to INT_MAX + * rows anyway. + */ + PacketListRecord::setMaxCache(prefs.gui_packet_list_cached_rows_max > INT_MAX ? INT_MAX : prefs.gui_packet_list_cached_rows_max); if ((bool) (prefs.gui_packet_list_sortable) != isSortingEnabled()) { setSortingEnabled(prefs.gui_packet_list_sortable); }