summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2017-02-13 01:36:58 +0100
committerPeter Wu <peter@lekensteyn.nl>2017-03-24 23:02:30 +0000
commitb4bc6c72c79fa121753cf94ae179ea73f1e911dd (patch)
treec07058dcabd11590f7e56f25e35aa4ffae45fa97 /ui
parentbff74696df796fca83df72fc7cec7cb917ebb205 (diff)
downloadwireshark-b4bc6c72c79fa121753cf94ae179ea73f1e911dd.tar.gz
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')
-rw-r--r--ui/qt/CMakeLists.txt2
-rw-r--r--ui/qt/Makefile.am2
-rw-r--r--ui/qt/voip_calls_dialog.cpp274
-rw-r--r--ui/qt/voip_calls_dialog.h12
-rw-r--r--ui/qt/voip_calls_dialog.ui52
-rw-r--r--ui/qt/voip_calls_info_model.cpp246
-rw-r--r--ui/qt/voip_calls_info_model.h96
7 files changed, 399 insertions, 285 deletions
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index f70606e1a4..d410638711 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -164,6 +164,7 @@ set(WIRESHARK_QT_HEADERS
uat_model.h
uat_tree_view.h
voip_calls_dialog.h
+ voip_calls_info_model.h
wireless_frame.h
wireshark_application.h
wireshark_dialog.h
@@ -333,6 +334,7 @@ set(WIRESHARK_QT_SRC
uat_model.cpp
uat_tree_view.cpp
voip_calls_dialog.cpp
+ voip_calls_info_model.cpp
wireless_frame.cpp
wireshark_application.cpp
wireshark_dialog.cpp
diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am
index eb1d77e7a4..a6ad706a55 100644
--- a/ui/qt/Makefile.am
+++ b/ui/qt/Makefile.am
@@ -295,6 +295,7 @@ MOC_HDRS = \
uat_model.h \
uat_tree_view.h \
voip_calls_dialog.h \
+ voip_calls_info_model.h \
wireless_frame.h \
wireshark_application.h \
wireshark_dialog.h \
@@ -576,6 +577,7 @@ WIRESHARK_QT_SRC = \
uat_model.cpp \
uat_tree_view.cpp \
voip_calls_dialog.cpp \
+ voip_calls_info_model.cpp \
wireless_frame.cpp \
wireshark_application.cpp \
wireshark_dialog.cpp
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);
}
/*
diff --git a/ui/qt/voip_calls_dialog.h b/ui/qt/voip_calls_dialog.h
index f9b9b22e69..f9d1b07092 100644
--- a/ui/qt/voip_calls_dialog.h
+++ b/ui/qt/voip_calls_dialog.h
@@ -28,6 +28,7 @@
#include "ui/voip_calls.h"
+#include "voip_calls_info_model.h"
#include "wireshark_dialog.h"
#include <QMenu>
@@ -35,7 +36,6 @@
struct _capture_file;
class QAbstractButton;
-class QTreeWidgetItem;
class SequenceInfo;
@@ -43,7 +43,6 @@ namespace Ui {
class VoipCallsDialog;
}
-class QTreeWidgetItem;
class VoipCallsDialog : public WiresharkDialog
{
Q_OBJECT
@@ -68,6 +67,8 @@ protected slots:
private:
Ui::VoipCallsDialog *ui;
+ VoipCallsInfoModel *call_infos_model_;
+ QSortFilterProxyModel *sorted_model_;
QWidget &parent_;
voip_calls_tapinfo_t tapinfo_;
@@ -84,7 +85,6 @@ private:
static void tapDraw(void *tapinfo_ptr);
void updateCalls();
- void updateWidgets();
void prepareFilter();
void showSequence();
void showPlayer();
@@ -93,14 +93,14 @@ private:
private slots:
void captureFileClosing();
- void on_callTreeWidget_itemActivated(QTreeWidgetItem *item, int);
- void on_callTreeWidget_itemSelectionChanged();
+ void on_callTreeView_activated(const QModelIndex &index);
void on_actionSelect_All_triggered();
void on_actionCopyAsCsv_triggered();
void on_actionCopyAsYaml_triggered();
void on_buttonBox_clicked(QAbstractButton *button);
void on_buttonBox_helpRequested();
- void on_todCheckBox_clicked();
+ void on_todCheckBox_stateChanged(int state);
+ void updateWidgets();
};
#endif // VOIP_CALLS_DIALOG_H
diff --git a/ui/qt/voip_calls_dialog.ui b/ui/qt/voip_calls_dialog.ui
index 0aae35bf34..1b0b4f09df 100644
--- a/ui/qt/voip_calls_dialog.ui
+++ b/ui/qt/voip_calls_dialog.ui
@@ -15,7 +15,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QTreeWidget" name="callTreeWidget">
+ <widget class="QTreeView" name="callTreeView">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
@@ -31,56 +31,6 @@
<property name="itemsExpandable">
<bool>false</bool>
</property>
- <column>
- <property name="text">
- <string>Start Time</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Stop Time</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Initial Speaker</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>From</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>To</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Protocol</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Duration</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Packets</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>State</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Comments</string>
- </property>
- </column>
</widget>
</item>
<item>
diff --git a/ui/qt/voip_calls_info_model.cpp b/ui/qt/voip_calls_info_model.cpp
new file mode 100644
index 0000000000..144829411d
--- /dev/null
+++ b/ui/qt/voip_calls_info_model.cpp
@@ -0,0 +1,246 @@
+/* voip_calls_info_model.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "voip_calls_info_model.h"
+#include <wsutil/utf8_entities.h>
+#include "qt_ui_utils.h"
+
+#include <QDateTime>
+
+VoipCallsInfoModel::VoipCallsInfoModel(QObject *parent) :
+ QAbstractTableModel(parent),
+ mTimeOfDay_(false)
+{
+}
+
+voip_calls_info_t *VoipCallsInfoModel::indexToCallInfo(const QModelIndex &index)
+{
+ return VariantPointer<voip_calls_info_t>::asPtr(index.data(Qt::UserRole));
+}
+
+QVariant VoipCallsInfoModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid()) {
+ return QVariant();
+ }
+
+ // call_info will be non-NULL since the index is valid
+ voip_calls_info_t *call_info = static_cast<voip_calls_info_t *>(callinfos_[index.row()]);
+
+ if (role == Qt::UserRole) {
+ return VariantPointer<voip_calls_info_t>::asQVariant(call_info);
+ }
+
+ if (role != Qt::DisplayRole) {
+ return QVariant();
+ }
+
+ switch ((Column) index.column()) {
+ case StartTime:
+ return timeData(&(call_info->start_fd->abs_ts), &(call_info->start_rel_ts));
+ case StopTime:
+ return timeData(&(call_info->stop_fd->abs_ts), &(call_info->stop_rel_ts));
+ case InitialSpeaker:
+ return address_to_display_qstring(&(call_info->initial_speaker));
+ case From:
+ return call_info->from_identity;
+ case To:
+ return call_info->to_identity;
+ case Protocol:
+ return ((call_info->protocol == VOIP_COMMON) && call_info->protocol_name) ?
+ call_info->protocol_name : voip_protocol_name[call_info->protocol];
+ case Duration:
+ {
+ guint callDuration = nstime_to_sec(&(call_info->stop_fd->abs_ts)) - nstime_to_sec(&(call_info->start_fd->abs_ts));
+ return 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'));
+ }
+ case Packets:
+ return call_info->npackets;
+ case State:
+ return QString(voip_call_state_name[call_info->call_state]);
+ case Comments:
+ /* Add comments based on the protocol */
+ switch (call_info->protocol) {
+ case VOIP_ISUP:
+ {
+ isup_calls_info_t *isup_info = (isup_calls_info_t *)call_info->prot_info;
+ return 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 = tr("On");
+ static const QString off_str = 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;
+ }
+ }
+ return 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:
+ return call_info->call_comment;
+ }
+ case ColumnCount:
+ g_assert_not_reached();
+ }
+ return QVariant();
+}
+
+QVariant VoipCallsInfoModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch ((Column) section) {
+ case StartTime:
+ return tr("Start Time");
+ case StopTime:
+ return tr("Stop Time");
+ case InitialSpeaker:
+ return tr("Initial Speaker");
+ case From:
+ return tr("From");
+ case To:
+ return tr("To");
+ case Protocol:
+ return tr("Protocol");
+ case Duration:
+ return tr("Duration");
+ case Packets:
+ return tr("Packets");
+ case State:
+ return tr("State");
+ case Comments:
+ return tr("Comments");
+ case ColumnCount:
+ g_assert_not_reached();
+ }
+ }
+ return QVariant();
+}
+
+int VoipCallsInfoModel::rowCount(const QModelIndex &parent) const
+{
+ // there are no children
+ if (parent.isValid()) {
+ return 0;
+ }
+
+ return callinfos_.size();
+}
+
+int VoipCallsInfoModel::columnCount(const QModelIndex &parent) const
+{
+ // there are no children
+ if (parent.isValid()) {
+ return 0;
+ }
+
+ return ColumnCount;
+}
+
+QVariant VoipCallsInfoModel::timeData(nstime_t *abs_ts, nstime_t *rel_ts) const
+{
+ if (mTimeOfDay_) {
+ return QDateTime::fromTime_t(nstime_to_sec(abs_ts)).toTimeSpec(Qt::LocalTime).toString("yyyy-MM-dd hh:mm:ss");
+ } else {
+ // XXX Pull digit count from capture file precision
+ return QString::number(nstime_to_sec(rel_ts), 'f', 6);
+ }
+}
+
+void VoipCallsInfoModel::setTimeOfDay(bool timeOfDay)
+{
+ mTimeOfDay_ = timeOfDay;
+ if (rowCount() > 0) {
+ // Update both the start and stop column in all rows.
+ emit dataChanged(index(0, StartTime), index(rowCount() - 1, StopTime));
+ }
+}
+
+void VoipCallsInfoModel::updateCalls(GQueue *callsinfos)
+{
+ if (callsinfos) {
+ GList *cur_call = g_queue_peek_nth_link(callsinfos, rowCount());
+ guint extra = g_list_length(cur_call);
+ if (extra > 0) {
+ beginInsertRows(QModelIndex(), rowCount(), rowCount() + extra - 1);
+ while (cur_call && cur_call->data) {
+ voip_calls_info_t *call_info = (voip_calls_info_t*) cur_call->data;
+ callinfos_.push_back(call_info);
+ cur_call = g_list_next(cur_call);
+ }
+ endInsertRows();
+ }
+ }
+}
+
+
+// Proxy model that allows columns to be sorted.
+VoipCallsInfoSortedModel::VoipCallsInfoSortedModel(QObject *parent) :
+ QSortFilterProxyModel(parent)
+{
+}
+
+bool VoipCallsInfoSortedModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+{
+ voip_calls_info_t *a = VoipCallsInfoModel::indexToCallInfo(source_left);
+ voip_calls_info_t *b = VoipCallsInfoModel::indexToCallInfo(source_right);
+
+ if (a && b) {
+ switch (source_left.column()) {
+ case VoipCallsInfoModel::StartTime:
+ return nstime_cmp(&(a->start_rel_ts), &(b->start_rel_ts)) < 0;
+ case VoipCallsInfoModel::StopTime:
+ return nstime_cmp(&(a->stop_rel_ts), &(b->stop_rel_ts)) < 0;
+ case VoipCallsInfoModel::InitialSpeaker:
+ return cmp_address(&(a->initial_speaker), &(b->initial_speaker)) < 0;
+ }
+ }
+
+ // fallback to string cmp on other fields
+ return QSortFilterProxyModel::lessThan(source_left, source_right);
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/voip_calls_info_model.h b/ui/qt/voip_calls_info_model.h
new file mode 100644
index 0000000000..025a3c9444
--- /dev/null
+++ b/ui/qt/voip_calls_info_model.h
@@ -0,0 +1,96 @@
+/* voip_calls_info_model.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef VOIP_CALLS_INFO_MODEL_H
+#define VOIP_CALLS_INFO_MODEL_H
+
+#include <config.h>
+#include <glib.h>
+
+#include "ui/voip_calls.h"
+#include "variant_pointer.h"
+
+#include <QAbstractTableModel>
+#include <QSortFilterProxyModel>
+
+class VoipCallsInfoModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ VoipCallsInfoModel(QObject *parent = 0);
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ void setTimeOfDay(bool timeOfDay);
+ void updateCalls(GQueue *callsinfos);
+
+ static voip_calls_info_t *indexToCallInfo(const QModelIndex &index);
+
+ enum Column
+ {
+ StartTime,
+ StopTime,
+ InitialSpeaker,
+ From,
+ To,
+ Protocol,
+ Duration,
+ Packets,
+ State,
+ Comments,
+ ColumnCount /* not an actual column, but used to find max. cols. */
+ };
+
+private:
+ QList<void *> callinfos_;
+ bool mTimeOfDay_;
+
+ QVariant timeData(nstime_t *abs_ts, nstime_t *rel_ts) const;
+};
+
+class VoipCallsInfoSortedModel : public QSortFilterProxyModel
+{
+ Q_OBJECT;
+
+public:
+ VoipCallsInfoSortedModel(QObject *parent = 0);
+
+protected:
+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
+};
+
+#endif // VOIP_CALLS_INFO_MODEL_H
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */