Qt: Add a packet diagram view.

Add a new top-level view that shows each packet as a series of diagrams
similar to what you'd find in a networking textook or an RFC.

Add proto_item_set_bits_offset_len so that we can display some diagram
fields correctly.

Bugs / to do:
  - Make this a separate dialog instead of a main window view?
  - Handle bitfields / flags

Change-Id: Iba4897a5bf1dcd73929dde6210d5483cf07f54df
Reviewed-on: https://code.wireshark.org/review/37497
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Gerald Combs 2020-05-05 11:58:52 -07:00 committed by Anders Broman
parent e846d238d7
commit 9b07412277
30 changed files with 1038 additions and 56 deletions

View File

@ -42,6 +42,7 @@ since version 3.2.0:
* Wireshark is able to decode, play and save iLBC payload on platforms where iLBC library (https://github.com/TimothyGu/libilbc) is available. * Wireshark is able to decode, play and save iLBC payload on platforms where iLBC library (https://github.com/TimothyGu/libilbc) is available.
* Decode As entries can now be copied from other profiles using a button in the dialog. * Decode As entries can now be copied from other profiles using a button in the dialog.
* sshdump can now be copied in multiple instances. Each instance will show up a different interface and will have its own profile. * sshdump can now be copied in multiple instances. Each instance will show up a different interface and will have its own profile.
* The main window now supports a packet diagram view, which shows each packet as a textbook-style diagram.
// === Removed Features and Support // === Removed Features and Support

View File

@ -66,6 +66,7 @@ other GUI programs.
. The _packet bytes pane_ (see <<ChUsePacketBytesPaneSection>>) displays the . The _packet bytes pane_ (see <<ChUsePacketBytesPaneSection>>) displays the
data from the packet selected in the packet list pane, and highlights the field data from the packet selected in the packet list pane, and highlights the field
selected in the packet details pane. selected in the packet details pane.
// . The _packet diagram pane_ (see <<ChUsePacketDiagramPaneSection>>) displays the packet selected in the packet list as a textbook-style diagram.
. The _statusbar_ (see <<ChUseStatusbarSection>>) shows some detailed . The _statusbar_ (see <<ChUseStatusbarSection>>) shows some detailed
information about the current program state and the captured data. information about the current program state and the captured data.
@ -406,6 +407,7 @@ image::wsug_graphics/ws-view-menu.png[{screenshot-attrs}]
|menu:Packet List[] ||This menu item hides or shows the packet list pane, see <<ChUsePacketListPaneSection>>. |menu:Packet List[] ||This menu item hides or shows the packet list pane, see <<ChUsePacketListPaneSection>>.
|menu:Packet Details[] ||This menu item hides or shows the packet details pane, see <<ChUsePacketDetailsPaneSection>>. |menu:Packet Details[] ||This menu item hides or shows the packet details pane, see <<ChUsePacketDetailsPaneSection>>.
|menu:Packet Bytes[] ||This menu item hides or shows the packet bytes pane, see <<ChUsePacketBytesPaneSection>>. |menu:Packet Bytes[] ||This menu item hides or shows the packet bytes pane, see <<ChUsePacketBytesPaneSection>>.
// |menu:Packet Diagram[] ||This menu item hides or shows the packet diagram pane. See <<ChUsePacketDiagramPaneSection>>.
|menu:Time Display Format[Date and Time of Day: 1970-01-01 01:02:03.123456]|| Selecting this tells Wireshark to display the time stamps in date and time of day format, see <<ChWorkTimeFormatsSection>>. |menu:Time Display Format[Date and Time of Day: 1970-01-01 01:02:03.123456]|| Selecting this tells Wireshark to display the time stamps in date and time of day format, see <<ChWorkTimeFormatsSection>>.
The fields “Time of Day”, “Date and Time of Day”, “Seconds Since Beginning of The fields “Time of Day”, “Date and Time of Day”, “Seconds Since Beginning of
@ -1042,6 +1044,25 @@ The context menu (right mouse click) of the tab labels will show a list of all
available pages. This can be helpful if the size in the pane is too small for available pages. This can be helpful if the size in the pane is too small for
all the tab labels. all the tab labels.
[[ChUsePacketDiagramPaneSection]]
////
=== The “Packet Diagram” Pane
The packet diagram pane shows the current packet (selected in the “Packet List”
pane) as a diagram, similar to ones used in textbooks and IETF RFCs.
[[ChUseWiresharkDiagramPane]]
.The “Packet Diagram” pane
image::wsug_graphics/ws-diagram-pane.png[{screenshot-attrs}]
This pane shows the protocols and top-level protocol fields of the packet selected in the “Packet List” pane as a series of diagrams.
There is a context menu (right mouse click) available.
For details see <<ChWorkPacketDiagramPanePopUpMenu>>.
////
[[ChUseStatusbarSection]] [[ChUseStatusbarSection]]
=== The Statusbar === The Statusbar

View File

@ -1860,7 +1860,7 @@ dissect_ip_v4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void*
ti = proto_tree_add_item(tree, proto_ip, tvb, offset, hlen, ENC_NA); ti = proto_tree_add_item(tree, proto_ip, tvb, offset, hlen, ENC_NA);
ip_tree = proto_item_add_subtree(ti, ett_ip); ip_tree = proto_item_add_subtree(ti, ett_ip);
tf = proto_tree_add_item(ip_tree, hf_ip_version, tvb, offset, 1, ENC_NA); tf = proto_tree_add_bits_item(ip_tree, hf_ip_version, tvb, 0, 4, ENC_NA);
if (iph->ip_ver != 4) { if (iph->ip_ver != 4) {
col_add_fstr(pinfo->cinfo, COL_INFO, col_add_fstr(pinfo->cinfo, COL_INFO,
"Bogus IPv4 version (%u, must be 4)", iph->ip_ver); "Bogus IPv4 version (%u, must be 4)", iph->ip_ver);
@ -1997,7 +1997,6 @@ dissect_ip_v4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void*
if (ip_security_flag) { if (ip_security_flag) {
/* RFC 3514 - The Security Flag in the IPv4 Header (April Fool's joke) */ /* RFC 3514 - The Security Flag in the IPv4 Header (April Fool's joke) */
proto_item *sf;
static int * const ip_flags_evil[] = { static int * const ip_flags_evil[] = {
&hf_ip_flags_sf, &hf_ip_flags_sf,
&hf_ip_flags_df, &hf_ip_flags_df,
@ -2005,10 +2004,10 @@ dissect_ip_v4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void*
NULL NULL
}; };
sf = proto_tree_add_bitmask_with_flags(ip_tree, tvb, offset + 6, hf_ip_flags, tf = proto_tree_add_bitmask_with_flags(ip_tree, tvb, offset + 6, hf_ip_flags,
ett_ip_flags, ip_flags_evil, ENC_BIG_ENDIAN, BMT_NO_FALSE | BMT_NO_TFS | BMT_NO_INT); ett_ip_flags, ip_flags_evil, ENC_BIG_ENDIAN, BMT_NO_FALSE | BMT_NO_TFS | BMT_NO_INT);
if (iph->ip_off & IP_RF) { if (iph->ip_off & IP_RF) {
expert_add_info(pinfo, sf, &ei_ip_evil_packet); expert_add_info(pinfo, tf, &ei_ip_evil_packet);
} }
} else { } else {
static int * const ip_flags[] = { static int * const ip_flags[] = {
@ -2017,12 +2016,13 @@ dissect_ip_v4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void*
&hf_ip_flags_mf, &hf_ip_flags_mf,
NULL NULL
}; };
proto_tree_add_bitmask_with_flags(ip_tree, tvb, offset + 6, hf_ip_flags, tf = proto_tree_add_bitmask_with_flags(ip_tree, tvb, offset + 6, hf_ip_flags,
ett_ip_flags, ip_flags, ENC_BIG_ENDIAN, BMT_NO_FALSE | BMT_NO_TFS | BMT_NO_INT); ett_ip_flags, ip_flags, ENC_BIG_ENDIAN, BMT_NO_FALSE | BMT_NO_TFS | BMT_NO_INT);
} }
proto_item_set_bits_offset_len(tf, 0, 3);
proto_tree_add_uint(ip_tree, hf_ip_frag_offset, tvb, offset + 6, 2, (iph->ip_off & IP_OFFSET)*8); tf = proto_tree_add_uint(ip_tree, hf_ip_frag_offset, tvb, offset + 6, 2, (iph->ip_off & IP_OFFSET)*8);
proto_item_set_bits_offset_len(tf, 3, 13);
iph->ip_ttl = tvb_get_guint8(tvb, offset + 8); iph->ip_ttl = tvb_get_guint8(tvb, offset + 8);
if (tree) { if (tree) {
@ -2334,7 +2334,7 @@ dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
col_clear(pinfo->cinfo, COL_INFO); col_clear(pinfo->cinfo, COL_INFO);
col_add_fstr(pinfo->cinfo, COL_INFO, "Bogus IP version (%u)", version); col_add_fstr(pinfo->cinfo, COL_INFO, "Bogus IP version (%u)", version);
ip_tree = proto_item_add_subtree(ti, ett_ip); ip_tree = proto_item_add_subtree(ti, ett_ip);
tf = proto_tree_add_item(ip_tree, hf_ip_version, tvb, 0, 1, ENC_NA); tf = proto_tree_add_bits_item(ip_tree, hf_ip_version, tvb, 0, 4, ENC_NA);
expert_add_info(pinfo, tf, &ei_ip_bogus_ip_version); expert_add_info(pinfo, tf, &ei_ip_bogus_ip_version);
return 1; return 1;
} }
@ -2438,7 +2438,7 @@ proto_register_ip(void)
static hf_register_info hf[] = { static hf_register_info hf[] = {
{ &hf_ip_version, { &hf_ip_version,
{ "Version", "ip.version", FT_UINT8, BASE_DEC, { "Version", "ip.version", FT_UINT8, BASE_DEC,
NULL, 0xF0, NULL, HFILL }}, NULL, 0x00, NULL, HFILL }},
{ &hf_ip_hdr_len, { &hf_ip_hdr_len,
{ "Header Length", "ip.hdr_len", FT_UINT8, BASE_DEC, { "Header Length", "ip.hdr_len", FT_UINT8, BASE_DEC,

View File

@ -6439,6 +6439,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
"%u bytes (%u)", tcph->th_hlen, tcph->th_hlen>>2); "%u bytes (%u)", tcph->th_hlen, tcph->th_hlen>>2);
tf = proto_tree_add_uint_format(tcp_tree, hf_tcp_flags, tvb, offset + 12, 2, tf = proto_tree_add_uint_format(tcp_tree, hf_tcp_flags, tvb, offset + 12, 2,
tcph->th_flags, "Flags: 0x%03x (%s)", tcph->th_flags, flags_str); tcph->th_flags, "Flags: 0x%03x (%s)", tcph->th_flags, flags_str);
proto_item_set_bits_offset_len(tf, 4, 12);
field_tree = proto_item_add_subtree(tf, ett_tcp_flags); field_tree = proto_item_add_subtree(tf, ett_tcp_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_res, tvb, offset + 12, 1, tcph->th_flags); proto_tree_add_boolean(field_tree, hf_tcp_flags_res, tvb, offset + 12, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_ns, tvb, offset + 12, 1, tcph->th_flags); proto_tree_add_boolean(field_tree, hf_tcp_flags_ns, tvb, offset + 12, 1, tcph->th_flags);

View File

@ -122,6 +122,7 @@ static const enum_val_t gui_layout_content[] = {
{"PLIST", "PLIST", 1}, {"PLIST", "PLIST", 1},
{"PDETAILS", "PDETAILS", 2}, {"PDETAILS", "PDETAILS", 2},
{"PBYTES", "PBYTES", 3}, {"PBYTES", "PBYTES", 3},
{"PDIAGRAM", "PDIAGRAM", 4},
{NULL, NULL, -1} {NULL, NULL, -1}
}; };

View File

@ -91,7 +91,8 @@ typedef enum {
layout_pane_content_none, layout_pane_content_none,
layout_pane_content_plist, layout_pane_content_plist,
layout_pane_content_pdetails, layout_pane_content_pdetails,
layout_pane_content_pbytes layout_pane_content_pbytes,
layout_pane_content_pdiagram,
} layout_pane_content_e; } layout_pane_content_e;
/* /*

View File

@ -6827,6 +6827,15 @@ proto_item_get_len(const proto_item *pi)
return fi ? fi->length : -1; return fi ? fi->length : -1;
} }
void
proto_item_set_bits_offset_len(proto_item *ti, int bits_offset, int bits_len) {
if (!ti) {
return;
}
FI_SET_FLAG(PNODE_FINFO(ti), FI_BITS_OFFSET(bits_offset));
FI_SET_FLAG(PNODE_FINFO(ti), FI_BITS_SIZE(bits_len));
}
char * char *
proto_item_get_display_repr(wmem_allocator_t *scope, proto_item *pi) proto_item_get_display_repr(wmem_allocator_t *scope, proto_item *pi)
{ {

View File

@ -991,7 +991,7 @@ static inline gboolean proto_item_is_url(proto_item *ti) {
#define PROTO_ITEM_IS_URL(ti) proto_item_is_url((ti)) #define PROTO_ITEM_IS_URL(ti) proto_item_is_url((ti))
/** Mark this protocol field as a URL /** Mark this protocol field as a URL
* @param ti The item to mark as a URL. May be NULL. * @param ti The item to mark as a URL. May be NULL.
*/ */
static inline void proto_item_set_url(proto_item *ti) { static inline void proto_item_set_url(proto_item *ti) {
if (ti) { if (ti) {
@ -1107,6 +1107,13 @@ WS_DLL_PUBLIC void proto_item_set_end(proto_item *pi, tvbuff_t *tvb, gint end);
@return the current length */ @return the current length */
WS_DLL_PUBLIC int proto_item_get_len(const proto_item *pi); WS_DLL_PUBLIC int proto_item_get_len(const proto_item *pi);
/** Set the bit offset and length for the specified proto_item.
* @param ti The item to set.
* @param bits_offset The number of bits from the beginning of the field.
* @param bits_offset The new length in bits.
*/
void proto_item_set_bits_offset_len(proto_item *ti, int bits_offset, int bits_len);
/** Get display representation of a proto_item. /** Get display representation of a proto_item.
* Can be used, for example, to append that to the parent item of * Can be used, for example, to append that to the parent item of
* that item. * that item.

View File

@ -17,3 +17,4 @@ mkdir ~/.config/wireshark/profiles/ctest
TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T ek -r test/captures/dhcp.pcap > test/baseline/dhcp.ek TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T ek -r test/captures/dhcp.pcap > test/baseline/dhcp.ek
TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T json -r test/captures/dhcp.pcap > test/baseline/dhcp.json TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T json -r test/captures/dhcp.pcap > test/baseline/dhcp.json
TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T jsonraw -r test/captures/dhcp.pcap > test/baseline/dhcp.jsonraw TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T jsonraw -r test/captures/dhcp.pcap > test/baseline/dhcp.jsonraw
TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T ek -r test/captures/dhcp.pcap -x > test/baseline/dhcp-raw.ek

File diff suppressed because one or more lines are too long

View File

@ -288,10 +288,10 @@
], ],
"ip": { "ip": {
"ip.version_raw": [ "ip.version_raw": [
"4", "45",
14, 14,
1, 1,
240, 0,
4 4
], ],
"ip.hdr_len_raw": [ "ip.hdr_len_raw": [
@ -1162,10 +1162,10 @@
], ],
"ip": { "ip": {
"ip.version_raw": [ "ip.version_raw": [
"4", "45",
14, 14,
1, 1,
240, 0,
4 4
], ],
"ip.hdr_len_raw": [ "ip.hdr_len_raw": [
@ -2054,10 +2054,10 @@
], ],
"ip": { "ip": {
"ip.version_raw": [ "ip.version_raw": [
"4", "45",
14, 14,
1, 1,
240, 0,
4 4
], ],
"ip.hdr_len_raw": [ "ip.hdr_len_raw": [
@ -2958,10 +2958,10 @@
], ],
"ip": { "ip": {
"ip.version_raw": [ "ip.version_raw": [
"4", "45",
14, 14,
1, 1,
240, 0,
4 4
], ],
"ip.hdr_len_raw": [ "ip.hdr_len_raw": [

View File

@ -181,6 +181,7 @@ set(WIRESHARK_QT_HEADERS
mtp3_summary_dialog.h mtp3_summary_dialog.h
multicast_statistics_dialog.h multicast_statistics_dialog.h
packet_comment_dialog.h packet_comment_dialog.h
packet_diagram.h
packet_dialog.h packet_dialog.h
packet_format_group_box.h packet_format_group_box.h
packet_list.h packet_list.h
@ -406,6 +407,7 @@ set(WIRESHARK_QT_SRC
manage_interfaces_dialog.cpp manage_interfaces_dialog.cpp
module_preferences_scroll_area.cpp module_preferences_scroll_area.cpp
packet_comment_dialog.cpp packet_comment_dialog.cpp
packet_diagram.cpp
packet_dialog.cpp packet_dialog.cpp
packet_format_group_box.cpp packet_format_group_box.cpp
packet_list.cpp packet_list.cpp

View File

@ -1462,7 +1462,7 @@ void IOGraphDialog::on_buttonBox_helpRequested()
wsApp->helpTopicAction(HELP_STATS_IO_GRAPH_DIALOG); wsApp->helpTopicAction(HELP_STATS_IO_GRAPH_DIALOG);
} }
// XXX - Copied from tcp_stream_dialog. This should be common code. // XXX - We have similar code in tcp_stream_dialog and packet_diagram. Should this be a common routine?
void IOGraphDialog::on_buttonBox_accepted() void IOGraphDialog::on_buttonBox_accepted()
{ {
QString file_name, extension; QString file_name, extension;

View File

@ -106,6 +106,9 @@ void LayoutPreferencesFrame::updateWidgets()
case layout_pane_content_pbytes: case layout_pane_content_pbytes:
ui->pane1PacketBytesRadioButton->setChecked(true); ui->pane1PacketBytesRadioButton->setChecked(true);
break; break;
case layout_pane_content_pdiagram:
ui->pane1PacketDiagramRadioButton->setChecked(true);
break;
case layout_pane_content_none: case layout_pane_content_none:
ui->pane1NoneRadioButton->setChecked(true); ui->pane1NoneRadioButton->setChecked(true);
break; break;
@ -121,6 +124,9 @@ void LayoutPreferencesFrame::updateWidgets()
case layout_pane_content_pbytes: case layout_pane_content_pbytes:
ui->pane2PacketBytesRadioButton->setChecked(true); ui->pane2PacketBytesRadioButton->setChecked(true);
break; break;
case layout_pane_content_pdiagram:
ui->pane2PacketDiagramRadioButton->setChecked(true);
break;
case layout_pane_content_none: case layout_pane_content_none:
ui->pane2NoneRadioButton->setChecked(true); ui->pane2NoneRadioButton->setChecked(true);
break; break;
@ -136,6 +142,9 @@ void LayoutPreferencesFrame::updateWidgets()
case layout_pane_content_pbytes: case layout_pane_content_pbytes:
ui->pane3PacketBytesRadioButton->setChecked(true); ui->pane3PacketBytesRadioButton->setChecked(true);
break; break;
case layout_pane_content_pdiagram:
ui->pane3PacketDiagramRadioButton->setChecked(true);
break;
case layout_pane_content_none: case layout_pane_content_none:
ui->pane3NoneRadioButton->setChecked(true); ui->pane3NoneRadioButton->setChecked(true);
break; break;
@ -208,6 +217,16 @@ void LayoutPreferencesFrame::on_pane1PacketBytesRadioButton_toggled(bool checked
ui->pane3NoneRadioButton->click(); ui->pane3NoneRadioButton->click();
} }
void LayoutPreferencesFrame::on_pane1PacketDiagramRadioButton_toggled(bool checked)
{
if (!checked) return;
prefs_set_enum_value(pref_layout_content_1_, layout_pane_content_pdiagram, pref_stashed);
if (ui->pane2PacketDiagramRadioButton->isChecked())
ui->pane2NoneRadioButton->click();
if (ui->pane3PacketDiagramRadioButton->isChecked())
ui->pane3NoneRadioButton->click();
}
void LayoutPreferencesFrame::on_pane1NoneRadioButton_toggled(bool checked) void LayoutPreferencesFrame::on_pane1NoneRadioButton_toggled(bool checked)
{ {
if (!checked) return; if (!checked) return;
@ -244,6 +263,16 @@ void LayoutPreferencesFrame::on_pane2PacketBytesRadioButton_toggled(bool checked
ui->pane3NoneRadioButton->click(); ui->pane3NoneRadioButton->click();
} }
void LayoutPreferencesFrame::on_pane2PacketDiagramRadioButton_toggled(bool checked)
{
if (!checked) return;
prefs_set_enum_value(pref_layout_content_2_, layout_pane_content_pdiagram, pref_stashed);
if (ui->pane1PacketDiagramRadioButton->isChecked())
ui->pane1NoneRadioButton->click();
if (ui->pane3PacketDiagramRadioButton->isChecked())
ui->pane3NoneRadioButton->click();
}
void LayoutPreferencesFrame::on_pane2NoneRadioButton_toggled(bool checked) void LayoutPreferencesFrame::on_pane2NoneRadioButton_toggled(bool checked)
{ {
if (!checked) return; if (!checked) return;
@ -280,6 +309,16 @@ void LayoutPreferencesFrame::on_pane3PacketBytesRadioButton_toggled(bool checked
ui->pane2NoneRadioButton->click(); ui->pane2NoneRadioButton->click();
} }
void LayoutPreferencesFrame::on_pane3PacketDiagramRadioButton_toggled(bool checked)
{
if (!checked) return;
prefs_set_enum_value(pref_layout_content_3_, layout_pane_content_pdiagram, pref_stashed);
if (ui->pane1PacketDiagramRadioButton->isChecked())
ui->pane1NoneRadioButton->click();
if (ui->pane2PacketDiagramRadioButton->isChecked())
ui->pane2NoneRadioButton->click();
}
void LayoutPreferencesFrame::on_pane3NoneRadioButton_toggled(bool checked) void LayoutPreferencesFrame::on_pane3NoneRadioButton_toggled(bool checked)
{ {
if (!checked) return; if (!checked) return;

View File

@ -54,14 +54,17 @@ private slots:
void on_pane1PacketListRadioButton_toggled(bool checked); void on_pane1PacketListRadioButton_toggled(bool checked);
void on_pane1PacketDetailsRadioButton_toggled(bool checked); void on_pane1PacketDetailsRadioButton_toggled(bool checked);
void on_pane1PacketBytesRadioButton_toggled(bool checked); void on_pane1PacketBytesRadioButton_toggled(bool checked);
void on_pane1PacketDiagramRadioButton_toggled(bool checked);
void on_pane1NoneRadioButton_toggled(bool checked); void on_pane1NoneRadioButton_toggled(bool checked);
void on_pane2PacketListRadioButton_toggled(bool checked); void on_pane2PacketListRadioButton_toggled(bool checked);
void on_pane2PacketDetailsRadioButton_toggled(bool checked); void on_pane2PacketDetailsRadioButton_toggled(bool checked);
void on_pane2PacketBytesRadioButton_toggled(bool checked); void on_pane2PacketBytesRadioButton_toggled(bool checked);
void on_pane2PacketDiagramRadioButton_toggled(bool checked);
void on_pane2NoneRadioButton_toggled(bool checked); void on_pane2NoneRadioButton_toggled(bool checked);
void on_pane3PacketListRadioButton_toggled(bool checked); void on_pane3PacketListRadioButton_toggled(bool checked);
void on_pane3PacketDetailsRadioButton_toggled(bool checked); void on_pane3PacketDetailsRadioButton_toggled(bool checked);
void on_pane3PacketBytesRadioButton_toggled(bool checked); void on_pane3PacketBytesRadioButton_toggled(bool checked);
void on_pane3PacketDiagramRadioButton_toggled(bool checked);
void on_pane3NoneRadioButton_toggled(bool checked); void on_pane3NoneRadioButton_toggled(bool checked);
void on_restoreButtonBox_clicked(QAbstractButton *button); void on_restoreButtonBox_clicked(QAbstractButton *button);
void on_packetListSeparatorCheckBox_toggled(bool checked); void on_packetListSeparatorCheckBox_toggled(bool checked);

View File

@ -212,6 +212,16 @@
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item>
<widget class="QRadioButton" name="pane1PacketDiagramRadioButton">
<property name="text">
<string>Packet Diagram</string>
</property>
<attribute name="buttonGroup">
<string notr="true">pane1ButtonGroup</string>
</attribute>
</widget>
</item>
<item> <item>
<widget class="QRadioButton" name="pane1NoneRadioButton"> <widget class="QRadioButton" name="pane1NoneRadioButton">
<property name="text"> <property name="text">
@ -263,6 +273,16 @@
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item>
<widget class="QRadioButton" name="pane2PacketDiagramRadioButton">
<property name="text">
<string>Packet Diagram</string>
</property>
<attribute name="buttonGroup">
<string notr="true">pane2ButtonGroup</string>
</attribute>
</widget>
</item>
<item> <item>
<widget class="QRadioButton" name="pane2NoneRadioButton"> <widget class="QRadioButton" name="pane2NoneRadioButton">
<property name="text"> <property name="text">
@ -314,6 +334,16 @@
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item>
<widget class="QRadioButton" name="pane3PacketDiagramRadioButton">
<property name="text">
<string>Packet Diagram</string>
</property>
<attribute name="buttonGroup">
<string notr="true">pane3ButtonGroup</string>
</attribute>
</widget>
</item>
<item> <item>
<widget class="QRadioButton" name="pane3NoneRadioButton"> <widget class="QRadioButton" name="pane3NoneRadioButton">
<property name="text"> <property name="text">

View File

@ -57,6 +57,7 @@ DIAG_ON(frame-larger-than=)
#include "funnel_statistics.h" #include "funnel_statistics.h"
#include "import_text_dialog.h" #include "import_text_dialog.h"
#include "interface_toolbar.h" #include "interface_toolbar.h"
#include "packet_diagram.h"
#include "packet_list.h" #include "packet_list.h"
#include "proto_tree.h" #include "proto_tree.h"
#include "simple_dialog.h" #include "simple_dialog.h"
@ -466,20 +467,23 @@ MainWindow::MainWindow(QWidget *parent) :
packet_list_->setProtoTree(proto_tree_); packet_list_->setProtoTree(proto_tree_);
packet_list_->installEventFilter(this); packet_list_->installEventFilter(this);
packet_diagram_ = new PacketDiagram(&master_split_);
welcome_page_ = main_ui_->welcomePage; welcome_page_ = main_ui_->welcomePage;
connect(proto_tree_, SIGNAL(fieldSelected(FieldInformation *)), connect(proto_tree_, &ProtoTree::fieldSelected,
this, SIGNAL(fieldSelected(FieldInformation *))); this, &MainWindow::fieldSelected);
connect(packet_list_, SIGNAL(fieldSelected(FieldInformation *)), connect(packet_list_, &PacketList::fieldSelected,
this, SIGNAL(fieldSelected(FieldInformation *))); this, &MainWindow::fieldSelected);
connect(this, SIGNAL(fieldSelected(FieldInformation *)), connect(this, &MainWindow::fieldSelected,
this, SLOT(setMenusForSelectedTreeRow(FieldInformation *))); this, &MainWindow::setMenusForSelectedTreeRow);
connect(this, SIGNAL(fieldSelected(FieldInformation *)), connect(this, &MainWindow::fieldSelected,
main_ui_->statusBar, SLOT(selectedFieldChanged(FieldInformation *))); main_ui_->statusBar, &MainStatusBar::selectedFieldChanged);
connect(this, SIGNAL(fieldHighlight(FieldInformation *)), connect(this, &MainWindow::fieldHighlight,
main_ui_->statusBar, SLOT(highlightedFieldChanged(FieldInformation *))); main_ui_->statusBar, &MainStatusBar::highlightedFieldChanged);
connect(wsApp, SIGNAL(captureActive(int)), this, SIGNAL(captureActive(int))); connect(wsApp, &WiresharkApplication::captureActive,
this, &MainWindow::captureActive);
byte_view_tab_ = new ByteViewTab(&master_split_); byte_view_tab_ = new ByteViewTab(&master_split_);
@ -543,14 +547,14 @@ MainWindow::MainWindow(QWidget *parent) :
filter_expression_toolbar_, &FilterExpressionToolBar::filterExpressionsChanged); filter_expression_toolbar_, &FilterExpressionToolBar::filterExpressionsChanged);
/* Connect change of capture file */ /* Connect change of capture file */
connect(this, SIGNAL(setCaptureFile(capture_file*)), connect(this, &MainWindow::setCaptureFile,
main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*))); main_ui_->searchFrame, &SearchFrame::setCaptureFile);
connect(this, SIGNAL(setCaptureFile(capture_file*)), connect(this, &MainWindow::setCaptureFile,
main_ui_->statusBar, SLOT(setCaptureFile(capture_file*))); main_ui_->statusBar, &MainStatusBar::setCaptureFile);
connect(this, SIGNAL(setCaptureFile(capture_file*)), connect(this, &MainWindow::setCaptureFile,
packet_list_, SLOT(setCaptureFile(capture_file*))); packet_list_, &PacketList::setCaptureFile);
connect(this, SIGNAL(setCaptureFile(capture_file*)), connect(this, &MainWindow::setCaptureFile,
proto_tree_, SLOT(setCaptureFile(capture_file*))); proto_tree_, &ProtoTree::setCaptureFile);
connect(wsApp, SIGNAL(zoomMonospaceFont(QFont)), connect(wsApp, SIGNAL(zoomMonospaceFont(QFont)),
packet_list_, SLOT(setMonospaceFont(QFont))); packet_list_, SLOT(setMonospaceFont(QFont)));
@ -701,6 +705,7 @@ QMenu *MainWindow::createPopupMenu()
menu->addAction(main_ui_->actionViewPacketList); menu->addAction(main_ui_->actionViewPacketList);
menu->addAction(main_ui_->actionViewPacketDetails); menu->addAction(main_ui_->actionViewPacketDetails);
menu->addAction(main_ui_->actionViewPacketBytes); menu->addAction(main_ui_->actionViewPacketBytes);
menu->addAction(main_ui_->actionViewPacketDiagram);
return menu; return menu;
} }
@ -2058,6 +2063,7 @@ void MainWindow::initShowHideMainWidgets()
shmw_actions[main_ui_->actionViewPacketList] = packet_list_; shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_; shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_; shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
shmw_actions[main_ui_->actionViewPacketDiagram] = packet_diagram_;
foreach(QAction *shmwa, shmw_actions.keys()) { foreach(QAction *shmwa, shmw_actions.keys()) {
shmwa->setData(QVariant::fromValue(shmw_actions[shmwa])); shmwa->setData(QVariant::fromValue(shmw_actions[shmwa]));

View File

@ -85,6 +85,7 @@ class FilterDialog;
class FunnelStatistics; class FunnelStatistics;
class WelcomePage; class WelcomePage;
class PacketCommentDialog; class PacketCommentDialog;
class PacketDiagram;
class PacketList; class PacketList;
class ProtoTree; class ProtoTree;
#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) #if defined(HAVE_LIBNL) && defined(HAVE_NL80211)
@ -191,7 +192,8 @@ private:
// probably be full-on values instead of pointers. // probably be full-on values instead of pointers.
PacketList *packet_list_; PacketList *packet_list_;
ProtoTree *proto_tree_; ProtoTree *proto_tree_;
ByteViewTab * byte_view_tab_; ByteViewTab *byte_view_tab_;
PacketDiagram *packet_diagram_;
QWidget *previous_focus_; QWidget *previous_focus_;
FileSetDialog *file_set_dialog_; FileSetDialog *file_set_dialog_;
QWidget empty_pane_; QWidget empty_pane_;

View File

@ -362,6 +362,7 @@
<addaction name="actionViewPacketList"/> <addaction name="actionViewPacketList"/>
<addaction name="actionViewPacketDetails"/> <addaction name="actionViewPacketDetails"/>
<addaction name="actionViewPacketBytes"/> <addaction name="actionViewPacketBytes"/>
<addaction name="actionViewPacketDiagram"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="menuTime_Display_Format"/> <addaction name="menuTime_Display_Format"/>
<addaction name="menuName_Resolution"/> <addaction name="menuName_Resolution"/>
@ -2473,6 +2474,20 @@
<string>Show or hide the packet bytes</string> <string>Show or hide the packet bytes</string>
</property> </property>
</action> </action>
<action name="actionViewPacketDiagram">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Packet &amp;Diagram</string>
</property>
<property name="toolTip">
<string>Show or hide the packet diagram</string>
</property>
</action>
<action name="actionViewInternalsConversationHashTables"> <action name="actionViewInternalsConversationHashTables">
<property name="text"> <property name="text">
<string>&amp;Conversation Hash Tables</string> <string>&amp;Conversation Hash Tables</string>

View File

@ -24,9 +24,10 @@
#include <QAction> #include <QAction>
#include <QToolBar> #include <QToolBar>
#include <ui/qt/packet_list.h>
#include <ui/qt/proto_tree.h>
#include <ui/qt/byte_view_tab.h> #include <ui/qt/byte_view_tab.h>
#include <ui/qt/packet_list.h>
#include <ui/qt/packet_diagram.h>
#include <ui/qt/proto_tree.h>
/* /*
* The generated Ui_MainWindow::setupUi() can grow larger than our configured limit, * The generated Ui_MainWindow::setupUi() can grow larger than our configured limit,
@ -56,6 +57,8 @@ QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
return proto_tree_; return proto_tree_;
case layout_pane_content_pbytes: case layout_pane_content_pbytes:
return byte_view_tab_; return byte_view_tab_;
case layout_pane_content_pdiagram:
return packet_diagram_;
default: default:
g_assert_not_reached(); g_assert_not_reached();
return NULL; return NULL;
@ -76,7 +79,8 @@ void MainWindow::layoutPanes()
<< prefs.gui_layout_content_3 << prefs.gui_layout_content_3
<< recent.packet_list_show << recent.packet_list_show
<< recent.tree_view_show << recent.tree_view_show
<< recent.byte_view_show; << recent.byte_view_show
<< recent.packet_diagram_show;
if (cur_layout_ == new_layout) return; if (cur_layout_ == new_layout) return;
@ -84,10 +88,11 @@ void MainWindow::layoutPanes()
// Reparent all widgets and add them back in the proper order below. // Reparent all widgets and add them back in the proper order below.
// This hides each widget as well. // This hides each widget as well.
packet_list_->freeze(); // Clears tree and byte view tabs. packet_list_->freeze(); // Clears tree, byte view tabs, and diagram.
packet_list_->setParent(main_ui_->mainStack); packet_list_->setParent(main_ui_->mainStack);
proto_tree_->setParent(main_ui_->mainStack); proto_tree_->setParent(main_ui_->mainStack);
byte_view_tab_->setParent(main_ui_->mainStack); byte_view_tab_->setParent(main_ui_->mainStack);
packet_diagram_->setParent(main_ui_->mainStack);
empty_pane_.setParent(main_ui_->mainStack); empty_pane_.setParent(main_ui_->mainStack);
extra_split_.setParent(main_ui_->mainStack); extra_split_.setParent(main_ui_->mainStack);
@ -156,6 +161,7 @@ void MainWindow::layoutPanes()
packet_list_->setVisible(ms_children.contains(packet_list_) && recent.packet_list_show); packet_list_->setVisible(ms_children.contains(packet_list_) && recent.packet_list_show);
proto_tree_->setVisible(ms_children.contains(proto_tree_) && recent.tree_view_show); proto_tree_->setVisible(ms_children.contains(proto_tree_) && recent.tree_view_show);
byte_view_tab_->setVisible(ms_children.contains(byte_view_tab_) && recent.byte_view_show); byte_view_tab_->setVisible(ms_children.contains(byte_view_tab_) && recent.byte_view_show);
packet_diagram_->setVisible(ms_children.contains(packet_diagram_) && recent.packet_diagram_show);
// Show the packet list here to prevent pending resize events changing columns // Show the packet list here to prevent pending resize events changing columns
// when the packet list is set as current widget for the first time. // when the packet list is set as current widget for the first time.

View File

@ -127,6 +127,7 @@ DIAG_ON(frame-larger-than=)
#include "mtp3_summary_dialog.h" #include "mtp3_summary_dialog.h"
#include "multicast_statistics_dialog.h" #include "multicast_statistics_dialog.h"
#include "packet_comment_dialog.h" #include "packet_comment_dialog.h"
#include "packet_diagram.h"
#include "packet_dialog.h" #include "packet_dialog.h"
#include "packet_list.h" #include "packet_list.h"
#include "credentials_dialog.h" #include "credentials_dialog.h"
@ -360,6 +361,7 @@ void MainWindow::updatePreferenceActions()
main_ui_->actionViewPacketList->setEnabled(prefs_has_layout_pane_content(layout_pane_content_plist)); main_ui_->actionViewPacketList->setEnabled(prefs_has_layout_pane_content(layout_pane_content_plist));
main_ui_->actionViewPacketDetails->setEnabled(prefs_has_layout_pane_content(layout_pane_content_pdetails)); main_ui_->actionViewPacketDetails->setEnabled(prefs_has_layout_pane_content(layout_pane_content_pdetails));
main_ui_->actionViewPacketBytes->setEnabled(prefs_has_layout_pane_content(layout_pane_content_pbytes)); main_ui_->actionViewPacketBytes->setEnabled(prefs_has_layout_pane_content(layout_pane_content_pbytes));
main_ui_->actionViewPacketDiagram->setEnabled(prefs_has_layout_pane_content(layout_pane_content_pdiagram));
main_ui_->actionViewNameResolutionPhysical->setChecked(gbl_resolv_flags.mac_name); main_ui_->actionViewNameResolutionPhysical->setChecked(gbl_resolv_flags.mac_name);
main_ui_->actionViewNameResolutionNetwork->setChecked(gbl_resolv_flags.network_name); main_ui_->actionViewNameResolutionNetwork->setChecked(gbl_resolv_flags.network_name);
@ -378,6 +380,7 @@ void MainWindow::updateRecentActions()
main_ui_->actionViewPacketList->setChecked(recent.packet_list_show && prefs_has_layout_pane_content(layout_pane_content_plist)); main_ui_->actionViewPacketList->setChecked(recent.packet_list_show && prefs_has_layout_pane_content(layout_pane_content_plist));
main_ui_->actionViewPacketDetails->setChecked(recent.tree_view_show && prefs_has_layout_pane_content(layout_pane_content_pdetails)); main_ui_->actionViewPacketDetails->setChecked(recent.tree_view_show && prefs_has_layout_pane_content(layout_pane_content_pdetails));
main_ui_->actionViewPacketBytes->setChecked(recent.byte_view_show && prefs_has_layout_pane_content(layout_pane_content_pbytes)); main_ui_->actionViewPacketBytes->setChecked(recent.byte_view_show && prefs_has_layout_pane_content(layout_pane_content_pbytes));
main_ui_->actionViewPacketDiagram->setChecked(recent.packet_diagram_show && prefs_has_layout_pane_content(layout_pane_content_pdiagram));
foreach(QAction *action, main_ui_->menuInterfaceToolbars->actions()) { foreach(QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
if (g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc)strcmp)) { if (g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc)strcmp)) {
@ -2299,6 +2302,9 @@ void MainWindow::showHideMainWidgets(QAction *action)
} else if (widget == byte_view_tab_) { } else if (widget == byte_view_tab_) {
recent.byte_view_show = show; recent.byte_view_show = show;
main_ui_->actionViewPacketBytes->setChecked(show); main_ui_->actionViewPacketBytes->setChecked(show);
} else if (widget == packet_diagram_) {
recent.packet_diagram_show = show;
main_ui_->actionViewPacketDiagram->setChecked(show);
} else { } else {
foreach(QAction *action, main_ui_->menuInterfaceToolbars->actions()) { foreach(QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
QToolBar *toolbar = action->data().value<QToolBar *>(); QToolBar *toolbar = action->data().value<QToolBar *>();

736
ui/qt/packet_diagram.cpp Normal file
View File

@ -0,0 +1,736 @@
/* packet_diagram.cpp
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "packet_diagram.h"
#include "math.h"
#include "epan/epan.h"
#include "epan/epan_dissect.h"
#include "wsutil/utf8_entities.h"
#include "wireshark_application.h"
#include "ui/qt/main_window.h"
#include "ui/qt/utils/proto_node.h"
#include "ui/qt/utils/variant_pointer.h"
#include <QContextMenuEvent>
#include <QGraphicsItem>
#include <QMenu>
#if defined(QT_SVG_LIB) && 0
#include <QBuffer>
#include <QMimeData>
#include <QSvgGenerator>
#endif
#include <QDebug>
// "rems" are root em widths, aka the regular font height, similar to rems in CSS.
class DiagramLayout {
public:
DiagramLayout() :
bits_per_row_(32),
small_font_rems_(0.75),
bit_width_rems_(1.0),
padding_rems_(0.5),
span_mark_offset_rems_(0.2),
show_fields_(false)
{
setFont(wsApp->font());
}
void setFont(QFont font) {
regular_font_ = font;
small_font_ = font;
small_font_.setPointSize(regular_font_.pointSize() * small_font_rems_);
QFontMetrics fm(regular_font_);
root_em_ = fm.height();
}
void setShowFields(bool show_fields = false) { show_fields_ = show_fields; }
int bitsPerRow() const { return bits_per_row_; }
const QFont regularFont() const { return regular_font_; }
const QFont smallFont() const { return small_font_; }
int bitWidth() const { return root_em_ * bit_width_rems_; }
int lineHeight() const { return root_em_; }
int hPadding() const { return root_em_ * padding_rems_; }
int vPadding() const { return root_em_ * padding_rems_; }
int spanMarkOffset() const { return root_em_ * span_mark_offset_rems_; }
int rowHeight() const {
int rows = show_fields_ ? 2 : 1;
return ((lineHeight() * rows) + (vPadding() * 2));
}
bool showFields() const { return show_fields_; }
private:
int bits_per_row_;
double small_font_rems_;
double bit_width_rems_;
double padding_rems_;
double span_mark_offset_rems_; // XXX Make this padding_rems_ / 2 instead?
bool show_fields_;
QFont regular_font_;
QFont small_font_;
int root_em_;
};
class FieldInformationGraphicsItem : public QGraphicsPolygonItem
{
public:
FieldInformationGraphicsItem(field_info *fi, int start_bit, int fi_length, const DiagramLayout *layout, QGraphicsItem *parent = nullptr) :
QGraphicsPolygonItem(QPolygonF(), parent),
finfo_(new FieldInformation(fi)),
start_bit_(start_bit),
layout_(layout),
collapsed_len_(fi_length),
collapsed_row_(-1)
{
Q_ASSERT(layout_);
for (int idx = 0; idx < NumSpanMarks; idx++) {
span_marks_[idx] = new QGraphicsLineItem(this);
span_marks_[idx]->hide();
}
int bits_per_row = layout_->bitsPerRow();
int row1_start = start_bit_ % bits_per_row;
int bits_remain = fi_length;
int row1_bits = bits_remain;
if (bits_remain + row1_start > bits_per_row) {
row1_bits = bits_per_row - row1_start;
bits_remain -= row1_bits;
if (row1_start == 0 && bits_remain > bits_per_row) {
// Collapse first row
bits_remain %= bits_per_row;
collapsed_row_ = 0;
}
} else {
bits_remain = 0;
}
int row2_bits = bits_remain;
if (bits_remain > bits_per_row) {
row2_bits = bits_per_row;
bits_remain -= bits_per_row;
if (bits_remain > bits_per_row) {
// Collapse second row
bits_remain %= bits_per_row;
collapsed_row_ = 1;
}
} else {
bits_remain = 0;
}
int row3_bits = bits_remain;
collapsed_len_ = row1_bits + row2_bits + row3_bits;
QRectF rr1, rr2, rr3;
QRectF row_rect = QRectF(row1_start, 0, row1_bits, 1);
unit_shape_ = QPolygonF(row_rect);
rr1 = row_rect;
unit_tr_ = row_rect;
if (row2_bits > 0) {
row_rect = QRectF(0, 1, row2_bits, 1);
unit_shape_ = unit_shape_.united(QPolygonF(row_rect));
rr2 = row_rect;
if (row2_bits > row1_bits) {
unit_tr_ = row_rect;
}
if (row3_bits > 0) {
row_rect = QRectF(0, 2, row3_bits, 1);
unit_shape_ = unit_shape_.united(QPolygonF(row_rect));
rr3 = row_rect;
}
QPainterPath pp;
pp.addPolygon(unit_shape_);
unit_shape_ = pp.simplified().toFillPolygon();
}
updateLayout();
if (finfo_->isValid()) {
setToolTip(QString("%1 (%2) = %3")
.arg(finfo_->headerInfo().name)
.arg(finfo_->headerInfo().abbreviation)
.arg(finfo_->toString()));
setData(Qt::UserRole, VariantPointer<field_info>::asQVariant(finfo_->fieldInfo()));
} else {
setToolTip(QObject::tr("Gap in dissection"));
}
}
int collapsedLength() { return collapsed_len_; }
void setPos(qreal x, qreal y) {
QGraphicsPolygonItem::setPos(x, y);
updateLayout();
}
int maxLeftY() {
qreal rel_len = (start_bit_ % layout_->bitsPerRow()) + collapsed_len_;
QPointF pt = mapToParent(QPointF(0, ceil(rel_len / layout_->bitsPerRow()) * layout_->rowHeight()));
return pt.y();
}
int maxRightY() {
qreal rel_len = (start_bit_ % layout_->bitsPerRow()) + collapsed_len_;
QPointF pt = mapToParent(QPointF(0, floor(rel_len / layout_->bitsPerRow()) * layout_->rowHeight()));
return pt.y();
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) {
painter->setPen(Qt::NoPen);
painter->save();
if (!finfo_->isValid()) {
QBrush brush = QBrush(option->palette.text().color(), Qt::BDiagPattern);
painter->setBrush(brush);
} else if (isSelected()) {
painter->setBrush(option->palette.highlight().color());
}
painter->drawPolygon(polygon());
painter->restore();
// Lower and inner right borders
painter->setPen(option->palette.text().color());
QPolygonF shape = polygon();
for (int idx = 1; idx < unit_shape_.size(); idx++) {
QPointF u_start = unit_shape_[idx - 1];
QPointF u_end = unit_shape_[idx];
QPointF start, end;
bool draw_line = false;
if (u_start.y() > 0 && u_start.y() == u_end.y()) {
draw_line = true;
} else if (u_start.x() > 0 && u_start.x() < layout_->bitsPerRow() && u_start.x() == u_end.x()) {
draw_line = true;
}
if (draw_line) {
start = shape[idx - 1];
end = shape[idx];
painter->drawLine(start, end);
}
}
if (!finfo_->isValid()) {
return;
}
// Field label(s)
QString label = finfo_->headerInfo().name;
paintLabel(painter, label, scaled_tr_);
if (layout_->showFields()) {
label = finfo_->toString();
paintLabel(painter, label, scaled_tr_.adjusted(0, scaled_tr_.height(), 0, scaled_tr_.height()));
}
}
private:
enum SpanMark {
TopLeft,
BottomLeft,
TopRight,
BottomRight,
NumSpanMarks
};
FieldInformation *finfo_;
int start_bit_;
const DiagramLayout *layout_;
int collapsed_len_;
int collapsed_row_;
QPolygonF unit_shape_;
QRectF unit_tr_;
QRectF scaled_tr_;
QGraphicsLineItem *span_marks_[NumSpanMarks];
void updateLayout() {
QTransform xform;
xform.scale(layout_->bitWidth(), layout_->rowHeight());
setPolygon(xform.map(unit_shape_));
scaled_tr_ = xform.mapRect(unit_tr_);
scaled_tr_.adjust(layout_->hPadding(), layout_->vPadding(), -layout_->hPadding(), -layout_->vPadding());
scaled_tr_.setHeight(layout_->lineHeight());
// Collapsed / span marks
for (int idx = 0; idx < NumSpanMarks; idx++) {
span_marks_[idx]->hide();
}
if (collapsed_row_ >= 0) {
QRectF bounding_rect = polygon().boundingRect();
qreal center_y = bounding_rect.top() + (layout_->rowHeight() * collapsed_row_) + (layout_->rowHeight() / 2);
qreal mark_w = layout_->bitWidth() / 3; // Each mark side to center
QLineF span_l = QLineF(-mark_w, mark_w / 2, mark_w, -mark_w / 2);
for (int idx = 0; idx < NumSpanMarks; idx++) {
QPointF center;
switch (idx) {
case TopLeft:
center = QPointF(bounding_rect.left(), center_y - layout_->spanMarkOffset());
break;
case BottomLeft:
center = QPointF(bounding_rect.left(), center_y + layout_->spanMarkOffset());
break;
case TopRight:
center = QPointF(bounding_rect.right(), center_y - layout_->spanMarkOffset());
break;
case BottomRight:
center = QPointF(bounding_rect.right(), center_y + layout_->spanMarkOffset());
break;
}
span_marks_[idx]->setLine(span_l.translated(center));
span_marks_[idx]->setZValue(zValue() - 0.1);
span_marks_[idx]->show();
}
}
}
void paintLabel(QPainter *painter, QString label, QRectF label_rect) {
QFontMetrics fm = QFontMetrics(layout_->regularFont());
painter->setFont(layout_->regularFont());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
int label_w = fm.horizontalAdvance(label);
#else
int label_w = fm.width(label);
#endif
if (label_w > label_rect.width()) {
painter->setFont(layout_->smallFont());
fm = QFontMetrics(layout_->smallFont());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
label_w = fm.horizontalAdvance(label);
#else
label_w = fm.width(label);
#endif
if (label_w > label_rect.width()) {
// XXX Use parent+ItemClipsChildrenToShape or setScale instead?
label = fm.elidedText(label, Qt::ElideRight, label_rect.width());
}
}
painter->drawText(label_rect, Qt::AlignCenter, label);
}
};
PacketDiagram::PacketDiagram(QWidget *parent) :
QGraphicsView(new QGraphicsScene(), parent),
layout_(new DiagramLayout),
cap_file_(nullptr),
root_node_(nullptr),
selected_field_(nullptr),
y_pos_(0)
{
setAccessibleName(tr("Packet diagram"));
setRenderHint(QPainter::Antialiasing);
// XXX Move to setMonospaceFont similar to ProtoTree
layout_->setFont(font());
connect(wsApp, &WiresharkApplication::appInitialized, this, &PacketDiagram::connectToMainWindow);
QGraphicsScene *this_scene = scene();
connect(this_scene, &QGraphicsScene::selectionChanged, this, &PacketDiagram::sceneSelectionChanged);
connect(wsApp, &WiresharkApplication::zoomRegularFont, this, &PacketDiagram::setFont);
}
PacketDiagram::~PacketDiagram()
{
delete layout_;
}
void PacketDiagram::setRootNode(proto_node *root_node)
{
scene()->clear();
root_node_ = root_node;
selected_field_ = nullptr;
y_pos_ = 0;
ProtoNode parent_node(root_node_);
if (!parent_node.isValid()) {
return;
}
ProtoNode::ChildIterator kids = parent_node.children();
while (kids.element().isValid())
{
proto_node *tl_node = kids.element().protoNode();
kids.next();
// Exclude all ("Frame") and nothing
if (tl_node->finfo->start == 0 && tl_node->finfo->length == (int) tvb_captured_length(cap_file_->edt->tvb)) {
continue;
}
if (tl_node->finfo->length < 1) {
continue;
}
addDiagram(tl_node);
}
}
void PacketDiagram::clear()
{
setRootNode(nullptr);
}
void PacketDiagram::setCaptureFile(capture_file *cf)
{
// For use by the main view, set the capture file which will later have a
// dissection (EDT) ready.
// The packet dialog sets a fixed EDT context and MUST NOT use this.
cap_file_ = cf;
}
void PacketDiagram::setFont(const QFont &font)
{
layout_->setFont(font);
setRootNode(root_node_);
}
void PacketDiagram::selectedFieldChanged(FieldInformation *finfo)
{
setSelectedField(finfo ? finfo->fieldInfo() : nullptr);
}
void PacketDiagram::selectedFrameChanged(QList<int> frames)
{
if (frames.count() == 1 && cap_file_ && cap_file_->edt && cap_file_->edt->tree) {
setRootNode(cap_file_->edt->tree);
} else {
// Clear the proto tree contents as they have become invalid.
setRootNode(nullptr);
}
}
void PacketDiagram::contextMenuEvent(QContextMenuEvent *event)
{
if (!event) {
return;
}
QAction *action;
QMenu ctx_menu(this);
action = ctx_menu.addAction(tr("Show Field Values"));
action->setCheckable(true);
action->setChecked(layout_->showFields());
connect(action, &QAction::toggled, this, &PacketDiagram::showFieldsToggled);
ctx_menu.addSeparator();
action = ctx_menu.addAction(tr("Save Diagram As" UTF8_HORIZONTAL_ELLIPSIS));
connect(action, &QAction::triggered, this, &PacketDiagram::saveAsTriggered);
action = ctx_menu.addAction(tr("Copy as Raster Image"));
connect(action, &QAction::triggered, this, &PacketDiagram::copyAsRasterTriggered);
#if defined(QT_SVG_LIB) && !defined(Q_OS_MAC)
action = ctx_menu.addAction(tr(UTF8_HORIZONTAL_ELLIPSIS "as SVG"));
connect(action, &QAction::triggered, this, &PacketDiagram::copyAsSvgTriggered);
#endif
ctx_menu.exec(event->globalPos());
}
void PacketDiagram::connectToMainWindow()
{
MainWindow *main_window = qobject_cast<MainWindow *>(wsApp->mainWindow());
if (!main_window) {
return;
}
connect(main_window, &MainWindow::setCaptureFile, this, &PacketDiagram::setCaptureFile);
connect(main_window, &MainWindow::fieldSelected, this, &PacketDiagram::selectedFieldChanged);
connect(main_window, &MainWindow::framesSelected, this, &PacketDiagram::selectedFrameChanged);
connect(this, &PacketDiagram::fieldSelected, main_window, &MainWindow::fieldSelected);
}
void PacketDiagram::sceneSelectionChanged()
{
field_info *sel_fi = nullptr;
if (! scene()->selectedItems().isEmpty()) {
sel_fi = VariantPointer<field_info>::asPtr(scene()->selectedItems().first()->data(Qt::UserRole));
}
if (sel_fi) {
FieldInformation finfo(sel_fi, this);
emit fieldSelected(&finfo);
} else {
emit fieldSelected(nullptr);
}
}
struct WireItem {
proto_item *item;
int start_bit;
int length;
};
void PacketDiagram::addDiagram(proto_node *tl_node)
{
QGraphicsItem *item;
QGraphicsSimpleTextItem *t_item;
int bits_per_row = layout_->bitsPerRow();
int bit_width = layout_->bitWidth();
int diag_w = bit_width * layout_->bitsPerRow();
qreal x = layout_->hPadding();
// Title
t_item = scene()->addSimpleText(tl_node->finfo->hfinfo->name);
t_item->setFont(layout_->regularFont());
t_item->setPos(0, y_pos_);
y_pos_ += layout_->lineHeight() + (bit_width / 4);
int border_top = y_pos_;
// Bit scale + tick marks
QList<int> tick_nums;
for (int tn = 0 ; tn < layout_->bitsPerRow(); tn += 16) {
tick_nums << tn << tn + 15;
}
qreal y_bottom = y_pos_ + bit_width;
QGraphicsItem *tl_item = scene()->addLine(x, y_bottom, x + diag_w, y_bottom);
for (int tick_n = 0; tick_n < bits_per_row; tick_n++) {
x = layout_->hPadding() + (tick_n * bit_width);
qreal y_top = y_pos_ + (tick_n % 8 == 0 ? 0 : bit_width / 2);
if (tick_n > 0) {
scene()->addLine(x, y_top, x, y_bottom);
}
if (tick_nums.contains(tick_n)) {
t_item = scene()->addSimpleText(QString::number(tick_n));
t_item->setFont(layout_->smallFont());
t_item->setPos(x + ((bit_width - t_item->boundingRect().width()) / 2.0) + 2, y_pos_);
}
}
y_pos_ = y_bottom;
x = layout_->hPadding();
// Top-level fields
int last_start_bit = -1;
int max_l_y = y_bottom;
QList<WireItem>wire_items;
for (proto_item *cur_item = tl_node->first_child; cur_item; cur_item = cur_item->next) {
if (proto_item_is_generated(cur_item) || proto_item_is_hidden(cur_item)) {
continue;
}
field_info *fi = cur_item->finfo;
int start_bit = ((fi->start - tl_node->finfo->start) * 8) + FI_GET_BITS_OFFSET(fi);
int length = FI_GET_BITS_SIZE(fi) ? FI_GET_BITS_SIZE(fi) : fi->length * 8;
if (start_bit <= last_start_bit || length <= 0) {
qDebug() << "Skipping pass 1" << fi->hfinfo->abbrev << start_bit << last_start_bit << length;
continue;
}
last_start_bit = start_bit;
WireItem wire_item = { cur_item, start_bit, length };
wire_items << wire_item;
}
qreal z_value = tl_item->zValue();
for (int idx = 0; idx < wire_items.size(); idx++) {
WireItem *wire_item = &wire_items[idx];
field_info *fi = wire_item->item->finfo;
if (idx < wire_items.size() - 1) {
WireItem *next_item = &wire_items[idx + 1];
if (wire_item->start_bit + wire_item->length > next_item->start_bit) {
wire_item->length = next_item->start_bit - wire_item->start_bit;
qDebug() << "Resized pass 2" << fi->hfinfo->abbrev << wire_item->start_bit << wire_item->length << next_item->start_bit;
if (wire_item->length <= 0) {
qDebug() << "Skipping pass 2" << fi->hfinfo->abbrev << wire_item->start_bit << wire_item->length;
continue;
}
}
if (next_item->start_bit > wire_item->start_bit + wire_item->length) {
int gap_start_bit = wire_item->start_bit + wire_item->length;
int gap_len = next_item->start_bit - gap_start_bit;
int y_off = (gap_start_bit / bits_per_row) * layout_->rowHeight();
// Stack each item behind the previous one.
z_value -= .01;
FieldInformationGraphicsItem *gap_item = new FieldInformationGraphicsItem(nullptr, gap_start_bit, gap_len, layout_);
gap_item->setPos(x, y_bottom + y_off);
gap_item->setZValue(z_value);
scene()->addItem(gap_item);
}
}
int y_off = (wire_item->start_bit / bits_per_row) * layout_->rowHeight();
// Stack each item behind the previous one.
z_value -= .01;
FieldInformationGraphicsItem *fi_item = new FieldInformationGraphicsItem(fi, wire_item->start_bit, wire_item->length, layout_);
fi_item->setPos(x, y_bottom + y_off);
fi_item->setFlag(QGraphicsItem::ItemIsSelectable);
fi_item->setAcceptedMouseButtons(Qt::LeftButton);
fi_item->setZValue(z_value);
scene()->addItem(fi_item);
y_pos_ = fi_item->maxRightY();
max_l_y = fi_item->maxLeftY();
}
// Left & right borders
scene()->addLine(x, border_top, x, max_l_y);
scene()->addLine(x + diag_w, border_top, x + diag_w, y_pos_);
// Inter-diagram margin
y_pos_ = max_l_y + bit_width;
// Set the proper color. Needed for dark mode on macOS + Qt 5.15.0 at least, possibly other cases.
foreach (item, scene()->items()) {
QGraphicsSimpleTextItem *t_item = qgraphicsitem_cast<QGraphicsSimpleTextItem *>(item);
if (t_item) {
t_item->setBrush(palette().text().color());
}
QGraphicsLineItem *l_item = qgraphicsitem_cast<QGraphicsLineItem *>(item);
if (l_item) {
l_item->setPen(palette().text().color());
}
}
}
void PacketDiagram::setSelectedField(field_info *fi)
{
QSignalBlocker blocker(this);
FieldInformationGraphicsItem *fi_item;
foreach (QGraphicsItem *item, scene()->items()) {
if (item->isSelected()) {
item->setSelected(false);
fi_item = qgraphicsitem_cast<FieldInformationGraphicsItem *>(item);
}
if (fi && VariantPointer<field_info>::asPtr(item->data(Qt::UserRole)) == fi) {
fi_item = qgraphicsitem_cast<FieldInformationGraphicsItem *>(item);
if (fi_item) {
fi_item->setSelected(true);
}
}
}
}
QImage PacketDiagram::exportToImage()
{
// Create a hi-res 2x scaled image.
int scale = 2;
QRect rr = QRect(0, 0, sceneRect().size().width() * scale, sceneRect().size().height() * scale);
QImage raster_diagram = QImage(rr.size(), QImage::Format_ARGB32);
QPainter raster_painter(&raster_diagram);
raster_painter.setRenderHint(QPainter::Antialiasing);
raster_painter.fillRect(rr, palette().base().color());
scene()->render(&raster_painter);
raster_painter.end();
return raster_diagram;
}
#if defined(QT_SVG_LIB) && 0
QByteArray PacketDiagram::exportToSvg()
{
QRect sr = QRect(0, 0, sceneRect().size().width(), sceneRect().size().height());
QBuffer svg_buf;
QSvgGenerator svg_diagram;
svg_diagram.setSize(sr.size());
svg_diagram.setViewBox(sr);
svg_diagram.setOutputDevice(&svg_buf);
QPainter svg_painter(&svg_diagram);
svg_painter.fillRect(sr, palette().base().color());
scene()->render(&svg_painter);
svg_painter.end();
return svg_buf.buffer();
}
#endif
void PacketDiagram::showFieldsToggled(bool checked)
{
layout_->setShowFields(checked);
setRootNode(root_node_);
}
// XXX - We have similar code in tcp_stream_dialog and io_graph_dialog. Should this be a common routine?
void PacketDiagram::saveAsTriggered()
{
QString file_name, extension;
QDir path(wsApp->lastOpenDir());
QString png_filter = tr("Portable Network Graphics (*.png)");
QString bmp_filter = tr("Windows Bitmap (*.bmp)");
// Gaze upon my beautiful graph with lossy artifacts!
QString jpeg_filter = tr("JPEG File Interchange Format (*.jpeg *.jpg)");
QStringList fl = QStringList() << png_filter << bmp_filter << jpeg_filter;
#if defined(QT_SVG_LIB) && 0
QString svg_filter = tr("Scalable Vector Graphics (*.svg)");
fl << svg_filter;
#endif
QString filter = fl.join(";;");
file_name = WiresharkFileDialog::getSaveFileName(this, wsApp->windowTitleString(tr("Save Graph As" UTF8_HORIZONTAL_ELLIPSIS)),
path.canonicalPath(), filter, &extension);
if (file_name.length() > 0) {
bool save_ok = false;
if (extension.compare(png_filter) == 0) {
QImage raster_diagram = exportToImage();
save_ok = raster_diagram.save(file_name, "PNG");
} else if (extension.compare(bmp_filter) == 0) {
QImage raster_diagram = exportToImage();
save_ok = raster_diagram.save(file_name, "BMP");
} else if (extension.compare(jpeg_filter) == 0) {
QImage raster_diagram = exportToImage();
save_ok = raster_diagram.save(file_name, "JPG");
}
#if defined(QT_SVG_LIB) && 0
else if (extension.compare(svg_filter) == 0) {
QByteArray svg_diagram = exportToSvg();
QFile file(file_name);
if (file.open(QIODevice::WriteOnly)) {
save_ok = file.write(svg_diagram) > 0;
file.close();
}
}
#endif
// else error dialog?
if (save_ok) {
path = QDir(file_name);
wsApp->setLastOpenDir(path.canonicalPath().toUtf8().constData());
}
}
}
void PacketDiagram::copyAsRasterTriggered()
{
QImage raster_diagram = exportToImage();
wsApp->clipboard()->setImage(raster_diagram);
}
#if defined(QT_SVG_LIB) && !defined(Q_OS_MAC) && 0
void PacketDiagram::copyAsSvgTriggered()
{
QByteArray svg_ba = exportToSvg();
// XXX It looks like we have to use/subclass QMacPasteboardMime in
// order for this to work on macOS.
// It might be easier to just do "Save As" instead.
QMimeData *md = new QMimeData();
md->setData("image/svg+xml", svg_buf);
wsApp->clipboard()->setMimeData(md);
}
#endif

72
ui/qt/packet_diagram.h Normal file
View File

@ -0,0 +1,72 @@
/* packet_diagram.h
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PACKET_DIAGRAM_H
#define PACKET_DIAGRAM_H
#include <config.h>
#include <epan/proto.h>
#include "cfile.h"
#include <ui/qt/utils/field_information.h>
#include <QGraphicsView>
class DiagramLayout;
class PacketDiagram : public QGraphicsView
{
Q_OBJECT
public:
PacketDiagram(QWidget *parent = nullptr);
~PacketDiagram();
void setRootNode(proto_node *root_node);
void clear();
signals:
void fieldSelected(FieldInformation *);
public slots:
void setCaptureFile(capture_file *cf);
void setFont(const QFont &font);
void selectedFieldChanged(FieldInformation *finfo);
void selectedFrameChanged(QList<int> frames);
protected:
virtual void contextMenuEvent(QContextMenuEvent *event) override;
private slots:
void connectToMainWindow();
void sceneSelectionChanged();
private:
void addDiagram(proto_node *tl_node);
void setSelectedField(field_info *fi);
QImage exportToImage();
#if defined(QT_SVG_LIB) && 0
QByteArray exportToSvg();
#endif
void showFieldsToggled(bool checked);
void saveAsTriggered();
void copyAsRasterTriggered();
#if defined(QT_SVG_LIB) && !defined(Q_OS_MAC) && 0
void copyAsSvgTriggered();
#endif
DiagramLayout *layout_;
capture_file *cap_file_;
proto_node *root_node_;
field_info *selected_field_;
int y_pos_;
};
#endif // PACKET_DIAGRAM_H

View File

@ -1823,6 +1823,7 @@ void TCPStreamDialog::transformYRange(const QCPRange &y_range1)
sp->yAxis2->setRangeLower(yp2.y1()); sp->yAxis2->setRangeLower(yp2.y1());
} }
// XXX - We have similar code in io_graph_dialog and packet_diagram. Should this be a common routine?
void TCPStreamDialog::on_buttonBox_accepted() void TCPStreamDialog::on_buttonBox_accepted()
{ {
QString file_name, extension; QString file_name, extension;

View File

@ -136,16 +136,23 @@ const QString FieldInformation::moduleName()
return module_name; return module_name;
} }
QString FieldInformation::toString()
{
QString repr;
gchar *repr_str;
repr_str = fvalue_to_string_repr(NULL, &fi_->value, FTREPR_DISPLAY, fi_->hfinfo->display);
if (repr_str) {
repr = repr_str;
}
wmem_free(NULL, repr_str);
return repr;
}
QString FieldInformation::url() QString FieldInformation::url()
{ {
QString url; QString url;
if (flag(FI_URL) && headerInfo().isValid && IS_FT_STRING(fi_->hfinfo->type)) { if (flag(FI_URL) && headerInfo().isValid && IS_FT_STRING(fi_->hfinfo->type)) {
gchar *url_str; url = toString();
url_str = fvalue_to_string_repr(NULL, &fi_->value, FTREPR_DISPLAY, fi_->hfinfo->display);
if (url_str) {
url = url_str;
}
wmem_free(NULL, url_str);
} }
return url; return url;
} }

View File

@ -60,6 +60,7 @@ public:
bool tvbContains(FieldInformation *); bool tvbContains(FieldInformation *);
unsigned flag(unsigned mask); unsigned flag(unsigned mask);
const QString moduleName(); const QString moduleName();
QString toString();
QString url(); QString url();
const QByteArray printableData(); const QByteArray printableData();

View File

@ -1188,13 +1188,17 @@ void WiresharkApplication::zoomTextFont(int zoomLevel)
{ {
// Scale by 10%, rounding to nearest half point, minimum 1 point. // Scale by 10%, rounding to nearest half point, minimum 1 point.
// XXX Small sizes repeat. It might just be easier to create a map of multipliers. // XXX Small sizes repeat. It might just be easier to create a map of multipliers.
zoomed_font_ = mono_font_;
qreal zoom_size = mono_font_.pointSize() * 2 * qPow(qreal(1.1), zoomLevel); qreal zoom_size = mono_font_.pointSize() * 2 * qPow(qreal(1.1), zoomLevel);
zoom_size = qRound(zoom_size) / qreal(2.0); zoom_size = qRound(zoom_size) / qreal(2.0);
zoom_size = qMax(zoom_size, qreal(1.0)); zoom_size = qMax(zoom_size, qreal(1.0));
zoomed_font_.setPointSizeF(zoom_size);
zoomed_font_ = mono_font_;
zoomed_font_.setPointSizeF(zoom_size);
emit zoomMonospaceFont(zoomed_font_); emit zoomMonospaceFont(zoomed_font_);
QFont zoomed_application_font = font();
zoomed_application_font.setPointSizeF(zoom_size);
emit zoomRegularFont(zoomed_application_font);
} }
#if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN) #if defined(HAVE_SOFTWARE_UPDATE) && defined(Q_OS_WIN)

View File

@ -205,6 +205,7 @@ signals:
/* Signals activation and stop of a capture. The value provides the number of active captures */ /* Signals activation and stop of a capture. The value provides the number of active captures */
void captureActive(int); void captureActive(int);
void zoomRegularFont(const QFont & font);
void zoomMonospaceFont(const QFont & font); void zoomMonospaceFont(const QFont & font);
public slots: public slots:

View File

@ -35,6 +35,7 @@
#define RECENT_KEY_PACKET_LIST_SHOW "gui.packet_list_show" #define RECENT_KEY_PACKET_LIST_SHOW "gui.packet_list_show"
#define RECENT_KEY_TREE_VIEW_SHOW "gui.tree_view_show" #define RECENT_KEY_TREE_VIEW_SHOW "gui.tree_view_show"
#define RECENT_KEY_BYTE_VIEW_SHOW "gui.byte_view_show" #define RECENT_KEY_BYTE_VIEW_SHOW "gui.byte_view_show"
#define RECENT_KEY_PACKET_DIAGRAM_SHOW "gui.packet_diagram_show"
#define RECENT_KEY_STATUSBAR_SHOW "gui.statusbar_show" #define RECENT_KEY_STATUSBAR_SHOW "gui.statusbar_show"
#define RECENT_KEY_PACKET_LIST_COLORIZE "gui.packet_list_colorize" #define RECENT_KEY_PACKET_LIST_COLORIZE "gui.packet_list_colorize"
#define RECENT_GUI_TIME_FORMAT "gui.time_format" #define RECENT_GUI_TIME_FORMAT "gui.time_format"
@ -844,6 +845,10 @@ write_profile_recent(void)
RECENT_KEY_BYTE_VIEW_SHOW, RECENT_KEY_BYTE_VIEW_SHOW,
recent.byte_view_show); recent.byte_view_show);
write_recent_boolean(rf, "Packet diagram show (hide)",
RECENT_KEY_PACKET_DIAGRAM_SHOW,
recent.packet_diagram_show);
write_recent_boolean(rf, "Statusbar show (hide)", write_recent_boolean(rf, "Statusbar show (hide)",
RECENT_KEY_STATUSBAR_SHOW, RECENT_KEY_STATUSBAR_SHOW,
recent.statusbar_show); recent.statusbar_show);
@ -1054,6 +1059,8 @@ read_set_recent_pair_static(gchar *key, const gchar *value,
parse_recent_boolean(value, &recent.tree_view_show); parse_recent_boolean(value, &recent.tree_view_show);
} else if (strcmp(key, RECENT_KEY_BYTE_VIEW_SHOW) == 0) { } else if (strcmp(key, RECENT_KEY_BYTE_VIEW_SHOW) == 0) {
parse_recent_boolean(value, &recent.byte_view_show); parse_recent_boolean(value, &recent.byte_view_show);
} else if (strcmp(key, RECENT_KEY_PACKET_DIAGRAM_SHOW) == 0) {
parse_recent_boolean(value, &recent.packet_diagram_show);
} else if (strcmp(key, RECENT_KEY_STATUSBAR_SHOW) == 0) { } else if (strcmp(key, RECENT_KEY_STATUSBAR_SHOW) == 0) {
parse_recent_boolean(value, &recent.statusbar_show); parse_recent_boolean(value, &recent.statusbar_show);
} else if (strcmp(key, RECENT_KEY_PACKET_LIST_COLORIZE) == 0) { } else if (strcmp(key, RECENT_KEY_PACKET_LIST_COLORIZE) == 0) {
@ -1323,6 +1330,7 @@ recent_read_profile_static(char **rf_path_return, int *rf_errno_return)
recent.packet_list_show = TRUE; recent.packet_list_show = TRUE;
recent.tree_view_show = TRUE; recent.tree_view_show = TRUE;
recent.byte_view_show = TRUE; recent.byte_view_show = TRUE;
recent.packet_diagram_show = TRUE;
recent.statusbar_show = TRUE; recent.statusbar_show = TRUE;
recent.packet_list_colorize = TRUE; recent.packet_list_colorize = TRUE;
recent.gui_time_format = TS_RELATIVE; recent.gui_time_format = TS_RELATIVE;

View File

@ -89,6 +89,7 @@ typedef struct recent_settings_tag {
gboolean packet_list_show; gboolean packet_list_show;
gboolean tree_view_show; gboolean tree_view_show;
gboolean byte_view_show; gboolean byte_view_show;
gboolean packet_diagram_show;
gboolean statusbar_show; gboolean statusbar_show;
gboolean packet_list_colorize; gboolean packet_list_colorize;
ts_type gui_time_format; ts_type gui_time_format;