diff options
author | Peter Wu <peter@lekensteyn.nl> | 2017-02-13 01:36:58 +0100 |
---|---|---|
committer | Peter Wu <peter@lekensteyn.nl> | 2017-03-24 23:02:30 +0000 |
commit | b4bc6c72c79fa121753cf94ae179ea73f1e911dd (patch) | |
tree | c07058dcabd11590f7e56f25e35aa4ffae45fa97 /ui/qt/voip_calls_dialog.cpp | |
parent | bff74696df796fca83df72fc7cec7cb917ebb205 (diff) |
Qt: refactor Voip Calls Dialog into Model/View
Functional improvements:
- "Time of day" option is now propagated to the CSV/YAML export.
- The sorting order is preserved in the CSV/YAML export.
Other changes:
- Convert column name identifiers to enum.
- CSV output will now always be quoted (previously numbers like packet
count were not quoted, but since CSV has no numeric type it should
be OK to add quotes). Side-effect of model design decision.
- Instead of clearing the widgets and re-adding all calls, now it will
add new calls to the model. Not tested with a live capture, if the
column data can change, maybe a dataChanged signal is needed.
Due to this patch, it should be easier to add a filter for finding calls
easier (e.g. by From, IP, etc.).
Note: extra QList is used in the model to ensure a O(1) lookup of calls
(rather than O(n) for GQueue).
Change-Id: Ie08462aae038d9c3daf1cc7a152b948724689017
Reviewed-on: https://code.wireshark.org/review/20084
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Peter Wu <peter@lekensteyn.nl>
Diffstat (limited to 'ui/qt/voip_calls_dialog.cpp')
-rw-r--r-- | ui/qt/voip_calls_dialog.cpp | 274 |
1 files changed, 46 insertions, 228 deletions
diff --git a/ui/qt/voip_calls_dialog.cpp b/ui/qt/voip_calls_dialog.cpp index 439226e9bb..c1bb74a460 100644 --- a/ui/qt/voip_calls_dialog.cpp +++ b/ui/qt/voip_calls_dialog.cpp @@ -28,13 +28,13 @@ #include "epan/dissectors/packet-h225.h" #include "ui/rtp_stream.h" -#include <wsutil/utf8_entities.h> #include "qt_ui_utils.h" #include "rtp_player_dialog.h" #include "sequence_dialog.h" #include "stock_icon.h" #include "wireshark_application.h" +#include "voip_calls_info_model.h" #include <QClipboard> #include <QContextMenuEvent> @@ -45,180 +45,14 @@ // - Don't select on right click // - Player // - Add a screenshot to the user's guide +// - Add filter for quickly searching through list? // Bugs: // - Preparing a filter overwrites the existing filter. The GTK+ UI appends. // We'll probably have to add an "append" parameter to MainWindow::filterPackets. -// VoipCallsTreeWidgetItem -// QTreeWidgetItem subclass that allows sorting - -const int start_time_col_ = 0; -const int stop_time_col_ = 1; -const int initial_speaker_col_ = 2; -const int from_col_ = 3; -const int to_col_ = 4; -const int protocol_col_ = 5; -const int duration_col_ = 6; -const int packets_col_ = 7; -const int state_col_ = 8; -const int comments_col_ = 9; - enum { voip_calls_type_ = 1000 }; -class VoipCallsTreeWidgetItem : public QTreeWidgetItem -{ -public: - VoipCallsTreeWidgetItem(QTreeWidget *tree, voip_calls_info_t *call_info) : - QTreeWidgetItem(tree, voip_calls_type_), - call_info_(call_info), - mTimeOfDay_(false) - { - drawData(); - } - - voip_calls_info_t *callInfo() { - // XXX Not needed? We explicitly pass selected conversations to RtpPlayerDialog. -// call_info_->selected = isSelected() ? TRUE : FALSE; - return call_info_; - } - - void drawData() { - if (!call_info_) { - setText(start_time_col_, QObject::tr("Error")); - return; - } - guint callDuration = nstime_to_sec(&(call_info_->stop_fd->abs_ts)) - nstime_to_sec(&(call_info_->start_fd->abs_ts)); - - if (mTimeOfDay_) { - setText(start_time_col_, QDateTime::fromTime_t(nstime_to_sec(&(call_info_->start_fd->abs_ts))).toTimeSpec(Qt::LocalTime).toString("yyyy-MM-dd hh:mm:ss")); - setText(stop_time_col_, QDateTime::fromTime_t(nstime_to_sec(&(call_info_->stop_fd->abs_ts))).toTimeSpec(Qt::LocalTime).toString("yyyy-MM-dd hh:mm:ss")); - } else { - // XXX Pull digit count from capture file precision - setText(start_time_col_, QString::number(nstime_to_sec(&(call_info_->start_rel_ts)), 'f', 6)); - setText(stop_time_col_, QString::number(nstime_to_sec(&(call_info_->stop_rel_ts)), 'f', 6)); - } - - setText(initial_speaker_col_, address_to_display_qstring(&(call_info_->initial_speaker))); - setText(from_col_, call_info_->from_identity); - setText(to_col_, call_info_->to_identity); - setText(protocol_col_, ((call_info_->protocol == VOIP_COMMON) && call_info_->protocol_name) ? - call_info_->protocol_name : voip_protocol_name[call_info_->protocol]); - setText(duration_col_, QString("%1:%2:%3").arg(callDuration / 3600, 2, 10, QChar('0')).arg((callDuration % 3600) / 60, 2, 10, QChar('0')).arg(callDuration % 60, 2, 10, QChar('0'))); - setText(packets_col_, QString::number(call_info_->npackets)); - setText(state_col_, voip_call_state_name[call_info_->call_state]); - - /* Add comments based on the protocol */ - QString call_comments; - switch (call_info_->protocol) { - case VOIP_ISUP: - { - isup_calls_info_t *isup_info = (isup_calls_info_t *)call_info_->prot_info; - call_comments = QString("%1-%2 %3 %4-%5") - .arg(isup_info->ni) - .arg(isup_info->opc) - .arg(UTF8_RIGHTWARDS_ARROW) - .arg(isup_info->ni) - .arg(isup_info->dpc); - } - break; - case VOIP_H323: - { - h323_calls_info_t *h323_info = (h323_calls_info_t *)call_info_->prot_info; - gboolean flag = FALSE; - static const QString on_str = QObject::tr("On"); - static const QString off_str = QObject::tr("Off"); - if (call_info_->call_state == VOIP_CALL_SETUP) { - flag = h323_info->is_faststart_Setup; - } else { - if ((h323_info->is_faststart_Setup) && (h323_info->is_faststart_Proc)) { - flag = TRUE; - } - } - call_comments = QObject::tr("Tunneling: %1 Fast Start: %2") - .arg(h323_info->is_h245Tunneling ? on_str : off_str) - .arg(flag ? on_str : off_str); - } - break; - case VOIP_COMMON: - default: - call_comments = call_info_->call_comment; - break; - } - setText(comments_col_, call_comments); - } - - // Return a QString, int, double, or invalid QVariant representing the raw column data. - QVariant colData(int col) const { - if (!call_info_) { - return QVariant(); - } - - switch(col) { - case start_time_col_: - return nstime_to_sec(&call_info_->start_rel_ts); - break; - case stop_time_col_: - return nstime_to_sec(&call_info_->stop_rel_ts); - break; - case initial_speaker_col_: - case from_col_: - case to_col_: - case protocol_col_: - case duration_col_: - case state_col_: - case comments_col_: - return text(col); - break; - case packets_col_: - return call_info_->npackets; - break; - default: - break; - } - return QVariant(); - } - - bool operator< (const QTreeWidgetItem &other) const - { - if (other.type() != voip_calls_type_) return QTreeWidgetItem::operator< (other); - const VoipCallsTreeWidgetItem *other_row = static_cast<const VoipCallsTreeWidgetItem *>(&other); - - if (!call_info_ || !other_row->call_info_) { - return QTreeWidgetItem::operator< (other); - } - - switch (treeWidget()->sortColumn()) { - case start_time_col_: - return nstime_cmp(&(call_info_->start_rel_ts), &(other_row->call_info_->start_rel_ts)) < 0; - break; - case stop_time_col_: - return nstime_cmp(&(call_info_->stop_rel_ts), &(other_row->call_info_->stop_rel_ts)) < 0; - break; - case initial_speaker_col_: - return cmp_address(&(call_info_->initial_speaker), &(other_row->call_info_->initial_speaker)) < 0; - break; - case packets_col_: - return call_info_->npackets < other_row->call_info_->npackets; - break; - default: - break; - } - - // Fall back to string comparison - return QTreeWidgetItem::operator <(other); - } - - void setTimeOfDay(bool timeOfDay) - { - mTimeOfDay_ = timeOfDay; - } - -private: - voip_calls_info_t *call_info_; - bool mTimeOfDay_; -}; - VoipCallsDialog::VoipCallsDialog(QWidget &parent, CaptureFile &cf, bool all_flows) : WiresharkDialog(parent, cf), ui(new Ui::VoipCallsDialog), @@ -227,7 +61,16 @@ VoipCallsDialog::VoipCallsDialog(QWidget &parent, CaptureFile &cf, bool all_flow ui->setupUi(this); loadGeometry(parent.width() * 4 / 5, parent.height() * 2 / 3); - ui->callTreeWidget->sortByColumn(start_time_col_, Qt::AscendingOrder); + // Create the model that stores the actual data and the proxy model that is + // responsible for sorting and filtering data in the display. + call_infos_model_ = new VoipCallsInfoModel(this); + sorted_model_ = new VoipCallsInfoSortedModel(this); + sorted_model_->setSourceModel(call_infos_model_); + ui->callTreeView->setModel(sorted_model_); + + connect(ui->callTreeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(updateWidgets())); + ui->callTreeView->sortByColumn(VoipCallsInfoModel::StartTime, Qt::AscendingOrder); setWindowSubtitle(all_flows ? tr("SIP Flows") : tr("VoIP Calls")); ctx_menu_.addAction(ui->actionSelect_All); @@ -359,38 +202,24 @@ void VoipCallsDialog::tapDraw(void *tapinfo_ptr) void VoipCallsDialog::updateCalls() { - GList *cur_call = g_queue_peek_nth_link(tapinfo_.callsinfos, ui->callTreeWidget->topLevelItemCount()); - ui->callTreeWidget->setSortingEnabled(false); + ui->callTreeView->setSortingEnabled(false); // Add any missing items - while (cur_call && cur_call->data) { - voip_calls_info_t *call_info = (voip_calls_info_t*) cur_call->data; - new VoipCallsTreeWidgetItem(ui->callTreeWidget, call_info); - cur_call = g_list_next(cur_call); - } - - // Fill in the tree - QTreeWidgetItemIterator iter(ui->callTreeWidget); - while (*iter) { - VoipCallsTreeWidgetItem *vcti = static_cast<VoipCallsTreeWidgetItem*>(*iter); - vcti->setTimeOfDay(ui->todCheckBox->isChecked()); - vcti->drawData(); - ++iter; - } + call_infos_model_->updateCalls(tapinfo_.callsinfos); // Resize columns - for (int i = 0; i < ui->callTreeWidget->columnCount(); i++) { - ui->callTreeWidget->resizeColumnToContents(i); + for (int i = 0; i < call_infos_model_->columnCount(); i++) { + ui->callTreeView->resizeColumnToContents(i); } - ui->callTreeWidget->setSortingEnabled(true); + ui->callTreeView->setSortingEnabled(true); updateWidgets(); } void VoipCallsDialog::updateWidgets() { - bool selected = ui->callTreeWidget->selectedItems().count() > 0 ? true : false; + bool selected = ui->callTreeView->selectionModel()->hasSelection(); bool have_ga_items = false; if (tapinfo_.graph_analysis && tapinfo_.graph_analysis->items) { @@ -412,7 +241,7 @@ void VoipCallsDialog::updateWidgets() void VoipCallsDialog::prepareFilter() { - if (ui->callTreeWidget->selectedItems().count() < 1 || !tapinfo_.graph_analysis) { + if (!ui->callTreeView->selectionModel()->hasSelection() || !tapinfo_.graph_analysis) { return; } @@ -421,9 +250,8 @@ void VoipCallsDialog::prepareFilter() /* Build a new filter based on frame numbers */ const char *or_prepend = ""; - foreach (QTreeWidgetItem *ti, ui->callTreeWidget->selectedItems()) { - VoipCallsTreeWidgetItem *vc_ti = static_cast<VoipCallsTreeWidgetItem *>(ti); - voip_calls_info_t *call_info = vc_ti->callInfo(); + foreach (QModelIndex index, ui->callTreeView->selectionModel()->selectedIndexes()) { + voip_calls_info_t *call_info = VoipCallsInfoModel::indexToCallInfo(index); if (!call_info) { return; } @@ -536,9 +364,8 @@ void VoipCallsDialog::showSequence() if (file_closed_) return; QSet<guint16> selected_calls; - foreach (QTreeWidgetItem *ti, ui->callTreeWidget->selectedItems()) { - VoipCallsTreeWidgetItem *vc_ti = static_cast<VoipCallsTreeWidgetItem *>(ti); - voip_calls_info_t *call_info = vc_ti->callInfo(); + foreach (QModelIndex index, ui->callTreeView->selectionModel()->selectedIndexes()) { + voip_calls_info_t *call_info = VoipCallsInfoModel::indexToCallInfo(index); if (!call_info) { return; } @@ -562,17 +389,19 @@ void VoipCallsDialog::showPlayer() #ifdef QT_MULTIMEDIA_LIB RtpPlayerDialog rtp_player_dialog(*this, cap_file_); - foreach (QTreeWidgetItem *ti, ui->callTreeWidget->selectedItems()) { - VoipCallsTreeWidgetItem *vc_ti = static_cast<VoipCallsTreeWidgetItem *>(ti); + foreach (QModelIndex index, ui->callTreeView->selectionModel()->selectedIndexes()) { + voip_calls_info_t *vci = VoipCallsInfoModel::indexToCallInfo(index); + if (!vci) continue; + for (GList *rsi_entry = g_list_first(tapinfo_.rtp_stream_list); rsi_entry; rsi_entry = g_list_next(rsi_entry)) { rtp_stream_info_t *rsi = (rtp_stream_info_t *)rsi_entry->data; if (!rsi) continue; //VOIP_CALLS_DEBUG("checking call %u, start frame %u == stream call %u, start frame %u, setup frame %u", - // vc_ti->callInfo()->call_num, vc_ti->callInfo()->start_fd->num, + // vci->call_num, vci->start_fd->num, // rsi->call_num, rsi->start_fd->num, rsi->setup_frame_number); - if (vc_ti->callInfo()->call_num == rsi->call_num) { - //VOIP_CALLS_DEBUG("adding call number %u", vc_ti->callInfo()->call_num); + if (vci->call_num == (guint)rsi->call_num) { + //VOIP_CALLS_DEBUG("adding call number %u", vci->call_num); rtp_player_dialog.addRtpStream(rsi); } } @@ -588,57 +417,44 @@ QList<QVariant> VoipCallsDialog::streamRowData(int row) const { QList<QVariant> row_data; - if (row >= ui->callTreeWidget->topLevelItemCount()) { + if (row >= sorted_model_->rowCount()) { return row_data; } - for (int col = 0; col < ui->callTreeWidget->columnCount(); col++) { + for (int col = 0; col < sorted_model_->columnCount(); col++) { if (row < 0) { - row_data << ui->callTreeWidget->headerItem()->text(col); + row_data << sorted_model_->headerData(col, Qt::Horizontal); } else { - VoipCallsTreeWidgetItem *vcti = static_cast<VoipCallsTreeWidgetItem*>(ui->callTreeWidget->topLevelItem(row)); - if (vcti) { - row_data << vcti->colData(col); - } + row_data << sorted_model_->index(row, col).data(); } } return row_data; } -void VoipCallsDialog::on_callTreeWidget_itemActivated(QTreeWidgetItem *item, int) +void VoipCallsDialog::on_callTreeView_activated(const QModelIndex &index) { - VoipCallsTreeWidgetItem *vc_ti = static_cast<VoipCallsTreeWidgetItem *>(item); - voip_calls_info_t *call_info = vc_ti->callInfo(); + voip_calls_info_t *call_info = VoipCallsInfoModel::indexToCallInfo(index); if (!call_info) { return; } emit goToPacket(call_info->start_fd->num); } -void VoipCallsDialog::on_callTreeWidget_itemSelectionChanged() -{ - updateWidgets(); -} - void VoipCallsDialog::on_actionSelect_All_triggered() { - ui->callTreeWidget->selectAll(); + ui->callTreeView->selectAll(); } void VoipCallsDialog::on_actionCopyAsCsv_triggered() { QString csv; QTextStream stream(&csv, QIODevice::Text); - for (int row = -1; row < ui->callTreeWidget->topLevelItemCount(); row++) { + for (int row = -1; row < sorted_model_->rowCount(); row++) { QStringList rdsl; foreach (QVariant v, streamRowData(row)) { - if (!v.isValid()) { - rdsl << "\"\""; - } else if ((int) v.type() == (int) QMetaType::QString) { - rdsl << QString("\"%1\"").arg(v.toString()); - } else { - rdsl << v.toString(); - } + QString strval = v.toString(); + // XXX should quotes (") in strval be stripped/sanitized? + rdsl << QString("\"%1\"").arg(strval); } stream << rdsl.join(",") << endl; } @@ -650,7 +466,7 @@ void VoipCallsDialog::on_actionCopyAsYaml_triggered() QString yaml; QTextStream stream(&yaml, QIODevice::Text); stream << "---" << endl; - for (int row = -1; row < ui->callTreeWidget->topLevelItemCount(); row ++) { + for (int row = -1; row < sorted_model_->rowCount(); row++) { stream << "-" << endl; foreach (QVariant v, streamRowData(row)) { stream << " - " << v.toString() << endl; @@ -675,9 +491,11 @@ void VoipCallsDialog::on_buttonBox_helpRequested() wsApp->helpTopicAction(HELP_TELEPHONY_VOIP_CALLS_DIALOG); } -void VoipCallsDialog::on_todCheckBox_clicked() +void VoipCallsDialog::on_todCheckBox_stateChanged(int state) { - updateCalls(); + call_infos_model_->setTimeOfDay(state == Qt::Checked); + ui->callTreeView->resizeColumnToContents(VoipCallsInfoModel::StartTime); + ui->callTreeView->resizeColumnToContents(VoipCallsInfoModel::StopTime); } /* |